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 7561a484f8de2..be6d3d10195f8 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": "31.0.0", + "version": "35.0.0", "files": { - "68111103cf6a45cb34025acaab5488606270170cf3e4bccee5883433fe58e704": { + "db9ef4d632616698eb575a61885fbee99b7479aae25d14e8cd4c6774b50a5996": { "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": "68111103cf6a45cb34025acaab5488606270170cf3e4bccee5883433fe58e704.json", + "objectKey": "db9ef4d632616698eb575a61885fbee99b7479aae25d14e8cd4c6774b50a5996.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 b435c2b732113..ba97007a7276d 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 @@ -14,15 +14,15 @@ "SecretSchedule18F2CB66": { "Type": "AWS::SecretsManager::RotationSchedule", "Properties": { - "SecretId": { - "Ref": "SecretA720EF05" - }, "HostedRotationLambda": { "ExcludeCharacters": " %+~`#$&*()|[]{}:;<>?!'/@\"\\", "RotationType": "MySQLSingleUser" }, "RotationRules": { "ScheduleExpression": "rate(30 days)" + }, + "SecretId": { + "Ref": "SecretA720EF05" } } }, @@ -75,9 +75,6 @@ "CustomSecretScheduleDD99F351": { "Type": "AWS::SecretsManager::RotationSchedule", "Properties": { - "SecretId": { - "Ref": "CustomSecret5DC95D87" - }, "HostedRotationLambda": { "ExcludeCharacters": "&@/", "RotationType": "MySQLSingleUser" @@ -85,6 +82,9 @@ "RotateImmediatelyOnUpdate": false, "RotationRules": { "ScheduleExpression": "rate(30 days)" + }, + "SecretId": { + "Ref": "CustomSecret5DC95D87" } } }, @@ -123,6 +123,85 @@ "Ref": "CustomSecret5DC95D87" } } + }, + "MySecret8FE80B51": { + "Type": "AWS::SecretsManager::Secret", + "Properties": { + "GenerateSecretString": {} + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "MySecretRotationSchedule673B961C": { + "Type": "AWS::SecretsManager::RotationSchedule", + "Properties": { + "HostedRotationLambda": { + "ExcludeCharacters": " %+~`#$&*()|[]{}:;<>?!'/@\"\\", + "MasterSecretArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":secretsmanager:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":secret:MasterSecret-??????" + ] + ] + }, + "RotationType": "PostgreSQLMultiUser" + }, + "RotationRules": { + "ScheduleExpression": "rate(30 days)" + }, + "SecretId": { + "Ref": "MySecret8FE80B51" + } + } + }, + "MySecretPolicyDAD0E682": { + "Type": "AWS::SecretsManager::ResourcePolicy", + "Properties": { + "ResourcePolicy": { + "Statement": [ + { + "Action": "secretsmanager:DeleteSecret", + "Effect": "Deny", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "SecretId": { + "Ref": "MySecret8FE80B51" + } + } } }, "Parameters": { 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 7925065efbcc4..c5cb2e5de6344 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":"31.0.0"} \ No newline at end of file +{"version":"35.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 a81244af673a1..deae0c29a0a82 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": "31.0.0", + "version": "35.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 bc1b5e28f4a97..f84123534049f 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,5 +1,5 @@ { - "version": "31.0.0", + "version": "35.0.0", "artifacts": { "cdk-integ-secret-hosted-rotation.assets": { "type": "cdk:asset-manifest", @@ -14,10 +14,11 @@ "environment": "aws://unknown-account/unknown-region", "properties": { "templateFile": "cdk-integ-secret-hosted-rotation.template.json", + "terminationProtection": false, "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}/68111103cf6a45cb34025acaab5488606270170cf3e4bccee5883433fe58e704.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/db9ef4d632616698eb575a61885fbee99b7479aae25d14e8cd4c6774b50a5996.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -69,6 +70,24 @@ "data": "CustomSecretPolicy8E81D2EB" } ], + "/cdk-integ-secret-hosted-rotation/MySecret/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MySecret8FE80B51" + } + ], + "/cdk-integ-secret-hosted-rotation/MySecret/RotationSchedule/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MySecretRotationSchedule673B961C" + } + ], + "/cdk-integ-secret-hosted-rotation/MySecret/Policy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MySecretPolicyDAD0E682" + } + ], "/cdk-integ-secret-hosted-rotation/BootstrapVersion": [ { "type": "aws:cdk:logicalId", 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 c18789e6a490b..1176163d6bf3d 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 @@ -22,8 +22,8 @@ } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_secretsmanager.CfnSecret", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.3.0" } }, "Schedule": { @@ -36,27 +36,27 @@ "attributes": { "aws:cdk:cloudformation:type": "AWS::SecretsManager::RotationSchedule", "aws:cdk:cloudformation:props": { - "secretId": { - "Ref": "SecretA720EF05" - }, "hostedRotationLambda": { "rotationType": "MySQLSingleUser", "excludeCharacters": " %+~`#$&*()|[]{}:;<>?!'/@\"\\" }, "rotationRules": { "scheduleExpression": "rate(30 days)" + }, + "secretId": { + "Ref": "SecretA720EF05" } } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_secretsmanager.CfnRotationSchedule", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.3.0" } } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_secretsmanager.RotationSchedule", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.3.0" } }, "Policy": { @@ -103,20 +103,20 @@ } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_secretsmanager.CfnResourcePolicy", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.3.0" } } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_secretsmanager.ResourcePolicy", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.3.0" } } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_secretsmanager.Secret", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.3.0" } }, "CustomSecret": { @@ -135,8 +135,8 @@ } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_secretsmanager.CfnSecret", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.3.0" } }, "Schedule": { @@ -149,9 +149,6 @@ "attributes": { "aws:cdk:cloudformation:type": "AWS::SecretsManager::RotationSchedule", "aws:cdk:cloudformation:props": { - "secretId": { - "Ref": "CustomSecret5DC95D87" - }, "hostedRotationLambda": { "rotationType": "MySQLSingleUser", "excludeCharacters": "&@/" @@ -159,18 +156,21 @@ "rotateImmediatelyOnUpdate": false, "rotationRules": { "scheduleExpression": "rate(30 days)" + }, + "secretId": { + "Ref": "CustomSecret5DC95D87" } } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_secretsmanager.CfnRotationSchedule", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.3.0" } } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_secretsmanager.RotationSchedule", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.3.0" } }, "Policy": { @@ -217,42 +217,181 @@ } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_secretsmanager.CfnResourcePolicy", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "MySecret": { + "id": "MySecret", + "path": "cdk-integ-secret-hosted-rotation/MySecret", + "children": { + "Resource": { + "id": "Resource", + "path": "cdk-integ-secret-hosted-rotation/MySecret/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::SecretsManager::Secret", + "aws:cdk:cloudformation:props": { + "generateSecretString": {} + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "RotationSchedule": { + "id": "RotationSchedule", + "path": "cdk-integ-secret-hosted-rotation/MySecret/RotationSchedule", + "children": { + "Resource": { + "id": "Resource", + "path": "cdk-integ-secret-hosted-rotation/MySecret/RotationSchedule/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::SecretsManager::RotationSchedule", + "aws:cdk:cloudformation:props": { + "hostedRotationLambda": { + "rotationType": "PostgreSQLMultiUser", + "masterSecretArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":secretsmanager:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":secret:MasterSecret-??????" + ] + ] + }, + "excludeCharacters": " %+~`#$&*()|[]{}:;<>?!'/@\"\\" + }, + "rotationRules": { + "scheduleExpression": "rate(30 days)" + }, + "secretId": { + "Ref": "MySecret8FE80B51" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" } } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_secretsmanager.ResourcePolicy", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "Policy": { + "id": "Policy", + "path": "cdk-integ-secret-hosted-rotation/MySecret/Policy", + "children": { + "Resource": { + "id": "Resource", + "path": "cdk-integ-secret-hosted-rotation/MySecret/Policy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::SecretsManager::ResourcePolicy", + "aws:cdk:cloudformation:props": { + "resourcePolicy": { + "Statement": [ + { + "Action": "secretsmanager:DeleteSecret", + "Effect": "Deny", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "secretId": { + "Ref": "MySecret8FE80B51" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" } } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_secretsmanager.Secret", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "MasterSecretImported": { + "id": "MasterSecretImported", + "path": "cdk-integ-secret-hosted-rotation/MasterSecretImported", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" } }, "BootstrapVersion": { "id": "BootstrapVersion", "path": "cdk-integ-secret-hosted-rotation/BootstrapVersion", "constructInfo": { - "fqn": "aws-cdk-lib.CfnParameter", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.3.0" } }, "CheckBootstrapVersion": { "id": "CheckBootstrapVersion", "path": "cdk-integ-secret-hosted-rotation/CheckBootstrapVersion", "constructInfo": { - "fqn": "aws-cdk-lib.CfnRule", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.3.0" } } }, "constructInfo": { - "fqn": "aws-cdk-lib.Stack", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.3.0" } }, "Tree": { @@ -260,13 +399,13 @@ "path": "Tree", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.2.26" + "version": "10.3.0" } } }, "constructInfo": { - "fqn": "aws-cdk-lib.App", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.3.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 0812c00e2bd0f..f49fa56b913a5 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 @@ -19,6 +19,14 @@ class TestStack extends cdk.Stack { hostedRotation: secretsmanager.HostedRotation.mysqlSingleUser(), rotateImmediatelyOnUpdate: false, }); + + const mySecret = new secretsmanager.Secret(this, 'MySecret'); + const importedSecret = secretsmanager.Secret.fromSecretNameV2(this, 'MasterSecretImported', 'MasterSecret'); + mySecret.addRotationSchedule('RotationSchedule', { + hostedRotation: secretsmanager.HostedRotation.postgreSqlMultiUser({ + masterSecret: importedSecret, + }), + }); } } 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 32f89b50f8d9e..aa8ae986f407b 100644 --- a/packages/aws-cdk-lib/aws-secretsmanager/lib/rotation-schedule.ts +++ b/packages/aws-cdk-lib/aws-secretsmanager/lib/rotation-schedule.ts @@ -325,6 +325,13 @@ export class HostedRotation implements ec2.IConnectable { this.masterSecret.denyAccountRootDelete(); } + let masterSecretArn: string | undefined; + if (this.masterSecret?.secretFullArn) { + masterSecretArn = this.masterSecret.secretArn; + } else if (this.masterSecret) { // ISecret as an imported secret with partial ARN + masterSecretArn = this.masterSecret.secretArn + '-??????'; + } + const defaultExcludeCharacters = Secret.isSecret(secret) ? secret.excludeCharacters ?? DEFAULT_PASSWORD_EXCLUDE_CHARS : DEFAULT_PASSWORD_EXCLUDE_CHARS; @@ -332,7 +339,7 @@ export class HostedRotation implements ec2.IConnectable { return { rotationType: this.type.name, kmsKeyArn: secret.encryptionKey?.keyArn, - masterSecretArn: this.masterSecret?.secretArn, + masterSecretArn: masterSecretArn, masterSecretKmsKeyArn: this.masterSecret?.encryptionKey?.keyArn, rotationLambdaName: this.props.functionName, vpcSecurityGroupIds: this._connections?.securityGroups?.map(s => s.securityGroupId).join(','), 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 dacfe106e0f8d..0a319267cc34a 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 @@ -589,6 +589,65 @@ describe('hosted rotation', () => { }, }); }); + + test('the arn is used as it is when specifying masterSecret as an imported secret with full arn', () => { + // GIVEN + const secret = new secretsmanager.Secret(stack, 'Secret'); + const importedSecret = secretsmanager.Secret.fromSecretCompleteArn(stack, 'MasterSecretImported', 'arn:aws:secretsmanager:us-east-1:123456789012:secret:MySecret-123456'); + + // WHEN + secret.addRotationSchedule('RotationSchedule', { + hostedRotation: secretsmanager.HostedRotation.postgreSqlMultiUser({ + masterSecret: importedSecret, + }), + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::SecretsManager::RotationSchedule', { + SecretId: { + Ref: 'SecretA720EF05', + }, + HostedRotationLambda: { + MasterSecretArn: 'arn:aws:secretsmanager:us-east-1:123456789012:secret:MySecret-123456', + }, + }); + }); + + test('the arn is used with -?????? when specifying masterSecret as an imported secret with partial arn', () => { + // GIVEN + const secret = new secretsmanager.Secret(stack, 'Secret'); + const importedSecret = secretsmanager.Secret.fromSecretNameV2(stack, 'MasterSecretImported', 'MySecret'); + + // WHEN + secret.addRotationSchedule('RotationSchedule', { + hostedRotation: secretsmanager.HostedRotation.postgreSqlMultiUser({ + masterSecret: importedSecret, + }), + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::SecretsManager::RotationSchedule', { + SecretId: { + Ref: 'SecretA720EF05', + }, + HostedRotationLambda: { + MasterSecretArn: { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':secretsmanager:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':secret:MySecret-??????', + ], + ], + }, + }, + }); + }); }); describe('manual rotations', () => {