Skip to main content

Terraform - AWS: Install Terraform on Linux & Windows, Deploy EC2 Instance with SSH Key and SG in default VPC, Deploy EC2 Instance with Scripted Docker installation, Create S3 Bucket

1736 words·
Terraform AWS EC2 S3
Table of Contents
Terraform - This article is part of a series.
Part 1: This Article

AWS Prerequisites
#

IAM User
#

Create 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
#

Create access keys for the IAM user, the keys should look as follows:

# Access key: 
AKIARCHUALI...

# Secret access key: 
QLqFZHQ+bB4YXrDxjiX1S...

AWS CLI (Linux)
#

Install AWS CLIv2
#

# 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

AWS Configure
#

# Start AWS CLI configuration
aws configure

# Shell output:
AWS Access Key ID [None]: AKIARCHUALI...
AWS Secret Access Key [None]: QLqFZHQ+bB4YXrDxjiX1S...
Default region name [None]: eu-central-1
Default output format [None]:

AWS CLI (Windows)
#

Install AWS CLIv2 for PowerShell
#

# 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

Install Terraform
#

Linux (Ubuntu)
#

Installation Script
#

Use the following script to install Terraform ob Debian based Linux distributions:

# Install the HashiCorp GPG key
wget -O- https://apt.releases.hashicorp.com/gpg | gpg --dearmor | sudo tee /usr/share/keyrings/hashicorp-archive-keyring.gpg > /dev/null

# Verify the GPG key fingerprint
gpg --no-default-keyring --keyring /usr/share/keyrings/hashicorp-archive-keyring.gpg --fingerprint

# Add the official HashiCorp repository 
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list

# Install Terraform
sudo apt update && sudo apt-get install terraform

Verify Installation
#

# Verify the installation / check version
terraform version

# Shell output:
Terraform v1.9.1
on linux_amd64

Windows
#

Download Terraform
#

https://developer.hashicorp.com/terraform/downloads

  • Extract the “terraform.exe” file and move it to a folder like C:\Terraform

Environment Path Variable
#

Create an Environment Path Variable that points to the directory of your terraform.exe file. Open Advanced system settings

Edit Path Variables

Create new Path Variable that points to the terraform.exe location


Terraform EC2 Project
#

Main Configuration
#

Project Folder & Terraform Provider
#

Create a folder for the Terraform project and create the provider manifest “terraform.tf”

# Create project folder and terraform.tf manifest
TF_PROJECT_NAME=terraform-aws-playground

mkdir $TF_PROJECT_NAME

cat << EOF >> "$TF_PROJECT_NAME/terraform.tf"
# Terraform Provider
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

# AWS Region
provider "aws" {
  region = "us-east-1"
}
EOF

The provider manifest should look like this:

# Terraform Provider
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

# AWS Region
provider "aws" {
  region = "us-east-1"
}

Initialize Terraform Project
#

This will download and install the AWS Terraform provider defined in the terraform.tf file with “hashicorp/aws”, as well as setting up the configuration files in the project directory.

# Initialize the Terraform project
terraform init

# Shell output:
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.

Example: EC2 and SG in default VPC
#

The following configuration starts an EC2 instance in the default VPC of the AWS Region “us-east-1”, with a SSH Key associated to it and creates a dedicated Security Group for the instance.

VPC: Default
AWS Region: “us-east-1”
Security Group inbound: Allow SSH from anywhere
Security Group outbound: Allow all traffic
EC2 IP: Public IP


SSH Key
#

# 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/
# ssh-key.tf
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" "terraform-sg" {
  name = "terraform-sg"

  # 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"]
  }
}

EC2 Instance
#

# main.tf
resource "aws_instance" "jkw-tf-vm1" {
  ami = "ami-0557a15b87f6559cf"
  instance_type = "t2.micro"
  vpc_security_group_ids = [aws_security_group.terraform-sg.id] # SG
  key_name = aws_key_pair.ssh-key-public.key_name # SSH key
 
  tags = {
    Name = "jkw-tf-vm1" # Name in AWS webinterface
  }
}

Outputs
#

Create a outputs manifest for the public IP of the EC2 instance:

# outputs.tf
output "jkw-tf-vm1" {
  value = aws_instance.jkw-tf-vm1.public_ip
  description = "EC2 public IP"
}

Verify Folder Structure
#

The folder structure should look like this:

terraform-aws-playground
├── aws-terraform.pub
├── main.tf
├── outputs.tf
├── security-group.tf
├── ssh-key.tf
├── terraform.tf
├── terraform.tfstate
└── terraform.tfstate.backup

Deploy Resources
#

# Update the necessary providers and modules
terraform init -upgrade
# Apply resources
terraform apply

# Shell output:
Apply complete! Resources: 3 added, 0 changed, 0 destroyed.

Outputs:

public_ip = "54.82.90.29"

Manually Print IP Output
#

You can also manually list the server IP addresses:

# Manually print EC2 IP
terraform output jkw-tf-vm1

# Shell output:
"54.82.90.29"

SSH Into EC2 Instance
#

# SSH into EC2 instance "jkw-tf-vm1"
ssh -i  ~/.ssh/aws-terraform ubuntu@54.82.90.29

List Deployed Resources
#

# List Terraform resources
terraform state list

# Shell output:
aws_instance.jkw-tf-vm1
aws_key_pair.ssh-key-public
aws_security_group.terraform-sg

Destroy Resources
#

# Destroy EC2 instance
terraform destroy -target=aws_instance.jkw-tf-vm1



Example: EC2 Run Bash Script / Install Docker
#

