Getting Started with Terraform: Setting Up Your Environment and Managing State with AWS

Getting Started with Terraform: Setting Up Your Environment and Managing State with AWS
Terraform

Introduction

Terraform has become a cornerstone for Infrastructure as Code (IaC), enabling developers to provision and manage infrastructure resources efficiently and consistently. Managing Terraform’s state is a critical component for team collaboration and ensuring infrastructure consistency. In this guide, you’ll learn how to set up Terraform, store its state remotely on an S3 bucket, and lock the state using DynamoDB to prevent conflicts. Keep in mind: This kind of setup is good for your practice and/or smaller projects.


Prerequisites

Before we dive into the setup, ensure you have the following:

  • AWS Account: You’ll need access to an AWS account.
  • AWS CLI: Installed and configured on your local machine. Installation guide.
  • Terraform CLI: Installed on your local machine. Installation guide.
  • IAM Role/Permissions: Ensure your AWS account has sufficient permissions to create and manage S3 buckets and DynamoDB tables. You can create a custom IAM policy for this purpose:
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:CreateBucket",
        "s3:PutBucketVersioning",
        "s3:PutEncryptionConfiguration",
        "s3:PutObject",
        "s3:GetObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::<unique-bucket-name>",
        "arn:aws:s3:::<unique-bucket-name>/*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "dynamodb:CreateTable",
        "dynamodb:PutItem",
        "dynamodb:GetItem",
        "dynamodb:DeleteItem",
        "dynamodb:DescribeTable"
      ],
      "Resource": "arn:aws:dynamodb:<region>:<account-id>:table/<lock-table-name>"
    }
  ]
}

Step 1: Setting Up the Development Environment

1.1 Install Terraform

Follow the official installation guide for your operating system. For example, on macOS with Homebrew:

tap 'hashicorp/tap'
brew install hashicorp/tap/terraform

Verify the installation:

terraform -version

1.2 Install and Configure AWS CLI

Install the AWS CLI using your system’s package manager or download it directly from AWS CLI Downloads. Configure it with your AWS credentials:

aws configure

You’ll be prompted to enter your AWS Access Key, Secret Key, Default Region, and Output Format.

1.3 Create a Project Directory

Create and navigate to a directory for your Terraform project:

mkdir terraform-project && cd terraform-project

Initialize Terraform:

terraform init

Step 2: Creating an IAM Role and Policy

2.1 Create an IAM Role

An IAM role allows you to delegate permissions for AWS services. Here’s how to create one:

  1. Open the IAM console at IAM Management Console.
  2. Navigate to Roles and click Create role.
  3. Select AWS Service and then choose EC2 (or another service if applicable).
  4. Click Next: Permissions.
  5. Attach a custom policy or managed policies with the required permissions (example provided below).
  6. Provide a role name, such as TerraformBackendRole, and click Create role.

2.2 Attach a Policy to the Role

Use the following example policy for S3 and DynamoDB permissions, replacing placeholders with your details:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:CreateBucket",
        "s3:PutBucketVersioning",
        "s3:PutEncryptionConfiguration",
        "s3:PutObject",
        "s3:GetObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::my-terraform-state-bucket",
        "arn:aws:s3:::my-terraform-state-bucket/*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "dynamodb:CreateTable",
        "dynamodb:PutItem",
        "dynamodb:GetItem",
        "dynamodb:DeleteItem",
        "dynamodb:DescribeTable"
      ],
      "Resource": "arn:aws:dynamodb:us-east-1:123456789012:table/TerraformLockTable"
    }
  ]
}

2.3 Assume the Role

To assume the role programmatically, use the AWS CLI:

aws sts assume-role \
    --role-arn "arn:aws:iam::123456789012:role/TerraformBackendRole" \
    --role-session-name "TerraformSession"

This will return temporary credentials that you can use to configure your AWS CLI or Terraform provider.

Configure Terraform with the Assumed Role

In your Terraform configuration file, you can specify the assumed role:

provider "aws" {
  region = "us-east-1"
  assume_role {
    role_arn     = "arn:aws:iam::123456789012:role/TerraformBackendRole"
    session_name = "TerraformSession"
  }
}

Step 3: Storing State on S3

Terraform state files keep track of the resources Terraform manages. Storing the state remotely ensures consistency and collaboration.

3.1 Create an S3 Bucket

Create an S3 bucket to store your Terraform state. Replace <unique-bucket-name> and <region> with appropriate values:

aws s3api create-bucket --bucket <unique-bucket-name> --region <region>

3.2 Enable Versioning

Enable versioning on the bucket to retain historical versions of the state file:

aws s3api put-bucket-versioning \
    --bucket <unique-bucket-name> \
    --versioning-configuration Status=Enabled

Step 4: Locking State with DynamoDB

To prevent simultaneous modifications to the Terraform state, you’ll use DynamoDB for state locking.

4.1 Create a DynamoDB Table

Create a DynamoDB table with a primary key named LockID:

aws dynamodb create-table \
    --table-name <lock-table-name> \
    --attribute-definitions AttributeName=LockID,AttributeType=S \
    --key-schema AttributeName=LockID,KeyType=HASH \
    --billing-mode PAY_PER_REQUEST

Step 5: Updating the Terraform Configuration

Configure Terraform to use your S3 bucket for remote state storage and DynamoDB for state locking. Create a backend.tf file with the following content:

terraform {
  backend "s3" {
    bucket         = "<unique-bucket-name>"
    key            = "terraform/state/terraform.tfstate"
    region         = "<region>"
    dynamodb_table = "<lock-table-name>"
    encrypt        = true
  }
}

Explanation of Configuration:

  • bucket: The name of your S3 bucket.
  • key: The path to the state file in the bucket.
  • region: The AWS region where your bucket is located.
  • dynamodb_table: The DynamoDB table for state locking.
  • encrypt: Ensures the state file is encrypted at rest.

Step 6: Verifying the Setup

6.1 Initialize the Backend

Run terraform init to initialize the backend configuration:

terraform init

If everything is configured correctly, Terraform will output a confirmation that the backend is initialized.

6.2 Common Issues and Troubleshooting

  • Bucket Already Exists: Ensure your bucket name is globally unique.
  • Insufficient Permissions: Verify your IAM role has permissions for S3 and DynamoDB.

Conclusion

In this guide, you’ve set up Terraform with a remote backend on AWS. By storing your Terraform state in an S3 bucket and locking it with DynamoDB, you’ve ensured collaboration safety and consistency.

Stay tuned for more advanced Terraform guides on madops.dev!

Read more