aws / aws/aws-cdk

aws_s3/aws_lambda/aws_iam: Cyclic reference when bucket and policy in stack A are referenced by role/lambda in stack B

Open
#26,539 2 comments 0 reactions 0 assignees View on GitHub
@aws-cdk/aws-iam bug effort/medium p2
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.