(aws-secretsmanager): Creating RotationSchedule when rotation lambda and secret are in different stack fails due to cyclic reference
- 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
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