Build Your Scalable Azure Infrastructure in Minutes with Terraform

Build Your Scalable Azure Infrastructure in Minutes with Terraform

Introduction:

In today's rapidly evolving cloud landscape, businesses are constantly seeking ways to streamline and optimize their infrastructure management processes. With the increasing adoption of cloud platforms like Microsoft Azure, the demand for efficient and scalable infrastructure provisioning solutions has never been greater.

In this blog post, we delve into the realm of Terraform, a powerful infrastructure as code (IaC) tool that empowers organizations to declaratively define and manage their cloud infrastructure. Specifically, we explore how Terraform can be utilized to orchestrate the creation of various Azure resources, ranging from resource groups and virtual networks to virtual machines and beyond.

Our objective is to provide a comprehensive guide on leveraging Terraform for Azure infrastructure deployment. We'll walk through the essential commands and best practices for initializing Terraform projects, validating configurations, generating execution plans, applying changes, and responsibly tearing down resources when they're no longer needed.

Whether you're a seasoned cloud architect looking to optimize your infrastructure provisioning workflows or a newcomer intrigued by the potential of Terraform, this guide aims to equip you with the knowledge and tools needed to Terraform your way to scalable Azure infrastructure. Let's embark on this journey to unlock the full potential of Azure with Terraform as our guiding beacon.

# Azure Provider source and version being used
terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "=3.0.0"
    }
  }
}

# Configure the Microsoft Azure Provider
provider "azurerm" {
  features {}
}

# To create the resource group named AZ-900
resource "azurerm_resource_group" "rc_group" {
  name     = "AZ-900"
  location = "east us"
  tags = {
    environment = "dev"
  }
}

This section initializes Terraform with the required Azure provider and version. It also sets up the Azure provider configuration. Then, it defines a resource group named "AZ-900" in the "east us" region with a "dev" tag for environment.

# Generate a random string for use in the domain name label
resource "random_string" "fqdn" {
  length  = 6
  special = false
  upper   = false
  numeric = false
}

This generates a random string of length 6 for use in creating a domain name label.

# Create a virtual network
resource "azurerm_virtual_network" "enchanted-siddhant-vn" {
  name                = "enchanted-virtual-network"
  resource_group_name = azurerm_resource_group.rc_group.name
  location            = azurerm_resource_group.rc_group.location
  address_space       = ["10.0.0.0/16"]

  tags = {
    environment = "dev"
  }
}

This creates a virtual network named "enchanted-virtual-network" within the previously defined resource group. It uses the CIDR block "10.0.0.0/16" for IP addressing and tags it with the "dev" environment.

# Create a subnet within the virtual network
resource "azurerm_subnet" "enchanted_subnet" {
  name                 = "enchanted_subnet"
  resource_group_name  = azurerm_resource_group.rc_group.name
  virtual_network_name = azurerm_virtual_network.enchanted-siddhant-vn.name
  address_prefixes     = ["10.0.1.0/24"]
}

This creates a subnet named "enchanted_subnet" within the previously created virtual network. It uses the CIDR block "10.0.1.0/24" for IP addressing.

# Create a public IP address for lb
resource "azurerm_public_ip" "enchanted_pia" {
  name                    = "enchanted-pia"
  location                = azurerm_resource_group.rc_group.location
  resource_group_name     = azurerm_resource_group.rc_group.name
  allocation_method       = "Static"
  idle_timeout_in_minutes = 30
  domain_name_label       = random_string.fqdn.result

  tags = {
    environment = "dev"
  }
}

This creates a public IP address named "enchanted-pia" with a static allocation method and a domain name label generated from the random string. It's associated with the previously defined resource group.

# Create a load balancer
resource "azurerm_lb" "enchanted-lb" {
  name                = "Enchanted-LoadBalancer"
  location            = azurerm_resource_group.rc_group.location
  resource_group_name = azurerm_resource_group.rc_group.name

  frontend_ip_configuration {
    name                 = "PublicIPAddress"
    public_ip_address_id = azurerm_public_ip.enchanted_pia.id
  }
}

This creates a load balancer named "Enchanted-LoadBalancer" within the previously defined resource group. It's configured with a frontend IP configuration using the previously created public IP address.

# Create a backend address pool for the load balancer
resource "azurerm_lb_backend_address_pool" "backend-address-pool" {
  loadbalancer_id = azurerm_lb.enchanted-lb.id
  name            = "BackEndAddressPool"
}

This creates a backend address pool named "BackEndAddressPool" for the load balancer, which is associated with the previously created load balancer.

# Create a probe for the load balancer
resource "azurerm_lb_probe" "lb-probe" {
  loadbalancer_id = azurerm_lb.enchanted-lb.id
  name            = "ssh-running-probe"
  port            = 22
}

This creates a probe named "ssh-running-probe" for the load balancer to check if port 22 is reachable.

# Create a rule for the load balancer
resource "azurerm_lb_rule" "lb-rule" {
  loadbalancer_id                = azurerm_lb.enchanted-lb.id
  name                           = "LBRule"
  protocol                       = "Tcp"
  frontend_port                  = 80
  backend_port                   = 80
  frontend_ip_configuration_name = "PublicIPAddress"
}

