Amazon Elastic Compute Engine (EC2) is a web-based service from Amazon which allow users to rent virtual compute services on which to run their own compute services.
Terraform is an open-source infrastructure as code software tool that provides a consistent CLI workflow to manage hundreds of cloud services. Terraform codifies cloud APIs into declarative configuration files.
In this tutorial, I will work you through the stages of deploying an Ec2 instance using Terraform. We will deploy an Ubuntu server and in turn enable an Nginx on it.
Steps to follow
Before we can proceed make sure you have an AWS account all set up and also you are familiar with Terraform commands init, plan and deploy.
- Create an AWS account
- Install Terraform on your machine be it on Linux, Windows, or macOS. For This tutorial, i will be using Ubuntu 22.04
- Create a Virtual Private Cloud VPC
- Create Internet Gateway
- Create route table
- Create a subnet
- Associate created subnet with route table you created earlier.
- Create a security group to allow SSH, HTTPS, and HTTP services
- Create a network interface with an IP in the subnet created earlier.
- Assign an elastic IP to the network interface
- Create an Ubuntu server and install Nginx
Before we can proceed let’s recap on Terraform’s useful commands.
Terraform init
command is used to initialize a working directory containing Terraform working files.- Terraform plan command creates an execution plan and lets you preview the changes that will be affected once you apply.
Terraform apply
command executes the action proposed by the terraform plan command.Terraform destroy
command deletes/destroy all the remote infrastructure managed by Terraform.
1. Create an AWS account.
To create an AWS account head over to aws.amazon and register your details. This step is easy to do.
2. Install Terraform on Ubuntu 22.04
We need to install Terraform first before we can proceed. I have covered how to install Terraform on ubuntu earlier. So check it out here.
3. Create a VPC
Amazon Virtual Private Cloud (Amazon VPC) enables you to launch AWS resources into a virtual private network that is already defined.
To create a VPC in Terraform we are going to use VSCode to define our resources. So head over to VSCode and create a folder named Terraform_projects. Cd into this folder and create a file called main.tf
. tf is an extension file for Terraform.
$ sudo mkdir terraform_projects
$ cd terraform_projects
$ touch main.tf
$ code .
To create a Terraform VPC, open main.tf
file and paste the following code.
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.16"
}
}
required_version = ">= 1.2.0"
}
# Provider, it configures the specified provider here aws
provider "aws" {
region = "us-east-1" # region where your server will be located
access_key = "Access key" # access key, this comes from AWS account
secret_key = "Your secret access key" # secret key comes from AWS Account
}
# Create a VPC
resource "aws_vpc" "prod-vpc" {
cidr_block = "10.0.0.0/24" # IP address range
tags = { # This specifies the name to be displayed on aws console
"Name" = "Prod-VPC"
}
}
4. Create an internet Gateway
Internet gateway enables resources such as EC2 instances in your public subnets to connect to the internet if the public IPv4 address or IPv6 is set.
To create a Terraform internet gateway resource we use the following configurations.
# create an internet gateway
resource "aws_internet_gateway" "prod-gateway" {
vpc_id = aws_vpc.prod-vpc.id #comes from the resource "aws_vpc" "prod-vpc"
tags = {
"Name" = "Prod_Gateway"
}
}
5. Create a route table
A route table contains a set of rules called routes that determine where traffic from your subnet or gateway is directed to.
To create a route table head over to your Vscode and continue from where we left. Paste the following code in main.tf
file.
# create a route table
resource "aws_route_table" "prod-route-table" {
vpc_id = aws_vpc.prod-vpc.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.prod-gateway.id
}
route {
ipv6_cidr_block = "::/0"
gateway_id = aws_internet_gateway.prod-gateway
}
tags = {
"Name" = "Prod-Route"
}
}
6. Create a subnet
A subnet is a range of IP addresses in a VPC. To use subnets you can specify a range in your configuration files.
To create a subnet resource, paste the following code into vscode as a continuation.
# Create a subnet
resource "aws_subnet" "prod-subnet0" {
vpc_id = aws_vpc.prod-vpc.id
availability_zone = "us-east-1c"
cidr_block = "10.0.1.0/24"
tags = {
"Name" = "Prod-Subnet"
}
}
7. Associate created subnet with route table
Route table association provides a resource to create an association between a route table and a subnet or internet gateway.
# Associate subnet with a route table
resource "aws_route_table_association" "Prod-associate" {
subnet_id = aws_subnet.prod-subnet0.id
route_table_id = aws_route_table.prod-route-table.id
}
8. Create a security group to allow SSH, HTTPS, and HTTP services
Securing your network is a mandatory thing to avoid any eavesdropping. We are going to enable ssh on port 22, HTTP on port 80, and HTTPS on port 443
# Create a security Group to allow traffc on port 22, 80, 443
resource "aws_security_group" "prod-allow_tls" {
name = "prod-allow_tls"
description = "Allow TLS inbound traffic"
vpc_id = aws_vpc.prod-vpc.id
ingress {
description = "HTTPS"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
ipv6_cidr_blocks = ["::/0"]
}
ingress {
description = "HTTP"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
ipv6_cidr_blocks = ["::/0"]
}
ingress {
description = "SSH"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
ipv6_cidr_blocks = ["::/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
ipv6_cidr_blocks = ["::/0"]
}
tags = {
Name = "Prod-Allow_tls"
}
}
9. Create a network interface
An elastic network interface is a logical networking component in a VPC that represents a virtual network card. To create a VPC we will use the following code.
# Create a network interface with an IP in the subnet that was created earlier
resource "aws_network_interface" "prod-nic" {
subnet_id = aws_subnet.prod-subnet0.id
private_ips = ["10.0.1.20"]
security_groups = [aws_security_group.prod-allow_tls.id]
}
10. Assign an elastic IP to the network interface
An elastic Ip address in AWS in the public IP address. So assign one to the network interface using the following code.
# Assign an elastic IP address to the network interface
resource "aws_eip" "prod-eip" {
vpc = true
network_interface = aws_network_interface.prod-nic.id
associate_with_private_ip = "10.0.1.20"
depends_on = [aws_internet_gateway.prod-gateway]
11. Create an Ubuntu server and install Nginx
The remaining thing now is to create an ubuntu server. Go to AWS console, running instances, click launch instance and search for Ubuntu and copy the AMI ID.
Create a key.pem file from your AWS console. Go
ec2 > key pair > create key pair > give a name and hit create
The key pair will be downloaded.
# Create an Ubuntu server and install Nginx
resource "aws_instance" "web-server-instance" {
ami = "ami-052efdf9da"
instance_type = "t2.micro"
availability_zone = "us-east-1c"
key_name = "prod-key"
network_interface {
device_index = 0
network_interface_id = aws_network_interface.prod-nic.id
}
user_data = <<-EOF
#!/bin/bash
sudo apt update -y
sudo apt install nginx -y
sudo systemctl start nginx
sudo bash -c 'This is my first Nginx server > /var/www/html/index.html'
EOF
tags = {
"Name" = "Web-Server"
}
}
We have finished creating our infrastructure, it’s now time to initialize the environment.
12. Initialize the environment.
We are going to use terraform init
command to start initialization.
terraform init
You will see an output like this.
Initializing the backend...
Initializing provider plugins...
- Finding hashicorp/aws versions matching "~> 4.16"...
- Installing hashicorp/aws v4.19.0...
- Installed hashicorp/aws v4.19.0 (signed by HashiCorp)
Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.
Terraform has been successfully initialized!
You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.
If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
The next command to use is terraform plan
terraform plan
You will see an output like this
# Sample output.
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
+ create
Terraform will perform the following actions:
# aws_eip.prod-eip will be created
+ resource "aws_eip" "prod-eip" {
+ allocation_id = (known after apply)
+ associate_with_private_ip = "10.0.1.20"
+ association_id = (known after apply)
+ carrier_ip = (known after apply)
+ customer_owned_ip = (known after apply)
+ domain = (known after apply)
+ id = (known after apply)
+ instance = (known after apply)
+ network_border_group = (known after apply)
+ network_interface = (known after apply)
+ private_dns = (known after apply)
+ private_ip = (known after apply)
+ public_dns = (known after apply)
+ public_ip = (known after apply)
+ public_ipv4_pool = (known after apply)
+ tags_all = (known after apply)
+ vpc = true
}
The plus sign shows that it will be added once you hit apply.
Let’s now apply our changes. We are going to use terraform apply
.
terraform apply
When prompted click yes to allow the changes to take effect.
Sample output.
Plan: 9 to add, 0 to change, 0 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
aws_vpc.prod-vpc: Creating...
aws_vpc.prod-vpc: Creation complete after 4s [id=vpc-07f54c3339d0775c1]
aws_subnet.prod-subnet0: Creating...
aws_internet_gateway.prod-gateway: Creating...
aws_security_group.prod-allow_tls: Creating...
aws_subnet.prod-subnet0: Creation complete after 2s [id=subnet-0044a170cf5c43058]
aws_internet_gateway.prod-gateway: Creation complete after 2s [id=igw-08b89fed03e5f826a]
aws_route_table.prod-route-table: Creating...
aws_route_table.prod-route-table: Creation complete after 3s [id=rtb-0d9116dfe11170024]
aws_route_table_association.Prod-associate: Creating...
aws_security_group.prod-allow_tls: Creation complete after 6s [id=sg-0cd6c56acfa6e3cac]
aws_network_interface.prod-nic: Creating...
aws_route_table_association.Prod-associate: Creation complete after 2s [id=rtbassoc-04899c139406df489]
aws_network_interface.prod-nic: Creation complete after 1s [id=eni-09555567dabe65b39]
aws_eip.prod-eip: Creating...
aws_instance.web-server-instance: Creating...
aws_eip.prod-eip: Creation complete after 2s [id=eipalloc-020315147d812b2be]
aws_instance.web-server-instance: Still creating... [10s elapsed]
aws_instance.web-server-instance: Still creating... [20s elapsed]
aws_instance.web-server-instance: Still creating... [30s elapsed]
aws_instance.web-server-instance: Creation complete after 36s [id=i-02b8bd47acb9f4db5]
Apply complete! Resources: 9 added, 0 changed, 0 destroyed.
If you go now to your AWS EC2 instance you will see a running server
You can now look for the assigned elastic IP address and paste it into your browser.
Lastly, before we can destroy the infrastructure lets ssh into the Ubuntu server.
Ec2 instnce > connect > ssh client, then follow the prompts.
Check to see if Nginx is running.
We cn now destroy our infrastructure with terraform destroy
command.
terraform destroy
Sample output
Plan: 0 to add, 0 to change, 9 to destroy.
Do you really want to destroy all resources?
Terraform will destroy all your managed infrastructure, as shown above.
There is no undo. Only 'yes' will be accepted to confirm.
Enter a value: yes
aws_route_table_association.Prod-associate: Destroying... [id=rtbassoc-04899c139406df489]
aws_eip.prod-eip: Destroying... [id=eipalloc-020315147d812b2be]
aws_instance.web-server-instance: Destroying... [id=i-02b8bd47acb9f4db5]
aws_route_table_association.Prod-associate: Destruction complete after 1s
aws_route_table.prod-route-table: Destroying... [id=rtb-0d9116dfe11170024]
aws_route_table.prod-route-table: Destruction complete after 1s
aws_eip.prod-eip: Destruction complete after 3s
aws_internet_gateway.prod-gateway: Destroying... [id=igw-08b89fed03e5f826a]
aws_internet_gateway.prod-gateway: Destruction complete after 2s
aws_instance.web-server-instance: Still destroying... [id=i-02b8bd47acb9f4db5, 10s elapsed]
aws_instance.web-server-instance: Still destroying... [id=i-02b8bd47acb9f4db5, 20s elapsed]
aws_instance.web-server-instance: Still destroying... [id=i-02b8bd47acb9f4db5, 30s elapsed]
aws_instance.web-server-instance: Destruction complete after 32s
aws_network_interface.prod-nic: Destroying... [id=eni-09555567dabe65b39]
aws_network_interface.prod-nic: Destruction complete after 1s
aws_subnet.prod-subnet0: Destroying... [id=subnet-0044a170cf5c43058]
aws_security_group.prod-allow_tls: Destroying... [id=sg-0cd6c56acfa6e3cac]
aws_subnet.prod-subnet0: Destruction complete after 1s
aws_security_group.prod-allow_tls: Destruction complete after 2s
aws_vpc.prod-vpc: Destroying... [id=vpc-07f54c3339d0775c1]
aws_vpc.prod-vpc: Destruction complete after 0s
Destroy complete! Resources: 9 destroyed.
If you head over to AWS Ec2 instance you will see terminated state.