How to Use Terraform for Infrastructure Automation

Amitt AshokAmitt Ashok
5 min read

Let's understand what a Provisioner is.

  • In Terraform, a provisioner is a tool used to run scripts or commands on a local or remote resource when it's being created or destroyed.

  • Provisioners are often used to set up a system, configure software, or perform extra tasks after the main infrastructure is ready.

Type of Provisioner

  1. Local-exec Provisioner

  2. Remote-exec Provisioner

  3. File Provisioner

Local-exec Provisioner:

  • Use it to run commands locally on the machine that is running Terraform.

  • To use a provisioner, you need to add a provisioner block inside the instance block.

      resource "aws_instance" "my-server" {
        ami           = "ami-0522ab6e1ddcc7055"
        instance_type = "t2.micro"
        key_name = "server-new"
          tags = {
            Name ="Server_1"
            }
       provisioner "local-exec" {
          command = "echo 'Hello, World!'"
        }
      }
    

    Remote-exec Provisioner:

    • Use it to run commands on a remote resource using SSH or WinRM.

        resource "aws_instance" "new-server" {
            ami = "ami-0522ab6e1ddcc7055"
            instance_type = "t2.micro"
            key_name = aws_key_pair.mykey.key_name
            vpc_security_group_ids = [aws_security_group.appsg.id]
            subnet_id = aws_subnet.app-subnet.id
      
        provisioner "remote-exec" {
            inline = [ 
            "echo 'Hello from remote instance'",
            "sudo apt update -y",  # Update package lists (for Ubuntu)
            "sudo apt-get install -y python3-pip",  # Install Python3-pip
            "sudo apt install -y python3-venv",  # Install python3-venv
            "cd /home/ubuntu",  # Navigate to the home directory
            "python3 -m venv venv",  # Create a virtual environment
            "source venv/bin/activate && pip install Flask",  # Activate the virtual environment and install Flask
            "source venv/bin/activate && nohup python app.py &", # Run app app in background
            ]
             }
        }
      

      File Provisioner:

      • Use it to copy files or directories from the local machine to the remote resource.

          resource "aws_instance" "new-server" {
              ami = "ami-0522ab6e1ddcc7055"
              instance_type = "t2.micro"
              key_name = aws_key_pair.mykey.key_name
              vpc_security_group_ids = [aws_security_group.appsg.id]
              subnet_id = aws_subnet.app-subnet.id
        
          connection {
              type        = "ssh"
              user        = "ubuntu"  # Username for your EC2 instance
              private_key = file ("/home/amitt-ashok/.ssh/id_rsa")  #Path to private key
              host        = self.public_ip
            }
        

Deploy a Flask application on an EC2 instance

Let's now use this knowledge to Deploy a simple Flask application on an EC2 instance using Terraform.

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}
provider "aws" {
    region = "ap-south-1"
}
#********* AWS Provider ******************#
variable "cidr" {
    default = "10.0.0.0/16"

}
resource "aws_key_pair" "mykey" {
    key_name = "terraform-keys"
    public_key = file("/home/amitt-ashok/.ssh/id_rsa.pub") # Path of public key in loacl machine
}
#********* AWS key pair *******************#

resource "aws_vpc" "app-vpc" {
  cidr_block       = var.cidr
  instance_tenancy = "default"

  tags = {
    Name = "my-aws-login"
  }
}
resource "aws_subnet" "app-subnet" {
  vpc_id     = aws_vpc.app-vpc.id
  cidr_block = "10.0.1.0/24"
  availability_zone = "ap-south-1a"
  map_public_ip_on_launch = true
    tags = {
    Name = "app-vpc-subent"
  }
}
resource "aws_internet_gateway" "app-gw" {
  vpc_id = aws_vpc.app-vpc.id
  tags = {
    Name = "main"
  }
}
resource "aws_route_table" "app-rt" {
  vpc_id = aws_vpc.app-vpc.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.app-gw.id
  }

