⋮⋮
hirenpateldev — blog/posts/terraform-basics.md
⎇ main
📁
🔍
📦
Explorer
HIRENPATELDEV
📁.config
📂about
📄README.md
📄uses.md
📂blog
posts.ts
#tags.ts
📂projects
repos.ts
📂off-keyboard
📜reading.log
🚗trips.log
🏋workout.log
.git-log
{}skills.json
$contact.sh
📄resume.md
📄README.md×
posts.ts×
terraform-basics.md×
← back to postsblog/posts/terraform-basics.md
--- frontmatter ---
title: "Terraform Basics - Your First AWS EC2 Instance"
date: "Thu Jul 23 2026 00:00:00 GMT+0000 (Coordinated Universal Time)"
tags: ["terraform", "aws", "iac"]
readMins: 6

# Terraform Basics - Your First AWS EC2 Instance

A first hands-on Terraform example, provisioning a single AWS EC2 instance and walking through init, validate, plan, apply, and destroy.

I've started learning Terraform, and this is the first example from my TerraformExamples learning repo. It's the classic "hello world" of Infrastructure as Code: spin up a single AWS EC2 instance and learn the five core commands you'll use in pretty much every Terraform project after this.

Pre-requisites

Before running anything, get these out of the way first.

Install the Terraform CLI by following HashiCorp's install guide, then confirm it worked with terraform --version.

terraform --version

Install the AWS CLI as well, then run aws configure and provide your access key, secret key, and default region. This is what populates the credentials file that Terraform reads from later.

A couple of other things worth checking before you dive in:

  • A default VPC should exist in your target AWS region. A fresh AWS account has one by default, so most people won't need to do anything here.
  • Your AWS credentials need to be configured locally, at %USERPROFILE%\.aws\credentials on Windows or $HOME/.aws/credentials on macOS/Linux.
  • The AMI ID used below needs to actually exist in your region. AMI IDs are region-specific and get deregistered over time, so if terraform plan complains about it, just swap in a current one for your region. Looking AMIs up dynamically through a data source is worth covering, but that's a topic for a later, more advanced example.

The Terraform manifest

Here's the entire configuration, just one file with three blocks.

# Terraform Settings Block
terraform {
  required_version = ">= 1.6.0"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0" # Pin to a major version for reproducible plans
    }
  }
}

# Provider Block
provider "aws" {
  profile = "default" # AWS Credentials Profile configured on your local machine
  region  = "us-east-1"
}

# Resource Block
resource "aws_instance" "ec2demo" {
  ami           = "ami-0b826bb6d96d2afe4" # Amazon Linux in us-east-1, update as per your region
  instance_type = "t3.micro"
}

What each block does:

  • The terraform block pins the CLI version and declares which providers this configuration needs. Pinning aws to ~> 5.0 means "any 5.x release," which keeps plans reproducible without locking you down to one exact patch version.
  • The provider "aws" block tells Terraform which cloud account and region to talk to. Setting profile = "default" just points it at the credentials profile already sitting in your local AWS config.
  • The resource block is the actual infrastructure being requested. resource "aws_instance" "ec2demo" declares an EC2 instance, gives it a local name (ec2demo) that Terraform uses to track it in state, and sets the two required arguments, ami and instance_type.

The five core commands

This example really exists to drill in the core Terraform workflow.

terraform init      # download the provider plugin and set up the working directory
terraform validate   # check the configuration is syntactically valid
terraform plan       # preview what Terraform will create, change, or destroy
terraform apply      # apply the plan and actually create the resources
  • init only needs to run once per working directory, or whenever providers change, and it downloads the hashicorp/aws provider binary into a local .terraform folder.
  • validate and plan are both safe, read-only checks that are worth running before every apply.
  • apply is the only command that actually touches infrastructure, and since it costs real money on AWS, it's the one worth double-checking before you confirm it.

Once apply finishes, head over to the AWS Console and check EC2 to confirm the instance is actually running.

Cleaning up

Since this is just a learning example, tear it down once you're done so it doesn't keep billing you.

terraform destroy

On Windows, also clean up the local Terraform working files (state and provider cache), but keep .terraform.lock.hcl committed.

Remove-Item -Recurse -Force .terraform
Remove-Item -Force terraform.tfstate*

The macOS/Linux equivalent is the same idea.

rm -rf .terraform
rm -rf terraform.tfstate*

It's worth keeping .terraform.lock.hcl and committing it to version control, since it locks the exact provider versions used, the same way package-lock.json does for npm.

Summary

Five commands cover the entire Terraform lifecycle. init sets up the working directory, validate and plan act as a safety net before touching anything real, apply provisions the infrastructure, and destroy tears it back down. Everything else in Terraform, whether that's variables, modules, remote state, or multiple environments, ultimately builds on top of this same loop.

thanks for reading. say hi →6 min read
⎇ main● 0 ⚠ 0UTF-8LF⌘K⌘F>_Ln 1, Col 1Spaces: 2Hiren · Ahmedabad