#Infrastructure
446 posts
Fixing the terraform state lock Error: Finding the Cause and When to Use force-unlock
Starting with how to read the Lock Info block in the "Error acquiring the state lock" message. We cover the check procedure that separates the normal case — a teammate or CI is mid-run — from the abnormal one where a crashed run left a stale lock, the conditions under which force-unlock is safe and why misusing it corrupts state, plus where to look when the lock lives in DynamoDB and the settings that prevent recurrence.
Terraform Operations #7 OpenTofu and Licensing: The BSL Switch, the IBM Acquisition, and How to Choose
We walk the timeline from the 2023 BSL license switch and the OpenTofu fork to the CNCF acceptance and the completed IBM acquisition of HashiCorp. We cover what the BSL actually prohibits and why it is irrelevant to most users, where the two tools have diverged, how to choose based on your situation and what to check before migrating — and close out the Terraform track with a note on the Associate certification.
Terraform Operations #6 Execution Platforms Compared: Plain CI, HCP Terraform, Atlantis, and Spacelift
Using how far a hand-built GitHub Actions workflow carries you — and where it starts to hurt — as the yardstick, we compare the managed HCP Terraform, the self-hosted OSS Atlantis, and the commercial Spacelift family of platforms. We examine what a platform actually adds — policy as code, RBAC, run history visualization — along with pricing structure, and lay out selection criteria by team size.
Terraform Operations #5 Structuring at Scale: Stack Boundaries, Cross-Stack References, and When to Adopt Terragrunt
Examining the problems that appear when a single state grows large — slow plans, a wide blast radius, lock contention between teams — and how to split it into stacks along change frequency, lifetime, and owning team. We compare the two ways to pass values between stacks, terraform_remote_state versus SSM parameters, and their difference in coupling, lay out monorepo conventions, and cover how to judge when boilerplate has crossed the threshold that justifies Terragrunt.
Terraform Operations #4 Security and Cost Scanning: PR Guardrails with trivy, checkov, and Infracost
We compare and adopt trivy and checkov, static scanners that catch security misconfigurations before apply, and set a rule for handling false-positive exceptions in code with the reason attached. With Infracost commenting monthly cost changes on every PR, cost review moves to the pre-deploy stage, and default_tags-based cost tracking completes the PR guardrails.
Terraform Operations #3 Code Quality and Testing: tflint, pre-commit, and terraform test
On top of fmt and validate, tflint catches provider-knowledge errors — instance types that do not exist, for example — before plan ever runs, and pre-commit hooks automate all of it at commit time. We learn Terraform's built-in test framework, terraform test, through a module-testing example covering tftest files and the run and assert structure, then close with a practical rule for choosing between plan tests and apply tests based on their cost difference.
Terraform Operations #2 Drift and State Surgery: Scheduled Detection, import Blocks, removed Blocks
Changes made outside the code need a detection system: a scheduled plan workflow with -detailed-exitcode catches drift within a day. We lay out the decision criteria for the three responses to drift — absorb into code, revert with apply, or hand over with ignore_changes. Then we adopt console-born resources with the import block, generate a code draft with -generate-config-out, and remove resources from state only — without destroying them — using the removed block.
Terraform Operations #1 Team Workflow: plan Review on PRs, apply via GitHub Actions
Local apply concentrates strong credentials on individual laptops and skips review — problems that surface fast on a team. This post builds the standard workflow with GitHub Actions: PRs get the plan output as a comment, and merging to main triggers apply in CI. We wire up OIDC role assumption, automate plan comments on PRs, and use concurrency to prevent simultaneous applies — the first step toward removing human hands from apply.
Terraform in Practice #10 Domain, HTTPS, and Completion: Route 53, ACM Validation, CloudFront and provider alias
Connecting the Route 53 hosted zone as a data source, issuing an ACM certificate through the three-resource DNS validation idiom, and attaching an HTTPS listener with a redirect to the ALB. We cover using a provider alias to work with us-east-1 for the CloudFront certificate, then wire up a CDN serving the locked-down S3 bucket via OAC and the alias records, completing the architecture built across ten parts.
PostgreSQL LISTEN/NOTIFY: How Far It Goes, and When to Move to a Message Queue
Sorting out the practical range of PostgreSQL's built-in pub/sub. How notifications ship at commit time as part of the transaction, the 8,000-byte payload cap and the send-only-keys principle, the volatility of notifications with no listener, the commit-serialization bottleneck at high publish volume, the resulting standard combination — notifications as the wake-up signal, work in a SKIP LOCKED table queue — and the criteria for moving to a real message queue.
Terraform in Practice #9 Environment Separation: Workspace Limits, Directory Layout, and moved Refactoring
Examining where the workspace approach falls short for environment separation, then setting up the standard practical layout that splits code into modules and envs directories. We refactor the code that grew in a single directory into network, compute, and data modules, using moved blocks to relocate addresses without recreating anything, and finish with dev and prod running the same modules with different values.
PostgreSQL Row-Level Security: Making the Database Enforce Multi-Tenant Isolation
A practical introduction to row-level access control (RLS). The structure that stops the worst multi-tenant accident — a missing WHERE tenant_id — with database enforcement instead of application discipline, the basic form of ENABLE ROW LEVEL SECURITY and CREATE POLICY, the standard current_setting pattern for per-request tenant selection and its connection-pool caveat, the performance condition (an index on tenant_id), and the cases where not using RLS is the better call.