Terraform AWS tutorial: getting started, explained simply
If you've decided to move your infrastructure to code rather than clicking through the AWS console, Terraform is your foundation. We'll walk you through installing Terraform, configuring it to talk to AWS, deploying your first resource, and running the core workflow that you'll repeat thousands of times in production.
Why Terraform matters for AWS deployments
Infrastructure as Code (IaC) shifts your AWS architecture from ephemeral click-ops to versioned, reviewable, repeatable declarations. Terraform reads your code, compares it to real cloud state, and makes the minimal changes needed to reach your desired state. For teams managing anything beyond a handful of resources, this eliminates manual drift, enables code review workflows, and makes disaster recovery a matter of running terraform apply again.
Before we start, you'll need an AWS account with programmatic access (an IAM user or role with appropriate permissions) and a terminal.
Install Terraform
Terraform is a single binary distributed for Linux, macOS, and Windows. You have two main options: use your OS package manager, or manage multiple versions with tfenv.
Using your system package manager
On macOS with Homebrew:
brew install terraformOn Ubuntu/Debian:
wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install terraformOn Windows with Chocolatey:
choco install terraformUsing tfenv for version management
In production environments across multiple teams, you often need different Terraform versions for different projects. tfenv solves this by letting you pin versions per directory, similar to rbenv or nvm.
Install tfenv from GitHub, then use it to install Terraform:
tfenv install 1.6.4
tfenv use 1.6.4Create a .terraform-version file in your project root to lock the version for your team:
echo "1.6.4" > .terraform-versionNow when anyone on your team runs terraform in that directory, tfenv automatically switches to version 1.6.4.
Verify the installation
Check that Terraform is installed and accessible:
terraform versionYou should see output like Terraform v1.6.4. If you see "command not found," the binary isn't in your PATH—revisit the installation steps for your OS.
Configure AWS credentials
Terraform needs to authenticate with AWS. The standard approach is to use the AWS CLI's credential files, which Terraform reads automatically.
If you haven't already, install the AWS CLI and configure it:
aws configureYou'll be prompted for your Access Key ID, Secret Access Key, default region, and output format. This creates ~/.aws/credentials and ~/.aws/config.
For local development, this is fine. In production pipelines (GitHub Actions, GitLab CI, Jenkins), you'll inject credentials as environment variables: AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY.
Create your first Terraform directory and provider block
Create a new directory for your infrastructure code:
mkdir terraform-aws-first && cd terraform-aws-firstNow create a file called main.tf. This is where you declare your infrastructure. The first block tells Terraform which provider (cloud platform) you're using and what version constraints apply.
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "ap-south-1"
}
Let's break this down:
- required_providers: Declares which Terraform providers your code needs. Here, we're saying we need the official AWS provider from Hashicorp, and we want version 5.x (the
~>operator allows patch updates but not minor version changes). - provider block: Configures the AWS provider. The
regionargument tells AWS which region to use for resources. We've chosenap-south-1(Mumbai), but you can use any AWS region likeus-east-1oreu-west-1.
You can also set the region via an environment variable: export AWS_REGION=ap-south-1, which overrides the provider block. Environment variables are often cleaner in CI/CD pipelines.
Create your first resource: an S3 bucket
Amazon S3 (Simple Storage Service) is AWS's object storage and a perfect first resource to deploy. It's straightforward, widely used, and has clear success criteria—you can see it in the AWS console immediately.
Add this to your main.tf file below the provider block:
resource "aws_s3_bucket" "my_bucket" {
bucket = "my-terraform-bucket-${data.aws_caller_identity.current.account_id}"
}
The structure is resource "resource_type" "local_name". Here, aws_s3_bucket is the resource type, and my_bucket is how you reference it in your Terraform code (not the actual AWS name).
The bucket argument sets the S3 bucket name. S3 bucket names must be globally unique across all AWS accounts. Using your account ID as a suffix ensures uniqueness. To access the account ID, we use a data source:
data "aws_caller_identity" "current" {}
Add this to your main.tf. Data sources fetch information from AWS; aws_caller_identity returns details about the AWS account you're authenticated to.
Your complete main.tf should now look like this:
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "ap-south-1"
}
data "aws_caller_identity" "current" {}
resource "aws_s3_bucket" "my_bucket" {
bucket = "my-terraform-bucket-${data.aws_caller_identity.current.account_id}"
}
Run the Terraform workflow: init, plan, apply
Terraform operates in three stages: init (download providers), plan (show what will change), and apply (make the changes).
terraform init
This command downloads the AWS provider plugin and sets up your working directory:
terraform initYou'll see output indicating that Terraform is downloading the AWS provider and initializing your backend (where Terraform stores state). On success, you'll see "Terraform has been successfully initialized!"
A new directory .terraform is created in your working directory. This contains the provider plugins. Add it to .gitignore—you don't commit this directory to version control.
terraform plan
Before making any real changes, preview what Terraform will do:
terraform planTerraform reads your .tf files, queries AWS for the current state, and outputs a diff. You'll see output similar to:
Terraform will perform the following actions:
# aws_s3_bucket.my_bucket will be created
+ resource "aws_s3_bucket" "my_bucket" {
+ arn = (known after apply)
+ bucket = "my-terraform-bucket-123456789012"
+ bucket_domain_name = (known after apply)
+ id = (known after apply)
+ region = (known after apply)
}
Plan: 1 to add, 0 to change, 0 to destroy.
The + indicates new resources. This is your chance to review changes before they hit production. In real workflows, this output is reviewed in a pull request.
terraform apply
Once you're confident in the plan, apply it:
terraform applyTerraform shows the same plan again and asks for confirmation. Type yes and press Enter. The S3 bucket is created, and Terraform writes the current state to terraform.tfstate (a JSON file tracking all your resources).
Verify the bucket exists in the AWS console, or use the AWS CLI:
aws s3 ls | grep my-terraform-bucketUnderstanding Terraform state
After terraform apply, a file named terraform.tfstate appears in your directory. This is Terraform's record of what resources exist in AWS and their properties. Never commit this file to version control—it contains sensitive data (database passwords, secret keys).
Add it to .gitignore:
echo "terraform.tfstate*" >> .gitignoreIn production, store state remotely in S3 or Terraform Cloud. We'll cover that in Day 3.
Common mistakes
- Forgetting
terraform init: If you try to runterraform planwithout initializing, you'll get an error about missing providers. Always init first. - S3 bucket name collisions: If your bucket name already exists globally,
terraform applyfails. Always include a unique suffix (account ID, timestamp, or random string). - Committing state files: State files are secrets. If your Terraform state ends up in Git history, rotate any credentials stored there immediately.
- Hardcoding AWS region: Avoid putting region directly in
main.tf`. Use environment variables or variables so your code is reusable across regions. - Ignoring the plan output: Always read
terraform plancarefully. Terraform sometimes destroys and recreates resources for subtle reasons (like renaming). This is especially dangerous in production.
Key takeaways
- Install Terraform via your OS package manager or use tfenv to manage multiple versions; always verify with
terraform version. - Configure the AWS provider with your region and ensure AWS credentials are available via
~/.aws/credentialsor environment variables. - Write your infrastructure as code using resource blocks; S3 buckets are a safe starting point.
- Follow the init → plan → apply workflow religiously; never skip the plan step.
- Keep your
terraform.tfstatefile out of Git and move it to remote storage (S3 or Terraform Cloud) before sharing code with your team.






Comments
Loading comments…