diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-secretsmanager/test/integ.hosted-rotation.js.snapshot/cdk-integ-secret-hosted-rotation.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-secretsmanager/test/integ.hosted-rotation.js.snapshot/cdk-integ-secret-hosted-rotation.assets.json index 6804fe656f48f..7561a484f8de2 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-secretsmanager/test/integ.hosted-rotation.js.snapshot/cdk-integ-secret-hosted-rotation.assets.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-secretsmanager/test/integ.hosted-rotation.js.snapshot/cdk-integ-secret-hosted-rotation.assets.json @@ -1,7 +1,7 @@ { - "version": "20.0.0", + "version": "31.0.0", "files": { - "80e7147ae17e29a7810c1890b8caa90a140f0089dcb2dce470bd13d88e5acc41": { + "68111103cf6a45cb34025acaab5488606270170cf3e4bccee5883433fe58e704": { "source": { "path": "cdk-integ-secret-hosted-rotation.template.json", "packaging": "file" @@ -9,7 +9,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "80e7147ae17e29a7810c1890b8caa90a140f0089dcb2dce470bd13d88e5acc41.json", + "objectKey": "68111103cf6a45cb34025acaab5488606270170cf3e4bccee5883433fe58e704.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-secretsmanager/test/integ.hosted-rotation.js.snapshot/cdk-integ-secret-hosted-rotation.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-secretsmanager/test/integ.hosted-rotation.js.snapshot/cdk-integ-secret-hosted-rotation.template.json index 2f661337010e5..100dd501b3c01 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-secretsmanager/test/integ.hosted-rotation.js.snapshot/cdk-integ-secret-hosted-rotation.template.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-secretsmanager/test/integ.hosted-rotation.js.snapshot/cdk-integ-secret-hosted-rotation.template.json @@ -82,6 +82,7 @@ "ExcludeCharacters": "&@/", "RotationType": "MySQLSingleUser" }, + "RotateImmediatelyOnUpdate": false, "RotationRules": { "AutomaticallyAfterDays": 30 } diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-secretsmanager/test/integ.hosted-rotation.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-secretsmanager/test/integ.hosted-rotation.js.snapshot/cdk.out index 588d7b269d34f..7925065efbcc4 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-secretsmanager/test/integ.hosted-rotation.js.snapshot/cdk.out +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-secretsmanager/test/integ.hosted-rotation.js.snapshot/cdk.out @@ -1 +1 @@ -{"version":"20.0.0"} \ No newline at end of file +{"version":"31.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-secretsmanager/test/integ.hosted-rotation.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-secretsmanager/test/integ.hosted-rotation.js.snapshot/integ.json index 1a8e6dc204e86..a81244af673a1 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-secretsmanager/test/integ.hosted-rotation.js.snapshot/integ.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-secretsmanager/test/integ.hosted-rotation.js.snapshot/integ.json @@ -1,5 +1,5 @@ { - "version": "20.0.0", + "version": "31.0.0", "testCases": { "integ.hosted-rotation": { "stacks": [ diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-secretsmanager/test/integ.hosted-rotation.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-secretsmanager/test/integ.hosted-rotation.js.snapshot/manifest.json index c0a8e248ba99f..bc1b5e28f4a97 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-secretsmanager/test/integ.hosted-rotation.js.snapshot/manifest.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-secretsmanager/test/integ.hosted-rotation.js.snapshot/manifest.json @@ -1,12 +1,6 @@ { - "version": "20.0.0", + "version": "31.0.0", "artifacts": { - "Tree": { - "type": "cdk:tree", - "properties": { - "file": "tree.json" - } - }, "cdk-integ-secret-hosted-rotation.assets": { "type": "cdk:asset-manifest", "properties": { @@ -23,7 +17,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/80e7147ae17e29a7810c1890b8caa90a140f0089dcb2dce470bd13d88e5acc41.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/68111103cf6a45cb34025acaab5488606270170cf3e4bccee5883433fe58e704.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -89,6 +83,12 @@ ] }, "displayName": "cdk-integ-secret-hosted-rotation" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } } } } \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-secretsmanager/test/integ.hosted-rotation.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-secretsmanager/test/integ.hosted-rotation.js.snapshot/tree.json index 2b151e47b7a18..8376f334fd4f0 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-secretsmanager/test/integ.hosted-rotation.js.snapshot/tree.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-secretsmanager/test/integ.hosted-rotation.js.snapshot/tree.json @@ -4,14 +4,6 @@ "id": "App", "path": "", "children": { - "Tree": { - "id": "Tree", - "path": "Tree", - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" - } - }, "cdk-integ-secret-hosted-rotation": { "id": "cdk-integ-secret-hosted-rotation", "path": "cdk-integ-secret-hosted-rotation", @@ -30,7 +22,7 @@ } }, "constructInfo": { - "fqn": "@aws-cdk/aws-secretsmanager.CfnSecret", + "fqn": "aws-cdk-lib.aws_secretsmanager.CfnSecret", "version": "0.0.0" } }, @@ -57,13 +49,13 @@ } }, "constructInfo": { - "fqn": "@aws-cdk/aws-secretsmanager.CfnRotationSchedule", + "fqn": "aws-cdk-lib.aws_secretsmanager.CfnRotationSchedule", "version": "0.0.0" } } }, "constructInfo": { - "fqn": "@aws-cdk/aws-secretsmanager.RotationSchedule", + "fqn": "aws-cdk-lib.aws_secretsmanager.RotationSchedule", "version": "0.0.0" } }, @@ -111,19 +103,19 @@ } }, "constructInfo": { - "fqn": "@aws-cdk/aws-secretsmanager.CfnResourcePolicy", + "fqn": "aws-cdk-lib.aws_secretsmanager.CfnResourcePolicy", "version": "0.0.0" } } }, "constructInfo": { - "fqn": "@aws-cdk/aws-secretsmanager.ResourcePolicy", + "fqn": "aws-cdk-lib.aws_secretsmanager.ResourcePolicy", "version": "0.0.0" } } }, "constructInfo": { - "fqn": "@aws-cdk/aws-secretsmanager.Secret", + "fqn": "aws-cdk-lib.aws_secretsmanager.Secret", "version": "0.0.0" } }, @@ -143,7 +135,7 @@ } }, "constructInfo": { - "fqn": "@aws-cdk/aws-secretsmanager.CfnSecret", + "fqn": "aws-cdk-lib.aws_secretsmanager.CfnSecret", "version": "0.0.0" } }, @@ -164,19 +156,20 @@ "rotationType": "MySQLSingleUser", "excludeCharacters": "&@/" }, + "rotateImmediatelyOnUpdate": false, "rotationRules": { "automaticallyAfterDays": 30 } } }, "constructInfo": { - "fqn": "@aws-cdk/aws-secretsmanager.CfnRotationSchedule", + "fqn": "aws-cdk-lib.aws_secretsmanager.CfnRotationSchedule", "version": "0.0.0" } } }, "constructInfo": { - "fqn": "@aws-cdk/aws-secretsmanager.RotationSchedule", + "fqn": "aws-cdk-lib.aws_secretsmanager.RotationSchedule", "version": "0.0.0" } }, @@ -224,32 +217,56 @@ } }, "constructInfo": { - "fqn": "@aws-cdk/aws-secretsmanager.CfnResourcePolicy", + "fqn": "aws-cdk-lib.aws_secretsmanager.CfnResourcePolicy", "version": "0.0.0" } } }, "constructInfo": { - "fqn": "@aws-cdk/aws-secretsmanager.ResourcePolicy", + "fqn": "aws-cdk-lib.aws_secretsmanager.ResourcePolicy", "version": "0.0.0" } } }, "constructInfo": { - "fqn": "@aws-cdk/aws-secretsmanager.Secret", + "fqn": "aws-cdk-lib.aws_secretsmanager.Secret", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "cdk-integ-secret-hosted-rotation/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "cdk-integ-secret-hosted-rotation/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", "version": "0.0.0" } } }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.85" + "version": "10.2.26" } } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "aws-cdk-lib.App", + "version": "0.0.0" } } } \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-secretsmanager/test/integ.hosted-rotation.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-secretsmanager/test/integ.hosted-rotation.ts index a5ec40c9ecc37..0812c00e2bd0f 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-secretsmanager/test/integ.hosted-rotation.ts +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-secretsmanager/test/integ.hosted-rotation.ts @@ -17,6 +17,7 @@ class TestStack extends cdk.Stack { }); customSecret.addRotationSchedule('Schedule', { hostedRotation: secretsmanager.HostedRotation.mysqlSingleUser(), + rotateImmediatelyOnUpdate: false, }); } } diff --git a/packages/aws-cdk-lib/aws-secretsmanager/README.md b/packages/aws-cdk-lib/aws-secretsmanager/README.md index c17bc84edf357..989da6bf614d4 100644 --- a/packages/aws-cdk-lib/aws-secretsmanager/README.md +++ b/packages/aws-cdk-lib/aws-secretsmanager/README.md @@ -124,6 +124,7 @@ const secret = new secretsmanager.Secret(this, 'Secret'); secret.addRotationSchedule('RotationSchedule', { hostedRotation: secretsmanager.HostedRotation.mysqlSingleUser(), + rotateImmediatelyOnUpdate: false, // by default, Secrets Manager rotates the secret immediately }); ``` diff --git a/packages/aws-cdk-lib/aws-secretsmanager/lib/rotation-schedule.ts b/packages/aws-cdk-lib/aws-secretsmanager/lib/rotation-schedule.ts index 11a0e17ab3d0d..736bf715ee435 100644 --- a/packages/aws-cdk-lib/aws-secretsmanager/lib/rotation-schedule.ts +++ b/packages/aws-cdk-lib/aws-secretsmanager/lib/rotation-schedule.ts @@ -42,6 +42,14 @@ export interface RotationScheduleOptions { * @default Duration.days(30) */ readonly automaticallyAfter?: Duration; + + /** + * Specifies whether to rotate the secret immediately or wait until the next + * scheduled rotation window. + * + * @default - secret is rotated immediately + */ + readonly rotateImmediatelyOnUpdate?: boolean; } /** @@ -132,6 +140,7 @@ export class RotationSchedule extends Resource { rotationLambdaArn: props.rotationLambda?.functionArn, hostedRotationLambda: props.hostedRotation?.bind(props.secret, this), rotationRules, + rotateImmediatelyOnUpdate: props.rotateImmediatelyOnUpdate, }); // Prevent secrets deletions when rotation is in place diff --git a/packages/aws-cdk-lib/aws-secretsmanager/test/rotation-schedule.test.ts b/packages/aws-cdk-lib/aws-secretsmanager/test/rotation-schedule.test.ts index 1fd2e7cd3268d..2452c751c7830 100644 --- a/packages/aws-cdk-lib/aws-secretsmanager/test/rotation-schedule.test.ts +++ b/packages/aws-cdk-lib/aws-secretsmanager/test/rotation-schedule.test.ts @@ -43,6 +43,34 @@ test('create a rotation schedule with a rotation Lambda', () => { }); }); +test('create a rotation schedule without immediate rotation', () => { + // GIVEN + const secret = new secretsmanager.Secret(stack, 'Secret'); + const rotationLambda = new lambda.Function(stack, 'Lambda', { + runtime: lambda.Runtime.NODEJS_14_X, + code: lambda.Code.fromInline('export.handler = event => event;'), + handler: 'index.handler', + }); + + // WHEN + new secretsmanager.RotationSchedule(stack, 'RotationSchedule', { + secret, + rotationLambda, + rotateImmediatelyOnUpdate: false, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::SecretsManager::RotationSchedule', { + SecretId: { + Ref: 'SecretA720EF05', + }, + RotationRules: { + AutomaticallyAfterDays: 30, + }, + RotateImmediatelyOnUpdate: false, + }); +}); + test('assign permissions for rotation schedule with a rotation Lambda', () => { // GIVEN const secret = new secretsmanager.Secret(stack, 'Secret');