Cyber Deals - Get up to 65% off on CKA, CKAD, CKS, KCNA, KCSA exams and courses!

Terraform – Infrastructure as Code


What is Terraform?

Terraform is an open-source Infrastructure as Code (IaC) tool by HashiCorp that lets you define, provision, and manage infrastructure across multiple cloud providers and on-premises platforms using a declarative configuration language (HCL). Instead of clicking through consoles or running manual scripts, you describe the desired end state and Terraform figures out the steps to get there.

Key properties:

  • Declarative — describe what you want, not how to build it
  • Idempotent — run terraform apply multiple times safely; only differences are acted on
  • Provider ecosystem — AWS, Azure, GCP, VMware, Cloudflare, Kubernetes, and hundreds more
  • State management — Terraform tracks real-world infrastructure in a state file so it can plan incremental changes

Core Concepts

ConceptDescription
ProviderPlugin that talks to an API (AWS, Azure, Cloudflare, VMware…)
ResourceA managed infrastructure object (aws_instance, cloudflare_record…)
VariableInput value, defined in variables.tf, passed via .tfvars or environment
OutputExported value from a root or child module (IPs, DNS names, inventory)
ModuleReusable bundle of resources with inputs and outputs
Stateterraform.tfstate — the live record of what Terraform manages
WorkspaceIsolated state namespace for multi-environment deployments from one codebase
Data SourceRead-only lookup of existing infrastructure not managed by Terraform
LocalComputed value (locals.tf) to avoid repetition in resource definitions

Terraform Workflow

# 1. Initialise — download providers and modules
terraform init

# 2. Validate — check HCL syntax and module references
terraform validate

# 3. Plan — dry-run: shows what will be created/changed/destroyed
terraform plan -var-file=<workspace>.tfvars

# 4. Apply — make it real
terraform apply -var-file=<workspace>.tfvars

# 5. Destroy — tear everything down
terraform destroy -var-file=<workspace>.tfvars

Always run terraform plan before apply in shared or production environments.


Terraform Workspaces

Workspaces let you manage completely independent deployments (different AWS accounts, different environments) from a single Terraform codebase, with isolated state per workspace.

terraform workspace list                   # show all workspaces
terraform workspace new new-account        # create a workspace for a second AWS account
terraform workspace select new-account     # switch to it
terraform plan -var-file=new-account.tfvars

Convention used in practice:

WorkspaceVar FileAWS Account
defaultdefault.tfvarsExisting environment
new-accountnew-account.tfvarsNew / separate AWS account

Rules to follow:

  • Always pass -var-file=<workspace>.tfvars — never rely on auto-loaded terraform.tfvars across workspaces
  • Set AWS_PROFILE to match the active workspace before running any command
  • Keep all *.tfvars files gitignored — they contain account-specific values and credentials

Terraform Modules

Modules package a set of resources so they can be reused across environments or projects. A typical split for an AWS project:

project/
├── main.tf           ← root module: VPC, security groups, jumpserver, DNS
├── variables.tf
├── output.tf
├── aap/              ← child module: AAP EC2 nodes, EFS
│   ├── ec2-aap.tf
│   ├── efs.tf
│   ├── variables.tf
│   └── output.tf
└── aapaio/           ← child module: AAP All-in-One node
    ├── ec2-aapaio.tf
    ├── variables.tf
    └── output.tf

The root module calls child modules, passing variables and receiving outputs:

module "aap" {
  source         = "./aap"
  vpc_id         = aws_vpc.main.id
  subnet_ids     = aws_subnet.private[*].id
  instance_type  = var.aap_instance_type
  ssh_key_name   = aws_key_pair.deployer.key_name
}

output "aap_private_ips" {
  value = module.aap.instance_private_ips
}

Real-World Use Cases

Ansible Lab on AWS

Provision a complete Ansible lab (control node + managed nodes + VPC networking) with a single terraform apply. The output includes ready-to-use ansible.cfg and inventory so you can start running playbooks immediately.

What it creates:

  • VPC with public subnet
  • Ansible control node (Fedora) with Ansible pre-installed
  • Configurable number of managed nodes (Amazon Linux / RHEL)
  • Security groups allowing SSH between control and managed nodes
  • Dynamic inventory via Terraform outputs
terraform init && terraform apply -auto-approve
terraform output   # get IPs
ssh fedora@<ANSIBLE_ENGINE_IP>
ansible all -m ping

Full article: Use Terraform to Create a FREE Ansible Lab in AWS


AAP All-in-One on AWS

A single c5.4xlarge instance in a public subnet with an Elastic IP — ideal for dev/test deployments of Ansible Automation Platform without the overhead of a full HA cluster.

Feature flag in terraform.tfvars:

enable_aapaio = true
enable_aap    = false

What it creates:

  • c5.4xlarge EC2 instance (RHEL 9)
  • Elastic IP (with optional Cloudflare DNS auto-update)
  • Security group: SSH (22), AAP UI (443, 8443), HTTP (80)
  • SSH key pair from local ~/.ssh/id_rsa.pub

AAP HA Cluster on AWS

A production-grade 9-node Ansible Automation Platform cluster across two availability zones, accessed through a secure bastion host.

Feature flag:

enable_aapaio = false
enable_aap    = true

Node layout:

NodeRoleCount
aap-ac1, aap-ac2Automation Controller2
aap-gw1, aap-gw2Automation Gateway2
aap-hub1, aap-hub2Automation Hub2
aap-eda1, aap-eda2Event-Driven Ansible2
aap-db1PostgreSQL Database1

