Terraform Fundamentals: HCL and the Core Workflow
Terraform uses HCL (HashiCorp Configuration Language) — a declarative language that describes the desired state of infrastructure. You write what you want to exist; Terraform figures out how to create it. Core workflow: Write (define resources in .tf files), Plan (terraform plan — preview what will be created, modified, or destroyed), Apply (terraform apply — provision the infrastructure), Destroy (terraform destroy — remove all managed resources). Key HCL constructs: resource blocks (declare a real infrastructure object — resource 'aws_instance' 'web' { ami = '...', instance_type = 't3.micro' }), data sources (query existing infrastructure not managed by this config — data 'aws_ami' 'latest' { ... }), variables (input values — var.region), locals (computed values within the config — local.common_tags), outputs (export values after apply — useful for module consumers or cross-stack references). Providers: plugins that enable Terraform to talk to specific APIs (AWS, Azure, GCP, Kubernetes, GitHub, Datadog) — declared in required_providers block, downloaded by terraform init.
Terraform State: Management and Best Practices
State is Terraform's memory — it maps your configuration to real-world resources. Without state, Terraform cannot know whether a resource already exists or needs to be created. Local state (terraform.tfstate) is the default — stored in the working directory, suitable only for solo experiments. Remote state is required for teams: Terraform Cloud/Enterprise, S3 bucket with DynamoDB locking (AWS), Azure Blob Storage, GCS bucket. State locking prevents two engineers from running apply simultaneously and corrupting state — DynamoDB provides this for S3 backends. Sensitive values in state: secrets end up in plaintext in state files (passwords, private keys) — always encrypt state storage and restrict access. Key state commands: terraform state list (show all managed resources), terraform state show resource.name (detail on one resource), terraform state mv (rename a resource in state), terraform state rm (remove a resource from state without destroying it — for importing or abandoning management), terraform import (bring an existing resource under Terraform management). Workspaces: isolated state files within the same backend — useful for dev/staging/prod environments from the same config.
Modules: Reusable Infrastructure Components
Modules are the fundamental unit of code reuse in Terraform. Every Terraform configuration is a module — the root module is the directory you run Terraform from. Child modules are called from the root using module blocks. Public modules: Terraform Registry (registry.terraform.io) hosts community and verified modules — use them with a source = 'registry address' and version constraint. Module inputs: variables declared in the module, passed from the caller. Module outputs: values the module exposes to the caller. Module best practices: keep modules focused and small (one concern per module), pin to specific versions (not 'latest'), use semantic versioning, document variables and outputs. Terraform Cloud private registry: publish internal modules for your organisation — consume with the same source syntax. Meta-arguments that work inside modules and resources: count (create N identical copies), for_each (create one instance per item in a map or set — more powerful than count because resources have meaningful names), depends_on (explicit dependency when implicit dependency graph is insufficient), lifecycle (create_before_destroy, prevent_destroy, ignore_changes).
Terraform Cloud and Enterprise
Terraform Cloud provides remote execution, state management, and collaboration features. Key features: remote runs (plan and apply execute in Terraform Cloud, not your laptop — consistent environment, audit log), VCS integration (trigger plans automatically on Git push, review plans in PRs before merging), workspace-level variables (store secrets in Terraform Cloud, not in repo), policy as code (OPA or Sentinel — enforce tagging standards, restrict instance types, require encryption — runs as a policy check step in the pipeline). Terraform Cloud workflow: create workspace > connect VCS repo > set variables > queue a plan > review and approve > apply. Free tier: up to five active workspaces, remote state storage, VCS integration. Team and Business tiers add: SSO, audit logging, self-hosted agents, policy sets, private module registry. Terraform Enterprise: self-hosted version for air-gapped environments or organizations with strict data residency requirements.
Terraform Testing and Best Practices
Testing Terraform code: terraform validate (checks HCL syntax and internal consistency — fast, runs without credentials), terraform plan (requires credentials — creates an execution plan, shows what will change), checkov (static analysis for security misconfigurations — checks for unencrypted S3, public security groups, missing encryption keys), tfsec (similar security scanner), terraform test (native testing framework in Terraform 1.6+ — write .tftest.hcl files that create temporary infrastructure, assert outputs, then destroy). The Terraform testing pyramid: static validation (validate, lint) > plan (preview changes) > apply in non-prod (integration test with real infrastructure) > apply in prod (production deployment with approval gates). Variable validation: add validation blocks inside variable declarations to enforce constraints (regex patterns, allowed values lists) before Terraform tries to create resources. Version constraints: use >= 1.0, < 2.0 style constraints in required_version and required_providers to prevent unexpected breaking changes.