agentsclimarketplace

Terraform module change

Skill bmaltais/skills/terraform-module-change

End-to-end workflow for making a change to an existing Terraform module resource: fetch provider docs, implement the change, write a pure-logic tftest fixture (no provider credentials needed), wire test discovery via Makefile, run the full validation cycle (terraform test → fmt → tflint), and update RELEASE_NOTES.md. Use when the user asks to add, update, or fix an argument on an existing Terraform resource, add a new behaviour to an existing resource block, ensure a resource re-syncs on upstream changes, or "update X to use the new Y argument". Trigger on phrases like "update this resource to use", "add X to my terraform resource", "ensure sync when", "how do I make terraform detect changes in", "update my tf code for provider version X", "write tests for this tf change". DO NOT use for: scaffolding new modules (use terraform-caf-azurerm-module), full provider version upgrades across a module (use eslz-module-upgrade), or pure validation runs without code changes (use terraform-validation-workflow).From its SKILL.md

Install
npx -y skills add bmaltais/skills --skill terraform-module-change

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

9.6 KB, ~2.1k tokens by cl100k_base, as published. Nobody here has run it

Terraform Module Change Workflow

End-to-end workflow for implementing a targeted change to an existing Terraform resource, with tests, validation, and release notes. Works for any azurerm / azuread / azapi resource in any L1 or L2 blueprint.

Reference files (load as needed):


Step 1 — Read the current code

Read the target .tf file before making any changes. Note all existing arguments, count/for_each expressions, depends_on, and lifecycle blocks — these must be preserved exactly.

When your plan introduces a NEW variable + module or variable + resource block, scan for related existing patterns first:

grep -rn 'source.*<module_fragment>' *.tf
grep -rn 'for_each.*var\.' *.tf

If a semantically related variable + module/resource already exists, present the extend-vs-add tradeoff to the user and wait for a choice before writing code:

Extend existing variableAdd new variable
ProsOne variable to learn; no new module callClean separation; independent defaults
ConsCallers must know new optional fieldsDuplication if underlying resource is identical
  • Add new when: different defaults or lifecycle rules that would silently break existing entries.
  • Extend existing when: purely opt-in behaviour, identical underlying resource, variable is type = any.

Directory convention check: before referencing a subdirectory (ESLZ/, tests/fixtures/, modules/), verify it exists:

ls -1d */ 2>/dev/null

If it doesn't exist and you intend to create it, state that explicitly in the plan.


Step 2 — Fetch provider documentation

grep -A5 'required_providers' main.tf | grep 'version'

Fetch docs:

https://registry.terraform.io/providers/hashicorp/azurerm/<VERSION>/docs/resources/<resource_name>

Read Arguments Reference. Note: new optional arguments, triggers/lifecycle hooks, and Changing this forces a new resource warnings (breaking changes).

Compatibility guard: when the user targets a specific provider version that differs from the pinned version, fetch docs for both and implement only the required intersection. State explicitly which options are available in the target.


Step 2b — Post plan to ADO work item (mandatory when a WI ID is in context)

Before writing any code, set WI to Active and post the implementation plan. See ref-ado-workflow.md for exact commands.

Plan comment must include: Goal, Design decisions, File layout, Test plan, Example usage. Do not proceed to Step 3 until the comment is posted.


Step 3 — Implement the change

Edit only the target resource block(s). Do not touch unrelated code.

For all patterns (count/for_each guards, try() usage, triggers, naming rules, azuread deprecations) see ref-implementation-patterns.md.

Key rules:

  • Never remove or rename existing arguments.
  • New optional arguments: use try(var.x, <provider_default>).
  • triggers blocks: add alongside existing depends_on, never replace it.
  • After adding a conditional for_each on a module: grep all callers and wrap every external reference in try() before proceeding.

Step 4 — Write a pure-logic test fixture

Create a fixture that exercises changed logic without provider credentials. See ref-test-fixture-templates.md for full templates.

tests/fixtures/<feature_name>/
  main.tf                    # pure locals + outputs, no providers
  <feature_name>.tftest.hcl  # plan-time assertions

