Skip to content
ExponaBLOG
TERRAFORM, IAC & CLOUD AUTOMATION · DAY 002 / 100

Terraform AWS tutorial: getting started, explained simply

Install Terraform and create your first resource.

Expona Admin7 min read

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:

hcl
brew install terraform

On Ubuntu/Debian:

hcl
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 terraform

On Windows with Chocolatey:

hcl
choco install terraform

Using 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:

hcl
tfenv install 1.6.4
tfenv use 1.6.4

Create a .terraform-version file in your project root to lock the version for your team:

hcl
echo "1.6.4" > .terraform-version

Now 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:

hcl
terraform version

You 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:

hcl
aws configure

You'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:

hcl
mkdir terraform-aws-first && cd terraform-aws-first

Now 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.

hcl
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 region argument tells AWS which region to use for resources. We've chosen ap-south-1 (Mumbai), but you can use any AWS region like us-east-1 or eu-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:

hcl
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:

hcl
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:

hcl
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:

hcl
terraform init

You'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:

hcl
terraform plan

Terraform reads your .tf files, queries AWS for the current state, and outputs a diff. You'll see output similar to:

hcl
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:

hcl
terraform apply

Terraform 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:

hcl
aws s3 ls | grep my-terraform-bucket

Understanding 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:

hcl
echo "terraform.tfstate*" >> .gitignore

In 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 run terraform plan without initializing, you'll get an error about missing providers. Always init first.
  • S3 bucket name collisions: If your bucket name already exists globally, terraform apply fails. 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 plan carefully. 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/credentials or 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.tfstate file out of Git and move it to remote storage (S3 or Terraform Cloud) before sharing code with your team.
ShareXLinkedInReddit

Found this useful? Support the writing.

Buy me a coffee
Written by
Expona Admin
Site administrator

Comments

Loading comments…

Leave a comment

Comments are reviewed before they appear. Be kind and stay on topic.

Related articles