Skip to main content

If you haven’t used or heard about Terraform before, you’re at the right place. Let’s learn the basics of Terraform so that you can start automating your project on AWS and GCP Cloud providers.

1. Terraform Overview

What is this tool?

  • A provisioning declarative tools mainly use for Cloud providers such as AWS
  • Terraform follow the Infrastructure as Code Principal
  • It uses the HCL syntax (Hashicorp Configuration Language)
  • It allows deploying and rollbacking your environment
  • It is Cloud agnostic, can be use for multiple Cloud providers

Terraform can look a bit overwhelming at first, however it becomes really easy to implement overtime. People tend to create resources manually and leave them hanging there after testing, Terraform is there to help to clean up your resources and save some time and money on repetitive tasks.

2. Similar tools to Terraform

If you’re looking at automating your infrastructure, or you’re just curious what’s out there, let’s have a look on this list:

  • Cloudformation (AWS only), Heat
  • Ansible, Chef, Puppet
  • Boto (Python), Apache-libcloud, and other Cloud SDKs

Each of these tools have their pros and cons, but as we’re focusing on learning the basics of Terraform, we will move ahead and start coding!

3. Requirements

To start our journey, let’s quickly review what you will need to be equip with:

  • Admin access to your machine (to install Terraform)
  • An AWS (Amazon Web Services) or a GCP (Google Cloud Platform) account
  • Admin access to AWS or GCP
  • Brew on MacOS

a. Getting Terraform

An easy and very handy tool for having Terraform up and ready on your Mac: TFSwitch. The installation is pretty straight forward, you can check this link to see how to install it, here are the main steps to reproduce in your terminal:

brew install warrensbox/tap/tfswitch

On Linux machine, use the following command:

curl -L https://raw.githubusercontent.com/warrensbox/terraform-switcher/release/install.sh | bash

Or if you’re system is using SnapCraft:

sudo snap install tfswitch

b. Choose your Terraform version to install

Following the previous installation, you will be able to choose your Terraform version to install:

Just type tfswitch in your terminal and select the version you want to install

You can double check if Terraform installation succeeded as follow:

Version of Terraform should be display after a successful installation

4. Setting up your AWS Access

As a prerequisite, you will need to have an AWS Account available and the best practice is not to use the root user, but rather create a different user with Admin access level.

Create a security key under your user profile in AWS

Navigate to your AWS user under AWS IAM tab, select security credentials and create an Access Key and Secret Key. These credentials are very sensitive, make sure to store securely and not to share with anyone.

Once generated, store them under your ~/.aws/credentials home folder as follow:

$ vi ~/.aws/credentials

If you don’t have this folder, you can create this folder manually, Terraform would look automatically for it. Here’s a sample of how this credentials file should look like (don’t forget to update with your own credentials!)

$ cat ~/.aws/credentials
[default]
aws_access_key_id = AKIA3BIBOATTA3MU7WBM
aws_secret_access_key = rpljbnxTkz4T4ojlhBs4yDAvFO3tyPy/cb9SOVpa

Final step, you should export the default region you’re going to deploy to. You got 2 possibilities:

  • Set the default region in aws folder
  • Export from your home profile (work the best for me)

Somehow, Terraform might have issue pulling the region from the aws config file, but I place this information here, it’s good practice to set that up in your aws profile:

$ cat ~/.aws/config
[default]
region = eu-west-1

To export the default region in your shell, just add the following line in your bash_profile (Don’t forget to source your file after modification):

$ cat ~/.bash_profile
export AWS_DEFAULT_REGION=eu-west-1

5. Deploying on AWS

Continuing our journey on AWS, we will deploy our first resource with Terraform, create a new folder in your document and add the following file:

✔ ~/Documents/test
16:36 $ cat ssm-resource.tf
resource "aws_ssm_parameter" "ssm" {
  name        = "/test/terraform-plan-result"
  description = "Create a SSM parameter"
  type        = "String"
  value       = "Successful"

  tags = {
    environment = "Testing"
  }
}

