Skip to content

Commit

Permalink
feat: role management policy resource
Browse files Browse the repository at this point in the history
  • Loading branch information
josh-barker committed Sep 18, 2023
1 parent 76cdafc commit 125c2ed
Show file tree
Hide file tree
Showing 29 changed files with 3,520 additions and 73 deletions.
9 changes: 9 additions & 0 deletions internal/services/authorization/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/hashicorp/go-azure-sdk/resource-manager/authorization/2020-10-01/roleassignmentschedulerequests"
"github.com/hashicorp/go-azure-sdk/resource-manager/authorization/2020-10-01/roleeligibilityscheduleinstances"
"github.com/hashicorp/go-azure-sdk/resource-manager/authorization/2020-10-01/roleeligibilityschedulerequests"
"github.com/hashicorp/go-azure-sdk/resource-manager/authorization/2020-10-01/rolemanagementpolicies"
"github.com/hashicorp/go-azure-sdk/resource-manager/authorization/2022-04-01/roleassignments"
"github.com/hashicorp/go-azure-sdk/resource-manager/authorization/2022-04-01/roledefinitions"
"github.com/hashicorp/terraform-provider-azurerm/internal/common"
Expand All @@ -25,6 +26,7 @@ type Client struct {
RoleEligibilityScheduleInstancesClient *roleeligibilityscheduleinstances.RoleEligibilityScheduleInstancesClient
ScopedRoleAssignmentsClient *roleassignments.RoleAssignmentsClient
ScopedRoleDefinitionsClient *roledefinitions.RoleDefinitionsClient
RoleManagementPoliciesClient *rolemanagementpolicies.RoleManagementPoliciesClient
}

