Skip to content

fix(cli): gen2-migration forward-direction function grants avoid circular deps - #14981

Open
sharonyajain wants to merge 1 commit into
aws-amplify:devfrom
sharonyajain:fix/gen2-migration-function-access-circular-deps
Open

sharonyajain wants to merge 1 commit into
aws-amplify:devfrom
sharonyajain:fix/gen2-migration-function-access-circular-deps

Conversation

@sharonyajain

Copy link
Copy Markdown
Contributor

Description of changes

amplify gen2-migration generate could produce a Gen2 app that fails to deploy with:

🛑 [CloudformationStackCircularDependencyError] circular dependency found between nested stacks [storage, auth, data, function]

Root cause. The generator emitted a function's access to auth/storage using
reverse-direction constructs:

  • auth: access: (allow) => [allow.resource(fn).to([...])] on defineAuth
  • storage: allow.resource(fn).to([...]) entries inside the defineStorage access block

Both make the auth/storage stack reference the function's role — an
auth → function / storage → function cross-stack dependency. When the same
function also accesses another category (e.g. data), and that category
references auth (data → auth), the reverse-direction grant closes a cross-stack
cycle. Setting resourceGroupName only co-locates resources in one stack and
cannot break a genuine cross-category cycle.

Fix. Emit the grants in the forward direction in backend.ts, on the
underlying CDK constructs, so the dependency stays function → auth /
function → storage:

  • auth → backend.auth.resources.userPool.grant(backend.<fn>.resources.lambda, ...cognito-idp actions), using a documented Amplify-permission → cognito-idp:* action mapping (the CDK grant works on raw IAM actions, not Amplify permission names).
  • storage → backend.storage.resources.bucket.grant{Read,Write,ReadWrite,Delete}(backend.<fn>.resources.lambda), picking the narrowest grant that covers the consolidated permission set.

Identity-based storage access (guest / authenticated / group) is unchanged and
still rendered as the per-path defineStorage access block; a bucket whose only
Gen1 access was function access now emits no access block at all.

Issue #, if available

Fixes #14727

Description of how you validated changes

  • Unit suites auth.generator.test.ts and s3.generator.test.ts rewritten to assert the forward-direction grants (spy on addPostDefineBackendStatement) — 39/39 pass, 28/28 snapshots.
  • End-to-end migration-app snapshots regenerated and passing, including fitness-tracker (the repro named in the issue) — the admin function's access block on defineAuth is replaced by backend.auth.resources.userPool.grant(backend.admin.resources.lambda, ...) in backend.ts. media-vault and store-locator snapshots also updated.
  • tsc --noEmit clean; eslint 0 errors on touched files; prettier clean.

Not yet validated: a live ampx sandbox deploy of the regenerated output confirming the circular-dependency error is gone. That requires an AWS account and a multi-category Gen1 app; flagging it as the remaining manual-verification gate.

One thing worth a maintainer's eye: the auth permission → cognito-idp:* mapping duplicates expansion logic that lives inside @aws-amplify/backend-auth. It needs a sanity-check against that source and a note to keep the table in sync.

Checklist

  • PR description included
  • yarn test passes (targeted gen2-migration generate suites; see above)
  • Tests are changed or added
  • Relevant documentation is changed or added (and PR referenced)
  • New AWS SDK calls or CloudFormation actions have been added to relevant test and service IAM policies
  • Pull request labels are added

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@sharonyajain
sharonyajain requested a review from a team as a code owner August 27, 2026 09:38
@sharonyajain sharonyajain self-assigned this Aug 27, 2026
@sharonyajain
sharonyajain force-pushed the fix/gen2-migration-function-access-circular-deps branch from 8ab9bfd to 7387f96 Compare August 27, 2026 12:38

@soberm soberm left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed the forward-direction grant fix — approach is directionally correct and breaks the cross-stack cycle (grants land on the function role, referencing only the auth/storage ARN); consolidation/de-dup/sort logic is sound and the three regenerated snapshots match the generator output. A few non-blocking notes inline. Also worth flagging: validation is unit + snapshot only — a one-off live ampx sandbox deploy of a regenerated app would confirm the CloudformationStackCircularDependencyError is actually gone end-to-end.

@sharonyajain
sharonyajain force-pushed the fix/gen2-migration-function-access-circular-deps branch from 7387f96 to 20af9e1 Compare September 7, 2026 12:56
…ular deps

The generate command emitted a function's access to auth and storage as
reverse-direction constructs: `access: (allow) => [allow.resource(fn).to([...])]`
on defineAuth, and `allow.resource(fn)` entries in the defineStorage access
block. Both make the auth/storage stack reference the function's role, i.e. an
`auth -> function` / `storage -> function` cross-stack dependency. When the same
function also accesses another category (e.g. data or storage) the usual
`data -> auth` / `storage -> auth` edges close a cross-stack cycle, and the
generated Gen2 app fails to deploy with CloudformationStackCircularDependencyError.
Setting resourceGroupName only co-locates resources and cannot break a genuine
cross-category cycle.

Emit the grants in the forward direction in backend.ts instead, on the underlying
CDK constructs, so the dependency stays `function -> auth` / `function -> storage`:

- auth: `backend.auth.resources.userPool.grant(backend.<fn>.resources.lambda, ...)`
  with a documented Amplify-permission -> cognito-idp action mapping (the CDK grant
  works on raw IAM actions rather than Amplify permission names).
- storage: `backend.storage.resources.bucket.grant{Read,Write,ReadWrite,Delete}(...)`,
  choosing the narrowest grant that covers the consolidated permission set.

Identity-based storage access (guest / authenticated / group) is unchanged and
still rendered as the per-path defineStorage access block; a bucket whose only
Gen1 access was function access now emits no access block at all.

Regenerated the fitness-tracker, media-vault, and store-locator migration-app
snapshots to reflect the forward-direction output.
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.

(gen2-migration) generate command produces stacks with circular dependencies

2 participants