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:
- 
AmazonEC2FullAccessFor EC2 deployments
- 
AmazonS3FullAccessFor 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