From 09cb003fb917714c0dc88b47cd05893c2a816d45 Mon Sep 17 00:00:00 2001 From: Luca Pizzini Date: Fri, 15 Dec 2023 00:57:20 +0100 Subject: [PATCH] fix(secretsmanager): cannot set hourly rotation (#28303) Allows to set hourly rotation up to 4 hours on secrets as per [official docs](https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotate-secrets_managed.html). Closes #28261. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- ...s-cdk-docdb-cluster-rotation.template.json | 2 +- .../tree.json | 2 +- ...aws-cdk-rds-cluster-rotation.template.json | 4 +- .../tree.json | 4 +- .../cdk-integ-cluster-snapshot.template.json | 2 +- .../tree.json | 2 +- .../aws-cdk-rds-instance.template.json | 2 +- .../integ.instance.lit.js.snapshot/tree.json | 2 +- ...dk-rds-integ-secret-rotation.template.json | 2 +- .../tree.json | 2 +- ...dk-rds-integ-secret-rotation.template.json | 2 +- .../tree.json | 2 +- ...integ-secret-hosted-rotation.template.json | 4 +- .../tree.json | 4 +- ...integ-secret-lambda-rotation.template.json | 2 +- .../tree.json | 2 +- .../test/integ.lambda-rotation.ts | 1 + .../aws-docdb/test/cluster.test.ts | 4 +- .../aws-cdk-lib/aws-rds/test/cluster.test.ts | 16 ++-- .../aws-cdk-lib/aws-rds/test/instance.test.ts | 8 +- .../lib/rotation-schedule.ts | 28 ++++-- .../test/rotation-schedule.test.ts | 92 +++++++++++++++++-- .../test/secret-rotation.test.ts | 2 +- 23 files changed, 141 insertions(+), 50 deletions(-) diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-rotation.lit.js.snapshot/aws-cdk-docdb-cluster-rotation.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-rotation.lit.js.snapshot/aws-cdk-docdb-cluster-rotation.template.json index 9206ab9a17ce8..1271bf7c1dca2 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-rotation.lit.js.snapshot/aws-cdk-docdb-cluster-rotation.template.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-rotation.lit.js.snapshot/aws-cdk-docdb-cluster-rotation.template.json @@ -501,7 +501,7 @@ ] }, "RotationRules": { - "AutomaticallyAfterDays": 30 + "ScheduleExpression": "rate(30 days)" }, "SecretId": { "Ref": "DatabaseSecretAttachmentE5D1B020" diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-rotation.lit.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-rotation.lit.js.snapshot/tree.json index 3bb92efa6d3ca..951bdfa30bb22 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-rotation.lit.js.snapshot/tree.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-rotation.lit.js.snapshot/tree.json @@ -825,7 +825,7 @@ ] }, "rotationRules": { - "automaticallyAfterDays": 30 + "scheduleExpression": "rate(30 days)" }, "secretId": { "Ref": "DatabaseSecretAttachmentE5D1B020" diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.cluster-rotation.lit.js.snapshot/aws-cdk-rds-cluster-rotation.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.cluster-rotation.lit.js.snapshot/aws-cdk-rds-cluster-rotation.template.json index 794425c15d58a..ce8f516772028 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.cluster-rotation.lit.js.snapshot/aws-cdk-rds-cluster-rotation.template.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.cluster-rotation.lit.js.snapshot/aws-cdk-rds-cluster-rotation.template.json @@ -599,7 +599,7 @@ ] }, "RotationRules": { - "AutomaticallyAfterDays": 30 + "ScheduleExpression": "rate(30 days)" }, "SecretId": { "Ref": "DatabaseSecretAttachmentE5D1B020" @@ -920,7 +920,7 @@ ] }, "RotationRules": { - "AutomaticallyAfterDays": 7 + "ScheduleExpression": "rate(7 days)" }, "SecretId": { "Ref": "CustomRotationOptionsSecretAttachment697A23BF" diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.cluster-rotation.lit.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.cluster-rotation.lit.js.snapshot/tree.json index 5c1665d932a55..301426d3607e9 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.cluster-rotation.lit.js.snapshot/tree.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.cluster-rotation.lit.js.snapshot/tree.json @@ -995,7 +995,7 @@ ] }, "rotationRules": { - "automaticallyAfterDays": 30 + "scheduleExpression": "rate(30 days)" }, "secretId": { "Ref": "DatabaseSecretAttachmentE5D1B020" @@ -1520,7 +1520,7 @@ ] }, "rotationRules": { - "automaticallyAfterDays": 7 + "scheduleExpression": "rate(7 days)" }, "secretId": { "Ref": "CustomRotationOptionsSecretAttachment697A23BF" diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.cluster-snapshot.js.snapshot/cdk-integ-cluster-snapshot.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.cluster-snapshot.js.snapshot/cdk-integ-cluster-snapshot.template.json index ebe5672ba9069..9a5f1622df555 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.cluster-snapshot.js.snapshot/cdk-integ-cluster-snapshot.template.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.cluster-snapshot.js.snapshot/cdk-integ-cluster-snapshot.template.json @@ -1403,7 +1403,7 @@ ] }, "RotationRules": { - "AutomaticallyAfterDays": 30 + "ScheduleExpression": "rate(30 days)" }, "SecretId": { "Ref": "FromSnapshotSnapshotSecretAttachmentA3F619B8" diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.cluster-snapshot.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.cluster-snapshot.js.snapshot/tree.json index 09ea615fb01f4..3b559b1a4d8bd 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.cluster-snapshot.js.snapshot/tree.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.cluster-snapshot.js.snapshot/tree.json @@ -2285,7 +2285,7 @@ ] }, "rotationRules": { - "automaticallyAfterDays": 30 + "scheduleExpression": "rate(30 days)" }, "secretId": { "Ref": "FromSnapshotSnapshotSecretAttachmentA3F619B8" diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.instance.lit.js.snapshot/aws-cdk-rds-instance.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.instance.lit.js.snapshot/aws-cdk-rds-instance.template.json index 0d3ab4158f690..a2f3e86d3ac10 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.instance.lit.js.snapshot/aws-cdk-rds-instance.template.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.instance.lit.js.snapshot/aws-cdk-rds-instance.template.json @@ -616,7 +616,7 @@ ] }, "RotationRules": { - "AutomaticallyAfterDays": 30 + "ScheduleExpression": "rate(30 days)" }, "SecretId": { "Ref": "InstanceSecretAttachment83BEE581" diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.instance.lit.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.instance.lit.js.snapshot/tree.json index 5633e4c3d92f9..7f75600ca577a 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.instance.lit.js.snapshot/tree.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.instance.lit.js.snapshot/tree.json @@ -1040,7 +1040,7 @@ ] }, "rotationRules": { - "automaticallyAfterDays": 30 + "scheduleExpression": "rate(30 days)" }, "secretId": { "Ref": "InstanceSecretAttachment83BEE581" diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.serverless-cluster-secret-rotation-custom-names.js.snapshot/aws-cdk-rds-integ-secret-rotation.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.serverless-cluster-secret-rotation-custom-names.js.snapshot/aws-cdk-rds-integ-secret-rotation.template.json index 9670594fcd147..0a12dc325c73e 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.serverless-cluster-secret-rotation-custom-names.js.snapshot/aws-cdk-rds-integ-secret-rotation.template.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.serverless-cluster-secret-rotation-custom-names.js.snapshot/aws-cdk-rds-integ-secret-rotation.template.json @@ -84,7 +84,7 @@ "RotationType": "MySQLSingleUser" }, "RotationRules": { - "AutomaticallyAfterDays": 30 + "ScheduleExpression": "rate(30 days)" } } }, diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.serverless-cluster-secret-rotation-custom-names.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.serverless-cluster-secret-rotation-custom-names.js.snapshot/tree.json index 8b0ca324ebc1d..b9adb54e8c634 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.serverless-cluster-secret-rotation-custom-names.js.snapshot/tree.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.serverless-cluster-secret-rotation-custom-names.js.snapshot/tree.json @@ -142,7 +142,7 @@ "excludeCharacters": " %+~`#$&*()|[]{}:;<>?!'/@\"\\" }, "rotationRules": { - "automaticallyAfterDays": 30 + "scheduleExpression": "rate(30 days)" } } }, diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.serverless-cluster-secret-rotation.js.snapshot/aws-cdk-rds-integ-secret-rotation.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.serverless-cluster-secret-rotation.js.snapshot/aws-cdk-rds-integ-secret-rotation.template.json index 84e9db49cabdc..c9972ef2c6403 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.serverless-cluster-secret-rotation.js.snapshot/aws-cdk-rds-integ-secret-rotation.template.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.serverless-cluster-secret-rotation.js.snapshot/aws-cdk-rds-integ-secret-rotation.template.json @@ -83,7 +83,7 @@ "RotationType": "MySQLSingleUser" }, "RotationRules": { - "AutomaticallyAfterDays": 30 + "ScheduleExpression": "rate(30 days)" } } }, diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.serverless-cluster-secret-rotation.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.serverless-cluster-secret-rotation.js.snapshot/tree.json index 324bde72c38a6..4a400c436d2ea 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.serverless-cluster-secret-rotation.js.snapshot/tree.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.serverless-cluster-secret-rotation.js.snapshot/tree.json @@ -141,7 +141,7 @@ "excludeCharacters": " %+~`#$&*()|[]{}:;<>?!'/@\"\\" }, "rotationRules": { - "automaticallyAfterDays": 30 + "scheduleExpression": "rate(30 days)" } } }, 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 100dd501b3c01..b435c2b732113 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 @@ -22,7 +22,7 @@ "RotationType": "MySQLSingleUser" }, "RotationRules": { - "AutomaticallyAfterDays": 30 + "ScheduleExpression": "rate(30 days)" } } }, @@ -84,7 +84,7 @@ }, "RotateImmediatelyOnUpdate": false, "RotationRules": { - "AutomaticallyAfterDays": 30 + "ScheduleExpression": "rate(30 days)" } } }, 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 8376f334fd4f0..c18789e6a490b 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 @@ -44,7 +44,7 @@ "excludeCharacters": " %+~`#$&*()|[]{}:;<>?!'/@\"\\" }, "rotationRules": { - "automaticallyAfterDays": 30 + "scheduleExpression": "rate(30 days)" } } }, @@ -158,7 +158,7 @@ }, "rotateImmediatelyOnUpdate": false, "rotationRules": { - "automaticallyAfterDays": 30 + "scheduleExpression": "rate(30 days)" } } }, diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-secretsmanager/test/integ.lambda-rotation.js.snapshot/cdk-integ-secret-lambda-rotation.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-secretsmanager/test/integ.lambda-rotation.js.snapshot/cdk-integ-secret-lambda-rotation.template.json index 58263e2820f90..658bd5c501b9f 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-secretsmanager/test/integ.lambda-rotation.js.snapshot/cdk-integ-secret-lambda-rotation.template.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-secretsmanager/test/integ.lambda-rotation.js.snapshot/cdk-integ-secret-lambda-rotation.template.json @@ -139,7 +139,7 @@ ] }, "RotationRules": { - "AutomaticallyAfterDays": 30 + "ScheduleExpression": "rate(4 hours)" }, "SecretId": { "Ref": "SecretA720EF05" diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-secretsmanager/test/integ.lambda-rotation.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-secretsmanager/test/integ.lambda-rotation.js.snapshot/tree.json index 207294c8edffe..d20587d644344 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-secretsmanager/test/integ.lambda-rotation.js.snapshot/tree.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-secretsmanager/test/integ.lambda-rotation.js.snapshot/tree.json @@ -180,7 +180,7 @@ ] }, "rotationRules": { - "automaticallyAfterDays": 30 + "scheduleExpression": "rate(4 hours)" }, "secretId": { "Ref": "SecretA720EF05" diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-secretsmanager/test/integ.lambda-rotation.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-secretsmanager/test/integ.lambda-rotation.ts index 4382ab413362b..568debb390304 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-secretsmanager/test/integ.lambda-rotation.ts +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-secretsmanager/test/integ.lambda-rotation.ts @@ -21,6 +21,7 @@ class TestStack extends cdk.Stack { handler: 'index.handler', code: lambda.Code.fromInline('NOOP'), }), + automaticallyAfter: cdk.Duration.hours(4), }); } } diff --git a/packages/aws-cdk-lib/aws-docdb/test/cluster.test.ts b/packages/aws-cdk-lib/aws-docdb/test/cluster.test.ts index 50b23408db59a..7b1ff72a62984 100644 --- a/packages/aws-cdk-lib/aws-docdb/test/cluster.test.ts +++ b/packages/aws-cdk-lib/aws-docdb/test/cluster.test.ts @@ -785,7 +785,7 @@ describe('DatabaseCluster', () => { 'Fn::GetAtt': ['DatabaseRotationSingleUser65F55654', 'Outputs.RotationLambdaARN'], }, RotationRules: { - AutomaticallyAfterDays: 5, + ScheduleExpression: 'rate(5 days)', }, }); }); @@ -899,7 +899,7 @@ describe('DatabaseCluster', () => { 'Fn::GetAtt': ['DatabaseRotation6B6E1D86', 'Outputs.RotationLambdaARN'], }, RotationRules: { - AutomaticallyAfterDays: 5, + ScheduleExpression: 'rate(5 days)', }, }); }); diff --git a/packages/aws-cdk-lib/aws-rds/test/cluster.test.ts b/packages/aws-cdk-lib/aws-rds/test/cluster.test.ts index 0b3644fc7e1a7..c900952d76f79 100644 --- a/packages/aws-cdk-lib/aws-rds/test/cluster.test.ts +++ b/packages/aws-cdk-lib/aws-rds/test/cluster.test.ts @@ -1975,7 +1975,7 @@ describe('cluster', () => { ], }, RotationRules: { - AutomaticallyAfterDays: 30, + ScheduleExpression: 'rate(30 days)', }, }); }); @@ -2006,7 +2006,7 @@ describe('cluster', () => { ], }, RotationRules: { - AutomaticallyAfterDays: 30, + ScheduleExpression: 'rate(30 days)', }, }); @@ -2058,7 +2058,7 @@ describe('cluster', () => { // THEN Template.fromStack(stack).hasResourceProperties('AWS::SecretsManager::RotationSchedule', { RotationRules: { - AutomaticallyAfterDays: 15, + ScheduleExpression: 'rate(15 days)', }, }); @@ -2125,7 +2125,7 @@ describe('cluster', () => { // THEN Template.fromStack(stack).hasResourceProperties('AWS::SecretsManager::RotationSchedule', { RotationRules: { - AutomaticallyAfterDays: 15, + ScheduleExpression: 'rate(15 days)', }, }); @@ -2231,7 +2231,7 @@ describe('cluster', () => { ], }, RotationRules: { - AutomaticallyAfterDays: 30, + ScheduleExpression: 'rate(30 days)', }, RotateImmediatelyOnUpdate: false, }); @@ -2266,7 +2266,7 @@ describe('cluster', () => { ], }, RotationRules: { - AutomaticallyAfterDays: 30, + ScheduleExpression: 'rate(30 days)', }, RotateImmediatelyOnUpdate: false, }); @@ -3439,7 +3439,7 @@ describe('cluster', () => { // THEN Template.fromStack(stack).hasResourceProperties('AWS::SecretsManager::RotationSchedule', { RotationRules: { - AutomaticallyAfterDays: 30, + ScheduleExpression: 'rate(30 days)', }, }); }); @@ -3493,7 +3493,7 @@ describe('cluster', () => { ], }, RotationRules: { - AutomaticallyAfterDays: 30, + ScheduleExpression: 'rate(30 days)', }, }); diff --git a/packages/aws-cdk-lib/aws-rds/test/instance.test.ts b/packages/aws-cdk-lib/aws-rds/test/instance.test.ts index 3e791f2a0ab81..92f9d41cccc1e 100644 --- a/packages/aws-cdk-lib/aws-rds/test/instance.test.ts +++ b/packages/aws-cdk-lib/aws-rds/test/instance.test.ts @@ -782,7 +782,7 @@ describe('instance', () => { ], }, RotationRules: { - AutomaticallyAfterDays: 30, + ScheduleExpression: 'rate(30 days)', }, }); }); @@ -810,7 +810,7 @@ describe('instance', () => { ], }, RotationRules: { - AutomaticallyAfterDays: 30, + ScheduleExpression: 'rate(30 days)', }, }); @@ -858,7 +858,7 @@ describe('instance', () => { // THEN Template.fromStack(stack).hasResourceProperties('AWS::SecretsManager::RotationSchedule', { RotationRules: { - AutomaticallyAfterDays: 15, + ScheduleExpression: 'rate(15 days)', }, }); @@ -921,7 +921,7 @@ describe('instance', () => { // THEN Template.fromStack(stack).hasResourceProperties('AWS::SecretsManager::RotationSchedule', { RotationRules: { - AutomaticallyAfterDays: 15, + ScheduleExpression: 'rate(15 days)', }, }); 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 a9f1ecdd0545d..32f89b50f8d9e 100644 --- a/packages/aws-cdk-lib/aws-secretsmanager/lib/rotation-schedule.ts +++ b/packages/aws-cdk-lib/aws-secretsmanager/lib/rotation-schedule.ts @@ -2,6 +2,7 @@ import { Construct } from 'constructs'; import { ISecret, Secret } from './secret'; import { CfnRotationSchedule } from './secretsmanager.generated'; import * as ec2 from '../../aws-ec2'; +import { Schedule } from '../../aws-events'; import * as iam from '../../aws-iam'; import * as kms from '../../aws-kms'; import * as lambda from '../../aws-lambda'; @@ -37,6 +38,7 @@ export interface RotationScheduleOptions { * Specifies the number of days after the previous rotation before * Secrets Manager triggers the next automatic rotation. * + * The minimum value is 4 hours. * The maximum value is 1000 days. * * A value of zero (`Duration.days(0)`) will not create RotationRules. @@ -126,18 +128,26 @@ export class RotationSchedule extends Resource { ); } - let automaticallyAfterDays: number | undefined = undefined; - if (props.automaticallyAfter && props.automaticallyAfter.toDays() > 1000) { - throw new Error(`automaticallyAfter must not be greater than 1000 days, got ${props.automaticallyAfter.toDays()} days`); - } - if (props.automaticallyAfter?.toMilliseconds() !== 0) { - automaticallyAfterDays = props.automaticallyAfter?.toDays() || 30; + let scheduleExpression: string | undefined; + if (props.automaticallyAfter) { + const automaticallyAfterMillis = props.automaticallyAfter.toMilliseconds(); + if (automaticallyAfterMillis > 0) { + if (automaticallyAfterMillis < Duration.hours(4).toMilliseconds()) { + throw new Error(`automaticallyAfter must not be smaller than 4 hours, got ${props.automaticallyAfter.toHours()} hours`); + } + if (automaticallyAfterMillis > Duration.days(1000).toMilliseconds()) { + throw new Error(`automaticallyAfter must not be greater than 1000 days, got ${props.automaticallyAfter.toDays()} days`); + } + scheduleExpression = Schedule.rate(props.automaticallyAfter).expressionString; + } + } else { + scheduleExpression = Schedule.rate(Duration.days(30)).expressionString; } - let rotationRules: CfnRotationSchedule.RotationRulesProperty | undefined = undefined; - if (automaticallyAfterDays !== undefined) { + let rotationRules: CfnRotationSchedule.RotationRulesProperty | undefined; + if (scheduleExpression) { rotationRules = { - automaticallyAfterDays, + scheduleExpression, }; } 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 81d14021be962..dacfe106e0f8d 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 @@ -38,7 +38,7 @@ test('create a rotation schedule with a rotation Lambda', () => { ], }, RotationRules: { - AutomaticallyAfterDays: 30, + ScheduleExpression: 'rate(30 days)', }, }); }); @@ -65,7 +65,7 @@ test('create a rotation schedule without immediate rotation', () => { Ref: 'SecretA720EF05', }, RotationRules: { - AutomaticallyAfterDays: 30, + ScheduleExpression: 'rate(30 days)', }, RotateImmediatelyOnUpdate: false, }); @@ -261,7 +261,7 @@ describe('hosted rotation', () => { ExcludeCharacters: " %+~`#$&*()|[]{}:;<>?!'/@\"\\", }, RotationRules: { - AutomaticallyAfterDays: 30, + ScheduleExpression: 'rate(30 days)', }, }); @@ -328,7 +328,7 @@ describe('hosted rotation', () => { RotationType: 'PostgreSQLMultiUser', }, RotationRules: { - AutomaticallyAfterDays: 30, + ScheduleExpression: 'rate(30 days)', }, }); @@ -411,7 +411,7 @@ describe('hosted rotation', () => { }, }, RotationRules: { - AutomaticallyAfterDays: 30, + ScheduleExpression: 'rate(30 days)', }, }); @@ -497,7 +497,7 @@ describe('hosted rotation', () => { }, }, RotationRules: { - AutomaticallyAfterDays: 30, + ScheduleExpression: 'rate(30 days)', }, }); @@ -652,6 +652,86 @@ test('rotation schedule should have a dependency on lambda permissions', () => { }); }); +test('automaticallyAfter set scheduleExpression with days duration', () => { + // GIVEN + const secret = new secretsmanager.Secret(stack, 'Secret'); + const rotationLambda = new lambda.Function(stack, 'Lambda', { + runtime: lambda.Runtime.NODEJS_LATEST, + code: lambda.Code.fromInline('export.handler = event => event;'), + handler: 'index.handler', + }); + + // WHEN + new secretsmanager.RotationSchedule(stack, 'RotationSchedule', { + secret, + rotationLambda, + automaticallyAfter: Duration.days(90), + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::SecretsManager::RotationSchedule', Match.objectEquals({ + SecretId: { Ref: 'SecretA720EF05' }, + RotationLambdaARN: { + 'Fn::GetAtt': [ + 'LambdaD247545B', + 'Arn', + ], + }, + RotationRules: { + ScheduleExpression: 'rate(90 days)', + }, + })); +}); + +test('automaticallyAfter set scheduleExpression with hours duration', () => { + // GIVEN + const secret = new secretsmanager.Secret(stack, 'Secret'); + const rotationLambda = new lambda.Function(stack, 'Lambda', { + runtime: lambda.Runtime.NODEJS_LATEST, + code: lambda.Code.fromInline('export.handler = event => event;'), + handler: 'index.handler', + }); + + // WHEN + new secretsmanager.RotationSchedule(stack, 'RotationSchedule', { + secret, + rotationLambda, + automaticallyAfter: Duration.hours(6), + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::SecretsManager::RotationSchedule', Match.objectEquals({ + SecretId: { Ref: 'SecretA720EF05' }, + RotationLambdaARN: { + 'Fn::GetAtt': [ + 'LambdaD247545B', + 'Arn', + ], + }, + RotationRules: { + ScheduleExpression: 'rate(6 hours)', + }, + })); +}); + +test('automaticallyAfter must not be smaller than 4 hours', () => { + // GIVEN + const secret = new secretsmanager.Secret(stack, 'Secret'); + const rotationLambda = new lambda.Function(stack, 'Lambda', { + runtime: lambda.Runtime.NODEJS_LATEST, + code: lambda.Code.fromInline('export.handler = event => event;'), + handler: 'index.handler', + }); + + // WHEN + // THEN + expect(() => new secretsmanager.RotationSchedule(stack, 'RotationSchedule', { + secret, + rotationLambda, + automaticallyAfter: Duration.hours(2), + })).toThrow(/automaticallyAfter must not be smaller than 4 hours, got 2 hours/); +}); + test('automaticallyAfter must not be greater than 1000 days', () => { // GIVEN const secret = new secretsmanager.Secret(stack, 'Secret'); diff --git a/packages/aws-cdk-lib/aws-secretsmanager/test/secret-rotation.test.ts b/packages/aws-cdk-lib/aws-secretsmanager/test/secret-rotation.test.ts index 0d24a57fed8ab..72f559bf7a265 100644 --- a/packages/aws-cdk-lib/aws-secretsmanager/test/secret-rotation.test.ts +++ b/packages/aws-cdk-lib/aws-secretsmanager/test/secret-rotation.test.ts @@ -63,7 +63,7 @@ test('secret rotation single user', () => { ], }, RotationRules: { - AutomaticallyAfterDays: 30, + ScheduleExpression: 'rate(30 days)', }, });