Hackernoon logoAn Intro to Terraform and AWS by@ncribt

An Intro to Terraform and AWS

Terraform is an open-source “infrastructure-as-code” software. Instead of using an interface or a command-line-interface to configure your infrastructure, you can describe your infrastructure with code. Terraform can interact with most of the big cloud platforms such as Google Cloud Platform (GCP), Azure, IBM Cloud, Oracle Cloud, Alibaba Cloud, etc. It means that if someday you need to migrate a part of your infrastructure to another cloud platform, it would be easier with Terraform. It also means that you only have to learn one. language” instead of each cloud platform’s specific solution.
image
NARUHODO Hacker Noon profile picture

NARUHODO

Software engineer during the week and occasionaly apprentice blogger during the weekend. I like JS and Rust. 🤓

Terraform is an open-source “infrastructure-as-code” software. Infrastructure-as-code means that instead of using an interface or a CLI (command-line-interface) to configure your infrastructure on a cloud platform such as AWS (Amazon Web Services), you can describe your infrastructure with code and let Terraform do its magic.

AWS provides their own solution for infrastructure-as-code. It is called CloudFormation. They also have a “simplified” one called SAM (Serverless Application Model) to build a serverless infrastructure (SAM is using CloudFormation behind the scenes).

Why should I learn Terraform if I’m using AWS and they provide their own solution?

To avoid vendor lock-in. Terraform can interact with most of the big cloud platforms such as Google Cloud Platform (GCP), Azure, IBM Cloud, Oracle Cloud, Alibaba Cloud, etc. It means that if someday you need to migrate a part of your infrastructure to another cloud platform, it would be easier with Terraform. It also means that you only have to learn one infrastructure-as-code “language” instead of each cloud platform’s specific solution.

Ok, but why even bother with infrastructure-as-cloud when I already have my AWS CLI, or I can use the web interface?

Depending on how big your infrastructure is, it can get difficult to keep track of all the resources you have. With infrastructure-as-code, you can just read it anytime you need. It also makes it simpler when you’re in a team, everybody has access to the infrastructure, and everybody can make changes. Because it is all code, you can use Git for collaboration and reviews.

Isn’t it overkill?

It could be; it depends on your infrastructure. If you’re wondering if it is overkill, you should also wonder if using AWS, for example, is the right decision. If the product you’re building is relatively small, or your goal is to go fast, you should consider using another cloud platform such as Heroku.

In that case, you wouldn’t have to worry about infrastructure at all. You would only need to link your Git repository and commit your code. Heroku would handle the infrastructure for you.

What about the Serverless Framework?

If your goal is to build a serverless application, then the Serverless Framework would make more sense. They also provide an infrastructure-as-code approach to deploy your functions to a cloud platform. But if you need other resources such as a database, or a messaging system, you would still need to configure it manually or use CloudFormation in parallel (if using AWS). Terraform and the Serverless Framework are not the same thing, and they can be used together.

Enough talking, we’re going to set up a simple EC2 instance (an EC2 instance is a simple virtual machine) on AWS with Terraform, so that you can see the different steps required to get started and how to use Terraform.

What you will need in order to follow the guide:

- An AWS account (we will be using the free-tier resources only, so you won’t be charged anything)

- The Terraform CLI

The first step will be to create a user (if you don’t have one already) to use on your local machine (Terraform will be using that user to perform actions on AWS). To keep it simple, I will give the user admin rights.

Navigate to AWS Console > IAM > Users and click on the “Add user” button:

image

Pick a username and tick the “Programmatic access” checkbox:

image

Select the “Attach existing policies directly” section, then tick the “AdministratorAccess” policy:

image

You can skip the tags screen, confirm your information on the confirmation screen then create the user.

Finally, take note of the “Access key ID” and “Secret access key”, we will need them later.

image

Create a new directory somewhere on your computer:

mkdir terraform-aws-example

Navigate inside the directory:

cd terraform-aws-example

Create a new file main.tf:

touch main.tf

That will be our main Terraform file. To keep this example simple, we will work only with that file, but in a real-world case you would split your Terraform configuration in different files.

Add the following to the main.tf file:

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

This tells Terraform that we will use a provider called aws and where it can find the source for it as well as the version we need.

Now we can configure the provider, add the following in the main.tf file after the previous block.

provider "aws" {
  region = "eu-central-1" # Use whatever region makes sense for you
  access_key = "my-access-key" # Put your "Access key ID" here
  secret_key = "my-secret-key" # Put your "Secret access key" here
}

For the region, I use eu-central-1 because I’m in Europe, but feel free to swap it (list of available regions).

For the access_key and secret_key, you need to copy the ones we got after creating the user on the AWS Console. This is, of course, bad practice if you plan to commit the code on Git, then it would be better to use environment variables, for example.

Finally, we can add the configuration for our EC2 instance.

resource "aws_instance" "my_instance" {
  ami           = "ami-043097594a7df80ec" # ID of the image (Amazon Linux, Ubuntu Server, etc)
  instance_type = "t2.micro" # Free tier instance with 1 vCPU and 1 GiB of memory
}

For the instance_type we use t2.micro because it is free (for up to 750 hours per month).

For the ami (which means Amazon Machine Image) it will depend on the region you picked; you can follow the official AWS guide here. The easiest way if you’re not familiar with AWS is to go directly in the AWS Console > EC2, make sure you’re on the correct region (top right), click on Instances (under Instances from the left side menu), then click on Launch instances

