I recently put together a small but realistic lab to deploy an Azure Application Gateway using Terraform.
The goal wasn’t to build anything clever or cutting-edge, but to create something simple, repeatable, and close to how this would be done in a real Azure environment.
The setup uses an Application Gateway with a public frontend IP and two backend virtual machines running NGINX.
Traffic comes in from the internet, hits the Application Gateway, and is then load-balanced across the backend VMs over their private IP addresses.
Everything is deployed declaratively using Terraform.
What made this exercise useful is that it mirrors the sort of work you actually do in enterprise Azure environments, rather than following a portal click-through or a toy example that hides important details.
The first step was creating the network properly…
The virtual network was defined with a dedicated subnet for the Application Gateway and a separate subnet for backend servers.
This separation is not optional with Application Gateway, and Terraform forces you to be explicit about it, which is a good thing.
It makes the architecture clear and avoids the kind of accidental designs you can end up with when clicking around the portal.
Once the network was in place, the Application Gateway itself was deployed using the Standard_v2 SKU.
A public IP was attached to the frontend, an HTTP listener was configured on port 80, and a basic routing rule was created. One important point here was explicitly defining a modern TLS policy.
Azure has tightened its defaults, and Terraform will surface those requirements quickly if you rely on implicit behaviour.
This is a good example of how infrastructure as code exposes platform changes that the portal often hides.
After the gateway was up, two Linux virtual machines were deployed into the backend subnet.
These VMs don’t have public IP addresses, which is how you’d normally design this in a real environment.
Instead of manually logging in to configure them, NGINX was installed using VM extensions. Each VM was configured to return its own name in the web response, which makes it very easy to see traffic being load-balanced.
The final step was wiring the backend virtual machines into the Application Gateway backend pool.
This turned out to be one of the most interesting parts of the lab.
The AzureRM Terraform provider has some quirks around how backend addresses are defined, and getting this right required understanding the exact schema Terraform expects rather than guessing based on the portal view.
Once the backend pool was populated with the VMs’ private IP addresses, the gateway updated in place and traffic started flowing immediately.
Testing the setup was as simple as copying the Application Gateway’s public IP into a browser and refreshing the page.
Seeing the response alternate between the two backend servers confirmed that everything was working end-to-end: networking, gateway configuration, backend health, and load balancing.
What makes this exercise valuable is not the size of the deployment, but what it demonstrates.
It shows how to structure Terraform projects cleanly, how to deploy Azure networking components in the correct order, how to deal with real provider edge cases, and how to build infrastructure that is boring, predictable, and easy to tear down when you’re done.
Cost control was also part of the process…
Resources were destroyed and recreated cleanly as needed, which is exactly how you should work in non-production subscriptions.
This kind of lab is far more useful than chasing lots of small service-specific demos.
It reflects real Azure networking patterns, aligns well with AZ-700 concepts, and reinforces the idea that good cloud engineering is about clarity and restraint rather than complexity.
Everything in this project can be version-controlled, reviewed, reproduced, and safely removed, which is ultimately the point of using Terraform in the first place.
If you’re learning Azure networking or Terraform, building something like this from scratch is a great way to move past theory and into practical understanding.
It’s not flashy, but it’s the kind of foundation that real systems are built on.