Infrastructure as Code has become the standard methodology for managing cloud environments, allowing engineers to define, preview, and deploy resources with repeatability and precision. At the heart of secure and scalable web front ends in Azure lies the Application Gateway, a sophisticated Layer-7 load balancer. This service goes far beyond simple packet forwarding; it manages and optimizes traffic to web applications based on complex factors, including round-robin distribution, cookie-based sessions, and path-based routing. When combined with the Web Application Firewall (WAF) v2 policy, it provides a robust security perimeter against common web exploits. The following analysis details the technical architecture, configuration requirements, and deployment workflows for establishing an Azure Application Gateway using Terraform, incorporating both standard deployment patterns and the emerging Application Gateway for Containers (AGFC) model.
Architectural Foundations and Core Components
To understand the deployment, one must first deconstruct the components that make up a functional Application Gateway environment. The Application Gateway is not a standalone entity; it relies on a specific network topology to function correctly. A typical deployment requires a resource group to scope the resources, a virtual network to provide the network fabric, and a dedicated subnet for the gateway itself. Additionally, a public IP address is necessary to expose the gateway to the internet, and the WAF policy defines the security rules that govern inbound traffic.
The Application Gateway acts as a traffic controller for web applications. It bases how it routes traffic on several factors. One of the most critical routing mechanisms is round-robin, which distributes load evenly across backend servers. Another is cookie-based affinity, which ensures that a specific user's requests are routed to the same backend server for the duration of their session, thereby preserving state. This session stickiness is managed through cookies, and the configuration allows for both hidden and visible cookie methods, though best practice dictates that only one method should be used in practice to avoid conflict and ensure predictable behavior.
Network Topology and Subnet Requirements
The network design is pivotal to the success of an Application Gateway deployment. The gateway requires a dedicated subnet within the virtual network. In a standard quickstart scenario, the virtual network is defined with a specific address space, such as 10.21.0.0/16. Within this network, two primary subnets are typically defined: one for the Application Gateway frontend (myAGSubnet) and one for the backend servers (myBackendSubnet). The frontend subnet, for example, might use the address prefix 10.21.0.0/24, while the backend subnet uses 10.21.1.0/24. This separation ensures that the load balancer and the application servers reside in distinct network segments, allowing for precise security group rules and network isolation.
The public IP address is another critical element. It is created within the resource group and associated with the Application Gateway's frontend IP configuration. This IP address serves as the entry point for all external traffic. In recent updates, Application Gateway frontends have gained support for dual-stack IP addresses, a feature currently in Preview. This advancement allows administrators to create up to four frontend IP addresses: two IPv4 addresses (public and private) and two IPv6 addresses (public and private). This dual-stack capability is essential for modern networks that are transitioning to or requiring IPv6 connectivity, providing a seamless migration path and broader reachability.
Terraform Configuration and HCL Syntax
Terraform enables the definition, preview, and deployment of cloud infrastructure. The entire process is driven by configuration files written in HashiCorp Configuration Language (HCL). This syntax allows you to specify the cloud provider, such as Azure, and the specific elements that make up your cloud infrastructure. Before any infrastructure is deployed, Terraform generates an execution plan. This plan allows you to preview your infrastructure changes, ensuring that the intended state matches the proposed reality. Once you verify the changes, you apply the execution plan to deploy the infrastructure. This preview-and-apply cycle is fundamental to preventing accidental disruptions or misconfigurations in production environments.
Provider Configuration and Authentication
The foundation of any Terraform Azure project is the provider configuration. The providers.tf file declares the necessary providers and their versions. For the standard Application Gateway deployment, the required providers are hashicorp/azurerm and hashicorp/random. The azurerm provider version is typically pinned to ~>3.0 or higher, while the random provider is used to generate unique identifiers for resources.
```terraform
terraform {
requiredversion = ">=1.2"
requiredproviders {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.0"
}
random = {
source = "hashicorp/random"
version = "~> 3.0"
}
}
}
provider "azurerm" {
features {}
}
```
A critical consideration for authentication arises when using the 4.x version of the azurerm provider. If you are utilizing this newer version, you must explicitly specify the Azure subscription ID to authenticate to Azure before running the Terraform commands. This ensures that Terraform knows exactly which subscription to target, preventing ambiguity in multi-tenant environments. Prior to running the deployment script, it is imperative to log in to your Azure subscription using the Azure CLI or Azure PowerShell. This step ensures that Terraform can authenticate and interact with your Azure account securely.
Resource Definitions and Backend Pools
The main.tf file contains the core resource definitions. The process begins with a random_string resource to generate a unique suffix for the resource group name, preventing conflicts in shared subscriptions. The resource group itself is defined with a location, such as eastus.
```terraform
resource "random_string" "rg" {
length = 8
upper = false
special = false
}
resource "azurermresourcegroup" "rg" {
name = "101-application-gateway-${random_string.rg.result}"
location = "eastus"
}
```
Following the resource group, the virtual network and subnets are created. The virtual network myVNet is configured with the address space 10.21.0.0/16. The frontend subnet and backend subnet are then defined with their respective address prefixes.
Backend address pools are a crucial aspect of Application Gateway configuration. These pools define the set of servers to which the gateway routes traffic. In advanced Terraform modules, such as those following the AVM framework, backend address pools are defined using a map of objects. Each object requires a name and optionally includes fqdns (Fully Qualified Domain Names) and ip_addresses. This structure allows for flexible backend definitions, whether the servers are identified by static IPs or dynamic DNS names.
terraform
variable "backend_address_pools" {
type = map(object({
name = string
fqdns = optional(set(string))
ip_addresses = optional(set(string))
}))
}
Health probes are another integral component. These probes monitor the health of the servers within the backend address pools. If a server is detected as unhealthy, the gateway stops routing traffic to it. The probes continue to monitor such an unhealthy server, and the gateway starts routing the traffic to it once again as soon as the probes detect it as healthy. This dynamic health checking ensures high availability and optimal performance, automatically failing over to healthy instances without manual intervention.
Advanced Modules and the AVM Framework
While basic resource definitions are sufficient for simple deployments, large-scale environments benefit from reusable Terraform modules. The AVM (Azure Verified Modules) framework provides a standardized way to consume Azure resources. However, there is a crucial caveat regarding the maturity of these modules. As the overall AVM framework is not generally available (GA) yet, the CI framework and test automation are not fully functional and implemented across all supported languages. Consequently, breaking changes are expected, and additional customer feedback is yet to be gathered and incorporated.
Versioning Constraints and Pre-Release Status
Due to the pre-GA status of the AVM framework, modules MUST NOT be published at version 1.0.0 or higher at this time. All modules must be published as a pre-release version (e.g., 0.1.0, 0.1.1, 0.2.0, etc.) until the AVM framework becomes GA. This versioning strategy signals to consumers that while the modules are functional, they are subject to change.
Despite this warning, it is important to note that this DOES NOT mean that the modules cannot be consumed and utilized. They CAN be leveraged in all types of environments, including development, testing, and production. Consumers can treat them just like any other Infrastructure as Code (IaC) module and raise issues or feature requests against them as they learn from the usage of the module. Users should also read the release notes for each version, if considering updating to a more recent version of a module, to see if there are any considerations or breaking changes.
Module Resources and Input Variables
The AVM module for Azure Application Gateway utilizes a specific set of resources to manage the gateway and its associated services. The following resources are used by this module:
| Resource Type | Name | Description |
|---|---|---|
azurerm_application_gateway |
.this |
The core Application Gateway resource |
azurerm_management_lock |
.this |
Prevents accidental deletion or modification |
azurerm_monitor_diagnostic_setting |
.this |
Configures diagnostic logging |
azurerm_public_ip |
.this |
The public IP address for the gateway |
azurerm_role_assignment |
.this |
Manages access control roles |
modtm_telemetry |
.telemetry |
Collects usage telemetry |
random_uuid |
.telemetry |
Generates unique identifiers for telemetry |
Additionally, the module utilizes data sources such as azapi_client_config and modtm_module_source to gather necessary configuration details. The input variables for the module are structured to enforce best practices. For instance, the cookie_based_affinity variable is a required field that determines if cookie-based affinity is enabled. This ensures that session management is explicitly configured rather than left to default settings, which might not align with specific application requirements.
Application Gateway for Containers (AGFC)
With the rise of containerized applications, Azure introduced the Application Gateway for Containers (AGFC). This service is designed to provide load balancing and traffic management specifically for containers. The deployment of AGFC using Terraform follows a distinct pattern compared to the standard Application Gateway.
Deploying AGFC Resources
To create an AGFC using Terraform, the process involves creating the load balancer, associating it to a subnet, and creating a frontend. The resource type for the load balancer is azurerm_application_load_balancer.
terraform
resource "azurerm_application_load_balancer" "alb" {
name = "tamops-alb"
location = "uksouth"
resource_group_name = azurerm_resource_group.rg.name
}
Once the load balancer is created, it must be associated with a subnet. This is achieved using the azurerm_application_load_balancer_subnet_association resource. The subnet ID is typically referenced from a previously created subnet, such as azurerm_subnet.appgw_subnet.id. A Network Security Group (NSG) can also be associated at this stage to control traffic flow.
terraform
resource "azurerm_application_load_balancer_subnet_association" "alb" {
name = "alb-subnet-association"
application_load_balancer_id = azurerm_application_load_balancer.alb.id
subnet_id = azurerm_subnet.appgw_subnet.id
network_security_group_id = azurerm_network_security_group.nsg.id
}
The final step in the basic AGFC setup is creating the frontend. This is done using the azurerm_application_load_balancer_frontend resource.
terraform
resource "azurerm_application_load_balancer_frontend" "alb" {
name = "alb-frontend"
application_load_balancer_id = azurerm_application_load_balancer.alb.id
}
By following these steps, you successfully deploy an Application Gateway for Containers with a frontend. While this basic setup provides the foundational connectivity, further configuration is required to integrate specific applications and define routing rules.
Security Integration: WAF v2 Policies
Security is paramount for any public-facing web application. The Application Gateway integrates with Azure Web Application Firewall (WAF) v2 to provide advanced protection. A WAF policy is created and associated with the Application Gateway. This policy can include custom rules, such as blocking traffic from a specific IP address.
The WAF policy configuration in Terraform allows for the definition of custom rules that can block, allow, or log traffic based on various criteria. For example, a custom rule can be created to block a specific IP address, enhancing the security posture of the application. The WAF policy ID is then referenced in the Application Gateway configuration to apply these security rules.
The integration of WAF v2 with the Application Gateway provides a comprehensive security solution that combines load balancing capabilities with web attack protection. This ensures that not only is the traffic efficiently distributed, but it is also filtered for malicious patterns before reaching the backend servers.
Output Variables and Deployment Verification
After defining the resources, it is essential to define output variables that provide useful information about the deployed infrastructure. These variables allow developers and operators to retrieve key details, such as resource names, IDs, and IP addresses, without needing to query the Azure portal or CLI.
```terraform
output "resourcegroupname" {
value = azurermresourcegroup.example
}
output "publicipaddress" {
value = azurermpublicip.example.ip_address
}
output "applicationgatewayid" {
value = azurermapplicationgateway.example.id
}
output "webapplicationfirewallpolicyid" {
value = azurermwebapplicationfirewallpolicy.example.id
}
```
These outputs are critical for post-deployment verification. By checking the public IP address, you can test connectivity to the Application Gateway. By verifying the resource IDs, you can ensure that the resources are correctly linked and that the WAF policy is applied. Testing the application gateway to make sure it works correctly is a mandatory step in the deployment process. This involves sending HTTP requests to the public IP address and verifying that the traffic is routed to the correct backend servers.
Conclusion
Deploying an Azure Application Gateway using Terraform is a structured process that leverages the power of Infrastructure as Code to manage complex network resources. From the basic definition of virtual networks and subnets to the advanced configuration of backend pools and WAF policies, Terraform provides the tools to build a scalable and secure web front end. The introduction of the AVM framework offers standardized modules for consumption, albeit with pre-release caveats that require careful attention to versioning and breaking changes. Meanwhile, the emergence of Application Gateway for Containers expands the scope of load balancing to containerized workloads, providing a unified approach to traffic management across different application architectures.
The dual-stack IP support, dynamic health probes, and integrated WAF v2 capabilities ensure that the Application Gateway remains at the forefront of web traffic management. By following best practices, such as using unique resource names, implementing proper authentication, and validating deployments through execution plans, organizations can confidently manage their Azure infrastructure. The ability to preview changes before deployment mitigates the risk of errors and ensures that the infrastructure aligns with the intended architecture. As Azure continues to evolve, with new features like dual-stack frontends and enhanced container integration, the Terraform ecosystem will continue to adapt, providing the necessary tools to implement these advancements with precision and reliability. The depth of configuration options, from simple round-robin distribution to complex cookie-based affinity and custom WAF rules, underscores the versatility of the Application Gateway as a cornerstone of modern Azure web architectures.