diff --git a/azurerm/resource_arm_role_assignment.go b/azurerm/resource_arm_role_assignment.go index 788f0e9c2b93..3e51abe36f8f 100644 --- a/azurerm/resource_arm_role_assignment.go +++ b/azurerm/resource_arm_role_assignment.go @@ -65,6 +65,18 @@ func resourceArmRoleAssignment() *schema.Resource { Required: true, ForceNew: true, }, + + "principal_type": { + Type: schema.TypeString, + Computed: true, + }, + + "skip_service_principal_aad_check": { + Type: schema.TypeBool, + Optional: true, + ForceNew: true, + Default: false, + }, }, } } @@ -126,6 +138,12 @@ func resourceArmRoleAssignmentCreate(d *schema.ResourceData, meta interface{}) e }, } + skipPrincipalCheck := d.Get("skip_service_principal_aad_check").(bool) + + if skipPrincipalCheck { + properties.RoleAssignmentProperties.PrincipalType = authorization.ServicePrincipal + } + if err := resource.Retry(300*time.Second, retryRoleAssignmentsClient(scope, name, properties, meta)); err != nil { return err } @@ -164,6 +182,7 @@ func resourceArmRoleAssignmentRead(d *schema.ResourceData, meta interface{}) err d.Set("scope", props.Scope) d.Set("role_definition_id", props.RoleDefinitionID) d.Set("principal_id", props.PrincipalID) + d.Set("principal_type", props.PrincipalType) //allows for import when role name is used (also if the role name changes a plan will show a diff) if roleId := props.RoleDefinitionID; roleId != nil { diff --git a/azurerm/resource_arm_role_assignment_test.go b/azurerm/resource_arm_role_assignment_test.go index 4a477d507c77..a5ac2d068ed8 100644 --- a/azurerm/resource_arm_role_assignment_test.go +++ b/azurerm/resource_arm_role_assignment_test.go @@ -25,8 +25,9 @@ func TestAccAzureRMRoleAssignment(t *testing.T) { "requiresImport": testAccAzureRMRoleAssignment_requiresImport, }, "assignment": { - "sp": testAccAzureRMActiveDirectoryServicePrincipal_servicePrincipal, - "group": testAccAzureRMActiveDirectoryServicePrincipal_group, + "sp": testAccAzureRMActiveDirectoryServicePrincipal_servicePrincipal, + "spType": testAccAzureRMActiveDirectoryServicePrincipal_servicePrincipalWithType, + "group": testAccAzureRMActiveDirectoryServicePrincipal_group, }, "management": { "assign": testAccAzureRMRoleAssignment_managementGroup, @@ -65,6 +66,9 @@ func testAccAzureRMRoleAssignment_emptyName(t *testing.T) { ResourceName: resourceName, ImportState: true, ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "skip_service_principal_aad_check", + }, }, }, }) @@ -91,6 +95,9 @@ func testAccAzureRMRoleAssignment_roleName(t *testing.T) { ResourceName: resourceName, ImportState: true, ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "skip_service_principal_aad_check", + }, }, }, }) @@ -146,6 +153,9 @@ func testAccAzureRMRoleAssignment_dataActions(t *testing.T) { ResourceName: resourceName, ImportState: true, ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "skip_service_principal_aad_check", + }, }, }, }) @@ -170,6 +180,9 @@ func testAccAzureRMRoleAssignment_builtin(t *testing.T) { ResourceName: resourceName, ImportState: true, ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "skip_service_principal_aad_check", + }, }, }, }) @@ -196,12 +209,16 @@ func testAccAzureRMRoleAssignment_custom(t *testing.T) { ResourceName: resourceName, ImportState: true, ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "skip_service_principal_aad_check", + }, }, }, }) } func testAccAzureRMActiveDirectoryServicePrincipal_servicePrincipal(t *testing.T) { + resourceName := "azurerm_role_assignment.test" ri := tf.AccRandTimeInt() id := uuid.New().String() @@ -212,6 +229,26 @@ func testAccAzureRMActiveDirectoryServicePrincipal_servicePrincipal(t *testing.T Steps: []resource.TestStep{ { Config: testAccAzureRMRoleAssignment_servicePrincipal(ri, id), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMRoleAssignmentExists("azurerm_role_assignment.test"), + resource.TestCheckResourceAttr(resourceName, "principal_type", "ServicePrincipal"), + ), + }, + }, + }) +} + +func testAccAzureRMActiveDirectoryServicePrincipal_servicePrincipalWithType(t *testing.T) { + ri := tf.AccRandTimeInt() + id := uuid.New().String() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMRoleAssignmentDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMRoleAssignment_servicePrincipalWithType(ri, id), Check: resource.ComposeTestCheckFunc( testCheckAzureRMRoleAssignmentExists("azurerm_role_assignment.test"), ), @@ -441,6 +478,28 @@ resource "azurerm_role_assignment" "test" { `, rInt, roleAssignmentID) } +func testAccAzureRMRoleAssignment_servicePrincipalWithType(rInt int, roleAssignmentID string) string { + return fmt.Sprintf(` +data "azurerm_subscription" "current" {} + +resource "azuread_application" "test" { + name = "acctestspa-%d" +} + +resource "azuread_service_principal" "test" { + application_id = "${azuread_application.test.application_id}" +} + +resource "azurerm_role_assignment" "test" { + name = "%s" + scope = "${data.azurerm_subscription.current.id}" + role_definition_name = "Reader" + principal_id = "${azuread_service_principal.test.id}" + skip_service_principal_aad_check = true +} +`, rInt, roleAssignmentID) +} + func testAccAzureRMRoleAssignment_group(rInt int, roleAssignmentID string) string { return fmt.Sprintf(` data "azurerm_subscription" "current" {} @@ -465,17 +524,17 @@ data "azurerm_subscription" "primary" {} data "azurerm_client_config" "test" {} data "azurerm_role_definition" "test" { - name = "Monitoring Reader" + name = "Monitoring Reader" } resource "azurerm_management_group" "test" { - group_id = "%s" + group_id = "%s" } resource "azurerm_role_assignment" "test" { - scope = "${azurerm_management_group.test.id}" - role_definition_id = "${data.azurerm_role_definition.test.id}" - principal_id = "${data.azurerm_client_config.test.service_principal_object_id}" + scope = "${azurerm_management_group.test.id}" + role_definition_id = "${data.azurerm_role_definition.test.id}" + principal_id = "${data.azurerm_client_config.test.service_principal_object_id}" } `, groupId) } diff --git a/website/docs/r/role_assignment.html.markdown b/website/docs/r/role_assignment.html.markdown index 294a06f58935..5704a3bb901e 100644 --- a/website/docs/r/role_assignment.html.markdown +++ b/website/docs/r/role_assignment.html.markdown @@ -133,12 +133,16 @@ The following arguments are supported: ~> **NOTE:** The Principal ID is also known as the Object ID (ie not the "Application ID" for applications). +* `skip_service_principal_aad_check` - (Optional) If the `principal_id` is a newly provisioned `Service Principal` set this value to `true` to skip the `Azure Active Directory` check which may fail due to replication lag. This argument is only valid if the `principal_id` is a `Service Principal` identity. If it is not a `Service Principal` identity it will cause the role assignment to fail. Defaults to `false`. + ## Attributes Reference The following attributes are exported: * `id` - The Role Assignment ID. +* `principal_type` - The type of the `principal_id`, e.g. User, Group, Service Principal, Application, etc. + ## Import Role Assignments can be imported using the `resource id`, e.g.