From c1c65c767b78bba2c8b050c7d9116be736434e49 Mon Sep 17 00:00:00 2001 From: Junyi Yi Date: Thu, 7 Jun 2018 18:09:15 -0700 Subject: [PATCH 1/3] Add field to --- azurerm/resource_arm_function_app.go | 61 ++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/azurerm/resource_arm_function_app.go b/azurerm/resource_arm_function_app.go index 8a3c3eb1113d..68471cb57f84 100644 --- a/azurerm/resource_arm_function_app.go +++ b/azurerm/resource_arm_function_app.go @@ -105,6 +105,33 @@ func resourceArmFunctionApp() *schema.Resource { }, }, + "identity": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Required: true, + DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + ValidateFunc: validation.StringInSlice([]string{ + string(web.SystemAssigned), + }, true), + }, + "principal_id": { + Type: schema.TypeString, + Computed: true, + }, + "tenant_id": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "tags": tagsSchema(), "default_hostname": { @@ -204,6 +231,12 @@ func resourceArmFunctionAppCreate(d *schema.ResourceData, meta interface{}) erro }, } + if v, ok := d.GetOk("identity.0.type"); ok { + siteEnvelope.Identity = &web.ManagedServiceIdentity{ + Type: web.ManagedServiceIdentityType(v.(string)), + } + } + createFuture, err := client.CreateOrUpdate(ctx, resGroup, name, siteEnvelope) if err != nil { return err @@ -263,6 +296,12 @@ func resourceArmFunctionAppUpdate(d *schema.ResourceData, meta interface{}) erro }, } + if v, ok := d.GetOk("identity.0.type"); ok { + siteEnvelope.Identity = &web.ManagedServiceIdentity{ + Type: web.ManagedServiceIdentityType(v.(string)), + } + } + future, err := client.CreateOrUpdate(ctx, resGroup, name, siteEnvelope) if err != nil { return err @@ -357,6 +396,10 @@ func resourceArmFunctionAppRead(d *schema.ResourceData, meta interface{}) error d.Set("client_affinity_enabled", props.ClientAffinityEnabled) } + if err := d.Set("identity", flattenFunctionAppIdentity(resp.Identity)); err != nil { + return err + } + appSettings := flattenAppServiceAppSettings(appSettingsResp.Properties) d.Set("storage_connection_string", appSettings["AzureWebJobsStorage"]) @@ -529,3 +572,21 @@ func flattenFunctionAppConnectionStrings(input map[string]*web.ConnStringValueTy return results } + +func flattenFunctionAppIdentity(identity *web.ManagedServiceIdentity) interface{} { + if identity == nil { + return make([]interface{}, 0) + } + + result := make(map[string]interface{}) + result["type"] = string(identity.Type) + + if identity.PrincipalID != nil { + result["principal_id"] = *identity.PrincipalID + } + if identity.TenantID != nil { + result["tenant_id"] = *identity.TenantID + } + + return []interface{}{result} +} From bbbfffea19db0bfe233614946b2e06126126de9c Mon Sep 17 00:00:00 2001 From: Junyi Yi Date: Thu, 7 Jun 2018 18:35:52 -0700 Subject: [PATCH 2/3] Add test cases for the new identity field. --- azurerm/resource_arm_function_app_test.go | 102 ++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/azurerm/resource_arm_function_app_test.go b/azurerm/resource_arm_function_app_test.go index b81f71fb371c..ed7c395b6101 100644 --- a/azurerm/resource_arm_function_app_test.go +++ b/azurerm/resource_arm_function_app_test.go @@ -2,6 +2,7 @@ package azurerm import ( "fmt" + "regexp" "strings" "testing" @@ -334,6 +335,69 @@ func TestAccAzureRMFunctionApp_consumptionPlan(t *testing.T) { }) } +func TestAccAzureRMFunctionApp_createIdentity(t *testing.T) { + resourceName := "azurerm_function_app.test" + ri := acctest.RandInt() + rs := strings.ToLower(acctest.RandString(11)) + config := testAccAzureRMFunctionApp_basicIdentity(ri, rs, testLocation()) + + uuidMatch := regexp.MustCompile("^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[8|9|aA|bB][a-fA-F0-9]{3}-[a-fA-F0-9]{12}$") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMFunctionAppDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMFunctionAppExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "identity.#", "1"), + resource.TestCheckResourceAttr(resourceName, "identity.0.type", "SystemAssigned"), + resource.TestMatchResourceAttr(resourceName, "identity.0.principal_id", uuidMatch), + resource.TestMatchResourceAttr(resourceName, "identity.0.tenant_id", uuidMatch), + ), + }, + }, + }) +} + +func TestAccAzureRMFunctionApp_updateIdentity(t *testing.T) { + resourceName := "azurerm_function_app.test" + ri := acctest.RandInt() + rs := strings.ToLower(acctest.RandString(11)) + + preConfig := testAccAzureRMFunctionApp_basic(ri, rs, testLocation()) + postConfig := testAccAzureRMFunctionApp_basicIdentity(ri, rs, testLocation()) + + uuidMatch := regexp.MustCompile("^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[8|9|aA|bB][a-fA-F0-9]{3}-[a-fA-F0-9]{12}$") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMFunctionAppDestroy, + Steps: []resource.TestStep{ + { + Config: preConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMAppServiceExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "identity.#", "0"), + ), + }, + { + Config: postConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMFunctionAppExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "identity.#", "1"), + resource.TestCheckResourceAttr(resourceName, "identity.0.type", "SystemAssigned"), + resource.TestMatchResourceAttr(resourceName, "identity.0.principal_id", uuidMatch), + resource.TestMatchResourceAttr(resourceName, "identity.0.tenant_id", uuidMatch), + ), + }, + }, + }) +} + func testCheckAzureRMFunctionAppDestroy(s *terraform.State) error { client := testAccProvider.Meta().(*ArmClient).appServicesClient @@ -850,3 +914,41 @@ resource "azurerm_function_app" "test" { } `, rInt, location, rString, rInt, rInt) } + +func testAccAzureRMFunctionApp_basicIdentity(rInt int, storage string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%[1]d" + location = "%[2]s" +} + +resource "azurerm_storage_account" "test" { + name = "acctestsa%[3]s" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + account_tier = "Standard" + account_replication_type = "LRS" +} + +resource "azurerm_app_service_plan" "test" { + name = "acctestASP-%[1]d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + sku { + tier = "Standard" + size = "S1" + } +} + +resource "azurerm_function_app" "test" { + name = "acctest-%[1]d-func" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + app_service_plan_id = "${azurerm_app_service_plan.test.id}" + storage_connection_string = "${azurerm_storage_account.test.primary_connection_string}" + + identity { + type = "SystemAssigned" + } +}`, rInt, location, storage) +} From 0f401a44c3dffdbfa83cb5199e34fb320805aeca Mon Sep 17 00:00:00 2001 From: Junyi Yi Date: Thu, 7 Jun 2018 18:43:36 -0700 Subject: [PATCH 3/3] Add documentation to the new identity field. --- website/docs/r/function_app.html.markdown | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/website/docs/r/function_app.html.markdown b/website/docs/r/function_app.html.markdown index a6f8011726f7..574d26d9c3be 100644 --- a/website/docs/r/function_app.html.markdown +++ b/website/docs/r/function_app.html.markdown @@ -111,6 +111,8 @@ The following arguments are supported: * `site_config` - (Optional) A `site_config` object as defined below. +* `identity` - (Optional) An `identity` block as defined below. + * `tags` - (Optional) A mapping of tags to assign to the resource. --- @@ -132,6 +134,13 @@ The following arguments are supported: * `websockets_enabled` - (Optional) Should WebSockets be enabled? +--- + +`identity` supports the following: + +* `type` - (Required) Specifies the identity type of the App Service. At this time the only allowed value is `SystemAssigned`. + + ## Attributes Reference The following attributes are exported: @@ -142,6 +151,17 @@ The following attributes are exported: * `outbound_ip_addresses` - A comma separated list of outbound IP addresses - such as `52.23.25.3,52.143.43.12` +* `identity` - An `identity` block as defined below, which contains the Managed Service Identity information for this App Service. + +--- + +`identity` exports the following: + +* `principal_id` - The Principal ID for the Service Principal associated with the Managed Service Identity of this App Service. + +* `tenant_id` - The Tenant ID for the Service Principal associated with the Managed Service Identity of this App Service. + + ## Import Function Apps can be imported using the `resource id`, e.g.