Skip to content

fix: test the declared Terraform floor in CI (ENG-8760) - #7

Merged
johnsca merged 8 commits into
mainfrom
johnsca/fix/ENG-8760/min-terraform-version
Oct 13, 2026
Merged

johnsca merged 8 commits into
mainfrom
johnsca/fix/ENG-8760/min-terraform-version

Conversation

@johnsca

@johnsca johnsca commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

ENG-8760

what

Adds a CI job that validates this module at the Terraform floor it declares, raises that floor to a supported line, and adds a docs job to keep the generated README tables honest. The lint job also gains a token for zizmor. The docs job reads .terraform-docs.yml, which the action ignores by default, and the config file is renamed from .terraform-docs.yaml so all five repos spell it the same way.

why

The ticket is about a sibling that declared a floor it could not run on, undetected because the pinned toolchain sat far above it. This module's floor was not wrong, but it was equally untested, so the job goes everywhere rather than only where the bug happened to surface.

The floor moves from 1.9 to 1.14. The 1.9 was accurate, since the cross-variable validations here need it, but accurate is not the same as worth supporting: 1.13 and everything below it no longer receives security fixes. The CI pin moves to 1.16.5 at the same time, so the floor and the pin stay distinct and the new job covers an end that lint does not.

This repo already had half the idea. The existing job pins providers to their declared minimums and validates against the pinned Terraform. The new one pins Terraform to its declared minimum and validates against whatever providers resolve. The combination a consumer honoring both advertised floors actually runs is still covered by neither. Closing that needs a third run and its own verification, so it is left as a follow-up rather than bolted on here.

The docs job is unrelated to floors but adjacent. This repo generates its README tables and had nothing checking they stayed current, which is precisely the state a sibling was already in, with its tables claiming the module had no requirements at all.

The job itself lives in stacklet/github-tooling, because five module repositories need the same check and it carries enough shell to be worth one copy rather than five. This PR references that action by commit SHA, naming the commit on github-tooling's main.

testing

  • Sandbox deploy. Planned from a root module against the sandbox Azure subscription with a service principal, at the declared floor of 1.14.0 and again at 1.16.4. Both produce the same 7 resources, and no resource, variable or output changed.
  • The README confirmed already identical to freshly generated output, so the new docs job passes on arrival rather than going red.
  • The extraction checked against a module carrying three constraints at once, one of them in a .tf.json and one split across lines. It returns all three, and Terraform's own init agrees on which one binds. The action it replaces returns an empty string for that module.
  • The docs job confirmed to load .terraform-docs.yml, which it was silently ignoring.
  • just lint clean at the new pin, and zizmor re-run with a token to confirm its five online audits schedule rather than skip. The prek release that job installs now comes from .tool-versions rather than being resolved at run time, so CI and a local run agree on it.
  • Repointed the uses: pin at 3865043, the commit github-tooling's main carries now that feat: add the validate-min-terraform action (ENG-8760) github-tooling#1 merged. The action resolves there, so this job runs for the first time on this PR.
  • Refreshed the pinned tools and re-ran just lint at them: terraform 1.16.5, tflint 0.64.0, just 1.58.0, uv 0.12.23. terraform-docs stays at 0.20.0, the version terraform-docs/gh-actions v1.4.1 bundles, so the docs job and just docs keep rendering with the same binary. The plans above ran at 1.16.4, the pin at the time.

docs

No documentation change. The new job enforces the existing generated output rather than altering it.

The azurerm deprecation warning visible in a local validate here is pre-existing and unrelated, and the provider.tf header already explains it.

@johnsca

johnsca commented Sep 30, 2026

Copy link
Copy Markdown
Contributor Author

@cubic-dev-ai please review this PR.

Filed by Claude on Cory's behalf.

@cubic-dev-ai

cubic-dev-ai Bot commented Sep 30, 2026

Copy link
Copy Markdown

@cubic-dev-ai please review this PR.

Filed by Claude on Cory's behalf.

