From d1b7ee455237d44ee9c57b63c09e67d05ae3b54d Mon Sep 17 00:00:00 2001 From: Tom Bamford Date: Fri, 29 May 2020 19:21:36 +0100 Subject: [PATCH 1/4] azuread_application: support optional_claims --- azuread/resource_application.go | 162 +++++++++++++++++++++++++++ azuread/resource_application_test.go | 23 ++++ 2 files changed, 185 insertions(+) diff --git a/azuread/resource_application.go b/azuread/resource_application.go index af97b61a3..2b53a62ae 100644 --- a/azuread/resource_application.go +++ b/azuread/resource_application.go @@ -20,6 +20,51 @@ import ( const resourceApplicationName = "azuread_application" +func applicationOptionalClaimSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + }, + + "source": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice( + []string{"user"}, + false, + ), + }, + "essential": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "additional_properties": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice( + []string{ + "dns_domain_and_sam_account_name", + "emit_as_roles", + "netbios_domain_and_sam_account_name", + "sam_account_name", + }, + false, + ), + }, + }, + }, + }, + } +} + func resourceApplication() *schema.Resource { return &schema.Resource{ Create: resourceApplicationCreate, @@ -154,6 +199,20 @@ func resourceApplication() *schema.Resource { }, }, + "optional_claims": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "access_token": applicationOptionalClaimSchema(), + "id_token": applicationOptionalClaimSchema(), + // TODO: enable when https://github.com/Azure/azure-sdk-for-go/issues/9714 resolved + //"saml_token": applicationOptionalClaimSchema(), + }, + }, + }, + "required_resource_access": { Type: schema.TypeSet, Optional: true, @@ -297,6 +356,7 @@ func resourceApplicationCreate(d *schema.ResourceData, meta interface{}) error { ReplyUrls: tf.ExpandStringSlicePtr(d.Get("reply_urls").(*schema.Set).List()), AvailableToOtherTenants: p.BoolI(d.Get("available_to_other_tenants")), RequiredResourceAccess: expandADApplicationRequiredResourceAccess(d), + OptionalClaims: expandADApplicationOptionalClaims(d), } if v, ok := d.GetOk("homepage"); ok { @@ -423,6 +483,10 @@ func resourceApplicationUpdate(d *schema.ResourceData, meta interface{}) error { properties.RequiredResourceAccess = expandADApplicationRequiredResourceAccess(d) } + if d.HasChange("optional_claims") { + properties.OptionalClaims = expandADApplicationOptionalClaims(d) + } + if d.HasChange("oauth2_permissions") { // if the permission already exists then it must be disabled // with no other changes before it can be edited or deleted @@ -552,6 +616,10 @@ func resourceApplicationRead(d *schema.ResourceData, meta interface{}) error { return fmt.Errorf("Error setting `required_resource_access`: %+v", err) } + if err := d.Set("optional_claims", flattenADApplicationOptionalClaims(app.OptionalClaims)); err != nil { + return fmt.Errorf("Error setting `optional_claims`: %+v", err) + } + if err := d.Set("app_role", graph.FlattenAppRoles(app.AppRoles)); err != nil { return fmt.Errorf("Error setting `app_role`: %+v", err) } @@ -677,6 +745,100 @@ func flattenADApplicationResourceAccess(in *[]graphrbac.ResourceAccess) []interf return accesses } +func expandADApplicationOptionalClaims(d *schema.ResourceData) *graphrbac.OptionalClaims { + result := graphrbac.OptionalClaims{} + + for _, raw := range d.Get("optional_claims").([]interface{}) { + optionalClaims := raw.(map[string]interface{}) + result.AccessToken = expandADApplicationOptionalClaim(optionalClaims["access_token"].([]interface{})) + result.IDToken = expandADApplicationOptionalClaim(optionalClaims["id_token"].([]interface{})) + // TODO: enable when https://github.com/Azure/azure-sdk-for-go/issues/9714 resolved + //result.SamlToken = expandADApplicationOptionalClaim(optionalClaims["saml_token"].([]interface{})) + } + return &result +} + +func expandADApplicationOptionalClaim(in []interface{}) *[]graphrbac.OptionalClaim { + optionalClaims := make([]graphrbac.OptionalClaim, 0, len(in)) + for _, optionalClaimRaw := range in { + optionalClaim := optionalClaimRaw.(map[string]interface{}) + + name := optionalClaim["name"].(string) + essential := optionalClaim["essential"].(bool) + additionalProps := make([]string, 0) + + if props := optionalClaim["additional_properties"]; props != nil { + for _, prop := range props.([]interface{}) { + additionalProps = append(additionalProps, prop.(string)) + } + } + + newClaim := graphrbac.OptionalClaim{ + Name: &name, + Essential: &essential, + AdditionalProperties: &additionalProps, + } + + if source := optionalClaim["source"].(string); source != "" { + newClaim.Source = &source + } + + optionalClaims = append(optionalClaims, newClaim) + } + + return &optionalClaims +} + +func flattenADApplicationOptionalClaims(in *graphrbac.OptionalClaims) interface{} { + optionalClaims := make(map[string]interface{}) + if claims := flattenADApplicationOptionalClaimsList(in.AccessToken); len(claims) > 0 { + optionalClaims["access_token"] = claims + } + if claims := flattenADApplicationOptionalClaimsList(in.IDToken); len(claims) > 0 { + optionalClaims["id_token"] = claims + } + // TODO: enable when https://github.com/Azure/azure-sdk-for-go/issues/9714 resolved + //if claims := flattenADApplicationOptionalClaimsList(in.SamlToken); len(claims) > 0 { + // optionalClaims["saml_token"] = claims + //} + var result []map[string]interface{} + if len(optionalClaims) == 0 { + return result + } + result = append(result, optionalClaims) + return result +} + +func flattenADApplicationOptionalClaimsList(in *[]graphrbac.OptionalClaim) []interface{} { + if in == nil { + return []interface{}{} + } + + optionalClaims := make([]interface{}, 0) + for _, claim := range *in { + optionalClaim := make(map[string]interface{}) + if claim.Name != nil { + optionalClaim["name"] = *claim.Name + } + if claim.Source != nil { + optionalClaim["source"] = *claim.Source + } + if claim.Essential != nil { + optionalClaim["essential"] = *claim.Essential + } + additionalProperties := make([]string, 0) + if props := claim.AdditionalProperties; props != nil { + for _, prop := range props.([]interface{}) { + additionalProperties = append(additionalProperties, prop.(string)) + } + } + optionalClaim["additional_properties"] = additionalProperties + optionalClaims = append(optionalClaims, optionalClaim) + } + + return optionalClaims +} + func expandADApplicationAppRoles(i interface{}) *[]graphrbac.AppRole { input := i.(*schema.Set).List() if len(input) == 0 { diff --git a/azuread/resource_application_test.go b/azuread/resource_application_test.go index 1990d12bf..db184e29e 100644 --- a/azuread/resource_application_test.go +++ b/azuread/resource_application_test.go @@ -66,6 +66,9 @@ func TestAccAzureADApplication_complete(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "identifier_uris.0", fmt.Sprintf("http://%d.hashicorptest.com/00000000-0000-0000-0000-00000000", ri)), resource.TestCheckResourceAttr(resourceName, "reply_urls.#", "1"), resource.TestCheckResourceAttr(resourceName, "group_membership_claims", "All"), + resource.TestCheckResourceAttr(resourceName, "optional_claims.#", "1"), + resource.TestCheckResourceAttr(resourceName, "optional_claims.0.access_token.#", "2"), + resource.TestCheckResourceAttr(resourceName, "optional_claims.0.id_token.#", "1"), resource.TestCheckResourceAttr(resourceName, "required_resource_access.#", "2"), resource.TestCheckResourceAttr(resourceName, "oauth2_permissions.#", "2"), resource.TestCheckResourceAttrSet(resourceName, "application_id"), @@ -117,6 +120,9 @@ func TestAccAzureADApplication_update(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "identifier_uris.0", fmt.Sprintf("http://%d.hashicorptest.com/00000000-0000-0000-0000-00000000", updatedri)), resource.TestCheckResourceAttr(resourceName, "reply_urls.#", "1"), resource.TestCheckResourceAttr(resourceName, "reply_urls.3714513888", "http://unittest.hashicorptest.com"), + resource.TestCheckResourceAttr(resourceName, "optional_claims.#", "1"), + resource.TestCheckResourceAttr(resourceName, "optional_claims.0.access_token.#", "2"), + resource.TestCheckResourceAttr(resourceName, "optional_claims.0.id_token.#", "1"), resource.TestCheckResourceAttr(resourceName, "required_resource_access.#", "2"), resource.TestCheckResourceAttr(resourceName, "oauth2_permissions.#", "2"), ), @@ -133,6 +139,7 @@ func TestAccAzureADApplication_update(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("acctest-APP-%[1]d", ri)), resource.TestCheckResourceAttr(resourceName, "identifier_uris.#", "0"), resource.TestCheckResourceAttr(resourceName, "reply_urls.#", "0"), + resource.TestCheckResourceAttr(resourceName, "optional_claims.#", "0"), ), }, { @@ -762,6 +769,22 @@ resource "azuread_application" "test" { value = "user_impersonation" } + optional_claims { + access_token { + name = "myclaim" + } + + access_token { + name = "otherclaim" + } + + id_token { + name = "userclaim" + source = "user" + essential = true + additional_properties = ["emit_as_roles"] + } + } owners = [azuread_user.test.object_id] } From c37e725bf1aa008299b24afcaf28fd88c80c40cf Mon Sep 17 00:00:00 2001 From: Tom Bamford Date: Fri, 29 May 2020 19:44:32 +0100 Subject: [PATCH 2/4] data.azuread_application: support optional_claims --- azuread/data_application.go | 18 ++++++ azuread/data_application_test.go | 5 ++ azuread/helpers/graph/application.go | 46 ++++++++++++++ azuread/resource_application.go | 95 ++++++++-------------------- 4 files changed, 94 insertions(+), 70 deletions(-) diff --git a/azuread/data_application.go b/azuread/data_application.go index 8c431c7c6..d30ce0604 100644 --- a/azuread/data_application.go +++ b/azuread/data_application.go @@ -86,6 +86,20 @@ func dataApplication() *schema.Resource { "app_roles": graph.SchemaAppRolesComputed(), + "optional_claims": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "access_token": graph.SchemaOptionalClaims(), + "id_token": graph.SchemaOptionalClaims(), + // TODO: enable when https://github.com/Azure/azure-sdk-for-go/issues/9714 resolved + //"saml_token": graph.SchemaOptionalClaims(), + }, + }, + }, + "required_resource_access": { Type: schema.TypeList, Computed: true, @@ -203,6 +217,10 @@ func dataApplicationRead(d *schema.ResourceData, meta interface{}) error { return fmt.Errorf("Error setting `required_resource_access`: %+v", err) } + if err := d.Set("optional_claims", flattenADApplicationOptionalClaims(app.OptionalClaims)); err != nil { + return fmt.Errorf("setting `optional_claims`: %+v", err) + } + if v := app.PublicClient; v != nil && *v { d.Set("type", "native") } else { diff --git a/azuread/data_application_test.go b/azuread/data_application_test.go index 345ef0402..879192c03 100644 --- a/azuread/data_application_test.go +++ b/azuread/data_application_test.go @@ -30,6 +30,7 @@ func TestAccAzureADApplicationDataSource_byObjectId(t *testing.T) { resource.TestCheckResourceAttr(dataSourceName, "homepage", fmt.Sprintf("https://acctest-APP-%d", ri)), resource.TestCheckResourceAttr(dataSourceName, "identifier_uris.#", "0"), resource.TestCheckResourceAttr(dataSourceName, "reply_urls.#", "0"), + resource.TestCheckResourceAttr(dataSourceName, "optional_claims.#", "0"), resource.TestCheckResourceAttr(dataSourceName, "required_resource_access.#", "0"), resource.TestCheckResourceAttr(dataSourceName, "type", "webapp/api"), resource.TestCheckResourceAttr(dataSourceName, "app_roles.#", "0"), @@ -65,6 +66,9 @@ func TestAccAzureADApplicationDataSource_byObjectIdComplete(t *testing.T) { resource.TestCheckResourceAttr(dataSourceName, "identifier_uris.#", "1"), resource.TestCheckResourceAttr(dataSourceName, "reply_urls.#", "1"), resource.TestCheckResourceAttr(dataSourceName, "oauth2_allow_implicit_flow", "true"), + resource.TestCheckResourceAttr(dataSourceName, "optional_claims.#", "1"), + resource.TestCheckResourceAttr(dataSourceName, "optional_claims.0.access_token.#", "2"), + resource.TestCheckResourceAttr(dataSourceName, "optional_claims.0.id_token.#", "1"), resource.TestCheckResourceAttr(dataSourceName, "required_resource_access.#", "2"), resource.TestCheckResourceAttr(dataSourceName, "group_membership_claims", "All"), resource.TestCheckResourceAttrSet(dataSourceName, "application_id"), @@ -94,6 +98,7 @@ func TestAccAzureADApplicationDataSource_byName(t *testing.T) { resource.TestCheckResourceAttr(dataSourceName, "homepage", fmt.Sprintf("https://acctest-APP-%d", ri)), resource.TestCheckResourceAttr(dataSourceName, "identifier_uris.#", "0"), resource.TestCheckResourceAttr(dataSourceName, "reply_urls.#", "0"), + resource.TestCheckResourceAttr(dataSourceName, "optional_claims.#", "0"), resource.TestCheckResourceAttr(dataSourceName, "required_resource_access.#", "0"), resource.TestCheckResourceAttr(dataSourceName, "oauth2_allow_implicit_flow", "false"), resource.TestCheckResourceAttrSet(dataSourceName, "application_id"), diff --git a/azuread/helpers/graph/application.go b/azuread/helpers/graph/application.go index 691b1e886..09910dc99 100644 --- a/azuread/helpers/graph/application.go +++ b/azuread/helpers/graph/application.go @@ -3,6 +3,7 @@ package graph import ( "context" "fmt" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" "log" "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" @@ -103,6 +104,51 @@ func SchemaOauth2PermissionsComputed() *schema.Schema { } } +func SchemaOptionalClaims() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + }, + + "source": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice( + []string{"user"}, + false, + ), + }, + "essential": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "additional_properties": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice( + []string{ + "dns_domain_and_sam_account_name", + "emit_as_roles", + "netbios_domain_and_sam_account_name", + "sam_account_name", + }, + false, + ), + }, + }, + }, + }, + } +} + func FlattenAppRoles(in *[]graphrbac.AppRole) []interface{} { if in == nil { return []interface{}{} diff --git a/azuread/resource_application.go b/azuread/resource_application.go index 2b53a62ae..8f6822311 100644 --- a/azuread/resource_application.go +++ b/azuread/resource_application.go @@ -20,51 +20,6 @@ import ( const resourceApplicationName = "azuread_application" -func applicationOptionalClaimSchema() *schema.Schema { - return &schema.Schema{ - Type: schema.TypeList, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - }, - - "source": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.StringInSlice( - []string{"user"}, - false, - ), - }, - "essential": { - Type: schema.TypeBool, - Optional: true, - Default: false, - }, - "additional_properties": { - Type: schema.TypeList, - Optional: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - ValidateFunc: validation.StringInSlice( - []string{ - "dns_domain_and_sam_account_name", - "emit_as_roles", - "netbios_domain_and_sam_account_name", - "sam_account_name", - }, - false, - ), - }, - }, - }, - }, - } -} - func resourceApplication() *schema.Resource { return &schema.Resource{ Create: resourceApplicationCreate, @@ -205,10 +160,10 @@ func resourceApplication() *schema.Resource { MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "access_token": applicationOptionalClaimSchema(), - "id_token": applicationOptionalClaimSchema(), + "access_token": graph.SchemaOptionalClaims(), + "id_token": graph.SchemaOptionalClaims(), // TODO: enable when https://github.com/Azure/azure-sdk-for-go/issues/9714 resolved - //"saml_token": applicationOptionalClaimSchema(), + //"saml_token": graph.SchemaOptionalClaims(), }, }, }, @@ -397,7 +352,7 @@ func resourceApplicationCreate(d *schema.ResourceData, meta interface{}) error { return client.Get(ctx, *app.ObjectID) }) if err != nil { - return fmt.Errorf("Error waiting for Application with ObjectId %q: %+v", *app.ObjectID, err) + return fmt.Errorf("waiting for Application with ObjectId %q: %+v", *app.ObjectID, err) } // follow suggested hack for azure-cli @@ -495,10 +450,10 @@ func resourceApplicationUpdate(d *schema.ResourceData, meta interface{}) error { resp, err := client.Get(ctx, d.Id()) if err != nil { if ar.ResponseWasNotFound(resp.Response) { - return fmt.Errorf("Error: AzureAD Application with ID %q was not found", d.Id()) + return fmt.Errorf("AzureAD Application with ID %q was not found", d.Id()) } - return fmt.Errorf("Error making Read request on AzureAD Application with ID %q: %+v", d.Id(), err) + return fmt.Errorf("making Read request on AzureAD Application with ID %q: %+v", d.Id(), err) } app = resp for _, OAuth2Permission := range *app.Oauth2Permissions { @@ -506,7 +461,7 @@ func resourceApplicationUpdate(d *schema.ResourceData, meta interface{}) error { } appProperties.Oauth2Permissions = app.Oauth2Permissions if _, err := client.Patch(ctx, d.Id(), appProperties); err != nil { - return fmt.Errorf("Error disabling OAuth2 permissions for Azure AD Application with ID %q: %+v", d.Id(), err) + return fmt.Errorf("disabling OAuth2 permissions for Azure AD Application with ID %q: %+v", d.Id(), err) } // now we can set the new state of the permission @@ -521,10 +476,10 @@ func resourceApplicationUpdate(d *schema.ResourceData, meta interface{}) error { resp, err := client.Get(ctx, d.Id()) if err != nil { if ar.ResponseWasNotFound(resp.Response) { - return fmt.Errorf("Error: AzureAD Application with ID %q was not found", d.Id()) + return fmt.Errorf("AzureAD Application with ID %q was not found", d.Id()) } - return fmt.Errorf("Error making Read request on AzureAD Application with ID %q: %+v", d.Id(), err) + return fmt.Errorf("making Read request on AzureAD Application with ID %q: %+v", d.Id(), err) } app = resp for _, appRole := range *app.AppRoles { @@ -532,7 +487,7 @@ func resourceApplicationUpdate(d *schema.ResourceData, meta interface{}) error { } appRolesProperties.AppRoles = app.AppRoles if _, err := client.Patch(ctx, d.Id(), appRolesProperties); err != nil { - return fmt.Errorf("Error disabling App Roles for Azure AD Application with ID %q: %+v", d.Id(), err) + return fmt.Errorf("disabling App Roles for Azure AD Application with ID %q: %+v", d.Id(), err) } // now we can set the new state of the app role @@ -552,12 +507,12 @@ func resourceApplicationUpdate(d *schema.ResourceData, meta interface{}) error { properties.PublicClient = p.Bool(true) properties.IdentifierUris = &[]string{} default: - return fmt.Errorf("Error paching Azure AD Application with ID %q: Unknow application type %v. Supported types are [webapp/api, native]", d.Id(), appType) + return fmt.Errorf("paching Azure AD Application with ID %q: Unknow application type %v. Supported types are [webapp/api, native]", d.Id(), appType) } } if _, err := client.Patch(ctx, d.Id(), properties); err != nil { - return fmt.Errorf("Error patching Azure AD Application with ID %q: %+v", d.Id(), err) + return fmt.Errorf("patching Azure AD Application with ID %q: %+v", d.Id(), err) } if v, ok := d.GetOkExists("owners"); ok && d.HasChange("owners") { @@ -582,7 +537,7 @@ func resourceApplicationRead(d *schema.ResourceData, meta interface{}) error { return nil } - return fmt.Errorf("Error retrieving Azure AD Application with ID %q: %+v", d.Id(), err) + return fmt.Errorf("retrieving Azure AD Application with ID %q: %+v", d.Id(), err) } d.Set("name", app.DisplayName) @@ -601,39 +556,39 @@ func resourceApplicationRead(d *schema.ResourceData, meta interface{}) error { } if err := d.Set("group_membership_claims", app.GroupMembershipClaims); err != nil { - return fmt.Errorf("Error setting `group_membership_claims`: %+v", err) + return fmt.Errorf("setting `group_membership_claims`: %+v", err) } if err := d.Set("identifier_uris", tf.FlattenStringSlicePtr(app.IdentifierUris)); err != nil { - return fmt.Errorf("Error setting `identifier_uris`: %+v", err) + return fmt.Errorf("setting `identifier_uris`: %+v", err) } if err := d.Set("reply_urls", tf.FlattenStringSlicePtr(app.ReplyUrls)); err != nil { - return fmt.Errorf("Error setting `reply_urls`: %+v", err) + return fmt.Errorf("setting `reply_urls`: %+v", err) } if err := d.Set("required_resource_access", flattenADApplicationRequiredResourceAccess(app.RequiredResourceAccess)); err != nil { - return fmt.Errorf("Error setting `required_resource_access`: %+v", err) + return fmt.Errorf("setting `required_resource_access`: %+v", err) } if err := d.Set("optional_claims", flattenADApplicationOptionalClaims(app.OptionalClaims)); err != nil { - return fmt.Errorf("Error setting `optional_claims`: %+v", err) + return fmt.Errorf("setting `optional_claims`: %+v", err) } if err := d.Set("app_role", graph.FlattenAppRoles(app.AppRoles)); err != nil { - return fmt.Errorf("Error setting `app_role`: %+v", err) + return fmt.Errorf("setting `app_role`: %+v", err) } if err := d.Set("oauth2_permissions", graph.FlattenOauth2Permissions(app.Oauth2Permissions)); err != nil { - return fmt.Errorf("Error setting `oauth2_permissions`: %+v", err) + return fmt.Errorf("setting `oauth2_permissions`: %+v", err) } owners, err := graph.ApplicationAllOwners(client, ctx, d.Id()) if err != nil { - return fmt.Errorf("Error getting owners for Application %q: %+v", *app.ObjectID, err) + return fmt.Errorf("getting owners for Application %q: %+v", *app.ObjectID, err) } if err := d.Set("owners", owners); err != nil { - return fmt.Errorf("Error setting `owners`: %+v", err) + return fmt.Errorf("setting `owners`: %+v", err) } return nil @@ -652,14 +607,14 @@ func resourceApplicationDelete(d *schema.ResourceData, meta interface{}) error { } if _, err := client.Patch(ctx, d.Id(), properties); err != nil { - return fmt.Errorf("Error patching Azure AD Application with ID %q: %+v", d.Id(), err) + return fmt.Errorf("patching Azure AD Application with ID %q: %+v", d.Id(), err) } } resp, err := client.Delete(ctx, d.Id()) if err != nil { if !ar.ResponseWasNotFound(resp) { - return fmt.Errorf("Error Deleting Azure AD Application with ID %q: %+v", d.Id(), err) + return fmt.Errorf("Deleting Azure AD Application with ID %q: %+v", d.Id(), err) } } @@ -937,7 +892,7 @@ func adApplicationSetOwnersTo(client graphrbac.ApplicationsClient, ctx context.C log.Printf("[DEBUG] Removing member with id %q from Azure AD group with id %q", ownerToDelete, id) if resp, err := client.RemoveOwner(ctx, id, ownerToDelete); err != nil { if !ar.ResponseWasNotFound(resp) { - return fmt.Errorf("Error Deleting group member %q from Azure AD Group with ID %q: %+v", ownerToDelete, id, err) + return fmt.Errorf("Deleting group member %q from Azure AD Group with ID %q: %+v", ownerToDelete, id, err) } } } From 5241902d4c6aeee7a7eb647925ac85889e349449 Mon Sep 17 00:00:00 2001 From: Tom Bamford Date: Fri, 29 May 2020 19:59:44 +0100 Subject: [PATCH 3/4] Documentation for azuread_application.optional_claims --- website/docs/d/application.html.markdown | 11 ++++++++++ website/docs/r/application.html.markdown | 28 ++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/website/docs/d/application.html.markdown b/website/docs/d/application.html.markdown index 757b90db4..5e74406b0 100644 --- a/website/docs/d/application.html.markdown +++ b/website/docs/d/application.html.markdown @@ -54,6 +54,8 @@ The following attributes are exported: * `group_membership_claims` - The `groups` claim issued in a user or OAuth 2.0 access token that the app expects. +* `optional_claims` - A collection of `access_token` or `id_token` blocks as documented below which list the optional claims configured for each token type. For more information see https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-optional-claims + * `owners` - A list of User Object IDs that are assigned ownership of the application registration. * `required_resource_access` - A collection of `required_resource_access` blocks as documented below. @@ -82,6 +84,15 @@ The following attributes are exported: --- +`access_token` and/or `id_token` blocks export the following: + +* `name` - The name of the optional claim. +* `source` - The source of the claim. If `source` is absent, the claim is a predefined optional claim. If `source` is `user`, the value of `name` is the extension property from the user object. +* `essential` - Whether the claim specified by the client is necessary to ensure a smooth authorization experience. +* `additional_properties` - List of Additional Properties of the claim. If a property exists in this list, it modifies the behaviour of the optional claim. + +--- + `oauth2_permission` block exports the following: * `id` - The unique identifier for one of the `OAuth2Permission` diff --git a/website/docs/r/application.html.markdown b/website/docs/r/application.html.markdown index c27a388c3..3b4b82127 100644 --- a/website/docs/r/application.html.markdown +++ b/website/docs/r/application.html.markdown @@ -83,6 +83,23 @@ resource "azuread_application" "example" { type = "Admin" value = "administer" } + + optional_claims { + access_token { + name = "myclaim" + } + + access_token { + name = "otherclaim" + } + + id_token { + name = "userclaim" + source = "user" + essential = true + additional_properties = ["emit_as_roles"] + } + } } ``` @@ -108,6 +125,8 @@ The following arguments are supported: * `group_membership_claims` - (Optional) Configures the `groups` claim issued in a user or OAuth 2.0 access token that the app expects. Defaults to `SecurityGroup`. Possible values are `None`, `SecurityGroup`, `DirectoryRole`, `ApplicationGroup` or `All`. +* `optional_claims` - A collection of `access_token` or `id_token` blocks as documented below which list the optional claims configured for each token type. For more information see https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-optional-claims + * `owners` - (Optional) A list of Azure AD Object IDs that will be granted ownership of the application. Defaults to the Object ID of the caller creating the application. If a list is specified the caller Object ID will no longer be included unless explicitly added to the list. * `required_resource_access` - (Optional) A collection of `required_resource_access` blocks as documented below. @@ -136,6 +155,15 @@ The following arguments are supported: --- +`access_token` and/or `id_token` blocks support the following: + +* `name` - The name of the optional claim. +* `source` - The source of the claim. If `source` is absent, the claim is a predefined optional claim. If `source` is `user`, the value of `name` is the extension property from the user object. +* `essential` - Whether the claim specified by the client is necessary to ensure a smooth authorization experience. +* `additional_properties` - List of Additional Properties of the claim. If a property exists in this list, it modifies the behaviour of the optional claim. + +--- + `app_role` supports the following: * `id` - The unique identifier of the `app_role`. From c27c95e1f9357fcddca42186f65504323339d51b Mon Sep 17 00:00:00 2001 From: Tom Bamford Date: Fri, 29 May 2020 20:44:40 +0100 Subject: [PATCH 4/4] Tidy imports --- azuread/helpers/graph/application.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azuread/helpers/graph/application.go b/azuread/helpers/graph/application.go index 09910dc99..eefbf9cbc 100644 --- a/azuread/helpers/graph/application.go +++ b/azuread/helpers/graph/application.go @@ -3,11 +3,11 @@ package graph import ( "context" "fmt" - "github.com/hashicorp/terraform-plugin-sdk/helper/validation" "log" "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" ) func SchemaAppRolesComputed() *schema.Schema {