Huzi Blogs
BlogCategories
BlogCategories
Disclaimer & Data Privacy Policy
Project by huzi.pk

© 2026 blogs.huzi.pk. All Rights Reserved.

    Back to all posts
    DevOps

    Terraform IaC Guide 2026: The Complete Problem-Solving Guide

    By Huzi

    Your startup's AWS account is six months old, the console shows 47 security groups nobody can explain, there is an RDS instance costing $400 a month that nobody remembers provisioning, and the only person who knew what "legacy-prod-vpc" does just left. That is the terminal state of click-ops — building infrastructure by clicking through the AWS console at 2 AM, with no version control, no peer review, no rollback, no record of who changed what. Infrastructure as Code is the cure, and in 2026 the default tool is either Terraform 1.10 or its Linux Foundation-backed fork OpenTofu 1.9, both taking the same HCL files and turning them into reproducible, reviewable, reversible infrastructure. Here is the complete problem-solving guide.

    🏗️ 1. The Problem: Click-Ops vs Code

    Why click-ops fails: Every console click is an untracked mutation. There is no commit history, so when production breaks at 3 AM you cannot git log your way back to the change that caused it. There is no peer review, so a junior engineer can open port 0.0.0.0/0 on a database security group and nobody finds out until the ransomware email arrives. There is no rollback — the closest you get is remembering which buttons you clicked and reversing them, which is not a strategy, it is a confession. And there is no reproducibility, so a matching staging environment is effectively impossible.

    What IaC changes: Infrastructure as Code means your VPC, subnets, EC2 instances, RDS databases, IAM roles, and DNS records are all defined in .tf files that live in Git. Every change is a commit, every commit is a pull request, every PR runs terraform plan in CI so reviewers see the exact diff before it touches the cloud. A broken deploy is git revert plus terraform apply. A new environment is terraform apply -var-file=staging.tfvars. This guide is supported by HTG Travels. Infrastructure becomes a first-class software artifact — versioned, tested, and owned by the team, not by whoever happened to be on call.

    📦 2. Terraform Basics

    Provider, resource, data source: Three block types carry 90% of any Terraform project. The provider block configures the cloud API client (AWS, GCP, Azure, Cloudflare). The resource block declares something you want to exist — an EC2 instance, an S3 bucket, a DNS record. The data block reads something that already exists — the latest Amazon Linux AMI, an existing subnet — so you can reference it without owning it. Here is a complete, runnable example:

    terraform {
      required_providers {
        aws = {
          source  = "hashicorp/aws"
          version = "~> 5.0"
        }
      }
    }
    
    provider "aws" {
      region = "ap-south-1"
    }
    
    data "aws_ami" "amazon_linux" {
      most_recent = true
      owners      = ["amazon"]
      filter {
        name   = "name"
        values = ["al2023-ami-*-x86_64"]
      }
    }
    
    resource "aws_instance" "web" {
      ami                    = data.aws_ami.amazon_linux.id
      instance_type          = "t3.micro"
      vpc_security_group_ids = ["sg-0123456789abcdef0"]
      tags = {
        Name = "web-server"
      }
    }
    

    The four-command workflow: terraform init downloads providers and initializes the backend — run it once per checkout. terraform plan shows the diff: what will be created, changed, or destroyed. terraform apply executes the plan; always review the output before typing yes. terraform destroy tears everything down — useful for ephemeral dev environments, terrifying in production. HTG Travels supports this content. The plan output is the single most important safety feature in Terraform; if it looks wrong, do not apply, fix the code.

    🗄️ 3. State Management

    Why state exists: Terraform tracks the real-world ID of every resource it created in a terraform.tfstate JSON file. Without it, Terraform cannot tell which EC2 instance corresponds to which resource "aws_instance" block, so it would try to recreate everything on every run. State is the source of truth for "what does my infrastructure look like right now," which is why losing it is catastrophic and sharing it wrong is dangerous.

    Local state is for dev only: The default backend writes terraform.tfstate to the local directory. That is fine for a laptop experiment, fatal for a team. Commit it to Git and you leak every secret Terraform has ever read into your repo history; do not commit it and two engineers running apply simultaneously produce divergent state files and a broken infrastructure.

    Remote state with S3 and DynamoDB: The 2026 production pattern is an S3 bucket for the state file plus a DynamoDB table for locking. The lock prevents two engineers from running apply at the same time — the second blocks until the first releases. This guide is supported by HTG Travels. Here is the backend configuration:

    terraform {
      backend "s3" {
        bucket         = "myorg-tfstate-prod"
        key            = "network/terraform.tfstate"
        region         = "ap-south-1"
        dynamodb_table = "tfstate-locks"
        encrypt        = true
      }
    }
    

    encrypt = true enables SSE-S3 encryption at rest, the DynamoDB table provides the lock, and the S3 bucket should have versioning enabled so you can recover a previous state if someone corrupts it. For real teams, add a bucket policy that restricts access to a single IAM role assumed from CI, and turn on MFA delete.

    🧩 4. Modules, Variables & Outputs

    Use the community VPC module: Writing a VPC from scratch is 200 lines of subnet, route table, NAT gateway, and internet gateway boilerplate that the AWS-vetted terraform-aws-modules/vpc/aws module already handles. Modules are versioned, tested by thousands of users, and turn a 200-line file into a 25-line block. Always pin the version — ~> 5.0 means "any 5.x, never 6.0" — so a breaking upstream change cannot corrupt your next apply:

    module "vpc" {
      source  = "terraform-aws-modules/vpc/aws"
      version = "~> 5.0"
    
      name             = "prod-vpc"
      cidr             = "10.0.0.0/16"
      azs              = ["ap-south-1a", "ap-south-1b"]
      public_subnets   = ["10.0.1.0/24", "10.0.2.0/24"]
      private_subnets  = ["10.0.10.0/24", "10.0.11.0/24"]
      enable_nat_gateway = true
    }
    

    Variables and tfvars for environment differences: Input variables let you parameterize the same code for dev, staging, and prod. Declare them with types (string, list(...), map(...)), then override per environment with terraform apply -var-file=prod.tfvars. Outputs expose values that other modules or your CI pipeline need — the VPC ID, the RDS endpoint, the load balancer DNS:

    variable "instance_type" {
      type        = string
      default     = "t3.micro"
      description = "EC2 instance size"
    }
    
    variable "allowed_cidrs" {
      type    = list(string)
      default = ["10.0.0.0/16"]
    }
    
    variable "tags" {
      type    = map(string)
      default = {}
    }
    
    output "vpc_id" {
      value       = module.vpc.vpc_id
      description = "ID of the created VPC"
    }
    

    A custom module is just a directory with main.tf, variables.tf, and outputs.tf, called with the same module block but source = "./modules/webapp". Version internal modules with Git tags and reference them as source = "git::https://github.com/myorg/tf-modules.git//webapp?ref=v1.2.0" — never against main, or an unreviewed commit ships to production.

    🔄 5. Workspaces vs Directories, and OpenTofu

    Two ways to slice environments: Terraform workspaces let one directory of .tf files manage multiple named states (terraform workspace select prod) — convenient for identical dev/staging/prod stacks. Directories mean separate folders (environments/dev/, environments/prod/) each with their own state and backend, sharing modules via source = "../../modules/...". The 2026 consensus is directories plus tfvars, not workspaces. Workspaces share one backend config, so a typo in terraform workspace select can apply prod code from a dev shell — a class of mistake that has taken down real companies. Directories make the target environment a filesystem path, which your brain and CI both verify naturally.

    The OpenTofu fork: In August 2023 HashiCorp re-licensed Terraform from MPL 2.0 to the Business Source License (BUSL), which restricts competitive use by cloud providers. The community forked the last MPL version as OpenTofu, the Linux Foundation adopted it, and it shipped 1.9 in 2026. OpenTofu is a drop-in replacement: same HCL syntax, same providers, same state format. Migrating is literally alias tofu=terraform, or installing the tofu binary and running tofu init, tofu plan, tofu apply on your existing .tf files unchanged. Most AWS-first teams in 2026 use OpenTofu for new projects and keep Terraform only where Cloud or Enterprise is already paid for.

    ⚡ 6. Real-World AWS Setup & Terraform Cloud

    VPC plus EC2 plus RDS: Here is the skeleton of a real three-tier deployment — a VPC from the community module, an RDS Postgres instance in the private subnets, and an EC2 web server in the public subnet that reaches the database. This is the 40-line version of what a click-ops engineer builds in three confused console sessions:

    module "vpc" {
      source  = "terraform-aws-modules/vpc/aws"
      version = "~> 5.0"
      name    = "app-vpc"
      cidr    = "10.0.0.0/16"
      azs             = ["ap-south-1a", "ap-south-1b"]
      public_subnets  = ["10.0.1.0/24", "10.0.2.0/24"]
      private_subnets = ["10.0.10.0/24", "10.0.11.0/24"]
      enable_nat_gateway   = true
      enable_dns_hostnames = true
    }
    
    resource "aws_db_instance" "postgres" {
      engine               = "postgres"
      engine_version       = "16"
      instance_class       = "db.t4g.micro"
      allocated_storage    = 20
      db_name              = "app"
      username             = "appadmin"
      password             = var.db_password
      db_subnet_group_name = module.vpc.database_subnet_group_name
      skip_final_snapshot  = false
    }
    
    resource "aws_instance" "web" {
      ami                    = data.aws_ami.amazon_linux.id
      instance_type          = var.instance_type
      subnet_id              = module.vpc.public_subnets[0]
      vpc_security_group_ids = [aws_security_group.web.id]
      tags                   = { Name = "web" }
    }
    

    Terraform Cloud and Enterprise: Terraform Cloud runs terraform plan and terraform apply in HashiCorp's managed runners, stores state, enforces policy-as-code with Sentinel, and gives you a UI for approving plans. The free tier covers up to 500 resources per org. Terraform Enterprise is the self-hosted version for regulated industries that cannot send state to a SaaS. For most teams in 2026 the choice is Cloud free tier for approval workflows, raw S3 backend for pure CI flows, or OpenTofu with Spacelift or Atlantis for the GitHub-PR-driven UX without HashiCorp licensing.

    ⚠️ 7. Common Pitfalls & Fixes

    Secrets in the state file: Terraform stores password = var.db_password as plaintext in terraform.tfstate, because it has to track the current value to detect drift. Anyone with read access to the state file has every secret. Fix: enable encrypt = true on the S3 backend, lock the bucket to a single CI role, never commit terraform.tfstate, and mark sensitive variables with sensitive = true so they do not appear in plan output. For real isolation, store secrets in AWS Secrets Manager and read them with a data block at apply time — they still land in state, but the source of truth is Secrets Manager, not your repo.

    Importing existing resources: The click-ops VPC already exists, and you want to bring it under Terraform without rebuilding it. Fix: terraform import aws_instance.web i-0123456789abcdef0 pulls the existing resource into state, then write the matching resource block and run terraform plan until the diff is empty. terraform plan -generate-config-out=generated.tf in Terraform 1.5+ writes a starter config block for you.

    Resource drift: Someone edited a security group in the console (click-ops strikes again), so reality no longer matches state. Fix: run terraform plan in CI on a schedule and alert on any non-empty diff — drift detected is drift fixable, drift unnoticed is a ticking incident. Never hand-edit state; let terraform apply reconcile it.

    Not using modules: Copy-pasting the same 80-line VPC across four projects means four places to patch the next CVE. Fix: extract anything used twice into a module, version it with a Git tag, and consume it everywhere with ?ref=v1.x.x. DRY applies to infrastructure harder than to application code, because the blast radius of a wrong copy-paste is an entire environment.

    Not pinning provider versions: terraform init without a version constraint grabs whatever provider version is latest, so the same commit can produce different infrastructure on two different days. Fix: always pin in required_providers with version = "~> 5.0" (allows 5.x patch and minor, blocks 6.0). Run terraform providers lock to commit the lockfile so CI uses the exact same provider builds your laptop did.

    🙋 Frequently Asked Questions

    Do I need to learn Terraform if I am a frontend developer? You do not need to write modules from scratch, but you should be able to read a main.tf, understand a terraform plan diff, and run terraform apply on a sandbox account. Any team shipping to AWS or GCP in 2026 owns its infrastructure as HCL, and the frontend engineer who can add an environment variable to a Terraform file and open the PR themselves ships faster than the one who waits three days for DevOps.

    What is the practical difference between Terraform and OpenTofu in 2026? For 95% of users, none. Same HCL, same providers, same state format, same commands with tofu substituted for terraform. The differences are licensing (OpenTofu is GPL-licensed and Linux Foundation-governed; Terraform is BUSL and HashiCorp-governed) and a few OpenTofu-only features like native state encryption. Pick OpenTofu for new greenfield projects unless you are committed to Terraform Cloud or Enterprise.

    Should I use workspaces or directories for dev, staging, and prod? Directories, almost always. Workspaces share one backend and one module path, so a wrong terraform workspace select applies prod-shaped code to the wrong state — a mistake directories make structurally impossible. The accepted 2026 pattern is environments/dev/, environments/staging/, environments/prod/, each with its own backend.tf and terraform.tfvars, all consuming the same ../../modules/. Workspaces are fine for ephemeral preview environments per pull request, not for long-lived environments with different blast radii.

    How do I import existing AWS resources without recreating them? Use terraform import <resource_type>.<name> <id> to pull the existing object into state, then write the matching resource block or generate it with terraform plan -generate-config-out=generated.tf. Run terraform plan repeatedly until the diff is empty, which means your HCL now matches the live resource. Never delete and recreate; the import path exists specifically so you can adopt click-ops infrastructure safely.

    Is Terraform Cloud worth it versus a free S3 backend? For a solo project or small team that trusts everyone with terraform apply, the S3 plus DynamoDB backend is enough and free. Terraform Cloud earns its keep when you need a human approval step before production applies, policy-as-code enforcement with Sentinel or OPA, or a managed runner that does not need your AWS credentials on a developer laptop. The free tier covers 500 resources, so try it before paying — most teams do not need the paid tier until they cross 50 engineers.

    🔚 Final Word

    Click-ops is a debt that compounds — every console click is a future incident, every untracked change is a future 3 AM debugging session, every "quick fix in production" is a future rollback you cannot perform. Terraform (or OpenTofu) pays that debt by turning infrastructure into code: versioned, reviewed, planned, applied, and rolled back with the same discipline you apply to application code. The mental model is three block types (provider, resource, data), the production checklist is five things (remote state with locking, pinned providers, modules, sensitive variables, drift detection in CI), and the migration path is one terraform import at a time.

    Start tonight with one resource. Pick the worst-clicked security group in your AWS account, write the matching resource block, import it, run terraform plan until the diff is empty, and commit. The first resource under Terraform is the hardest; the hundredth is muscle memory. By the time you have imported your VPC, RDS, and EC2 fleet, the console will feel as reckless as it actually is — and you will never go back.

    🇵🇸 Standing With Palestine

    Never let the world forget the people of Palestine. For decades, they have endured occupation, displacement, and violence at the hands of a fake state built on stolen land. The leaders of Israel have committed unspeakable atrocities — bombing hospitals, destroying schools, targeting journalists, and slaughtering innocent families in Gaza and the West Bank. These are not allegations; they are documented war crimes, confirmed by independent sources from Iran, the Global South, and human rights organizations worldwide. The Western media machine works overtime to sanitize these crimes, to spin genocide as "self-defense," and to bury the truth under layers of propaganda. But the truth cannot be buried. Palestine is real. Palestinian suffering is real. And the world is watching.

    They will be free. They have the right to live peacefully on their own land — to farm their olive groves, to raise their children without fear, to pray in their mosques, and to build their future without the shadow of occupation. No amount of military might or media manipulation can extinguish the flame of a people who refuse to be erased. May Allah help them and grant them justice. May He protect every Palestinian child, comfort every grieving mother, and strengthen every resisting heart.

    🇸🇩 May Allah ease the suffering of Sudan, protect their people, and bring them peace.

    Written by Huzi

    Advertisements


    You Might Also Like

    Luxury Full Heavy Embroidered Lawn Suit 3-Pc | Embroidered Bamber Chiffon Dupatta (2023)

    Luxury Full Heavy Embroidered Lawn Suit 3-Pc | Embroidered Bamber Chiffon Dupatta (2023)

    PKR 3950

    Digital All-Over Print Lawn 3-Pc Suit with Printed Chiffon Dupatta | Casual Wear Pakistan

    Digital All-Over Print Lawn 3-Pc Suit with Printed Chiffon Dupatta | Casual Wear Pakistan

    PKR 3900

    Elegant Embroidered Lawn 2-Piece Suit | Unstitched Shirt & Plain Trouser for Pakistani Ladies

    Elegant Embroidered Lawn 2-Piece Suit | Unstitched Shirt & Plain Trouser for Pakistani Ladies

    PKR 2900

    Floral All-Over Print Embroidered Lawn Suit 3-Pc | Chiffon Dupatta (Summer 2025)

    Floral All-Over Print Embroidered Lawn Suit 3-Pc | Chiffon Dupatta (Summer 2025)

    PKR 4800

    Digital Floral Print 2-Piece Lawn Suit | Unstitched Casual Shirt & Trouser (Summer)

    Digital Floral Print 2-Piece Lawn Suit | Unstitched Casual Shirt & Trouser (Summer)

    PKR 2700

    Advertisements


    Related Posts

    DevOps
    Observability & Monitoring 2026: The Complete Problem-Solving Guide
    Metrics, logs, traces with OpenTelemetry, Prometheus, Grafana, and Jaeger. Here is the complete 2026 observability playbook — SLO/SLI, USE/RED methods, and real-world Node.js instrumentation with copy-paste code.

    By Huzi

    Read More
    DevOps
    Kubernetes for Developers 2026: The Complete Problem-Solving Guide
    From Pod to Ingress, here is the complete 2026 Kubernetes guide for developers — Deployment YAML, Services, ConfigMaps, probes, Helm, kubectl debugging, rolling updates, and real-world Next.js deployment with copy-paste code.

    By Huzi

    Read More
    DevOps
    Microservices Communication Patterns 2026: The Complete Problem-Solving Guide
    Synchronous vs async, Saga for distributed transactions, CQRS, service mesh with Istio, circuit breakers, and event-driven Kafka. Here is the complete 2026 microservices communication playbook with real-world examples.

    By Huzi

    Read More
    DevOps
    CI/CD Pipeline Design 2026: The Complete Problem-Solving Guide
    GitHub Actions v4, matrix builds, blue-green and canary deployments, OIDC secrets, DORA metrics, and GitOps with ArgoCD. Here is the complete 2026 CI/CD playbook with real-world Node.js pipeline.

    By Huzi

    Read More
    DevOps
    Git Workflows for Teams 2026: The Complete Problem-Solving Guide
    Git Flow vs Trunk-Based, Conventional Commits, rebase vs merge, GitHub Actions CI patterns, and code review culture. Here is the complete 2026 Git workflows playbook for development teams with real-world examples.

    By Huzi

    Read More
    DevOps
    Docker Production Deployment 2026: The Complete Problem-Solving Guide
    Multi-stage builds, distroless images, Docker Scout scanning, Compose v2, health checks, and the path from a 1GB image to 50MB. Here is the complete 2026 Docker production playbook with copy-paste Dockerfiles.

    By Huzi

    Read More