diff --git a/azuread/resource_service_principal.go b/azuread/resource_service_principal.go index 4de4c03bf8..e845e2753a 100644 --- a/azuread/resource_service_principal.go +++ b/azuread/resource_service_principal.go @@ -21,6 +21,7 @@ func resourceServicePrincipal() *schema.Resource { return &schema.Resource{ Create: resourceServicePrincipalCreate, Read: resourceServicePrincipalRead, + Update: resourceServicePrincipalUpdate, Delete: resourceServicePrincipalDelete, Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, @@ -34,6 +35,11 @@ func resourceServicePrincipal() *schema.Resource { ValidateFunc: validate.UUID, }, + "app_role_assignment_required": { + Type: schema.TypeBool, + Optional: true, + }, + "display_name": { Type: schema.TypeString, Computed: true, @@ -71,6 +77,11 @@ func resourceServicePrincipalCreate(d *schema.ResourceData, meta interface{}) er // given there's no way to change it - we'll just default this to true AccountEnabled: p.Bool(true), } + + if v, ok := d.GetOk("app_role_assignment_required"); ok { + properties.AppRoleAssignmentRequired = p.Bool(v.(bool)) + } + if v, ok := d.GetOk("tags"); ok { properties.Tags = tf.ExpandStringSlicePtr(v.(*schema.Set).List()) } @@ -94,6 +105,23 @@ func resourceServicePrincipalCreate(d *schema.ResourceData, meta interface{}) er return resourceServicePrincipalRead(d, meta) } +func resourceServicePrincipalUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).servicePrincipalsClient + ctx := meta.(*ArmClient).StopContext + + var properties graphrbac.ServicePrincipalUpdateParameters + + if d.HasChange("app_role_assignment_required") { + properties.AppRoleAssignmentRequired = p.Bool(d.Get("app_role_assignment_required").(bool)) + } + + if _, err := client.Update(ctx, d.Id(), properties); err != nil { + return fmt.Errorf("Error patching Azure AD Service Principal with ID %q: %+v", d.Id(), err) + } + + return resourceServicePrincipalRead(d, meta) +} + func resourceServicePrincipalRead(d *schema.ResourceData, meta interface{}) error { client := meta.(*ArmClient).servicePrincipalsClient ctx := meta.(*ArmClient).StopContext @@ -113,6 +141,7 @@ func resourceServicePrincipalRead(d *schema.ResourceData, meta interface{}) erro d.Set("application_id", app.AppID) d.Set("display_name", app.DisplayName) d.Set("object_id", app.ObjectID) + d.Set("app_role_assignment_required", app.AppRoleAssignmentRequired) // tags doesn't exist as a property, so extract it if err := d.Set("tags", app.Tags); err != nil { diff --git a/azuread/resource_service_principal_test.go b/azuread/resource_service_principal_test.go index db618f770a..c9f4c4e481 100644 --- a/azuread/resource_service_principal_test.go +++ b/azuread/resource_service_principal_test.go @@ -27,6 +27,7 @@ func TestAccAzureADServicePrincipal_basic(t *testing.T) { resource.TestCheckResourceAttrSet(resourceName, "display_name"), resource.TestCheckResourceAttrSet(resourceName, "application_id"), resource.TestCheckResourceAttr(resourceName, "oauth2_permissions.#", "1"), + resource.TestCheckResourceAttr(resourceName, "app_role_assignment_required", "false"), resource.TestCheckResourceAttr(resourceName, "oauth2_permissions.0.admin_consent_description", fmt.Sprintf("Allow the application to access %s on behalf of the signed-in user.", fmt.Sprintf("acctestApp-%s", id))), resource.TestCheckResourceAttrSet(resourceName, "object_id"), ), @@ -53,6 +54,7 @@ func TestAccAzureADServicePrincipal_complete(t *testing.T) { Config: testAccADServicePrincipal_complete(id), Check: resource.ComposeTestCheckFunc( testCheckADServicePrincipalExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "app_role_assignment_required", "true"), resource.TestCheckResourceAttr(resourceName, "tags.#", "3"), resource.TestCheckResourceAttrSet(resourceName, "object_id"), ), @@ -66,6 +68,52 @@ func TestAccAzureADServicePrincipal_complete(t *testing.T) { }) } +func TestAccAzureADServicePrincipal_update(t *testing.T) { + resourceName := "azuread_service_principal.test" + id := uuid.New().String() + updatedId := uuid.New().String() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckADServicePrincipalDestroy, + Steps: []resource.TestStep{ + { + Config: testAccADServicePrincipal_basic(id), + Check: resource.ComposeTestCheckFunc( + testCheckADServicePrincipalExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "display_name"), + resource.TestCheckResourceAttrSet(resourceName, "application_id"), + resource.TestCheckResourceAttr(resourceName, "app_role_assignment_required", "false"), + ), + }, + { + Config: testAccADServicePrincipal_complete(updatedId), + Check: resource.ComposeTestCheckFunc( + testCheckADServicePrincipalExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.#", "3"), + resource.TestCheckResourceAttrSet(resourceName, "object_id"), + resource.TestCheckResourceAttr(resourceName, "app_role_assignment_required", "true"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccADServicePrincipal_basic(id), + Check: resource.ComposeTestCheckFunc( + testCheckADServicePrincipalExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "display_name"), + resource.TestCheckResourceAttrSet(resourceName, "application_id"), + resource.TestCheckResourceAttr(resourceName, "app_role_assignment_required", "false"), + ), + }, + }, + }) +} + func testCheckADServicePrincipalExists(name string) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[name] @@ -131,7 +179,8 @@ resource "azuread_application" "test" { } resource "azuread_service_principal" "test" { - application_id = "${azuread_application.test.application_id}" + application_id = "${azuread_application.test.application_id}" + app_role_assignment_required = true tags = ["test", "multiple", "CapitalS"] } diff --git a/website/docs/r/service_principal.html.markdown b/website/docs/r/service_principal.html.markdown index ed5271755f..9b761df616 100644 --- a/website/docs/r/service_principal.html.markdown +++ b/website/docs/r/service_principal.html.markdown @@ -4,14 +4,13 @@ page_title: "Azure Active Directory: azuread_service_principal" sidebar_current: "docs-azuread-resource-azuread-service-principal-x" description: |- Manages a Service Principal associated with an Application within Azure Active Directory. - --- # azuread_service_principal Manages a Service Principal associated with an Application within Azure Active Directory. --> **NOTE:** If you're authenticating using a Service Principal then it must have permissions to both `Read and write all applications` and `Sign in and read user profile` within the `Windows Azure Active Directory` API. Please see The [Granting a Service Principal permission to manage AAD](../auth/service_principal_configuration.html) for the required steps. +-> **NOTE:** If you're authenticating using a Service Principal then it must have permissions to both `Read and write all applications` and `Sign in and read user profile` within the `Windows Azure Active Directory` API. Please see The [Granting a Service Principal permission to manage AAD](../auth/service_principal_configuration.html) for the required steps. ## Example Usage @@ -26,7 +25,8 @@ resource "azuread_application" "example" { } resource "azuread_service_principal" "example" { - application_id = "${azuread_application.example.application_id}" + application_id = "${azuread_application.example.application_id}" + app_role_assignment_required = false tags = ["example", "tags", "here"] } @@ -38,6 +38,8 @@ The following arguments are supported: * `application_id` - (Required) The ID of the Azure AD Application for which to create a Service Principal. +* `app_role_assignment_required` - (Optional) Does this Service Principal require an AppRoleAssignment to a user or group before Azure AD will issue a user or access token to the application? Defaults to `false`. + * `tags` - (Optional) A list of tags to apply to the Service Principal. ## Attributes Reference @@ -52,6 +54,8 @@ The following attributes are exported: * `display_name` - The Display Name of the Azure Active Directory Application associated with this Service Principal. +* `app_role_assignment_required` - Whether this Service Principal requires an AppRoleAssignment to a user or group before Azure AD will issue a user or access token to the application. + * `oauth2_permissions` - A collection of OAuth 2.0 permissions exposed by the associated application. Each permission is covered by a `oauth2_permission` block as documented below. ---