aws / aws/aws-cdk

(aws-secretsmanager): Creating RotationSchedule when rotation lambda and secret are in different stack fails due to cyclic reference

Open
#33,336 3 comments 0 reactions 0 assignees View on GitHub
@aws-cdk/aws-secretsmanager bug effort/medium p3
Dominant language
TypeScript
Stars
12.9k
Forks
4.6k
Avg merge
2d 3h
Merged PRs (30d)
83

Description

### Describe the bug

Currently when creating a RotationSchedule (either directly or using `secret.addRotationSchedule`) for a secret when the rotation lambda is in a different stack will cause `cdk synth` to fail with a cyclic reference error.

### Regression Issue

- [ ] Select this option if this issue appears to be a regression.

### Last Known Working CDK Version

_No response_

### Expected Behavior

We should be able to create a RotationSchedule for a secret even if the lambda function exists in another stack

### Current Behavior

Currently when creating a RotationSchedule (either directly or using `secret.addRotationSchedule`) for a secret when the rotation lambda is in a different stack. `cdk synth` will fail with the below error

```
Error: 'SecretStack' depends on 'LambdaStack' ({SecretStack/TestSecret/RotationSchedule/Resource}.addDependency({LambdaStack/TestLambda/InvokeN0--a2GKfZP0JmDqDE--Vhhu6+A0TUv3NyNbk4YM+FKNc=})). Adding this dependency (LambdaStack -> SecretStack/TestSecret/Resource.Ref) would create a cyclic reference.
at LambdaStack._addAssemblyDependency (C:\secret-rotation-lambda\node_modules\aws-cdk-lib\core\lib\stack.js:1:11960)
at operateOnDependency (C:\secret-rotation-lambda\node_modules\aws-cdk-lib\core\lib\deps.js:1:1796)
at addDependency (C:\secret-rotation-lambda\node_modules\aws-cdk-lib\core\lib\deps.js:1:532)
at LambdaStack.addDependency (C:\secret-rotation-lambda\node_modules\aws-cdk-lib\core\lib\stack.js:1:9022)
at resolveValue (C:\secret-rotation-lambda\node_modules\aws-cdk-lib\core\lib\private\refs.js:1:3840)
at resolveReferences (C:\secret-rotation-lambda\node_modules\aws-cdk-lib\core\lib\private\refs.js:1:1473)
at prepareApp (C:\secret-rotation-lambda\node_modules\aws-cdk-lib\core\lib\private\prepare-app.js:1:806)
at synthesize (C:\secret-rotation-lambda\node_modules\aws-cdk-lib\core\lib\private\synthesis.js:1:1607)
at App.synth (C:\secret-rotation-lambda\node_modules\aws-cdk-lib\core\lib\stage.js:1:2474)
at process. (C:\secret-rotation-lambda\node_modules\aws-cdk-lib\core\lib\app.js:1:1767)
```

Unpacking the above, the RotationSchedule resource has a dependency on the generated `AWS::Lambda::Permission` which was added via https://github.com/aws/aws-cdk/pull/26512.

Even when using an aws-cdk version earlier than the change eg) [v2.92.0](https://github.com/aws/aws-cdk/releases/tag/v2.92.0) the request will fail with the below error

```
Error: 'LambdaStack' depends on 'SecretStack' (LambdaStack -> SecretStack/TestSecret/Resource.Ref). Adding this dependency (SecretStack -> LambdaStack/TestLambda/Resource.Arn) would create a cyclic reference.
at SecretStack._addAssemblyDependency (C:\secret-rotation-lambda\node_modules\aws-cdk-lib\core\lib\stack.js:1:10330)
at operateOnDependency (C:\secret-rotation-lambda\node_modules\aws-cdk-lib\core\lib\deps.js:1:1649)
at addDependency (C:\secret-rotation-lambda\node_modules\aws-cdk-lib\core\lib\deps.js:1:321)
at SecretStack.addDependency (C:\secret-rotation-lambda\node_modules\aws-cdk-lib\core\lib\stack.js:1:7442)
at resolveValue (C:\secret-rotation-lambda\node_modules\aws-cdk-lib\core\lib\private\refs.js:1:3259)
at synthesize (C:\secret-rotation-lambda\node_modules\aws-cdk-lib\core\lib\private\synthesis.js:1:922)
at App.synth (C:\secret-rotation-lambda\node_modules\aws-cdk-lib\core\lib\stage.js:1:2052)
at process. (C:\secret-rotation-lambda\node_modules\aws-cdk-lib\core\lib\app.js:1:1448)
```

I suspect this is because the lambda function needs to `!Ref TestSecret` for permissions and the RotationSchedule needs `rotationLambdaArn: !GetAtt TestLambda.arn`

### Reproduction Steps

Repo: https://github.com/jacklin213/secret-rotation-lambda

app.ts
```
import * as cdk from 'aws-cdk-lib';
import { SecretStack } from '../lib/secret-stack';
import { LambdaStack } from '../lib/lambda-stack';

const app = new cdk.App();
const lambdaStack = new LambdaStack(app, 'LambdaStack', {
env: { account: '000000000000', region: 'us-east-1' }
});
new SecretStack(app, 'SecretStack', {
env: { account: '000000000000', region: 'us-east-1' },
rotationLambda: lambdaStack.lambda
});
```

lambda-stack.ts
```
import * as cdk from 'aws-cdk-lib';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import { Construct } from 'constructs';

export class LambdaStack extends cdk.Stack {
readonly lambda: lambda.Function;
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);

this.lambda = new lambda.Function(this, 'TestLambda', {
runtime: lambda.Runtime.NODEJS_18_X,
handler: 'index.handler',
code: new lambda.InlineCode('console.log("works")')
});
}
}
```

secret-stack.ts
```
import * as cdk from 'aws-cdk-lib';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import { Secret } from 'aws-cdk-lib/aws-secretsmanager';
import { Construct } from 'constructs';

interface SecretStackProps extends cdk.StackProps {
rotationLambda: lambda.Function;
}

export class SecretStack extends cdk.Stack {
readonly secret: Secret;
constructor(scope: Construct, id: string, props: SecretStackProps) {
super(scope, id, props);

this.secret = new Secret(this, 'TestSecret');
this.secret.addRotationSchedule('RotationSchedule', {
automaticallyAfter: cdk.Duration.days(30),
rotateImmediatelyOnUpdate: true,
rotationLambda: props.rotationLambda
});
}
}
```

Then run `cdk synth` to see the issue

### Possible Solution

_No response_

### Additional Information/Context

_No response_

### CDK CLI Version

2.178.1 (build ae342cb)

### Framework Version

_No response_

### Node.js Version

v18.17.0

### OS

Windows

### Language

TypeScript

### Language Version

5.6.3

### Other information

_No response_

Contributor guide

Open the contributing guide

Research direction

Reproduce the failure with cdk synth using app.ts, lambda-stack.ts, and secret-stack.ts from the linked example. Start by tracing SecretStack's addRotationSchedule call and the generated Lambda permission across the two stacks. Done means the cross-stack RotationSchedule synthesizes successfully without a cyclic reference.

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
Clearly specified
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.