AWS terraform lab

AWS Terraform Lab

Overview

This lab will guide you through creating SSH keys, installing Terraform, and provisioning EC2 infrastructure using Infrastructure as Code principles. You’ll work from your existing user instance created in the intro lab to create and manage a new web server instance.

Prerequisites

Your group identifier remains “groupXY” For example, if your group is 12 your group identifier is “group12”.

Lab Objectives

By the end of this lab, you will have:


Part 1: Verify S3 Bucket for Terraform Backend

Step 1.1: Navigate to S3 Console

  1. Open the AWS console and navigate to S3

Step 2.2: Verify the S3 bucket exists

  1. Verify that a bucket for your group exists. This bucket was created in a previous lab. The name of the bucket corresponds to your group identifier:

Example: if your group identifier is 12, the bucket name would be nsrc-group12-data


Part 2: Connect to Your Existing Instance

Step 2.1: Access Your User Instance

  1. Navigate to EC2 Console
  2. Click “Instances” in the left navigation
  3. Find your group instance named “groupXY-server”. Your group created this instance during the AWS VM lab
  4. Select your instance and click “Connect”
  5. Choose “EC2 Instance Connect” tab
  6. Click “Connect” to open the browser terminal

Part 3: SSH Key Generation

You will create an SSH key pair (private and public keys), and upload just the public part to AWS EC2 for use in a new VM that you will create.

Step 3.1: Generate SSH Key Pair

  1. Generate a new SSH key pair, with empty passphrase on the private key:
ssh-keygen -t ed25519 -f ~/.ssh/manager-key -N ""
  1. Verify the key files were created:
ls -la ~/.ssh/

You should see manager-key (private key) and manager-key.pub (public key).

Step 4.2: Upload Public Key to AWS

  1. Display the public key content:
cat ~/.ssh/manager-key.pub

It’s one line of text, with three parts: the key type, some binary data, and a key identifier.

  1. Upload the public key to AWS EC2:
aws ec2 import-key-pair --key-name "groupXY-manager" --public-key-material fileb://~/.ssh/manager-key.pub

(for example, the key name might be group12-manager)

  1. Verify the key pair was uploaded:
aws ec2 describe-key-pairs --key-names "groupXY-manager"

(You could name the key anything you like. Make sure you verify the name you uploaded.)


Part 4: Terraform Installation

Step 4.1: Download and Install Terraform

  1. Update the system package index:
sudo apt update
  1. Download the Terraform binary:
wget https://releases.hashicorp.com/terraform/1.12.2/terraform_1.12.2_linux_amd64.zip
  1. Install unzip if not already available:
sudo apt install -y unzip
  1. Unzip the Terraform binary:
unzip terraform_1.12.2_linux_amd64.zip
  1. Move Terraform to system PATH:
sudo mv terraform /usr/local/bin/
  1. Verify Terraform installation:
terraform version

Part 5: Terraform Configuration Files

Step 5.1: Create Terraform Directories

  1. Create the terraform directory structure:
mkdir -p ~/terraform/ec2-instance
cd ~/terraform/ec2-instance
  1. Verify you are in the correct location:
pwd

You should see: /home/ubuntu/terraform/ec2-instance

  1. Verify the directory structure:
ls -la

You should see that this is empty apart from the current directory (.) and the parent directory (..).

(The -l flag gives a long format listing. Names starting with . are normally hidden; the -a flag causes them to be shown)

Step 5.2: Create Variables File

Before we start, be aware we will need to find some information to properly fill the variables file values

group_identifier: groupXY (remember? your group)

region: the AWS region (example: ap-southeast-1 )

VPC ID: the ID of the VPC we are working in

instance_ami: The image ID (AMI) for the operating system you want to use as the base to build your server

Create the variables.tf file:

nano variables.tf

Add the following content:

variable "aws_region" {
  description = "AWS region for resources"
  type        = string
  default     = "ap-southeast-1"
}

variable "group_identifier" {
  description = "Unique identifier for the group"
  type        = string
  default     = "groupXY"   # change to your group identifier
}

variable "instance_ami" {
  description = "AMI ID for EC2 instance"
  type        = string
  default     = "ami-08e7e250e7e3deb9b"  # Ubuntu 24.04 LTS for ap-southeast-1
}

variable "vpc_id" {
  description = "VPC ID for resources"
  type        = string
  default     = "vpc-066d87efc4131ee00"
}

variable "ssh_key" {
  description = "public ssh key"
  type        = string
  default     = "groupXY-manager" # your group ssh key (change)
}

Step 5.3: Create Main Configuration File

Remember! Your group S3 bucket name needs to be updated, as well as the region. When defining the terraform backend variables are not allowed

Create the main.tf file:

nano main.tf

Add the following content:

terraform {
  required_version = ">= 1.0"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }

  # variables not allowed in backend block
  backend "s3" {
    bucket = "nsrc-groupXY-data"
    key    = "terraform/ec2-instance/terraform.tfstate"
    region = "ap-southeast-1"
  }
}

#we can use variables from now on
provider "aws" {
  region = var.aws_region
}

data "aws_vpc" "selected" {
  id = var.vpc_id
}

data "aws_subnets" "selected" {
  filter {
    name   = "vpc-id"
    values = [var.vpc_id]
  }
}