Infrastructure:

  • VPC with public (jumpserver) and private (AAP nodes) subnets across 2 AZs
  • NAT Gateway for outbound internet from private nodes
  • EFS shared storage mounted on Hub nodes
  • Nginx reverse proxy on jumpserver with Let’s Encrypt SSL (aap.lab.gineesh.com)
  • Terraform output generates a complete AAP installer inventory
# Generate AAP installer inventory
terraform output -raw aap_inventory > inventory-hosts.txt

# SSH to jumpserver
ssh -i ~/.ssh/id_rsa ec2-user@<jumpserver-eip>

# SSH to AAP nodes via bastion proxy
ssh -i ~/.ssh/id_rsa \
  -o ProxyCommand="ssh -W %h:%p -i ~/.ssh/id_rsa ec2-user@<jumpserver-eip>" \
  ec2-user@<aap-private-ip>

SSL — Nginx + Let’s Encrypt (not AWS ACM/ALB):

terraform output -raw aap_inventory > inventory.ini
cd playbooks
ansible-playbook -i ../inventory.ini setup-nginx-lb.yml

Nginx terminates HTTPS, load-balances to port 8446 across the two Gateway nodes using least_conn, and handles WebSocket for the AAP UI.


Terraform + Ansible Integration

A common pattern: Terraform provisions the infrastructure, then Ansible configures it. Terraform outputs feed directly into Ansible inventory.

# output.tf — emit a ready-to-use AAP inventory
output "aap_inventory" {
  value = templatefile("${path.module}/ansible-inventory-template.ini", {
    jumpserver_ip  = aws_eip.jumpserver.public_ip
    controller_ips = [for i in module.aap.controller_ips : i]
    gateway_ips    = [for i in module.aap.gateway_ips : i]
    hub_ips        = [for i in module.aap.hub_ips : i]
    eda_ips        = [for i in module.aap.eda_ips : i]
    db_ip          = module.aap.db_ip
    efs_dns_name   = module.aap.efs_dns_name
  })
}
# Handoff from Terraform to Ansible
terraform output -raw aap_inventory > inventory.ini
ansible-playbook -i inventory.ini site.yml

Cloudflare DNS Automation

Instead of manually updating DNS after every infrastructure recreation (Elastic IPs change on destroy), Terraform manages Cloudflare DNS records automatically.

# cloudflare-dns.tf
resource "cloudflare_record" "aap" {
  zone_id = var.cloudflare_zone_id
  name    = "aap.lab"
  value   = aws_eip.jumpserver.public_ip
  type    = "A"
  proxied = false   # must be false for Let's Encrypt to work
}

Credentials via environment variables (not in code):

export TF_VAR_cloudflare_api_token=$(cat ~/.config/cloudflare)
export TF_VAR_cloudflare_zone_id="your-zone-id"

After every terraform apply, DNS auto-updates to the new EIP — no manual steps.

Note: The zone must be the root domain (gineesh.com), not the subdomain (lab.gineesh.com).


VMware Automation

Terraform with the VMware vSphere provider automates VM creation, cloning, and import workflows on-premises — useful for AAP lab environments without public cloud spend.

Use cases covered:

  • Create VMs from templates (vmware-create-vm)
  • Import existing VMs into Terraform state (vmware-import)
  • Consistent naming and resource tagging across vSphere clusters

OpenShift on VMware

Provision the underlying VMware infrastructure (VMs, networks, storage) needed to run an OpenShift cluster on vSphere, then hand off to the OpenShift installer.


Best Practices

State management

  • Use remote state (S3 + DynamoDB for AWS, Terraform Cloud) for team environments
  • Never commit terraform.tfstate or terraform.tfstate.d/ to git
  • One state file per workspace — never share state across environments

Variables and secrets

  • All *.tfvars files gitignored — use .tfvars.example as a template
  • Sensitive values (cloudflare_api_token, AWS credentials) via environment variables or a secrets manager — never hardcoded
  • Mark sensitive outputs: sensitive = true

Modules

  • Root module for shared infrastructure (VPC, security groups, DNS)
  • Child modules for logical groups of resources (compute cluster, storage, networking)
  • Use feature flags in tfvars (enable_aap = true/false) rather than editing module source

Naming conventions

  • Resource names should encode role and index: aap-ac1, aap-gw2, aap-hub1
  • Tags on every resource: Name, Environment, ManagedBy = Terraform

Documentation

  • Always output a human-readable summary (terraform output) after apply
  • Use terraform-docs to auto-generate module documentation from variable and output blocks

Terraform Tools

ToolPurpose
terragruntDRY wrapper for Terraform — manages multiple module calls, remote state config, and environment promotion
terratestGo-based framework for writing automated tests for Terraform modules
terrascanStatic analysis for security and compliance issues in Terraform code
terratagAutomatically add or enforce tags across all Terraform resources
tfswitchSwitch quickly between multiple installed Terraform versions
tflintLinter for catching Terraform errors and enforcing best practices
terraform-docsAuto-generate documentation from Terraform module variables and outputs
InframapVisualise a tfstate or HCL as a provider-specific dependency graph
Terraform VisualBrowser-based visualiser for terraform plan output

Certification & Learning References

Official HashiCorp Resources

Concepts

Practice and Community

Video Courses


More Terraform Content on TechBeatly

For in-depth articles, tutorials, and cheat sheets visit the Terraform section on TechBeatly: