From d1780bbdf783b2b70a3f5859f6c1c8b226b82557 Mon Sep 17 00:00:00 2001 From: swupdiedoowap <65856666+swupdiedoowap@users.noreply.github.com> Date: Thu, 22 Aug 2024 08:42:54 +0200 Subject: [PATCH 1/8] Update role_management_policy_resource.go --- .../authorization/role_management_policy_resource.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/internal/services/authorization/role_management_policy_resource.go b/internal/services/authorization/role_management_policy_resource.go index cac16bfc0e89..7bb4922441c8 100644 --- a/internal/services/authorization/role_management_policy_resource.go +++ b/internal/services/authorization/role_management_policy_resource.go @@ -111,9 +111,16 @@ func (r RoleManagementPolicyResource) Arguments() map[string]*pluginsdk.Schema { Required: true, ForceNew: true, ValidateFunc: validation.Any( + // Elevated access for a global admin is needed to assign roles in this scope: + // https://docs.microsoft.com/en-us/azure/role-based-access-control/elevate-access-global-admin#azure-cli + // It seems only user account is allowed to be elevated access. + validation.StringMatch(regexp.MustCompile("/providers/Microsoft.Subscription.*"), "Subscription scope is invalid"), + + billingValidate.EnrollmentID, commonids.ValidateManagementGroupID, - commonids.ValidateResourceGroupID, commonids.ValidateSubscriptionID, + commonids.ValidateResourceGroupID, + azure.ValidateResourceID, ), }, From 7fc509eae5c37278740a3ea2f2c9e14909b7936e Mon Sep 17 00:00:00 2001 From: swupdiedoowap <65856666+swupdiedoowap@users.noreply.github.com> Date: Thu, 22 Aug 2024 08:47:28 +0200 Subject: [PATCH 2/8] Update role_management_policy_data_source.go --- .../authorization/role_management_policy_data_source.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/internal/services/authorization/role_management_policy_data_source.go b/internal/services/authorization/role_management_policy_data_source.go index 35f485cf69b9..d680aebccae8 100644 --- a/internal/services/authorization/role_management_policy_data_source.go +++ b/internal/services/authorization/role_management_policy_data_source.go @@ -109,9 +109,16 @@ func (r RoleManagementPolicyDataSource) Arguments() map[string]*pluginsdk.Schema Type: pluginsdk.TypeString, Required: true, ValidateFunc: validation.Any( + // Elevated access for a global admin is needed to assign roles in this scope: + // https://docs.microsoft.com/en-us/azure/role-based-access-control/elevate-access-global-admin#azure-cli + // It seems only user account is allowed to be elevated access. + validation.StringMatch(regexp.MustCompile("/providers/Microsoft.Subscription.*"), "Subscription scope is invalid"), + + billingValidate.EnrollmentID, commonids.ValidateManagementGroupID, - commonids.ValidateResourceGroupID, commonids.ValidateSubscriptionID, + commonids.ValidateResourceGroupID, + azure.ValidateResourceID, ), }, } From 5427fb28e3ddc21c6397b4e48449b1f9831e61d5 Mon Sep 17 00:00:00 2001 From: swupdiedoowap <65856666+swupdiedoowap@users.noreply.github.com> Date: Thu, 22 Aug 2024 09:06:01 +0200 Subject: [PATCH 3/8] Update role_management_policy_data_source.go --- .../authorization/role_management_policy_data_source.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/services/authorization/role_management_policy_data_source.go b/internal/services/authorization/role_management_policy_data_source.go index d680aebccae8..535372220c82 100644 --- a/internal/services/authorization/role_management_policy_data_source.go +++ b/internal/services/authorization/role_management_policy_data_source.go @@ -16,6 +16,9 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" + "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" + billingValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/billing/validate" ) type RoleManagementPolicyDataSource struct{} From 963582b9e8dbb0fe44d8ebc91c1614fa3566b0f1 Mon Sep 17 00:00:00 2001 From: swupdiedoowap <65856666+swupdiedoowap@users.noreply.github.com> Date: Thu, 22 Aug 2024 09:07:30 +0200 Subject: [PATCH 4/8] Update role_management_policy_resource.go --- .../services/authorization/role_management_policy_resource.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/services/authorization/role_management_policy_resource.go b/internal/services/authorization/role_management_policy_resource.go index 7bb4922441c8..e9df27baa68e 100644 --- a/internal/services/authorization/role_management_policy_resource.go +++ b/internal/services/authorization/role_management_policy_resource.go @@ -16,6 +16,8 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/services/authorization/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" + "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" + billingValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/billing/validate" ) type RoleManagementPolicyResource struct{} From b739e855b015c544097556a73ab23ad8b9bb7f23 Mon Sep 17 00:00:00 2001 From: swupdiedoowap <65856666+swupdiedoowap@users.noreply.github.com> Date: Thu, 22 Aug 2024 09:07:45 +0200 Subject: [PATCH 5/8] Update role_management_policy_data_source.go --- .../services/authorization/role_management_policy_data_source.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/services/authorization/role_management_policy_data_source.go b/internal/services/authorization/role_management_policy_data_source.go index 535372220c82..d5dac3210275 100644 --- a/internal/services/authorization/role_management_policy_data_source.go +++ b/internal/services/authorization/role_management_policy_data_source.go @@ -17,7 +17,6 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" - "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" billingValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/billing/validate" ) From cf0045475f98df3a8b4ce26edbd1324c63eff4bc Mon Sep 17 00:00:00 2001 From: Grand Lord Kevin Date: Mon, 26 Aug 2024 16:52:53 +0200 Subject: [PATCH 6/8] unified validation with other role assignments --- .../role_management_policy_data_source.go | 4 +- ...role_management_policy_data_source_test.go | 43 ++++++ .../role_management_policy_resource.go | 5 +- .../role_management_policy_resource_test.go | 138 ++++++++++++++++++ 4 files changed, 186 insertions(+), 4 deletions(-) diff --git a/internal/services/authorization/role_management_policy_data_source.go b/internal/services/authorization/role_management_policy_data_source.go index d5dac3210275..c9a6adb48cc4 100644 --- a/internal/services/authorization/role_management_policy_data_source.go +++ b/internal/services/authorization/role_management_policy_data_source.go @@ -13,11 +13,11 @@ import ( "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonids" "github.com/hashicorp/go-azure-sdk/resource-manager/authorization/2020-10-01/rolemanagementpolicies" + "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" + billingValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/billing/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" - "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" - billingValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/billing/validate" ) type RoleManagementPolicyDataSource struct{} diff --git a/internal/services/authorization/role_management_policy_data_source_test.go b/internal/services/authorization/role_management_policy_data_source_test.go index 804c2b42e919..394780897d4b 100644 --- a/internal/services/authorization/role_management_policy_data_source_test.go +++ b/internal/services/authorization/role_management_policy_data_source_test.go @@ -55,6 +55,20 @@ func TestAccRoleManagementPolicyDataSource_subscription(t *testing.T) { }) } +func TestAccRoleManagementPolicyDataSource_resource(t *testing.T) { + data := acceptance.BuildTestData(t, "data.azurerm_role_management_policy", "test") + r := RoleManagementPolicyDataSource{} + + data.DataSourceTest(t, []acceptance.TestStep{ + { + Config: r.resource(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).Key("name").Exists(), + ), + }, + }) +} + func (RoleManagementPolicyDataSource) managementGroup(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" {} @@ -116,3 +130,32 @@ data "azurerm_role_management_policy" "test" { } ` } + +func (RoleManagementPolicyDataSource) resource(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" {} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%[1]s" + location = "%[2]s" +} + +resource "azurerm_storage_account" "test" { + name = "accteststg%[1]s" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + account_tier = "Standard" + account_replication_type = "LRS" +} + +data "azurerm_role_definition" "contributor" { + name = "Contributor" + scope = azurerm_resource_group.test.id +} + +data "azurerm_role_management_policy" "test" { + role_definition_id = data.azurerm_role_definition.contributor.id + scope = azurerm_storage_account.test.id +} +`, data.RandomString, data.Locations.Primary) +} diff --git a/internal/services/authorization/role_management_policy_resource.go b/internal/services/authorization/role_management_policy_resource.go index e9df27baa68e..9a91194ec842 100644 --- a/internal/services/authorization/role_management_policy_resource.go +++ b/internal/services/authorization/role_management_policy_resource.go @@ -6,18 +6,19 @@ package authorization import ( "context" "fmt" + "regexp" "time" "github.com/hashicorp/go-azure-helpers/lang/pointer" "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonids" "github.com/hashicorp/go-azure-sdk/resource-manager/authorization/2020-10-01/rolemanagementpolicies" + "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" "github.com/hashicorp/terraform-provider-azurerm/internal/services/authorization/parse" + billingValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/billing/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" - "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" - billingValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/billing/validate" ) type RoleManagementPolicyResource struct{} diff --git a/internal/services/authorization/role_management_policy_resource_test.go b/internal/services/authorization/role_management_policy_resource_test.go index e75bff77b82e..e2b61de94acf 100644 --- a/internal/services/authorization/role_management_policy_resource_test.go +++ b/internal/services/authorization/role_management_policy_resource_test.go @@ -125,6 +125,36 @@ func TestAccRoleManagementPolicy_subscription(t *testing.T) { }) } +func TestAccRoleManagementPolicy_resource(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_role_management_policy", "test") + r := RoleManagementPolicyResource{} + + // Ignore the dangling resource post-test as the policy remains while the resource exists, or is in a pending deletion state + data.ResourceTestSkipCheckDestroyed(t, []acceptance.TestStep{ + { + Config: r.resource(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("active_assignment_rules.0.expire_after").HasValue("P30D"), + check.That(data.ResourceName).Key("eligible_assignment_rules.0.expiration_required").HasValue("false"), + check.That(data.ResourceName).Key("notification_rules.0.eligible_assignments.0.approver_notifications.0.notification_level").HasValue("All"), + ), + }, + data.ImportStep(), + { + Config: r.resourceUpdate(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("active_assignment_rules.0.expire_after").HasValue("P15D"), + check.That(data.ResourceName).Key("eligible_assignment_rules.0.expiration_required").HasValue("true"), + check.That(data.ResourceName).Key("activation_rules.0.approval_stage.0.primary_approver.0.type").HasValue("Group"), + check.That(data.ResourceName).Key("notification_rules.0.eligible_assignments.0.approver_notifications.0.notification_level").HasValue("Critical"), + ), + }, + data.ImportStep(), + }) +} + func (RoleManagementPolicyResource) Exists(ctx context.Context, clients *clients.Client, state *terraform.InstanceState) (*bool, error) { client := clients.Authorization.RoleManagementPoliciesClient @@ -451,3 +481,111 @@ resource "azurerm_role_management_policy" "test" { } `, r.resourceGroupTemplate(data), data.RandomString, requireApproval) } + +func (RoleManagementPolicyResource) resourceTemplate(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" {} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%[1]s" + location = "%[2]s" +} + +resource "azurerm_storage_account" "test" { + name = "accteststg%[1]s" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + account_tier = "Standard" + account_replication_type = "LRS" +} + +data "azurerm_role_definition" "contributor" { + name = "Contributor" + scope = azurerm_storage_account.test.id +} +`, data.RandomString, data.Locations.Primary) +} + +func (r RoleManagementPolicyResource) resource(data acceptance.TestData) string { + return fmt.Sprintf(` +%[1]s + +resource "azurerm_role_management_policy" "test" { + scope = azurerm_storage_account.test.id + role_definition_id = data.azurerm_role_definition.contributor.id + + active_assignment_rules { + expire_after = "P30D" + } + + eligible_assignment_rules { + expiration_required = false + } + + notification_rules { + eligible_assignments { + approver_notifications { + notification_level = "All" + default_recipients = false + additional_recipients = ["someone@example.com"] + } + } + } +} +`, r.resourceTemplate(data), data.RandomString) +} + +func (r RoleManagementPolicyResource) resourceUpdate(data acceptance.TestData) string { + return fmt.Sprintf(` +%[1]s + +provider "azuread" {} + +resource "azuread_group" "approver" { + display_name = "PIM Approver Test %[2]s" + mail_enabled = false + security_enabled = true +} + +resource "azurerm_role_management_policy" "test" { + scope = azurerm_storage_account.test.id + role_definition_id = data.azurerm_role_definition.contributor.id + + active_assignment_rules { + expire_after = "P15D" + } + + eligible_assignment_rules { + expiration_required = true + } + + activation_rules { + maximum_duration = "PT1H" + require_approval = true + approval_stage { + primary_approver { + object_id = azuread_group.approver.object_id + type = "Group" + } + } + } + + notification_rules { + eligible_assignments { + approver_notifications { + notification_level = "Critical" + default_recipients = false + additional_recipients = ["someone@example.com"] + } + } + eligible_activations { + assignee_notifications { + notification_level = "All" + default_recipients = true + additional_recipients = ["someone.else@example.com"] + } + } + } +} +`, r.resourceTemplate(data), data.RandomString) +} From 178c7ccb424d4fea1fa29fb097e70f8bf23ecaa7 Mon Sep 17 00:00:00 2001 From: Grand Lord Kevin Date: Tue, 27 Aug 2024 08:37:49 +0200 Subject: [PATCH 7/8] updated docs with correct input scopes --- website/docs/d/role_management_policy.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/d/role_management_policy.html.markdown b/website/docs/d/role_management_policy.html.markdown index 0b91502764d6..15584f6c0f59 100644 --- a/website/docs/d/role_management_policy.html.markdown +++ b/website/docs/d/role_management_policy.html.markdown @@ -51,7 +51,7 @@ data "azurerm_role_management_policy" "example" { ## Argument Reference * `role_definition_id` - (Required) The scoped Role Definition ID of the role for which this policy applies. -* `scope` - (Required) The scope to which this Role Management Policy applies. Can refer to a management group, a subscription or a resource group. +* `scope` - (Required) The scope to which this Role Management Policy applies. Can refer to a management group, a subscription, a resource group or a resource. ## Attributes Reference From b58a59fae809e0bd323c9dbc25e71f40d03b6a24 Mon Sep 17 00:00:00 2001 From: Grand Lord Kevin Date: Tue, 27 Aug 2024 08:47:35 +0200 Subject: [PATCH 8/8] updated docs with correct input scopes --- website/docs/r/role_management_policy.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/role_management_policy.html.markdown b/website/docs/r/role_management_policy.html.markdown index e79d070d9a54..a1439187bf72 100644 --- a/website/docs/r/role_management_policy.html.markdown +++ b/website/docs/r/role_management_policy.html.markdown @@ -119,7 +119,7 @@ resource "azurerm_role_management_policy" "example" { * `eligible_assignment_rules` - (Optional) An `eligible_assignment_rules` block as defined below. * `notification_rules` - (Optional) A `notification_rules` block as defined below. * `role_definition_id` - (Required) The scoped Role Definition ID of the role for which this policy will apply. Changing this forces a new resource to be created. -* `scope` - (Required) The scope to which this Role Management Policy will apply. Can refer to a management group, a subscription or a resource group. Changing this forces a new resource to be created. +* `scope` - (Required) The scope to which this Role Management Policy will apply. Can refer to a management group, a subscription, a resource group or a resource. Changing this forces a new resource to be created. ---