diff --git a/azurerm/resource_arm_application_gateway.go b/azurerm/resource_arm_application_gateway.go index d9de9c34ce79..785236a2125d 100644 --- a/azurerm/resource_arm_application_gateway.go +++ b/azurerm/resource_arm_application_gateway.go @@ -49,6 +49,34 @@ func resourceArmApplicationGateway() *schema.Resource { Required: true, ForceNew: true, }, + "identity": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Optional: true, + Default: string(network.ResourceIdentityTypeUserAssigned), + ValidateFunc: validation.StringInSlice([]string{ + string(network.ResourceIdentityTypeUserAssigned), + }, false), + }, + "identity_ids": { + Type: schema.TypeList, + Required: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.NoZeroValues, + }, + }, + }, + }, + }, // Required "backend_address_pool": { @@ -1352,6 +1380,10 @@ func resourceArmApplicationGatewayCreateUpdate(d *schema.ResourceData, meta inte }, } + if _, ok := d.GetOk("identity"); ok { + gateway.Identity = expandAzureRmApplicationGatewayIdentity(d) + } + for _, backendHttpSettings := range *backendHTTPSettingsCollection { if props := backendHttpSettings.ApplicationGatewayBackendHTTPSettingsPropertiesFormat; props != nil { if props.HostName == nil || props.PickHostNameFromBackendAddress == nil { @@ -1457,6 +1489,11 @@ func resourceArmApplicationGatewayRead(d *schema.ResourceData, meta interface{}) } d.Set("zones", applicationGateway.Zones) + identity := flattenRmApplicationGatewayIdentity(applicationGateway.Identity) + if err := d.Set("identity", identity); err != nil { + return err + } + if props := applicationGateway.ApplicationGatewayPropertiesFormat; props != nil { flattenedCerts := flattenApplicationGatewayAuthenticationCertificates(props.AuthenticationCertificates, d) if setErr := d.Set("authentication_certificate", flattenedCerts); setErr != nil { @@ -1587,6 +1624,50 @@ func resourceArmApplicationGatewayDelete(d *schema.ResourceData, meta interface{ return nil } +func expandAzureRmApplicationGatewayIdentity(d *schema.ResourceData) *network.ManagedServiceIdentity { + v := d.Get("identity") + identities := v.([]interface{}) + identity := identities[0].(map[string]interface{}) + identityType := network.ResourceIdentityType(identity["type"].(string)) + + identityIds := make(map[string]*network.ManagedServiceIdentityUserAssignedIdentitiesValue) + for _, id := range identity["identity_ids"].([]interface{}) { + identityIds[id.(string)] = &network.ManagedServiceIdentityUserAssignedIdentitiesValue{} + } + + appGatewayIdentity := network.ManagedServiceIdentity{ + Type: identityType, + } + + if identityType == network.ResourceIdentityTypeUserAssigned { + appGatewayIdentity.UserAssignedIdentities = identityIds + } + + return &appGatewayIdentity +} + +func flattenRmApplicationGatewayIdentity(identity *network.ManagedServiceIdentity) []interface{} { + if identity == nil { + return make([]interface{}, 0) + } + + result := make(map[string]interface{}) + result["type"] = string(identity.Type) + if result["type"] == "userAssigned" { + result["type"] = "UserAssigned" + } + + identityIds := make([]string, 0) + if identity.UserAssignedIdentities != nil { + for key := range identity.UserAssignedIdentities { + identityIds = append(identityIds, key) + } + } + result["identity_ids"] = identityIds + + return []interface{}{result} +} + func expandApplicationGatewayAuthenticationCertificates(d *schema.ResourceData) *[]network.ApplicationGatewayAuthenticationCertificate { vs := d.Get("authentication_certificate").([]interface{}) results := make([]network.ApplicationGatewayAuthenticationCertificate, 0) diff --git a/azurerm/resource_arm_application_gateway_test.go b/azurerm/resource_arm_application_gateway_test.go index 7394ae9c8c10..a9c49ee48fe9 100644 --- a/azurerm/resource_arm_application_gateway_test.go +++ b/azurerm/resource_arm_application_gateway_test.go @@ -5,6 +5,7 @@ import ( "regexp" "testing" + "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" @@ -947,6 +948,27 @@ func TestAccAzureRMApplicationGateway_gatewayIP(t *testing.T) { }, }) } +func TestAccAzureRMApplicationGateway_UserAssignedIdentity(t *testing.T) { + resourceName := "azurerm_application_gateway.test" + ri := tf.AccRandTimeInt() + rs := acctest.RandString(14) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMApplicationGatewayDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMApplicationGateway_UserDefinedIdentity(ri, testLocation(), rs), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMApplicationGatewayExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "identity.0.type", "UserAssigned"), + resource.TestCheckResourceAttr(resourceName, "identity.0.identity_ids.#", "1"), + ), + }, + }, + }) +} func testCheckAzureRMApplicationGatewayExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { @@ -1076,6 +1098,96 @@ resource "azurerm_application_gateway" "test" { `, template, rInt) } +func testAccAzureRMApplicationGateway_UserDefinedIdentity(rInt int, location string, rString string) string { + template := testAccAzureRMApplicationGateway_template(rInt, location) + return fmt.Sprintf(` +%s + +# since these variables are re-used - a locals block makes this more maintainable +locals { + backend_address_pool_name = "${azurerm_virtual_network.test.name}-beap" + frontend_port_name = "${azurerm_virtual_network.test.name}-feport" + frontend_ip_configuration_name = "${azurerm_virtual_network.test.name}-feip" + http_setting_name = "${azurerm_virtual_network.test.name}-be-htst" + listener_name = "${azurerm_virtual_network.test.name}-httplstn" + request_routing_rule_name = "${azurerm_virtual_network.test.name}-rqrt" +} + +resource "azurerm_user_assigned_identity" "test" { + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + + name = "acctest%s" +} + +resource "azurerm_public_ip" "test_standard" { + name = "acctest-pubip-%d-standard" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "Standard" + allocation_method = "Static" +} + +resource "azurerm_application_gateway" "test" { + name = "acctestag-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + + sku { + name = "Standard_v2" + tier = "Standard_v2" + capacity = 1 + } + + identity { + identity_ids = ["${azurerm_user_assigned_identity.test.id}"] + } + + gateway_ip_configuration { + name = "my-gateway-ip-configuration" + subnet_id = "${azurerm_subnet.test.id}" + } + + frontend_port { + name = "${local.frontend_port_name}" + port = 80 + } + + frontend_ip_configuration { + name = "${local.frontend_ip_configuration_name}" + public_ip_address_id = "${azurerm_public_ip.test_standard.id}" + } + + backend_address_pool { + name = "${local.backend_address_pool_name}" + } + + backend_http_settings { + name = "${local.http_setting_name}" + cookie_based_affinity = "Disabled" + port = 80 + protocol = "Http" + request_timeout = 1 + } + + http_listener { + name = "${local.listener_name}" + frontend_ip_configuration_name = "${local.frontend_ip_configuration_name}" + frontend_port_name = "${local.frontend_port_name}" + protocol = "Http" + } + + request_routing_rule { + name = "${local.request_routing_rule_name}" + rule_type = "Basic" + http_listener_name = "${local.listener_name}" + backend_address_pool_name = "${local.backend_address_pool_name}" + backend_http_settings_name = "${local.http_setting_name}" + } +} +`, template, rString, rInt, rInt) +} + func testAccAzureRMApplicationGateway_zones(rInt int, location string) string { template := testAccAzureRMApplicationGateway_template(rInt, location) return fmt.Sprintf(` diff --git a/website/docs/r/application_gateway.html.markdown b/website/docs/r/application_gateway.html.markdown index dea5786e8fdd..7f4dd07fb13e 100644 --- a/website/docs/r/application_gateway.html.markdown +++ b/website/docs/r/application_gateway.html.markdown @@ -135,6 +135,8 @@ The following arguments are supported: * `http_listener` - (Required) One or more `http_listener` blocks as defined below. +* `identity` - (Optional) A `identity` block. + * `request_routing_rule` - (Required) One or more `request_routing_rule` blocks as defined below. * `sku` - (Required) A `sku` block as defined below. @@ -291,6 +293,14 @@ A `http_listener` block supports the following: --- +A `identity` block supports the following: + +* `type` - (Optional) The Managed Service Identity Type of this Application Gateway. The only possible value is `UserAssigned`. Defaults to `UserAssigned`. + +* `identity_ids` - (Required) Specifies a list with a single user managed identity id to be assigned to the Application Gateway. + +--- + A `match` block supports the following: * `body` - (Optional) A snippet from the Response Body which must be present in the Response. Defaults to `*`.