@johnsca I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 4 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.
Architecture diagram
sequenceDiagram
    participant GH as GitHub Actions Runner
    participant CI as CI Workflow
    participant Lint as Lint Job
    participant Docs as Docs Job (NEW)
    participant Validate as Validate at Declared Floor (NEW)
    participant TF as Terraform CLI
    participant TFMin as terraform-min-max Action
    participant Providers as Terraform Providers (azurerm, random, time)
    participant README as README Generator

    Note over GH,README: CI/CD Pipeline for Terraform Module

    CI->>GH: Trigger workflow (push/PR)
    GH-->>CI: Start jobs

    CI->>Lint: Run pre-commit checks
    Lint->>Lint: Execute checks (incl. zizmor)
    Note over Lint: GH_TOKEN now passed to zizmor<br/>for online audits

    CI->>Docs: NEW: Check documentation job
    Docs->>README: terraform-docs action
    README-->>Docs: Generated README tables
    alt README out of date
        Docs-->>CI: Fail (fail-on-diff)
    else README current
        Docs-->>CI: Pass
    end

    CI->>Validate: NEW: Validate at declared floor job
    Validate->>Validate: Assert single required_version in provider.tf
    Validate->>TFMin: Read declared floor
    TFMin-->>Validate: minVersion (1.14.0)
    Validate->>TF: Setup Terraform at floor version
    TF-->>Validate: Terraform 1.14.0 ready
    Validate->>TF: terraform init -backend=false
    TF->>Providers: Resolve providers
    Providers-->>TF: Provider versions
    Validate->>TF: terraform validate
    TF-->>Validate: Validation result
    Validate-->>CI: Pass/fail

    Note over CI: Existing validate-min-providers job<br/>still runs unchanged<br/>(pins providers, uses pinned TF 1.16.4)
Loading

Re-trigger cubic

