terraform-skill

Diagnoses and designs safe Terraform releases, provisioners, multi-environment isolation, and fresh-host bootstrap. Use when writing or reviewing plan/apply wrappers, null_resource, remote-exec, local-exec, file provisioners, cloud-init, Docker Compose or Caddy deployment; when staging and productio

By daymade · 599 installs

npx skills add daymade/claude-code-skills --skill terraform-skill

Source repository · Upstream listing

Terraform Release and Provisioner Safety Prevent a valid looking Terraform workflow from publishing unvalidated bytes or widening a change's blast radius. Keep the user's business outcome and the actual mutation surface ahead of plan counts, green wrappers, or process completeness. Route the task Release, shared gateway, saved plan, staging receipt, or production promotion: read [release safety and environment parity.md](references/release safety and environment parity.md). A second environment, DNS ownership, state, or snapshots: read [multi env isolation.md](references/multi env isolation.md). Pre deploy checks or a validator: read [pre deploy validation.md](references/pre deploy validation.md). Fresh instance or empty data disk: read [zero to deploy checklist.md](references/zero to deploy checklist.md). One known provisioner symptom: use the matching pattern below. Operating contract 1. Use the repository's canonical wrapper when it has one. Do not bypass it with a raw Terraform, SSH, SCP, helper script, or console path because a gate rejects the planned release. 2. Prefer provider resources, image baking, cloud init, or configuration management. HashiCorp recommends exhausting purpose built alternatives because Terraform cannot model provisioner side effects predictably. When a provisioner remains necessary, make its artifact, target, lock, validation, and readback explicit. 3. Inventory every resource or recovery tool that can write the target runtime. A validator attached to only one writer does not protect another writer of the same shared service. 4. Give staging and production one required key schema. Let values differ; never let a key be required in one environment and optional, defaulted, absent, or allowed empty in another. 5. Validate the exact candidate bytes, Compose rendered environment, and immutable runtime image before the first live write or restart. Keep post deploy checks too: they detect damage but cannot prevent the first bad mutation. 6. Treat a saved plan as an executable artifact. Bind it to reviewed source/artifact identity and apply that exact file. A successful apply is not a staging receipt; record promotion evidence only after every required live verifier succeeds. 7. Require an explicit production decision at the last reversible point. A deadline, PLAN DIGEST , CONFIRM , or agent inference is not production authorization. 8. Stop after the requested result is verified. Do not turn a single service fix into full stack drift reconciliation, recovery redesign, or unrelated hardening. Provisioner traps (symptom → fix) Use these incident derived symptom patterns to choose the next falsifying check. Confirm the current source and runtime before promoting a historical cause into the present diagnosis. docker: not found in remote exec cloud init still installing Docker when provisioner SSHs in. rsync: connection unexpectedly closed in local exec Do not infer a universal Terraform limitation from this symptom. A second SSH client can lose to the target's connection budget, SSH policy, or a competing deploy. Keep local exec local: package an immutable artifact there, then use a Terraform managed upload or a purpose built deploy system. Give every apply a unique remote staging path; never share /tmp/src.tar.gz across concurrent applies. macOS BSD tar: exclude must come BEFORE the source argument. cloud init status shows "running" forever apt get y does not suppress debconf dialogs. Packages like iptables persistent block on TTY prompts. Known offenders: iptables persistent , postfix , mysql server , wireshark common . EACCES: permission denied in container logs, container Restarting Host volume dirs are root owned; container runs as non root (uid 1001). Fix before docker compose up : Find UID: grep adduser. u or USER in Dockerfile. Provisioner fails but no diagnostic output Keep fail fast behavior; attach diagnostics to failure instead of disabling set e . Otherwise an early failed command can be overwritten by a later green health check. Container Restarting — database tables missing DB migrations not in provisioner. PostgreSQL docker entrypoint initdb.d only runs on empty data dir. Explicitly create DB + run migrations: Compose uses an unexpected value despite .env Compose interpolation gives the invoking shell higher precedence than env file or project .env . An old exported value can therefore override the reviewed environment silently. Inspect what Compose actually used; unset ambient overrides when the env file is meant to be authoritative. TLS handshake fails: Invalid format for Authorization header Caddy's Cloudflare DNS module expects a scoped API Token through Bearer authentication. Do not infer credential type, validity, or permissions from length/prefix alone. Verify the token with Cloudflare's official endpoint, then exercise the exact zone operation or provider path required by the release. If the credential is absent or wrong, create a least privilege API Token through Cloudflare's current dashboard/API flow and grant only the zones/operations the provider needs. Follow the official creation contract rather than copying permission group IDs that may drift: <https://developers.cloudflare.com/fundamentals/api/get started/create token/ . TLS fails on staging but works on production — hardcoded domains Caddyfile or compose has literal domain names. Staging Caddy loads production config, tries to get certs for domains it doesn't own → ACME fails. Caddyfile : Use {$VAR} — Caddy evaluates env vars at startup. Compose : Use ${VAR:?required} — fail fast if unset or empty. Pass the env var to the gateway container so Caddy can read it: Do not stop at this local assertion. Put all runtime required keys in one schema, require the same set from every environment file, render the exact Compose service environment, and run the exact deployed Caddy image with that full environment before mutating live files. Caddy {$VAR} expansion can become an empty token before parsing; a Caddyfile default is not an environment completeness check. OAuth login fails: Social sign in failed Casdoor init data.json contains hardcoded redirect URIs. createDatabase=true only applies init data on first ever DB creation — not on restarts. Fix via SQL in provisioner: Also check AUTH CASDOOR ISSUER — it must match the Casdoor subdomain ( auth.staging.example.com ), not the app root domain. Multi environment isolation Before creating a second environment, grep .tf files for hardcoded names. See [references/multi env isolation.md](references/multi env isolation.md) for the complete matrix. Environment isolation does not mean configuration contract drift. Keep one required key manifest and the same validation path for every environment. Staging and production may use different domains, credentials, instance sizes, and feature values; they must not disagree about whether a runtime key is required, optional, allowed empty, or silently defaulted. Will fail on apply (globally unique): Resource Scope Fix SSH key pair Region "${env} deploy" SLS log project Account "${env} logs" CloudMonitor contact Account "${env} ops" DNS duplication trap : Two environments creating A records for the same name in the same Cloudflare zone → two independent record IDs → DNS round robin → ~50% traffic to wrong instance. Fix: use subdomain isolation ( staging.example.com ) or separate zones. Remember to create DNS records for ALL subdomains Caddy serves (e.g., auth.staging , minio.staging ). Snapshot cross contamination : Unfiltered data "alicloud ecs snapshots" returns ALL account snapshots. New env inherits old 100GB snapshot, fails creating 40GB disk. Gate with variable: Do NOT add count to the data source — changes its state address, causes drift. Pre deploy validation Run the cheapest checks first, but do not let a syntax check certify runtime behavior. HashiCorp's terraform validate checks syntax and internal consistency without remote state or provider APIs. Preconditions can block before their resource action; postconditions run after change and do not undo what already happened; check assertions warn and continue. Choose the mechanism by when damage must be prevented. Key checks (see [references/pre deploy validation.md](references/pre deploy validation.md)): 1. Format, initialize without backend where appropriate, and run terraform validate . 2. Compare every environment against one required key schema; reject missing, duplicate, and empty values. 3. Render the exact Compose model from the candidate files and controlled interpolation environment. 4. Run the production validator from the exact immutable image/module set against dangerous and healthy fixtures. 5. Generate a saved plan; review resource addresses, actions, target scope, artifact identity, and digest. 6. Apply that exact plan only after the required environment specific authorization. 7. Verify deployed identity and the real user path independently; only then issue a staging/promotion receipt. Zero to deployment Fresh disks expose every implicit dependency. See [references/zero to deploy checklist.md](references/zero to deploy checklist.md). Key items that break provisioners on fresh instances: 1. Directories : mkdir p /data/{svc1,svc2} in cloud init — file provisioner fails if target dir missing 2. Databases : Explicit CREATE DATABASE — PG init scripts only run on empty data dir 3. Migrations : Tracked in schema migrations table, applied idempotently 4. Provisioner ordering : depends on between resources sharing Docker networks 5. Memory : Stop non critical containers during Docker build on small instances (≤8GB) 6. Environment contract : Every runtime required value uses the same required key schema in every environment 7. Credential capability : Verify current status and the exact required operation; do not trust shape alone