Create Azure Standard Load Balancer using Terraform
Step-00: Introduction
We are going to create Azure Standard Load Balancer Resources as part of this demo. azurerm_public_ip azurerm_lb azurerm_lb_backend_address_pool azurerm_lb_probe azurerm_lb_rule azurerm_network_interface_backend_address_pool_association Comment Azure Bastion Service as we already using Azure Bastion Host approach with Linux VM Step-01: c9-01-web-loadbalancer-input-variables.tf
Step-02: c9-02-web-loadbalancer-resource.tf
resource "azurerm_public_ip" "web_lbpublicip" { name = "${local.resource_name_prefix}-lbpublicip" resource_group_name = azurerm_resource_group.rg.name location = azurerm_resource_group.rg.location allocation_method = "Static" sku = "Standard" tags = local.common_tags }
resource "azurerm_lb" "web_lb" { name = "${local.resource_name_prefix}-web-lb" location = azurerm_resource_group.rg.location resource_group_name = azurerm_resource_group.rg.name sku = "Standard" frontend_ip_configuration { name = "web-lb-publicip-1" public_ip_address_id = azurerm_public_ip.web_lbpublicip.id } }
resource "azurerm_lb_backend_address_pool" "web_lb_backend_address_pool" { name = "web-backend" loadbalancer_id = azurerm_lb.web_lb.id }
resource "azurerm_lb_probe" "web_lb_probe" { name = "tcp-probe" protocol = "Tcp" port = 80 loadbalancer_id = azurerm_lb.web_lb.id resource_group_name = azurerm_resource_group.rg.name }
resource "azurerm_lb_rule" "web_lb_rule_app1" { name = "web-app1-rule" protocol = "Tcp" frontend_port = 80 backend_port = 80 frontend_ip_configuration_name = azurerm_lb.web_lb.frontend_ip_configuration[0].name backend_address_pool_id = azurerm_lb_backend_address_pool.web_lb_backend_address_pool.id probe_id = azurerm_lb_probe.web_lb_probe.id loadbalancer_id = azurerm_lb.web_lb.id resource_group_name = azurerm_resource_group.rg.name }
https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/network_interface_backend_address_pool_association
resource "azurerm_network_interface_backend_address_pool_association" "web_nic_lb_associate" { network_interface_id = azurerm_network_interface.web_linuxvm_nic.id ip_configuration_name = azurerm_network_interface.web_linuxvm_nic.ip_configuration[0].name backend_address_pool_id = azurerm_lb_backend_address_pool.web_lb_backend_address_pool.id } Step-03: c9-03-web-loadbalancer-outputs.tf
output "web_lb_public_ip_address" { description = "Web Load Balancer Public Address" value = azurerm_public_ip.web_lbpublicip.ip_address }
output "web_lb_id" { description = "Web Load Balancer ID." value = azurerm_lb.web_lb.id }
output "web_lb_frontend_ip_configuration" { description = "Web LB frontend_ip_configuration Block" value = [azurerm_lb.web_lb.frontend_ip_configuration] } Step-04: c8-04-AzureBastionService.tf
Comment Azure Bastion Service which takes longer time to create Resource Also we have the Azure Bastion Host Linux VM for us if required to login to Private VMs /*
resource "azurerm_subnet" "bastion_service_subnet" { name = var.bastion_service_subnet_name resource_group_name = azurerm_resource_group.rg.name virtual_network_name = azurerm_virtual_network.vnet.name address_prefixes = var.bastion_service_address_prefixes }
resource "azurerm_public_ip" "bastion_service_publicip" { name = "${local.resource_name_prefix}-bastion-service-publicip" location = azurerm_resource_group.rg.location resource_group_name = azurerm_resource_group.rg.name allocation_method = "Static" sku = "Standard" }
resource "azurerm_bastion_host" "bastion_host" { name = "${local.resource_name_prefix}-bastion-service" location = azurerm_resource_group.rg.location resource_group_name = azurerm_resource_group.rg.name
ip_configuration { name = "configuration" subnet_id = azurerm_subnet.bastion_service_subnet.id public_ip_address_id = azurerm_public_ip.bastion_service_publicip.id } } */
Step-01: c9-04-web-loadbalancer-inbound-nat-rules.tf
resource "azurerm_lb_nat_rule" "web_lb_inbound_nat_rule_22" {
name = "ssh-1022-vm-22"
protocol = "Tcp"
frontend_port = 1022
backend_port = 22
frontend_ip_configuration_name = azurerm_lb.web_lb.frontend_ip_configuration[0].name
resource_group_name = azurerm_resource_group.rg.name
loadbalancer_id = azurerm_lb.web_lb.id
}
resource "azurerm_network_interface_nat_rule_association" "web_nic_nat_rule_associate" { network_interface_id = azurerm_network_interface.web_linuxvm_nic.id ip_configuration_name = azurerm_network_interface.web_linuxvm_nic.ip_configuration[0].name nat_rule_id = azurerm_lb_nat_rule.web_lb_inbound_nat_rule_22.id }
Step-05: Execute Terraform Commands
terraform init
terraform validate
terraform plan
terraform apply -auto-approve Step-06: Verify Resources
- Azure Resource Group
- Azure Virtual Network
- Azure Subnets (Web, App, DB, Bastion)
- Azure Network Security Groups (Web, App, DB, Bastion)
- View the topology
- Verify Terraform Outputs in Terraform CLI
- Verify Network Interface created for Web Linux VM
- Verify Web Linux VM
- Verify Network Security Groups associated with VM (web Subnet NSG)
- View Topology at Web Linux VM -> Networking
- Verify if only private IP associated with Web Linux VM
- Verify Bastion Host VM Public IP
- Verify Bastion Host VM Network Interface
- Verify Bastion VM
- Verify Bastion VM -> Networking -> NSG Rules
- Verify Bastion VM Topology
- Connect to Bastion Host Linux VM ssh -i ssh-keys/terraform-azure.pem azureuser@ sudo su - cd /tmp ls
- terraform-azure.pem file should be present in /tmp directory
- Connect to Web Linux VM ssh -i ssh-keys/terraform-azure.pem azureuser@ sudo su - cd /var/log tail -100f cloud-init-output.log cd /var/www/html ls -lrt cd /var/www/html/app1 ls -lrt exit exit
- Verify Public IP Address for Standard Load Balancer
- Verify Standard Load Balancer (SLB) Resource
- Verify SLB - Frontend IP Configuration
- Verify SLB - Backend Pools
- Verify SLB - Health Probes
- Verify SLB - Load Balancing Rules
- Verify SLB - Insights
- Verify SLB - Diagnose and Solve Problems
http:// http:///app1/index.html http:///app1/metadata.html Step-07: Delete Resources
terraform destroy [or] terraform apply -destroy -auto-approve
rm -rf .terraform* rm -rf terraform.tfstate* Step-08: Additional Cautionary Note
When your Linux VM NIC is associated with Security Group, the deletion criteria has issues with Azure Provider Due to that below related errors might come. This is provider related bug. In our usecase we didn't associate any NSG to VMs directly, we are using subnet level NSG, so this error will not come for us. Even this error comes when we associate NSG with VM NIC, just go to Azure Portal Console and delete that resource group so that all associated resources will be deleted. azurerm_public_ip.bastion_host_publicip: Still destroying... [id=/subscriptions/82808767-144c-4c66-a320-...Addresses/hr-dev-bastion-host-publicip, 10s elapsed] azurerm_subnet.bastionsubnet: Still destroying... [id=/subscriptions/82808767-144c-4c66-a320-...vnet/subnets/hr-dev-vnet-bastionsubnet, 10s elapsed] azurerm_subnet.bastionsubnet: Destruction complete after 10s azurerm_public_ip.bastion_host_publicip: Destruction complete after 12s ╷ │ Error: Error waiting for removal of Backend Address Pool Association for NIC "hr-dev-linuxvm-nic" (Resource Group "hr-dev-rg"): Code="OperationNotAllowed" Message="Operation 'startTenantUpdate' is not allowed on VM 'hr-dev-linuxvm1' since the VM is marked for deletion. You can only retry the Delete operation (or wait for an ongoing one to complete)." Details=[] │