Skip to content

chore(iac): remove the unused roleAssignmentGuidPrefix ARM parameter - #1306

Draft
IAmJSD wants to merge 1 commit into
LeanerCloud:mainfrom
IAmJSD:fix/issue-143
Draft

IAmJSD wants to merge 1 commit into
LeanerCloud:mainfrom
IAmJSD:fix/issue-143

Conversation

@IAmJSD

@IAmJSD IAmJSD commented Oct 23, 2026

Copy link
Copy Markdown
Collaborator

Root cause

arm/CUDly-CrossSubscription/template.json declared a roleAssignmentGuidPrefix parameter with a [newGuid()] default. Its description said it was the "Base GUID used to derive deterministic role assignment names", but nothing in the template read it. The custom role definition name and all three role assignment names come from guid(...) seeds over the principal, a literal and subscription().subscriptionId, so the parameter did nothing.

Fix: remove it rather than wire it in

The parameter is deleted from the template. Wiring it in was rejected for three reasons:

  • Wiring it in would break redeploys. With the newGuid() default in the guid(...) seeds, every deployment would produce new assignment names. Azure refuses a second assignment of the same role to the same principal at the same scope (RoleAssignmentExists), so a customer re-running the template (which known-issues.md tells existing tenants to do) would fail. Any fixed default would also rename the assignments every existing tenant already has, with the same result.
  • Nobody passes it today, so removing it breaks no shipped command. ARM fails a deployment that supplies an undeclared parameter, so I checked every caller. arm/CUDly-CrossSubscription/setup.sh and both redeploy commands in known-issues.md pass only servicePrincipalObjectId. The federation bundles served by internal/api / internal/iacfiles (azure-wif-deploy.sh, azure-wif-bicep-deploy.sh, azure-wif-bicep-params.json) deploy iac/federation/azure-target/bicep/azure-wif.{bicep,arm.json}, not this template, and none of them mention the parameter. A repo-wide grep (frontend included) finds it only in the template, the 2026-09-02 audit report (A13-020, which recommends deleting it) and the scripts/testdata/role-parity fixtures.
  • The existing seeds are already deterministic per (principal, subscription), which is what the parameter's description promised.

The only callers removal could break are customers who wrote their own command passing roleAssignmentGuidPrefix=... by hand. The parameter was documented as "Leave at default", so that should be rare. Such a command will now fail with ARM's "parameter not present in the original template" error, and the fix is to drop the argument.

Regression tests

New file iac/arm_cross_subscription_template_test.go, in the existing iac test package that already tests arm/CUDly-CrossSubscription/setup-gcp-wif.sh:

  • TestCrossSubscriptionTemplateReadsEveryParameter fails if any declared parameter is never referenced as parameters('<name>') outside the parameters section (matched case-insensitively, like ARM).
  • TestCrossSubscriptionTemplateHasNoNewGuidDefault fails if a parameter defaults to newGuid(). It catches the "wire it up" variant, which the reference check alone would pass.
  • TestCrossSubscriptionDeployCommandsPassOnlyDeclaredParameters parses each az deployment sub create command for this template in setup.sh and known-issues.md (continuation lines joined) and fails if a command passes a --parameters key= the template does not declare. It also fails if it finds no command in a source file, so it cannot pass vacuously.

Closes #143

Verification

Unit and fixture evidence only. Nothing was deployed, no az command was run and no Azure API was called.

export PATH=$HOME/.local/go/bin:$HOME/go/bin:$HOME/.local/bin:$PATH
export AWS_CONFIG_FILE=/dev/null AWS_SHARED_CREDENTIALS_FILE=/dev/null AWS_EC2_METADATA_DISABLED=true

Fail before the template change (test file added, template untouched):

$ go test ./iac/ -run CrossSubscription -count=1 -v
--- FAIL: TestCrossSubscriptionTemplateReadsEveryParameter
    parameter "roleAssignmentGuidPrefix" is declared but no variable, resource or output reads parameters('roleAssignmentGuidPrefix')
--- FAIL: TestCrossSubscriptionTemplateHasNoNewGuidDefault
    parameter "roleAssignmentGuidPrefix" has a newGuid() default: {... "defaultValue": "[newGuid()]" ...}
--- PASS: TestCrossSubscriptionDeployCommandsPassOnlyDeclaredParameters
FAIL

Pass after:

$ go test ./iac/ -run CrossSubscription -count=1 -v
--- PASS: TestCrossSubscriptionTemplateReadsEveryParameter
--- PASS: TestCrossSubscriptionTemplateHasNoNewGuidDefault
--- PASS: TestCrossSubscriptionDeployCommandsPassOnlyDeclaredParameters
ok  github.com/LeanerCloud/cloud-commitments-platform/iac

Mutation probe for the caller guard: I temporarily added --parameters roleAssignmentGuidPrefix="x" to the setup.sh deploy command. TestCrossSubscriptionDeployCommandsPassOnlyDeclaredParameters then failed with setup.sh: deploy command passes undeclared parameter "roleAssignmentGuidPrefix", which ARM rejects. I reverted the probe afterwards.

Other checks run on this branch:

