Connect to a private EC2 instance using session manager from a Custom VPC built with terraform.

Introduction

In modern-day web architecture, we deploy our database or application layer into a private instance. We do this because we don't want any outside connectivity from the public internet, but most times, we want to be able to access the application in the private subnet and also install or update packages from the instance.

We will be using Terraform to build the custom VPC, private and public subnets, nat gateways, route tables and private instances we will connect to.

Step 1

Log into AWS Console, create an IAM role and attach the AmazonSSMManagedInstanceCore permission policy, it enables AWS Systems Manager service core functionality.

Please take note of the IAM role name.

Step 2

Move over to your system, open up your visual studio code or any other code editor, create a main.tf inside a folder, and copy the following code.

Copy the full code inside your main.tf, you can follow the comments for an explanation of what is being done.

The below code, creates a custom VPC, private and public subnets in us-east-1a, private and public route tables, Internet gateway, nat gateway, and launches an ec2 instance inside the private subnet.

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.0"
    }
  }
}

# Configure the AWS Provider. 
provider "aws" {
  region     = "us-east-1"
  # access_key = "xxxxxx" if you have aws configured as default profile, ignore this
  # secret_key = "xxx" if not please create fresh access key and secret key in aws
}


# Create a custom VPC
resource "aws_vpc" "first-vpc" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_hostnames = "true"
  tags = {
    Name = "first-vpc"
  }
}

# Create Internet Gateway
resource "aws_internet_gateway" "first-gw" {
  vpc_id = aws_vpc.first-vpc.id
}


# Create Public Route Table
resource "aws_route_table" "public_route" {
  vpc_id = aws_vpc.first-vpc.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.first-gw.id
  }
}


# Create Public Subnet
resource "aws_subnet" "public_subnet" {
  vpc_id                  = aws_vpc.first-vpc.id
  cidr_block              = "10.0.1.0/24"
  availability_zone       = "us-east-1a"
  map_public_ip_on_launch = true

  tags = {
    Name = "public_subnet"
  }
}


# Associate public subnet to route table
resource "aws_route_table_association" "public-association" {
  subnet_id      = aws_subnet.public_subnet.id
  route_table_id = aws_route_table.public_route.id
}


# Create Elastic IP
resource "aws_eip" "ip" {
  vpc = true
  tags = {
    Name = "test-elasticIP"
  }
}


# Create nat Gateway and launch in a public subnet
resource "aws_nat_gateway" "test_nat" {
  allocation_id = aws_eip.ip.id
  subnet_id     = aws_subnet.public_subnet.id

  tags = {
    Name = "test_nat"
  }

}


# Create Private Route Table
resource "aws_route_table" "private_route" {
  vpc_id = aws_vpc.first-vpc.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_nat_gateway.test_nat.id
  }
}

# Create Private Subnet
resource "aws_subnet" "private_subnet" {
  vpc_id            = aws_vpc.first-vpc.id
  cidr_block        = "10.0.2.0/24"
  availability_zone = "us-east-1a"
  tags = {
    Name = "private_subnet"
  }
}

# Associate private subnet to route table
resource "aws_route_table_association" "private-association" {
  subnet_id      = aws_subnet.private_subnet.id
  route_table_id = aws_route_table.private_route.id
}


# Launch instance into private
resource "aws_instance" "private_web" {
  ami                  = "ami-0889a44b331db0194" # Amazon-linux-2023 comes with SSM Agent installed
  instance_type        = "t2.micro"
  subnet_id            = aws_subnet.private_subnet.id
  iam_instance_profile = "AccessSSM_Role" # Update with iam role created earlier.

  tags = {
    Name = "private-web"
  }

}

Step 3

Run "terraform init" and "terraform apply --auto-approve"

Step 4

Once ready, move over to the console, go to EC2, wait for the instance to complete status checks, click on connect, move over to SSM manager tab and click to connect.

Switch to root user to install packages

sudo su

ping google.com and run "yum install httpd" to confirm if the instance has access to the internet through the nat gateway.

Step 5

Remove all resources by running "terraform destroy --auto-approve"

You can access the GitHub repo at this link - https://github.com/tunjiaramide/connect-private-instance