diff --git a/azurerm/internal/services/appconfiguration/app_configuration_resource.go b/azurerm/internal/services/appconfiguration/app_configuration_resource.go index 03265ec1ec21..e8d06fca31fd 100644 --- a/azurerm/internal/services/appconfiguration/app_configuration_resource.go +++ b/azurerm/internal/services/appconfiguration/app_configuration_resource.go @@ -50,6 +50,31 @@ func resourceArmAppConfiguration() *schema.Resource { "location": azure.SchemaLocation(), + "identity": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(appconfiguration.SystemAssigned), + }, false), + }, + "principal_id": { + Type: schema.TypeString, + Computed: true, + }, + "tenant_id": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + // the API changed and now returns the rg in lowercase // revert when https://github.com/Azure/azure-sdk-for-go/issues/6606 is fixed "resource_group_name": azure.SchemaResourceGroupNameDiffSuppress(), @@ -199,6 +224,8 @@ func resourceArmAppConfigurationCreate(d *schema.ResourceData, meta interface{}) Tags: tags.Expand(d.Get("tags").(map[string]interface{})), } + parameters.Identity = expandAppConfigurationIdentity(d.Get("identity").([]interface{})) + future, err := client.Create(ctx, resourceGroup, name, parameters) if err != nil { return fmt.Errorf("Error creating App Configuration %q (Resource Group %q): %+v", name, resourceGroup, err) @@ -239,6 +266,10 @@ func resourceArmAppConfigurationUpdate(d *schema.ResourceData, meta interface{}) Tags: tags.Expand(d.Get("tags").(map[string]interface{})), } + if d.HasChange("identity") { + parameters.Identity = expandAppConfigurationIdentity(d.Get("identity").([]interface{})) + } + future, err := client.Update(ctx, id.ResourceGroup, id.Name, parameters) if err != nil { return fmt.Errorf("Error updating App Configuration %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) @@ -308,6 +339,10 @@ func resourceArmAppConfigurationRead(d *schema.ResourceData, meta interface{}) e d.Set("secondary_read_key", accessKeys.secondaryReadKey) d.Set("secondary_write_key", accessKeys.secondaryWriteKey) + if err := d.Set("identity", flattenAppConfigurationIdentity(resp.Identity)); err != nil { + return fmt.Errorf("Error setting `identity`: %+v", err) + } + return tags.FlattenAndSet(d, resp.Tags) } @@ -408,3 +443,39 @@ func flattenAppConfigurationAccessKey(input appconfiguration.APIKey) []interface }, } } + +func expandAppConfigurationIdentity(identities []interface{}) *appconfiguration.ResourceIdentity { + if len(identities) == 0 { + return &appconfiguration.ResourceIdentity{ + Type: appconfiguration.None, + } + } + identity := identities[0].(map[string]interface{}) + identityType := appconfiguration.IdentityType(identity["type"].(string)) + return &appconfiguration.ResourceIdentity{ + Type: identityType, + } +} +func flattenAppConfigurationIdentity(identity *appconfiguration.ResourceIdentity) []interface{} { + if identity == nil || identity.Type == appconfiguration.None { + return []interface{}{} + } + + principalId := "" + if identity.PrincipalID != nil { + principalId = *identity.PrincipalID + } + + tenantId := "" + if identity.TenantID != nil { + tenantId = *identity.TenantID + } + + return []interface{}{ + map[string]interface{}{ + "type": string(identity.Type), + "principal_id": principalId, + "tenant_id": tenantId, + }, + } +} diff --git a/azurerm/internal/services/appconfiguration/tests/app_configuration_resource_test.go b/azurerm/internal/services/appconfiguration/tests/app_configuration_resource_test.go index ed04220e71b7..7d960e64c16f 100644 --- a/azurerm/internal/services/appconfiguration/tests/app_configuration_resource_test.go +++ b/azurerm/internal/services/appconfiguration/tests/app_configuration_resource_test.go @@ -90,6 +90,61 @@ func TestAccAppConfigurationResource_complete(t *testing.T) { }) } +func TestAccAppConfigurationResource_identity(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_app_configuration", "test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + CheckDestroy: testCheckAppConfigurationDestroy, + Steps: []resource.TestStep{ + { + Config: testAppConfigurationResource_identity(data), + Check: resource.ComposeTestCheckFunc( + testCheckAppConfigurationExists(data.ResourceName), + ), + }, + data.ImportStep(), + }, + }) +} + +func TestAccAppConfigurationResource_identityUpdated(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_app_configuration", "test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + CheckDestroy: testCheckAppConfigurationDestroy, + Steps: []resource.TestStep{ + { + Config: testAppConfigurationResource_standard(data), + Check: resource.ComposeTestCheckFunc( + testCheckAppConfigurationExists(data.ResourceName), + ), + }, + data.ImportStep(), + { + Config: testAppConfigurationResource_identity(data), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(data.ResourceName, "identity.#", "1"), + resource.TestCheckResourceAttr(data.ResourceName, "identity.0.type", "SystemAssigned"), + resource.TestCheckResourceAttrSet(data.ResourceName, "identity.0.principal_id"), + resource.TestCheckResourceAttrSet(data.ResourceName, "identity.0.tenant_id"), + ), + }, + data.ImportStep(), + { + Config: testAppConfigurationResource_standard(data), + Check: resource.ComposeTestCheckFunc( + testCheckAppConfigurationExists(data.ResourceName), + ), + }, + data.ImportStep(), + }, + }) +} + func TestAccAppConfigurationResource_update(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_app_configuration", "test") @@ -248,6 +303,34 @@ resource "azurerm_app_configuration" "test" { `, data.RandomInteger, data.Locations.Primary, data.RandomInteger) } +func testAppConfigurationResource_identity(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_app_configuration" "test" { + name = "testaccappconf%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + sku = "standard" + + identity { + type = "SystemAssigned" + } + + tags = { + environment = "development" + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +} + func testAppConfigurationResource_completeUpdated(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { diff --git a/website/docs/r/app_configuration.html.markdown b/website/docs/r/app_configuration.html.markdown index 90ea0f6c33eb..391a13c5070f 100644 --- a/website/docs/r/app_configuration.html.markdown +++ b/website/docs/r/app_configuration.html.markdown @@ -38,10 +38,18 @@ The following arguments are supported: * `sku` - (Optional) The SKU name of the the App Configuration. Possible values are `free` and `standard`. +* `identity` - (Optional) An `identity` block as defined below. + ~> **NOTE:** Azure does not allow a downgrade from `standard` to `free`. * `tags` - (Optional) A mapping of tags to assign to the resource. +--- + +An `identity` block supports the following: + +* `type` - (Required) Specifies the identity type of the App Configuration. At this time the only allowed value is `SystemAssigned`. + --- ## Attributes Reference @@ -59,6 +67,16 @@ The following attributes are exported: * `secondary_write_key` - A `secondary_write_key` block as defined below containing the secondary write access key. +* `identity` - An `identity` block as defined below. + +--- + +An `identity` block exports the following: + +* `principal_id` - The ID of the Principal (Client) in Azure Active Directory. + +* `tenant_id` - The ID of the Azure Active Directory Tenant. + --- A `primary_read_key` block exports the following: