Skip to content

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

Merged
IAmJSD merged 3 commits into
LeanerCloud:mainfrom
IAmJSD:fix/issue-143
Oct 26, 2026
Merged

IAmJSD merged 3 commits 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

Warning

Review limit reached

  • Run on-demand review

This review includes 2 billable files and costs up to $0.50.

Or wait 57 minutes for your next included review.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration
Commits

Reviewing files that changed from the base of the PR and between eb165b4 and 18150b1.

Files selected for processing: 2
  • arm/CUDly-CrossSubscription/template.json
  • iac/arm_cross_subscription_template_test.go
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

IAmJSD and others added 2 commits October 25, 2026 15:56
Brings in main's CI fixes (LeanerCloud#1236, LeanerCloud#1252) and CI changes since, so this PR gets a fresh CI run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Refreshes the branch on current main to get a fresh CI run. The
previous run's arm64 Docker build hung in its build step until the
45-minute job timeout. The same job takes about three minutes on main
and on other branches with this workflow. No files conflicted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@IAmJSD

IAmJSD commented Oct 25, 2026

Copy link
Copy Markdown
Collaborator Author

Merged current main into the branch. No files conflicted. New head: 18150b1.

The earlier red CI came from "Build Docker Image (arm64)". In run 40212212609 its build step hung until the 45-minute job timeout. That job usually takes about 3 minutes on main and on other branches, and this branch doesn't touch the Dockerfile or the build. On the fresh run (40238125769), the arm64 Docker build passed in 3 minutes, so it looks like a transient runner hang. No code change was needed.

Checks:

  • go build and go vet (default, integration, e2e) pass. golangci-lint reports 0 issues on ./iac/ with and without the integration tag.
  • go test -race passes on ./iac/, and the template parses as JSON.
  • The git-secrets scan is clean, and the allowlist test gives 137 PASS.

@IAmJSD

IAmJSD commented Oct 25, 2026

Copy link
Copy Markdown
Collaborator Author

Independent review (IAmJSD's agent, fresh context, given only the diff, the issue and the PR text): VERDICT: APPROVE 18150b119011d73f888870cba44b80f1bfaaaa6b

Blocking:

  • none

Non-blocking:

  • iac/arm_cross_subscription_template_test.go:93: paramArg only captures the first key= after each --parameters. A future --parameters a=1 b=2 or --parameters @params.json would have its later keys, or the file's keys, go unchecked. Neither form appears today. Matching every key= token up to the next --flag would close the gap.
  • The scripts/testdata/role-parity/* fixtures still declare the parameter. The PR body says so, and they're checker inputs only (test-azure-role-parity.sh 78/78), so this is fine.

Attacked:

  • Callers: a repo-wide grep (outside .git and node_modules) finds roleAssignmentGuidPrefix only in the template, the dated audit report and the parity fixtures. CUDly-CrossSubscription is referenced by setup.sh, known-issues.md, the parity scripts and unrelated GCP/Terraform files. It is not go:embed-served and not linked from the frontend, so no shipped command passes the removed parameter. I also checked that none of the template's guid(...) seeds read it, so the role assignment and definition names are unchanged for existing tenants (no RoleAssignmentExists on redeploy).
  • Fail-before: with the merge-base template.json restored, TestCrossSubscriptionTemplateReadsEveryParameter and TestCrossSubscriptionTemplateHasNoNewGuidDefault both fail. They pass on the head. The file was restored afterwards.
  • Checks run:
    • jq empty on the template
    • go build ./...
    • go vet ./iac/, with and without -tags integration
    • go test -race -short -count=1 ./iac/ ./internal/iacfiles/
    • scripts/check-azure-role-parity.sh (OK, 4 tuples) and scripts/test-azure-role-parity.sh (78 passed)
    • golangci-lint on ./iac/ (0 issues)
    • the head merges cleanly with main 5a2e31e

Body-stale:

  • none

Reviewer: rB20. Bar while the maintainers are away (set by IAmJSD as acting owner): one approving independent review on the head, two for money, auth and security paths.

@IAmJSD

IAmJSD commented Oct 26, 2026

Copy link
Copy Markdown
Collaborator Author

Local verification at 18150b119011d73f888870cba44b80f1bfaaaa6b merged onto main (eb9e4802), together with #1277, #1284, #1288, #1290, #1294, #1306 in that order (combined tree 6bb85247, Linux, Go 1.26.10): go build ./..., go vet with and without -tags integration, the full go test -race -short -count=1 ./..., and go test -race -tags integration -count=1 on every changed package, against Docker Postgres. All pass. CI Success and pre-commit are green on this head, with 1 approving independent review(s) on it. Nothing was run against any cloud.

@IAmJSD
IAmJSD marked this pull request as ready for review October 26, 2026 00:01
@IAmJSD
IAmJSD merged commit 1ceaafd into LeanerCloud:main Oct 26, 2026
38 checks passed
@IAmJSD

IAmJSD commented Oct 26, 2026

Copy link
Copy Markdown
Collaborator Author

Merged by IAmJSD's agent at IAmJSD's request (IAmJSD is acting owner while the maintainers are away), under the bar IAmJSD set: 1 approving independent review(s) on 18150b11, green CI, and the combined-tree verification above. Squash-merged at exactly 18150b119011d73f888870cba44b80f1bfaaaa6b.

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