Skip to main content

Command Palette

Search for a command to run...

Terraform with AWS: A Beginner's Guide

Published
•6 min read•View as Markdown

Introduction

Terraform is an Infrastructure as Code (IaC) tool that allows you to define and provision infrastructure using a declarative configuration language. This guide will help you learn how to use Terraform with AWS from scratch.

Prerequisites

  • Basic understanding of cloud concepts

  • AWS account

  • Command line familiarity

1. Setting Up Your Environment

Installing Terraform

On macOS (using Homebrew):

brew install terraform

On Windows (using Chocolatey):

choco install terraform

On Linux:

wget https://releases.hashicorp.com/terraform/1.7.5/terraform_1.7.5_linux_amd64.zip
unzip terraform_1.7.5_linux_amd64.zip
sudo mv terraform /usr/local/bin/

Verify the installation:

terraform version

Installing AWS CLI

On macOS:

brew install awscli

On Windows: Download and run the AWS CLI MSI installer from the AWS website.

On Linux:

curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install

Configuring AWS Credentials

Run the following command and follow the prompts:

aws configure

You'll need to provide:

  • AWS Access Key ID

  • AWS Secret Access Key

  • Default region name (e.g., us-east-1)

  • Default output format (json)

2. Terraform Basics

Key Concepts

  • Provider: Plugin that allows Terraform to interact with a specific cloud provider (AWS in our case)

  • Resource: An infrastructure component (e.g., EC2 instance, S3 bucket)

  • Data Source: Used to fetch information about existing resources

  • Variables: Parameters that can be used throughout your configuration

  • Outputs: Values that are displayed after running Terraform and can be used by other configurations

  • Modules: Reusable components that encapsulate a set of resources

Terraform Workflow

  1. Initialize: terraform init

  2. Plan: terraform plan

  3. Apply: terraform apply

  4. Destroy: terraform destroy (when you want to remove resources)

3. Your First Terraform Configuration

Create a new directory for your project:

mkdir terraform-aws-demo
cd terraform-aws-demo

Create a file named main.tf:

# Configure the AWS Provider
provider "aws" {
  region = "us-east-1"
}

# Create a VPC
resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"

  tags = {
    Name = "terraform-demo-vpc"
  }
}

# Create a subnet
resource "aws_subnet" "main" {
  vpc_id     = aws_vpc.main.id
  cidr_block = "10.0.1.0/24"

  tags = {
    Name = "terraform-demo-subnet"
  }
}

Initialize Terraform:

terraform init

Plan your changes:

terraform plan

Apply your changes:

terraform apply

When prompted, type "yes" to confirm.

4. Using Variables

Create a file named variables.tf:

variable "region" {
  description = "AWS region"
  type        = string
  default     = "us-east-1"
}

variable "vpc_cidr" {
  description = "CIDR block for the VPC"
  type        = string
  default     = "10.0.0.0/16"
}

variable "subnet_cidr" {
  description = "CIDR block for the subnet"
  type        = string
  default     = "10.0.1.0/24"
}

Update main.tf to use these variables:

provider "aws" {
  region = var.region
}

resource "aws_vpc" "main" {
  cidr_block = var.vpc_cidr

  tags = {
    Name = "terraform-demo-vpc"
  }
}

resource "aws_subnet" "main" {
  vpc_id     = aws_vpc.main.id
  cidr_block = var.subnet_cidr

  tags = {
    Name = "terraform-demo-subnet"
  }
}

5. Creating an EC2 Instance

Add the following to your main.tf:

# Create a security group
resource "aws_security_group" "allow_ssh" {
  name        = "allow_ssh"
  description = "Allow SSH inbound traffic"
  vpc_id      = aws_vpc.main.id

  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

# Create an Internet Gateway
resource "aws_internet_gateway" "gw" {
  vpc_id = aws_vpc.main.id
}

# Create a route table
resource "aws_route_table" "r" {
  vpc_id = aws_vpc.main.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.gw.id
  }
}

# Associate subnet with route table
resource "aws_route_table_association" "a" {
  subnet_id      = aws_subnet.main.id
  route_table_id = aws_route_table.r.id
}