data "aws_subnet" "selected" {
  id = data.aws_subnets.selected.ids[0]
}

resource "aws_security_group" "terraform" {
  name_prefix = "${var.group_identifier}-terraform"
  vpc_id      = var.vpc_id

  ingress {
    description = "HTTP"
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    description = "HTTPS"
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    description = "SSH"
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "${var.group_identifier}-terraform"
  }
}

resource "aws_instance" "terraform" {
  ami             = var.instance_ami
  instance_type   = "t2.small"
  key_name        = "${var.ssh_key}"
  subnet_id       = data.aws_subnet.selected.id

  vpc_security_group_ids = [aws_security_group.terraform.id]

  tags = {
    Name = "${var.group_identifier}-terraform"
  }

  ## we will install nginx to show user_data
  ## capabilities
  user_data = <<-EOF
              #!/bin/bash
              apt update
              apt install -y nginx
              systemctl start nginx
              systemctl enable nginx
              echo "<h1>Terraform Server - ${var.group_identifier}</h1>" > /var/www/html/index.html
              EOF
}

output "instance_id" {
  description = "ID of the EC2 instance"
  value       = aws_instance.terraform.id
}

output "instance_public_ip" {
  description = "Public IP address of the EC2 instance"
  value       = aws_instance.terraform.public_ip
}

output "instance_public_dns" {
  description = "Public DNS name of the EC2 instance"
  value       = aws_instance.terraform.public_dns
}

Part 6: Deploy Infrastructure

Step 6.1: Initialize Terraform

  1. Initialize Terraform with the backend configuration:
terraform init

It has created a hidden directory called .terraform and a hidden file .terraform.lock.hcl, which you can see if you run ls -la

$ ls -la
total 24
drwxrwxr-x 3 ubuntu ubuntu 4096 Aug 12 14:11 .
drwxrwxr-x 3 ubuntu ubuntu 4096 Aug 12 14:05 ..
drwxr-xr-x 3 ubuntu ubuntu 4096 Aug 12 14:10 .terraform
-rw-r--r-- 1 ubuntu ubuntu 1407 Aug 12 14:11 .terraform.lock.hcl
-rw-rw-r-- 1 ubuntu ubuntu 2472 Aug 12 14:10 main.tf
-rw-rw-r-- 1 ubuntu ubuntu  685 Aug 12 14:10 variables.tf

Step 6.2: Plan Infrastructure Changes

  1. Review the planned infrastructure changes:
terraform plan

Watch carefully!

Terraform is listing the changes it plans to commit. You should always review the output of the plan with the utmost care.

This is the last chance to make sure everything you planned to do.

We call it: the last possible responsible moment!

If you are sure, let’s continue.

Step 6.3: Apply Configuration

  1. Deploy the infrastructure:
terraform apply
  1. Type yes when prompted to confirm the deployment.

Part 7: Test results

Step 7.1: Verify Deployment

  1. Note the output values displayed after successful deployment
  2. Verify the instance is running:
aws ec2 describe-instances --filters "Name=tag:Name,Values=groupXY-terraform" --query 'Reservations[*].Instances[*].[InstanceId,State.Name,PublicIpAddress]' --output table

Part 8: Terraform data

Step 8.1: See what Terraform can tell

  1. See outputs
terraform output
  1. See what Terraform knows
terraform show
  1. Note that Terraform has created a state file, under prefix terraform/, as an object in your S3 bucket. You can check it is there using the AWS console, or at the command line:
aws s3 ls s3://nsrc-groupXY-data
aws s3 ls --recursive s3://nsrc-groupXY-data/terraform/
  1. From the “terraform show” output, find your instance’s public IP address, and enter that into a browser. If it refuses the connection, it may still be in the process of booting up; try again a minute or two later.

Good job!

Part 9 (optional): use Terraform to apply changes

If you have time available, this is an extra step you can do.

Edit your main.tf and change the instance type from t2.small to t2.micro

Check the output of terraform plan. It it going to destroy and recreate the instance, or is it going to update it in-place?

To update the instance to match the new desired configuration, use terraform apply as before, and watch the output.

Note that this particular change requires restarting your instance, and it will likely get a new IP address.


Expected Results

After completing this lab, you should have:

Additional information

For this lab, we needed to supply VPC ID and AMI ID to Terraform. Where did these values come from?

You can obtain the VPC ID from the AWS Console, by searching for the “VPC” service, or from the command line:

aws ec2 describe-vpcs

You can get AMI IDs using the “AMI Catalog” under EC2. Note that different regions have their own AMI IDs.

Trick!

You can do a search with the AWS CLI to find an AMI from a given vendor like Canonical (Ubuntu), this is an example:

aws ec2 describe-images --owners 099720109477 \
  --filters "Name=name,Values=ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-*" \
  "Name=state,Values=available" --query 'sort_by(Images, &CreationDate)[-1].ImageId' \
  --region ap-southeast-1

The output would be something like this: ami-08e7e250e7e3deb9b

Over time as new versions of the AMI are released, the AMI ID would change. The important part is understanding how to use the AWS CLI to find the information.

These were the pieces of data we had to supply:

It is also possible to do such searches within terraform itself, using “data sources” such as the aws_ami data source.