resource "aws_route_table_association" "app-rt-1" {
    subnet_id = aws_subnet.app-subnet.id
    route_table_id = aws_route_table.app-rt.id
}
resource "aws_security_group" "appsg" {
    name   = "my-app"
    vpc_id = aws_vpc.app-vpc.id

    ingress {     # for allowing traffic 
        description = "HTTP from anywhere"
        from_port   = 80
        to_port     = 80
        protocol    = "tcp"
        cidr_blocks = ["0.0.0.0/0"]
    }


    ingress {
        description = "SSH from anywhere"
        from_port   = 22
        to_port     = 22
        protocol    = "tcp"
        cidr_blocks = ["0.0.0.0/0"]
    }
    egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

    tags = {
        Name = "app-sg"
    }
   }
#************* VPC block *****************#
resource "aws_instance" "new-server" {
    ami = "ami-0522ab6e1ddcc7055"
    instance_type = "t2.micro"
    key_name = aws_key_pair.mykey.key_name
    vpc_security_group_ids = [aws_security_group.appsg.id]
    subnet_id = aws_subnet.app-subnet.id

connection {
    type        = "ssh"
    user        = "ubuntu"  # Replace with the appropriate username for your EC2 instance
    private_key = file ("/home/amitt-ashok/.ssh/id_rsa")  # Path to private key
    host        = self.public_ip
  }

provisioner "file" {
    source      = "/home/amitt-ashok/flask-app/app.py"  # Path to local file
    destination = "/home/ubuntu/app.py"  # Path on the remote instance where file will save
  }
provisioner "remote-exec" {
    inline = [ 
    "echo 'Hello from remote instance'",
    "sudo apt update -y",  # Update package lists (for Ubuntu)
    "sudo apt-get install -y python3-pip",  # Install Python3-pip
    "sudo apt install -y python3-venv",  # Install python3-venv
    "cd /home/ubuntu",  # Navigate to the home directory
    "python3 -m venv venv",  # Create a virtual environment
    "source venv/bin/activate && pip install Flask",  # Activate the virtual environment and install Flask
    "source venv/bin/activate && nohup python app.py &",
    ]
     }
} 
# ************** EC2 instance Block ***************#

Workspaces:

Why do we need workspaces? Imagine a team using dev, stage, and prod environments with the same configuration file. Each team needs different tags, instance types, and S3 buckets. We create a module with a .tfvars file and ask each team to make changes as needed.

If someone from the dev team updates the configuration, a .tfstate file is created. Later, if someone from the stage team makes changes, it will modify the existing .tfstate file. Ideally, we want to create a new configuration for each environment.

To avoid this conflict, we use workspaces. Workspaces help eliminate this issue and allow each team to collaborate properly.

In Terraform, a workspace is a separate environment where the Terraform state is stored and managed. Workspaces lets you handle multiple environments (like development, staging, and production) with the same Terraform configuration files.

Key Concepts of Workspaces

Default Workspace:

    • Every Terraform setup has a default workspace called default. This workspace is made automatically when you start a new Terraform directory.

      • If you don't create or switch to another workspace, all actions will happen in the default workspace.

Multiple Workspaces:

    • You can create multiple workspaces for different environments or tasks. Each workspace has its state file, letting you manage separate environments on your own.

      • For example, you can have dev, staging, and prod workspaces.

Basic Commands for Workspace Management

To create a terraform workspace

terraform workspace new dev with the same, you can create others also

Switch to a particular workspace

terraform workspace select dev

To check which workspace you are in now

terraform workspace show

You can see that with workspaces, Terraform manages a separate state file for each environment.

── main.tf
├── module
│   └── ec2-instance
│       └── main.tf
├── terraform.tfstate.d
│   ├── dev
│   │   ├── terraform.tfstate
│   │   └── terraform.tfstate.backup
│   ├── production
│   │   ├── terraform.tfstate
│   │   └── terraform.tfstate.backup
│   └── stage
│       ├── terraform.tfstate
│       └── terraform.tfstate.backup
└── terraform.tfvars

In summary, mastering provisioners and workspaces in Terraform empower you to manage infrastructure with flexibility and precision. These tools are key to maintaining clear environment separation and streamlining your deployment processes.

What are your thoughts on using provisioners and workspaces in Terraform? Share your experiences or ask questions in the comments below!

See you Soon......

0
Subscribe to my newsletter

Read articles from Amitt Ashok directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Amitt Ashok
Amitt Ashok

Hey Hi there, Amit here... I love to explore new things and learn new technology.