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