Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce Identity in azurerm_function_app #1369

Merged
merged 3 commits into from
Jun 8, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions azurerm/resource_arm_function_app.go
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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"])
Expand Down Expand Up @@ -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}
}
102 changes: 102 additions & 0 deletions azurerm/resource_arm_function_app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package azurerm

import (
"fmt"
"regexp"
"strings"
"testing"

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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)
}
20 changes: 20 additions & 0 deletions website/docs/r/function_app.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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.

---
Expand All @@ -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:
Expand All @@ -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.
Expand Down