How to Use Terraform for Infrastructure Automation
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
Local-exec Provisioner
Remote-exec Provisioner
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.
- If you don't create or switch to another workspace, all actions will happen in the
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
, andprod
workspaces.
- For example, you can have
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......
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.