Back to Home

How to Use Terraform with AWS: Complete Guide

This comprehensive guide teaches you how to use Terraform with AWS to manage cloud infrastructure as code. Covering provider setup, resource definition, state management, security best practices, and CI/CD integration, you'll gain practical skills for production-grade AWS deployments.

How to Use Terraform with AWS: Step-by-Step Tutorial
Advertisement 728x90

Using Terraform with AWS: The Complete Guide

Using Terraform with AWS: The Complete Guide

Infrastructure management has evolved from manual console clicks and fragile scripts to declarative code that can be versioned, reviewed, and shared. For teams building on Amazon Web Services, Terraform has become the standard for infrastructure as code (IaC)—and knowing how to use terraform with aws effectively is now a core skill for cloud engineers. This guide walks you through the complete workflow, from setting up the AWS provider to deploying real resources with production-grade practices.

What You'll Learn

By the end of this guide, you'll understand the complete Terraform workflow on AWS—from provider configuration and resource definition to state management and team collaboration. You'll be able to deploy, update, and destroy AWS infrastructure confidently using Terraform's declarative approach, and you'll walk away with a clear mental model for structuring your own projects. You'll also learn practical safeguards to prevent costly mistakes like deploying to the wrong AWS account.

Google AdInline article slot

Understanding the Core Concepts: Providers, Resources, and State

Before writing any configuration, it helps to understand Terraform's fundamental building blocks. Unlike AWS CloudFormation—which is native to AWS and assumes resources are being created within an AWS account—Terraform is cloud-agnostic and requires you to explicitly declare providers that interact with specific APIs .

What Is a Provider?

A provider is a plugin that acts as the bridge between Terraform and a target platform. The AWS Provider is what allows Terraform to call AWS APIs to create, read, update, and delete resources . Because Terraform supports multiple providers—AWS, Kubernetes, Helm, and even third-party SaaS tools—you can provision an Amazon EKS cluster and deploy Helm charts into it within the same configuration .

Resources and Data Sources

A resource defines a component of your infrastructure: an EC2 instance, an S3 bucket, or a security group. Resources are declared declaratively—you specify what you want, and Terraform figures out how to create it .

Google AdInline article slot

A data source, on the other hand, queries AWS for information about existing resources. For example, instead of hardcoding an AMI ID that might change or vary by region, you can use a data source to fetch the most recent Amazon Linux 2023 AMI dynamically .

State: The Source of Truth

Terraform maintains a state file that tracks the resources it manages. This state maps your configuration to real infrastructure and is essential for planning updates, detecting drift, and destroying resources cleanly . For team environments, storing state in a remote backend like Amazon S3 is highly recommended to prevent conflicts and ensure consistency .


Getting Started: Installing Tools and Configuring AWS Credentials

Prerequisites

Before you write a single line of configuration, set up your local environment:

Google AdInline article slot
  1. Terraform CLI 1.5 or later – Download the binary and add it to your PATH .
  2. AWS CLI – Install and configure it with IAM credentials that have sufficient permissions .
  3. An AWS account – Use the Free Tier for practice to avoid unexpected charges .

Authenticating the AWS Provider

Terraform's AWS provider authenticates using the same methods as the AWS CLI. The simplest approach is to set environment variables with your IAM credentials:

export AWS_ACCESS_KEY_ID=<your_access_key>
export AWS_SECRET_ACCESS_KEY=<your_secret_key>

Alternatively, Terraform can read from the default credential file at ~/.aws/credentials .


Step 1: Write Your First Terraform Configuration

Create a new directory for your project and define the provider and resources. Terraform configuration files use HashiCorp Configuration Language (HCL) and end with .tf .

The terraform Block

The terraform block configures Terraform itself, including provider sources and version constraints. Pinning provider versions prevents unexpected drift when the provider is updated .

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
  required_version = ">= 1.2"
}

The source argument points to the provider in the Terraform Registry. HashiCorp maintains the AWS provider, so its source is hashicorp/aws .

The provider Block

The provider block configures the AWS provider, including the default region:

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

Define an EC2 Instance

A resource block declares an infrastructure component. Here's how to define an EC2 instance using a data source to fetch the latest Ubuntu AMI:

data "aws_ami" "ubuntu" {
  most_recent = true
  owners      = ["099720109477"] # Canonical

  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-*"]
  }
}

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

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

Using a data source for the AMI keeps your configuration dynamic and portable across regions .


Step 2: Initialize, Plan, and Apply

The Terraform workflow consists of three core commands :

terraform init

Initialize the working directory. This downloads the required provider plugins and sets up the backend:

terraform init

Terraform reads the required_providers block and installs the AWS provider version you specified .

terraform plan

Preview the changes Terraform will make to your infrastructure:

terraform plan

The plan shows additions, modifications, and deletions—this is your opportunity to catch mistakes before applying .

terraform apply

Apply the configuration to create resources. Terraform will show the plan again and prompt for confirmation:

terraform apply

Type yes to proceed. Terraform will provision the EC2 instance and output the results .

terraform destroy

When you're done, clean up to avoid charges:

terraform destroy

Terraform calculates what to delete based on the state file and prompts for confirmation .


Step 3: Add a Security Group and Provisioning Logic

A server is useless without network access. Add a security group to control inbound and outbound traffic :

resource "aws_security_group" "web" {
  name        = "web-sg"
  description = "Allow HTTP and SSH"
  vpc_id      = var.vpc_id

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

  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["10.0.0.0/16"] # Restrict SSH
  }

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

