aws_s3/aws_lambda/aws_iam: Cyclic reference when bucket and policy in stack A are referenced by role/lambda in stack B
- Dominant language
- TypeScript
- Stars
- 12.9k
- Forks
- 4.6k
- Avg merge
- 2d 3h
- Merged PRs (30d)
- 83
Description
### Describe the bug
Stack A
- S3 Bucket
- IAM Policy
Stack B
- Lambda (which references S3 bucket in environment variables)
- Role (that adds the policy from Stack A)
This results in a cyclic dependency error: `Error: 'BucketStack' depends on 'LambdaStack' (BucketStack -> LambdaStack/IAM - Role (Lambda Execution)/Resource.Ref). Adding this dependency (LambdaStack -> BucketStack/S3 - Bucket/Resource.Ref) would create a cyclic reference`
### Expected Behavior
The app should synth properly.
### Current Behavior
`Error: 'BucketStack' depends on 'LambdaStack' (BucketStack -> LambdaStack/IAM - Role (Lambda Execution)/Resource.Ref). Adding this dependency (LambdaStack -> BucketStack/S3 - Bucket/Resource.Ref) would create a cyclic reference`
### Reproduction Steps
```ts
#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import {
aws_iam as iam,
aws_lambda as lambda,
aws_s3 as s3
} from 'aws-cdk-lib'
import { Construct } from 'constructs';
const env = { account: '123456789012', region: 'us-east-1' }
class StorageStack extends cdk.Stack {
bucket: s3.Bucket;
bucketPolicy: iam.Policy;
constructor(scope: Construct, id: string, props: cdk.StackProps) {
super(scope, id, props);
this.bucket = new s3.Bucket(this, 'S3 - Bucket', {});
const listAllBucketsPolicyStatement = new iam.PolicyStatement({
actions: ['s3:ListAllMyBuckets'],
resources: ['*']
});
this.bucketPolicy = new iam.Policy(this, 'IAM - Policy (S3 Bucket Access)', {
statements: [ listAllBucketsPolicyStatement ]
});
}
}
class LambdaStack extends cdk.Stack {
constructor(scope: Construct, id: string, stackProps: cdk.StackProps, bucketName: string, policy: iam.Policy) {
super(scope, id, stackProps);
const lambdaExecutionrole = new iam.Role(this, 'IAM - Role (Lambda Execution)', {
assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
})
lambdaExecutionrole.attachInlinePolicy(policy); // Comment out this line
new lambda.Function(this, 'Lambda - Function', {
runtime: lambda.Runtime.NODEJS_18_X,
handler: 'index.handler',
code: lambda.Code.fromAsset('./Lib/Lambda'),
role: lambdaExecutionrole,
environment: { bucketName } // Comment out this line
});
}
}
const app = new cdk.App();
const storageStack = new StorageStack(app, 'BucketStack', { env });
new LambdaStack(app, 'LambdaStack', { env }, storageStack.bucket.bucketName, storageStack.bucketPolicy)
```
### Possible Solution
_No response_
### Additional Information/Context
_No response_
### CDK CLI Version
2.87.0 (build 9fca790)
### Framework Version
_No response_
### Node.js Version
v18.16.1
### OS
macOS 13.2.1 (22D68)
### Language
Typescript
### Language Version
typescript@5.1.6
### Other information
```
Error: 'BucketStack' depends on 'LambdaStack' (BucketStack -> LambdaStack/IAM - Role (Lambda Execution)/Resource.Ref). Adding this dependency (LambdaStack -> BucketStack/S3 - Bucket/Resource.Ref) would create a cyclic reference.
at LambdaStack._addAssemblyDependency (/Users/john.towler/source/personal/cdk-bug/node_modules/aws-cdk-lib/core/lib/stack.js:1:10330)
at operateOnDependency (/Users/john.towler/source/personal/cdk-bug/node_modules/aws-cdk-lib/core/lib/deps.js:1:1649)
at addDependency (/Users/john.towler/source/personal/cdk-bug/node_modules/aws-cdk-lib/core/lib/deps.js:1:321)
at LambdaStack.addDependency (/Users/john.towler/source/personal/cdk-bug/node_modules/aws-cdk-lib/core/lib/stack.js:1:7442)
at resolveValue (/Users/john.towler/source/personal/cdk-bug/node_modules/aws-cdk-lib/core/lib/private/refs.js:1:3259)
at resolveReferences (/Users/john.towler/source/personal/cdk-bug/node_modules/aws-cdk-lib/core/lib/private/refs.js:1:870)
at prepareApp (/Users/john.towler/source/personal/cdk-bug/node_modules/aws-cdk-lib/core/lib/private/prepare-app.js:1:586)
at synthesize (/Users/john.towler/source/personal/cdk-bug/node_modules/aws-cdk-lib/core/lib/private/synthesis.js:1:922)
at App.synth (/Users/john.towler/source/personal/cdk-bug/node_modules/aws-cdk-lib/core/lib/stage.js:1:2052)
at process. (/Users/john.towler/source/personal/cdk-bug/node_modules/aws-cdk-lib/core/lib/app.js:1:1448)
```
Contributor guide
Research direction
Start with the TypeScript reproduction and run `cdk synth` using aws-cdk-lib 2.87.0 to reproduce the cyclic dependency. Read the stack dependency and reference-resolution paths named in the trace: `core/lib/stack.js`, `core/lib/deps.js`, and `core/lib/private/refs.js`. Done means the two-stack example synthesizes without the reported cycle while preserving the bucket reference and attached policy.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- aws, typescript
- Domain
- cloud, infrastructure
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 42/100