This creates a rule named "LBRule" for the load balancer, which forwards traffic from port 80 on the frontend to port 80 on the backend.

# Create a virtual machine scale set
resource "azurerm_linux_virtual_machine_scale_set" "enchanted-vmss" {
  name                = "enchanted-vmss"
  resource_group_name = azurerm_resource_group.rc_group.name
  location            = azurerm_resource_group.rc_group.location
  sku                 = "Standard_B1s"
  instances           = 2
  admin_username      = "siddhant"

  admin_ssh_key {
    username   = "siddhant"
    public_key = file("~/.ssh/id_rsa.pub")
  }

  source_image_reference {
    publisher = "Canonical"
    offer     = "0001-com-ubuntu-server-jammy"
    sku       = "22_04-lts"
    version   = "latest"
  }

  os_disk {
    storage_account_type = "Standard_LRS"
    caching              = "ReadWrite"
  }

  network_interface {
    name    = "enchanted-nic"
    primary = true

    ip_configuration {
      name      = "internal"
      primary   = true
      subnet_id = azurerm_subnet.enchanted_subnet.id
    }
  }
}

This creates a virtual machine scale set named "enchanted-vmss" with 2 instances, using Ubuntu 22.04 LTS as the operating system. Each instance is associated with a network interface within the previously created subnet.

# Create a public IP address for the virtual machine
resource "azurerm_public_ip" "enchanted_pi" {
  name                = "enchanted-public-ip"
  location            = azurerm_resource_group.rc_group.location
  resource_group_name = azurerm_resource_group.rc_group.name
  allocation_method   = "Static"
  domain_name_label   = "${random_string.fqdn.result}-ssh"
}

This creates a public IP address with a static allocation method for the virtual machine, appending "-ssh" to the domain name label generated earlier.

# Create a network interface for the virtual machine
resource "azurerm_network_interface" "enchanted-nic" {
  name                = "enchanted-nic"
  location            = azurerm_resource_group.rc_group.location
  resource_group_name = azurerm_resource_group.rc_group.name

  ip_configuration {
    name                          = "internal"
    subnet_id                     = azurerm_subnet.enchanted_subnet.id
    private_ip_address_allocation = "Dynamic"
    public_ip_address_id          = azurerm_public_ip.enchanted_pi.id
  }
  tags = {
    environment = "dev"
  }
}

This creates a network interface named "enchanted-nic" for the virtual machine, with a dynamic private IP address allocation and associating it with the previously created subnet and public IP address.

# Create a virtual machine
resource "azurerm_linux_virtual_machine" "enchanted-vm" {
  name                = "siddhant-enchanted-vm"
  resource_group_name = azurerm_resource_group.rc_group.name
  location            = azurerm_resource_group.rc_group.location
  size                = "Standard_B1s"
  admin_username      = "siddhant"
  network_interface_ids = [
    azurerm_network_interface.enchanted-nic.id,
  ]

  admin_ssh_key {
    username   = "siddhant"
    public_key = file("~/.ssh/id_rsa.pub")
  }

  os_disk {
    caching              = "ReadWrite"
    storage_account_type = "Standard_LRS"
  }

  source_image_reference {
    publisher = "Canonical"
    offer     = "0001-com-ubuntu-server-jammy"
    sku       = "22_04-lts"
    version   = "latest"
  }
}

Finally, this creates a virtual machine named "siddhant-enchanted-vm" with Ubuntu 22.04 LTS as the operating system, using the network interface created earlier.

This Terraform script automates the creation of various Azure resources like resource groups, virtual networks, subnets, load balancers, virtual machine scale sets, public IP addresses, and virtual machines, providing a scalable and reproducible infrastructure setup.

After writing all the above terraform script lets understand some commands.

  1. terraform init: This command initializes a Terraform working directory by downloading the necessary provider plugins and modules defined in the configuration files. It prepares the directory for Terraform operations.

     terraform init
    
  2. terraform validate: This command validates the syntax and configuration of the Terraform files in the working directory. It checks for any errors or warnings in the configuration before applying changes.

     terraform validate
    
  3. terraform plan: This command generates an execution plan based on the current state of the infrastructure and the desired state defined in the Terraform configuration files. It shows what actions Terraform will take to achieve the desired state, such as creating, modifying, or destroying resources.

     terraform plan
    
  4. terraform apply: This command applies the changes described in the execution plan generated by the terraform plan command. It creates, updates, or deletes resources as necessary to achieve the desired state defined in the Terraform configuration files.

     terraform apply -auto-approve
    
  5. terraform destroy: This command destroys all the resources defined in the Terraform configuration files. It removes all infrastructure provisioned by Terraform, helping to clean up resources that are no longer needed or to tear down an environment entirely.

     terraform destroy -auto-approve
    

These commands together form the core workflow of using Terraform to manage infrastructure as code, from initialization and validation to planning, applying changes, and finally, destroying resources when necessary.

Subscribe for more!!