func NewClient(o *common.ClientOptions) (*Client, error) {
Expand Down Expand Up @@ -71,9 +73,16 @@ func NewClient(o *common.ClientOptions) (*Client, error) {
}
o.Configure(scopedRoleDefinitionsClient.Client, o.Authorizers.ResourceManager)

roleManagementPoliciesClient, _ := rolemanagementpolicies.NewRoleManagementPoliciesClientWithBaseURI(o.Environment.ResourceManager)
if err != nil {
return nil, fmt.Errorf("building Role Managemetn Policies Client: %+v", err)
}
o.Configure(roleManagementPoliciesClient.Client, o.Authorizers.ResourceManager)

return &Client{
RoleAssignmentsClient: &roleAssignmentsClient,
RoleDefinitionsClient: &roleDefinitionsClient,
RoleManagementPoliciesClient: roleManagementPoliciesClient,
RoleAssignmentScheduleRequestClient: roleAssignmentScheduleRequestsClient,
RoleAssignmentScheduleInstancesClient: roleAssignmentScheduleInstancesClient,
RoleEligibilityScheduleRequestClient: roleEligibilityScheduleRequestClient,
Expand Down
66 changes: 66 additions & 0 deletions internal/services/authorization/parse/role_management_policy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package parse

import (
"fmt"
"strings"

"github.com/hashicorp/go-azure-sdk/resource-manager/authorization/2020-10-01/rolemanagementpolicies"
)

type RoleManagementPolicyID struct {
Scope string
RoleManagementPolicyName string
RoleDefinitionId string
}

func NewRoleManagementPolicyID(scope string, roleManagementPolicyName string, roleDefinitionId string) RoleManagementPolicyID {
return RoleManagementPolicyID{
Scope: scope,
RoleManagementPolicyName: roleManagementPolicyName,
RoleDefinitionId: roleDefinitionId,
}
}

func (id RoleManagementPolicyID) ID() string {
fmtString := "%s/providers/Microsoft.Authorization/roleManagementPolicies/%s|%s"
return fmt.Sprintf(fmtString, id.Scope, id.RoleManagementPolicyName, id.RoleDefinitionId)
}

func (id RoleManagementPolicyID) ScopedRoleManagementPolicyId() rolemanagementpolicies.ScopedRoleManagementPolicyId {
return rolemanagementpolicies.NewScopedRoleManagementPolicyID(id.Scope, id.RoleManagementPolicyName)
}

func (id RoleManagementPolicyID) String() string {
segments := []string{
fmt.Sprintf("RoleManagementPolicyName %q", id.RoleManagementPolicyName),
fmt.Sprintf("Scope %q", id.Scope),
fmt.Sprintf("Role Definition Id %q", id.RoleDefinitionId),
}
segmentsStr := strings.Join(segments, " / ")
return fmt.Sprintf("%s: (%s)", "Role Management Policy", segmentsStr)
}

// RoleManagementPolicyId is a pseudo ID for storing Role Definition ID parameter as this it not retrievable from API
// It is formed of the Azure Resource ID for the Role Management Policy ID and the Role Definition ID it is created against
func RoleManagementPolicyId(input string) (*RoleManagementPolicyID, error) {
parts := strings.Split(input, "|")
if len(parts) != 2 {
return nil, fmt.Errorf("could not parse Role Management Policy ID, invalid format %q", input)
}

roleManagementPolicyID := RoleManagementPolicyID{}

rawRoleManagementPolicyId := parts[0]
rawRoleDefinitionId := parts[1]

roleManagementPolicyId, err := rolemanagementpolicies.ParseScopedRoleManagementPolicyID(rawRoleManagementPolicyId)
if err != nil {
return nil, err
}
roleManagementPolicyID.Scope = roleManagementPolicyId.Scope
roleManagementPolicyID.RoleManagementPolicyName = roleManagementPolicyId.RoleManagementPolicyName

roleManagementPolicyID.RoleDefinitionId = rawRoleDefinitionId

return &roleManagementPolicyID, nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ func (PimActiveRoleAssignmentResource) Arguments() map[string]*pluginsdk.Schema
Type: pluginsdk.TypeList,
MaxItems: 1,
Optional: true,
Computed: true,
ForceNew: true,
Description: "The schedule details of this role assignment.",
Elem: &pluginsdk.Resource{
Expand Down
88 changes: 52 additions & 36 deletions internal/services/authorization/pim_active_role_assignment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,21 @@ import (

type PimActiveRoleAssignmentResource struct{}

// TODO: update the management policy configuration so that it can have no expiration
// Depends on new resource - azurerm_role_management_policy - https://github.com/hashicorp/terraform-provider-azurerm/pull/20496
// func TestAccPimActiveRoleAssignment_noExpiration(t *testing.T) {
// data := acceptance.BuildTestData(t, "azurerm_pim_active_role_assignment", "test")
// r := PimActiveRoleAssignmentResource{}

// data.ResourceTest(t, r, []acceptance.TestStep{
// {
// Config: r.noExpirationConfig(),
// Check: acceptance.ComposeTestCheckFunc(
// check.That(data.ResourceName).ExistsInAzure(r),
// check.That(data.ResourceName).Key("scope").Exists(),
// ),
// },
// data.ImportStep("schedule.0.start_date_time"),
// })
// }
func TestAccPimActiveRoleAssignment_noExpiration(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_pim_active_role_assignment", "test")
r := PimActiveRoleAssignmentResource{}

data.ResourceTest(t, r, []acceptance.TestStep{
{
Config: r.noExpirationConfig(),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
check.That(data.ResourceName).Key("scope").Exists(),
),
},
data.ImportStep("schedule.0.start_date_time"),
})
}

func TestAccPimActiveRoleAssignment_expirationByDurationHoursConfig(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_pim_active_role_assignment", "test")
Expand Down Expand Up @@ -154,30 +152,48 @@ func (r PimActiveRoleAssignmentResource) Exists(ctx context.Context, client *cli
return utils.Bool(foundDirectAssignment), nil
}

// func (PimActiveRoleAssignmentResource) noExpirationConfig() string {
// return `
// data "azurerm_subscription" "primary" {}
func (PimActiveRoleAssignmentResource) noExpirationConfig() string {
return `
data "azurerm_subscription" "primary" {}
data "azurerm_client_config" "test" {}
data "azurerm_role_definition" "test" {
name = "Monitoring Data Reader"
}
resource "azurerm_role_management_policy" "test" {
scope = data.azurerm_subscription.primary.id
role_definition_id = "${data.azurerm_subscription.primary.id}${data.azurerm_role_definition.test.id}"
// data "azurerm_client_config" "test" {}
assignment {
eligible {
allow_permanent = true
}
active {
allow_permanent = true
}
}
}
// data "azurerm_role_definition" "test" {
// name = "Monitoring Data Reader"
// }
resource "azurerm_pim_active_role_assignment" "test" {
scope = data.azurerm_subscription.primary.id
role_definition_id = "${data.azurerm_subscription.primary.id}${data.azurerm_role_definition.test.id}"
principal_id = data.azurerm_client_config.test.object_id
// resource "azurerm_pim_active_role_assignment" "test" {
// scope = data.azurerm_subscription.primary.id
// role_definition_id = "${data.azurerm_subscription.primary.id}${data.azurerm_role_definition.test.id}"
// principal_id = data.azurerm_client_config.test.object_id
justification = "No Expiration"
// justification = "No Expiration"
ticket {
number = "1"
system = "example ticket system"
}
// ticket {
// number = "1"
// system = "example ticket system"
// }
// }
// `
// }
depends_on = [ # Wait for policy to be defined for no expiration
azurerm_role_management_policy.test
]
}
`
}

func (PimActiveRoleAssignmentResource) expirationByDurationHoursConfig(data acceptance.TestData) string {
return fmt.Sprintf(`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ func (PimEligibleRoleAssignmentResource) Arguments() map[string]*pluginsdk.Schem
Type: pluginsdk.TypeList,
MaxItems: 1,
Optional: true,
Computed: true,
ForceNew: true,
Description: "The schedule details of this eligible role assignment.",
Elem: &pluginsdk.Resource{
Expand Down Expand Up @@ -587,6 +588,9 @@ func (r PimEligibleRoleAssignmentResource) mapRoleEligibilityScheduleRequestProp
}
output.DurationDays = days
}
} else {
output.DurationDays = 0
output.DurationHours = 0
}

output.EndDateTime = pointer.From(input.EndDateTime)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,21 @@ import (

type PimEligibleRoleAssignmentResource struct{}

// TODO: update the management policy configuration so that it can have no expiration
// Depends on new resource - azurerm_role_management_policy - https://github.com/hashicorp/terraform-provider-azurerm/pull/20496
// func TestAccPimEligibleRoleAssignment_noExpiration(t *testing.T) {
// data := acceptance.BuildTestData(t, "azurerm_pim_eligible_role_assignment", "test")
// r := PimEligibleRoleAssignmentResource{}

// data.ResourceTest(t, r, []acceptance.TestStep{
// {
// Config: r.noExpirationConfig(data),
// Check: acceptance.ComposeTestCheckFunc(
// check.That(data.ResourceName).ExistsInAzure(r),
// check.That(data.ResourceName).Key("scope").Exists(),
// ),
// },
// data.ImportStep("schedule.0.start_date_time"),
// })
// }
func TestAccPimEligibleRoleAssignment_noExpiration(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_pim_eligible_role_assignment", "test")
r := PimEligibleRoleAssignmentResource{}

data.ResourceTest(t, r, []acceptance.TestStep{
{
Config: r.noExpirationConfig(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
check.That(data.ResourceName).Key("scope").Exists(),
),
},
data.ImportStep("schedule.0.start_date_time"),
})
}

func TestAccPimEligibleRoleAssignment_expirationByDurationHoursConfig(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_pim_eligible_role_assignment", "test")
Expand Down Expand Up @@ -192,32 +190,50 @@ resource "azuread_group" "test" {
`, data.RandomInteger, data.RandomString)
}

// func (PimEligibleRoleAssignmentResource) noExpirationConfig(data acceptance.TestData) string {
// return fmt.Sprintf(`
// data "azurerm_subscription" "primary" {}
func (PimEligibleRoleAssignmentResource) noExpirationConfig(data acceptance.TestData) string {
return fmt.Sprintf(`
data "azurerm_subscription" "primary" {}
// data "azurerm_client_config" "test" {}
data "azurerm_client_config" "test" {}
// data "azurerm_role_definition" "test" {
// name = "Disk Backup Reader"
// }
data "azurerm_role_definition" "test" {
name = "Load Test Reader"
}
// %s
resource "azurerm_role_management_policy" "test" {
scope = data.azurerm_subscription.primary.id
role_definition_id = "${data.azurerm_subscription.primary.id}${data.azurerm_role_definition.test.id}"
// resource "azurerm_pim_eligible_role_assignment" "test" {
// scope = data.azurerm_subscription.primary.id
// role_definition_id = "${data.azurerm_subscription.primary.id}${data.azurerm_role_definition.test.id}"
// principal_id = azuread_user.test.object_id
assignment {
eligible {
allow_permanent = true
}
active {
allow_permanent = true
}
}
}
%s
// justification = "No Expiration"
resource "azurerm_pim_eligible_role_assignment" "test" {
scope = data.azurerm_subscription.primary.id
role_definition_id = "${data.azurerm_subscription.primary.id}${data.azurerm_role_definition.test.id}"
principal_id = azuread_user.test.object_id
// ticket {
// number = "1"
// system = "example ticket system"
// }
// }
// `, aadUser(data))
// }
justification = "No Expiration"
ticket {
number = "1"
system = "example ticket system"
}
depends_on = [ # Wait for policy to be defined for no expiration
azurerm_role_management_policy.test
]
}
`, aadUser(data))
}

func (PimEligibleRoleAssignmentResource) expirationByDurationHoursConfig(data acceptance.TestData) string {
return fmt.Sprintf(`
Expand Down
2 changes: 2 additions & 0 deletions internal/services/authorization/registration.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type Registration struct {

var _ sdk.TypedServiceRegistrationWithAGitHubLabel = Registration{}
var _ sdk.UntypedServiceRegistrationWithAGitHubLabel = Registration{}
var _ sdk.TypedServiceRegistrationWithAGitHubLabel = Registration{}

func (r Registration) AssociatedGitHubLabel() string {
return "service/authorization"
Expand Down Expand Up @@ -55,6 +56,7 @@ func (r Registration) Resources() []sdk.Resource {
PimActiveRoleAssignmentResource{},
PimEligibleRoleAssignmentResource{},
RoleAssignmentMarketplaceResource{},
RoleManagementPolicyResource{},
}
return resources
}
Loading

0 comments on commit 125c2ed

Please sign in to comment.