johnsca and others added 3 commits October 5, 2026 15:36
[ENG-8760](https://stacklet.atlassian.net/browse/ENG-8760)

### what

Adds a CI job that validates this module at the Terraform floor it declares, and
a `lint-docs` job to keep the README's generated tables honest.

### why

ENG-8760 found `terraform-aws-onboarding-account` declaring a floor it could not
run on, undetected because `.tool-versions` pins a version far above it. This
repo's floor is correct, but it was equally untested, so the job goes into all
five module repos rather than only the one that was broken.

This repo already had half the idea. `validate-min-providers` pins the
*providers* to their declared minimums and validates against the pinned
Terraform; the new job pins *Terraform* to its declared minimum and validates
against whatever providers resolve. Between them, the combination a consumer
honoring both advertised floors actually runs is still uncovered. Closing that
needs a third run and its own verification, so it is left for a follow-up rather
than bolted on here.

A guard step ahead of the new job asserts exactly one `required_version`, in
`provider.tf`. The action that resolves the floor picks its source file by
shortest filename rather than by a canonical name.

`lint-docs` is unrelated to the floor but adjacent: this repo generates its
README tables with terraform-docs and had nothing checking they stayed current.
`terraform-aws-stacklet-cur-setup` was in that state and its table had silently
drifted to claim the module has no requirements at all.

The lint job now passes a token to zizmor. Its five online audits skip silently
without one, and the summary line reads identically either way, so CI reported
success while the supply-chain audits never ran.

### testing

- [x] Ran the version-resolution action locally against this `provider.tf`:
      resolves `>= 1.9.0, < 2.0.0` to `1.9.0`, and the module validates there
      against current providers.
- [x] Confirmed the README is already byte-identical to freshly regenerated
      terraform-docs 0.20.0 output, so the new `lint-docs` job passes on arrival.
- [x] The guard step exercised both ways under bash and GNU grep: passes as-is,
      and fails when a second `required_version` is planted in another file.
- [x] `just lint` clean, and zizmor re-run with a token to confirm the five
      online audits schedule rather than skip.
- [ ] Sandbox deploy. Expect a no-op: no resource, variable or output changed.

### docs

No documentation change. The new `lint-docs` job enforces the existing
terraform-docs output rather than altering it.

The pre-existing `resource_manager_id` deprecation warning from azurerm is
untouched and unrelated; it is already explained in the `provider.tf` header.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The old floor was the oldest release that could run the module, which meant
standing behind Terraform lines that no longer receive security fixes. Declare
1.14 instead, and move the CI pin to the current 1.16.4 so the floor and the
pin stay distinct.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
terraform-min-max finds the constraint by regex, takes the first match per
file, ignores .tf.json, and picks a file by shortest path. The guard step
added to compensate was the same regex with the same blind spots, so it could
never be more correct than the thing it was guarding. On a required_version
split across lines the action extracts an empty string, which semver reads as
"any version", so the floor resolves to the oldest Terraform ever released.

terraform-docs parses the configuration and reports every required_version in
the module, which removes the question of which file holds the real one. The
floor is then the highest declared lower bound, which is what Terraform
applies. Pinned by digest because terraform-docs publishes no immutable
releases, so neither a tag nor a release checksum is fixed.

Also point the docs job at .terraform-docs.yml, which it was ignoring, and
settle on that spelling in the two repos that used .terraform-docs.yaml.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@johnsca
johnsca force-pushed the johnsca/fix/ENG-8760/min-terraform-version branch from e8238f6 to b399ac3 Compare October 5, 2026 15:50
@johnsca

johnsca commented Oct 5, 2026

Copy link
Copy Markdown
Contributor Author

@cubic-dev-ai please review this PR.

Filed by Claude on Cory's behalf.

@cubic-dev-ai

cubic-dev-ai Bot commented Oct 5, 2026

Copy link
Copy Markdown

@cubic-dev-ai please review this PR.

Filed by Claude on Cory's behalf.

@johnsca I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 5 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.
Architecture diagram
sequenceDiagram
    participant GH as GitHub Actions
    participant WF as CI Workflow
    participant Lint as Lint Job
    participant Docs as Docs Job
    participant MinTF as Validate Min Terraform Job
    participant MinProv as Validate Min Providers Job
    participant TFD as terraform-docs Container
    participant TF as Terraform CLI
    participant Repo as Repo Context

    Note over GH,Repo: CI Pipeline for Terraform Module

    GH->>WF: Trigger workflow
    WF->>Lint: Run lint job
    Lint->>Lint: pre-commit checks incl. zizmor
    Lint->>GH: GH_TOKEN injection
    GH-->>Lint: github.token

    WF->>Docs: Run docs job
    Docs->>Repo: Checkout with persist-credentials: false
    Docs->>TFD: Run terraform-docs action
    TFD->>Repo: Read .terraform-docs.yml config
    TFD->>Repo: Generate README tables
    alt Docs up to date
        TFD-->>Docs: No diff
        Docs-->>WF: Pass
    else Docs outdated
        TFD-->>Docs: Diff detected
        Docs-->>WF: Fail
    end

    WF->>MinTF: Run validate-min-terraform job
    MinTF->>Repo: Checkout with persist-credentials: false
    MinTF->>TFD: Run terraform-docs image (json output)
    TFD->>Repo: Parse all required_version constraints
    TFD-->>MinTF: Constraints list
    MinTF->>MinTF: Extract highest lower bound (>= or ~>)
    alt Constraints valid
        MinTF->>MinTF: Compute floor version
        MinTF->>TF: setup-terraform with floor version
        TF->>Repo: terraform init -backend=false
        TF->>Repo: terraform validate
        TF-->>MinTF: Validation result
        MinTF-->>WF: Pass
    else No required_version or unparseable
        MinTF-->>WF: Fail with clear error
    end

    WF->>MinProv: Run validate-min-providers job
    MinProv->>Repo: Checkout
    MinProv->>TF: Pin providers to minimums
    MinProv->>Repo: Validate against pinned Terraform version
    MinProv-->>WF: Pass/Fail
Loading

Re-trigger cubic

@johnsca
johnsca force-pushed the johnsca/fix/ENG-8760/min-terraform-version branch from e3a3327 to 88320f6 Compare October 6, 2026 20:04
@johnsca

johnsca commented Oct 6, 2026

Copy link
Copy Markdown
Contributor Author

@cubic-dev-ai please review this PR.

Filed by Claude on Cory's behalf.

@cubic-dev-ai

cubic-dev-ai Bot commented Oct 6, 2026

Copy link
Copy Markdown

@cubic-dev-ai please review this PR.

Filed by Claude on Cory's behalf.

@johnsca I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 5 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.
Architecture diagram
sequenceDiagram
    participant GH as GitHub Actions
    participant Lint as Lint Job
    participant Zizmor as zizmor
    participant Docs as Docs Job
    participant TFDocs as terraform-docs
    participant MinTerra as Min-Terraform Job
    participant Action as validate-min-terraform Action
    participant Terraform as Terraform CLI
    participant Validator as terraform validate
    participant Repo as Module Repo

    Note over GH,Repo: CI Pipeline for Terraform Module

    GH->>Lint: Trigger on push/PR
    Lint->>Zizmor: pre-commit checks with GH_TOKEN
    Note over Lint,Zizmor: CHANGED: Supply GH_TOKEN so online audits run
    Zizmor-->>Lint: Supply-chain audit results

    GH->>Docs: Run docs job
    Docs->>TFDocs: Check documentation with config-file
    Note over Docs,TFDocs: NEW: Explicitly load .terraform-docs.yml
    TFDocs->>Repo: Read .terraform-docs.yml + module files
    Repo-->>TFDocs: Configuration and source
    TFDocs->>TFDocs: Generate README tables
    TFDocs-->>Docs: Diff result
    alt README out of date
        Docs-->>GH: Fail job
    else README current
        Docs-->>GH: Pass job
    end

    GH->>MinTerra: Run min-terraform job
    MinTerra->>Action: Invoke validate-min-terraform action
    Note over MinTerra,Action: NEW: Pin Terraform to declared floor
    Action->>Repo: Read required_version constraint
    Repo-->>Action: ">= 1.14.0, < 2.0.0"
    Action->>Action: Extract minimum version (1.14.0)
    Action->>Terraform: Execute init with Terraform 1.14.0
    Terraform->>Terraform: Resolve providers at minimum
    Terraform->>Validator: Run terraform validate
    Validator-->>Terraform: Validation results
    Terraform-->>Action: Plan/validate output
    alt Validation succeeds
        Action-->>MinTerra: Success
        MinTerra-->>GH: Pass job
    else Validation fails
        Action-->>MinTerra: Error
        MinTerra-->>GH: Fail job
    end
Loading

Re-trigger cubic

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@johnsca
johnsca force-pushed the johnsca/fix/ENG-8760/min-terraform-version branch from 88320f6 to e98ac08 Compare October 6, 2026 20:47
@johnsca
johnsca marked this pull request as ready for review October 6, 2026 21:03
@johnsca
johnsca requested a review from a team as a code owner October 6, 2026 21:03

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 5 files

Confidence score: 2/5

  • In .github/workflows/ci.yml, a pull request can add a pre-commit hook that exfiltrates the inherited contents: read token. Keep GH_TOKEN out of hook environments and scope it only to the command that needs it.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name=".github/workflows/ci.yml">

<violation number="1" location=".github/workflows/ci.yml:45">
P2: `GH_TOKEN` is inherited by every pre-commit hook, including commands defined by the PR's `.pre-commit-config.yaml`. A malicious pull request can add a hook that exfiltrates the `contents: read` token; run online zizmor separately with the token instead.</violation>
</file>
Architecture diagram
sequenceDiagram
    participant GH as GitHub Actions
    participant Lint as Lint Job
    participant Ziz as zizmor
    participant Docs as Docs Job
    participant TD as terraform-docs
    participant MinT as Min Terraform Job
    participant TA as Toolchain Action
    participant TF as Terraform CLI
    
    Note over GH: CI Pipeline
    GH->>Lint: Run lint job
    Lint->>Ziz: pre-commit checks
    Ziz->>Ziz: Online supply-chain audits
    Note over Ziz: GH_TOKEN: ${{ github.token }}<br/>NEW: Enables 5 online audits that<br/>previously skipped silently
    Ziz-->>Lint: Results
    
    GH->>Docs: Run lint-docs job
    Docs->>TD: terraform-docs/gh-actions
    TD->>TD: Read .terraform-docs.yml config<br/>NEW: Explicit config-file path
    TD->>TD: Generate README tables
    alt README matches generated docs
        TD-->>Docs: Pass (no diff)
    else README out of date
        TD-->>Docs: Fail (diff detected)
    end
    Docs-->>GH: Status
    
    GH->>MinT: Run validate-min-terraform job
    MinT->>TA: stacklet/github-tooling/validate-min-terraform
    Note over TA: Pinned by commit SHA
    TA->>TA: Extract required_version from provider.tf
    TA->>TA: CHANGED: Parse constraint ">= 1.14.0, < 2.0.0"
    TA->>TF: Install Terraform at floor (1.14.0)
    TA->>TF: terraform init -backend=false
    TA->>TF: terraform validate
    Note over TF: CHANGED: Validates against<br/>declared minimum (1.14.0) instead of<br/>pin (.tool-versions: 1.16.4)
    TF-->>TA: Validation results
    TA-->>MinT: Pass/Fail
    MinT-->>GH: Status
Loading

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread .github/workflows/ci.yml Resolved

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 1 file (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread .github/workflows/ci.yml Resolved
@johnsca

johnsca commented Oct 6, 2026

Copy link
Copy Markdown
Contributor Author

@cubic-dev-ai please re-review

@cubic-dev-ai

cubic-dev-ai Bot commented Oct 6, 2026

Copy link
Copy Markdown

@cubic-dev-ai please re-review

@johnsca I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 5 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.
Architecture diagram
sequenceDiagram
    participant Dev as Developer
    participant CI as GitHub Actions CI
    participant Pre as pre-commit / prek
    participant Docs as terraform-docs
    participant TF as Terraform CLI
    participant GH as GitHub API

    Note over Dev,CI: PR Validation Pipeline

    Dev->>CI: Push / PR to module repo
    CI->>CI: Dispatch workflows

    par Lint Job
        CI->>Pre: prek-action with pinned version 0.5.5
        Pre->>GH: Read write token (GH_TOKEN)
        alt Token available
            GH-->>Pre: Schedule & run 5 online zizmor audits
        else Token missing
            Pre->>Pre: Skip audits silently
        end
        Pre-->>CI: Lint results
    and Docs Job
        CI->>Docs: terraform-docs/gh-actions
        Docs->>Docs: Load .terraform-docs.yml config
        Docs->>Docs: Generate README tables
        alt Docs current
            Docs-->>CI: Pass (no diff)
        else Docs stale
            Docs-->>CI: Fail (diff detected)
        end
    and Provider Validation
        CI->>TF: terraform init (pinned providers)
        TF-->>CI: Provider state
        CI->>TF: terraform validate
        TF-->>CI: Validation results
    and Floor Validation Job
        CI->>TF: github-tooling/validate-min-terraform
        TF->>TF: Read required_version from provider.tf
        TF->>TF: Resolve floor = 1.14.0
        TF->>TF: Install Terraform 1.14.0
        TF->>TF: terraform init + validate
        alt Floor passes
            TF-->>CI: Success
        else Floor fails
            TF-->>CI: Failure (floor not tested)
        end
    end

    CI-->>Dev: Aggregate status check
Loading

Re-trigger cubic

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@johnsca
johnsca force-pushed the johnsca/fix/ENG-8760/min-terraform-version branch from 7c8cf10 to 08e9ddf Compare October 6, 2026 22:13

@jtroup jtroup left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of the comments from my other review apply here too, but I won't repeat them here. Thanks @johnsca

@johnsca
johnsca force-pushed the johnsca/fix/ENG-8760/min-terraform-version branch from 35481a5 to 1cf5378 Compare October 8, 2026 16:13
terraform-docs stays at 0.20.0: terraform-docs/gh-actions v1.4.1, which the
docs job runs, bundles that exact version, so moving it here would have CI
check the README against a different renderer than `just docs` uses.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@johnsca
johnsca force-pushed the johnsca/fix/ENG-8760/min-terraform-version branch from 1cf5378 to 883ad04 Compare October 9, 2026 21:16
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@johnsca
johnsca merged commit c913640 into main Oct 13, 2026
10 checks passed
@johnsca
johnsca deleted the johnsca/fix/ENG-8760/min-terraform-version branch October 13, 2026 17:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants