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

add support for pick-hostname behaviours #2658

61 changes: 49 additions & 12 deletions azurerm/resource_arm_application_gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,12 @@ func resourceArmApplicationGateway() *schema.Resource {
}, true),
},

"pick_host_name_from_backend_address": {
Type: schema.TypeBool,
Optional: true,
Default: false,
},
metacpp marked this conversation as resolved.
Show resolved Hide resolved

"request_timeout": {
Type: schema.TypeInt,
Optional: true,
Expand Down Expand Up @@ -499,7 +505,7 @@ func resourceArmApplicationGateway() *schema.Resource {

"host": {
Type: schema.TypeString,
Required: true,
Optional: true,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that this is optional we should probably add validation somewhere to make sure if this is empty, pick_host_name_from_backend_address is true.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also update the documentation with this change

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@katbyte might you have any suggestions re "add validation somewhere"? - it seems like current validation mechanisms are per-property.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mdsills, in the create function you can check both properties and if neither are set return an error.

Something like:

host := v["pick_host_name_from_backend_address"].(string)
pick := v["pick_host_name_from_backend_address"].(bool)
if !pick && host == "" {
return fmt.Errorf("one of `host` or `pick_host_name_from_backend_http_settings` must be set")
}

if pick && host != "" {
return fmt.Errorf("Only one of `host` or `pick_host_name_from_backend_http_settings` must be set")
}

disclaimer that code might not compile 😅

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the pointer. it's ended up looking rather different, but hopefully still acceptable, or at least a step towards acceptable...

},

"interval": {
Expand All @@ -517,6 +523,12 @@ func resourceArmApplicationGateway() *schema.Resource {
Required: true,
},

"pick_host_name_from_backend_http_settings": {
Type: schema.TypeBool,
Optional: true,
Default: false,
},
metacpp marked this conversation as resolved.
Show resolved Hide resolved

"minimum_servers": {
Type: schema.TypeInt,
Optional: true,
Expand Down Expand Up @@ -789,6 +801,20 @@ func resourceArmApplicationGatewayCreateUpdate(d *schema.ResourceData, meta inte
},
}

for _, probe := range *probes {
probeProperties := *probe.ApplicationGatewayProbePropertiesFormat
host := *probeProperties.Host
pick := *probeProperties.PickHostNameFromBackendHTTPSettings

if host == "" && !pick {
return fmt.Errorf("One of `host` or `pick_host_name_from_backend_http_settings` must be set")
}

if host != "" && pick {
return fmt.Errorf("Only one of `host` or `pick_host_name_from_backend_http_settings` can be set")
}
}

if _, ok := d.GetOk("waf_configuration"); ok {
gateway.ApplicationGatewayPropertiesFormat.WebApplicationFirewallConfiguration = expandApplicationGatewayWafConfig(d)
}
Expand Down Expand Up @@ -1092,15 +1118,17 @@ func expandApplicationGatewayBackendHTTPSettings(d *schema.ResourceData, gateway
port := int32(v["port"].(int))
protocol := v["protocol"].(string)
cookieBasedAffinity := v["cookie_based_affinity"].(string)
pickHostNameFromBackendAddress := v["pick_host_name_from_backend_address"].(bool)
requestTimeout := int32(v["request_timeout"].(int))

setting := network.ApplicationGatewayBackendHTTPSettings{
Name: &name,
ApplicationGatewayBackendHTTPSettingsPropertiesFormat: &network.ApplicationGatewayBackendHTTPSettingsPropertiesFormat{
CookieBasedAffinity: network.ApplicationGatewayCookieBasedAffinity(cookieBasedAffinity),
Port: utils.Int32(port),
Protocol: network.ApplicationGatewayProtocol(protocol),
RequestTimeout: utils.Int32(requestTimeout),
CookieBasedAffinity: network.ApplicationGatewayCookieBasedAffinity(cookieBasedAffinity),
PickHostNameFromBackendAddress: utils.Bool(pickHostNameFromBackendAddress),
Port: utils.Int32(port),
Protocol: network.ApplicationGatewayProtocol(protocol),
RequestTimeout: utils.Int32(requestTimeout),
},
}

Expand Down Expand Up @@ -1158,6 +1186,9 @@ func flattenApplicationGatewayBackendHTTPSettings(input *[]network.ApplicationGa
if port := props.Port; port != nil {
output["port"] = int(*port)
}
if pickHostNameFromBackendAddress := props.PickHostNameFromBackendAddress; pickHostNameFromBackendAddress != nil {
output["pick_host_name_from_backend_address"] = *pickHostNameFromBackendAddress
}
output["protocol"] = string(props.Protocol)
if timeout := props.RequestTimeout; timeout != nil {
output["request_timeout"] = int(*timeout)
Expand Down Expand Up @@ -1549,17 +1580,19 @@ func expandApplicationGatewayProbes(d *schema.ResourceData) *[]network.Applicati
protocol := v["protocol"].(string)
timeout := int32(v["timeout"].(int))
unhealthyThreshold := int32(v["unhealthy_threshold"].(int))
pickHostNameFromBackendHTTPSettings := v["pick_host_name_from_backend_http_settings"].(bool)

output := network.ApplicationGatewayProbe{
Name: utils.String(name),
ApplicationGatewayProbePropertiesFormat: &network.ApplicationGatewayProbePropertiesFormat{
Host: utils.String(host),
Interval: utils.Int32(interval),
MinServers: utils.Int32(minServers),
Path: utils.String(probePath),
Protocol: network.ApplicationGatewayProtocol(protocol),
Timeout: utils.Int32(timeout),
UnhealthyThreshold: utils.Int32(unhealthyThreshold),
Host: utils.String(host),
Interval: utils.Int32(interval),
MinServers: utils.Int32(minServers),
Path: utils.String(probePath),
Protocol: network.ApplicationGatewayProtocol(protocol),
Timeout: utils.Int32(timeout),
UnhealthyThreshold: utils.Int32(unhealthyThreshold),
PickHostNameFromBackendHTTPSettings: utils.Bool(pickHostNameFromBackendHTTPSettings),
},
}

Expand Down Expand Up @@ -1625,6 +1658,10 @@ func flattenApplicationGatewayProbes(input *[]network.ApplicationGatewayProbe) [
output["unhealthy_threshold"] = int(*threshold)
}

if pickHostNameFromBackendHTTPSettings := props.PickHostNameFromBackendHTTPSettings; pickHostNameFromBackendHTTPSettings != nil {
output["pick_host_name_from_backend_http_settings"] = *pickHostNameFromBackendHTTPSettings
}

if minServers := props.MinServers; minServers != nil {
output["minimum_servers"] = int(*minServers)
}
Expand Down
206 changes: 206 additions & 0 deletions azurerm/resource_arm_application_gateway_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,56 @@ func TestAccAzureRMApplicationGateway_probes(t *testing.T) {
})
}

func TestAccAzureRMApplicationGateway_probesPickHostNameFromBackendHTTPSettings(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_probesPickHostNameFromBackendHTTPSettings(ri, testLocation()),
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMApplicationGatewayExists(resourceName),
resource.TestCheckResourceAttr(resourceName, "probe.0.pick_host_name_from_backend_http_settings", "true"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func TestAccAzureRMApplicationGateway_settingsPickHostNameFromBackendAddress(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_settingsPickHostNameFromBackendAddress(ri, testLocation()),
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMApplicationGatewayExists(resourceName),
resource.TestCheckResourceAttr(resourceName, "backend_http_settings.0.pick_host_name_from_backend_address", "true"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func TestAccAzureRMApplicationGateway_sslCertificate(t *testing.T) {
resourceName := "azurerm_application_gateway.test"
ri := tf.AccRandTimeInt()
Expand Down Expand Up @@ -837,6 +887,162 @@ resource "azurerm_application_gateway" "test" {
`, template, rInt)
}

func testAccAzureRMApplicationGateway_probesPickHostNameFromBackendHTTPSettings(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"
probe_name = "${azurerm_virtual_network.test.name}-probe"
request_routing_rule_name = "${azurerm_virtual_network.test.name}-rqrt"
}

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"
pick_host_name_from_backend_address = true
port = 80
probe_name = "${local.probe_name}"
protocol = "Http"
request_timeout = 1
}

probe {
name = "${local.probe_name}"
protocol = "Http"
path = "/test"
timeout = 120
interval = 300
unhealthy_threshold = 8
pick_host_name_from_backend_http_settings = true
}

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, rInt)
}

func testAccAzureRMApplicationGateway_settingsPickHostNameFromBackendAddress(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"
request_routing_rule_name = "${azurerm_virtual_network.test.name}-rqrt"
}

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"
pick_host_name_from_backend_address = true
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, rInt)
}

func testAccAzureRMApplicationGateway_sslCertificate(rInt int, location string) string {
template := testAccAzureRMApplicationGateway_template(rInt, location)
return fmt.Sprintf(`
Expand Down
6 changes: 5 additions & 1 deletion website/docs/r/application_gateway.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,8 @@ A `backend_http_settings` block supports the following:

* `request_timeout` - (Required) The request timeout in seconds, which must be between 1 and 86400 seconds.

* `pick_host_name_from_backend_address` - (Optional) Whether host header should be picked from the host name of the backend server. Defaults to `false`.

* `authentication_certificate` - (Optional) One or more `authentication_certificate` blocks.

---
Expand Down Expand Up @@ -267,7 +269,7 @@ A `path_rule` block supports the following:

A `probe` block support the following:

* `host` - (Required) The Hostname used for this Probe. If the Application Gateway is configured for a single site, by default the Host name should be specified as ‘127.0.0.1’, unless otherwise configured in custom probe.
* `host` - (Optional) The Hostname used for this Probe. If the Application Gateway is configured for a single site, by default the Host name should be specified as ‘127.0.0.1’, unless otherwise configured in custom probe. Cannot be set if `pick_host_name_from_backend_http_settings` is set to `true`.

* `interval` - (Required) The Interval between two consecutive probes in seconds. Possible values range from 1 second to a maximum of 86,400 seconds.

Expand All @@ -281,6 +283,8 @@ A `probe` block support the following:

* `unhealthy_threshold` - (Required) The Unhealthy Threshold for this Probe, which indicates the amount of retries which should be attempted before a node is deemed unhealthy. Possible values are from 1 - 20 seconds.

* `pick_host_name_from_backend_http_settings` - (Optional) Whether the host header should be picked from the backend http settings. Defaults to `false`.

* `match` - (Optional) A `match` block as defined above.

* `minimum_servers` - (Optional) The minimum number of servers that are always marked as healthy. Defaults to `0`.
Expand Down