jq empty arm/CUDly-CrossSubscription/template.json                 # valid JSON
bash scripts/check-azure-role-parity.sh                            # OK: 4 expected grant tuples, all subscription-anchored
bash scripts/test-azure-role-parity.sh                             # Results: 78 passed, 0 failed
go test ./iac/ -count=1                                            # ok
go test ./internal/api -run Federation -count=1                    # ok
go test ./internal/iacfiles/ -count=1                              # ok
go test ./internal/mocks/ -count=1                                 # ok
go build ./... && go vet ./... && go vet -tags integration ./...   # clean
golangci-lint run --allow-parallel-runners ./iac/...               # 0 issues (v2.10.1)
gocyclo -over 10 -ignore "_test\.go" iac/                          # nothing over 10

Combined-tree verification. This branch was tested together with the other queued branches on a local, never-pushed tree: queue/platform @ b56afb2b = origin/main a14dc8f2 plus these heads, merged in this order:

Branch Tested head
fix/issue-204 76f85a4
fix/issue-438 78fe011
fix/issue-838 120b07f
fix/issue-143 aef1256
fix/issue-349 cfbb551
fix/issue-80 63405f9
fix/issue-199 36b9f8f
fix/issue-41 96fc237
fix/issue-299 6af14ed
fix/issue-324 3ab6729

fix/issue-324 and fix/issue-204 both edit .github/workflows/ci.yml next to the azure-kv-access-policy job. The tree kept #324's new "Run Function URL reader self-tests" step and #204's updated job comment; whichever PR merges second takes that same resolution.

Linux, Go 1.26.9, AWS_CONFIG_FILE=/dev/null AWS_SHARED_CREDENTIALS_FILE=/dev/null AWS_EC2_METADATA_DISABLED=true; Docker Postgres via testcontainers. The full suite ran on ed0c7174, which differs from b56afb2b only by one .gitallowed line on fix/issue-324:

  • Passed: go build ./..., go vet ./..., go vet -tags integration ./... and the full go test -race -short -count=1 ./....
  • Static checks: golangci-lint v2.10.1 found 0 issues on every changed package, with and without --build-tags integration. gocyclo -over 10 is silent on the changed non-test files.
  • Integration tests, every changed package that has them, one package at a time (-tags integration -count=1, 60-minute local timeout): internal/api, internal/purchase, internal/scheduler and internal/server all pass.
  • Secrets: the first run caught a bare placeholder account ID in fix/issue-324's new tftest, now allowlisted with an anchored .gitallowed line. Re-run at b56afb2b: git-secrets, using the repo's scripts/setup-git-secrets.sh patterns in a fresh repo with an isolated HOME, found nothing in the changed files, and a planted fake key was flagged. scripts/test-git-secrets-allowlist.sh: 137 PASS, 0 FAIL.
  • Nothing was applied to any cloud.

Out of scope

  • The scripts/testdata/role-parity/* fixtures were copied from this template and still declare roleAssignmentGuidPrefix. The parity checker ignores it, and they are standalone checker inputs that still pass, so I left them alone to keep the diff small. They can be cleaned up in a follow-up if wanted.
  • The A13-020 entry in docs/audits/codebase-audit-2026-09-02.md still reads "issue: (pending cross-reference)". It is a dated audit snapshot, so I did not edit it.
  • Azure deploys have been paused since 2026-10-20. This change has not been applied to any real subscription, and nothing here deploys it. It takes effect the next time a customer runs setup.sh or the documented redeploy.

Maintainer: please mirror the issue's triage labels on this PR: type/chore, impact/internal, priority/p3, severity/low, urgency/eventually, effort/xs, triaged.

🤖 Generated with Claude Code

arm/CUDly-CrossSubscription/template.json declared roleAssignmentGuidPrefix
with a [newGuid()] default and described it as the base GUID for
deterministic role assignment names, but nothing in the template read it:
the custom role name and all three assignment names are guid() seeds over
the principal, a literal and subscription().subscriptionId.

Delete it rather than wire it in. Wiring the newGuid() default into the
seeds would rename every assignment on each redeploy, and Azure refuses a
second assignment of the same role to the same principal at the same scope
(RoleAssignmentExists), so existing customers could no longer redeploy.
No shipped command passes the parameter (setup.sh and the known-issues.md
redeploy commands pass only servicePrincipalObjectId), so removal breaks
none of them.

The new iac tests fail if a declared parameter goes unread, if a parameter
defaults to newGuid(), or if a shipped deploy command for the template
passes a parameter the template no longer declares.

Closes LeanerCloud#143

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@IAmJSD IAmJSD added impact/internal Team-internal only type/chore Maintenance / non-user-visible triaged Item has been triaged priority/p3 Polish / idea / may never ship severity/low Minor harm urgency/eventually No deadline effort/xs Trivial / one-liner labels Oct 23, 2026
@coderabbitai

coderabbitai Bot commented Oct 23, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/xs Trivial / one-liner impact/internal Team-internal only priority/p3 Polish / idea / may never ship severity/low Minor harm triaged Item has been triaged type/chore Maintenance / non-user-visible urgency/eventually No deadline

Projects

None yet

Development

Successfully merging this pull request may close these issues.

chore(iac): remove or wire up unused roleAssignmentGuidPrefix ARM parameter

1 participant