Attach the security group to your EC2 instance by referencing aws_security_group.web.id in the vpc_security_group_ids argument.

Automating Software Installation with User Data

Use EC2 user data to install software when the instance first boots. For example, to install Nginx on Amazon Linux 2023 :

resource "aws_instance" "web" {
  ami                    = data.aws_ami.al2023.id
  instance_type          = "t3.micro"
  vpc_security_group_ids = [aws_security_group.web.id]
  key_name               = var.key_name

  user_data = <<-EOF
              #!/bin/bash
              dnf install -y nginx
              systemctl start nginx
              echo "<h1>Hello from Terraform</h1>" > /usr/share/nginx/html/index.html
              EOF
}

User data runs during the initial boot and is a simple way to bootstrap instances without additional configuration management tools .


Step 4: Use Variables, Outputs, and Modules

Variables for Reusability

Hardcoding values makes configurations brittle. Define variables in a variables.tf file :

variable "instance_type" {
  description = "EC2 instance type"
  type        = string
  default     = "t2.micro"
}

variable "aws_region" {
  description = "AWS region"
  type        = string
  default     = "us-west-2"
}

Reference variables in your configuration with var.instance_type.

Outputs to Display Information

Outputs let you retrieve information after applying, such as the instance's public IP:

output "instance_public_ip" {
  description = "Public IP of the EC2 instance"
  value       = aws_instance.app_server.public_ip
}

Modules for Reusable Components

Modules are containers for multiple resources that can be shared across projects. The Terraform Registry provides thousands of community modules, and you can also write your own . Using modules keeps your root configuration clean and promotes reusability.


Step 5: Remote State and Team Collaboration

For any team environment, storing state locally is dangerous—it can become outdated or conflict with other users' changes. Configure a remote backend, typically using Amazon S3 with DynamoDB for state locking :

terraform {
  backend "s3" {
    bucket         = "my-terraform-state-bucket"
    key            = "prod/terraform.tfstate"
    region         = "us-west-2"
    encrypt        = true
    dynamodb_table = "terraform-locks"
  }
}

This configuration ensures state is shared and locked during apply operations, preventing concurrent modifications that could corrupt the state file .


Step 6: Security Best Practices

Restrict Provider to Specific AWS Accounts

One of the most dangerous mistakes is running Terraform against the wrong AWS account—for example, intending to deploy to development but accidentally using production credentials. The allowed_account_ids setting in the provider block creates a safety net :

provider "aws" {
  region  = "us-west-2"
  allowed_account_ids = ["123456789012"] # Only this account
}

If you attempt to run Terraform with credentials from a different account, the plan will error immediately, preventing costly mistakes .

Use IAM Roles, Not Static Keys

For EC2 instances, always use IAM instance profiles rather than embedding static credentials. This improves security and simplifies credential rotation .

Version Control and Review

All Terraform configurations should be stored in version control. Pull requests for infrastructure changes should be reviewed just like application code—this is the core value of IaC .


How to Use Terraform with AWS in CI/CD Pipelines

Integrating Terraform into CI/CD pipelines automates deployments and enforces consistency. A typical GitHub Actions workflow :

name: Terraform Apply
on:
  push:
    branches: [ main ]
jobs:
  terraform:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: hashicorp/setup-terraform@v1
      - name: Terraform Init
        run: terraform init
      - name: Terraform Plan
        run: terraform plan
      - name: Terraform Apply
        run: terraform apply -auto-approve

This approach eliminates manual apply steps and ensures every main branch merge deploys infrastructure that has been reviewed and tested .


Frequently Asked Questions

1. What is the difference between Terraform and AWS CloudFormation?

Terraform is cloud-agnostic and supports multiple providers—AWS, Azure, Google Cloud, and hundreds of others—while CloudFormation is AWS-only. Terraform also offers a more flexible workflow with plan/apply stages and supports a wider range of third-party resources .

2. Where does Terraform store the state of my infrastructure?

By default, Terraform stores state in a local terraform.tfstate file. For production and team use, you should configure a remote backend like Amazon S3 to store state centrally with locking to prevent conflicts .

3. What are Terraform modules and why should I use them?

Modules are containers for multiple resources that can be reused across projects. Using modules reduces duplication, enforces consistency, and lets you share infrastructure patterns across teams via the Terraform Registry .

4. How do I avoid accidentally deploying to the wrong AWS account?

Use the allowed_account_ids setting in your AWS provider block to restrict Terraform to specific account IDs. If the credentials don't match the allowed list, Terraform will error before making any changes .

5. What is the difference between terraform plan and terraform apply?

terraform plan previews the changes Terraform would make to your infrastructure. terraform apply executes those changes. Always run plan first to catch mistakes before modifying resources .


Sources

  1. Terrateam, "How to deploy Nginx with Terraform on AWS" (2026)
  2. AWS Prescriptive Guidance, "Understanding Terraform resources and providers" (2024)
  3. AWS Prescriptive Guidance, "Terraform AWS Provider best practices" (2024)
  4. HashiCorp Developer, "Create infrastructure" (2025)
  5. Refine, "Getting Started with Terraform on AWS" (2024)
  6. DevelopersIO, "Terraform AWS Provider allowed_account_ids" (2025)
  7. GitHub, "terraform-basics-aws" repository

— Editorial Team

Advertisement 728x90

Read Next