Setting Up an AWS EC2 instance with SSH access using Terraform

Hasitha Algewatta
5 min readOct 3, 2018

--

This attempts to guide you through all the nuances in trying to create a SSH access enabled EC2 instance using Terraform from scratch. We will go through setting up the aws provider to finally logging on to the instance.

Prerequisites

  • Have aws-cli installed and aws configured with secret keys
  • A key pair for EC2 instances

Setting up AWS provider

Provider tells Terraform which service it’s going to use to set up resources.

//connections.tf
provider "aws" {
region = "us-east-1"
}

If you have configured aws by running “aws configure” as I did you do not need to provide the secret keys in the terraform file.

Setting up VPC

VPC which stands for Virtual Private Network allows you to launch AWS resources in a (virtual) network you define and completely control. AWS will set up a default VPC for you to use but here we will be creating our own VPC.

//network.tf
resource "aws_vpc" "test-env" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true
tags {
Name = "test-env"
}
}

Here we are asking Terraform to set up an AWS VPC resource named “test-env”. The CIDR BLOCK is used to configure the expanse of our network. Higher the value that comes after “/” the smaller our network will be. A tag called “Name” was added to let us easily identify the VPC.

Setting up inputs

While this is not necessary I thought it would be handy to use inputs as this will allow me to use the same terraform plan to launch different instances.

Creating variables in Terraform is as easy as shown below

//variables.tfvariable "ami_name" {}variable "ami_id" {}variable "ami_key_pair_name" {}

If you keep the variable empty as shown Terraform will prompt for them to be given as input during Terraform plan execution. The variable ami_name will be used as a tag and the variable ami_id to find the instance to be launched. AMI ID is found next to AMI name as can be seen highlighted in the picture below.

Locating AMI ID

ami_key_pair_name will be used to input the key name that will be used in the creation of the EC2 instance. More on that later.

Setting up subnets

Next we will set up a subnet in our newly created VPC. Later we will attach an internet gateway to this subnet allowing public traffic to come through. This will allow us to keep part of the VPC private and unexposed.

//subnets.tf
resource "aws_subnet" "subnet-uno" {
cidr_block = "${cidrsubnet(aws_vpc.test-env.cidr_block, 3, 1)}"
vpc_id = "${aws_vpc.test-env.id}"
availability_zone = "us-east-1a"
}

Visit http://blog.itsjustcode.net/blog/2017/11/18/terraform-cidrsubnet-deconstructed/ if you find cidrsubnet() a little difficult to digest which has it wonderfully explained.

vpc_id is used to attach the subnet to the VPC we previously created.

Setting up security groups

Before we try to launch an instance we should first create a security group to suit our needs. Even though AWS by default allows all traffic outside this is disabled in terraform and therefore has to be explicitly stated.

//security.tf
resource "aws_security_group" "ingress-all-test" {
name = "allow-all-sg"vpc_id = "${aws_vpc.test-env.id}"ingress {
cidr_blocks = [
"0.0.0.0/0"
]
from_port = 22
to_port = 22
protocol = "tcp"
}
// Terraform removes the default rule
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}

Here we define a security group that will allow anyone to connect through port 22. It will also forward all traffic without restriction. Using vpc_id we attach it to the VPC we have created. The ingress block is used to describe how incoming traffic will be treated. Here we have defined a rule to accept connections from all IPs on port 22. The egress block defines the rule for outgoing traffic and here we have defined it to allow all.

Launching EC2 instance

Now that we have most of it set up let’s launch our instance. For this we have to set up an aws_instance resource.

//servers.tf
resource "aws_instance" "test-ec2-instance" {
ami = "${var.ami_id}"
instance_type = "t2.micro"
key_name = "${var.ami_key_pair_name}"
security_groups = ["${aws_security_group.ingress-all-test.id}"]
tags {
Name = "${var.ami_name}"
}
subnet_id = "${aws_subnet.subnet-uno.id}"
}

Here if you look closely you’ll see that we have used the input variables we previously declared. key_name used here should be an existing key as it is one of the inputs. Also we have used to the security group we just created. This will be launched in the subnet we created before.

Attaching an elastic IP

For us to be able to access the instance it should have a public IP. This is where elastic IPs become useful for us. We will update our network.tf to below.

resource "aws_vpc" "test-env" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true
tags {
Name = "test-env"
}
}
resource "aws_eip" "ip-test-env" {
instance = "${aws_instance.test-ec2-instance.id}"
vpc = true
}

This will attach a public ip to our instance. But if you try to ssh into it you ‘ll get a request timed out error. This is because even though we set up the elastic ip we have not yet configured traffic to be routed into it.

Setting up an internet gateway

In order to route traffic from the internet to our VPC we need to set up an internet gateway.

//gateways.tf
resource "aws_internet_gateway" "test-env-gw" {
vpc_id = "${aws_vpc.test-env.id}"
tags {
Name = "test-env-gw"
}
}

Setting up route tables

Update subnets.tf with the following

//subnets.tf
resource "aws_route_table" "route-table-test-env" {
vpc_id = "${aws_vpc.test-env.id}"
route {
cidr_block = "0.0.0.0/0"
gateway_id = "${aws_internet_gateway.test-env-gw.id}"
}
tags {
Name = "test-env-route-table"
}
}
resource "aws_route_table_association" "subnet-association" {
subnet_id = "${aws_subnet.subnet-uno.id}"
route_table_id = "${aws_route_table.route-table-test-env.id}"
}

Here we define a aws route table and attach the internet gateway created to it. Next we create an association between the subnet and the route table we just created which will expose the subnet to the internet allowing us access.

Now it’s ready!

Now we have setup everything we want. Run terraform apply and provide the inputs correctly. This will create the infrastructure from scratch and launch an EC2 instance. After all checks are done and the instance is running you will be able to ssh to the instance successfully! :-)

--

--