Quick Links

Automation can save you time, money, and decrease errors. One popular tool intended to assist in that goal is Terraform. Terraform is intended for versioning infrastructure through configuration as code. Another common practice is moving on-premises workloads to the cloud, and one of the biggest is Amazon Web Services (AWS). Along with offering a robust virtual machine (VM) service called Amazon Elastic Compute Cloud (EC2) , you have the ability to automate the deployment of EC2 instances via Terraform.

In this article, we explore a practical example of deploying an EC2 instance using the Terraform tool into AWS.

Terraform

To automate your infrastructure, a tool that can build, change, and version that configuration is crucial for success, which Terraform excels at. There are four steps to creating and deploying an EC2 instance:

  • Install and Initialize Terraform
  • Define the Credentials & Create the Terraform configuration
  • Plan the Terraform configuration (that is, validate)
  • Apply the Terraform configuration

Setting Up Terraform

Before you do anything else, you need to download Terraform and put it in a location that's within your system's path environmental variable (or modify your path to include the Terraform binary location). Thankfully, Terraform is distributed as a single binary, which makes it very easy to upgrade as well!

Credentials

First, you need to define our credentials, or how Terraform will actually access your AWS environment. There are a few options for storing your credentials, but it's highly recommended that you do not hardcode your credentials into your configuration file. More secure ways exist, which we will review, and it keeps it out of your version control.

Two potential options are to define the access keys within environmental variables or within an on-disk secrets file. Both are kept outside of your version control. Let's look at defining your credentials via environmental variables.

        $ export AWS_ACCESS_KEY_ID="AWSAccessKey"
$ export AWS_SECRET_ACCESS_KEY="AWSSecretKey"
$ export AWS_DEFAULT_REGION="us-west-2"

To use the above credentials within a configuration file, you would actually define the provider of aws without any options. The benefit of using environmental variables is that no credentials are stored on disk. Terraform knows to look for those environmental names when it runs for the aws provider. The downside is that the credentials are only available for as long as the session is running.

        provider "aws" {}
    

More commonly, credentials are stored in on-disk in a default location. In Linux, that location is ~/.aws/credentials. To create the file yourself, you would follow the following format:

        [default]
aws_access_key_id=AWSAccessKey
aws_secret_access_key=AWSSecretKey

If you have the AWS CLI installed, you can run aws configure and it will generate that file for you. As you can see, we define the region within the aws provider and the default profile, which matches what we have defined within our credentials file. Additionally, we need to define a region as well so that Terraform knows where to create the resources.

        provider "aws" {
  region = "us-west-2"
  profile = "default"
}

Initializing Terraform

Next, you need to initialize Terraform. If you haven't done so, create a [provision.tf](http://provision.tf) file in your working directory. Inside the file, put the provider information as created above, utilizing whatever method works best for you.

Run the terraform init command in the same location where you have defined your configuration. Terraform sets up some local files and configuration to enable it to run properly. Now, you can move on to defining your credentials and configuration. This will install the providers, namely AWS, but only if you have a .tf file that contains the basic provider within the directory that you are working.

Defining Your Configuration

Now that you have set up your credentials, you are ready to define your EC2 instance. You are going to create a pretty simple t2.micro instance with the latest Ubuntu image. A couple of things to note on the following configuration. First is the data section, where you are pulling in the AMI that you want to use. When Terraform runs, it will use this information to actually search the AWS AMI catalog, find the AMI, and return the ID for use within the data.aws_ami.ubuntu.id variable.

To break down the search you are doing within the data section, see the following steps:

  1. Using most_recent = true , only return the newest result if multiple AMIs are returned
  2. Filter out the name of the AMI to match ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-*
  3. Finally make sure that the owners is that of AMI itself, in this case 099720109477data "aws_ami" "ubuntu" { most_recent = truefilter { name = "name" values = ["ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-*"] }owners = ["099720109477"] }

Now that you've retrieved an AMI for use, define the EC2 instance that you are going to create. Using a very simple configuration, you use the ID that was defined within your data section, use an instance_type of t2.micro, and tag the instance LinuxVM. That's all that is needed for a very basic deployment of an instance.

        resource "aws_instance" "web" {
  ami = data.aws_ami.ubuntu.id
  instance_type = "t2.micro"

  tags = {
    Name = "LinuxVM"
  }
}

Many examples show ${data.aws_ami.ubuntu.id}, but in recent versions if you have an interpolation only value (that is, only some value wrapped in ${} is the value), then you can omit the ${}wrapping and leave off the quotation marks.

Planning Your Configuration

Now that your data and resource sections are defined, you need to see what the plan is for this configuration change. By running the command terraform plan, you can verify what the output will actually look like. Output is truncated for readability.

        user@computer# ./terraform plan
Refreshing Terraform state in-memory prior to plan...
...

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_instance.web will be created
  + resource "aws_instance" "web" {
      + ami = "data.aws_ami.ubuntu.id"
      + arn = (known after apply)
      + associate_public_ip_address = (known after apply)
...

Plan: 1 to add, 0 to change, 0 to destroy.

Applying Your Configuration

If everything above looks good, go ahead and actually apply your configuration changes and provision your EC2 instance by using the command terraform apply. As you can see from the following output, Terraform shows what actions are taking place and any errors that occur. Output is truncated for readability.

        user@computer:~/work# ./terraform apply
data.aws_ami.ubuntu: Refreshing state...

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_instance.web will be created
  + resource "aws_instance" "web" {
      + ami = "ami-0a7d051a1c4b54f65"
...
      + instance_type = "t2.micro"
...
      + tags = {
          + "Name" = "LinuxVM"
        }
...
Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_instance.web: Creating...
aws_instance.web: Still creating... [10s elapsed]
aws_instance.web: Still creating... [20s elapsed]
aws_instance.web: Still creating... [30s elapsed]
aws_instance.web: Creation complete after 35s [id=i-0b1758ce816b4d346]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Terraform and AWS

Moving the configuration of your AWS resources to Terraform enables you to track your changes through version control and create repeatable configurations to ease the deployment of your resources. Many resources and data sources are available within the AWS provider. Even more useful is the ability to define your entire infrastructure, AWS or another, through a Terraform configuration. With this flexibility, you will find that Terraform saves you a large amount of time and effort down the road!