Minimum test set: (1) feature disabled → count == 0, (2) feature enabled → count == 1, (3) new expression produces expected value, (4) edge case.

Key rules: terraform { required_version = ">= 1.2.6" } required; no provider or resource blocks; each local comments the source file it mirrors; output declarations in fmt-canonical format (re-read after terraform fmt before editing).


Step 5 — Wire test discovery

If tests/Makefile does not exist, create it using the template in ref-makefile-template.md. If it already exists, the glob fixtures/*/main.tf auto-discovers new suites — no edits needed.


Step 6 — Run the full validation cycle

Run in this exact order. Stop on first failure.

# 1. Run tests
cd tests && make test

# 2. Format
cd ..
terraform fmt -recursive

# 3. Lint
tflint --recursive

# 4. Check deprecation warnings (run from module root)
TF_DATA_DIR="$(mktemp -d)" terraform init -backend=false -input=false -no-color >/dev/null && \
TF_DATA_DIR="$TF_DATA_DIR" terraform validate 2>&1 | grep -i 'deprecat\|warning' || true

Workspace-cleanliness guard: always use a temporary TF_DATA_DIR for init/validate to avoid .terraform/ and .terraform.lock.hcl noise in the repo root. Remove any artifacts before reporting completion.

Guard — editing fixture files after fmt: terraform fmt rewrites whitespace. If you edit a fixture after fmt, re-read it first — use the post-fmt content as oldString.

tflint warnings: fix warnings from your changes; report pre-existing ones but don't modify unrelated code. Never add ignore rules without user approval.

Deprecation warnings from validate: any Warning: Argument is deprecated in a file you changed must be fixed. See ref-implementation-patterns.md for the end_date_relative fix.


Step 7 — Update RELEASE_NOTES.md

Prepend a new entry using YYYYMMDD.N format:

## 20260525.0

- Add `triggers` to `azurerm_virtual_network_peering` so peering re-syncs when address space changes.
- Add `tests/fixtures/vnet_peering_triggers/` with 6 plan-time tests.
- Add `tests/Makefile` with auto-discovering `test` target.

Rules:

  • Newest entry at top. One bullet per logical change (not per file edited).
  • Avoid sub-release churn: consolidate all iterations within a session into one entry. Only create a new numbered entry if changes are genuinely independent.
  • If ADO WI is in context: post a completion comment after updating RELEASE_NOTES (proactively, for every edit including follow-on changes). See ref-ado-workflow.md. Include: what changed, design decisions made and rejected, test count, release note entry.
  • If the implementation required a design pivot: call it out explicitly in the completion comment — don't leave stale plan comments as the last record of intent.

Step 8 — Create ESLZ documentation (mandatory when a new variable is introduced)

If the change introduces a new top-level variable set via tfvars, create or update ESLZ/<variable_name>.tfvars. See ref-eslz-template.md for the template.

Required content: header comment, minimal example, full-featured example, inline comments for non-obvious fields, valid scope/RG notes.

Run terraform fmt on the file. Create ESLZ/ directory if it doesn't exist.


Step 9 — Final completeness sweep (mandatory)

Verify every artifact actually exists on disk before declaring done:

ls -la <new_files_created>
head -5 RELEASE_NOTES.md
ls ESLZ/*.tfvars 2>/dev/null
cd tests && make test 2>&1 | tail -5

Checklist

  • Provider docs read for the exact version in use
  • Existing arguments and depends_on preserved unchanged
  • New argument uses try() or a safe default for backward compat
  • Test fixture has terraform { required_version = ... }, no provider blocks
  • Tests cover: disabled (count=0), enabled (count=1), new behaviour, edge case
  • make test passes from tests/
  • terraform fmt -recursive produces no diff
  • tflint --recursive introduces no new warnings vs. before the change
  • RELEASE_NOTES.md updated with entry at the top
  • ESLZ tfvars example created/updated (when new variable introduced)
  • Final completeness sweep run — all artifacts confirmed on disk

What ships with it: 5 files

8.7 KB alongside SKILL.md

Keep looking

Skills are one crate of 326,835. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.