The following Terraform deployment 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 must be located in the Terraform project folder.

Note: This Terraform deployment used the security-group.tf and outputs.tf manifest from the previous examples.

VPC: Default
AWS Region: “us-east-1”
Security Group inbound: Allow SSH from anywhere
Security Group outbound: Allow all traffic
EC2 IP: Public IP


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

Main.tf
#

# Copy the private SSH key into the Terraform project folder
cp ~/.ssh/aws-terraform ~/$TF_PROJECT_NAME/
# main.tf
resource "aws_instance" "jkw-tf-vm1" {
  ami = "ami-0557a15b87f6559cf"
  instance_type = "t2.micro"
  vpc_security_group_ids = [aws_security_group.terraform-sg.id] # SG
  key_name = aws_key_pair.ssh-key-public.key_name # SSH key
 
  tags = {
    Name = "jkw-tf-vm1" # Name in AWS webinterface
  }

# 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 connection
 connection {
   type        = "ssh"
   user        = "ubuntu"
   private_key = file("aws-terraform") # Private SSH key name
   host        = aws_instance.jkw-tf-vm1.public_ip
   }
}

Verify Folder Structure
#

The files in the Terraform project folder should look like this:

terraform-aws-playground
├── aws-terraform
├── aws-terraform.pub
├── install-docker.sh
├── main.tf
├── outputs.tf
├── security-group.tf
├── ssh-key.tf
├── terraform.tf
├── terraform.tfstate
└── terraform.tfstate.backup

Deploy Resources
#

# Update the necessary providers and modules
terraform init -upgrade
# Apply resources
terraform apply

# Shell output:
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Outputs:

jkw-tf-vm1 = "54.144.109.137"

SSH Into EC2 Instance
#

# SSH into EC2 instance "jkw-tf-vm1"
ssh -i  ~/.ssh/aws-terraform ubuntu@54.144.109.137

# Verify the Docker installation
sudo docker ps

List Deployed Resources
#

# List Terraform resources
terraform state list

# Shell output:
aws_instance.jkw-tf-vm1
aws_key_pair.ssh-key-public
aws_security_group.terraform-sg

Destroy Resources
#

# Destroy EC2 instance
terraform destroy -target=aws_instance.jkw-tf-vm1

# Destroy SSH key
terraform destroy -target=aws_key_pair.ssh-key-public

# Destroy security group
terraform destroy -target=aws_security_group.terraform-sg

# Destroy all project resources
terraform destroy



Main Configuration
#

Project Folder & Terraform Provider
#

Create a folder for the Terraform project and create the provider manifest “terraform.tf”

# Create project folder and terraform.tf manifest
TF_PROJECT_NAME=terraform-aws-playground

mkdir $TF_PROJECT_NAME

cat << EOF >> "$TF_PROJECT_NAME/terraform.tf"
# Terraform Provider
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

# AWS Region
provider "aws" {
  region = "us-east-1"
}
EOF

The provider manifest should look like this:

# Terraform Provider
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

# AWS Region
provider "aws" {
  region = "us-east-1"
}

Initialize Terraform Project
#

This will download and install the AWS Terraform provider defined in the terraform.tf file with “hashicorp/aws”, as well as setting up the configuration files in the project directory.

# Initialize the Terraform project
terraform init

Example: Create S3 Bucket
#

This configuration creates a S3 Bucket withthe following settings:

  • Bucket Versioning: Enabled
  • Server-side encryption: Enabled
  • Block all public access: Enabled

S3 Bucket / main.tf
#

# main.tf
resource "aws_s3_bucket" "s3-bucket-1" {

  bucket = "jkw-some-bucket" # Actual S3 Bucket name
  force_destroy = true # Delete with terraform destroy
}

# Bucket Versioning
resource "aws_s3_bucket_versioning" "enabled" {
  bucket = aws_s3_bucket.s3-bucket-1.id
  versioning_configuration {
    status = "Enabled"
  }
}

# Enable server-side encryption by default
resource "aws_s3_bucket_server_side_encryption_configuration" "default" {
  bucket = aws_s3_bucket.s3-bucket-1.id

  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm = "AES256"
    }
  }
}

# Block all public access
resource "aws_s3_bucket_public_access_block" "public_access" {
  bucket                  = aws_s3_bucket.s3-bucket-1.id
  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

S3 Bucket ARN / outputs.tf
#

# Output: S3 Bucket ARN
output "s3_bucket_arn" {
  value       = aws_s3_bucket.s3-bucket-1.arn
  description = "S3 ARN"
}

Verify the Folderstructure
#

The files in the Terraform project should look like this:

terraform-aws-playground
├── main.tf
├── outputs.tf
└── terraform.tf

Deploy Resources
#

# Update the necessary providers and modules
terraform init -upgrade
# Apply resources
terraform apply

# Shell output:
Apply complete! Resources: 4 added, 0 changed, 0 destroyed.

Outputs:

s3_bucket_arn = "arn:aws:s3:::jkw-some-bucket"

Manually Print S3 Bucket ARN
#

# Print S3 Bucket ARN
terraform output s3_bucket_arn

# Shell output:
"arn:aws:s3:::jkw-some-bucket"

Delete Resources
#

# Delete the S3 Bucket
terraform destroy -target=aws_s3_bucket.s3-bucket-1

# Alternative
terraform destroy

Links #

# Install AWS CLI with PowerShell
https://docs.aws.amazon.com/powershell/latest/userguide/pstools-getting-set-up-windows.html
Terraform - This article is part of a series.
Part 1: This Article