Skip to main content

Automating Infrastructure Deployment with Terraform

1. AUTOMATING VIRTUAL MACHINE DEPLOYMENT -
Create a Terraform Script to work in GCP/Azure such that it creates a specified number of VM(s) 

In this scenario, You can specify the instance types, OS images, networking configurations, and any additional settings required.

BEST PRACTISE TO FOLLOW FOR THIS SCENARIO

Use Version Control: Store your Terraform code in a version control system like Git. This allows you to track changes, collaborate with others, and roll back to previous configurations if needed.

Organize Your Code: Structure your Terraform code into modules, each responsible for specific resources or components. This promotes code reusability and makes it easier to manage complex infrastructures.

Keep Secrets Secure: Avoid hardcoding sensitive information like passwords or API keys directly in your Terraform code. Instead, use environment variables, external data sources, or cloud provider-specific secret management tools to store and retrieve secrets securely.

Use Variables and Input Parameters: Define variables for your Terraform configuration to make it more flexible and adaptable. Use input parameters to allow users to customize resource settings during deployment.

Leverage Provider Plugins: Terraform has provider plugins for various cloud platforms. Use the latest versions of these providers to access the most up-to-date features and improvements.

Use Remote State Storage: Store your Terraform state in a remote backend like GCP Storage Bucket or Azure Blob Storage. This ensures that the state file is centralized, accessible by the team, and secure.

Apply Least Privilege Principle: When authenticating to cloud providers, use service accounts, managed identities, or roles with the least privileges required for your Terraform scripts.

Use Terraform Modules: Create reusable Terraform modules for common resource configurations. This not only simplifies your main configuration but also enhances maintainability and promotes consistency across projects.

Dependency Management: Use module dependencies to manage the order of resource creation. This helps avoid conflicts and errors when resources depend on others.

Enable Logging and Monitoring: Enable logging and monitoring for your cloud resources to track changes, monitor performance, and respond to issues promptly.

Plan and Apply Separation:  Distinguish the Terraform phases of planning and implementing. Before using terraform apply to implement changes, perform a terraform plan to review the changes.

Version Pinning: Pin the version of the Terraform providers and modules you are currently using to stop unforeseen changes when newer versions are released.

Testing: To ensure that your infrastructure is deployed properly, think about implementing automated testing for your Terraform configurations using tools like Terratest.

Documentation: Always write up your Terraform code, and give detailed guidelines for using and maintaining the infrastructure.

