Terraform Course
Installation and Setup
Five lessons of theory. Now you actually install Terraform, configure AWS, and run your first real apply against real infrastructure. By the end of this lesson, you will have a working Terraform environment and a server running in the cloud.
This lesson covers
Installing Terraform on Mac, Windows, and Linux → Configuring AWS credentials → Your first real project from scratch → Running init, plan, apply, destroy for real
What You Need Before Starting
Three things are required before this lesson makes sense practically:
| Requirement | Details | Cost |
|---|---|---|
| AWS Account | Free tier account at aws.amazon.com | Free to create |
| AWS CLI | Command line tool for AWS — needed for credential configuration | Free |
| A terminal | Terminal on Mac/Linux, PowerShell or WSL on Windows | Built in |
About AWS costs
The resources used in this lesson — a single t2.micro EC2 instance — are covered by the AWS free tier for the first 12 months of a new account. You will destroy the instance at the end of the lesson. Even outside the free tier, a t2.micro running for less than an hour costs a few cents. Always run terraform destroy at the end of every practice session.
Step 1 — Install Terraform
Terraform is distributed as a single binary. Pick the instructions for your operating system.
New terms:
- Homebrew — the package manager for macOS. If you do not have it, install it first from brew.sh. It manages software installations and updates from the terminal.
- Chocolatey — the package manager for Windows, equivalent to Homebrew. Installable from chocolatey.org.
- apt — the package manager built into Ubuntu and Debian Linux. Already present on all Ubuntu systems.
- PATH — an environment variable that tells your operating system where to look for executable programs. When Terraform is installed correctly, the
terraformcommand is available from any directory because its location is in your PATH.
# macOS — using Homebrew
brew tap hashicorp/tap
brew install hashicorp/tap/terraform
# Windows — using Chocolatey (run PowerShell as Administrator)
choco install terraform
# Ubuntu / Debian Linux
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
# Verify the installation — run this on any operating system
terraform version
$ terraform version Terraform v1.7.0 on darwin_arm64 Your version of Terraform is out of date! The latest version is 1.7.1. You can update by downloading from https://www.terraform.io/downloads
What just happened?
- Terraform is installed and on your PATH. The
terraform versioncommand works from any directory. The version number confirms which release you have. - The update message is normal. Terraform releases frequently. Being one patch version behind is fine for learning. For production, always keep Terraform updated — security patches and bug fixes ship regularly.
- darwin_arm64 means macOS on Apple Silicon. You will see
linux_amd64on Linux orwindows_amd64on Windows. This confirms the correct binary for your architecture was installed.
Step 2 — Configure AWS Credentials
Terraform needs permission to create resources in your AWS account. It gets that permission through AWS credentials — an access key ID and a secret access key — tied to an IAM user with the right permissions.
New terms:
- IAM — Identity and Access Management. AWS's system for controlling who can do what inside your account. Every person, application, or tool that interacts with AWS does so through an IAM identity.
- IAM User — a permanent identity in AWS representing a person or application. For Terraform on a local machine, you create an IAM user with programmatic access and use its credentials.
- Access Key ID — the public part of an AWS credential pair. Starts with
AKIA. Safe to share — useless without the secret. - Secret Access Key — the private part of the credential pair. Never share this. Never commit it to Git. If it leaks, rotate it immediately from the AWS console.
- aws configure — the AWS CLI command that stores your credentials in
~/.aws/credentialson your local machine. Terraform reads from this file automatically — no configuration needed in your.tffiles.
# Step 1 — Create an IAM user in the AWS console
# Go to: AWS Console → IAM → Users → Create user
# Attach policy: AdministratorAccess (for learning only — use least privilege in production)
# Create access key: Security credentials → Create access key → CLI
# Step 2 — Configure the AWS CLI with your credentials
aws configure
$ aws configure
AWS Access Key ID [None]: AKIAIOSFODNN7EXAMPLE
AWS Secret Access Key [None]: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
Default region name [None]: us-east-1
Default output format [None]: json
$ aws sts get-caller-identity
{
"UserId": "AIDIODR4TAW7CSEXAMPLE",
"Account": "123456789012",
"Arn": "arn:aws:iam::123456789012:user/terraform-user"
}What just happened?
- aws configure wrote your credentials to disk. They are stored in
~/.aws/credentialsand~/.aws/config. Terraform reads these files automatically when it initialises — you never need to put credentials in your.tffiles. - aws sts get-caller-identity confirms it works. This command asks AWS "who am I?" and returns your account ID and user ARN. If this succeeds, Terraform can authenticate with the same credentials. Run this to verify before every new machine setup.
- The credentials in this output are examples. Your real credentials look similar but are unique to your account. Treat them like passwords.
Never put credentials in your .tf files
You will find examples online that put the access key and secret directly in the provider block like this: access_key = "AKIA...". Never do this. Credentials in code get committed to Git. Git repositories get made public by accident. People's AWS accounts get compromised within minutes of a credential appearing on GitHub — automated bots scan for them around the clock. The AWS CLI credentials file is the correct approach for local development.
Step 3 — Create Your First Real Project
Create a new directory for this project. All Terraform configuration for a project lives in one directory. Create three files — the standard starting structure for any Terraform project.
# Create the project directory and files
mkdir terraform-first-project
cd terraform-first-project
touch main.tf variables.tf outputs.tf
Now open the project in your editor and add the following content to each file:
# versions.tf — pin provider versions (create this file too)
terraform {
required_version = ">= 1.5.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = var.region
}
# variables.tf
variable "region" {
description = "AWS region to deploy into"
type = string
default = "us-east-1"
}
variable "instance_type" {
description = "EC2 instance size"
type = string
default = "t2.micro"
}
variable "environment" {
description = "Deployment environment"
type = string
default = "dev"
}
# main.tf
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = var.instance_type
tags = {
Name = "web-${var.environment}"
Environment = var.environment
ManagedBy = "Terraform"
}
}
# outputs.tf
output "instance_id" {
description = "EC2 instance ID"
value = aws_instance.web.id
}
output "public_ip" {
description = "Public IP address"
value = aws_instance.web.public_ip
}
Your project directory should now contain four files: versions.tf, variables.tf, main.tf, and outputs.tf. This four-file structure is the standard starting point for every Terraform project — not one giant file, not dozens of small ones.
Step 4 — Run the Full Workflow
Now run each command in order. This is the first time you are running Terraform against real AWS infrastructure — read every line of output.
# Initialise — downloads the AWS provider
terraform init
# Format and validate
terraform fmt
terraform validate
# Preview what will be created
terraform plan
# Build the infrastructure
terraform apply
# After you are done — destroy everything
terraform destroy
$ terraform init
Initializing the backend...
Initializing provider plugins...
- Finding hashicorp/aws versions matching "~> 5.0"...
- Installing hashicorp/aws v5.31.0...
- Installed hashicorp/aws v5.31.0 (signed by HashiCorp)
Terraform has created a lock file .terraform.lock.hcl to record
the provider selections made above.
Terraform has been successfully initialized!
$ terraform fmt
(no output — files were already clean)
$ terraform validate
Success! The configuration is valid.
$ terraform plan
Terraform will perform the following actions:
# aws_instance.web will be created
+ resource "aws_instance" "web" {
+ ami = "ami-0c55b159cbfafe1f0"
+ id = (known after apply)
+ instance_type = "t2.micro"
+ public_ip = (known after apply)
+ tags = {
+ "Environment" = "dev"
+ "ManagedBy" = "Terraform"
+ "Name" = "web-dev"
}
}
Plan: 1 to add, 0 to change, 0 to destroy.
$ terraform apply
Plan: 1 to add, 0 to change, 0 to destroy.
Do you want to perform these actions?
Enter a value: yes
aws_instance.web: Creating...
aws_instance.web: Still creating... [10s elapsed]
aws_instance.web: Still creating... [20s elapsed]
aws_instance.web: Creation complete after 32s [id=i-0abc123def456789]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Outputs:
instance_id = "i-0abc123def456789"
public_ip = "54.211.89.132"
$ terraform destroy
Plan: 0 to add, 0 to change, 1 to destroy.
Do you want to perform these actions?
Enter a value: yes
aws_instance.web: Destroying... [id=i-0abc123def456789]
aws_instance.web: Destruction complete after 30s
Destroy complete! Resources: 1 destroyed.What just happened?
- terraform init downloaded a real provider. The AWS provider binary — v5.31.0 — was fetched from the Terraform Registry and stored in
.terraform/providers/. A lock file was created. This happens once per project setup. - terraform fmt produced no output. Silence from
terraform fmtmeans all files were already correctly formatted. When it does format a file, it prints the filename. - A real EC2 instance was created in AWS. The instance ID
i-0abc123def456789is a real AWS resource ID. If you log into your AWS console right now and go to EC2, you would see this instance running. - Outputs printed after apply. The
instance_idandpublic_ipvalues were printed automatically after the apply completed — pulled from the state file where Terraform recorded them. - terraform destroy removed the instance cleanly. Terraform read the state file, identified the instance it had created, called the AWS API to terminate it, and updated the state file to reflect that nothing is managed anymore.
What Was Created on Disk
After running this workflow your project directory contains several new files and folders. Each one matters:
.terraform/
The directory containing downloaded provider binaries. Recreated by terraform init. Add to .gitignore — never commit this folder.
.terraform.lock.hcl
The provider lock file. Records the exact version and checksum of every downloaded provider. Commit this to Git — it ensures everyone on your team uses identical provider versions.
terraform.tfstate
The state file. JSON record of every resource Terraform manages. After terraform destroy, it records that no resources exist. For a team project, this file lives in a remote backend — not on your local disk. Add to .gitignore.
terraform.tfstate.backup
A backup of the previous state file, created automatically before every apply. Exists so you can recover if something goes wrong during an apply. Add to .gitignore.
The .gitignore every Terraform project needs from day one
# Terraform .terraform/ terraform.tfstate terraform.tfstate.backup *.tfplan # Never ignore — commit these # .terraform.lock.hcl
Common Mistakes
Using the wrong AMI ID for your region
AMI IDs are region-specific. The ID ami-0c55b159cbfafe1f0 is Amazon Linux 2 in us-east-1. If you deploy to eu-west-1 or any other region, this AMI ID does not exist there and the apply will fail. Always check that your AMI ID matches your region. In later lessons we cover data sources — a way to look up the correct AMI ID dynamically so you never hardcode it.
Forgetting to run terraform destroy after practice
EC2 instances bill by the hour. A t2.micro left running for a month costs around $8 outside the free tier — not catastrophic, but avoidable. Build the habit of always running terraform destroy when you finish a practice session. Check your AWS console periodically for orphaned resources.
Running terraform apply before terraform init
Without terraform init, the provider plugins do not exist in the project directory. Terraform will fail with an error saying it cannot find the provider. Always run init first — once per new project, and again whenever you add a new provider to your configuration.
Practice Questions
1. You have just created a new Terraform project directory with your .tf files. What is the first command you must run before anything else?
2. Which AWS CLI command stores your access key and secret key in the local credentials file that Terraform reads automatically?
3. After terraform init, one file must be committed to Git and one folder must be gitignored. Which file must be committed?
Quiz
1. What is the correct way to provide AWS credentials to Terraform on a local development machine?
2. You copy an ami argument from a tutorial and your apply fails with "InvalidAMIID.NotFound". What is most likely wrong?
3. You have finished a practice session and want to make sure you are not paying for any resources you created. Which command removes everything Terraform built?
Up Next · Lesson 7
Configuration Files
You created four files and it worked. Lesson 7 explains exactly what goes in each one — and the file structure decisions that make large projects manageable instead of chaotic.