From 5061a8d9c59bc7380290de93aa13e4d6e8119932 Mon Sep 17 00:00:00 2001 From: Meng Xin Zhu Date: Wed, 16 Jun 2021 22:11:05 +0800 Subject: [PATCH] fix(secretsmanager): support secrets rotation in partition 'aws-cn' (#14608) closes #13385 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .gitallowed | 7 +++ .../@aws-cdk/aws-docdb/test/cluster.test.ts | 8 +-- .../integ.cluster-rotation.lit.expected.json | 50 ++++++++++++---- .../integ.cluster-rotation.lit.expected.json | 50 ++++++++++++---- .../test/integ.instance.lit.expected.json | 32 +++++++++- .../aws-secretsmanager/lib/secret-rotation.ts | 58 ++++++++++++++++++- .../test/secret-rotation.test.ts | 8 ++- 7 files changed, 181 insertions(+), 32 deletions(-) diff --git a/.gitallowed b/.gitallowed index 2fa8726e1171d..43827f7ad99b7 100644 --- a/.gitallowed +++ b/.gitallowed @@ -23,3 +23,10 @@ account: '856666278305' account: '840364872350' account: '422531588944' account: '924023996002' + +# The account IDs of password rotation applications of Serverless Application Repository +# https://docs.aws.amazon.com/secretsmanager/latest/userguide/enable-rotation-rds.html +# partition aws +account: '297356227824' +# partition aws-cn +account: '193023089310' diff --git a/packages/@aws-cdk/aws-docdb/test/cluster.test.ts b/packages/@aws-cdk/aws-docdb/test/cluster.test.ts index 1dd057fa29653..beb564ed738e5 100644 --- a/packages/@aws-cdk/aws-docdb/test/cluster.test.ts +++ b/packages/@aws-cdk/aws-docdb/test/cluster.test.ts @@ -541,8 +541,8 @@ describe('DatabaseCluster', () => { // THEN expectCDK(stack).to(haveResource('AWS::Serverless::Application', { Location: { - ApplicationId: 'arn:aws:serverlessrepo:us-east-1:297356227824:applications/SecretsManagerMongoDBRotationSingleUser', - SemanticVersion: '1.1.60', + ApplicationId: { 'Fn::FindInMap': ['DatabaseRotationSingleUserSARMapping9AEB3E55', { Ref: 'AWS::Partition' }, 'applicationId'] }, + SemanticVersion: { 'Fn::FindInMap': ['DatabaseRotationSingleUserSARMapping9AEB3E55', { Ref: 'AWS::Partition' }, 'semanticVersion'] }, }, Parameters: { endpoint: { @@ -653,8 +653,8 @@ describe('DatabaseCluster', () => { // THEN expectCDK(stack).to(haveResource('AWS::Serverless::Application', { Location: { - ApplicationId: 'arn:aws:serverlessrepo:us-east-1:297356227824:applications/SecretsManagerMongoDBRotationMultiUser', - SemanticVersion: '1.1.60', + ApplicationId: { 'Fn::FindInMap': ['DatabaseRotationSARMappingE46CFA92', { Ref: 'AWS::Partition' }, 'applicationId'] }, + SemanticVersion: { 'Fn::FindInMap': ['DatabaseRotationSARMappingE46CFA92', { Ref: 'AWS::Partition' }, 'semanticVersion'] }, }, Parameters: { endpoint: { diff --git a/packages/@aws-cdk/aws-docdb/test/integ.cluster-rotation.lit.expected.json b/packages/@aws-cdk/aws-docdb/test/integ.cluster-rotation.lit.expected.json index 13a549e5063de..6187414b9eb75 100644 --- a/packages/@aws-cdk/aws-docdb/test/integ.cluster-rotation.lit.expected.json +++ b/packages/@aws-cdk/aws-docdb/test/integ.cluster-rotation.lit.expected.json @@ -96,15 +96,15 @@ "VPCPublicSubnet1NATGatewayE0556630": { "Type": "AWS::EC2::NatGateway", "Properties": { + "SubnetId": { + "Ref": "VPCPublicSubnet1SubnetB4246D30" + }, "AllocationId": { "Fn::GetAtt": [ "VPCPublicSubnet1EIP6AD938E8", "AllocationId" ] }, - "SubnetId": { - "Ref": "VPCPublicSubnet1SubnetB4246D30" - }, "Tags": [ { "Key": "Name", @@ -193,15 +193,15 @@ "VPCPublicSubnet2NATGateway3C070193": { "Type": "AWS::EC2::NatGateway", "Properties": { + "SubnetId": { + "Ref": "VPCPublicSubnet2Subnet74179F39" + }, "AllocationId": { "Fn::GetAtt": [ "VPCPublicSubnet2EIP4947BC00", "AllocationId" ] }, - "SubnetId": { - "Ref": "VPCPublicSubnet2Subnet74179F39" - }, "Tags": [ { "Key": "Name", @@ -290,15 +290,15 @@ "VPCPublicSubnet3NATGatewayD3048F5C": { "Type": "AWS::EC2::NatGateway", "Properties": { + "SubnetId": { + "Ref": "VPCPublicSubnet3Subnet631C5E25" + }, "AllocationId": { "Fn::GetAtt": [ "VPCPublicSubnet3EIPAD4BC883", "AllocationId" ] }, - "SubnetId": { - "Ref": "VPCPublicSubnet3Subnet631C5E25" - }, "Tags": [ { "Key": "Name", @@ -747,8 +747,24 @@ "Type": "AWS::Serverless::Application", "Properties": { "Location": { - "ApplicationId": "arn:aws:serverlessrepo:us-east-1:297356227824:applications/SecretsManagerMongoDBRotationSingleUser", - "SemanticVersion": "1.1.3" + "ApplicationId": { + "Fn::FindInMap": [ + "DatabaseRotationSingleUserSARMapping9AEB3E55", + { + "Ref": "AWS::Partition" + }, + "applicationId" + ] + }, + "SemanticVersion": { + "Fn::FindInMap": [ + "DatabaseRotationSingleUserSARMapping9AEB3E55", + { + "Ref": "AWS::Partition" + }, + "semanticVersion" + ] + } }, "Parameters": { "endpoint": { @@ -794,5 +810,17 @@ } } } + }, + "Mappings": { + "DatabaseRotationSingleUserSARMapping9AEB3E55": { + "aws": { + "applicationId": "arn:aws:serverlessrepo:us-east-1:297356227824:applications/SecretsManagerMongoDBRotationSingleUser", + "semanticVersion": "1.1.60" + }, + "aws-cn": { + "applicationId": "arn:aws-cn:serverlessrepo:cn-north-1:193023089310:applications/SecretsManagerMongoDBRotationSingleUser", + "semanticVersion": "1.1.37" + } + } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-rds/test/integ.cluster-rotation.lit.expected.json b/packages/@aws-cdk/aws-rds/test/integ.cluster-rotation.lit.expected.json index 74a0642b875c8..6d8c335221156 100644 --- a/packages/@aws-cdk/aws-rds/test/integ.cluster-rotation.lit.expected.json +++ b/packages/@aws-cdk/aws-rds/test/integ.cluster-rotation.lit.expected.json @@ -96,15 +96,15 @@ "VPCPublicSubnet1NATGatewayE0556630": { "Type": "AWS::EC2::NatGateway", "Properties": { + "SubnetId": { + "Ref": "VPCPublicSubnet1SubnetB4246D30" + }, "AllocationId": { "Fn::GetAtt": [ "VPCPublicSubnet1EIP6AD938E8", "AllocationId" ] }, - "SubnetId": { - "Ref": "VPCPublicSubnet1SubnetB4246D30" - }, "Tags": [ { "Key": "Name", @@ -193,15 +193,15 @@ "VPCPublicSubnet2NATGateway3C070193": { "Type": "AWS::EC2::NatGateway", "Properties": { + "SubnetId": { + "Ref": "VPCPublicSubnet2Subnet74179F39" + }, "AllocationId": { "Fn::GetAtt": [ "VPCPublicSubnet2EIP4947BC00", "AllocationId" ] }, - "SubnetId": { - "Ref": "VPCPublicSubnet2Subnet74179F39" - }, "Tags": [ { "Key": "Name", @@ -290,15 +290,15 @@ "VPCPublicSubnet3NATGatewayD3048F5C": { "Type": "AWS::EC2::NatGateway", "Properties": { + "SubnetId": { + "Ref": "VPCPublicSubnet3Subnet631C5E25" + }, "AllocationId": { "Fn::GetAtt": [ "VPCPublicSubnet3EIPAD4BC883", "AllocationId" ] }, - "SubnetId": { - "Ref": "VPCPublicSubnet3Subnet631C5E25" - }, "Tags": [ { "Key": "Name", @@ -769,8 +769,24 @@ "Type": "AWS::Serverless::Application", "Properties": { "Location": { - "ApplicationId": "arn:aws:serverlessrepo:us-east-1:297356227824:applications/SecretsManagerRDSMySQLRotationSingleUser", - "SemanticVersion": "1.1.60" + "ApplicationId": { + "Fn::FindInMap": [ + "DatabaseRotationSingleUserSARMapping9AEB3E55", + { + "Ref": "AWS::Partition" + }, + "applicationId" + ] + }, + "SemanticVersion": { + "Fn::FindInMap": [ + "DatabaseRotationSingleUserSARMapping9AEB3E55", + { + "Ref": "AWS::Partition" + }, + "semanticVersion" + ] + } }, "Parameters": { "endpoint": { @@ -817,5 +833,17 @@ } } } + }, + "Mappings": { + "DatabaseRotationSingleUserSARMapping9AEB3E55": { + "aws": { + "applicationId": "arn:aws:serverlessrepo:us-east-1:297356227824:applications/SecretsManagerRDSMySQLRotationSingleUser", + "semanticVersion": "1.1.60" + }, + "aws-cn": { + "applicationId": "arn:aws-cn:serverlessrepo:cn-north-1:193023089310:applications/SecretsManagerRDSMySQLRotationSingleUser", + "semanticVersion": "1.1.37" + } + } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-rds/test/integ.instance.lit.expected.json b/packages/@aws-cdk/aws-rds/test/integ.instance.lit.expected.json index 0de11e4fd5c4f..421ad34dab71e 100644 --- a/packages/@aws-cdk/aws-rds/test/integ.instance.lit.expected.json +++ b/packages/@aws-cdk/aws-rds/test/integ.instance.lit.expected.json @@ -813,8 +813,24 @@ "Type": "AWS::Serverless::Application", "Properties": { "Location": { - "ApplicationId": "arn:aws:serverlessrepo:us-east-1:297356227824:applications/SecretsManagerRDSOracleRotationSingleUser", - "SemanticVersion": "1.1.60" + "ApplicationId": { + "Fn::FindInMap": [ + "InstanceRotationSingleUserSARMappingFEA0C86E", + { + "Ref": "AWS::Partition" + }, + "applicationId" + ] + }, + "SemanticVersion": { + "Fn::FindInMap": [ + "InstanceRotationSingleUserSARMappingFEA0C86E", + { + "Ref": "AWS::Partition" + }, + "semanticVersion" + ] + } }, "Parameters": { "endpoint": { @@ -1122,5 +1138,17 @@ "Type": "String", "Description": "Artifact hash for asset \"884431e2bc651d2b61bd699a29dc9684b0f66911f06bd3ed0635f854bf18e147\"" } + }, + "Mappings": { + "InstanceRotationSingleUserSARMappingFEA0C86E": { + "aws": { + "applicationId": "arn:aws:serverlessrepo:us-east-1:297356227824:applications/SecretsManagerRDSOracleRotationSingleUser", + "semanticVersion": "1.1.60" + }, + "aws-cn": { + "applicationId": "arn:aws-cn:serverlessrepo:cn-north-1:193023089310:applications/SecretsManagerRDSOracleRotationSingleUser", + "semanticVersion": "1.1.37" + } + } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-secretsmanager/lib/secret-rotation.ts b/packages/@aws-cdk/aws-secretsmanager/lib/secret-rotation.ts index 0d0ed6e75c348..fb52821c88729 100644 --- a/packages/@aws-cdk/aws-secretsmanager/lib/secret-rotation.ts +++ b/packages/@aws-cdk/aws-secretsmanager/lib/secret-rotation.ts @@ -1,7 +1,7 @@ import * as ec2 from '@aws-cdk/aws-ec2'; import * as lambda from '@aws-cdk/aws-lambda'; import * as serverless from '@aws-cdk/aws-sam'; -import { Duration, Names, Stack, Token } from '@aws-cdk/core'; +import { Duration, Names, Stack, Token, CfnMapping, Aws } from '@aws-cdk/core'; import { Construct } from 'constructs'; import { ISecret } from './secret'; @@ -20,6 +20,7 @@ export interface SecretRotationApplicationOptions { */ readonly isMultiUser?: boolean; } + /** * A secret rotation serverless application. */ @@ -110,11 +111,15 @@ export class SecretRotationApplication { /** * The application identifier of the rotation application + * + * @deprecated only valid when deploying to the 'aws' partition. Use `applicationArnForPartition` instead. */ public readonly applicationId: string; /** * The semantic version of the rotation application + * + * @deprecated only valid when deploying to the 'aws' partition. Use `semanticVersionForPartition` instead. */ public readonly semanticVersion: string; @@ -123,11 +128,45 @@ export class SecretRotationApplication { */ public readonly isMultiUser?: boolean; + /** + * The application name of the rotation application + */ + private readonly applicationName: string; + constructor(applicationId: string, semanticVersion: string, options?: SecretRotationApplicationOptions) { this.applicationId = `arn:aws:serverlessrepo:us-east-1:297356227824:applications/${applicationId}`; this.semanticVersion = semanticVersion; + this.applicationName = applicationId; this.isMultiUser = options && options.isMultiUser; } + + /** + * Returns the application ARN for the current partition. + * Can be used in combination with a `CfnMapping` to automatically select the correct ARN based on the current partition. + */ + public applicationArnForPartition(partition: string) { + if (partition === 'aws') { + return this.applicationId; + } else if (partition === 'aws-cn') { + return `arn:aws-cn:serverlessrepo:cn-north-1:193023089310:applications/${this.applicationName}`; + } else { + throw new Error(`unsupported partition: ${partition}`); + } + } + + /** + * The semantic version of the app for the current partition. + * Can be used in combination with a `CfnMapping` to automatically select the correct version based on the current partition. + */ + public semanticVersionForPartition(partition: string) { + if (partition === 'aws') { + return this.semanticVersion; + } else if (partition === 'aws-cn') { + return '1.1.37'; + } else { + throw new Error(`unsupported partition: ${partition}`); + } + } } /** @@ -255,8 +294,23 @@ export class SecretRotation extends CoreConstruct { } } + const sarMapping = new CfnMapping(this, 'SARMapping', { + mapping: { + 'aws': { + applicationId: props.application.applicationArnForPartition('aws'), + semanticVersion: props.application.semanticVersionForPartition('aws'), + }, + 'aws-cn': { + applicationId: props.application.applicationArnForPartition('aws-cn'), + semanticVersion: props.application.semanticVersionForPartition('aws-cn'), + }, + }, + }); const application = new serverless.CfnApplication(this, 'Resource', { - location: props.application, + location: { + applicationId: sarMapping.findInMap(Aws.PARTITION, 'applicationId'), + semanticVersion: sarMapping.findInMap(Aws.PARTITION, 'semanticVersion'), + }, parameters, }); diff --git a/packages/@aws-cdk/aws-secretsmanager/test/secret-rotation.test.ts b/packages/@aws-cdk/aws-secretsmanager/test/secret-rotation.test.ts index e6e4943c2c74a..55c7388393619 100644 --- a/packages/@aws-cdk/aws-secretsmanager/test/secret-rotation.test.ts +++ b/packages/@aws-cdk/aws-secretsmanager/test/secret-rotation.test.ts @@ -74,8 +74,12 @@ test('secret rotation single user', () => { expect(stack).toHaveResource('AWS::Serverless::Application', { Location: { - ApplicationId: 'arn:aws:serverlessrepo:us-east-1:297356227824:applications/SecretsManagerRDSMySQLRotationSingleUser', - SemanticVersion: '1.1.60', + ApplicationId: { + 'Fn::FindInMap': ['SecretRotationSARMappingC10A2F5D', { Ref: 'AWS::Partition' }, 'applicationId'], + }, + SemanticVersion: { + 'Fn::FindInMap': ['SecretRotationSARMappingC10A2F5D', { Ref: 'AWS::Partition' }, 'semanticVersion'], + }, }, Parameters: { endpoint: {