From 2e7320f9164fc4028f5ecb63044e4fbbc59d9b55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Fri, 8 Nov 2024 17:01:24 +0100 Subject: [PATCH] azurerm_pim_active_role_assignment: add support for RBAC conditions. --- .../pim_eligible_role_assignment_resource.go | 53 +++++++++++++--- ..._eligible_role_assignment_resource_test.go | 62 +++++++++++++++++++ ...pim_eligible_role_assignment.html.markdown | 7 +++ 3 files changed, 113 insertions(+), 9 deletions(-) diff --git a/internal/services/authorization/pim_eligible_role_assignment_resource.go b/internal/services/authorization/pim_eligible_role_assignment_resource.go index 4cd7f71c0ffa..089d892cc60f 100644 --- a/internal/services/authorization/pim_eligible_role_assignment_resource.go +++ b/internal/services/authorization/pim_eligible_role_assignment_resource.go @@ -40,6 +40,8 @@ type PimEligibleRoleAssignmentModel struct { Justification string `tfschema:"justification"` TicketInfo []PimEligibleRoleAssignmentTicketInfo `tfschema:"ticket"` ScheduleInfo []PimEligibleRoleAssignmentScheduleInfo `tfschema:"schedule"` + Condition string `tfschema:"condition"` + ConditionVersion string `tfschema:"condition_version"` } type PimEligibleRoleAssignmentTicketInfo struct { @@ -114,6 +116,24 @@ func (PimEligibleRoleAssignmentResource) Arguments() map[string]*pluginsdk.Schem Description: "The justification for this eligible role assignment", }, + "condition": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + RequiredWith: []string{"condition_version"}, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "condition_version": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + RequiredWith: []string{"condition"}, + ValidateFunc: validation.StringInSlice([]string{ + "2.0", + }, false), + }, + "schedule": { Type: pluginsdk.TypeList, MaxItems: 1, @@ -285,16 +305,28 @@ func (r PimEligibleRoleAssignmentResource) Create() sdk.ResourceFunc { return err } + properties := &roleeligibilityschedulerequests.RoleEligibilityScheduleRequestProperties{ + Justification: pointer.To(config.Justification), + PrincipalId: id.PrincipalId, + RequestType: roleeligibilityschedulerequests.RequestTypeAdminAssign, + RoleDefinitionId: id.RoleDefinitionId, + Scope: pointer.To(scopeId.ID()), + ScheduleInfo: scheduleInfo, + TicketInfo: ticketInfo, + } + + condition := config.Condition + conditionVersion := config.ConditionVersion + + if condition != "" && conditionVersion != "" { + properties.Condition = pointer.To(condition) + properties.ConditionVersion = pointer.To(conditionVersion) + } else if condition != "" || conditionVersion != "" { + return fmt.Errorf("`condition` and `conditionVersion` should be both set or unset") + } + payload := roleeligibilityschedulerequests.RoleEligibilityScheduleRequest{ - Properties: &roleeligibilityschedulerequests.RoleEligibilityScheduleRequestProperties{ - Justification: pointer.To(config.Justification), - PrincipalId: id.PrincipalId, - RequestType: roleeligibilityschedulerequests.RequestTypeAdminAssign, - RoleDefinitionId: id.RoleDefinitionId, - Scope: pointer.To(scopeId.ID()), - ScheduleInfo: scheduleInfo, - TicketInfo: ticketInfo, - }, + Properties: properties, } roleEligibilityScheduleRequestName, err := uuid.GenerateUUID() @@ -396,6 +428,9 @@ func (r PimEligibleRoleAssignmentResource) Read() sdk.ResourceFunc { state.PrincipalType = string(pointer.From(request.Properties.PrincipalType)) state.RoleDefinitionId = request.Properties.RoleDefinitionId + state.Condition = pointer.From(request.Properties.Condition) + state.ConditionVersion = pointer.From(request.Properties.ConditionVersion) + if ticketInfo := request.Properties.TicketInfo; ticketInfo != nil { if len(state.TicketInfo) == 0 { state.TicketInfo = make([]PimEligibleRoleAssignmentTicketInfo, 1) diff --git a/internal/services/authorization/pim_eligible_role_assignment_resource_test.go b/internal/services/authorization/pim_eligible_role_assignment_resource_test.go index c1bff96d64fa..f0f35c62655a 100644 --- a/internal/services/authorization/pim_eligible_role_assignment_resource_test.go +++ b/internal/services/authorization/pim_eligible_role_assignment_resource_test.go @@ -121,6 +121,22 @@ func TestAccPimEligibleRoleAssignment_expirationByDate(t *testing.T) { }) } +func TestAccPimEligibleRoleAssignment_condition(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_pim_eligible_role_assignment", "test") + r := PimEligibleRoleAssignmentResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.condition(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("condition").Exists(), + check.That(data.ResourceName).Key("condition_version").HasValue("2.0"), + ), + }, + }) +} + func (r PimEligibleRoleAssignmentResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { id, err := parse.PimRoleAssignmentID(state.ID) if err != nil { @@ -412,3 +428,49 @@ resource "azurerm_pim_eligible_role_assignment" "import" { } `, r.importSetup(data)) } + +func (r PimEligibleRoleAssignmentResource) condition(data acceptance.TestData) string { + return fmt.Sprintf(` +data "azurerm_subscription" "primary" {} + +data "azurerm_client_config" "test" {} + +data "azurerm_role_definition" "test" { + name = "Storage Blob Data Contributor" +} + +/* +%[1]s +*/ + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%[2]d" + location = "%[3]s" +} + +resource "time_static" "test" {} + +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 = "fce9dbfd-1e1f-4c4a-95e7-dbe9211c191d" + + schedule { + start_date_time = time_static.test.rfc3339 + expiration { + duration_hours = 8 + } + } + + justification = "Expiration Duration Set" + + condition_version = "2.0" + condition = "((!(ActionMatches{'Microsoft.Storage/storageAccounts/blobServices/containers/blobs/read'})) OR (@Resource[Microsoft.Storage/storageAccounts/blobServices/containers:name] StringEquals 'test'))" + + ticket { + number = "1" + system = "example ticket system" + } +} +`, r.template(data), data.RandomInteger, data.Locations.Primary) +} diff --git a/website/docs/r/pim_eligible_role_assignment.html.markdown b/website/docs/r/pim_eligible_role_assignment.html.markdown index a57e2c1087b3..d0e95a8efd0b 100644 --- a/website/docs/r/pim_eligible_role_assignment.html.markdown +++ b/website/docs/r/pim_eligible_role_assignment.html.markdown @@ -98,6 +98,13 @@ The following arguments are supported: * `ticket` - (Optional) A `ticket` block as defined below. Changing this forces a new resource to be created. + +~> **NOTE:** If one of `condition` or `condition_version` is set both fields must be present. + +* `condition` - (Optional) The condition that limits the resources that the role can be assigned to. Changing this forces a new resource to be created. + +* `condition_version` - (Optional) The version of the condition. Currently accepted value is `2.0`. Changing this forces a new resource to be created. + --- An `expiration` block supports the following: