Mcaf module
Author or structurally review a Schuberg Philis MCAF Terraform module (`terraform-<provider>-mcaf-<name>`). Use when creating a new MCAF module, modifying an existing one, or reviewing a PR against one. This skill layers MCAF-specific rules on top of the generic `terraform` skill — start there for baseline guidance (module layout, variable/output design, testing, CI, security) and come here for the Schuberg Philis specifics (`terraform.tf` not `versions.tf`, reuse of `mcaf-github-workflows`, `var.tags` on every taggable resource, native `terraform test` with `mock_provider`, conventional-commit PR labels, release-drafter flow, and the recurring anti-patterns observed across the 91-module corpus). For a full qualitative review producing good/bad/verdict reports, use the `review-mcaf` skill instead.From its SKILL.md
npx -y skills add schubergphilis/agents.md --skill mcaf-moduleAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 8 stars8 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
12.2 KB, as published. Nobody here has run it
MCAF module
MCAF-specific overlay on top of the generic terraform skill. Read that first for baseline Terraform/OpenTofu rules. This skill only documents the deltas — the things MCAF does that a generic module wouldn't.
The authoritative source for every MCAF rule is GUIDE.md bundled with this skill.
When this skill applies
- Creating a new repo named
terraform-<provider>-mcaf-<name>. - Editing files in an existing MCAF module.
- Reviewing a PR to an MCAF module for structural compliance.
- User mentions "MCAF module", "mcaf-github-workflows", or a
schubergphilis/terraform-*-mcaf-*repo.
For a full qualitative review (good/bad, design critique, verdict), use review-mcaf.
Reference implementations
- AWS full-surface:
terraform-aws-mcaf-s3 - AWS thin wrapper:
terraform-aws-mcaf-kms - Azure full-surface:
terraform-azure-mcaf-storage-account - GitHub / GitLab / TFE reference:
terraform-github-mcaf-repository - Shared CI:
schubergphilis/mcaf-github-workflows
Diff against the reference for that provider when unsure.
MCAF deltas from the generic terraform skill
These override or tighten the base skill. Everything not listed here defers to the generic skill.
Filenames
- Use
terraform.tf(notversions.tf) forrequired_version+required_providers. - Prefer
Taskfile.yaml(lowercase.yaml). - No camelCase file names (
functionApp.tfetc.).
Version pinning floors
The exact floor number is not important — what matters is the bounds: a floor-only constraint for Terraform and most providers (no upper bound), and a major-version range for AzureAD, Azurerm, and Datadog (floor + upper bound at the next major). Don't bikeshed the minor; just ensure the constraint shape is correct.
- Terraform:
required_versionmust allow the newest Terraform (no upper bound). Any floor is acceptable (e.g.">= 1.9"). - AWS:
>= 6(no upper bound). - AzureAD:
>= 3, < 4. Azurerm:>= 4, < 5. Datadog:>= 3, < 4. Upper bound at the next major.
Resource labels
- AWS / GitHub / Datadog / TFE / GitLab →
"default". - Azure →
"this". - Never mix in one repo.
Tags
Every taggable resource references var.tags. No try(var.tags) cargo-cult. No local.tags indirection unless the module genuinely needs to inject extra tags.
CI via mcaf-github-workflows
Five workflow files copied verbatim from schubergphilis/mcaf-github-workflows, with the DO NOT CHANGE THIS FILE DIRECTLY header intact:
pr-validation.yamlterraform-validation.yamllabel-synchronization.yamlrelease-drafter.yamlupdate-changelog.yaml
Plus terraform-test.yaml if using Taskfile-driven tests.
Do not hand-edit these files. If you need different behaviour, change it upstream in mcaf-github-workflows.
PR requirements
- Conventional-commit title with one of:
breaking,bug,chore,documentation,enhancement,feature,fix,security. - At least one matching label.
- Breaking change → entry in
UPGRADING.md.
Pre-commit
The canonical config is maintained in mcaf-github-workflows/sync-root/.pre-commit-config.yaml. Read the repository's copy for exact hooks and versions.
Key policy notes on skipped checkov rules:
CKV_GIT_5/CKV_GLB_1— MCAF requires ≥1 approval, not ≥2.CKV_TF_1— modules use semver tags, not commit SHAs.
Taskfile
version: "3"
env: { TF_IN_AUTOMATION: 1 }
tasks:
default:
cmds: [{ cmd: "task --list", ignore_error: true }]
silent: true
clean:
cmds:
- rm -rf .terraform.lock.hcl .terraform
- rm -rf **/.terraform.lock.hcl **/.terraform
silent: true
test:
cmds: ["terraform init", "terraform test"]
silent: true
verbose-test:
cmds: ["terraform init", "terraform test -verbose"]
silent: true
README structure
# terraform-<provider>-mcaf-<name>
<one-line purpose>
IMPORTANT: We do not pin modules to versions in our examples. We highly recommend that in your code you pin the version to the exact version you are using so that your infrastructure remains stable.
<prose sections for non-obvious behaviour>
<!-- BEGIN_TF_DOCS -->
<!-- END_TF_DOCS -->
## Licensing
100% Open Source and licensed under the Apache License Version 2.0. See [LICENSE](LICENSE) for full details.
Release flow
release-draftermaintains a draft release on every merge.- Version bump rules:
breaking→ majorfeature/enhancement→ minorfix/bug/security/documentation/chore→ patch
no-changeloglabel excludes a PR from release notes.- Only MCAF Contributors publish releases.
Lifecycle / currency (MCAF floors)
Block new code adding instances of these:
- Terraform
required_versionset and allows the newest Terraform (no upper bound). Any floor is acceptable. - AWS
>= 6(no upper bound). AzureAD>= 3, < 4, Azurerm>= 4, < 5, Datadog>= 3, < 4(upper bound at next major). - No exact or patch-tight provider pins inside a module.
- No deprecated AWS resources (
aws_s3_bucket_object, inlineaws_s3_bucketsub-blocks). - No deprecated HCL constructors (
list(...),map(...)). - Action pins up to date:
hashicorp/setup-terraform@v3,actions/checkout@v4,actions/github-script@v7,terraform-docs/[email protected].
If a module has older action pins, it has drifted from mcaf-github-workflows — re-sync the workflow files rather than hand-bumping.
Supply chain (MCAF-specific)
Blocking findings for a PR review:
- Any action using
@master/@main— mutable ref. - Any action from
Dirrk,triat,danielchabr,anothrNick,jessfraz— replace with the MCAF-standard equivalent. - Node
package.jsonwithout a committed lockfile. - Python dependency in a Lambda
requirements.txtwithout==X.Y.Z. - Docker image action pinned by mutable tag — prefer
@sha256:....
MCAF trusted publishers (major-tag OK):
actions, github, hashicorp, terraform-docs, terraform-linters, bridgecrewio, arduino, release-drafter, amannn, marocchino, agilepathway, crazy-max, stefanzweifel.
Everything else → SHA pin or replace.
Recurring MCAF anti-patterns
The generic terraform skill's anti-pattern list covers most cases. These are the MCAF-corpus-specific additions:
- Two sources of truth for resource group on Azure —
var.resource_group_nameANDvar.<obj>.resource_group_namewith acount-toggled internalazurerm_resource_group.this. The caller owns the RG, always. - Hard-pinned child-module refs —
source = "github.com/schubergphilis/...?ref=vX.Y.Z". Use registry +version = "~> X.Y". - Floating
:latest/:mainimage defaults — e.g.ghcr.io/schubergphilis/<image>:main. Pin to a known-good version and document"latest"as opt-in. try(var.tags)around a var with a default — cargo-cult that masks real errors.
Bug classes spotted in the corpus worth explicit spot-checks:
- Swapped output wiring (VPC endpoint IDs pointing at the wrong endpoint).
each.value.*vsvar.*copy-paste insidefor_each(user node pool inheriting system pool).- Policy conditions using
.namewhere.arnis required. archive_file.output_pathas a relative path (writes to caller's CWD).- References to
azurerm_resource_group.thisthat is never declared. - Cron expressions with the wrong number of fields.
Authoring workflow
- Create repo
terraform-<provider>-mcaf-<name>inschubergphilis, default branchmain. - Copy skeleton from the reference for that provider (see list above). Don't copy lock files or state.
- Replace
.github/workflows/*and.pre-commit-config.yamlwith current versions frommcaf-github-workflows(preserveDO NOT CHANGEheader). - Write
terraform.tfwith arequired_versionthat allows the newest Terraform (e.g.">= 1.9"— floor only, no upper bound) and provider ranges. - Write
variables.tfwith types, descriptions, validations,sensitive/nullableas needed. Follow arg order rule. - Write
main.tf. Referencevar.tagson every taggable resource. Use canonical label ("default"or"this"). - Write
outputs.tf:id/arn/namefirst, each withdescription. No whole-resource dumps. - Write
examples/default/main.tfusingsource = "../../", noversion =. - Add one example per major feature branch.
- Write
tests/main.tftest.hclwithmock_provider,run "setup"if fixtures,run "default", and arunper conditional path.expect_failuresfor negatives. - Write
Taskfile.yamlwithdefault,clean,test,verbose-test. - Write
README.md; inject terraform-docs locally; CI regenerates on every PR. - Standard
CONTRIBUTING.md. pre-commit run -auntil clean.- Push. CI must go green on
terraform-validationandpr-validation. Apply a label. - On merge,
release-drafterupdates the draft release. Publish from the Releases page.
PR review workflow
In order, fail fast:
- PR hygiene — conventional-commit title? label applied?
CHANGELOG.md/UPGRADING.mdupdated? - File layout — right locations? no
versions.tf/backend.tf/module.tf/ camelCase /Makefile? terraform.tf— provider bumps widen the upper bound?required_versionnot lowered?- Variables — new var has
description+type? enumerated values havevalidation? secretssensitive = true? rename/retype triggersbreakinglabel +UPGRADING.md? arg order inside block correct? - Outputs —
descriptionpresent? no whole-resource leak? sensitive values marked? - Tags — new taggable resource references
var.tags?try(var.tags)removed if present? - Examples — at least one exercises the change? still valid?
- Tests — new
runblock covers the new branch?expect_failuresfor negatives? - Workflows / pre-commit — untouched? If edited: reject and point at
mcaf-github-workflows. - LCM / supply-chain — no new
@masterref, no flagged publisher, no hard-pinned child module, provider floors still met. - Anti-patterns — walk the corpus-specific list.
- README — prose sections accurate? (TF-docs block will be re-injected by CI.)
Common traps
- Using
versions.tfbecause some older module did — useterraform.tf. - Pinning providers with
version = "6.1.2". Use ranges. - Hand-written
## UsageHCL snippets in the README — point toexamples/instead. - Editing a
mcaf-github-workflows-sourced file locally. - Mock provider omitted from tests — test tries to hit a real cloud.
- Dumping every attribute into
outputs.tf. - Mixed default representations:
""vsnull. Prefernull. - Creating a resource group inside a module when caller should own it.
Useful one-liners
# Diff workflows against current mcaf-github-workflows
gh api repos/schubergphilis/mcaf-github-workflows/contents/.github/workflow-templates | jq -r '.[].name'
# Validate loop locally
for d in examples/*/; do (cd "$d" && terraform init -backend=false && terraform validate); done
# Mocked tests
task test
# Render the terraform-docs section
terraform-docs . --sort-by required
Related
terraformskill — generic baseline.review-mcafskill — qualitative module review.GUIDE.md— the MCAF way-of-working reference.