Skip to content

decommission: resolve the workspace Terraform version and fail clearly without a token - #73

Merged
dev-milos merged 2 commits into
mainfrom
fix/decommission-token-and-tf-version
Aug 14, 2026
Merged

decommission: resolve the workspace Terraform version and fail clearly without a token#73
dev-milos merged 2 commits into
mainfrom
fix/decommission-token-and-tf-version

Conversation

@dev-milos

@dev-milos dev-milos commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator

Closes G-Research/gr-oss#1436
Closes G-Research/gr-oss#1437

The decommission workflow could not run at all. Two independent defects, both in the path
before any state is touched, and the feature is unusable until both land — hence one pull
request.

The token

discover declares no environment:, so it cannot read an environment-scoped secret, which
is where the installation guide puts the HCP token. The caller's secrets.TFC_TOKEN is also
evaluated outside any environment, so it resolves to an empty string and terraform init
fails with:

Error: Required token could not be found

— which points nowhere near the cause.

The workflow already carried exactly this reasoning for a different input:

# WORKSPACE must be a repository-level Actions variable: the discover job and the
# concurrency group run outside the environment and can't read environment-scoped vars.

The same constraint applies to the token and was not carried across.

The CLI version

Both jobs installed the latest Terraform. plan and apply are unaffected because they
execute remotely, but state rm runs locally against remote state and is refused unless
the CLI satisfies the workspace constraint:

The local Terraform version (1.15.8) does not meet the version requirements
for remote workspace <org>/<workspace> (~>1.14.0).

This one is time-dependent. It worked when the feature was written and broke on its own once a
newer Terraform shipped — no change in either repository was needed. Every consumer inherits
it as soon as their workspace constraint falls behind, and it surfaces only when someone
actually needs to decommission, which is when an orphaned repository is already breaking plans
for everything else.

Approach

One composite action, resolve-tf-version, covers both. It needs the same three values either
way, and both defects want to fail early and clearly:

  • refuses to continue without a token, naming the repository-level requirement
  • reads the version the workspace requires and hands it to setup-terraform

It never falls back to the latest release — installing a version the workspace rejects is
the failure it exists to prevent.

This mirrors link-backend, the existing precedent for extracting the few lines both jobs
need.

Version shapes

A workspace may report an exact version or a pessimistic constraint, and setup-terraform
accepts exact versions and <-style constraints but not ~>. Verified across the shapes:

Constraints are conventionally written with a space, so spacing is normalised before
matching rather than one spelling being privileged.

Workspace reports Installed
1.14.3 1.14.3
~> 1.14.0 or ~>1.14.0 <1.15.0
~> 1.9.0 <1.10.0
~> 1.14, >= 1.14.0, latest, empty rejected with an actionable error

Rejecting the two-component ~>1.14 is deliberate: it means >=1.14, <2.0, not the same
thing, and guessing there would install a version the workspace might refuse.

Applied to both jobs

The version failure was observed only in the mutating job — with a mismatched CLI, discover
completed fine, since terraform show -json and terraform state list tolerate it, and only
state rm refused.

Both jobs still resolve it. discover reads state, a future version could break that too, and
two jobs installing different CLIs is a trap for the next reader.

graformer is deliberately untouched, and the action's description says why: plan and apply
run remotely, so the local CLI is only a client there.

Documentation

docs/workflows.md covers Import and Drift Check and never covered Decommission. The
setup requirements are load-bearing — they are half of the token fix — so the new section
states them explicitly, including that the token and WORKSPACE must both be
repository-level, unlike every other workflow here.

It also settles the operating order. The original description of the feature says to delete the
config first and then run the workflow; the code refuses to run without it, and testing
confirmed the code:

##[error]no config found for '<repo>' under repos/ or importer_tmp_dir/
        — run decommission while the config is still present, then delete the YAML

Verification

Both defects were reproduced against a real organisation during release testing, and the same
path re-verifies the fix. Still to run once this is on a testable ref:

  • the two guard cases keep rejecting before Terraform starts
  • with the token only in environments, discover fails on the new guard with an actionable
    message rather than on terraform init
  • with the token at repository level, a full run completes without a pinned version
    addresses enumerated including composite-keyed environment resources, approval gate holding,
    state rm succeeding, the repository on GitHub untouched, and deleting the config afterwards
    planning no changes

The last group was confirmed manually during testing with both workarounds in place; this run
is what shows the workarounds are no longer needed.

…y without a token

The workflow could not run at all. Two independent defects, both in the path before
any state is touched.

The discover job declares no environment, so it cannot read an environment-scoped
secret - which is where the installation guide puts the HCP token. The caller
reference resolves to empty and terraform init fails with a bare authentication
error pointing nowhere near the cause. The workflow already carried this reasoning
for WORKSPACE; it was not carried across to the token.

Both jobs also installed the latest Terraform. Plan and apply are unaffected because
they execute remotely, but state rm runs locally against remote state and is refused
unless the CLI satisfies the workspace constraint. That made the workflow
time-dependent: it worked when written and broke on its own once a newer Terraform
shipped, with no change in either repository.

A single composite action covers both. It refuses to continue without a token,
naming the repository-level requirement, then reads the version the workspace
requires and hands it to setup-terraform. It never falls back to the latest release,
since installing a version the workspace rejects is the failure it exists to prevent.

A workspace may report an exact version or a pessimistic constraint. Exact versions
pass through, ~>X.Y.Z becomes the <X.(Y+1).0 form setup-terraform understands, and
anything else fails loudly rather than being guessed at.

docs/workflows.md gains the Decommission section it never had. The setup
requirements are load-bearing here, and it settles the operating order: the config
must still be present when the workflow runs, and is deleted afterwards.
Terraform constraints are conventionally written with a space, as in "~> 1.14.0".
The patterns required the digit immediately after the operator, so a workspace
using the usual spelling was rejected as untranslatable rather than resolved.

Stripping spaces before matching also means the remaining operator shapes only need
a case arm to support, not a second spelling each.
@dev-milos
dev-milos marked this pull request as ready for review August 14, 2026 09:19

@bitwisecook bitwisecook 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.

LGTM

@dev-milos
dev-milos merged commit 30dbfb8 into main Aug 14, 2026
1 check passed
@dev-milos
dev-milos deleted the fix/decommission-token-and-tf-version branch August 14, 2026 10:22
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.

3 participants