What we will do in this example is to create a SSM parameter in AWS containing the value “Successful”.

Once you’ve added this file, you can initialise Terraform as follow:

$ terraform init

Initializing the backend...

Initializing provider plugins...
- Finding latest version of hashicorp/aws...
- Installing hashicorp/aws v3.53.0...
- Installed hashicorp/aws v3.53.0 (signed by HashiCorp)

Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

You can notice a new file and folder added to your folder:

  • .terraform.lock.hcl: Dependency lock file
  • .terraform: temporary Terraform folder containing downloaded plugins and external modules if any

a. Applying your changes

Time to deploy your changes! For this, just use the following command:

$ terraform apply

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_ssm_parameter.ssm will be created
  + resource "aws_ssm_parameter" "ssm" {
      + arn         = (known after apply)
      + data_type   = (known after apply)
      + description = "Create a SSM parameter"
      + id          = (known after apply)
      + key_id      = (known after apply)
      + name        = "/test/terraform-plan-result"
      + tags        = {
          + "environment" = "Testing"
        }
      + tags_all    = {
          + "environment" = "Testing"
        }
      + tier        = "Standard"
      + type        = "String"
      + value       = (sensitive value)
      + version     = (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: yes

aws_ssm_parameter.ssm: Creating...
aws_ssm_parameter.ssm: Creation complete after 1s [id=/test/terraform-plan-result]

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

By using Terraform apply command, the system will display the Terraform plan as shown above and will prompt you to whether or not accept these changes.

Let’s find out if your deployment was successful:

Check in AWS Systems Manager > Parameter Store, you should find your ssm parameter in it

Alternatively, if you have aws cli installed on your machine you can use this command in your terminal:

$ aws ssm get-parameter --name=/test/terraform-plan-result
{
    "Parameter": {
        "Name": "/test/terraform-plan-result",
        "DataType": "text",
        "LastModifiedDate": 1628246169.383,
        "Value": "Successful",
        "Version": 1,
        "Type": "String",
        "ARN": "arn:aws:ssm:eu-west-1::parameter/test/terraform-plan-result"
    }
}

Notice also that a newly created file appeared in your folder: terraform.tfstate. Terraform must store state about your managed infrastructure and configuration. This state is used by Terraform to map real world resources to your configuration, keep track of metadata, and to improve performance for large infrastructures.

b. Modifying your value

Let’s modify our previous file as follow:

$ cat ssm-resource.tf
resource "aws_ssm_parameter" "ssm" {
  name        = "/test/terraform-plan-result"
  description = "Create a SSM parameter"
  type        = "String"
  value       = "Successful again"

  tags = {
    environment = "Testing"
  }
}

If you apply your file, notice that Terraform plan would indicate that it would modify the value recorded in your SSM parameter

$ terraform apply
aws_ssm_parameter.ssm: Refreshing state... [id=/test/terraform-plan-result]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # aws_ssm_parameter.ssm will be updated in-place
  ~ resource "aws_ssm_parameter" "ssm" {
        id          = "/test/terraform-plan-result"
        name        = "/test/terraform-plan-result"
        tags        = {
            "environment" = "Testing"
        }
      ~ value       = (sensitive value)
        # (7 unchanged attributes hidden)
    }

Plan: 0 to add, 1 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_ssm_parameter.ssm: Modifying... [id=/test/terraform-plan-result]
aws_ssm_parameter.ssm: Modifications complete after 1s [id=/test/terraform-plan-result]

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

You can check changes has we previously did:

$ aws ssm get-parameter --name=/test/terraform-plan-result
{
    "Parameter": {
        "Name": "/test/terraform-plan-result",
        "DataType": "text",
        "LastModifiedDate": 1628248360.443,
        "Value": "Successful again",
        "Version": 2,
        "Type": "String",
        "ARN": "arn:aws:ssm:eu-west-1:758601614566:parameter/test/terraform-plan-result"
    }
}

Perfect, we can now jump our final step on our journey on AWS

c. Cleaning up your changes

Time to clean up your changes. It’s very simple, just use the following command:

$ terraform destroy
aws_ssm_parameter.ssm: Refreshing state... [id=/test/terraform-plan-result]

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

Terraform will perform the following actions:

  # aws_ssm_parameter.ssm will be destroyed
  - resource "aws_ssm_parameter" "ssm" {
      - arn         = "arn:aws:ssm:eu-west-1:758601614566:parameter/test/terraform-plan-result" -> null
      - data_type   = "text" -> null
      - description = "Create a SSM parameter" -> null
      - id          = "/test/terraform-plan-result" -> null
      - name        = "/test/terraform-plan-result" -> null
      - tags        = {
          - "environment" = "Testing"
        } -> null
      - tags_all    = {
          - "environment" = "Testing"
        } -> null
      - tier        = "Standard" -> null
      - type        = "String" -> null
      - value       = (sensitive value)
      - version     = 1 -> null
    }

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

Do you really want to destroy all resources?
  Terraform will destroy all your managed infrastructure, as shown above.
  There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value: yes

aws_ssm_parameter.ssm: Destroying... [id=/test/terraform-plan-result]
aws_ssm_parameter.ssm: Destruction complete after 0s

Destroy complete! Resources: 1 destroyed.

And that’s it, you learned the basics of Terraform on AWS. Let’s see now on GCP in the next section.

6. Setting up your GCP Access

In this section, we assume that you do already have a GCP account configured, it’s relatively easy if you’re using a Google account (Gmail for instance). For more information, have a look at this link. Once you’ve got your account created, you will also need to provide a project more information on this link. Once you get there, you’re good to go for the next steps:

Navigate to IAM > Service accounts and create a new one with owner access

Important to note that Owner gives a wide range of access, make sure to store this key safely. Following this previous step, create a new key for this service account as follow:

Generate a new service account key

Store these credentials under your home directory under ~/.gcloud/ (Create this folder if necessary). Once completed, export the following path to Terraform (Replace with the name of your file and don’t forget to source your file again):

$ cat ~/.bash_profile
export GOOGLE_APPLICATION_CREDENTIALS=~/.gcloud/testing-terraform-322112-0e8aa445c80d.json

And that’s it! In the next section, we will see how to deploy to GCP with Terraform.

7. Deploying to GCP

Continuing our journey on GCP, we will deploy our first resource with Terraform on GCP, add the following file in a test folder:

provider "google" {
  project     = "testing-terraform-322112"
}

resource "google_secret_manager_secret" "secret-basic" {
  secret_id = "secret"

  labels = {
    label = "my-label"
  }

  replication {
    user_managed {
      replicas {
        location = "europe-west2"
      }
      replicas {
        location = "europe-west1"
      }
    }
  }
}

Notes: You can retrieve your Project ID information from the json file you previously downloaded

As you can see in the code above, we will create a new secret entry in the Secret Manager of Google Cloud. If this is a new project, you will need to activate the Secret Manager API as follow (You might need to enable billing on your project depending of your case, secret manager is part of Google Cloud Free Tier, more information on this page):

Enable Secret Manager API, it can takes a minute

Once that’s done, we can initialise Terraform as follow:

$ terraform init

Initializing the backend...

Initializing provider plugins...
- Finding latest version of hashicorp/google...
- Installing hashicorp/google v3.78.0...
- Installed hashicorp/google v3.78.0 (signed by HashiCorp)

Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
provider "google" {
provider "google" {
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

Like for AWS, Terraform will download a set of temporary files and a folder in your current directory necessary for deploying on GCP. We can jump to the next phase.

a. Applying your changes

It is time to deploy what we did previously! To do so, just apply the following command:

$ terraform apply

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:

  # google_secret_manager_secret.secret-basic will be created
  + resource "google_secret_manager_secret" "secret-basic" {
      + create_time = (known after apply)
      + expire_time = (known after apply)
      + id          = (known after apply)
      + labels      = {
          + "label" = "my-label"
        }
      + name        = (known after apply)
      + project     = (known after apply)
      + secret_id   = "secret"

      + replication {
          + user_managed {
              + replicas {
                  + location = "europe-west2"
                }
              + replicas {
                  + location = "europe-west1"
                }
            }
        }
    }

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

google_secret_manager_secret.secret-basic: Creating...
google_secret_manager_secret.secret-basic: Creation complete after 1s [id=projects/testing-terraform-322112/secrets/secret]

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

Congratulations! You just deploy your first resource on Google Cloud, let’s check the result:

Find your newly created secret under Secret Manager of the GCP Console

As for AWS or GCP, Terraform create a file call .terraform.tfstate in your current directory to keep track of your changes as the one we’re going to do in next section

b. Modifying your value

We will edit our secret now and modify the value of our label as follow:

$ cat secrets.tf
provider "google" {
  project     = "testing-terraform-322112"
}

resource "google_secret_manager_secret" "secret-basic" {
  secret_id = "secret"

  labels = {
    label = "my-new-label"
  }

  replication {
    user_managed {
      replicas {
        location = "europe-west2"
      }
      replicas {
        location = "europe-west1"
      }
    }
  }
}

Let’s apply our new value to Secret Manager:

$ terraform apply
google_secret_manager_secret.secret-basic: Refreshing state... [id=projects/testing-terraform-322112/secrets/secret]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # google_secret_manager_secret.secret-basic will be updated in-place
  ~ resource "google_secret_manager_secret" "secret-basic" {
        id          = "projects/testing-terraform-322112/secrets/secret"
      ~ labels      = {
          ~ "label" = "my-label" -> "my-new-label"
        }
        name        = "projects/190842846554/secrets/secret"
        # (3 unchanged attributes hidden)

        # (1 unchanged block hidden)
    }

Plan: 0 to add, 1 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

google_secret_manager_secret.secret-basic: Modifying... [id=projects/testing-terraform-322112/secrets/secret]
google_secret_manager_secret.secret-basic: Modifications complete after 0s [id=projects/testing-terraform-322112/secrets/secret]

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

You can double check the result on the same previous page we saw previously. The new label should be updated.

c. Cleaning up your changes

It’s finally time for us to clean up our changes! For us to realise this, just apply the following command:

$ terraform destroy
google_secret_manager_secret.secret-basic: Refreshing state... [id=projects/testing-terraform-322112/secrets/secret]

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

Terraform will perform the following actions:

  # google_secret_manager_secret.secret-basic will be destroyed
  - resource "google_secret_manager_secret" "secret-basic" {
      - create_time = "2021-08-06T13:37:07.017995Z" -> null
      - id          = "projects/testing-terraform-322112/secrets/secret" -> null
      - labels      = {
          - "label" = "my-new-label"
        } -> null
      - name        = "projects/190842846554/secrets/secret" -> null
      - project     = "testing-terraform-322112" -> null
      - secret_id   = "secret" -> null

      - replication {
          - automatic = false -> null

          - user_managed {
              - replicas {
                  - location = "europe-west2" -> null
                }
              - replicas {
                  - location = "europe-west1" -> null
                }
            }
        }
    }

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

Do you really want to destroy all resources?
  Terraform will destroy all your managed infrastructure, as shown above.
  There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value: yes

google_secret_manager_secret.secret-basic: Destroying... [id=projects/testing-terraform-322112/secrets/secret]
google_secret_manager_secret.secret-basic: Destruction complete after 2s

Destroy complete! Resources: 1 destroyed.

You can double check on GCP Console, your secret entry should have been deleted.

8. What’s next?

Congratulations! You made it that far in this documentation, so before we continue, don’t forget to deactivate your service accounts and / or AWS Access Keys if you don’t need them anymore. For these examples, we used Admin and Owner access level but in reality, we want to use AWS IAM roles and / or Service Accounts with restricted access when we’re deploying Terraform in a pipeline for example. This is part of best practices in information security.

To go further in your Terraform journey, I would recommend checking on Terraform Documentation website to have more information on how to configure your repository and what are the Terraform best practices. I will keep posting some tips and articles on my blog so then, you’ll get the most valuable information for your infrastructure.