The following Terraform configuration creates an EC2 instance in the default VPC, copys a bash script via SSH to the EC2 instance, changes the permission of the script and executes the script.
The script install-docker.sh
and a SSH key pair must be located in the Terraform project folder.
AWS Prerequisites #
IAM User & Add Permissions #
Create an IAM user and attach the following policies directly:
-
AmazonEC2FullAccess
For EC2 deployments -
AmazonS3FullAccess
For S3 Bucket deployments
Create access keys for the IAM user
Install AWS CLI #
Linux #
# Update packages
sudo apt update
# Unstall zip tool
sudo apt install unzip -y
# Download AWS CLI zip file
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
# Unzip
unzip awscliv2.zip
# Install
sudo ./aws/install
# Verify installation / check version
/usr/local/bin/aws --version
Windows #
# Download the AWS CLI installation package
Invoke-WebRequest -Uri https://awscli.amazonaws.com/AWSCLIV2.msi -OutFile AWSCLIV2.msi
# Install AWS CLI
Start-Process msiexec.exe -ArgumentList '/i AWSCLIV2.msi /qn' -Wait
# Start new shell & verify installation
aws --version
AWS Configure #
Add the IAM user access keys:
# Start AWS CLI configuration
aws configure
Terraform EC2 Stack #
File and Folder Structure #
The file and folder structure of the Terraform project looks like this:
aws-ec2-stack
├── aws-terraform # Private SSH key
├── aws-terraform.pub # Public SSH key
├── compute.tf
├── install-docker.sh # Bash script
├── outputs.tf
├── security-group.tf
├── terraform.tf
└── variables.tf
Project Folder #
# Create project folder
TF_PROJECT_NAME=aws-ec2-stack
mkdir $TF_PROJECT_NAME && cd $TF_PROJECT_NAME
SSH Key Pair #
# Create a SSH key pair
ssh-keygen -t rsa -b 4096 -f ~/.ssh/aws-terraform
# Copy the public SSH key into the Terraform project folder
cp ~/.ssh/aws-terraform.pub ~/$TF_PROJECT_NAME/
# Copy the private SSH key into the Terraform project folder
cp ~/.ssh/aws-terraform ~/$TF_PROJECT_NAME/
Bash Script #
- install-docker.sh
#!/bin/bash
# Install Docker and Docker Compose on Ubuntu
sudo apt-get update
sudo apt-get install \
ca-certificates \
curl \
gnupg \
lsb-release -y
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y
Terraform Configuration Files #
Terraform Provider #
- terraform.tf
# Terraform Provider
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
# Provider AWS Region
provider "aws" {
alias = "aws_region"
region = var.aws_region
}
Variables #
- variables.tf
# AWS Region
variable "aws_region" {
description = "AWS Region"
type = string
default = "us-east-1"
}
# EC2 Image ID
variable "ami_id" {
default = "ami-0e2c8caa4b6378d8c" # Define EC2 AMI ID
}
EC2 Instance #
compute.tf
# EC2 Instance
resource "aws_instance" "ec2" {
provider = aws.aws_region
ami = var.ami_id
instance_type = "t2.micro"
key_name = aws_key_pair.ssh_key_public.key_name
vpc_security_group_ids = [aws_security_group.vpc_sg.id]
tags = {
Name = "EC2 instance"
Env = "Production"
}
# SSH connection
connection {
type = "ssh"
user = "ubuntu"
private_key = file("aws-terraform") # Private SSH key name / must be in project folder
host = aws_instance.ec2.public_ip
}
# Copy script into EC2 instance
provisioner "file" {
source = "install-docker.sh"
destination = "/home/ubuntu/install-docker.sh"
}
# Change script permission, run script
provisioner "remote-exec" {
inline = [
"chmod +x /home/ubuntu/install-docker.sh",
"sudo /home/ubuntu/install-docker.sh",
]
}
}
# SSH Key
resource "aws_key_pair" "ssh_key_public" {
key_name = "ssh-key-public"
public_key = file("aws-terraform.pub") # Public SSH key name
}
Security Group #
- security-group.tf
resource "aws_security_group" "vpc_sg" {
provider = aws.aws_region
name = "SG"
description = "Security group webserver"
# Inbound: allow SSH from Anywhere
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
# Inbound: allow HTTP from Anywhere
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
# Inbound: allow HTTPS from Anywhere
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
# Outbound: allow all traffic
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "SG"
Env = "Production"
}
}
Outputs #
- outputs.tf
# EC2 Instance public IP
output "VM_Public_IP" {
value = aws_instance.ec2.public_ip
}
# EC2 Instance private IP
output "VM_Private_IP" {
value = aws_instance.ec2.private_ip
}
Deploy Configuration #
Initialize Terraform Project #
This will download and install the AWS Terraform provider.
# Initialize the Terraform project
terraform init
Validate Configuration Files #
# Validates the syntax and structure of Terraform configuration files
terraform validate
# Shell output:
Success! The configuration is valid.
Plan the Deployment #
# Dry run / preview changes before applying them
terraform plan
Apply the Configuration #
# Create network stack
terraform apply -auto-approve
# Shell output:
Apply complete! Resources: 3 added, 0 changed, 0 destroyed.
Outputs:
VM_Private_IP = "172.31.18.125"
VM_Public_IP = "98.80.77.215"
Verify Setup #
SSH into EC2 Instance #
# SSH into the EC2 instance
ssh -i ~/.ssh/aws-terraform ubuntu@98.80.77.215
Verify Docker Installation #
# Switch to root user
sudo su
# Verify the Docker installation
sudo docker ps
# Shell output:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES