Terraform Basics — Infrastructure as Code
Learn Terraform fundamentals — HCL syntax, providers, resources, variables, outputs, and the init/plan/apply/destroy workflow.
What you'll learn
- Understand what Terraform is and why it's used
- Write basic HCL configurations with resources, variables, and outputs
- Use the core Terraform workflow: init, plan, apply, destroy
- Understand Terraform state and why it matters
- Use data sources to reference existing infrastructure
Relevant for certifications
What is Terraform?
Terraform is an open-source Infrastructure as Code (IaC) tool by HashiCorp. It lets you define infrastructure in declarative HCL (HashiCorp Configuration Language) and manage it across any cloud provider.
You write code → Terraform plans changes → Terraform applies changes → Infrastructure exists
Benefits over clicking in the console:
- Reproducible — run the same code, get the same infrastructure
- Version controlled — track infrastructure changes in Git
- Idempotent — run
applymultiple times safely - Multi-cloud — one tool for Azure, AWS, GCP, Kubernetes, and 1000+ providers
Core Terraform Workflow
terraform init # Download providers, initialise backend
terraform plan # Show what WILL change (dry run)
terraform apply # Apply changes (creates/modifies/destroys)
terraform destroy # Destroy all managed resources
Tip
Always run terraform plan before apply. Review the output carefully — a + means create, ~ means modify, - means destroy.
HCL Syntax Fundamentals
Providers
A provider is a plugin that knows how to talk to a specific API (Azure, AWS, etc.):
# versions.tf
terraform {
required_version = ">= 1.5"
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.0"
}
}
}
provider "azurerm" {
features {}
}
Resources
A resource is the main building block — it represents a piece of infrastructure:
# main.tf
resource "azurerm_resource_group" "main" {
name = "rg-myapp-prod-uksouth"
location = "UK South"
tags = {
Environment = "Production"
ManagedBy = "Terraform"
}
}
resource "azurerm_storage_account" "store" {
name = "stmyappprod001"
resource_group_name = azurerm_resource_group.main.name
location = azurerm_resource_group.main.location
account_tier = "Standard"
account_replication_type = "LRS"
}
Notice the reference: azurerm_resource_group.main.name — Terraform resolves this dependency automatically.
Variables
Variables make configurations reusable:
# variables.tf
variable "environment" {
type = string
description = "Deployment environment (dev, test, prod)"
default = "dev"
}
variable "location" {
type = string
default = "UK South"
}
variable "tags" {
type = map(string)
default = {}
}
Use variables in resources:
resource "azurerm_resource_group" "main" {
name = "rg-myapp-${var.environment}"
location = var.location
}
Provide values via:
terraform apply -var="environment=prod"
# Or via a .tfvars file:
terraform apply -var-file="prod.tfvars"
Outputs
Outputs expose values after apply — useful for consuming by other modules or scripts:
# outputs.tf
output "resource_group_id" {
value = azurerm_resource_group.main.id
description = "Resource group ID"
}
output "storage_account_name" {
value = azurerm_storage_account.store.name
}
terraform output # All outputs
terraform output storage_account_name # Specific output
Data Sources
Data sources let you query existing infrastructure that isn't managed by this Terraform code:
# Reference an existing resource group
data "azurerm_resource_group" "existing" {
name = "rg-network-prod"
}
# Use it in a resource
resource "azurerm_virtual_network" "vnet" {
name = "vnet-app"
resource_group_name = data.azurerm_resource_group.existing.name
location = data.azurerm_resource_group.existing.location
address_space = ["10.0.0.0/16"]
}
Terraform State
The state file (terraform.tfstate) is Terraform's database of what it created. It maps your HCL code to real cloud resources.
terraform state list # List all resources in state
terraform state show azurerm_resource_group.main # Show state for a resource
terraform state rm azurerm_resource_group.main # Remove from state (but NOT from cloud)
Never edit state manually
The state file is JSON. Editing it by hand will corrupt your state. Use terraform state commands instead.
Remote State
For teams, always use remote state (not local terraform.tfstate):
terraform {
backend "azurerm" {
resource_group_name = "rg-terraform-state"
storage_account_name = "stterraformstate001"
container_name = "tfstate"
key = "prod/main.tfstate"
}
}
Benefits:
- Shared across the team
- Locked during applies (prevents concurrent changes)
- Not committed to Git (state contains secrets)
Complete Example: Azure Resource Group + Storage
# main.tf
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.0"
}
}
}
provider "azurerm" {
features {}
}
resource "azurerm_resource_group" "rg" {
name = "rg-${var.project}-${var.environment}"
location = var.location
}
resource "azurerm_storage_account" "sa" {
name = "st${var.project}${var.environment}"
resource_group_name = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
account_tier = "Standard"
account_replication_type = "LRS"
min_tls_version = "TLS1_2"
}
# variables.tf
variable "project" { default = "myapp" }
variable "environment" { default = "dev" }
variable "location" { default = "UK South" }
# outputs.tf
output "rg_name" { value = azurerm_resource_group.rg.name }
output "sa_name" { value = azurerm_storage_account.sa.name }
Common Interview Questions
Q: What is Terraform state and why is it important? The state file maps Terraform code to real infrastructure. Without it, Terraform can't know what already exists and would try to recreate everything. It also stores metadata like resource dependencies.
Q: What is the difference between terraform plan and terraform apply?
plan shows what changes WOULD be made (dry run — no infrastructure is changed). apply actually executes those changes. Always review plan output before applying.
Q: What is terraform import used for?
terraform import brings existing cloud resources under Terraform management — it adds them to the state file. You still need to write the HCL resource block manually.
Q: How does Terraform handle resource dependencies?
Terraform automatically builds a dependency graph from resource references (e.g., azurerm_resource_group.main.name). Explicit dependencies can be added with depends_on.
Common Mistakes
- Committing state files to Git — state can contain secrets; use remote backends
- Not pinning provider versions —
~> 3.0means>= 3.0, < 4.0; without pinning, unexpected upgrades can break configs - Using
terraform destroyin production — always double-check your state and workspace - One large
main.tf— split intomain.tf,variables.tf,outputs.tf,versions.tffrom the start
What to Learn Next
- Terraform Modules — reusable infrastructure components
- Terraform Remote State and Backends — team workflows
- Terraform on Azure — Real Project — hands-on lab