Part 1 - Getting set up.

By Ben Outram / 2018-06-15

There are several dependencies that we are going to address before we get started with our Terraform project. Don't panic, we are going to walk you through each one!

Set up Terraform

The Terraform installation contains a single binary called terraform. It needs to be available on the PATH.

Follow the official installation guide.

Install Terraform and verify the installation by checking that you can run terraform from your terminal.

$ terraform --version
Terraform v0.11.7

Set up an AWS user for Terraform

We're going to assume that because of your interest in this lab that you already have an AWS account. If you don't, head over to AWS and create one now before you carry on.

Terraform requires the Access key ID and Secret access key of a user that has programmatic access enabled. Rather than using the default root user account which has access to all AWS services and resources, let's create a more restricted account using the Identity and Access Management (IAM) service.

  1. Navigate to the IAM console at https://console.aws.amazon.com/iam/.
  2. Navigate to 'Users' and then click the 'Add user' button.
  3. Enter a user name and select the access type: 'Programmatic access'.
  4. Skip the Permissions section as we will cover this later.
  5. Click the 'Create user' button. Make a note of the 'Access key ID' and 'Secret access key' fields.

It's important to record the 'Secret access key' securely (e.g. in a password manager) as it won't be displayed again.

Set up a Hosted Zone in Route 53 for the desired domain of the application

Later we will create an alias record within a Hosted Zone for the domain name to point a terraform subdomain at the load balancer that distributes traffic to our web application.

This project will need a Hosted Zone in AWS Route 53 for the domain.

If you have purchased the domain via AWS then a Hosted Zone should already exist in Route 53.

If you don't have a Hosted Zone for the domain then you need to create one. Follow the steps here to migrate an existing domain's DNS service to Route 53. Route 53 will assign a unique set of four name servers for the hosted zone. When you have them you need to replace your domain registrar's NS records with these four entries.

Request a SSL certificate in AWS Certificate Manager

Our Terraform project will secure an application load balancer with a SSL certificate. The SSL certificate will be managed by AWS Certificate Manager and we need to know the Amazon Resource Name (ARN) for it.

If you already have a wildcard certificate for your domain name in AWS Certificate Manager then you can simply lookup the ARN. Open AWS Certificate Manager, select the certificate, and make a note of the ARN.

If you don't already have a certificate then you need to request one.

  1. Open AWS Certificate Manager at https://console.aws.amazon.com/acm/.
  2. Click the 'Request a certificate' button.
  3. Either enter the domain name with a wildcard (e.g. *.example.com) if you intend to use the certificate to protect several sites, or enter the domain name with the terraform hostname that this project will use (e.g. terraform.example.com). Click the 'Next' button.
  4. Choose 'DNS validation' as the validation method. This will allow you to use Route 53 to add a CNAME entry to the DNS record set to validate ownership of the domain name.
  5. Click the 'Review' button, followed by 'Confirm and request'.
  6. Follow the step to validate ownership of the domain name.
  7. Make a note of the certificate ARN.

Set up a key pair to control access to web server instances

This SSH key pair will be used to control login access to all EC2 web server instances that are created.

From a terminal (or GitBash if you are using Windows):

$ ssh-keygen -t rsa -b 4096 -C "email@example.com"

You will be prompted to enter a file in which to save the key. Enter a new filename, e.g. id_rsa_terraform_lab.

Generating public/private rsa key pair.
Enter file in which to save the key (/home/you/.ssh/id_rsa):

Optionally, enter a secure passphrase for the SSH key.

Grant User permissions required for Terraform actions

Terraform is unable to check which IAM permissions are missing during the plan phase and will fail during the apply phase if the user doesn't have the necessary permissions.

Give our Terraform IAM user the ability to configure resources and services.

TODO: PowerUserAccess is not ideal.

  1. Navigate to the IAM console at https://console.aws.amazon.com/iam/.
  2. Navigate to 'Groups' and then click the 'Create New Group' button.
  3. Enter a group name, e.g. TerraformUsers and then click the 'Next Step' button.
  4. In the list of policies, select the AWS Managed policy 'PowerUserAccess'. Click the 'Next Step' button.
  5. Click the 'Create Group' button.
  6. Open the group. From the 'Users' tab add the Terraform IAM user that we created earlier.

Create Terraform variables

We will create variables to extract the values that we've just noted in this section, i.e. the AWS IAM Access key ID, Secret Access Key, Route 53 Hosted Zone name, SSL Certificate ARN and the path to the SSH public key.

Let's also create a variable to hold the AWS region which we will use throughout the project.

Create a new folder for your project. This will be our working directory containing all terraform *.tf configuration files.

Define the variables by creating a file variables.tf with the following content:

variable "access_key" {}
variable "secret_key" {}
variable "region" {}
variable "public_key_path" {}
variable "certificate_arn" {}
variable "route53_hosted_zone_name" {}

Note that the variables contain no default values. We will use separate configuration files and assign variables within these files.

Terraform automatically loads variables from files matching terraform.tfvars and *.auto.tfvars that are present in the current directory. We can also use the -var-file flag directly when running Terraform to specify an additional file.

Create a file terraform.tfvars with the region. We will use the AWS EU (Ireland) region eu-west-1 for this example:

region = "eu-west-1"

Let's separate our other variables into a user settings file. It will contain secrets and other values only applicable to you. If you are going to commit your project code into version control then make sure you ignore this file (e.g. by using a .gitignore file).

Create a file user.tfvars with the following template:

access_key = ""
secret_key = ""
public_key_path = ""
certificate_arn = ""
route53_hosted_zone_name = ""

Populate the properties as follows. All values need to be surrounded by double quotes.

  1. access_key and secret_key are the Access Key ID and Secret Access Key of the AWS IAM user that we created for Terraform.
  2. public_key_path is the local path to the OpenSSH public key file, e.g. /home/you/.ssh/id_rsa_terraform_lab.pub.
  3. certificate_arn is the ARN of the AWS Certificate Manager SSL certificate for the domain name that you want to use, e.g. arn:aws:acm:eu-west-1:123456789012:certificate/12345678-1234-1234-1234-123456789012.
  4. route53_hosted_zone_name is the domain name of the Hosted Zone managed in Route 53 e.g. example.com.

Define the AWS Provider

In Terraform, providers are responsible for creating and managing infrastructure for a particular service. Before we can get started with AWS we need to define a provider and configure it with credentials for authentication.

Create a file project.tf and add the AWS provider:

provider "aws" {
  version    = "~> 0.1"
  region     = "${var.region}"
  access_key = "${var.access_key}"
  secret_key = "${var.secret_key}"
}

Here we use interpolation syntax to reference some of the variables that we defined earlier.

Initialise Terraform

Now that we have our folder with some Terraform configuration we can initialise it ready for use. From the working directory:

$ terraform init

This will initialise a default "local" backend and download the AWS provider plugin.

We can also try our first plan. Since we haven't defined any infrastructure just yet, there will be no changes to apply!

$ terraform plan -var-file="user.tfvars"
------------------------------------------------------------------------
No changes. Infrastructure is up-to-date.

You can find all the source code for this part of the lab here in GitHub.

More posts in this series.