# Create an EC2 instance
resource "aws_instance" "web" {
  ami           = "ami-0c55b159cbfafe1f0"  # Amazon Linux 2 AMI ID (update as needed)
  instance_type = "t2.micro"
  subnet_id     = aws_subnet.main.id
  vpc_security_group_ids = [aws_security_group.allow_ssh.id]
  associate_public_ip_address = true

  tags = {
    Name = "terraform-demo-instance"
  }
}

# Output the public IP of the instance
output "instance_public_ip" {
  value = aws_instance.web.public_ip
}

Note: You'll need to update the AMI ID with a valid and current one for your region.

6. Organizing Your Configuration

For larger projects, it's best to organize your Terraform code into separate files:

  • main.tf: Main configuration file

  • variables.tf: Variable declarations

  • outputs.tf: Output definitions

  • terraform.tfvars: Variable values (not committed to version control when contains sensitive data)

Example outputs.tf:

output "vpc_id" {
  value = aws_vpc.main.id
}

output "subnet_id" {
  value = aws_subnet.main.id
}

output "instance_public_ip" {
  value = aws_instance.web.public_ip
}

Example terraform.tfvars:

region      = "us-west-2"
vpc_cidr    = "10.0.0.0/16"
subnet_cidr = "10.0.1.0/24"

7. Using Terraform Modules

Modules allow you to create reusable components. Create a directory structure:

terraform-aws-demo/
├── main.tf
├── variables.tf
├── outputs.tf
├── terraform.tfvars
└── modules/
    └── vpc/
        ├── main.tf
        ├── variables.tf
        └── outputs.tf

Example module (modules/vpc/main.tf):

resource "aws_vpc" "main" {
  cidr_block = var.vpc_cidr

  tags = {
    Name = var.vpc_name
  }
}

resource "aws_subnet" "main" {
  vpc_id     = aws_vpc.main.id
  cidr_block = var.subnet_cidr

  tags = {
    Name = "${var.vpc_name}-subnet"
  }
}

Example module variables (modules/vpc/variables.tf):

variable "vpc_cidr" {
  description = "CIDR block for the VPC"
  type        = string
}

variable "subnet_cidr" {
  description = "CIDR block for the subnet"
  type        = string
}

variable "vpc_name" {
  description = "Name of the VPC"
  type        = string
  default     = "terraform-demo-vpc"
}

Example module outputs (modules/vpc/outputs.tf):

output "vpc_id" {
  value = aws_vpc.main.id
}

output "subnet_id" {
  value = aws_subnet.main.id
}

Using the module in your main configuration:

module "vpc" {
  source = "./modules/vpc"

  vpc_cidr    = var.vpc_cidr
  subnet_cidr = var.subnet_cidr
  vpc_name    = "terraform-demo-module-vpc"
}

resource "aws_instance" "web" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t2.micro"
  subnet_id     = module.vpc.subnet_id

  tags = {
    Name = "terraform-demo-instance"
  }
}

8. State Management

Terraform keeps track of your infrastructure in a state file. By default, this is stored locally as terraform.tfstate. For team environments, you should use remote state storage:

terraform {
  backend "s3" {
    bucket = "my-terraform-state"
    key    = "dev/terraform.tfstate"
    region = "us-east-1"
  }
}

This requires an S3 bucket to be created beforehand.

9. Best Practices

  1. Version Control: Store your Terraform configurations in a version control system like Git

  2. Remote State: Use remote state storage for team environments

  3. State Locking: Prevent concurrent modifications to the same infrastructure

  4. Workspaces: Use workspaces for managing multiple environments (dev, staging, prod)

  5. Secrets Management: Don't hardcode sensitive information in your configuration

  6. Modules: Use modules for reusable components

  7. Tagging: Implement a consistent tagging strategy for your resources

10. Next Steps

  • Learn about Terraform Cloud for team collaboration

  • Explore more complex AWS resources (RDS, ECS, Lambda, etc.)

  • Implement a CI/CD pipeline for your Terraform code

  • Study Terraform's advanced features like dynamic blocks and functions

References