Terraform is a Infrastructure As Code (IAC) tool used to provisioning and managing infrastructure on-premises and in the cloud .Let’s understand terraform with an example use-case by automating the AWS infrastructure.
Pre-requisites :
Basics of aws service provider, ec2, VPC, security group, Internet gateway, Router.
Knowledge on key-pairs in aws.
Aws command line.
Basics of docker and how to run a container.
Install Terraform :
Follow easy steps in video to install terraform.
Terraform terminologies :
Providers
Resources
Commands -[init, plan, apply]
Variables
Providers :
Providers in terraform manages the resources in service providers like AWS, Azure, GCP etc.
provider "aws" {
access_key="GET-ACCESS-KEY-FROM-AWS"
secret_key="GET-SECRET-KEY-FROM-AWS"
}
In the above code, provider is AWS, which facilitates the required resources. It takes two required parameters, access_key and secret_key which will be provided by the service providers.
Resources :
Resources are the primary building blocks that represent the infrastructure or the service you want to create, manage, or, update.
resource "aws_vpc" "my-app-vpc" {
cidr_block = "10.0.0.0/24"
tags = {
Name="my-app-vpc"
}
}
In the above code, VPC with name ‘my-app-vpc‘ is created within IP-address range ‘10.0.0.0/24‘ .
Commands :
init - terraform init is used to initialize the new or existing configuration. It prepares terraform files.
plan - terraform plan is used to create execution plan, it shows the operations performed by terraform to achieve desired state.
apply - terraform apply is used to apply the changes in described in configuration files to transform from current state to desired state.
Variables :
Variables are used to hold value of any entity. They are reusable, and eliminates hard-coding of values in the configuration file.
variable "my-ip" {
description = "My IP address"
}
In the above code the variable ‘my-ip‘ is declared with a description ‘My IP address‘, the value to it is assigned in another file with extension .tfvars. There is no need of importing the file into main.tf.
#variables.tfvars
my-ip="171.48.82.145"
Provisioning EC2 [AWS] :
Steps :
Create custom VPC
Create custom subnet
Create Route Table & Internet Gateway
Create Firewall (Security group)
Provision EC2 instance
Deploy nginx Docker container
Refer this Github repository and make some required changes to achieve desired output.
1. Create custom VPC :
Use the ‘aws_vpc’ resource to create a VPC, cidr_block is required in the resource definition which denotes the range of IP addresses for the VPC.
variable "VPC-cidr-block" {
description = "value for VPC-cidr-block"
}
#Value in variables.tfvars
VPC-cidr-block="10.0.0.0/16"
#Creating a VPC in your aws
resource "aws_vpc" "my-app-vpc" {
cidr_block = var.VPC-cidr-block
tags = {
Name="my-app-vpc"
}
}
VPC resource is defined with a name my-app-vpc and cidr_block=’10.0.0.0/16’ .
2. Create custom subnet :
Use aws_subnet to create a subnet, it has three required parameters vpc_id, cidr_block & availability_zone.
vpc_id denotes to which vpc the current subnet belongs to.
cidr_block specifies what range of IP addresses are allocated to this subnet.
availability_zone specifies where the subnet is created, in our case the subnet is created in ‘us-east-1a‘ availability zone.
#variables
variable "subnet-cidr-block" {
description = "value for subnet-cidr-block"
}
variable "availability_zone" {
description = "Availability zone for subnet-1"
}
#Values for variables in variables.tfvars
subnet-cidr-block="10.0.10.0/24"
availability_zone = "us-east-1a"
#Create a subnet inside the VPC
resource "aws_subnet" "my-app-subnet-1" {
vpc_id = aws_vpc.my-app-vpc.id
cidr_block = var.subnet-cidr-block
availability_zone = var.availability_zone
tags = {
Name="Subnet-1-my-app"
}
}
Subnet resource is defined by name ‘Subnet-1-my-app‘ with all the required parameters.
3. Create Route Table & Internet Gateway :
Use ‘aws_internet_gateway’ resource to create a internet gateway , it has one required attribute vpc_id, which is used to denote the id of the VPC for which the internet gateway is created.
#Internet gateway to handle the incoming and outgoing routes on subnets
resource "aws_internet_gateway" "my-app-igw" {
vpc_id = aws_vpc.my-app-vpc.id
tags={
Name="my-app-igw"
}
}
An internet gateway with name ‘my-app-igw‘ is created for the VPC in our infrastructure.
Use ‘aws_route_table‘ resource to create a route table for the subnet, it has three required parameters vpc_id, cidr_block, gateway_id.
vpc_id defines on which VPC the route table has to be created.
cidr_block defines range of IP addresses to which the route applies.
gateway_id specifies which gateway handles the route.
#Route table for the subnet in vpc
resource "aws_route_table" "my-app-route-table" {
vpc_id = aws_vpc.my-app-vpc.id
route {
cidr_block="0.0.0.0/0"
gateway_id=aws_internet_gateway.my-app-igw.id
}
tags = {
Name="my-app-route-table"
}
}
Route table for subnet in VPC with name ‘my-app-route-table‘ is created.
4. Create secuity group :
Use the resource ‘aws_security_group‘ to create a security group, it has two required attributes ingress & egress to define the inbound and outbound rules in the context of security group.
#Variable
variable "my-ip" {
description = "My IP address"
}
#Variable value
my-ip='171.48.82.145/32'
#Security group Creation
resource "aws_security_group" "my-app-sg" {
name="my-app-sg"
vpc_id=aws_vpc.my-app-vpc.id
ingress{
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = [var.my-ip ]
}
ingress{
from_port = 8080
to_port = 8080
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" ]
prefix_list_ids = []
}
tags = {
Name="my-app-sg"
}
}
ingress rule for port 22 with protocol ‘tcp‘ is only to accept incoming traffic from particular IP address which is included in ‘var.my_ip‘.
ingress rule for port 8080 with protocol ‘tcp‘ is to accept traffic from any IP address.
egress rule is to reach the internet if the request is made by any IP address.
This resource definition creates a security group with name ‘my-app-sg‘.
5. Provision EC2 instance :
Provisioning ec2 instance is based on two other steps .
Fetching ami ID
Create an ec2 instance
Fetching ami ID :
Use the data ‘aws_ami‘ to fetch the required ami ID from aws, it has three required parameters most_recent, owners, filter.
most_recent is a boolean value used to fetch the recent image from aws.
owners is an array which specifies the list of providers like ‘amazon‘
filter is used to segregate the image IDs based on the specified condition which can be name or virtualization-type.
#Fetch aws ami iamge id
data "aws_ami" "latest-amazon-linux-image" {
most_recent =true
owners = [ "amazon" ]
filter {
name = "name"
values = [ "amzn2-ami-hvm-*-x86_64-gp2" ]
}
filter {
name = "virtualization-type"
values =["hvm"]
}
}
Create an ec2 instance :
Use the resource ‘aws_instance‘ to create a ec2 instance, it has seven required attributes ami, instance_type, subnet_id, vpc_security_group, availability_zone, associate_public_ip_address, key_name.
ami defines the image id from aws.
instance_type defines the configuration of instance like ‘t2.micro‘, ’t2.large’ etc.
subnet_id denotes in which subnet the ec2 instance has to be created.
vpc_security_group specifies the security group which should be attached to the instance.
availability_zone specifies where the ec2 instance lies.
associate_public_ip_address is a boolean value which is set to true.
key_name is the name of the private key which is used to ssh into the instance.
#variable
variable "instance_type" {
description = "Type of instance"
}
#values for variables
instance_type="t2.micro"
resource "aws_instance" "my-app-server" {
ami = data.aws_ami.latest-amazon-linux-image.id
instance_type = var.instance_type
subnet_id = aws_subnet.my-app-subnet-1.id
vpc_security_group_ids = [ aws_security_group.my-app-sg.id ]
availability_zone = var.availability_zone
associate_public_ip_address = true
key_name = "server-key-pair"
tags = {
Name="my-app-server"
}
#Execute commands when the server is started
user_data =file("entry-script.sh")
}
This resource definition creates an ec2 instance with the defined attributes.
6. Deploy nginx Docker container :
In the above resource for creating aws_instance we have another optional parameter named ‘user_data‘ which is used to run the specified commands when the server starts initially.
In the last line of code for aws_instance in point number 5, a file named entry-script.sh is included which consists of the commands to be executed.
#!/bin/bash
sudo yum update -y && sudo yum install -y docker
sudo systemctl start docker
sudo usermod -aG docker ec2-user
docker run -p 8080:80 nginx
This shell script updates, installs docker, starts the docker and runs the nginx container by binding the port 8080 to 80.
Connect to ec2-instance using the private key which is associated with ec2 instance, in our case it is ‘server-key.pem‘ file.
Since we have installed docker in the instance with terraform, check it by running the nginx container as shown below.
Open the port in browser to check the running nginx contanier.