From 0b2658a862576383c8e863ededfc668bf805ec64 Mon Sep 17 00:00:00 2001 From: Russ Freeman Date: Wed, 6 Feb 2019 00:01:45 +0000 Subject: [PATCH 01/24] WIP initial schema --- azurerm/resource_arm_application_gateway.go | 63 +++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/azurerm/resource_arm_application_gateway.go b/azurerm/resource_arm_application_gateway.go index 178979603940..2eb0684d35b8 100644 --- a/azurerm/resource_arm_application_gateway.go +++ b/azurerm/resource_arm_application_gateway.go @@ -444,6 +444,69 @@ func resourceArmApplicationGateway() *schema.Resource { Type: schema.TypeString, Computed: true, }, + + "redirection": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "redirection_type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + "Permanent", + "Temporary", + "Found", + "See Other", + }, true), + }, + + "target_type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + "Listener", + "External Site", + }, true), + }, + + "target_listener": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "listener_id": { + Type: schema.TypeString, + Required: true, + }, + + "include_path": { + Type: schema.TypeBool, + Optional: true, + }, + }, + }, + }, + + "target_external_site_url": { + Type: schema.TypeString, + Optional: true, + }, + + "include_query_string": { + Type: schema.TypeBool, + Optional: true, + }, + + "include_path": { + Type: schema.TypeBool, + Optional: true, + }, + }, + }, + }, }, }, }, From 188b3acc4cb87ee33901857f1f9361d0781644f2 Mon Sep 17 00:00:00 2001 From: rustyf Date: Mon, 18 Feb 2019 00:03:52 +0000 Subject: [PATCH 02/24] Fleshed out implementation and adjusted schema to align with azure API docs --- azurerm/resource_arm_application_gateway.go | 264 +++++++++++++++----- 1 file changed, 206 insertions(+), 58 deletions(-) diff --git a/azurerm/resource_arm_application_gateway.go b/azurerm/resource_arm_application_gateway.go index 2eb0684d35b8..c274eba7e7e5 100644 --- a/azurerm/resource_arm_application_gateway.go +++ b/azurerm/resource_arm_application_gateway.go @@ -2,8 +2,6 @@ package azurerm import ( "fmt" - "log" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-08-01/network" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/validation" @@ -12,6 +10,7 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" + "log" ) func resourceArmApplicationGateway() *schema.Resource { @@ -444,68 +443,49 @@ func resourceArmApplicationGateway() *schema.Resource { Type: schema.TypeString, Computed: true, }, + }, + }, + }, - "redirection": { - Type: schema.TypeList, - Optional: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "redirection_type": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice([]string{ - "Permanent", - "Temporary", - "Found", - "See Other", - }, true), - }, + "redirect_configuration": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + }, - "target_type": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice([]string{ - "Listener", - "External Site", - }, true), - }, + "redirect_type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + "Permanent", + "Temporary", + "Found", + "SeeOther", + }, true), + }, - "target_listener": { - Type: schema.TypeList, - Optional: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "listener_id": { - Type: schema.TypeString, - Required: true, - }, - - "include_path": { - Type: schema.TypeBool, - Optional: true, - }, - }, - }, - }, + "target_listener_name": { + Type: schema.TypeString, + Required: true, + }, - "target_external_site_url": { - Type: schema.TypeString, - Optional: true, - }, + "target_url": { + Type: schema.TypeString, + Optional: true, + }, - "include_query_string": { - Type: schema.TypeBool, - Optional: true, - }, + "include_path": { + Type: schema.TypeBool, + Optional: true, + }, - "include_path": { - Type: schema.TypeBool, - Optional: true, - }, - }, - }, + "include_query_string": { + Type: schema.TypeBool, + Optional: true, }, }, }, @@ -744,6 +724,11 @@ func resourceArmApplicationGateway() *schema.Resource { Required: true, }, + "default_redirect_configuration_name": { + Type: schema.TypeString, + Optional: true, + }, + "path_rule": { Type: schema.TypeList, Required: true, @@ -782,6 +767,11 @@ func resourceArmApplicationGateway() *schema.Resource { Computed: true, }, + "redirect_configuration_id": { + Type: schema.TypeString, + Computed: true, + }, + "id": { Type: schema.TypeString, Computed: true, @@ -800,6 +790,11 @@ func resourceArmApplicationGateway() *schema.Resource { Computed: true, }, + "default_redirect_configuration_id": { + Type: schema.TypeString, + Computed: true, + }, + "id": { Type: schema.TypeString, Computed: true, @@ -924,6 +919,7 @@ func resourceArmApplicationGatewayCreateUpdate(d *schema.ResourceData, meta inte httpListeners := expandApplicationGatewayHTTPListeners(d, gatewayID) probes := expandApplicationGatewayProbes(d) requestRoutingRules := expandApplicationGatewayRequestRoutingRules(d, gatewayID) + redirectConfigurations := expandApplicationGatewayRedirectConfigurations(d, gatewayID) sku := expandApplicationGatewaySku(d) sslCertificates := expandApplicationGatewaySslCertificates(d) sslPolicy := expandApplicationGatewaySslPolicy(d) @@ -945,6 +941,7 @@ func resourceArmApplicationGatewayCreateUpdate(d *schema.ResourceData, meta inte HTTPListeners: httpListeners, Probes: probes, RequestRoutingRules: requestRoutingRules, + RedirectConfigurations: redirectConfigurations, Sku: sku, SslCertificates: sslCertificates, SslPolicy: sslPolicy, @@ -1077,6 +1074,14 @@ func resourceArmApplicationGatewayRead(d *schema.ResourceData, meta interface{}) return fmt.Errorf("Error setting `request_routing_rule`: %+v", setErr) } + redirectConfigurations, err := flattenApplicationGatewayRedirectConfigurations(props.RedirectConfigurations) + if err != nil { + return fmt.Errorf("Error flattening `redirect configuration`: %+v", err) + } + if setErr := d.Set("redirect_configuration", redirectConfigurations); setErr != nil { + return fmt.Errorf("Error setting `redirect configuration`: %+v", setErr) + } + if setErr := d.Set("sku", flattenApplicationGatewaySku(props.Sku)); setErr != nil { return fmt.Errorf("Error setting `sku`: %+v", setErr) } @@ -1913,6 +1918,13 @@ func expandApplicationGatewayRequestRoutingRules(d *schema.ResourceData, gateway } } + if redirectConfigName := v["redirect_config_name"].(string); redirectConfigName != "" { + redirectConfigID := fmt.Sprintf("%s/redirectConfigurations/%s", gatewayID, redirectConfigName) + rule.ApplicationGatewayRequestRoutingRulePropertiesFormat.RedirectConfiguration = &network.SubResource{ + ID: utils.String(redirectConfigID), + } + } + results = append(results, rule) } @@ -1988,6 +2000,110 @@ func flattenApplicationGatewayRequestRoutingRules(input *[]network.ApplicationGa } } + if redirect := props.RedirectConfiguration; redirect != nil { + if redirect.ID != nil { + redirectId, err := parseAzureResourceID(*redirect.ID) + if err != nil { + return nil, err + } + redirectName := redirectId.Path["redirectConfigurations"] + output["redirect_configuration_name"] = redirectName + output["redirect_configuration_id"] = *redirect.ID + } + } + + results = append(results, output) + } + } + + return results, nil +} + +func expandApplicationGatewayRedirectConfigurations(d *schema.ResourceData, gatewayID string) *[]network.ApplicationGatewayRedirectConfiguration { + + vs := d.Get("redirect_configuration").([]interface{}) + results := make([]network.ApplicationGatewayRedirectConfiguration, 0) + + for _, raw := range vs { + v := raw.(map[string]interface{}) + + name := v["name"].(string) + redirectType := v["redirect_type"].(string) + + targetListenerName := v["target_listener_name"].(string) + targetListenerID := fmt.Sprintf("%s/httpListeners/%s", gatewayID, targetListenerName) + + targetUrl := v["target_url"].(string) + + includePath := v["include_path"].(bool) + includeQueryString := v["include_query_string"].(bool) + + output := network.ApplicationGatewayRedirectConfiguration{ + Name: utils.String(name), + ApplicationGatewayRedirectConfigurationPropertiesFormat: &network.ApplicationGatewayRedirectConfigurationPropertiesFormat{ + RedirectType: network.ApplicationGatewayRedirectType(redirectType), + + TargetListener: &network.SubResource{ + ID: utils.String(targetListenerID), + }, + TargetURL: utils.String(targetUrl), + + IncludePath: utils.Bool(includePath), + IncludeQueryString: utils.Bool(includeQueryString), + }, + } + + results = append(results, output) + } + + return &results +} + +func flattenApplicationGatewayRedirectConfigurations(input *[]network.ApplicationGatewayRedirectConfiguration) ([]interface{}, error) { + results := make([]interface{}, 0) + if input == nil { + return results, nil + } + + for _, config := range *input { + if props := config.ApplicationGatewayRedirectConfigurationPropertiesFormat; props != nil { + + output := map[string]interface{}{ + "redirect_type": string(props.RedirectType), + } + + if config.ID != nil { + output["id"] = *config.ID + } + + if config.Name != nil { + output["name"] = *config.Name + } + + if listener := props.TargetListener; listener != nil { + if listener.ID != nil { + listenerId, err := parseAzureResourceID(*listener.ID) + if err != nil { + return nil, err + } + targetListenerName := listenerId.Path["httpListeners"] + output["target_listener_name"] = targetListenerName + output["target_listener_id"] = *listener.ID + } + } + + if config.TargetURL != nil { + output["target_url"] = *config.TargetURL + } + + if config.IncludePath != nil { + output["include_path"] = *config.IncludePath + } + + if config.IncludeQueryString != nil { + output["include_query_string"] = *config.IncludeQueryString + } + results = append(results, output) } } @@ -2112,9 +2228,11 @@ func expandApplicationGatewayURLPathMaps(d *schema.ResourceData, gatewayID strin name := v["name"].(string) defaultBackendAddressPoolName := v["default_backend_address_pool_name"].(string) defaultBackendHTTPSettingsName := v["default_backend_http_settings_name"].(string) + defaultRedirectConfigurationName := v["default_redirect_configuration_name"].(string) defaultBackendAddressPoolID := fmt.Sprintf("%s/backendAddressPools/%s", gatewayID, defaultBackendAddressPoolName) defaultBackendHTTPSettingsID := fmt.Sprintf("%s/backendHttpSettingsCollection/%s", gatewayID, defaultBackendHTTPSettingsName) + defaultRedirectConfigurationID := fmt.Sprintf("%s/redirectConfigurations/%s", gatewayID, defaultRedirectConfigurationName) pathRules := make([]network.ApplicationGatewayPathRule, 0) for _, ruleConfig := range v["path_rule"].([]interface{}) { @@ -2148,6 +2266,13 @@ func expandApplicationGatewayURLPathMaps(d *schema.ResourceData, gatewayID strin } } + if redirectConfigurationName := ruleConfigMap["redirect_configuration_name"].(string); redirectConfigurationName != "" { + redirectConfigurationID := fmt.Sprintf("%s/redirectConfigurations/%s", gatewayID, redirectConfigurationName) + rule.ApplicationGatewayPathRulePropertiesFormat.RedirectConfiguration = &network.SubResource{ + ID: utils.String(redirectConfigurationID), + } + } + pathRules = append(pathRules, rule) } @@ -2160,6 +2285,9 @@ func expandApplicationGatewayURLPathMaps(d *schema.ResourceData, gatewayID strin DefaultBackendHTTPSettings: &network.SubResource{ ID: utils.String(defaultBackendHTTPSettingsID), }, + DefaultRedirectConfiguration: &network.SubResource{ + ID: utils.String(defaultRedirectConfigurationID), + }, PathRules: &pathRules, }, } @@ -2208,6 +2336,16 @@ func flattenApplicationGatewayURLPathMaps(input *[]network.ApplicationGatewayURL output["default_backend_http_settings_id"] = *settings.ID } + if redirect := props.DefaultRedirectConfiguration; redirect != nil && redirect.ID != nil { + settingsId, err := parseAzureResourceID(*redirect.ID) + if err != nil { + return nil, err + } + redirectConfigurationName := settingsId.Path["redirectConfigurations"] + output["default_redirect_configuration_name"] = redirectConfigurationName + output["default_redirect_configuration_id"] = *redirect.ID + } + pathRules := make([]interface{}, 0) if rules := props.PathRules; rules != nil { for _, rule := range *rules { @@ -2242,6 +2380,16 @@ func flattenApplicationGatewayURLPathMaps(input *[]network.ApplicationGatewayURL ruleOutput["backend_http_settings_id"] = *backend.ID } + if redirect := ruleProps.RedirectConfiguration; redirect != nil && redirect.ID != nil { + redirectId, err := parseAzureResourceID(*redirect.ID) + if err != nil { + return nil, err + } + redirectConfigurationName := redirectId.Path["redirectConfigurations"] + ruleOutput["redirect_configuration_name"] = redirectConfigurationName + ruleOutput["redirect_configuration_id"] = *redirect.ID + } + pathOutputs := make([]interface{}, 0) if paths := ruleProps.Paths; paths != nil { for _, rulePath := range *paths { From 7f16cdc7a99f2b5c0e75218164fb6510ddc10331 Mon Sep 17 00:00:00 2001 From: Russell Freeman Date: Mon, 18 Feb 2019 12:59:45 +0000 Subject: [PATCH 03/24] Documentation draft --- .../docs/r/application_gateway.html.markdown | 36 ++++++++++++++++--- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/website/docs/r/application_gateway.html.markdown b/website/docs/r/application_gateway.html.markdown index 8530d78d9d6a..53e905d81010 100644 --- a/website/docs/r/application_gateway.html.markdown +++ b/website/docs/r/application_gateway.html.markdown @@ -54,6 +54,7 @@ locals { 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" + redirect_configuration_name = "${azurerm_virtual_network.test.name}-rdrcfg" } resource "azurerm_application_gateway" "network" { @@ -102,11 +103,11 @@ resource "azurerm_application_gateway" "network" { } 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}" + 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}" } } ``` @@ -155,6 +156,8 @@ The following arguments are supported: * `custom_error_configuration` - (Optional) One or more `custom_error_configuration` blocks as defined below. +* `redirect_configuration` - (Optional) A `redirect_configuration` block as defined below. + --- A `authentication_certificate` block supports the following: @@ -369,6 +372,22 @@ A `custom_error_configuration` block supports the following: * `custom_error_page_url` - (Required) Error page URL of the application gateway customer error. +--- + +A `redirect_configuration` block supports the following: + +* `name` - (Required) Unique name of the redirect configuration block + +* `redirect_type` - (Required) The type of redirect. Possible values are `Permanent`, `Temporary`, `Found` and `SeeOther` + +* `target_listener_name` - (Optional) The name of the listener to redirect to. This parameter is mutually exclusive with `target_url`. + +* `target_url` - (Optional) The Url to redirect the request to. This parameter is mutually exclusive with `target_listener_name`. + +* `include_path` - (Optional) Whether or not to include the path in the redirected Url. Defaults to `false` + +* `include_query_string` - (Optional) Whether or not to include the query string in the redirected Url. Default to `false` + ## Attributes Reference The following attributes are exported: @@ -401,6 +420,7 @@ The following attributes are exported: * `custom_error_configuration` - A list of `custom_error_configuration` blocks as defined below. +* `redirect_configuration` - A list of `redirect_configuration` blocks as defined below. --- A `authentication_certificate` block exports the following: @@ -513,6 +533,12 @@ A `custom_error_configuration` block exports the following: * `id` - The ID of the Custom Error Configuration. +--- + +A `redirect_configuration` block exports the following: + +* `id` - The ID of the Redirect Configuration. + ## Import Application Gateway's can be imported using the `resource id`, e.g. From a74ff577de1d51f7551637623311dde341eb32ff Mon Sep 17 00:00:00 2001 From: Russell Freeman Date: Mon, 18 Feb 2019 15:01:32 +0000 Subject: [PATCH 04/24] Test WIP --- .../resource_arm_application_gateway_test.go | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/azurerm/resource_arm_application_gateway_test.go b/azurerm/resource_arm_application_gateway_test.go index 65c08c476e58..73e3a9c7aef0 100644 --- a/azurerm/resource_arm_application_gateway_test.go +++ b/azurerm/resource_arm_application_gateway_test.go @@ -844,6 +844,101 @@ resource "azurerm_application_gateway" "test" { `, template, rInt) } +func testAccAzureRMApplicationGateway_httpListenerRedirect(rInt int, location 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_port_name2 = "${azurerm_virtual_network.test.name}-feport2" + 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" + target_listener_name = "${azurerm_virtual_network.test.name}-trgthttplstn" + request_routing_rule_name = "${azurerm_virtual_network.test.name}-rqrt" + path_rule_name = "${azurerm_virtual_network.test.name}-pathrule1" + url_path_map_name = "${azurerm_virtual_network.test.name}-urlpath1" + redirect_configuration_name = "${azurerm_virtual_network.test.name}-Port80To8888Redirect" +} + +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_Small" + tier = "Standard" + capacity = 2 + } + + gateway_ip_configuration { + name = "my-gateway-ip-configuration" + subnet_id = "${azurerm_subnet.test.id}" + } + + frontend_port { + name = "${local.frontend_port_name}" + port = 80 + } + + frontend_port { + name = "${local.frontend_port_name2}" + port = 8888 + } + + frontend_ip_configuration { + name = "${local.frontend_ip_configuration_name}" + public_ip_address_id = "${azurerm_public_ip.test.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" + } + + http_listener { + name = "${local.target_listener_name}" + frontend_ip_configuration_name = "${local.frontend_ip_configuration_name}" + frontend_port_name = "${local.frontend_port_name2}" + protocol = "Http" + } + + request_routing_rule { + name = "${local.request_routing_rule_name}" + rule_type = "Basic" + http_listener_name = "${local.listener_name}" + redirect_configuration_name = "${local.redirect_configuration_name}" + } + + redirect_configuration { + name = "${local.redirect_configuration_name}" + redirect_type = "Permanent" + target_listener_name = "${local.listener_name2}" + include_path = true + include_query_string = false + } +} +`, template, rInt) +} + func testAccAzureRMApplicationGateway_probes(rInt int, location string) string { template := testAccAzureRMApplicationGateway_template(rInt, location) return fmt.Sprintf(` From b299e85d6d89805c9e4a75d8a122258d5c8906a9 Mon Sep 17 00:00:00 2001 From: rustyf Date: Mon, 18 Feb 2019 22:12:44 +0000 Subject: [PATCH 05/24] Ensure we can name redirect from routing rule --- azurerm/resource_arm_application_gateway.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/azurerm/resource_arm_application_gateway.go b/azurerm/resource_arm_application_gateway.go index c274eba7e7e5..8664724a446e 100644 --- a/azurerm/resource_arm_application_gateway.go +++ b/azurerm/resource_arm_application_gateway.go @@ -419,6 +419,11 @@ func resourceArmApplicationGateway() *schema.Resource { Optional: true, }, + "redirect_configuration_name": { + Type: schema.TypeString, + Optional: true, + }, + "backend_address_pool_id": { Type: schema.TypeString, Computed: true, @@ -443,6 +448,11 @@ func resourceArmApplicationGateway() *schema.Resource { Type: schema.TypeString, Computed: true, }, + + "redirect_configuration_id": { + Type: schema.TypeString, + Computed: true, + }, }, }, }, @@ -2104,6 +2114,8 @@ func flattenApplicationGatewayRedirectConfigurations(input *[]network.Applicatio output["include_query_string"] = *config.IncludeQueryString } + // TODO: Do we need/want to return the collections of referencing []SubResource ? + results = append(results, output) } } From 2979ffa61be3ed12442924434b7c82324538fe09 Mon Sep 17 00:00:00 2001 From: rustyf Date: Mon, 18 Feb 2019 22:13:49 +0000 Subject: [PATCH 06/24] First stab at test for target httpListener scenario --- .../resource_arm_application_gateway_test.go | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/azurerm/resource_arm_application_gateway_test.go b/azurerm/resource_arm_application_gateway_test.go index 73e3a9c7aef0..ce4ecefd584d 100644 --- a/azurerm/resource_arm_application_gateway_test.go +++ b/azurerm/resource_arm_application_gateway_test.go @@ -162,6 +162,33 @@ func TestAccAzureRMApplicationGateway_pathBasedRouting(t *testing.T) { }) } +func TestAccAzureRMApplicationGateway_httpListenerRedirect(t *testing.T) { + resourceName := "azurerm_application_gateway.test" + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMApplicationGatewayDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMApplicationGateway_httpListenerRedirect(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMApplicationGatewayExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "redirect_type", "Basic"), + resource.TestCheckResourceAttr(resourceName, "include_path", "true"), + resource.TestCheckResourceAttr(resourceName, "include_query_string", "false"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccAzureRMApplicationGateway_customErrorConfigurations(t *testing.T) { resourceName := "azurerm_application_gateway.test" ri := tf.AccRandTimeInt() @@ -930,7 +957,7 @@ resource "azurerm_application_gateway" "test" { redirect_configuration { name = "${local.redirect_configuration_name}" - redirect_type = "Permanent" + redirect_type = "Temporary" target_listener_name = "${local.listener_name2}" include_path = true include_query_string = false From ddbf0f4371ef4bf6ea139808dd2ed3bc82aa5aa9 Mon Sep 17 00:00:00 2001 From: rustyf Date: Wed, 20 Feb 2019 22:41:03 +0000 Subject: [PATCH 07/24] Missing computed ID properties (finally understand how they work) --- azurerm/resource_arm_application_gateway.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/azurerm/resource_arm_application_gateway.go b/azurerm/resource_arm_application_gateway.go index 8664724a446e..1ccc757da98f 100644 --- a/azurerm/resource_arm_application_gateway.go +++ b/azurerm/resource_arm_application_gateway.go @@ -497,6 +497,16 @@ func resourceArmApplicationGateway() *schema.Resource { Type: schema.TypeBool, Optional: true, }, + + "id": { + Type: schema.TypeString, + Computed: true, + }, + + "target_listener_id": { + Type: schema.TypeString, + Computed: true, + }, }, }, }, @@ -1928,7 +1938,7 @@ func expandApplicationGatewayRequestRoutingRules(d *schema.ResourceData, gateway } } - if redirectConfigName := v["redirect_config_name"].(string); redirectConfigName != "" { + if redirectConfigName := v["redirect_configuration_name"].(string); redirectConfigName != "" { redirectConfigID := fmt.Sprintf("%s/redirectConfigurations/%s", gatewayID, redirectConfigName) rule.ApplicationGatewayRequestRoutingRulePropertiesFormat.RedirectConfiguration = &network.SubResource{ ID: utils.String(redirectConfigID), From 1ff625c9ef83816182c05b6d84c830240cc812f4 Mon Sep 17 00:00:00 2001 From: rustyf Date: Wed, 20 Feb 2019 22:42:12 +0000 Subject: [PATCH 08/24] Mis-spelt variable --- azurerm/resource_arm_application_gateway_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azurerm/resource_arm_application_gateway_test.go b/azurerm/resource_arm_application_gateway_test.go index ce4ecefd584d..c16c4d354aee 100644 --- a/azurerm/resource_arm_application_gateway_test.go +++ b/azurerm/resource_arm_application_gateway_test.go @@ -958,7 +958,7 @@ resource "azurerm_application_gateway" "test" { redirect_configuration { name = "${local.redirect_configuration_name}" redirect_type = "Temporary" - target_listener_name = "${local.listener_name2}" + target_listener_name = "${local.target_listener_name}" include_path = true include_query_string = false } From a9ca44db1080bd6ef2f1a2220a0d2e7618580b8a Mon Sep 17 00:00:00 2001 From: rustyf Date: Sun, 24 Feb 2019 21:44:21 +0000 Subject: [PATCH 09/24] Use Go Azure SDK constants --- azurerm/resource_arm_application_gateway.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/azurerm/resource_arm_application_gateway.go b/azurerm/resource_arm_application_gateway.go index 1ccc757da98f..25dcdf9f1335 100644 --- a/azurerm/resource_arm_application_gateway.go +++ b/azurerm/resource_arm_application_gateway.go @@ -471,10 +471,10 @@ func resourceArmApplicationGateway() *schema.Resource { Type: schema.TypeString, Required: true, ValidateFunc: validation.StringInSlice([]string{ - "Permanent", - "Temporary", - "Found", - "SeeOther", + string(network.Permanent), + string(network.Temporary), + string(network.Found), + string(network.SeeOther), }, true), }, From 91abb6e048bea4afb4b60e1df83da9ceed2be483 Mon Sep 17 00:00:00 2001 From: rustyf Date: Sun, 24 Feb 2019 22:13:58 +0000 Subject: [PATCH 10/24] Declare some mutual exclusions using ConflictsWith... --- azurerm/resource_arm_application_gateway.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/azurerm/resource_arm_application_gateway.go b/azurerm/resource_arm_application_gateway.go index 25dcdf9f1335..1b8bf1b06684 100644 --- a/azurerm/resource_arm_application_gateway.go +++ b/azurerm/resource_arm_application_gateway.go @@ -407,11 +407,13 @@ func resourceArmApplicationGateway() *schema.Resource { "backend_address_pool_name": { Type: schema.TypeString, Optional: true, + ConflictsWith: []string{"redirect_configuration_name"}, }, "backend_http_settings_name": { Type: schema.TypeString, Optional: true, + ConflictsWith: []string{"redirect_configuration_name"}, }, "url_path_map_name": { @@ -422,6 +424,9 @@ func resourceArmApplicationGateway() *schema.Resource { "redirect_configuration_name": { Type: schema.TypeString, Optional: true, + ConflictsWith: []string{ + "backend_address_pool_name", + "backend_http_settings_name"}, }, "backend_address_pool_id": { @@ -481,16 +486,19 @@ func resourceArmApplicationGateway() *schema.Resource { "target_listener_name": { Type: schema.TypeString, Required: true, + ConflictsWith: []string{"target_url"}, }, "target_url": { Type: schema.TypeString, Optional: true, + ConflictsWith: []string{"target_listener_name"}, }, "include_path": { Type: schema.TypeBool, Optional: true, + ConflictsWith: []string{"target_url"}, }, "include_query_string": { From cbdd8357530acf39553993573451a598a7e89ffa Mon Sep 17 00:00:00 2001 From: rustyf Date: Wed, 27 Feb 2019 23:01:59 +0000 Subject: [PATCH 11/24] Ensure http_listener optional now (in case of redirection) Remove conflicts (not usable for nested resources) Add validation in expansion funcs --- azurerm/resource_arm_application_gateway.go | 72 +++++++++++++++------ 1 file changed, 53 insertions(+), 19 deletions(-) diff --git a/azurerm/resource_arm_application_gateway.go b/azurerm/resource_arm_application_gateway.go index 1b8bf1b06684..c23786220a1f 100644 --- a/azurerm/resource_arm_application_gateway.go +++ b/azurerm/resource_arm_application_gateway.go @@ -401,19 +401,17 @@ func resourceArmApplicationGateway() *schema.Resource { "http_listener_name": { Type: schema.TypeString, - Required: true, + Optional: true, }, "backend_address_pool_name": { Type: schema.TypeString, Optional: true, - ConflictsWith: []string{"redirect_configuration_name"}, }, "backend_http_settings_name": { Type: schema.TypeString, Optional: true, - ConflictsWith: []string{"redirect_configuration_name"}, }, "url_path_map_name": { @@ -424,9 +422,6 @@ func resourceArmApplicationGateway() *schema.Resource { "redirect_configuration_name": { Type: schema.TypeString, Optional: true, - ConflictsWith: []string{ - "backend_address_pool_name", - "backend_http_settings_name"}, }, "backend_address_pool_id": { @@ -485,20 +480,17 @@ func resourceArmApplicationGateway() *schema.Resource { "target_listener_name": { Type: schema.TypeString, - Required: true, - ConflictsWith: []string{"target_url"}, + Optional: true, }, "target_url": { Type: schema.TypeString, Optional: true, - ConflictsWith: []string{"target_listener_name"}, }, "include_path": { Type: schema.TypeBool, Optional: true, - ConflictsWith: []string{"target_url"}, }, "include_query_string": { @@ -777,12 +769,17 @@ func resourceArmApplicationGateway() *schema.Resource { "backend_address_pool_name": { Type: schema.TypeString, - Required: true, + Optional: true, }, "backend_http_settings_name": { Type: schema.TypeString, - Required: true, + Optional: true, + }, + + "redirect_configuration_name": { + Type: schema.TypeString, + Optional: true, }, "backend_address_pool_id": { @@ -1912,19 +1909,21 @@ func expandApplicationGatewayRequestRoutingRules(d *schema.ResourceData, gateway name := v["name"].(string) ruleType := v["rule_type"].(string) - httpListenerName := v["http_listener_name"].(string) - httpListenerID := fmt.Sprintf("%s/httpListeners/%s", gatewayID, httpListenerName) rule := network.ApplicationGatewayRequestRoutingRule{ Name: utils.String(name), ApplicationGatewayRequestRoutingRulePropertiesFormat: &network.ApplicationGatewayRequestRoutingRulePropertiesFormat{ RuleType: network.ApplicationGatewayRequestRoutingRuleType(ruleType), - HTTPListener: &network.SubResource{ - ID: utils.String(httpListenerID), - }, }, } + if httpListenerName := v["http_listener_name"].(string); httpListenerName != "" { + httpListenerID := fmt.Sprintf("%s/httpListeners/%s", gatewayID, httpListenerName) + rule.ApplicationGatewayRequestRoutingRulePropertiesFormat.HTTPListener = &network.SubResource{ + ID: utils.String(httpListenerID), + } + } + if backendAddressPoolName := v["backend_address_pool_name"].(string); backendAddressPoolName != "" { backendAddressPoolID := fmt.Sprintf("%s/backendAddressPools/%s", gatewayID, backendAddressPoolName) rule.ApplicationGatewayRequestRoutingRulePropertiesFormat.BackendAddressPool = &network.SubResource{ @@ -1968,6 +1967,22 @@ func flattenApplicationGatewayRequestRoutingRules(input *[]network.ApplicationGa for _, config := range *input { if props := config.ApplicationGatewayRequestRoutingRulePropertiesFormat; props != nil { + if props.HTTPListener == nil && props.RedirectConfiguration == nil { + return nil, fmt.Errorf("[ERROR] Specify either `redirect_configuration_name` or `http_listener_name`") + } + + if props.HTTPListener != nil && props.RedirectConfiguration != nil { + return nil, fmt.Errorf("[ERROR] Conflict between `http_listener_name` and `redirect_configuration_name` (targer listener not applicable when redirection specified)") + } + + if props.BackendAddressPool != nil && props.RedirectConfiguration != nil { + return nil, fmt.Errorf("[ERROR] Conflict between `backend_address_pool_name` and `redirect_configuration_name` (back-end pool not applicable when redirection specified)") + } + + if props.BackendHTTPSettings != nil && props.RedirectConfiguration != nil { + return nil, fmt.Errorf("[ERROR] Conflict between `backend_http_settings_name` and `redirect_configuration_name` (back-end settings not applicable when redirection specified)") + } + output := map[string]interface{}{ "rule_type": string(props.RuleType), } @@ -2096,6 +2111,17 @@ func flattenApplicationGatewayRedirectConfigurations(input *[]network.Applicatio for _, config := range *input { if props := config.ApplicationGatewayRedirectConfigurationPropertiesFormat; props != nil { + if config.TargetListener == nil && config.TargetURL == nil { + return nil, fmt.Errorf("[ERROR] Specify either `target_listener_name` or `target_url`") + } + if props.TargetListener != nil && config.TargetURL != nil { + return nil, fmt.Errorf("[ERROR] Conflict between `target_listener_name` and `target_url` (redirection is either to URL or target listener)") + } + + if config.TargetURL != nil && config.IncludePath != nil { + return nil, fmt.Errorf("[ERROR] `include_path` is not a valid option when `target_url` is specified") + } + output := map[string]interface{}{ "redirect_type": string(props.RedirectType), } @@ -2132,8 +2158,6 @@ func flattenApplicationGatewayRedirectConfigurations(input *[]network.Applicatio output["include_query_string"] = *config.IncludeQueryString } - // TODO: Do we need/want to return the collections of referencing []SubResource ? - results = append(results, output) } } @@ -2389,7 +2413,17 @@ func flattenApplicationGatewayURLPathMaps(input *[]network.ApplicationGatewayURL ruleOutput["name"] = *rule.Name } + // TODO: Note below: ruleProps versus props - bug? if ruleProps := rule.ApplicationGatewayPathRulePropertiesFormat; props != nil { + + if ruleProps.BackendAddressPool != nil && ruleProps.RedirectConfiguration != nil { + return nil, fmt.Errorf("[ERROR] Conflict between `backend_address_pool_name` and `redirect_configuration_name` (back-end pool not applicable when redirection specified)") + } + + if ruleProps.BackendHTTPSettings != nil && ruleProps.RedirectConfiguration != nil { + return nil, fmt.Errorf("[ERROR] Conflict between `backend_http_settings_name` and `redirect_configuration_name` (back-end settings not applicable when redirection specified)") + } + if pool := ruleProps.BackendAddressPool; pool != nil && pool.ID != nil { poolId, err := parseAzureResourceID(*pool.ID) if err != nil { From db7aef2e454e4ad6a540f010988f4d4d1a52974f Mon Sep 17 00:00:00 2001 From: rustyf Date: Wed, 27 Feb 2019 23:36:40 +0000 Subject: [PATCH 12/24] Correction - HTTPListener required on routing rule --- azurerm/resource_arm_application_gateway.go | 22 ++++++--------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/azurerm/resource_arm_application_gateway.go b/azurerm/resource_arm_application_gateway.go index c23786220a1f..67897dcf71e1 100644 --- a/azurerm/resource_arm_application_gateway.go +++ b/azurerm/resource_arm_application_gateway.go @@ -401,7 +401,7 @@ func resourceArmApplicationGateway() *schema.Resource { "http_listener_name": { Type: schema.TypeString, - Optional: true, + Required: true, }, "backend_address_pool_name": { @@ -1909,21 +1909,19 @@ func expandApplicationGatewayRequestRoutingRules(d *schema.ResourceData, gateway name := v["name"].(string) ruleType := v["rule_type"].(string) + httpListenerName := v["http_listener_name"].(string) + httpListenerID := fmt.Sprintf("%s/httpListeners/%s", gatewayID, httpListenerName) rule := network.ApplicationGatewayRequestRoutingRule{ Name: utils.String(name), ApplicationGatewayRequestRoutingRulePropertiesFormat: &network.ApplicationGatewayRequestRoutingRulePropertiesFormat{ RuleType: network.ApplicationGatewayRequestRoutingRuleType(ruleType), + HTTPListener: &network.SubResource{ + ID: utils.String(httpListenerID), + }, }, } - if httpListenerName := v["http_listener_name"].(string); httpListenerName != "" { - httpListenerID := fmt.Sprintf("%s/httpListeners/%s", gatewayID, httpListenerName) - rule.ApplicationGatewayRequestRoutingRulePropertiesFormat.HTTPListener = &network.SubResource{ - ID: utils.String(httpListenerID), - } - } - if backendAddressPoolName := v["backend_address_pool_name"].(string); backendAddressPoolName != "" { backendAddressPoolID := fmt.Sprintf("%s/backendAddressPools/%s", gatewayID, backendAddressPoolName) rule.ApplicationGatewayRequestRoutingRulePropertiesFormat.BackendAddressPool = &network.SubResource{ @@ -1967,14 +1965,6 @@ func flattenApplicationGatewayRequestRoutingRules(input *[]network.ApplicationGa for _, config := range *input { if props := config.ApplicationGatewayRequestRoutingRulePropertiesFormat; props != nil { - if props.HTTPListener == nil && props.RedirectConfiguration == nil { - return nil, fmt.Errorf("[ERROR] Specify either `redirect_configuration_name` or `http_listener_name`") - } - - if props.HTTPListener != nil && props.RedirectConfiguration != nil { - return nil, fmt.Errorf("[ERROR] Conflict between `http_listener_name` and `redirect_configuration_name` (targer listener not applicable when redirection specified)") - } - if props.BackendAddressPool != nil && props.RedirectConfiguration != nil { return nil, fmt.Errorf("[ERROR] Conflict between `backend_address_pool_name` and `redirect_configuration_name` (back-end pool not applicable when redirection specified)") } From 2fc242e714dc76e06f76e3306e7862a27eb6d8c5 Mon Sep 17 00:00:00 2001 From: rustyf Date: Wed, 27 Feb 2019 23:48:46 +0000 Subject: [PATCH 13/24] tests for basic and path-based routing rules with redirection in place (with target listener and target URL mix) (not run yet - local environment issues) --- .../resource_arm_application_gateway_test.go | 165 +++++++++++++++++- 1 file changed, 162 insertions(+), 3 deletions(-) diff --git a/azurerm/resource_arm_application_gateway_test.go b/azurerm/resource_arm_application_gateway_test.go index c16c4d354aee..37315dfe9cda 100644 --- a/azurerm/resource_arm_application_gateway_test.go +++ b/azurerm/resource_arm_application_gateway_test.go @@ -175,9 +175,44 @@ func TestAccAzureRMApplicationGateway_httpListenerRedirect(t *testing.T) { Config: testAccAzureRMApplicationGateway_httpListenerRedirect(ri, testLocation()), Check: resource.ComposeTestCheckFunc( testCheckAzureRMApplicationGatewayExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "redirect_type", "Basic"), - resource.TestCheckResourceAttr(resourceName, "include_path", "true"), - resource.TestCheckResourceAttr(resourceName, "include_query_string", "false"), + resource.TestCheckResourceAttrSet(resourceName, "redirect_configuration.0.name"), + resource.TestCheckResourceAttr(resourceName, "redirect_configuration.0.redirect_type", "Temporary"), + resource.TestCheckResourceAttrSet(resourceName, "redirect_configuration.0.target_listener_name"), + resource.TestCheckResourceAttr(resourceName, "redirect_configuration.0.include_path", "true"), + resource.TestCheckResourceAttr(resourceName, "redirect_configuration.0.include_query_string", "false"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMApplicationGateway_pathBasedRoutingRedirect(t *testing.T) { + resourceName := "azurerm_application_gateway.test" + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMApplicationGatewayDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMApplicationGateway_pathBasedRoutingRedirect(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMApplicationGatewayExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "redirect_configuration.0.name"), + resource.TestCheckResourceAttr(resourceName, "redirect_configuration.0.redirect_type", "Found"), + resource.TestCheckResourceAttr(resourceName, "redirect_configuration.0.target_url", "http://www/example.com"), + resource.TestCheckResourceAttr(resourceName, "redirect_configuration.0.include_query_string", "true"), + resource.TestCheckResourceAttrSet(resourceName, "redirect_configuration.1.name"), + resource.TestCheckResourceAttr(resourceName, "redirect_configuration.1.redirect_type", "Permanent"), + resource.TestCheckResourceAttrSet(resourceName, "redirect_configuration.1.target_listener_name"), + resource.TestCheckResourceAttr(resourceName, "redirect_configuration.1.include_path", "false"), + resource.TestCheckResourceAttr(resourceName, "redirect_configuration.1.include_query_string", "false"), ), }, { @@ -966,6 +1001,130 @@ resource "azurerm_application_gateway" "test" { `, template, rInt) } +func testAccAzureRMApplicationGateway_pathBasedRoutingRedirect(rInt int, location 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" + target_listener_name = "${azurerm_virtual_network.test.name}-trgthttplstn" + request_routing_rule_name = "${azurerm_virtual_network.test.name}-rqrt" + path_rule_name = "${azurerm_virtual_network.test.name}-pathrule1" + path_rule_name2 = "${azurerm_virtual_network.test.name}-pathrule2" + url_path_map_name = "${azurerm_virtual_network.test.name}-urlpath1" + redirect_configuration_name = "${azurerm_virtual_network.test.name}-PathRedirect" + redirect_configuration_name2 = "${azurerm_virtual_network.test.name}-PathRedirect2" + target_url = "http://www.example.com" +} + +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_Small" + tier = "Standard" + capacity = 2 + } + + 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.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" + } + + http_listener { + name = "${local.target_listener_name}" + frontend_ip_configuration_name = "${local.frontend_ip_configuration_name}" + frontend_port_name = "${local.frontend_port_name2}" + protocol = "Http" + } + + request_routing_rule { + name = "${local.request_routing_rule_name}" + rule_type = "PathBasedRouting" + url_path_map_name = "${local.url_path_map_name}" + http_listener_name = "${local.listener_name}" + } + + url_path_map { + name = "${local.url_path_map_name}" + default_backend_address_pool_name = "${local.backend_address_pool_name}" + default_backend_http_settings_name = "${local.http_setting_name}" + + path_rule { + name = "${local.path_rule_name}" + redirect_configuration_name = "${local.redirect_configuration_name}" + + paths = [ + "/test", + ] + } + + path_rule { + name = "${local.path_rule_name2}" + redirect_configuration_name = "${local.redirect_configuration_name2}" + + paths = [ + "/test2", + ] + } + + } + + redirect_configuration { + name = "${local.redirect_configuration_name}" + redirect_type = "Found" + target_url = "${local.target_url}" + include_query_string = true + } + + redirect_configuration { + name = "${local.redirect_configuration_name2}" + redirect_type = "Permanent" + target_listener_name = "${local.target_listener_name}" + include_path = false + include_query_string = false + } +} +`, template, rInt) +} + func testAccAzureRMApplicationGateway_probes(rInt int, location string) string { template := testAccAzureRMApplicationGateway_template(rInt, location) return fmt.Sprintf(` From 5d18d1ed8e5b62b5fb79c484248d04fd303b0e72 Mon Sep 17 00:00:00 2001 From: rustyf Date: Thu, 28 Feb 2019 00:00:00 +0000 Subject: [PATCH 14/24] Validate for no empty strings --- azurerm/resource_arm_application_gateway.go | 30 ++++++++++++--------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/azurerm/resource_arm_application_gateway.go b/azurerm/resource_arm_application_gateway.go index 67897dcf71e1..8af5a080184d 100644 --- a/azurerm/resource_arm_application_gateway.go +++ b/azurerm/resource_arm_application_gateway.go @@ -420,8 +420,9 @@ func resourceArmApplicationGateway() *schema.Resource { }, "redirect_configuration_name": { - Type: schema.TypeString, - Optional: true, + Type: schema.TypeString, + Optional: true, + ValidateFunc: validate.NoEmptyStrings, }, "backend_address_pool_id": { @@ -463,8 +464,9 @@ func resourceArmApplicationGateway() *schema.Resource { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "name": { - Type: schema.TypeString, - Required: true, + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, }, "redirect_type": { @@ -479,13 +481,15 @@ func resourceArmApplicationGateway() *schema.Resource { }, "target_listener_name": { - Type: schema.TypeString, - Optional: true, + Type: schema.TypeString, + Optional: true, + ValidateFunc: validate.NoEmptyStrings, }, "target_url": { - Type: schema.TypeString, - Optional: true, + Type: schema.TypeString, + Optional: true, + ValidateFunc: validate.NoEmptyStrings, }, "include_path": { @@ -745,8 +749,9 @@ func resourceArmApplicationGateway() *schema.Resource { }, "default_redirect_configuration_name": { - Type: schema.TypeString, - Optional: true, + Type: schema.TypeString, + Optional: true, + ValidateFunc: validate.NoEmptyStrings, }, "path_rule": { @@ -778,8 +783,9 @@ func resourceArmApplicationGateway() *schema.Resource { }, "redirect_configuration_name": { - Type: schema.TypeString, - Optional: true, + Type: schema.TypeString, + Optional: true, + ValidateFunc: validate.NoEmptyStrings, }, "backend_address_pool_id": { From 32cdb8941c42d246ea1e469001050881c69c7958 Mon Sep 17 00:00:00 2001 From: rustyf Date: Sun, 3 Mar 2019 16:51:45 +0000 Subject: [PATCH 15/24] Introduce NilOrEmpty() validation to correctly check strings, i.e. TargetURL is fully specified. Not sure yet what to do with mutually-exclusive SubResource * references. --- azurerm/helpers/validate/strings.go | 4 ++++ azurerm/resource_arm_application_gateway.go | 8 ++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/azurerm/helpers/validate/strings.go b/azurerm/helpers/validate/strings.go index 1dec1bd2e07f..7bd744512338 100644 --- a/azurerm/helpers/validate/strings.go +++ b/azurerm/helpers/validate/strings.go @@ -18,3 +18,7 @@ func NoEmptyStrings(i interface{}, k string) ([]string, []error) { return nil, nil } + +func NilOrEmpty(s *string) bool { + return s == nil || *s == "" +} diff --git a/azurerm/resource_arm_application_gateway.go b/azurerm/resource_arm_application_gateway.go index 8af5a080184d..2aa9869b4c67 100644 --- a/azurerm/resource_arm_application_gateway.go +++ b/azurerm/resource_arm_application_gateway.go @@ -477,7 +477,7 @@ func resourceArmApplicationGateway() *schema.Resource { string(network.Temporary), string(network.Found), string(network.SeeOther), - }, true), + }, false), }, "target_listener_name": { @@ -2107,14 +2107,14 @@ func flattenApplicationGatewayRedirectConfigurations(input *[]network.Applicatio for _, config := range *input { if props := config.ApplicationGatewayRedirectConfigurationPropertiesFormat; props != nil { - if config.TargetListener == nil && config.TargetURL == nil { + if config.TargetListener == nil && validate.NilOrEmpty(config.TargetURL) { return nil, fmt.Errorf("[ERROR] Specify either `target_listener_name` or `target_url`") } - if props.TargetListener != nil && config.TargetURL != nil { + if props.TargetListener != nil && !validate.NilOrEmpty(config.TargetURL) { return nil, fmt.Errorf("[ERROR] Conflict between `target_listener_name` and `target_url` (redirection is either to URL or target listener)") } - if config.TargetURL != nil && config.IncludePath != nil { + if !validate.NilOrEmpty(config.TargetURL) && config.IncludePath != nil { return nil, fmt.Errorf("[ERROR] `include_path` is not a valid option when `target_url` is specified") } From be131cfeb4c7f7317bade10d0f7f9b7f396ce6a7 Mon Sep 17 00:00:00 2001 From: rustyf Date: Sun, 3 Mar 2019 16:55:07 +0000 Subject: [PATCH 16/24] Fixed bug where wrong local was referenced. --- azurerm/resource_arm_application_gateway.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/azurerm/resource_arm_application_gateway.go b/azurerm/resource_arm_application_gateway.go index 2aa9869b4c67..e6b6e03574f9 100644 --- a/azurerm/resource_arm_application_gateway.go +++ b/azurerm/resource_arm_application_gateway.go @@ -2409,8 +2409,7 @@ func flattenApplicationGatewayURLPathMaps(input *[]network.ApplicationGatewayURL ruleOutput["name"] = *rule.Name } - // TODO: Note below: ruleProps versus props - bug? - if ruleProps := rule.ApplicationGatewayPathRulePropertiesFormat; props != nil { + if ruleProps := rule.ApplicationGatewayPathRulePropertiesFormat; ruleProps != nil { if ruleProps.BackendAddressPool != nil && ruleProps.RedirectConfiguration != nil { return nil, fmt.Errorf("[ERROR] Conflict between `backend_address_pool_name` and `redirect_configuration_name` (back-end pool not applicable when redirection specified)") From 81b3acded66221535f5a6d62e5ef75f5357c844e Mon Sep 17 00:00:00 2001 From: rustyf Date: Sun, 3 Mar 2019 21:43:16 +0000 Subject: [PATCH 17/24] Re-word to be consistent with other docs --- website/docs/r/application_gateway.html.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/docs/r/application_gateway.html.markdown b/website/docs/r/application_gateway.html.markdown index 53e905d81010..f73758b9343a 100644 --- a/website/docs/r/application_gateway.html.markdown +++ b/website/docs/r/application_gateway.html.markdown @@ -380,9 +380,9 @@ A `redirect_configuration` block supports the following: * `redirect_type` - (Required) The type of redirect. Possible values are `Permanent`, `Temporary`, `Found` and `SeeOther` -* `target_listener_name` - (Optional) The name of the listener to redirect to. This parameter is mutually exclusive with `target_url`. +* `target_listener_name` - (Optional) The name of the listener to redirect to. This field cannot be set when `target_url` is set. -* `target_url` - (Optional) The Url to redirect the request to. This parameter is mutually exclusive with `target_listener_name`. +* `target_url` - (Optional) The Url to redirect the request to. This field cannot be set when `target_listener_name` is set. * `include_path` - (Optional) Whether or not to include the path in the redirected Url. Defaults to `false` From 28ec52740f6b8f2576d13d337e917ee5f378aa79 Mon Sep 17 00:00:00 2001 From: rustyf Date: Mon, 4 Mar 2019 22:02:37 +0000 Subject: [PATCH 18/24] Ensuring we properly check sub resources --- azurerm/resource_arm_application_gateway.go | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/azurerm/resource_arm_application_gateway.go b/azurerm/resource_arm_application_gateway.go index e6b6e03574f9..032809087a56 100644 --- a/azurerm/resource_arm_application_gateway.go +++ b/azurerm/resource_arm_application_gateway.go @@ -1971,11 +1971,11 @@ func flattenApplicationGatewayRequestRoutingRules(input *[]network.ApplicationGa for _, config := range *input { if props := config.ApplicationGatewayRequestRoutingRulePropertiesFormat; props != nil { - if props.BackendAddressPool != nil && props.RedirectConfiguration != nil { + if applicationGatewayHasSubResource(props.BackendAddressPool) && applicationGatewayHasSubResource(props.RedirectConfiguration) { return nil, fmt.Errorf("[ERROR] Conflict between `backend_address_pool_name` and `redirect_configuration_name` (back-end pool not applicable when redirection specified)") } - if props.BackendHTTPSettings != nil && props.RedirectConfiguration != nil { + if applicationGatewayHasSubResource(props.BackendHTTPSettings) && applicationGatewayHasSubResource(props.RedirectConfiguration) { return nil, fmt.Errorf("[ERROR] Conflict between `backend_http_settings_name` and `redirect_configuration_name` (back-end settings not applicable when redirection specified)") } @@ -2058,6 +2058,10 @@ func flattenApplicationGatewayRequestRoutingRules(input *[]network.ApplicationGa return results, nil } +func applicationGatewayHasSubResource(subResource *network.SubResource) bool { + return subResource != nil && subResource.ID != nil && *subResource.ID != "" +} + func expandApplicationGatewayRedirectConfigurations(d *schema.ResourceData, gatewayID string) *[]network.ApplicationGatewayRedirectConfiguration { vs := d.Get("redirect_configuration").([]interface{}) @@ -2107,15 +2111,15 @@ func flattenApplicationGatewayRedirectConfigurations(input *[]network.Applicatio for _, config := range *input { if props := config.ApplicationGatewayRedirectConfigurationPropertiesFormat; props != nil { - if config.TargetListener == nil && validate.NilOrEmpty(config.TargetURL) { - return nil, fmt.Errorf("[ERROR] Specify either `target_listener_name` or `target_url`") + if !applicationGatewayHasSubResource(config.TargetListener) && validate.NilOrEmpty(config.TargetURL) { + return nil, fmt.Errorf("[ERROR] Set either `target_listener_name` or `target_url`") } - if props.TargetListener != nil && !validate.NilOrEmpty(config.TargetURL) { + if applicationGatewayHasSubResource(config.TargetListener) && !validate.NilOrEmpty(config.TargetURL) { return nil, fmt.Errorf("[ERROR] Conflict between `target_listener_name` and `target_url` (redirection is either to URL or target listener)") } if !validate.NilOrEmpty(config.TargetURL) && config.IncludePath != nil { - return nil, fmt.Errorf("[ERROR] `include_path` is not a valid option when `target_url` is specified") + return nil, fmt.Errorf("[ERROR] `include_path` is not a valid option when `target_url` is set") } output := map[string]interface{}{ From 2cf117622b412caac8798c39ed3148dfd477e182 Mon Sep 17 00:00:00 2001 From: rustyf Date: Mon, 4 Mar 2019 23:38:33 +0000 Subject: [PATCH 19/24] Roll-out further use of resource reference check --- azurerm/resource_arm_application_gateway.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/azurerm/resource_arm_application_gateway.go b/azurerm/resource_arm_application_gateway.go index 032809087a56..2ef5643e5e5d 100644 --- a/azurerm/resource_arm_application_gateway.go +++ b/azurerm/resource_arm_application_gateway.go @@ -2415,11 +2415,11 @@ func flattenApplicationGatewayURLPathMaps(input *[]network.ApplicationGatewayURL if ruleProps := rule.ApplicationGatewayPathRulePropertiesFormat; ruleProps != nil { - if ruleProps.BackendAddressPool != nil && ruleProps.RedirectConfiguration != nil { + if applicationGatewayHasSubResource(ruleProps.BackendAddressPool) && applicationGatewayHasSubResource(ruleProps.RedirectConfiguration) { return nil, fmt.Errorf("[ERROR] Conflict between `backend_address_pool_name` and `redirect_configuration_name` (back-end pool not applicable when redirection specified)") } - if ruleProps.BackendHTTPSettings != nil && ruleProps.RedirectConfiguration != nil { + if applicationGatewayHasSubResource(ruleProps.BackendHTTPSettings) && applicationGatewayHasSubResource(ruleProps.RedirectConfiguration) { return nil, fmt.Errorf("[ERROR] Conflict between `backend_http_settings_name` and `redirect_configuration_name` (back-end settings not applicable when redirection specified)") } @@ -2448,8 +2448,8 @@ func flattenApplicationGatewayURLPathMaps(input *[]network.ApplicationGatewayURL if err != nil { return nil, err } - redirectConfigurationName := redirectId.Path["redirectConfigurations"] - ruleOutput["redirect_configuration_name"] = redirectConfigurationName + redirectConfigurationName2 := redirectId.Path["redirectConfigurations"] + ruleOutput["redirect_configuration_name"] = redirectConfigurationName2 ruleOutput["redirect_configuration_id"] = *redirect.ID } From 6c6fd64f854ce32669fae35f23e4af96daf2650e Mon Sep 17 00:00:00 2001 From: rustyf Date: Mon, 4 Mar 2019 23:40:02 +0000 Subject: [PATCH 20/24] Fixed path-based rule with redirection test (Path Map test still failing acceptance - error 400) --- azurerm/resource_arm_application_gateway_test.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/azurerm/resource_arm_application_gateway_test.go b/azurerm/resource_arm_application_gateway_test.go index 37315dfe9cda..8563ef02d5fc 100644 --- a/azurerm/resource_arm_application_gateway_test.go +++ b/azurerm/resource_arm_application_gateway_test.go @@ -1010,6 +1010,7 @@ func testAccAzureRMApplicationGateway_pathBasedRoutingRedirect(rInt int, locatio locals { backend_address_pool_name = "${azurerm_virtual_network.test.name}-beap" frontend_port_name = "${azurerm_virtual_network.test.name}-feport" + frontend_port_name2 = "${azurerm_virtual_network.test.name}-feport2" 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" @@ -1020,7 +1021,7 @@ locals { url_path_map_name = "${azurerm_virtual_network.test.name}-urlpath1" redirect_configuration_name = "${azurerm_virtual_network.test.name}-PathRedirect" redirect_configuration_name2 = "${azurerm_virtual_network.test.name}-PathRedirect2" - target_url = "http://www.example.com" + target_url = "http://www.example.com" } resource "azurerm_application_gateway" "test" { @@ -1044,6 +1045,11 @@ resource "azurerm_application_gateway" "test" { port = 80 } + frontend_port { + name = "${local.frontend_port_name2}" + port = 8888 + } + frontend_ip_configuration { name = "${local.frontend_ip_configuration_name}" public_ip_address_id = "${azurerm_public_ip.test.id}" @@ -1110,7 +1116,7 @@ resource "azurerm_application_gateway" "test" { redirect_configuration { name = "${local.redirect_configuration_name}" redirect_type = "Found" - target_url = "${local.target_url}" + target_url = "${local.target_url}" include_query_string = true } From 6a79696f30a48e23aae4b1cb21e5d39030d817ea Mon Sep 17 00:00:00 2001 From: rustyf Date: Wed, 6 Mar 2019 00:38:39 +0000 Subject: [PATCH 21/24] Ensure that optional redirect configuration properties are only set if specified Ensure that default HTTP settings and back-end pool settings are now optional on path URL map (to facilitate redirections) Tweak tests to reflect better understanding of path URL map --- azurerm/resource_arm_application_gateway.go | 86 ++++++++++++------- .../resource_arm_application_gateway_test.go | 18 ++-- 2 files changed, 61 insertions(+), 43 deletions(-) diff --git a/azurerm/resource_arm_application_gateway.go b/azurerm/resource_arm_application_gateway.go index 2ef5643e5e5d..f8a4c0179604 100644 --- a/azurerm/resource_arm_application_gateway.go +++ b/azurerm/resource_arm_application_gateway.go @@ -740,12 +740,12 @@ func resourceArmApplicationGateway() *schema.Resource { "default_backend_address_pool_name": { Type: schema.TypeString, - Required: true, + Optional: true, }, "default_backend_http_settings_name": { Type: schema.TypeString, - Required: true, + Optional: true, }, "default_redirect_configuration_name": { @@ -2073,27 +2073,30 @@ func expandApplicationGatewayRedirectConfigurations(d *schema.ResourceData, gate name := v["name"].(string) redirectType := v["redirect_type"].(string) - targetListenerName := v["target_listener_name"].(string) - targetListenerID := fmt.Sprintf("%s/httpListeners/%s", gatewayID, targetListenerName) - - targetUrl := v["target_url"].(string) - - includePath := v["include_path"].(bool) - includeQueryString := v["include_query_string"].(bool) - output := network.ApplicationGatewayRedirectConfiguration{ Name: utils.String(name), ApplicationGatewayRedirectConfigurationPropertiesFormat: &network.ApplicationGatewayRedirectConfigurationPropertiesFormat{ RedirectType: network.ApplicationGatewayRedirectType(redirectType), + }, + } - TargetListener: &network.SubResource{ - ID: utils.String(targetListenerID), - }, - TargetURL: utils.String(targetUrl), + if includePath := v["include_path"].(bool); includePath { + output.ApplicationGatewayRedirectConfigurationPropertiesFormat.IncludePath = utils.Bool(includePath) + } - IncludePath: utils.Bool(includePath), - IncludeQueryString: utils.Bool(includeQueryString), - }, + if includeQueryString := v["include_query_string"].(bool); includeQueryString { + output.ApplicationGatewayRedirectConfigurationPropertiesFormat.IncludeQueryString = utils.Bool(includeQueryString) + } + + if targetListenerName := v["target_listener_name"].(string); targetListenerName != "" { + targetListenerID := fmt.Sprintf("%s/httpListeners/%s", gatewayID, targetListenerName) + output.ApplicationGatewayRedirectConfigurationPropertiesFormat.TargetListener = &network.SubResource{ + ID: utils.String(targetListenerID), + } + } + + if targetUrl := v["target_url"].(string); targetUrl != "" { + output.ApplicationGatewayRedirectConfigurationPropertiesFormat.TargetURL = utils.String(targetUrl) } results = append(results, output) @@ -2280,13 +2283,6 @@ func expandApplicationGatewayURLPathMaps(d *schema.ResourceData, gatewayID strin v := raw.(map[string]interface{}) name := v["name"].(string) - defaultBackendAddressPoolName := v["default_backend_address_pool_name"].(string) - defaultBackendHTTPSettingsName := v["default_backend_http_settings_name"].(string) - defaultRedirectConfigurationName := v["default_redirect_configuration_name"].(string) - - defaultBackendAddressPoolID := fmt.Sprintf("%s/backendAddressPools/%s", gatewayID, defaultBackendAddressPoolName) - defaultBackendHTTPSettingsID := fmt.Sprintf("%s/backendHttpSettingsCollection/%s", gatewayID, defaultBackendHTTPSettingsName) - defaultRedirectConfigurationID := fmt.Sprintf("%s/redirectConfigurations/%s", gatewayID, defaultRedirectConfigurationName) pathRules := make([]network.ApplicationGatewayPathRule, 0) for _, ruleConfig := range v["path_rule"].([]interface{}) { @@ -2333,19 +2329,35 @@ func expandApplicationGatewayURLPathMaps(d *schema.ResourceData, gatewayID strin output := network.ApplicationGatewayURLPathMap{ Name: utils.String(name), ApplicationGatewayURLPathMapPropertiesFormat: &network.ApplicationGatewayURLPathMapPropertiesFormat{ - DefaultBackendAddressPool: &network.SubResource{ - ID: utils.String(defaultBackendAddressPoolID), - }, - DefaultBackendHTTPSettings: &network.SubResource{ - ID: utils.String(defaultBackendHTTPSettingsID), - }, - DefaultRedirectConfiguration: &network.SubResource{ - ID: utils.String(defaultRedirectConfigurationID), - }, PathRules: &pathRules, }, } + // treating these three as optional as seems necessary when redirection is also an alternative. Not explicit in the documentation, though + // see https://docs.microsoft.com/en-us/rest/api/application-gateway/applicationgateways/createorupdate#applicationgatewayurlpathmap + // see also az docs https://docs.microsoft.com/en-us/cli/azure/network/application-gateway/url-path-map?view=azure-cli-latest#az-network-application-gateway-url-path-map-create + + if defaultBackendAddressPoolName := v["default_backend_address_pool_name"].(string); defaultBackendAddressPoolName != "" { + defaultBackendAddressPoolID := fmt.Sprintf("%s/backendAddressPools/%s", gatewayID, defaultBackendAddressPoolName) + output.ApplicationGatewayURLPathMapPropertiesFormat.DefaultBackendAddressPool = &network.SubResource{ + ID: utils.String(defaultBackendAddressPoolID), + } + } + + if defaultBackendHTTPSettingsName := v["default_backend_http_settings_name"].(string); defaultBackendHTTPSettingsName != "" { + defaultBackendHTTPSettingsID := fmt.Sprintf("%s/backendHttpSettingsCollection/%s", gatewayID, defaultBackendHTTPSettingsName) + output.ApplicationGatewayURLPathMapPropertiesFormat.DefaultBackendHTTPSettings = &network.SubResource{ + ID: utils.String(defaultBackendHTTPSettingsID), + } + } + + if defaultRedirectConfigurationName := v["default_redirect_configuration_name"].(string); defaultRedirectConfigurationName != "" { + defaultRedirectConfigurationID := fmt.Sprintf("%s/redirectConfigurations/%s", gatewayID, defaultRedirectConfigurationName) + output.ApplicationGatewayURLPathMapPropertiesFormat.DefaultRedirectConfiguration = &network.SubResource{ + ID: utils.String(defaultRedirectConfigurationID), + } + } + results = append(results, output) } @@ -2415,10 +2427,18 @@ func flattenApplicationGatewayURLPathMaps(input *[]network.ApplicationGatewayURL if ruleProps := rule.ApplicationGatewayPathRulePropertiesFormat; ruleProps != nil { + if applicationGatewayHasSubResource(props.DefaultBackendAddressPool) && applicationGatewayHasSubResource(ruleProps.RedirectConfiguration) { + return nil, fmt.Errorf("[ERROR] Conflict between `default_backend_address_pool_name` and `redirect_configuration_name` (default back-end pool not applicable when redirection specified)") + } + if applicationGatewayHasSubResource(ruleProps.BackendAddressPool) && applicationGatewayHasSubResource(ruleProps.RedirectConfiguration) { return nil, fmt.Errorf("[ERROR] Conflict between `backend_address_pool_name` and `redirect_configuration_name` (back-end pool not applicable when redirection specified)") } + if applicationGatewayHasSubResource(props.DefaultBackendHTTPSettings) && applicationGatewayHasSubResource(ruleProps.RedirectConfiguration) { + return nil, fmt.Errorf("[ERROR] Conflict between `default_backend_http_settings_name` and `redirect_configuration_name` (default back-end settings not applicable when redirection specified)") + } + if applicationGatewayHasSubResource(ruleProps.BackendHTTPSettings) && applicationGatewayHasSubResource(ruleProps.RedirectConfiguration) { return nil, fmt.Errorf("[ERROR] Conflict between `backend_http_settings_name` and `redirect_configuration_name` (back-end settings not applicable when redirection specified)") } diff --git a/azurerm/resource_arm_application_gateway_test.go b/azurerm/resource_arm_application_gateway_test.go index 8563ef02d5fc..cbc77ec34d61 100644 --- a/azurerm/resource_arm_application_gateway_test.go +++ b/azurerm/resource_arm_application_gateway_test.go @@ -162,7 +162,7 @@ func TestAccAzureRMApplicationGateway_pathBasedRouting(t *testing.T) { }) } -func TestAccAzureRMApplicationGateway_httpListenerRedirect(t *testing.T) { +func TestAccAzureRMApplicationGateway_routingRedirect_httpListener(t *testing.T) { resourceName := "azurerm_application_gateway.test" ri := tf.AccRandTimeInt() @@ -172,7 +172,7 @@ func TestAccAzureRMApplicationGateway_httpListenerRedirect(t *testing.T) { CheckDestroy: testCheckAzureRMApplicationGatewayDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMApplicationGateway_httpListenerRedirect(ri, testLocation()), + Config: testAccAzureRMApplicationGateway_routingRedirect_httpListener(ri, testLocation()), Check: resource.ComposeTestCheckFunc( testCheckAzureRMApplicationGatewayExists(resourceName), resource.TestCheckResourceAttrSet(resourceName, "redirect_configuration.0.name"), @@ -191,7 +191,7 @@ func TestAccAzureRMApplicationGateway_httpListenerRedirect(t *testing.T) { }) } -func TestAccAzureRMApplicationGateway_pathBasedRoutingRedirect(t *testing.T) { +func TestAccAzureRMApplicationGateway_routingRedirect_pathBased(t *testing.T) { resourceName := "azurerm_application_gateway.test" ri := tf.AccRandTimeInt() @@ -201,12 +201,12 @@ func TestAccAzureRMApplicationGateway_pathBasedRoutingRedirect(t *testing.T) { CheckDestroy: testCheckAzureRMApplicationGatewayDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMApplicationGateway_pathBasedRoutingRedirect(ri, testLocation()), + Config: testAccAzureRMApplicationGateway_routingRedirect_pathBased(ri, testLocation()), Check: resource.ComposeTestCheckFunc( testCheckAzureRMApplicationGatewayExists(resourceName), resource.TestCheckResourceAttrSet(resourceName, "redirect_configuration.0.name"), resource.TestCheckResourceAttr(resourceName, "redirect_configuration.0.redirect_type", "Found"), - resource.TestCheckResourceAttr(resourceName, "redirect_configuration.0.target_url", "http://www/example.com"), + resource.TestCheckResourceAttr(resourceName, "redirect_configuration.0.target_url", "http://www.example.com"), resource.TestCheckResourceAttr(resourceName, "redirect_configuration.0.include_query_string", "true"), resource.TestCheckResourceAttrSet(resourceName, "redirect_configuration.1.name"), resource.TestCheckResourceAttr(resourceName, "redirect_configuration.1.redirect_type", "Permanent"), @@ -906,7 +906,7 @@ resource "azurerm_application_gateway" "test" { `, template, rInt) } -func testAccAzureRMApplicationGateway_httpListenerRedirect(rInt int, location string) string { +func testAccAzureRMApplicationGateway_routingRedirect_httpListener(rInt int, location string) string { template := testAccAzureRMApplicationGateway_template(rInt, location) return fmt.Sprintf(` %s @@ -1001,7 +1001,7 @@ resource "azurerm_application_gateway" "test" { `, template, rInt) } -func testAccAzureRMApplicationGateway_pathBasedRoutingRedirect(rInt int, location string) string { +func testAccAzureRMApplicationGateway_routingRedirect_pathBased(rInt int, location string) string { template := testAccAzureRMApplicationGateway_template(rInt, location) return fmt.Sprintf(` %s @@ -1090,13 +1090,11 @@ resource "azurerm_application_gateway" "test" { url_path_map { name = "${local.url_path_map_name}" - default_backend_address_pool_name = "${local.backend_address_pool_name}" - default_backend_http_settings_name = "${local.http_setting_name}" + default_redirect_configuration_name = "${local.redirect_configuration_name}" path_rule { name = "${local.path_rule_name}" redirect_configuration_name = "${local.redirect_configuration_name}" - paths = [ "/test", ] From 57e73e786a0e49c1ca393d6b6f070ec7a3eddd09 Mon Sep 17 00:00:00 2001 From: rustyf Date: Wed, 6 Mar 2019 23:31:08 +0000 Subject: [PATCH 22/24] Some missing parameters for redirect_configuration use. Plus some previously required properties are now optional. --- .../docs/r/application_gateway.html.markdown | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/website/docs/r/application_gateway.html.markdown b/website/docs/r/application_gateway.html.markdown index f73758b9343a..d90d3139b3e9 100644 --- a/website/docs/r/application_gateway.html.markdown +++ b/website/docs/r/application_gateway.html.markdown @@ -274,9 +274,11 @@ A `path_rule` block supports the following: * `paths` - (Required) A list of Paths used in this Path Rule. -* `backend_address_pool_name` - (Required) The Name of the Backend Address Pool to use for this Path Rule. +* `backend_address_pool_name` - (Optional) The Name of the Backend Address Pool to use for this Path Rule. Cannot be set if `redirect_configuration_name` is set. -* `backend_http_settings_name` - (Required) The Name of the Backend HTTP Settings Collection to use for this Path Rule. +* `backend_http_settings_name` - (Optional) The Name of the Backend HTTP Settings Collection to use for this Path Rule. Cannot be set if `redirect_configuration_name` is set. + +* `redirect_configuration_name` - (Optional) The Name of a Redirect Configuration to use for this Path Rule. Cannot be set if `backend_address_pool_name` or `backend_http_settings_name` is set. --- @@ -312,9 +314,11 @@ A `request_routing_rule` block supports the following: * `http_listener_name` - (Required) The Name of the HTTP Listener which should be used for this Routing Rule. -* `backend_address_pool_name` - (Optional) The Name of the Backend Address Pool which should be used for this Routing Rule. +* `backend_address_pool_name` - (Optional) The Name of the Backend Address Pool which should be used for this Routing Rule. Cannot be set if `redirect_configuration_name` is set. + +* `backend_http_settings_name` - (Optional) The Name of the Backend HTTP Settings Collection which should be used for this Routing Rule. Cannot be set if `redirect_configuration_name` is set. -* `backend_http_settings_name` - (Optional) The Name of the Backend HTTP Settings Collection which should be used for this Routing Rule. +* `redirect_configuration_name` - (Optional) The Name of the Redirect Configuration which should be used for this Routing Rule. Cannot be set if either `backend_address_pool_name` or `backend_http_settings_name` is set. * `url_path_map_name` - (Optional) The Name of the URL Path Map which should be associated with this Routing Rule. @@ -344,9 +348,11 @@ A `url_path_map` block supports the following: * `name` - (Required) The Name of the URL Path Map. -* `default_backend_address_pool_name` - (Required) The Name of the Default Backend Address Pool which should be used for this URL Path Map. +* `default_backend_address_pool_name` - (Optional) The Name of the Default Backend Address Pool which should be used for this URL Path Map. Cannot be set if there are path_rules with re-direct configurations set. + +* `default_backend_http_settings_name` - (Optional) The Name of the Default Backend HTTP Settings Collection which should be used for this URL Path Map. Cannot be set if there are path_rules with re-direct configurations set. -* `default_backend_http_settings_name` - (Required) The Name of the Default Backend HTTP Settings Collection which should be used for this URL Path Map. +* `default_redirect_configuration_name` - (Optional) The Name of the Default Redirect Configuration which should be used for this URL Path Map. Cannot be set if there are path_rules with Backend Address Pool or HTTP Settings set. * `path_rule` - (Required) One or more `path_rule` blocks as defined above. @@ -380,9 +386,9 @@ A `redirect_configuration` block supports the following: * `redirect_type` - (Required) The type of redirect. Possible values are `Permanent`, `Temporary`, `Found` and `SeeOther` -* `target_listener_name` - (Optional) The name of the listener to redirect to. This field cannot be set when `target_url` is set. +* `target_listener_name` - (Optional) The name of the listener to redirect to. Cannot be set if `target_url` is set. -* `target_url` - (Optional) The Url to redirect the request to. This field cannot be set when `target_listener_name` is set. +* `target_url` - (Optional) The Url to redirect the request to. Cannot be set if `target_listener_name` is set. * `include_path` - (Optional) Whether or not to include the path in the redirected Url. Defaults to `false` @@ -487,6 +493,8 @@ A `path_rule` block exports the following: * `backend_http_settings_id` - The ID of the Backend HTTP Settings Collection used in this Path Rule. +* `redirect_configuration_id` - The ID of the Redirect Configuration used in this Path Rule. + --- A `probe` block exports the following: @@ -505,6 +513,8 @@ A `request_routing_rule` block exports the following: * `backend_http_settings_id` - The ID of the associated Backend HTTP Settings Configuration. +* `redirect_configuration_id` - The ID of the associated Redirect Configuration. + * `url_path_map_id` - The ID of the associated URL Path Map. --- @@ -525,6 +535,8 @@ A `url_path_map` block exports the following: * `default_backend_http_settings_id` - The ID of the Default Backend HTTP Settings Collection. +* `default_redirect_configuration_id` - The ID of the Default Redirect Configuration. + * `path_rule` - A list of `path_rule` blocks as defined above. --- From 0d74b8f71b2f0ed90b7dbdc3739b748bdcc1a3a0 Mon Sep 17 00:00:00 2001 From: rustyf Date: Sun, 17 Mar 2019 14:57:44 +0000 Subject: [PATCH 23/24] Drop nilOrEmpty() on the basis that it's hardly providing much value/abstraction --- azurerm/helpers/validate/strings.go | 4 ---- azurerm/resource_arm_application_gateway.go | 6 +++--- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/azurerm/helpers/validate/strings.go b/azurerm/helpers/validate/strings.go index 7bd744512338..1dec1bd2e07f 100644 --- a/azurerm/helpers/validate/strings.go +++ b/azurerm/helpers/validate/strings.go @@ -18,7 +18,3 @@ func NoEmptyStrings(i interface{}, k string) ([]string, []error) { return nil, nil } - -func NilOrEmpty(s *string) bool { - return s == nil || *s == "" -} diff --git a/azurerm/resource_arm_application_gateway.go b/azurerm/resource_arm_application_gateway.go index f8a4c0179604..e78568d2a7db 100644 --- a/azurerm/resource_arm_application_gateway.go +++ b/azurerm/resource_arm_application_gateway.go @@ -2114,14 +2114,14 @@ func flattenApplicationGatewayRedirectConfigurations(input *[]network.Applicatio for _, config := range *input { if props := config.ApplicationGatewayRedirectConfigurationPropertiesFormat; props != nil { - if !applicationGatewayHasSubResource(config.TargetListener) && validate.NilOrEmpty(config.TargetURL) { + if !applicationGatewayHasSubResource(config.TargetListener) && (config.TargetURL == nil || *config.TargetURL == "") { return nil, fmt.Errorf("[ERROR] Set either `target_listener_name` or `target_url`") } - if applicationGatewayHasSubResource(config.TargetListener) && !validate.NilOrEmpty(config.TargetURL) { + if applicationGatewayHasSubResource(config.TargetListener) && config.TargetURL != nil && *config.TargetURL != "" { return nil, fmt.Errorf("[ERROR] Conflict between `target_listener_name` and `target_url` (redirection is either to URL or target listener)") } - if !validate.NilOrEmpty(config.TargetURL) && config.IncludePath != nil { + if config.TargetURL != nil && *config.TargetURL != "" && config.IncludePath != nil { return nil, fmt.Errorf("[ERROR] `include_path` is not a valid option when `target_url` is set") } From 4598d22ae3e47dc73bf6fa5c9e56821cab229622 Mon Sep 17 00:00:00 2001 From: rustyf Date: Sun, 17 Mar 2019 15:22:52 +0000 Subject: [PATCH 24/24] goimports --- azurerm/resource_arm_application_gateway.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/azurerm/resource_arm_application_gateway.go b/azurerm/resource_arm_application_gateway.go index e78568d2a7db..99d0949f3a69 100644 --- a/azurerm/resource_arm_application_gateway.go +++ b/azurerm/resource_arm_application_gateway.go @@ -2,6 +2,8 @@ package azurerm import ( "fmt" + "log" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-08-01/network" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/validation" @@ -10,7 +12,6 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" - "log" ) func resourceArmApplicationGateway() *schema.Resource {