image

Then get the ID of the Amazon Linux 2 (Free Tier)

image

The final version of your main.tf should look like this:

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

provider "aws" {
  region = "eu-central-1" # Use whatever region makes sense for you
  access_key = "my-access-key" # Put your "Access key ID" here
  secret_key = "my-secret-key" # Put your "Secret access key" here
}

resource "aws_instance" "my_instance" {
  ami           = "ami-043097594a7df80ec" # ID of the image (Amazon Linux, Ubuntu Server, etc)
  instance_type = "t2.micro" # Free tier instance with 1 vCPU and 1 GiB of memory
}

We are now ready to create our instance with Terraform.

Make sure you have the Terraform CLI installed, if not head to the download page and follow the instructions relevant to your OS.

Before applying our configuration, we need to initialize the Terraform project with the following command:

terraform init

To format your code, you can use the command:

terraform fmt

To validate your code, you can use the command:

terraform validate

Let’s now create our instance with:

terraform apply

Terraform will show you a scary list of all the resources it will create. It should look like this:

Terraform used the selected providers to generate the following execution plan. Resource actions are
indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_instance.my_instance will be created
  + resource "aws_instance" "my_instance" {
      + ami                                  = "ami-043097594a7df80ec"
      + arn                                  = (known after apply)
      + associate_public_ip_address          = (known after apply)
      + availability_zone                    = (known after apply)
      + cpu_core_count                       = (known after apply)
      + cpu_threads_per_core                 = (known after apply)
      + get_password_data                    = false
      + host_id                              = (known after apply)
      + id                                   = (known after apply)
      + instance_initiated_shutdown_behavior = (known after apply)
      + instance_state                       = (known after apply)
      + instance_type                        = "t2.micro"
      + ipv6_address_count                   = (known after apply)
      + ipv6_addresses                       = (known after apply)
      + key_name                             = (known after apply)
      + outpost_arn                          = (known after apply)
      + password_data                        = (known after apply)
      + placement_group                      = (known after apply)
      + primary_network_interface_id         = (known after apply)
      + private_dns                          = (known after apply)
      + private_ip                           = (known after apply)
      + public_dns                           = (known after apply)
      + public_ip                            = (known after apply)
      + secondary_private_ips                = (known after apply)
      + security_groups                      = (known after apply)
      + source_dest_check                    = true
      + subnet_id                            = (known after apply)
      + tags_all                             = (known after apply)
      + tenancy                              = (known after apply)
      + vpc_security_group_ids               = (known after apply)

      + ebs_block_device {
          + delete_on_termination = (known after apply)
          + device_name           = (known after apply)
          + encrypted             = (known after apply)
          + iops                  = (known after apply)
          + kms_key_id            = (known after apply)
          + snapshot_id           = (known after apply)
          + tags                  = (known after apply)
          + throughput            = (known after apply)
          + volume_id             = (known after apply)
          + volume_size           = (known after apply)
          + volume_type           = (known after apply)
        }

      + enclave_options {
          + enabled = (known after apply)
        }

      + ephemeral_block_device {
          + device_name  = (known after apply)
          + no_device    = (known after apply)
          + virtual_name = (known after apply)
        }

      + metadata_options {
          + http_endpoint               = (known after apply)
          + http_put_response_hop_limit = (known after apply)
          + http_tokens                 = (known after apply)
        }

      + network_interface {
          + delete_on_termination = (known after apply)
          + device_index          = (known after apply)
          + network_interface_id  = (known after apply)
        }

      + root_block_device {
          + delete_on_termination = (known after apply)
          + device_name           = (known after apply)
          + encrypted             = (known after apply)
          + iops                  = (known after apply)
          + kms_key_id            = (known after apply)
          + tags                  = (known after apply)
          + throughput            = (known after apply)
          + volume_id             = (known after apply)
          + volume_size           = (known after apply)
          + volume_type           = (known after apply)
        }
    }

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:

Type yes to confirm. Terraform will then create your instance on AWS, and it will let you know when it's done.

Now head to the AWS Console > EC2 > Instances, make sure you’re on the correct region. You should see your new instance in the list.

image

Congratulations, you’ve just used code to describe then create a resource on AWS!

By default, the Terraform CLI keeps track of your configuration state locally. You can configure an external source for your state such as a S3 bucket or you can also use the Terraform Cloud (they provide a free plan).

If you need to change something, simply update the code and use terraform apply. Terraform will compare your configuration with its state and determine what needs to be changed.

If you need to destroy the whole infrastructure, you can use

terraform destroy

Terraform will then destroy all the resources related to your configuration.

This is where the guide ends, and I hope it was helpful!

Final Thoughts

There is a lot more to learn about Terraform. You can use variables to create resources dynamically. You can configure what output Terraform should display after applying your configuration, etc.

You will also need to learn the APIs relevant for your cloud platform providers, but they have good documentation. For example, the AWS API documentation can be found here.

Discover more posts from me on my personal blog: https://naruhodo.dev

Previously published here: https://naruhodo.dev/terraform-and-aws-basics/

NARUHODO Hacker Noon profile picture
by NARUHODO @ncribt. Software engineer during the week and occasionaly apprentice blogger during the weekend. I like JS and Rust. 🤓Read my stories

Tags

Join Hacker Noon

Create your free account to unlock your custom reading experience.