STEPS :

  1. Using best practice, make your directory as (You can use VS Code or any other editor for smooth operations) :

    Figure 1 - Directory Structure



  2.  Write main.tf file for GCP/Azure as :
    GCP (main.tf) -->
    locals {
      num_vms = var.num_vms
    }

    module "gcp_vms" {
      source = "./modules/gcp"

      vm_count       = local.num_vms
      project_id     = var.gcp_project_id
      region         = var.gcp_region
      zone           = var.gcp_zone
      machine_type   = var.gcp_machine_type
    }

    Azure (main.tf) -->
    module "azure_vms" {
      source = "./modules/azure"

      vm_count         = local.num_vms
      location         = var.azure_location
      resource_group   = var.azure_resource_group
      vm_size          = var.azure_vm_size
      admin_username   = var.azure_admin_username
      image_publisher  = var.azure_image_publisher
      image_offer      = var.azure_image_offer
      image_sku        = var.azure_image_sku
      image_version    = var.azure_image_version
    }

  3. Define vars.tf file to make variables. They will fetch the value during run time via console. You can also use $terraform apply --var-file="/path/FILENAME.tfvars" if your variables file is in another directory.

    variable "num_vms" {
      description = "Number of VMs to create"
    }

    # GCP Variables (vars.tf) -->
    variable "gcp_project_id" {
      description = "GCP Project ID"
    }

    variable "gcp_region" {
      description = "GCP Region"
      default     = "us-central1"
    }

    variable "gcp_zone" {
      description = "GCP Zone"
      default     = "us-central1-a"
    }

    variable "gcp_machine_type" {
      description = "GCP VM Machine Type"
      default     = "n1-standard-1"
    }

    variable "num_vms" {
      description = "Number of VM to make"
      default     = "1"
    }

    # Azure Variables (vars.tf) -->
    variable "azure_location" {
      description = "Azure Region"
      default     = "East US"
    }

    variable "azure_resource_group" {
      description = "Azure Resource Group"
    }

    variable "azure_vm_size" {
      description = "Azure VM Size"
      default     = "Standard_DS1_v2"
    }

    variable "azure_admin_username" {
      description = "Azure VM Admin Username"
      default     = "adminuser"
    }

    variable "azure_image_publisher" {
      description = "Azure VM Image Publisher"
      default     = "Canonical"
    }

    variable "azure_image_offer" {
      description = "Azure VM Image Offer"
      default     = "UbuntuServer"
    }

    variable "azure_image_sku" {
      description = "Azure VM Image SKU"
      default     = "18.04-LTS"
    }

    variable "azure_image_version" {
      description = "Azure VM Image Version"
      default     = "latest"
    }
     
  4. Now, make another file as provider_gcp.tf / provider_azure.tf as per your respective Cloud.

    provider_gcp.tf -->
    provider "google"
    {
    credentials = file("path/to/gcp_credentials.json")
    }

    provider_azure.tf -->
    provider "azurerm"
    {
    features{}
    }


  5. Now, make a gcp.tfvars / azure.tfvars file with the content as below. This .tfvars file helps us initialize variables, declaratively for Terraform Configuration.
    Another way is to pass value in Terminal  using Environment Variables, use TF_VAR_[variable_name] = "value" to pass value.
    $TF_VAR_instance_type="t2.small"
    $ terraform plan

    We can also pass variables in Command Line using:
    terraform plan -var="instance_type=t2.large"
    But, let us suppose we have to pass multiple values, then using .tfvars file comes in handy.
    eg:
    gcp.tfvars -->
    num_vms = 2
    gcp_project_id = "your-gcp-project-id"

    azure.tfvars -->
    num_vms = 2
    azure_resource_group = "your-azure-resource-group-name"

    Now let us suppose, we need same configuration with different values, then we can create multiple .tfvars file like prod.tfvars, staging.tfvars and so on. We can select .tfvars file when using terraform plan/apply using -var-file="" flag. eg : $ terraform plan -var-file=”prod.tfvars”

    Figure 2 - Precedence of Variables




  6.  Now create a main.tf for GCP/Azure (Copy only GCP or Azure's code). If you want to create main.tf for both Cloud providers(GCP & Azure), make modules(folders) in respective directories:
    GCP --->   project/modules/gcp/main.tf
    AZURE --->   project/modules/azure/main.tf


GCP (main.tf) -->
variable "vm_count" {
  description = "Number of VMs to create"
}

variable "project_id" {
  description = "GCP Project ID"
}

variable "region" {
  description = "GCP Region"
}

variable "zone" {
  description = "GCP Zone"
}

variable "machine_type" {
  description = "GCP VM Machine Type"
}

resource "google_compute_instance" "gcp_vms" {
  count        = var.vm_count
  name         = "gcp-vm-${count.index + 1}"
  machine_type = var.machine_type
  zone         = var.zone
  project      = var.project_id

  boot_disk {
    initialize_params {
      image = "debian-cloud/debian-10"
    }
  }

  network_interface {
    network = "default"
  }
}


Azure (main.tf) -->
variable "vm_count" {
  description = "Number of VMs to create"
}

variable "location" {
  description = "Azure Region"
}

variable "resource_group" {
  description = "Azure Resource Group"
}

variable "vm_size" {
  description = "Azure VM Size"
}

variable "admin_username" {
  description = "Azure VM Admin Username"
}

variable "image_publisher" {
  description = "Azure VM Image Publisher"
}

variable "image_offer" {
  description = "Azure VM Image Offer"
}

variable "image_sku" {
  description = "Azure VM Image SKU"
}

variable "image_version" {
  description = "Azure VM Image Version"
}

resource "azurerm_virtual_machine" "azure_vms" {
  count = var.vm_count
  name  = "azure-vm-${count.index + 1}"
  location = var.location
  resource_group_name = var.resource_group

  vm_size = var.vm_size

  storage_image_reference {
    publisher = var.image_publisher
    offer     = var.image_offer
    sku       = var.image_sku
    version   = var.image_version
  }

  storage_os_disk {
    name              = "osdisk-${count.index + 1}"
    caching           = "ReadWrite"
    create_option     = "FromImage"
    managed_disk_type = "Standard_LRS"
  }

  network_interface {
    name    = "nic-${count.index + 1}"
    primary = true

    ip_configuration {
      name      = "ipconfig-${count.index + 1}"
      subnet_id = azurerm_subnet.example.id
      private_ip_address_allocation = "Dynamic"
    }
  }

  os_profile {
    computer_name  = "azure-vm-${count.index + 1}"
    admin_username = var.admin_username
  }

  os_profile_linux_config {
    disable_password_authentication = true
  }
}

Remember to replace the placeholders (your-gcp-project-id, your-azure-resource-group-name, path/to/gcp_credentials.json) with your actual GCP project ID, Azure resource group name, and the path to your GCP service account key file, respectively.

With this setup, you can now use the following commands to deploy the infrastructure:


  1. terraform init (to initialize the Terraform configuration)
  2. terraform apply -var-file=gcp.tfvars (to create VMs in GCP)
  3. terraform apply -var-file=azure.tfvars (to create VMs in Azure)

    Figure 3 - Final Directory Structure




    Get complete code here :-> GitHub Repo





Comments

  1. Makes it easy to deploy IaaS solutions, more TF content wuld be useful

    ReplyDelete

Post a Comment