AWSTemplateFormatVersion: "2010-09-09" Description: Provides RDS configuration. Parameters: AvailabilityZones: Type: List VpcID: Type: AWS::EC2::VPC::Id PrivateSubnet1: Type: AWS::EC2::Subnet::Id PrivateSubnet2: Type: AWS::EC2::Subnet::Id DBReaderInstanceClass: Type: String DBWriterInstanceClass: Type: String PerformanceInsightsRetentionPeriod: Type: Number CreateRDSProxy: Type: String LayerVersionArn: Type: String Conditions: IsRDSProxy: !Equals [!Ref CreateRDSProxy, true] Transform: AWS::Serverless-2016-10-31 Resources: DBCluster: Type: AWS::RDS::DBCluster Properties: AvailabilityZones: - !Select [0, !Ref AvailabilityZones] - !Select [1, !Ref AvailabilityZones] DatabaseName: main DBSubnetGroupName: !Ref DBSubnetGroup StorageEncrypted: true EnableIAMDatabaseAuthentication: !If - IsRDSProxy - false - true Engine: aurora-mysql MasterUsername: !Join - "" - - '{{resolve:secretsmanager:' - !Ref DBSecret - :SecretString:username::}} MasterUserPassword: !Join - "" - - '{{resolve:secretsmanager:' - !Ref DBSecret - :SecretString:password::}} VpcSecurityGroupIds: - !Ref RDSSecurityGroup DBSubnetGroup: Type: AWS::RDS::DBSubnetGroup Properties: DBSubnetGroupDescription: CloudFormation Sample Aurora Subnet Group SubnetIds: - !Ref PrivateSubnet1 - !Ref PrivateSubnet2 DBWriterInstance: Type: AWS::RDS::DBInstance Properties: DBInstanceClass: !Ref DBWriterInstanceClass DBClusterIdentifier: !Ref DBCluster Engine: aurora-mysql PubliclyAccessible: false DBReaderInstance1: Type: AWS::RDS::DBInstance DependsOn: - DBWriterInstance Properties: DBInstanceClass: !Ref DBReaderInstanceClass EnablePerformanceInsights: true PerformanceInsightsRetentionPeriod: !Ref PerformanceInsightsRetentionPeriod DBClusterIdentifier: !Ref DBCluster Engine: aurora-mysql PubliclyAccessible: false DBReaderInstance2: Type: AWS::RDS::DBInstance DependsOn: - DBWriterInstance Properties: DBInstanceClass: !Ref DBReaderInstanceClass EnablePerformanceInsights: true PerformanceInsightsRetentionPeriod: !Ref PerformanceInsightsRetentionPeriod DBClusterIdentifier: !Ref DBCluster Engine: aurora-mysql PubliclyAccessible: false DBSecret: Type: AWS::SecretsManager::Secret Properties: KmsKeyId: alias/aws/secretsmanager GenerateSecretString: ExcludePunctuation: true GenerateStringKey: password SecretStringTemplate: '{"username": "admin"}' RDSSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: RDS Security Group SecurityGroupEgress: - CidrIp: 127.0.0.1/32 Description: Limits security group egress traffic to localhost IpProtocol: "-1" VpcId: !Ref VpcID DBProxy: Type: AWS::RDS::DBProxy DependsOn: - SecretsManagerSeviceAccessPolicy Properties: Auth: - AuthScheme: SECRETS IAMAuth: REQUIRED SecretArn: !Ref DBSecret DBProxyName: !Sub proxy-${AWS::Partition}-${AWS::AccountId}-${AWS::Region}-98765abc EngineFamily: MYSQL RoleArn: !GetAtt DBSecretsRole.Arn VpcSubnetIds: - !Ref PrivateSubnet1 - !Ref PrivateSubnet2 RequireTLS: true VpcSecurityGroupIds: - !Ref ProxySecurityGroup Condition: IsRDSProxy ProxyReaderEndpoint: Type: AWS::RDS::DBProxyEndpoint Properties: DBProxyEndpointName: proxy-reader-endpoint DBProxyName: !Ref DBProxy VpcSubnetIds: - !Ref PrivateSubnet1 - !Ref PrivateSubnet2 TargetRole: READ_ONLY VpcSecurityGroupIds: - !Ref ProxySecurityGroup Condition: IsRDSProxy ProxySecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: RDS Proxy Security Group SecurityGroupEgress: - DestinationSecurityGroupId: !Ref RDSSecurityGroup Description: Allow db outbound traffic IpProtocol: tcp FromPort: 3306 ToPort: 3306 VpcId: !Ref VpcID Condition: IsRDSProxy ProxyToRDSIngress: Type: AWS::EC2::SecurityGroupIngress Properties: IpProtocol: tcp Description: Proxy to RDS ingress rule FromPort: 3306 GroupId: !Ref RDSSecurityGroup SourceSecurityGroupId: !Ref ProxySecurityGroup ToPort: 3306 Condition: IsRDSProxy DBSecretsRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Statement: - Action: sts:AssumeRole Effect: Allow Principal: Service: rds.amazonaws.com Condition: IsRDSProxy SecretsManagerSeviceAccessPolicy: Type: AWS::IAM::ManagedPolicy Properties: Description: !Sub Allows RDS Proxy to retrieve a secret from Secrets Manager with the correct ARN. Stack ${AWS::StackName} PolicyDocument: Version: "2012-10-17" Statement: - Sid: GetSecretValue Effect: Allow Action: - secretsmanager:GetSecretValue Resource: - !Ref DBSecret - !Sub arn:${AWS::Partition}:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:Amazon_rds_proxy_multitenant_load_test/Proxy_secret_for_user* Roles: - !Ref DBSecretsRole Condition: IsRDSProxy DBProxyTargetGroup: Type: AWS::RDS::DBProxyTargetGroup DependsOn: - DBWriterInstance - DBReaderInstance1 - DBReaderInstance2 Properties: DBProxyName: !Ref DBProxy TargetGroupName: default ConnectionPoolConfigurationInfo: ConnectionBorrowTimeout: 120 MaxConnectionsPercent: 90 MaxIdleConnectionsPercent: 50 DBClusterIdentifiers: - !Ref DBCluster Condition: IsRDSProxy DbClusterResourceIdFunction: Type: AWS::Serverless::Function DependsOn: - DescribeClustersPolicy Properties: Description: Get DB cluster id. Handler: cluster.handler Runtime: python3.8 CodeUri: ../src/functions/rds Layers: - !Ref LayerVersionArn Policies: - !Ref DescribeClustersPolicy DescribeClustersPolicy: Type: AWS::IAM::ManagedPolicy Properties: Description: !Sub Allows the DescribeDBClusters permission for the Lambda function that retrieves the DB Cluster ID. Stack ${AWS::StackName} PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - rds:DescribeDBClusters Resource: !Sub arn:${AWS::Partition}:rds:${AWS::Region}:${AWS::AccountId}:cluster:${DBCluster} DbClusterResourceIdCustom: Type: Custom::DbClusterResourceId Properties: ServiceToken: !GetAtt DbClusterResourceIdFunction.Arn DBClusterIdentifier: !Ref DBCluster Version: "1.0" Outputs: RDSSecurityGroup: Value: !Ref RDSSecurityGroup ProxySecurityGroup: Value: !Ref ProxySecurityGroup Condition: IsRDSProxy ClusterEndpoint: Value: !GetAtt DBCluster.Endpoint.Address ClusterReaderEndpoint: Value: !GetAtt DBCluster.ReadEndpoint.Address ProxyEndpoint: Value: !GetAtt ProxyReaderEndpoint.Endpoint Condition: IsRDSProxy DBSecret: Value: !Ref DBSecret DBProxyName: Value: !Ref DBProxy Condition: IsRDSProxy DBProxyArn: Value: !GetAtt DBProxy.DBProxyArn Condition: IsRDSProxy AuroraReplicaName1: Value: !Ref DBReaderInstance1 AuroraReplicaName2: Value: !Ref DBReaderInstance2 DBClusterName: Value: !Ref DBCluster ClusterEndpointResourceId: Value: !If - IsRDSProxy - !Select - 6 - !Split - ':' - !GetAtt DBProxy.DBProxyArn - !GetAtt DbClusterResourceIdCustom.DbClusterResourceId