Skip to content

Commit

Permalink
azurerm_role_management_policy - Support for resource scope (#27205)
Browse files Browse the repository at this point in the history
* Update role_management_policy_resource.go

* Update role_management_policy_data_source.go

* Update role_management_policy_data_source.go

* Update role_management_policy_resource.go

* Update role_management_policy_data_source.go

* unified validation with other role assignments

* updated docs with correct input scopes

* updated docs with correct input scopes

---------

Co-authored-by: Grand Lord Kevin <[email protected]>
  • Loading branch information
swupdiedoowap and Grand Lord Kevin authored Sep 20, 2024
1 parent f697f04 commit 590483c
Show file tree
Hide file tree
Showing 6 changed files with 204 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ 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"
)
Expand Down Expand Up @@ -109,9 +111,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,
),
},
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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" {}
Expand Down Expand Up @@ -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)
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,17 @@ 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"
)
Expand Down Expand Up @@ -111,9 +114,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,
),
},

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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 = ["[email protected]"]
}
}
}
}
`, 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 = ["[email protected]"]
}
}
eligible_activations {
assignee_notifications {
notification_level = "All"
default_recipients = true
additional_recipients = ["[email protected]"]
}
}
}
}
`, r.resourceTemplate(data), data.RandomString)
}
2 changes: 1 addition & 1 deletion website/docs/d/role_management_policy.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion website/docs/r/role_management_policy.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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.

---

Expand Down

0 comments on commit 590483c

Please sign in to comment.