From 00e8a7b877a732b4236227f6462bf3914ce7a214 Mon Sep 17 00:00:00 2001 From: Hirotaka Tagawa / wafuwafu13 <jaruwafu@gmail.com> Date: Wed, 13 Mar 2024 01:40:58 +0000 Subject: [PATCH 1/4] feat(ecs-patterns): support `securityGroups` in `NetworkLoadBalancedFargateService` (#29431) ### Issue # (if applicable) Closes #29430 (related to https://github.com/aws/aws-cdk/pull/29186#issuecomment-1959231406) ### Reason for this change We can't specify `securityGroups` in `NetworkLoadBalancedFargateService` now. ### Description of changes - Add `securityGroups` property to `NetworkLoadBalancedFargateServiceProps`. - Add unit test - Add integ test ### Description of how you validated changes - Pass unit test - Pass integ test ### Checklist - [x] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../aws-ecs-integ-lb-fargate.assets.json | 19 + .../aws-ecs-integ-lb-fargate.template.json | 750 ++++++++++ .../fargate/integ.nlb-sg.js.snapshot/cdk.out | 1 + .../integ.nlb-sg.js.snapshot/integ.json | 12 + ...efaultTestDeployAssert3391A20B.assets.json | 19 + ...aultTestDeployAssert3391A20B.template.json | 36 + .../integ.nlb-sg.js.snapshot/manifest.json | 323 ++++ .../integ.nlb-sg.js.snapshot/tree.json | 1304 +++++++++++++++++ .../test/fargate/integ.nlb-sg.ts | 40 + .../aws-cdk-lib/aws-ecs-patterns/README.md | 17 +- .../network-load-balanced-fargate-service.ts | 9 +- .../load-balanced-fargate-service.test.ts | 44 + 12 files changed, 2572 insertions(+), 2 deletions(-) create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-ecs-patterns/test/fargate/integ.nlb-sg.js.snapshot/aws-ecs-integ-lb-fargate.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-ecs-patterns/test/fargate/integ.nlb-sg.js.snapshot/aws-ecs-integ-lb-fargate.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-ecs-patterns/test/fargate/integ.nlb-sg.js.snapshot/cdk.out create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-ecs-patterns/test/fargate/integ.nlb-sg.js.snapshot/integ.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-ecs-patterns/test/fargate/integ.nlb-sg.js.snapshot/l3FargateTestDefaultTestDeployAssert3391A20B.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-ecs-patterns/test/fargate/integ.nlb-sg.js.snapshot/l3FargateTestDefaultTestDeployAssert3391A20B.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-ecs-patterns/test/fargate/integ.nlb-sg.js.snapshot/manifest.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-ecs-patterns/test/fargate/integ.nlb-sg.js.snapshot/tree.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-ecs-patterns/test/fargate/integ.nlb-sg.ts diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecs-patterns/test/fargate/integ.nlb-sg.js.snapshot/aws-ecs-integ-lb-fargate.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs-patterns/test/fargate/integ.nlb-sg.js.snapshot/aws-ecs-integ-lb-fargate.assets.json new file mode 100644 index 0000000000000..17197614dcd47 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs-patterns/test/fargate/integ.nlb-sg.js.snapshot/aws-ecs-integ-lb-fargate.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "01677902a566d4c905bae44eaf146aefbe867b471036ab56d5abe5f9622c0ccd": { + "source": { + "path": "aws-ecs-integ-lb-fargate.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "01677902a566d4c905bae44eaf146aefbe867b471036ab56d5abe5f9622c0ccd.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecs-patterns/test/fargate/integ.nlb-sg.js.snapshot/aws-ecs-integ-lb-fargate.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs-patterns/test/fargate/integ.nlb-sg.js.snapshot/aws-ecs-integ-lb-fargate.template.json new file mode 100644 index 0000000000000..69b2823074894 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs-patterns/test/fargate/integ.nlb-sg.js.snapshot/aws-ecs-integ-lb-fargate.template.json @@ -0,0 +1,750 @@ +{ + "Resources": { + "Vpc8378EB38": { + "Type": "AWS::EC2::VPC", + "Properties": { + "CidrBlock": "10.0.0.0/16", + "EnableDnsHostnames": true, + "EnableDnsSupport": true, + "InstanceTenancy": "default", + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-lb-fargate/Vpc" + } + ] + } + }, + "VpcPublicSubnet1Subnet5C2D37C4": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "AvailabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "" + } + ] + }, + "CidrBlock": "10.0.0.0/18", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "Name", + "Value": "aws-ecs-integ-lb-fargate/Vpc/PublicSubnet1" + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "VpcPublicSubnet1RouteTable6C95E38E": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-lb-fargate/Vpc/PublicSubnet1" + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "VpcPublicSubnet1RouteTableAssociation97140677": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet1RouteTable6C95E38E" + }, + "SubnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + } + } + }, + "VpcPublicSubnet1DefaultRoute3DA9E72A": { + "Type": "AWS::EC2::Route", + "Properties": { + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VpcIGWD7BA715C" + }, + "RouteTableId": { + "Ref": "VpcPublicSubnet1RouteTable6C95E38E" + } + }, + "DependsOn": [ + "VpcVPCGWBF912B6E" + ] + }, + "VpcPublicSubnet1EIPD7E02669": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc", + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-lb-fargate/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPublicSubnet1NATGateway4D7517AA": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "AllocationId": { + "Fn::GetAtt": [ + "VpcPublicSubnet1EIPD7E02669", + "AllocationId" + ] + }, + "SubnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-lb-fargate/Vpc/PublicSubnet1" + } + ] + }, + "DependsOn": [ + "VpcPublicSubnet1DefaultRoute3DA9E72A", + "VpcPublicSubnet1RouteTableAssociation97140677" + ] + }, + "VpcPublicSubnet2Subnet691E08A3": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "AvailabilityZone": { + "Fn::Select": [ + 1, + { + "Fn::GetAZs": "" + } + ] + }, + "CidrBlock": "10.0.64.0/18", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "Name", + "Value": "aws-ecs-integ-lb-fargate/Vpc/PublicSubnet2" + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "VpcPublicSubnet2RouteTable94F7E489": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-lb-fargate/Vpc/PublicSubnet2" + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "VpcPublicSubnet2RouteTableAssociationDD5762D8": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet2RouteTable94F7E489" + }, + "SubnetId": { + "Ref": "VpcPublicSubnet2Subnet691E08A3" + } + } + }, + "VpcPublicSubnet2DefaultRoute97F91067": { + "Type": "AWS::EC2::Route", + "Properties": { + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VpcIGWD7BA715C" + }, + "RouteTableId": { + "Ref": "VpcPublicSubnet2RouteTable94F7E489" + } + }, + "DependsOn": [ + "VpcVPCGWBF912B6E" + ] + }, + "VpcPublicSubnet2EIP3C605A87": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc", + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-lb-fargate/Vpc/PublicSubnet2" + } + ] + } + }, + "VpcPublicSubnet2NATGateway9182C01D": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "AllocationId": { + "Fn::GetAtt": [ + "VpcPublicSubnet2EIP3C605A87", + "AllocationId" + ] + }, + "SubnetId": { + "Ref": "VpcPublicSubnet2Subnet691E08A3" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-lb-fargate/Vpc/PublicSubnet2" + } + ] + }, + "DependsOn": [ + "VpcPublicSubnet2DefaultRoute97F91067", + "VpcPublicSubnet2RouteTableAssociationDD5762D8" + ] + }, + "VpcPrivateSubnet1Subnet536B997A": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "AvailabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "" + } + ] + }, + "CidrBlock": "10.0.128.0/18", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + }, + { + "Key": "Name", + "Value": "aws-ecs-integ-lb-fargate/Vpc/PrivateSubnet1" + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "VpcPrivateSubnet1RouteTableB2C5B500": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-lb-fargate/Vpc/PrivateSubnet1" + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "VpcPrivateSubnet1RouteTableAssociation70C59FA6": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet1RouteTableB2C5B500" + }, + "SubnetId": { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + } + } + }, + "VpcPrivateSubnet1DefaultRouteBE02A9ED": { + "Type": "AWS::EC2::Route", + "Properties": { + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "VpcPublicSubnet1NATGateway4D7517AA" + }, + "RouteTableId": { + "Ref": "VpcPrivateSubnet1RouteTableB2C5B500" + } + } + }, + "VpcPrivateSubnet2Subnet3788AAA1": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "AvailabilityZone": { + "Fn::Select": [ + 1, + { + "Fn::GetAZs": "" + } + ] + }, + "CidrBlock": "10.0.192.0/18", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + }, + { + "Key": "Name", + "Value": "aws-ecs-integ-lb-fargate/Vpc/PrivateSubnet2" + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "VpcPrivateSubnet2RouteTableA678073B": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-lb-fargate/Vpc/PrivateSubnet2" + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "VpcPrivateSubnet2RouteTableAssociationA89CAD56": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet2RouteTableA678073B" + }, + "SubnetId": { + "Ref": "VpcPrivateSubnet2Subnet3788AAA1" + } + } + }, + "VpcPrivateSubnet2DefaultRoute060D2087": { + "Type": "AWS::EC2::Route", + "Properties": { + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "VpcPublicSubnet2NATGateway9182C01D" + }, + "RouteTableId": { + "Ref": "VpcPrivateSubnet2RouteTableA678073B" + } + } + }, + "VpcIGWD7BA715C": { + "Type": "AWS::EC2::InternetGateway", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-lb-fargate/Vpc" + } + ] + } + }, + "VpcVPCGWBF912B6E": { + "Type": "AWS::EC2::VPCGatewayAttachment", + "Properties": { + "InternetGatewayId": { + "Ref": "VpcIGWD7BA715C" + }, + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "FargateCluster7CCD5F93": { + "Type": "AWS::ECS::Cluster" + }, + "SecurityGroupNlbFA132873": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "aws-ecs-integ-lb-fargate/SecurityGroupNlb", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "SecurityGroupIngress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "from 0.0.0.0/0:32768-65535", + "FromPort": 32768, + "IpProtocol": "tcp", + "ToPort": 65535 + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "SecurityGroupServiceDE1E5E6A": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "aws-ecs-integ-lb-fargate/SecurityGroupService", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "SecurityGroupIngress": [ + { + "Description": { + "Fn::Join": [ + "", + [ + "from ", + { + "Fn::GetAtt": [ + "SecurityGroupNlbFA132873", + "GroupId" + ] + }, + ":ALL PORTS" + ] + ] + }, + "FromPort": 0, + "IpProtocol": "tcp", + "SourceSecurityGroupId": { + "Fn::GetAtt": [ + "SecurityGroupNlbFA132873", + "GroupId" + ] + }, + "ToPort": 65535 + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "NLBFargateServiceLB659EC17C": { + "Type": "AWS::ElasticLoadBalancingV2::LoadBalancer", + "Properties": { + "LoadBalancerAttributes": [ + { + "Key": "deletion_protection.enabled", + "Value": "false" + } + ], + "Scheme": "internet-facing", + "SecurityGroups": [ + { + "Fn::GetAtt": [ + "SecurityGroupNlbFA132873", + "GroupId" + ] + } + ], + "Subnets": [ + { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + }, + { + "Ref": "VpcPublicSubnet2Subnet691E08A3" + } + ], + "Type": "network" + }, + "DependsOn": [ + "VpcPublicSubnet1DefaultRoute3DA9E72A", + "VpcPublicSubnet1RouteTableAssociation97140677", + "VpcPublicSubnet2DefaultRoute97F91067", + "VpcPublicSubnet2RouteTableAssociationDD5762D8" + ] + }, + "NLBFargateServiceLBPublicListenerB0DCA73C": { + "Type": "AWS::ElasticLoadBalancingV2::Listener", + "Properties": { + "DefaultActions": [ + { + "TargetGroupArn": { + "Ref": "NLBFargateServiceLBPublicListenerECSGroupC469CAA2" + }, + "Type": "forward" + } + ], + "LoadBalancerArn": { + "Ref": "NLBFargateServiceLB659EC17C" + }, + "Port": 80, + "Protocol": "TCP" + } + }, + "NLBFargateServiceLBPublicListenerECSGroupC469CAA2": { + "Type": "AWS::ElasticLoadBalancingV2::TargetGroup", + "Properties": { + "Port": 80, + "Protocol": "TCP", + "TargetType": "ip", + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "NLBFargateServiceTaskDefTaskRole6C88F40B": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "ecs-tasks.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "NLBFargateServiceTaskDefB836FA89": { + "Type": "AWS::ECS::TaskDefinition", + "Properties": { + "ContainerDefinitions": [ + { + "Essential": true, + "Image": "amazon/amazon-ecs-sample", + "LogConfiguration": { + "LogDriver": "awslogs", + "Options": { + "awslogs-group": { + "Ref": "NLBFargateServiceTaskDefwebLogGroupC4A42FE2" + }, + "awslogs-stream-prefix": "NLBFargateService", + "awslogs-region": { + "Ref": "AWS::Region" + } + } + }, + "Name": "web", + "PortMappings": [ + { + "ContainerPort": 80, + "Protocol": "tcp" + } + ] + } + ], + "Cpu": "512", + "ExecutionRoleArn": { + "Fn::GetAtt": [ + "NLBFargateServiceTaskDefExecutionRoleF6D642D5", + "Arn" + ] + }, + "Family": "awsecsinteglbfargateNLBFargateServiceTaskDef1265FF34", + "Memory": "1024", + "NetworkMode": "awsvpc", + "RequiresCompatibilities": [ + "FARGATE" + ], + "TaskRoleArn": { + "Fn::GetAtt": [ + "NLBFargateServiceTaskDefTaskRole6C88F40B", + "Arn" + ] + } + } + }, + "NLBFargateServiceTaskDefwebLogGroupC4A42FE2": { + "Type": "AWS::Logs::LogGroup", + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "NLBFargateServiceTaskDefExecutionRoleF6D642D5": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "ecs-tasks.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "NLBFargateServiceTaskDefExecutionRoleDefaultPolicy90080805": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "NLBFargateServiceTaskDefwebLogGroupC4A42FE2", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "NLBFargateServiceTaskDefExecutionRoleDefaultPolicy90080805", + "Roles": [ + { + "Ref": "NLBFargateServiceTaskDefExecutionRoleF6D642D5" + } + ] + } + }, + "NLBFargateServiceB92AC095": { + "Type": "AWS::ECS::Service", + "Properties": { + "Cluster": { + "Ref": "FargateCluster7CCD5F93" + }, + "DeploymentConfiguration": { + "Alarms": { + "AlarmNames": [], + "Enable": false, + "Rollback": false + }, + "MaximumPercent": 200, + "MinimumHealthyPercent": 50 + }, + "EnableECSManagedTags": false, + "HealthCheckGracePeriodSeconds": 60, + "LaunchType": "FARGATE", + "LoadBalancers": [ + { + "ContainerName": "web", + "ContainerPort": 80, + "TargetGroupArn": { + "Ref": "NLBFargateServiceLBPublicListenerECSGroupC469CAA2" + } + } + ], + "NetworkConfiguration": { + "AwsvpcConfiguration": { + "AssignPublicIp": "DISABLED", + "SecurityGroups": [ + { + "Fn::GetAtt": [ + "SecurityGroupServiceDE1E5E6A", + "GroupId" + ] + } + ], + "Subnets": [ + { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + }, + { + "Ref": "VpcPrivateSubnet2Subnet3788AAA1" + } + ] + } + }, + "TaskDefinition": { + "Ref": "NLBFargateServiceTaskDefB836FA89" + } + }, + "DependsOn": [ + "NLBFargateServiceLBPublicListenerECSGroupC469CAA2", + "NLBFargateServiceLBPublicListenerB0DCA73C", + "NLBFargateServiceTaskDefTaskRole6C88F40B" + ] + } + }, + "Outputs": { + "NLBFargateServiceLoadBalancerDNSC2B2922F": { + "Value": { + "Fn::GetAtt": [ + "NLBFargateServiceLB659EC17C", + "DNSName" + ] + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value<String>", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecs-patterns/test/fargate/integ.nlb-sg.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs-patterns/test/fargate/integ.nlb-sg.js.snapshot/cdk.out new file mode 100644 index 0000000000000..1f0068d32659a --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs-patterns/test/fargate/integ.nlb-sg.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"36.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecs-patterns/test/fargate/integ.nlb-sg.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs-patterns/test/fargate/integ.nlb-sg.js.snapshot/integ.json new file mode 100644 index 0000000000000..6d95f61c34e55 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs-patterns/test/fargate/integ.nlb-sg.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "36.0.0", + "testCases": { + "l3FargateTest/DefaultTest": { + "stacks": [ + "aws-ecs-integ-lb-fargate" + ], + "assertionStack": "l3FargateTest/DefaultTest/DeployAssert", + "assertionStackName": "l3FargateTestDefaultTestDeployAssert3391A20B" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecs-patterns/test/fargate/integ.nlb-sg.js.snapshot/l3FargateTestDefaultTestDeployAssert3391A20B.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs-patterns/test/fargate/integ.nlb-sg.js.snapshot/l3FargateTestDefaultTestDeployAssert3391A20B.assets.json new file mode 100644 index 0000000000000..4e6f5d15656c9 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs-patterns/test/fargate/integ.nlb-sg.js.snapshot/l3FargateTestDefaultTestDeployAssert3391A20B.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "l3FargateTestDefaultTestDeployAssert3391A20B.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecs-patterns/test/fargate/integ.nlb-sg.js.snapshot/l3FargateTestDefaultTestDeployAssert3391A20B.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs-patterns/test/fargate/integ.nlb-sg.js.snapshot/l3FargateTestDefaultTestDeployAssert3391A20B.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs-patterns/test/fargate/integ.nlb-sg.js.snapshot/l3FargateTestDefaultTestDeployAssert3391A20B.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value<String>", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecs-patterns/test/fargate/integ.nlb-sg.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs-patterns/test/fargate/integ.nlb-sg.js.snapshot/manifest.json new file mode 100644 index 0000000000000..796449a3f0f70 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs-patterns/test/fargate/integ.nlb-sg.js.snapshot/manifest.json @@ -0,0 +1,323 @@ +{ + "version": "36.0.0", + "artifacts": { + "aws-ecs-integ-lb-fargate.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "aws-ecs-integ-lb-fargate.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "aws-ecs-integ-lb-fargate": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "aws-ecs-integ-lb-fargate.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}/01677902a566d4c905bae44eaf146aefbe867b471036ab56d5abe5f9622c0ccd.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "aws-ecs-integ-lb-fargate.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "aws-ecs-integ-lb-fargate.assets" + ], + "metadata": { + "/aws-ecs-integ-lb-fargate/Vpc/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "Vpc8378EB38" + } + ], + "/aws-ecs-integ-lb-fargate/Vpc/PublicSubnet1/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet1Subnet5C2D37C4" + } + ], + "/aws-ecs-integ-lb-fargate/Vpc/PublicSubnet1/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet1RouteTable6C95E38E" + } + ], + "/aws-ecs-integ-lb-fargate/Vpc/PublicSubnet1/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet1RouteTableAssociation97140677" + } + ], + "/aws-ecs-integ-lb-fargate/Vpc/PublicSubnet1/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet1DefaultRoute3DA9E72A" + } + ], + "/aws-ecs-integ-lb-fargate/Vpc/PublicSubnet1/EIP": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet1EIPD7E02669" + } + ], + "/aws-ecs-integ-lb-fargate/Vpc/PublicSubnet1/NATGateway": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet1NATGateway4D7517AA" + } + ], + "/aws-ecs-integ-lb-fargate/Vpc/PublicSubnet2/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet2Subnet691E08A3" + } + ], + "/aws-ecs-integ-lb-fargate/Vpc/PublicSubnet2/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet2RouteTable94F7E489" + } + ], + "/aws-ecs-integ-lb-fargate/Vpc/PublicSubnet2/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet2RouteTableAssociationDD5762D8" + } + ], + "/aws-ecs-integ-lb-fargate/Vpc/PublicSubnet2/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet2DefaultRoute97F91067" + } + ], + "/aws-ecs-integ-lb-fargate/Vpc/PublicSubnet2/EIP": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet2EIP3C605A87" + } + ], + "/aws-ecs-integ-lb-fargate/Vpc/PublicSubnet2/NATGateway": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet2NATGateway9182C01D" + } + ], + "/aws-ecs-integ-lb-fargate/Vpc/PrivateSubnet1/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPrivateSubnet1Subnet536B997A" + } + ], + "/aws-ecs-integ-lb-fargate/Vpc/PrivateSubnet1/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPrivateSubnet1RouteTableB2C5B500" + } + ], + "/aws-ecs-integ-lb-fargate/Vpc/PrivateSubnet1/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPrivateSubnet1RouteTableAssociation70C59FA6" + } + ], + "/aws-ecs-integ-lb-fargate/Vpc/PrivateSubnet1/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPrivateSubnet1DefaultRouteBE02A9ED" + } + ], + "/aws-ecs-integ-lb-fargate/Vpc/PrivateSubnet2/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPrivateSubnet2Subnet3788AAA1" + } + ], + "/aws-ecs-integ-lb-fargate/Vpc/PrivateSubnet2/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPrivateSubnet2RouteTableA678073B" + } + ], + "/aws-ecs-integ-lb-fargate/Vpc/PrivateSubnet2/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPrivateSubnet2RouteTableAssociationA89CAD56" + } + ], + "/aws-ecs-integ-lb-fargate/Vpc/PrivateSubnet2/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPrivateSubnet2DefaultRoute060D2087" + } + ], + "/aws-ecs-integ-lb-fargate/Vpc/IGW": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcIGWD7BA715C" + } + ], + "/aws-ecs-integ-lb-fargate/Vpc/VPCGW": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcVPCGWBF912B6E" + } + ], + "/aws-ecs-integ-lb-fargate/FargateCluster/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "FargateCluster7CCD5F93" + } + ], + "/aws-ecs-integ-lb-fargate/SecurityGroupNlb/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "SecurityGroupNlbFA132873" + } + ], + "/aws-ecs-integ-lb-fargate/SecurityGroupService/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "SecurityGroupServiceDE1E5E6A" + } + ], + "/aws-ecs-integ-lb-fargate/NLBFargateService/LB/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "NLBFargateServiceLB659EC17C" + } + ], + "/aws-ecs-integ-lb-fargate/NLBFargateService/LB/PublicListener/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "NLBFargateServiceLBPublicListenerB0DCA73C" + } + ], + "/aws-ecs-integ-lb-fargate/NLBFargateService/LB/PublicListener/ECSGroup/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "NLBFargateServiceLBPublicListenerECSGroupC469CAA2" + } + ], + "/aws-ecs-integ-lb-fargate/NLBFargateService/LoadBalancerDNS": [ + { + "type": "aws:cdk:logicalId", + "data": "NLBFargateServiceLoadBalancerDNSC2B2922F" + } + ], + "/aws-ecs-integ-lb-fargate/NLBFargateService/TaskDef/TaskRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "NLBFargateServiceTaskDefTaskRole6C88F40B" + } + ], + "/aws-ecs-integ-lb-fargate/NLBFargateService/TaskDef/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "NLBFargateServiceTaskDefB836FA89" + } + ], + "/aws-ecs-integ-lb-fargate/NLBFargateService/TaskDef/web/LogGroup/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "NLBFargateServiceTaskDefwebLogGroupC4A42FE2" + } + ], + "/aws-ecs-integ-lb-fargate/NLBFargateService/TaskDef/ExecutionRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "NLBFargateServiceTaskDefExecutionRoleF6D642D5" + } + ], + "/aws-ecs-integ-lb-fargate/NLBFargateService/TaskDef/ExecutionRole/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "NLBFargateServiceTaskDefExecutionRoleDefaultPolicy90080805" + } + ], + "/aws-ecs-integ-lb-fargate/NLBFargateService/Service/Service": [ + { + "type": "aws:cdk:logicalId", + "data": "NLBFargateServiceB92AC095" + } + ], + "/aws-ecs-integ-lb-fargate/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/aws-ecs-integ-lb-fargate/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "aws-ecs-integ-lb-fargate" + }, + "l3FargateTestDefaultTestDeployAssert3391A20B.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "l3FargateTestDefaultTestDeployAssert3391A20B.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "l3FargateTestDefaultTestDeployAssert3391A20B": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "l3FargateTestDefaultTestDeployAssert3391A20B.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}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "l3FargateTestDefaultTestDeployAssert3391A20B.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "l3FargateTestDefaultTestDeployAssert3391A20B.assets" + ], + "metadata": { + "/l3FargateTest/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/l3FargateTest/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "l3FargateTest/DefaultTest/DeployAssert" + }, + "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-ecs-patterns/test/fargate/integ.nlb-sg.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs-patterns/test/fargate/integ.nlb-sg.js.snapshot/tree.json new file mode 100644 index 0000000000000..8e06bfa34093f --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs-patterns/test/fargate/integ.nlb-sg.js.snapshot/tree.json @@ -0,0 +1,1304 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "aws-ecs-integ-lb-fargate": { + "id": "aws-ecs-integ-lb-fargate", + "path": "aws-ecs-integ-lb-fargate", + "children": { + "Vpc": { + "id": "Vpc", + "path": "aws-ecs-integ-lb-fargate/Vpc", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-ecs-integ-lb-fargate/Vpc/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::VPC", + "aws:cdk:cloudformation:props": { + "cidrBlock": "10.0.0.0/16", + "enableDnsHostnames": true, + "enableDnsSupport": true, + "instanceTenancy": "default", + "tags": [ + { + "key": "Name", + "value": "aws-ecs-integ-lb-fargate/Vpc" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnVPC", + "version": "0.0.0" + } + }, + "PublicSubnet1": { + "id": "PublicSubnet1", + "path": "aws-ecs-integ-lb-fargate/Vpc/PublicSubnet1", + "children": { + "Subnet": { + "id": "Subnet", + "path": "aws-ecs-integ-lb-fargate/Vpc/PublicSubnet1/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "availabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "" + } + ] + }, + "cidrBlock": "10.0.0.0/18", + "mapPublicIpOnLaunch": true, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Public" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Public" + }, + { + "key": "Name", + "value": "aws-ecs-integ-lb-fargate/Vpc/PublicSubnet1" + } + ], + "vpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnet", + "version": "0.0.0" + } + }, + "Acl": { + "id": "Acl", + "path": "aws-ecs-integ-lb-fargate/Vpc/PublicSubnet1/Acl", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "aws-ecs-integ-lb-fargate/Vpc/PublicSubnet1/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "aws-ecs-integ-lb-fargate/Vpc/PublicSubnet1" + } + ], + "vpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRouteTable", + "version": "0.0.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "aws-ecs-integ-lb-fargate/Vpc/PublicSubnet1/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VpcPublicSubnet1RouteTable6C95E38E" + }, + "subnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnetRouteTableAssociation", + "version": "0.0.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "aws-ecs-integ-lb-fargate/Vpc/PublicSubnet1/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "destinationCidrBlock": "0.0.0.0/0", + "gatewayId": { + "Ref": "VpcIGWD7BA715C" + }, + "routeTableId": { + "Ref": "VpcPublicSubnet1RouteTable6C95E38E" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRoute", + "version": "0.0.0" + } + }, + "EIP": { + "id": "EIP", + "path": "aws-ecs-integ-lb-fargate/Vpc/PublicSubnet1/EIP", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::EIP", + "aws:cdk:cloudformation:props": { + "domain": "vpc", + "tags": [ + { + "key": "Name", + "value": "aws-ecs-integ-lb-fargate/Vpc/PublicSubnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnEIP", + "version": "0.0.0" + } + }, + "NATGateway": { + "id": "NATGateway", + "path": "aws-ecs-integ-lb-fargate/Vpc/PublicSubnet1/NATGateway", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::NatGateway", + "aws:cdk:cloudformation:props": { + "allocationId": { + "Fn::GetAtt": [ + "VpcPublicSubnet1EIPD7E02669", + "AllocationId" + ] + }, + "subnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + }, + "tags": [ + { + "key": "Name", + "value": "aws-ecs-integ-lb-fargate/Vpc/PublicSubnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnNatGateway", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.PublicSubnet", + "version": "0.0.0" + } + }, + "PublicSubnet2": { + "id": "PublicSubnet2", + "path": "aws-ecs-integ-lb-fargate/Vpc/PublicSubnet2", + "children": { + "Subnet": { + "id": "Subnet", + "path": "aws-ecs-integ-lb-fargate/Vpc/PublicSubnet2/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "availabilityZone": { + "Fn::Select": [ + 1, + { + "Fn::GetAZs": "" + } + ] + }, + "cidrBlock": "10.0.64.0/18", + "mapPublicIpOnLaunch": true, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Public" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Public" + }, + { + "key": "Name", + "value": "aws-ecs-integ-lb-fargate/Vpc/PublicSubnet2" + } + ], + "vpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnet", + "version": "0.0.0" + } + }, + "Acl": { + "id": "Acl", + "path": "aws-ecs-integ-lb-fargate/Vpc/PublicSubnet2/Acl", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "aws-ecs-integ-lb-fargate/Vpc/PublicSubnet2/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "aws-ecs-integ-lb-fargate/Vpc/PublicSubnet2" + } + ], + "vpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRouteTable", + "version": "0.0.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "aws-ecs-integ-lb-fargate/Vpc/PublicSubnet2/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VpcPublicSubnet2RouteTable94F7E489" + }, + "subnetId": { + "Ref": "VpcPublicSubnet2Subnet691E08A3" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnetRouteTableAssociation", + "version": "0.0.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "aws-ecs-integ-lb-fargate/Vpc/PublicSubnet2/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "destinationCidrBlock": "0.0.0.0/0", + "gatewayId": { + "Ref": "VpcIGWD7BA715C" + }, + "routeTableId": { + "Ref": "VpcPublicSubnet2RouteTable94F7E489" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRoute", + "version": "0.0.0" + } + }, + "EIP": { + "id": "EIP", + "path": "aws-ecs-integ-lb-fargate/Vpc/PublicSubnet2/EIP", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::EIP", + "aws:cdk:cloudformation:props": { + "domain": "vpc", + "tags": [ + { + "key": "Name", + "value": "aws-ecs-integ-lb-fargate/Vpc/PublicSubnet2" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnEIP", + "version": "0.0.0" + } + }, + "NATGateway": { + "id": "NATGateway", + "path": "aws-ecs-integ-lb-fargate/Vpc/PublicSubnet2/NATGateway", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::NatGateway", + "aws:cdk:cloudformation:props": { + "allocationId": { + "Fn::GetAtt": [ + "VpcPublicSubnet2EIP3C605A87", + "AllocationId" + ] + }, + "subnetId": { + "Ref": "VpcPublicSubnet2Subnet691E08A3" + }, + "tags": [ + { + "key": "Name", + "value": "aws-ecs-integ-lb-fargate/Vpc/PublicSubnet2" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnNatGateway", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.PublicSubnet", + "version": "0.0.0" + } + }, + "PrivateSubnet1": { + "id": "PrivateSubnet1", + "path": "aws-ecs-integ-lb-fargate/Vpc/PrivateSubnet1", + "children": { + "Subnet": { + "id": "Subnet", + "path": "aws-ecs-integ-lb-fargate/Vpc/PrivateSubnet1/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "availabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "" + } + ] + }, + "cidrBlock": "10.0.128.0/18", + "mapPublicIpOnLaunch": false, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Private" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Private" + }, + { + "key": "Name", + "value": "aws-ecs-integ-lb-fargate/Vpc/PrivateSubnet1" + } + ], + "vpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnet", + "version": "0.0.0" + } + }, + "Acl": { + "id": "Acl", + "path": "aws-ecs-integ-lb-fargate/Vpc/PrivateSubnet1/Acl", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "aws-ecs-integ-lb-fargate/Vpc/PrivateSubnet1/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "aws-ecs-integ-lb-fargate/Vpc/PrivateSubnet1" + } + ], + "vpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRouteTable", + "version": "0.0.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "aws-ecs-integ-lb-fargate/Vpc/PrivateSubnet1/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VpcPrivateSubnet1RouteTableB2C5B500" + }, + "subnetId": { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnetRouteTableAssociation", + "version": "0.0.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "aws-ecs-integ-lb-fargate/Vpc/PrivateSubnet1/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "destinationCidrBlock": "0.0.0.0/0", + "natGatewayId": { + "Ref": "VpcPublicSubnet1NATGateway4D7517AA" + }, + "routeTableId": { + "Ref": "VpcPrivateSubnet1RouteTableB2C5B500" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRoute", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.PrivateSubnet", + "version": "0.0.0" + } + }, + "PrivateSubnet2": { + "id": "PrivateSubnet2", + "path": "aws-ecs-integ-lb-fargate/Vpc/PrivateSubnet2", + "children": { + "Subnet": { + "id": "Subnet", + "path": "aws-ecs-integ-lb-fargate/Vpc/PrivateSubnet2/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "availabilityZone": { + "Fn::Select": [ + 1, + { + "Fn::GetAZs": "" + } + ] + }, + "cidrBlock": "10.0.192.0/18", + "mapPublicIpOnLaunch": false, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Private" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Private" + }, + { + "key": "Name", + "value": "aws-ecs-integ-lb-fargate/Vpc/PrivateSubnet2" + } + ], + "vpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnet", + "version": "0.0.0" + } + }, + "Acl": { + "id": "Acl", + "path": "aws-ecs-integ-lb-fargate/Vpc/PrivateSubnet2/Acl", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "aws-ecs-integ-lb-fargate/Vpc/PrivateSubnet2/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "aws-ecs-integ-lb-fargate/Vpc/PrivateSubnet2" + } + ], + "vpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRouteTable", + "version": "0.0.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "aws-ecs-integ-lb-fargate/Vpc/PrivateSubnet2/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VpcPrivateSubnet2RouteTableA678073B" + }, + "subnetId": { + "Ref": "VpcPrivateSubnet2Subnet3788AAA1" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnetRouteTableAssociation", + "version": "0.0.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "aws-ecs-integ-lb-fargate/Vpc/PrivateSubnet2/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "destinationCidrBlock": "0.0.0.0/0", + "natGatewayId": { + "Ref": "VpcPublicSubnet2NATGateway9182C01D" + }, + "routeTableId": { + "Ref": "VpcPrivateSubnet2RouteTableA678073B" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRoute", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.PrivateSubnet", + "version": "0.0.0" + } + }, + "IGW": { + "id": "IGW", + "path": "aws-ecs-integ-lb-fargate/Vpc/IGW", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::InternetGateway", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "aws-ecs-integ-lb-fargate/Vpc" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnInternetGateway", + "version": "0.0.0" + } + }, + "VPCGW": { + "id": "VPCGW", + "path": "aws-ecs-integ-lb-fargate/Vpc/VPCGW", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::VPCGatewayAttachment", + "aws:cdk:cloudformation:props": { + "internetGatewayId": { + "Ref": "VpcIGWD7BA715C" + }, + "vpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnVPCGatewayAttachment", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.Vpc", + "version": "0.0.0" + } + }, + "FargateCluster": { + "id": "FargateCluster", + "path": "aws-ecs-integ-lb-fargate/FargateCluster", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-ecs-integ-lb-fargate/FargateCluster/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ECS::Cluster", + "aws:cdk:cloudformation:props": {} + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ecs.CfnCluster", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ecs.Cluster", + "version": "0.0.0" + } + }, + "SecurityGroupNlb": { + "id": "SecurityGroupNlb", + "path": "aws-ecs-integ-lb-fargate/SecurityGroupNlb", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-ecs-integ-lb-fargate/SecurityGroupNlb/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SecurityGroup", + "aws:cdk:cloudformation:props": { + "groupDescription": "aws-ecs-integ-lb-fargate/SecurityGroupNlb", + "securityGroupEgress": [ + { + "cidrIp": "0.0.0.0/0", + "description": "Allow all outbound traffic by default", + "ipProtocol": "-1" + } + ], + "securityGroupIngress": [ + { + "cidrIp": "0.0.0.0/0", + "ipProtocol": "tcp", + "fromPort": 32768, + "toPort": 65535, + "description": "from 0.0.0.0/0:32768-65535" + } + ], + "vpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSecurityGroup", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.SecurityGroup", + "version": "0.0.0" + } + }, + "SecurityGroupService": { + "id": "SecurityGroupService", + "path": "aws-ecs-integ-lb-fargate/SecurityGroupService", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-ecs-integ-lb-fargate/SecurityGroupService/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SecurityGroup", + "aws:cdk:cloudformation:props": { + "groupDescription": "aws-ecs-integ-lb-fargate/SecurityGroupService", + "securityGroupEgress": [ + { + "cidrIp": "0.0.0.0/0", + "description": "Allow all outbound traffic by default", + "ipProtocol": "-1" + } + ], + "securityGroupIngress": [ + { + "sourceSecurityGroupId": { + "Fn::GetAtt": [ + "SecurityGroupNlbFA132873", + "GroupId" + ] + }, + "ipProtocol": "tcp", + "fromPort": 0, + "toPort": 65535, + "description": { + "Fn::Join": [ + "", + [ + "from ", + { + "Fn::GetAtt": [ + "SecurityGroupNlbFA132873", + "GroupId" + ] + }, + ":ALL PORTS" + ] + ] + } + } + ], + "vpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSecurityGroup", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.SecurityGroup", + "version": "0.0.0" + } + }, + "NLBFargateService": { + "id": "NLBFargateService", + "path": "aws-ecs-integ-lb-fargate/NLBFargateService", + "children": { + "LB": { + "id": "LB", + "path": "aws-ecs-integ-lb-fargate/NLBFargateService/LB", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-ecs-integ-lb-fargate/NLBFargateService/LB/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ElasticLoadBalancingV2::LoadBalancer", + "aws:cdk:cloudformation:props": { + "loadBalancerAttributes": [ + { + "key": "deletion_protection.enabled", + "value": "false" + } + ], + "scheme": "internet-facing", + "securityGroups": [ + { + "Fn::GetAtt": [ + "SecurityGroupNlbFA132873", + "GroupId" + ] + } + ], + "subnets": [ + { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + }, + { + "Ref": "VpcPublicSubnet2Subnet691E08A3" + } + ], + "type": "network" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_elasticloadbalancingv2.CfnLoadBalancer", + "version": "0.0.0" + } + }, + "PublicListener": { + "id": "PublicListener", + "path": "aws-ecs-integ-lb-fargate/NLBFargateService/LB/PublicListener", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-ecs-integ-lb-fargate/NLBFargateService/LB/PublicListener/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ElasticLoadBalancingV2::Listener", + "aws:cdk:cloudformation:props": { + "defaultActions": [ + { + "type": "forward", + "targetGroupArn": { + "Ref": "NLBFargateServiceLBPublicListenerECSGroupC469CAA2" + } + } + ], + "loadBalancerArn": { + "Ref": "NLBFargateServiceLB659EC17C" + }, + "port": 80, + "protocol": "TCP" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_elasticloadbalancingv2.CfnListener", + "version": "0.0.0" + } + }, + "ECSGroup": { + "id": "ECSGroup", + "path": "aws-ecs-integ-lb-fargate/NLBFargateService/LB/PublicListener/ECSGroup", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-ecs-integ-lb-fargate/NLBFargateService/LB/PublicListener/ECSGroup/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ElasticLoadBalancingV2::TargetGroup", + "aws:cdk:cloudformation:props": { + "port": 80, + "protocol": "TCP", + "targetType": "ip", + "vpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_elasticloadbalancingv2.CfnTargetGroup", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_elasticloadbalancingv2.NetworkTargetGroup", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_elasticloadbalancingv2.NetworkListener", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_elasticloadbalancingv2.NetworkLoadBalancer", + "version": "0.0.0" + } + }, + "LoadBalancerDNS": { + "id": "LoadBalancerDNS", + "path": "aws-ecs-integ-lb-fargate/NLBFargateService/LoadBalancerDNS", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnOutput", + "version": "0.0.0" + } + }, + "TaskDef": { + "id": "TaskDef", + "path": "aws-ecs-integ-lb-fargate/NLBFargateService/TaskDef", + "children": { + "TaskRole": { + "id": "TaskRole", + "path": "aws-ecs-integ-lb-fargate/NLBFargateService/TaskDef/TaskRole", + "children": { + "ImportTaskRole": { + "id": "ImportTaskRole", + "path": "aws-ecs-integ-lb-fargate/NLBFargateService/TaskDef/TaskRole/ImportTaskRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-ecs-integ-lb-fargate/NLBFargateService/TaskDef/TaskRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "ecs-tasks.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-ecs-integ-lb-fargate/NLBFargateService/TaskDef/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ECS::TaskDefinition", + "aws:cdk:cloudformation:props": { + "containerDefinitions": [ + { + "essential": true, + "image": "amazon/amazon-ecs-sample", + "name": "web", + "portMappings": [ + { + "containerPort": 80, + "protocol": "tcp" + } + ], + "logConfiguration": { + "logDriver": "awslogs", + "options": { + "awslogs-group": { + "Ref": "NLBFargateServiceTaskDefwebLogGroupC4A42FE2" + }, + "awslogs-stream-prefix": "NLBFargateService", + "awslogs-region": { + "Ref": "AWS::Region" + } + } + } + } + ], + "cpu": "512", + "executionRoleArn": { + "Fn::GetAtt": [ + "NLBFargateServiceTaskDefExecutionRoleF6D642D5", + "Arn" + ] + }, + "family": "awsecsinteglbfargateNLBFargateServiceTaskDef1265FF34", + "memory": "1024", + "networkMode": "awsvpc", + "requiresCompatibilities": [ + "FARGATE" + ], + "taskRoleArn": { + "Fn::GetAtt": [ + "NLBFargateServiceTaskDefTaskRole6C88F40B", + "Arn" + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ecs.CfnTaskDefinition", + "version": "0.0.0" + } + }, + "web": { + "id": "web", + "path": "aws-ecs-integ-lb-fargate/NLBFargateService/TaskDef/web", + "children": { + "LogGroup": { + "id": "LogGroup", + "path": "aws-ecs-integ-lb-fargate/NLBFargateService/TaskDef/web/LogGroup", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-ecs-integ-lb-fargate/NLBFargateService/TaskDef/web/LogGroup/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Logs::LogGroup", + "aws:cdk:cloudformation:props": {} + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_logs.CfnLogGroup", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_logs.LogGroup", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ecs.ContainerDefinition", + "version": "0.0.0" + } + }, + "ExecutionRole": { + "id": "ExecutionRole", + "path": "aws-ecs-integ-lb-fargate/NLBFargateService/TaskDef/ExecutionRole", + "children": { + "ImportExecutionRole": { + "id": "ImportExecutionRole", + "path": "aws-ecs-integ-lb-fargate/NLBFargateService/TaskDef/ExecutionRole/ImportExecutionRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-ecs-integ-lb-fargate/NLBFargateService/TaskDef/ExecutionRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "ecs-tasks.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "aws-ecs-integ-lb-fargate/NLBFargateService/TaskDef/ExecutionRole/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-ecs-integ-lb-fargate/NLBFargateService/TaskDef/ExecutionRole/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": [ + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "NLBFargateServiceTaskDefwebLogGroupC4A42FE2", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "policyName": "NLBFargateServiceTaskDefExecutionRoleDefaultPolicy90080805", + "roles": [ + { + "Ref": "NLBFargateServiceTaskDefExecutionRoleF6D642D5" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Policy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ecs.FargateTaskDefinition", + "version": "0.0.0" + } + }, + "Service": { + "id": "Service", + "path": "aws-ecs-integ-lb-fargate/NLBFargateService/Service", + "children": { + "Service": { + "id": "Service", + "path": "aws-ecs-integ-lb-fargate/NLBFargateService/Service/Service", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ECS::Service", + "aws:cdk:cloudformation:props": { + "cluster": { + "Ref": "FargateCluster7CCD5F93" + }, + "deploymentConfiguration": { + "maximumPercent": 200, + "minimumHealthyPercent": 50, + "alarms": { + "alarmNames": [], + "enable": false, + "rollback": false + } + }, + "enableEcsManagedTags": false, + "healthCheckGracePeriodSeconds": 60, + "launchType": "FARGATE", + "loadBalancers": [ + { + "targetGroupArn": { + "Ref": "NLBFargateServiceLBPublicListenerECSGroupC469CAA2" + }, + "containerName": "web", + "containerPort": 80 + } + ], + "networkConfiguration": { + "awsvpcConfiguration": { + "assignPublicIp": "DISABLED", + "subnets": [ + { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + }, + { + "Ref": "VpcPrivateSubnet2Subnet3788AAA1" + } + ], + "securityGroups": [ + { + "Fn::GetAtt": [ + "SecurityGroupServiceDE1E5E6A", + "GroupId" + ] + } + ] + } + }, + "taskDefinition": { + "Ref": "NLBFargateServiceTaskDefB836FA89" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ecs.CfnService", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ecs.FargateService", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ecs_patterns.NetworkLoadBalancedFargateService", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "aws-ecs-integ-lb-fargate/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "aws-ecs-integ-lb-fargate/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "l3FargateTest": { + "id": "l3FargateTest", + "path": "l3FargateTest", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "l3FargateTest/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "l3FargateTest/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "l3FargateTest/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "l3FargateTest/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "l3FargateTest/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "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-ecs-patterns/test/fargate/integ.nlb-sg.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs-patterns/test/fargate/integ.nlb-sg.ts new file mode 100644 index 0000000000000..a88af6f0e0ee9 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs-patterns/test/fargate/integ.nlb-sg.ts @@ -0,0 +1,40 @@ +import * as ec2 from 'aws-cdk-lib/aws-ec2'; +import * as ecs from 'aws-cdk-lib/aws-ecs'; +import * as cdk from 'aws-cdk-lib'; +import * as integ from '@aws-cdk/integ-tests-alpha'; +import * as ecsPatterns from 'aws-cdk-lib/aws-ecs-patterns'; + +const app = new cdk.App(); +const stack = new cdk.Stack(app, 'aws-ecs-integ-lb-fargate'); + +// Create VPC and cluster +const vpc = new ec2.Vpc(stack, 'Vpc', { maxAzs: 2, restrictDefaultSecurityGroup: false }); +const cluster = new ecs.Cluster(stack, 'FargateCluster', { vpc }); +const securityGroupNlb = new ec2.SecurityGroup(stack, 'SecurityGroupNlb', { + vpc, + allowAllOutbound: true, +}); +securityGroupNlb.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcpRange(32768, 65535)); +const securityGroupService = new ec2.SecurityGroup(stack, 'SecurityGroupService', { + vpc, + allowAllOutbound: true, +}); +securityGroupService.addIngressRule(ec2.Peer.securityGroupId(securityGroupNlb.securityGroupId), ec2.Port.allTcp()); + +// Create NLB service with security group +const networkLoadBalancedFargateService = new ecsPatterns.NetworkLoadBalancedFargateService(stack, 'NLBFargateService', { + cluster, + memoryLimitMiB: 1024, + cpu: 512, + taskImageOptions: { + image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), + }, + securityGroups: [securityGroupService], +}); +networkLoadBalancedFargateService.loadBalancer.connections.addSecurityGroup(securityGroupNlb); + +new integ.IntegTest(app, 'l3FargateTest', { + testCases: [stack], +}); + +app.synth(); diff --git a/packages/aws-cdk-lib/aws-ecs-patterns/README.md b/packages/aws-cdk-lib/aws-ecs-patterns/README.md index 6dda31ecccfaf..f927f78127524 100644 --- a/packages/aws-cdk-lib/aws-ecs-patterns/README.md +++ b/packages/aws-cdk-lib/aws-ecs-patterns/README.md @@ -1072,4 +1072,19 @@ const networkLoadBalancedFargateService = new ecsPatterns.NetworkLoadBalancedFar image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), }, }); -``` \ No newline at end of file +``` + +### Set securityGroups for NetworkLoadBalancedFargateService + +```ts +declare const vpc: ec2.Vpc; +declare const securityGroup: ec2.SecurityGroup; +const queueProcessingFargateService = new ecsPatterns.NetworkLoadBalancedFargateService(this, 'Service', { + vpc, + memoryLimitMiB: 512, + taskImageOptions: { + image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), + }, + securityGroups: [securityGroup], +}); +``` diff --git a/packages/aws-cdk-lib/aws-ecs-patterns/lib/fargate/network-load-balanced-fargate-service.ts b/packages/aws-cdk-lib/aws-ecs-patterns/lib/fargate/network-load-balanced-fargate-service.ts index 993a17a92aed2..cf5b0389914c8 100644 --- a/packages/aws-cdk-lib/aws-ecs-patterns/lib/fargate/network-load-balanced-fargate-service.ts +++ b/packages/aws-cdk-lib/aws-ecs-patterns/lib/fargate/network-load-balanced-fargate-service.ts @@ -1,5 +1,5 @@ import { Construct } from 'constructs'; -import { SubnetSelection } from '../../../aws-ec2'; +import { ISecurityGroup, SubnetSelection } from '../../../aws-ec2'; import { FargateService, FargateTaskDefinition } from '../../../aws-ecs'; import { FeatureFlags } from '../../../core'; import * as cxapi from '../../../cx-api'; @@ -25,6 +25,12 @@ export interface NetworkLoadBalancedFargateServiceProps extends NetworkLoadBalan */ readonly taskSubnets?: SubnetSelection; + /** + * The security groups to associate with the service. If you do not specify a security group, a new security group is created. + * + * @default - A new security group is created. + */ + readonly securityGroups?: ISecurityGroup[]; } /** @@ -102,6 +108,7 @@ export class NetworkLoadBalancedFargateService extends NetworkLoadBalancedServic platformVersion: props.platformVersion, deploymentController: props.deploymentController, circuitBreaker: props.circuitBreaker, + securityGroups: props.securityGroups, vpcSubnets: props.taskSubnets, enableExecuteCommand: props.enableExecuteCommand, capacityProviderStrategies: props.capacityProviderStrategies, diff --git a/packages/aws-cdk-lib/aws-ecs-patterns/test/fargate/load-balanced-fargate-service.test.ts b/packages/aws-cdk-lib/aws-ecs-patterns/test/fargate/load-balanced-fargate-service.test.ts index 5092f257a96fb..18b6b929f76f6 100644 --- a/packages/aws-cdk-lib/aws-ecs-patterns/test/fargate/load-balanced-fargate-service.test.ts +++ b/packages/aws-cdk-lib/aws-ecs-patterns/test/fargate/load-balanced-fargate-service.test.ts @@ -2022,4 +2022,48 @@ describe('NetworkLoadBalancedFargateService', () => { }, }); }); + + test('specify security group', () => { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'Vpc'); + const cluster = new ecs.Cluster(stack, 'Cluster', { vpc, clusterName: 'MyCluster' }); + const securityGroup = new ec2.SecurityGroup(stack, 'SecurityGroup', { + allowAllOutbound: false, + description: 'Example', + securityGroupName: 'Rolly', + vpc, + }); + + // WHEN + new ecsPatterns.NetworkLoadBalancedFargateService(stack, 'Service', { + cluster, + taskImageOptions: { + image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), + }, + securityGroups: [securityGroup], + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { + LaunchType: 'FARGATE', + }); + + Template.fromStack(stack).hasResourceProperties('AWS::EC2::SecurityGroup', { + GroupDescription: 'Example', + GroupName: 'Rolly', + SecurityGroupEgress: [ + { + CidrIp: '255.255.255.255/32', + Description: 'Disallow all traffic', + FromPort: 252, + IpProtocol: 'icmp', + ToPort: 86, + }, + ], + VpcId: { + Ref: 'Vpc8378EB38', + }, + }); + }); }); From 4582ac5c9e2cd9c7de289be06682ecd5b813b26a Mon Sep 17 00:00:00 2001 From: Jimmy Gaussen <jimmy.gaussen@gmail.com> Date: Wed, 13 Mar 2024 18:50:28 +0100 Subject: [PATCH 2/4] chore(msk-alpha): update KafkaVersion (#29440) ### Issue # (if applicable) Could not find any in the backlog ### Reason for this change Update the CDK listed Kafka versions to match the current availability, as well as add missing deprecated versions ### Description of changes * Added latest version * `3.6.0` also supports tiered storage, see [docs](https://docs.aws.amazon.com/msk/latest/developerguide/msk-tiered-storage.html#msk-tiered-storage-requirements). I replaced the prefix check with a list of supported versions, as I'm not sure if say every reason after 3.6.0 will support it, and the `.tiered` prefix isn't consistently applied anymore * Added two unlisted, deprecated versions, as they are still returned by the SDK. The goal is to remove any differences between the SDK and the CDK to ease future automation ### Description of how you validated changes I compared the current CDK versions to live SDK data, using the `kafka:ListKafkaVersions` API results. ### Checklist - [x] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --------- Co-authored-by: GZ <yuanhaoz@amazon.com> --- packages/@aws-cdk/aws-msk-alpha/README.md | 5 +-- .../aws-msk-alpha/lib/cluster-version.ts | 35 ++++++++++++++++++- .../aws-msk-alpha/test/cluster.test.ts | 8 ++++- 3 files changed, 44 insertions(+), 4 deletions(-) diff --git a/packages/@aws-cdk/aws-msk-alpha/README.md b/packages/@aws-cdk/aws-msk-alpha/README.md index 81b74be3aa279..8155f8829a756 100644 --- a/packages/@aws-cdk/aws-msk-alpha/README.md +++ b/packages/@aws-cdk/aws-msk-alpha/README.md @@ -217,7 +217,8 @@ You can configure an MSK cluster storage mode using the `storageMode` property. Tiered storage is a low-cost storage tier for Amazon MSK that scales to virtually unlimited storage, making it cost-effective to build streaming data applications. -> Visit [Tiered storage](https://docs.aws.amazon.com/msk/latest/developerguide/msk-tiered-storage.html) for more details. +> Visit [Tiered storage](https://docs.aws.amazon.com/msk/latest/developerguide/msk-tiered-storage.html) +to see the list of compatible Kafka versions and for more details. ```ts declare const vpc: ec2.Vpc; @@ -225,7 +226,7 @@ declare const bucket: s3.IBucket; const cluster = new msk.Cluster(this, 'cluster', { clusterName: 'myCluster', - kafkaVersion: msk.KafkaVersion.V2_8_2_TIERED, + kafkaVersion: msk.KafkaVersion.V3_6_0, vpc, storageMode: msk.StorageMode.TIERED, }); diff --git a/packages/@aws-cdk/aws-msk-alpha/lib/cluster-version.ts b/packages/@aws-cdk/aws-msk-alpha/lib/cluster-version.ts index 405c0c6ee0ecc..b3c5277018b1c 100644 --- a/packages/@aws-cdk/aws-msk-alpha/lib/cluster-version.ts +++ b/packages/@aws-cdk/aws-msk-alpha/lib/cluster-version.ts @@ -11,6 +11,15 @@ export class KafkaVersion { */ public static readonly V1_1_1 = KafkaVersion.of('1.1.1'); + /** + * **Deprecated by Amazon MSK. You can't create a Kafka cluster with a deprecated version.** + * + * Kafka version 2.1.0 + * + * @deprecated use the latest runtime instead + */ + public static readonly V2_1_0 = KafkaVersion.of('2.1.0'); + /** * Kafka version 2.2.1 */ @@ -21,6 +30,15 @@ export class KafkaVersion { */ public static readonly V2_3_1 = KafkaVersion.of('2.3.1'); + /** + * **Deprecated by Amazon MSK. You can't create a Kafka cluster with a deprecated version.** + * + * Kafka version 2.4.1 + * + * @deprecated use the latest runtime instead + */ + public static readonly V2_4_1 = KafkaVersion.of('2.4.1'); + /** * Kafka version 2.4.1 */ @@ -111,6 +129,11 @@ export class KafkaVersion { */ public static readonly V3_5_1 = KafkaVersion.of('3.5.1'); + /** + * Kafka version 3.6.0 + */ + public static readonly V3_6_0 = KafkaVersion.of('3.6.0'); + /** * Custom cluster version * @param version custom version number @@ -119,6 +142,16 @@ export class KafkaVersion { return new KafkaVersion(version); } + /** + * List of Kafka versions that support tiered storage + * + * @see https://docs.aws.amazon.com/msk/latest/developerguide/msk-tiered-storage.html#msk-tiered-storage-requirements + */ + private static readonly TIERED_STORAGE_COMPATIBLE_VERSIONS = [ + KafkaVersion.V2_8_2_TIERED, + KafkaVersion.V3_6_0, + ].map(({ version }) => version); + /** * * @param version cluster version number @@ -129,6 +162,6 @@ export class KafkaVersion { * Checks if the cluster version supports tiered storage mode. */ public isTieredStorageCompatible() { - return this.version.endsWith('.tiered'); + return KafkaVersion.TIERED_STORAGE_COMPATIBLE_VERSIONS.includes(this.version); }; } diff --git a/packages/@aws-cdk/aws-msk-alpha/test/cluster.test.ts b/packages/@aws-cdk/aws-msk-alpha/test/cluster.test.ts index babbb235b40a3..7c4bd6a10f4ec 100644 --- a/packages/@aws-cdk/aws-msk-alpha/test/cluster.test.ts +++ b/packages/@aws-cdk/aws-msk-alpha/test/cluster.test.ts @@ -790,6 +790,12 @@ describe('MSK Cluster', () => { describe('created with storage mode', () => { describe('with tiered storage mode', () => { + test('version.isTieredStorageCompatible', () => { + expect(msk.KafkaVersion.V2_8_2_TIERED.isTieredStorageCompatible()).toBeTruthy(); + expect(msk.KafkaVersion.V3_5_1.isTieredStorageCompatible()).toBeFalsy(); + expect(msk.KafkaVersion.V3_6_0.isTieredStorageCompatible()).toBeTruthy(); + }); + test('create a cluster with tiered storage mode', () => { new msk.Cluster(stack, 'Cluster', { clusterName: 'cluster', @@ -797,7 +803,7 @@ describe('MSK Cluster', () => { kafkaVersion: msk.KafkaVersion.V2_8_2_TIERED, vpc, storageMode: msk.StorageMode.TIERED, - }), + }); Template.fromStack(stack).hasResourceProperties('AWS::MSK::Cluster', { StorageMode: 'TIERED', }); From 135b5208dce849f49b6f540f7199ece057a7ef22 Mon Sep 17 00:00:00 2001 From: Calvin Combs <66279577+comcalvi@users.noreply.github.com> Date: Wed, 13 Mar 2024 13:20:12 -0700 Subject: [PATCH 3/4] feat(CLI): improved nested stack diff (#29172) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Issue # (if applicable) ### Reason for this change The existing nested stack diff places a fake property, `NestedTemplate`, in the templates to be diffed. This prevents displaying resource replacement information in the diff, like we do for top level stacks. This PR does *not* add changeset replacement information from changesets, but it does add replacement information from the spec. ### Description of changes Reworked nested stack diff to treat nested stacks as top level stacks. This improves the visual UX and sets us up for using changesets with nested stacks. #### Before <img width="957" alt="Screenshot 2024-02-19 at 1 47 59 PM" src="https://github.com/aws/aws-cdk/assets/66279577/a94275c4-e7c3-4d2c-a924-ee61c36bea4d"> #### After <img width="957" alt="Screenshot 2024-02-19 at 1 48 48 PM" src="https://github.com/aws/aws-cdk/assets/66279577/5263aaf9-ef2f-4228-b413-81e780c4b8f8"> ### Description of how you validated changes Unit tests + manual tests. ### Checklist - [x] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../cloudformation-diff/lib/diff-template.ts | 6 +- .../cloudformation-diff/lib/format.ts | 8 +- packages/aws-cdk/lib/api/deployments.ts | 10 +- .../api/evaluate-cloudformation-template.ts | 34 +- .../aws-cdk/lib/api/hotswap-deployments.ts | 22 +- .../aws-cdk/lib/api/nested-stack-helpers.ts | 85 ++-- packages/aws-cdk/lib/cdk-toolkit.ts | 16 +- packages/aws-cdk/lib/diff.ts | 35 +- .../api/cloudformation-deployments.test.ts | 381 ++++++++++-------- .../api/hotswap/nested-stacks-hotswap.test.ts | 251 ++++++------ packages/aws-cdk/test/diff.test.ts | 357 +++++++++++++--- ...with-two-nested-stacks-stack.template.json | 7 +- ...mbda-two-stacks-stack.nested.template.json | 4 +- packages/aws-cdk/test/util.ts | 4 +- 14 files changed, 757 insertions(+), 463 deletions(-) diff --git a/packages/@aws-cdk/cloudformation-diff/lib/diff-template.ts b/packages/@aws-cdk/cloudformation-diff/lib/diff-template.ts index 1902d757f486d..284bb3a8d5f46 100644 --- a/packages/@aws-cdk/cloudformation-diff/lib/diff-template.ts +++ b/packages/@aws-cdk/cloudformation-diff/lib/diff-template.ts @@ -54,7 +54,7 @@ export function fullDiff( normalize(newTemplate); const theDiff = diffTemplate(currentTemplate, newTemplate); if (changeSet) { - filterFalsePositivies(theDiff, changeSet); + filterFalsePositives(theDiff, changeSet); addImportInformation(theDiff, changeSet); } if (isImport) { @@ -64,7 +64,7 @@ export function fullDiff( return theDiff; } -function diffTemplate( +export function diffTemplate( currentTemplate: { [key: string]: any }, newTemplate: { [key: string]: any }, ): types.TemplateDiff { @@ -235,7 +235,7 @@ function addImportInformation(diff: types.TemplateDiff, changeSet?: CloudFormati } } -function filterFalsePositivies(diff: types.TemplateDiff, changeSet: CloudFormation.DescribeChangeSetOutput) { +function filterFalsePositives(diff: types.TemplateDiff, changeSet: CloudFormation.DescribeChangeSetOutput) { const replacements = findResourceReplacements(changeSet); diff.resources.forEachDifference((logicalId: string, change: types.ResourceDifference) => { if (change.resourceType.includes('AWS::Serverless')) { diff --git a/packages/@aws-cdk/cloudformation-diff/lib/format.ts b/packages/@aws-cdk/cloudformation-diff/lib/format.ts index 7935f774fd468..724af468c2f45 100644 --- a/packages/@aws-cdk/cloudformation-diff/lib/format.ts +++ b/packages/@aws-cdk/cloudformation-diff/lib/format.ts @@ -30,7 +30,7 @@ export interface FormatStream extends NodeJS.WritableStream { export function formatDifferences( stream: FormatStream, templateDiff: TemplateDiff, - logicalToPathMap: { [logicalId: string]: string } = { }, + logicalToPathMap: { [logicalId: string]: string } = {}, context: number = 3) { const formatter = new Formatter(stream, logicalToPathMap, templateDiff, context); @@ -59,7 +59,7 @@ export function formatDifferences( export function formatSecurityChanges( stream: NodeJS.WritableStream, templateDiff: TemplateDiff, - logicalToPathMap: {[logicalId: string]: string} = {}, + logicalToPathMap: { [logicalId: string]: string } = {}, context?: number) { const formatter = new Formatter(stream, logicalToPathMap, templateDiff, context); @@ -254,7 +254,7 @@ class Formatter { const oldStr = JSON.stringify(oldObject, null, 2); const newStr = JSON.stringify(newObject, null, 2); const diff = _diffStrings(oldStr, newStr, this.context); - for (let i = 0 ; i < diff.length ; i++) { + for (let i = 0; i < diff.length; i++) { this.print('%s %s %s', linePrefix, i === 0 ? '└─' : ' ', diff[i]); } } else { @@ -466,7 +466,7 @@ function _diffStrings(oldStr: string, newStr: string, context: number): string[] function _findIndent(lines: string[]): number { let indent = Number.MAX_SAFE_INTEGER; for (const line of lines) { - for (let i = 1 ; i < line.length ; i++) { + for (let i = 1; i < line.length; i++) { if (line.charAt(i) !== ' ') { indent = indent > i - 1 ? i - 1 : indent; break; diff --git a/packages/aws-cdk/lib/api/deployments.ts b/packages/aws-cdk/lib/api/deployments.ts index 925cdebd45e15..b7bea90c56c28 100644 --- a/packages/aws-cdk/lib/api/deployments.ts +++ b/packages/aws-cdk/lib/api/deployments.ts @@ -7,7 +7,7 @@ import { CredentialsOptions, SdkForEnvironment, SdkProvider } from './aws-auth/s import { deployStack, DeployStackResult, destroyStack, DeploymentMethod } from './deploy-stack'; import { EnvironmentResources, EnvironmentResourcesRegistry } from './environment-resources'; import { HotswapMode } from './hotswap/common'; -import { loadCurrentTemplateWithNestedStacks, loadCurrentTemplate, flattenNestedStackNames, TemplateWithNestedStackCount } from './nested-stack-helpers'; +import { loadCurrentTemplateWithNestedStacks, loadCurrentTemplate, RootTemplateWithNestedStacks } from './nested-stack-helpers'; import { CloudFormationStack, Template, ResourcesToImport, ResourceIdentifierSummaries } from './util/cloudformation'; import { StackActivityProgress } from './util/cloudformation/stack-activity-monitor'; import { replaceEnvPlaceholders } from './util/placeholders'; @@ -327,13 +327,9 @@ export class Deployments { public async readCurrentTemplateWithNestedStacks( rootStackArtifact: cxapi.CloudFormationStackArtifact, retrieveProcessedTemplate: boolean = false, - ): Promise<TemplateWithNestedStackCount> { + ): Promise<RootTemplateWithNestedStacks> { const sdk = (await this.prepareSdkWithLookupOrDeployRole(rootStackArtifact)).stackSdk; - const templateWithNestedStacks = await loadCurrentTemplateWithNestedStacks(rootStackArtifact, sdk, retrieveProcessedTemplate); - return { - deployedTemplate: templateWithNestedStacks.deployedTemplate, - nestedStackCount: flattenNestedStackNames(templateWithNestedStacks.nestedStackNames).length, - }; + return loadCurrentTemplateWithNestedStacks(rootStackArtifact, sdk, retrieveProcessedTemplate); } public async readCurrentTemplate(stackArtifact: cxapi.CloudFormationStackArtifact): Promise<Template> { diff --git a/packages/aws-cdk/lib/api/evaluate-cloudformation-template.ts b/packages/aws-cdk/lib/api/evaluate-cloudformation-template.ts index 329bf2ec9ba59..5a1cc675bff85 100644 --- a/packages/aws-cdk/lib/api/evaluate-cloudformation-template.ts +++ b/packages/aws-cdk/lib/api/evaluate-cloudformation-template.ts @@ -1,7 +1,7 @@ import * as AWS from 'aws-sdk'; import { PromiseResult } from 'aws-sdk/lib/request'; import { ISDK } from './aws-auth'; -import { NestedStackNames } from './nested-stack-helpers'; +import { NestedStackTemplates } from './nested-stack-helpers'; export interface ListStackResources { listStackResources(): Promise<AWS.CloudFormation.StackResourceSummary[]>; @@ -102,7 +102,7 @@ export interface EvaluateCloudFormationTemplateProps { readonly partition: string; readonly urlSuffix: (region: string) => string; readonly sdk: ISDK; - readonly nestedStackNames?: { [nestedStackLogicalId: string]: NestedStackNames }; + readonly nestedStacks?: { [nestedStackLogicalId: string]: NestedStackTemplates }; } export class EvaluateCloudFormationTemplate { @@ -114,7 +114,7 @@ export class EvaluateCloudFormationTemplate { private readonly partition: string; private readonly urlSuffix: (region: string) => string; private readonly sdk: ISDK; - private readonly nestedStackNames: { [nestedStackLogicalId: string]: NestedStackNames }; + private readonly nestedStacks: { [nestedStackLogicalId: string]: NestedStackTemplates }; private readonly stackResources: ListStackResources; private readonly lookupExport: LookupExport; @@ -136,7 +136,7 @@ export class EvaluateCloudFormationTemplate { this.sdk = props.sdk; // We need names of nested stack so we can evaluate cross stack references - this.nestedStackNames = props.nestedStackNames ?? {}; + this.nestedStacks = props.nestedStacks ?? {}; // The current resources of the Stack. // We need them to figure out the physical name of a resource in case it wasn't specified by the user. @@ -163,7 +163,7 @@ export class EvaluateCloudFormationTemplate { partition: this.partition, urlSuffix: this.urlSuffix, sdk: this.sdk, - nestedStackNames: this.nestedStackNames, + nestedStacks: this.nestedStacks, }); } @@ -386,17 +386,15 @@ export class EvaluateCloudFormationTemplate { } if (foundResource.ResourceType == 'AWS::CloudFormation::Stack' && attribute?.startsWith('Outputs.')) { - // need to resolve attributes from another stack's Output section - const dependantStackName = this.findNestedStack(logicalId, this.nestedStackNames); - if (!dependantStackName) { + const dependantStack = this.findNestedStack(logicalId, this.nestedStacks); + if (!dependantStack || !dependantStack.physicalName) { //this is a newly created nested stack and cannot be hotswapped return undefined; } - const dependantStackTemplate = this.template.Resources[logicalId]; const evaluateCfnTemplate = await this.createNestedEvaluateCloudFormationTemplate( - dependantStackName, - dependantStackTemplate?.Properties?.NestedTemplate, - dependantStackTemplate.newValue?.Properties?.Parameters); + dependantStack.physicalName, + dependantStack.generatedTemplate, + dependantStack.generatedTemplate.Parameters!); // Split Outputs.<refName> into 'Outputs' and '<refName>' and recursively call evaluate return evaluateCfnTemplate.evaluateCfnExpression({ 'Fn::GetAtt': attribute.split(/\.(.*)/s) }); @@ -406,14 +404,14 @@ export class EvaluateCloudFormationTemplate { return this.formatResourceAttribute(foundResource, attribute); } - private findNestedStack(logicalId: string, nestedStackNames: { - [nestedStackLogicalId: string]: NestedStackNames; - }): string | undefined { - for (const [nestedStackLogicalId, { nestedChildStackNames, nestedStackPhysicalName }] of Object.entries(nestedStackNames)) { + private findNestedStack(logicalId: string, nestedStacks: { + [nestedStackLogicalId: string]: NestedStackTemplates; + }): NestedStackTemplates | undefined { + for (const nestedStackLogicalId of Object.keys(nestedStacks)) { if (nestedStackLogicalId === logicalId) { - return nestedStackPhysicalName; + return nestedStacks[nestedStackLogicalId]; } - const checkInNestedChildStacks = this.findNestedStack(logicalId, nestedChildStackNames); + const checkInNestedChildStacks = this.findNestedStack(logicalId, nestedStacks[nestedStackLogicalId].nestedStackTemplates); if (checkInNestedChildStacks) return checkInNestedChildStacks; } return undefined; diff --git a/packages/aws-cdk/lib/api/hotswap-deployments.ts b/packages/aws-cdk/lib/api/hotswap-deployments.ts index 51404d624b92e..efebb414d6f92 100644 --- a/packages/aws-cdk/lib/api/hotswap-deployments.ts +++ b/packages/aws-cdk/lib/api/hotswap-deployments.ts @@ -11,7 +11,7 @@ import { isHotswappableEcsServiceChange } from './hotswap/ecs-services'; import { isHotswappableLambdaFunctionChange } from './hotswap/lambda-functions'; import { skipChangeForS3DeployCustomResourcePolicy, isHotswappableS3BucketDeploymentChange } from './hotswap/s3-bucket-deployments'; import { isHotswappableStateMachineChange } from './hotswap/stepfunctions-state-machines'; -import { loadCurrentTemplateWithNestedStacks, NestedStackNames } from './nested-stack-helpers'; +import { NestedStackTemplates, loadCurrentTemplateWithNestedStacks } from './nested-stack-helpers'; import { CloudFormationStack } from './util/cloudformation'; import { print } from '../logging'; @@ -78,12 +78,12 @@ export async function tryHotswapDeployment( partition: (await sdk.currentAccount()).partition, urlSuffix: (region) => sdk.getEndpointSuffix(region), sdk, - nestedStackNames: currentTemplate.nestedStackNames, + nestedStacks: currentTemplate.nestedStacks, }); - const stackChanges = cfn_diff.fullDiff(currentTemplate.deployedTemplate, stackArtifact.template); + const stackChanges = cfn_diff.fullDiff(currentTemplate.deployedRootTemplate, stackArtifact.template); const { hotswappableChanges, nonHotswappableChanges } = await classifyResourceChanges( - stackChanges, evaluateCfnTemplate, sdk, currentTemplate.nestedStackNames, + stackChanges, evaluateCfnTemplate, sdk, currentTemplate.nestedStacks, ); logNonHotswappableChanges(nonHotswappableChanges, hotswapMode); @@ -109,7 +109,7 @@ async function classifyResourceChanges( stackChanges: cfn_diff.TemplateDiff, evaluateCfnTemplate: EvaluateCloudFormationTemplate, sdk: ISDK, - nestedStackNames: { [nestedStackName: string]: NestedStackNames }, + nestedStackNames: { [nestedStackName: string]: NestedStackTemplates }, ): Promise<ClassifiedResourceChanges> { const resourceDifferences = getStackResourceDifferences(stackChanges); @@ -225,12 +225,12 @@ function filterDict<T>(dict: { [key: string]: T }, func: (t: T) => boolean): { [ async function findNestedHotswappableChanges( logicalId: string, change: cfn_diff.ResourceDifference, - nestedStackNames: { [nestedStackName: string]: NestedStackNames }, + nestedStackTemplates: { [nestedStackName: string]: NestedStackTemplates }, evaluateCfnTemplate: EvaluateCloudFormationTemplate, sdk: ISDK, ): Promise<ClassifiedResourceChanges> { - const nestedStackName = nestedStackNames[logicalId].nestedStackPhysicalName; - if (!nestedStackName) { + const nestedStack = nestedStackTemplates[logicalId]; + if (!nestedStack.physicalName) { return { hotswappableChanges: [], nonHotswappableChanges: [{ @@ -244,14 +244,14 @@ async function findNestedHotswappableChanges( } const evaluateNestedCfnTemplate = await evaluateCfnTemplate.createNestedEvaluateCloudFormationTemplate( - nestedStackName, change.newValue?.Properties?.NestedTemplate, change.newValue?.Properties?.Parameters, + nestedStack.physicalName, nestedStack.generatedTemplate, change.newValue?.Properties?.Parameters, ); const nestedDiff = cfn_diff.fullDiff( - change.oldValue?.Properties?.NestedTemplate, change.newValue?.Properties?.NestedTemplate, + nestedStackTemplates[logicalId].deployedTemplate, nestedStackTemplates[logicalId].generatedTemplate, ); - return classifyResourceChanges(nestedDiff, evaluateNestedCfnTemplate, sdk, nestedStackNames[logicalId].nestedChildStackNames); + return classifyResourceChanges(nestedDiff, evaluateNestedCfnTemplate, sdk, nestedStackTemplates[logicalId].nestedStackTemplates); } /** Returns 'true' if a pair of changes is for the same resource. */ diff --git a/packages/aws-cdk/lib/api/nested-stack-helpers.ts b/packages/aws-cdk/lib/api/nested-stack-helpers.ts index acbbb1e2a9245..3242794aa1039 100644 --- a/packages/aws-cdk/lib/api/nested-stack-helpers.ts +++ b/packages/aws-cdk/lib/api/nested-stack-helpers.ts @@ -5,59 +5,38 @@ import { ISDK } from './aws-auth'; import { LazyListStackResources, ListStackResources } from './evaluate-cloudformation-template'; import { CloudFormationStack, Template } from './util/cloudformation'; -export interface TemplateWithNestedStackNames { +export interface NestedStackTemplates { + readonly physicalName: string | undefined; readonly deployedTemplate: Template; - readonly nestedStackNames: { [nestedStackLogicalId: string]: NestedStackNames }; + readonly generatedTemplate: Template; + readonly nestedStackTemplates: { [nestedStackLogicalId: string]: NestedStackTemplates}; } -export interface NestedStackNames { - readonly nestedStackPhysicalName: string | undefined; - readonly nestedChildStackNames: { [logicalId: string]: NestedStackNames }; -} - -export interface TemplateWithNestedStackCount { - readonly deployedTemplate: Template; - readonly nestedStackCount: number; +export interface RootTemplateWithNestedStacks { + readonly deployedRootTemplate: Template; + readonly nestedStacks: { [nestedStackLogicalId: string]: NestedStackTemplates }; } /** - * Reads the currently deployed template from CloudFormation and adds a - * property, `NestedTemplate`, to any nested stacks that appear in either - * the deployed template or the newly synthesized template. `NestedTemplate` - * is populated with contents of the nested template by mutating the - * `template` property of `rootStackArtifact`. This is done for all - * nested stack resources to arbitrary depths. + * Reads the currently deployed template and all of its nested stack templates from CloudFormation. */ export async function loadCurrentTemplateWithNestedStacks( rootStackArtifact: cxapi.CloudFormationStackArtifact, sdk: ISDK, retrieveProcessedTemplate: boolean = false, -): Promise<TemplateWithNestedStackNames> { - const deployedTemplate = await loadCurrentTemplate(rootStackArtifact, sdk, retrieveProcessedTemplate); - const nestedStackNames = await addNestedTemplatesToGeneratedAndDeployedStacks(rootStackArtifact, sdk, { +): Promise<RootTemplateWithNestedStacks> { + const deployedRootTemplate = await loadCurrentTemplate(rootStackArtifact, sdk, retrieveProcessedTemplate); + const nestedStacks = await loadNestedStacks(rootStackArtifact, sdk, { generatedTemplate: rootStackArtifact.template, - deployedTemplate: deployedTemplate, + deployedTemplate: deployedRootTemplate, deployedStackName: rootStackArtifact.stackName, }); return { - deployedTemplate, - nestedStackNames, + deployedRootTemplate, + nestedStacks, }; } -export function flattenNestedStackNames(nestedStackNames: { [nestedStackLogicalId: string]: NestedStackNames }): string[] { - const nameList = []; - for (const key of Object.keys(nestedStackNames)) { - nameList.push(key); - - if (Object.keys(nestedStackNames[key].nestedChildStackNames).length !== 0) { - flattenNestedStacksHelper(nestedStackNames[key].nestedChildStackNames, nameList); - } - } - - return nameList; -} - /** * Returns the currently deployed template from CloudFormation that corresponds to `stackArtifact`. */ @@ -76,13 +55,13 @@ async function loadCurrentStackTemplate( return stack.template(); } -async function addNestedTemplatesToGeneratedAndDeployedStacks( +async function loadNestedStacks( rootStackArtifact: cxapi.CloudFormationStackArtifact, sdk: ISDK, parentTemplates: StackTemplates, -): Promise<{ [nestedStackLogicalId: string]: NestedStackNames }> { +): Promise<{ [nestedStackLogicalId: string]: NestedStackTemplates }> { const listStackResources = parentTemplates.deployedStackName ? new LazyListStackResources(sdk, parentTemplates.deployedStackName) : undefined; - const nestedStackNames: { [nestedStackLogicalId: string]: NestedStackNames } = {}; + const nestedStacks: { [nestedStackLogicalId: string]: NestedStackTemplates } = {}; for (const [nestedStackLogicalId, generatedNestedStackResource] of Object.entries(parentTemplates.generatedTemplate.Resources ?? {})) { if (!isCdkManagedNestedStack(generatedNestedStackResource)) { continue; @@ -91,19 +70,11 @@ async function addNestedTemplatesToGeneratedAndDeployedStacks( const assetPath = generatedNestedStackResource.Metadata['aws:asset:path']; const nestedStackTemplates = await getNestedStackTemplates(rootStackArtifact, assetPath, nestedStackLogicalId, listStackResources, sdk); - generatedNestedStackResource.Properties.NestedTemplate = nestedStackTemplates.generatedTemplate; - - const deployedParentTemplate = parentTemplates.deployedTemplate; - deployedParentTemplate.Resources = deployedParentTemplate.Resources ?? {}; - const deployedNestedStackResource = deployedParentTemplate.Resources[nestedStackLogicalId] ?? {}; - deployedParentTemplate.Resources[nestedStackLogicalId] = deployedNestedStackResource; - deployedNestedStackResource.Type = deployedNestedStackResource.Type ?? 'AWS::CloudFormation::Stack'; - deployedNestedStackResource.Properties = deployedNestedStackResource.Properties ?? {}; - deployedNestedStackResource.Properties.NestedTemplate = nestedStackTemplates.deployedTemplate; - - nestedStackNames[nestedStackLogicalId] = { - nestedStackPhysicalName: nestedStackTemplates.deployedStackName, - nestedChildStackNames: await addNestedTemplatesToGeneratedAndDeployedStacks( + nestedStacks[nestedStackLogicalId] = { + deployedTemplate: nestedStackTemplates.deployedTemplate, + generatedTemplate: nestedStackTemplates.generatedTemplate, + physicalName: nestedStackTemplates.deployedStackName, + nestedStackTemplates: await loadNestedStacks( rootStackArtifact, sdk, nestedStackTemplates, @@ -111,7 +82,7 @@ async function addNestedTemplatesToGeneratedAndDeployedStacks( }; } - return nestedStackNames; + return nestedStacks; } async function getNestedStackTemplates( @@ -153,16 +124,6 @@ function isCdkManagedNestedStack(stackResource: any): stackResource is NestedSta return stackResource.Type === 'AWS::CloudFormation::Stack' && stackResource.Metadata && stackResource.Metadata['aws:asset:path']; } -function flattenNestedStacksHelper(nestedStackNames: { [logicalId: string]: NestedStackNames }, nameList: string[]) { - for (const key of Object.keys(nestedStackNames)) { - nameList.push(key); - - if (Object.keys(nestedStackNames[key].nestedChildStackNames).length !== 0) { - flattenNestedStacksHelper(nestedStackNames[key].nestedChildStackNames, nameList); - } - } -} - interface StackTemplates { readonly generatedTemplate: any; readonly deployedTemplate: any; diff --git a/packages/aws-cdk/lib/cdk-toolkit.ts b/packages/aws-cdk/lib/cdk-toolkit.ts index c67b02dbc9cf2..05aeee002c068 100644 --- a/packages/aws-cdk/lib/cdk-toolkit.ts +++ b/packages/aws-cdk/lib/cdk-toolkit.ts @@ -156,7 +156,7 @@ export class CdkToolkit { const template = deserializeStructure(await fs.readFile(options.templatePath, { encoding: 'UTF-8' })); diffs = options.securityOnly ? numberFromBool(printSecurityDiff(template, stacks.firstStack, RequireApproval.Broadening, changeSet)) - : printStackDiff(template, stacks.firstStack, strict, contextLines, quiet, changeSet, stream); + : printStackDiff(template, stacks.firstStack.template, strict, contextLines, quiet, changeSet, stream); } else { // Compare N stacks against deployed templates for (const stack of stacks.stackArtifacts) { @@ -164,11 +164,11 @@ export class CdkToolkit { stream.write(format('Stack %s\n', chalk.bold(stack.displayName))); } - const templateWithNames = await this.props.deployments.readCurrentTemplateWithNestedStacks( + const templateWithNestedStacks = await this.props.deployments.readCurrentTemplateWithNestedStacks( stack, options.compareAgainstProcessedTemplate, ); - const currentTemplate = templateWithNames.deployedTemplate; - const nestedStackCount = templateWithNames.nestedStackCount; + const currentTemplate = templateWithNestedStacks.deployedRootTemplate; + const nestedStacks = templateWithNestedStacks.nestedStacks; const resourcesToImport = await this.tryGetResources(await this.props.deployments.resolveEnvironment(stack)); if (resourcesToImport) { @@ -203,10 +203,10 @@ export class CdkToolkit { // pass a boolean to print if the stack is a migrate stack in order to set all resource diffs to import const stackCount = options.securityOnly - ? (numberFromBool(printSecurityDiff(currentTemplate, stack, RequireApproval.Broadening, changeSet)) > 0 ? 1 : 0) - : (printStackDiff(currentTemplate, stack, strict, contextLines, quiet, changeSet, stream, !!resourcesToImport) > 0 ? 1 : 0); + ? (numberFromBool(printSecurityDiff(currentTemplate, stack, RequireApproval.Broadening, changeSet))) + : (printStackDiff(currentTemplate, stack, strict, contextLines, quiet, changeSet, stream, nestedStacks, !!resourcesToImport)); - diffs += stackCount + nestedStackCount; + diffs += stackCount; } } @@ -1531,4 +1531,4 @@ function buildParameterMap(parameters: { } return parameterMap; -} \ No newline at end of file +} diff --git a/packages/aws-cdk/lib/diff.ts b/packages/aws-cdk/lib/diff.ts index fa897430ad37b..b49f288fc2534 100644 --- a/packages/aws-cdk/lib/diff.ts +++ b/packages/aws-cdk/lib/diff.ts @@ -1,8 +1,10 @@ +import { format } from 'util'; import * as cxschema from '@aws-cdk/cloud-assembly-schema'; import * as cfnDiff from '@aws-cdk/cloudformation-diff'; import * as cxapi from '@aws-cdk/cx-api'; import { CloudFormation } from 'aws-sdk'; import * as chalk from 'chalk'; +import { NestedStackTemplates } from './api/nested-stack-helpers'; import { print, warning } from './logging'; /** @@ -14,7 +16,7 @@ import { print, warning } from './logging'; * @param context lines of context to use in arbitrary JSON diff * @param quiet silences \'There were no differences\' messages * - * @returns the count of differences that were rendered. + * @returns the number of stacks in this stack tree that have differences, including the top-level root stack */ export function printStackDiff( oldTemplate: any, @@ -23,7 +25,8 @@ export function printStackDiff( context: number, quiet: boolean, changeSet?: CloudFormation.DescribeChangeSetOutput, - stream?: cfnDiff.FormatStream, + stream: cfnDiff.FormatStream = process.stderr, + nestedStackTemplates?: { [nestedStackLogicalId: string]: NestedStackTemplates }, isImport?: boolean): number { let diff = cfnDiff.fullDiff(oldTemplate, newTemplate.template, changeSet, isImport); @@ -49,8 +52,10 @@ export function printStackDiff( }); } + let stackDiffCount = 0; if (!diff.isEmpty) { - cfnDiff.formatDifferences(stream || process.stderr, diff, { + stackDiffCount++; + cfnDiff.formatDifferences(stream, diff, { ...logicalIdMapFromTemplate(oldTemplate), ...buildLogicalToPathMap(newTemplate), }, context); @@ -61,7 +66,29 @@ export function printStackDiff( print(chalk.yellow(`Omitted ${filteredChangesCount} changes because they are likely mangled non-ASCII characters. Use --strict to print them.`)); } - return diff.differenceCount; + for (const nestedStackLogicalId of Object.keys(nestedStackTemplates ?? {})) { + if (!nestedStackTemplates) { + break; + } + const nestedStack = nestedStackTemplates[nestedStackLogicalId]; + if (!quiet) { + stream.write(format('Stack %s\n', chalk.bold(nestedStack.physicalName ?? nestedStackLogicalId))); + } + + (newTemplate as any)._template = nestedStack.generatedTemplate; + stackDiffCount += printStackDiff( + nestedStack.deployedTemplate, + newTemplate, + strict, + context, + quiet, + undefined, + stream, + nestedStack.nestedStackTemplates, + ); + } + + return stackDiffCount; } export enum RequireApproval { diff --git a/packages/aws-cdk/test/api/cloudformation-deployments.test.ts b/packages/aws-cdk/test/api/cloudformation-deployments.test.ts index ac1fed6176e1b..cbaf7c3d8746c 100644 --- a/packages/aws-cdk/test/api/cloudformation-deployments.test.ts +++ b/packages/aws-cdk/test/api/cloudformation-deployments.test.ts @@ -246,39 +246,16 @@ test('readCurrentTemplateWithNestedStacks() can handle non-Resources in the temp ); // WHEN - const nestedStackCount = (await deployments.readCurrentTemplateWithNestedStacks(rootStack)).nestedStackCount; - const deployedTemplate = (await deployments.readCurrentTemplateWithNestedStacks(rootStack)).deployedTemplate; + const deployedTemplate = (await deployments.readCurrentTemplateWithNestedStacks(rootStack)).deployedRootTemplate; + const nestedStacks = (await deployments.readCurrentTemplateWithNestedStacks(rootStack)).nestedStacks; // THEN - expect(nestedStackCount).toEqual(1); expect(deployedTemplate).toEqual({ Resources: { NestedStack: { Type: 'AWS::CloudFormation::Stack', Properties: { TemplateURL: 'https://www.magic-url.com', - NestedTemplate: { - Resources: { - NestedResource: { - Type: 'AWS::Something', - Properties: { - Property: 'old-value', - }, - }, - }, - Outputs: { - NestedOutput: { - Value: { - Ref: 'NestedResource', - }, - }, - }, - Parameters: { - NestedParam: { - Type: 'String', - }, - }, - }, }, Metadata: { 'aws:asset:path': 'one-output-one-param-stack.nested.template.json', @@ -293,33 +270,62 @@ test('readCurrentTemplateWithNestedStacks() can handle non-Resources in the temp Type: 'AWS::CloudFormation::Stack', Properties: { TemplateURL: 'https://www.magic-url.com', - NestedTemplate: { - Resources: { - NestedResource: { - Type: 'AWS::Something', - Properties: { - Property: 'new-value', - }, - }, + }, + Metadata: { + 'aws:asset:path': 'one-output-one-param-stack.nested.template.json', + }, + }, + }, + }); + + expect(nestedStacks).toEqual({ + NestedStack: { + deployedTemplate: { + Outputs: { + NestedOutput: { + Value: { + Ref: 'NestedResource', }, - Outputs: { - NestedOutput: { - Value: { - Ref: 'NestedResource', - }, - }, + }, + }, + Parameters: { + NestedParam: { + Type: 'String', + }, + }, + Resources: { + NestedResource: { + Properties: { + Property: 'old-value', }, - Parameters: { - NestedParam: { - Type: 'Number', - }, + Type: 'AWS::Something', + }, + }, + }, + generatedTemplate: { + Outputs: { + NestedOutput: { + Value: { + Ref: 'NestedResource', }, }, }, - Metadata: { - 'aws:asset:path': 'one-output-one-param-stack.nested.template.json', + Parameters: { + NestedParam: { + Type: 'Number', + }, + }, + Resources: { + NestedResource: { + Properties: { + Property: 'new-value', + }, + Type: 'AWS::Something', + }, }, }, + nestedStackTemplates: {}, + physicalName: 'NestedStack', }, }); }); @@ -453,65 +459,16 @@ test('readCurrentTemplateWithNestedStacks() with a 3-level nested + sibling stru ); // WHEN - const nestedStackCount = (await deployments.readCurrentTemplateWithNestedStacks(rootStack)).nestedStackCount; - const deployedTemplate = (await deployments.readCurrentTemplateWithNestedStacks(rootStack)).deployedTemplate; + const deployedTemplate = (await deployments.readCurrentTemplateWithNestedStacks(rootStack)).deployedRootTemplate; + const nestedStacks = (await deployments.readCurrentTemplateWithNestedStacks(rootStack)).nestedStacks; // THEN - expect(nestedStackCount).toEqual(3); expect(deployedTemplate).toEqual({ Resources: { NestedStack: { Type: 'AWS::CloudFormation::Stack', Properties: { TemplateURL: 'https://www.magic-url.com', - NestedTemplate: { - Resources: { - GrandChildStackA: { - Type: 'AWS::CloudFormation::Stack', - Properties: { - TemplateURL: 'https://www.magic-url.com', - NestedTemplate: { - Resources: { - SomeResource: { - Type: 'AWS::Something', - Properties: { - Property: 'old-value', - }, - }, - }, - }, - }, - Metadata: { - 'aws:asset:path': 'one-resource-stack.nested.template.json', - }, - }, - GrandChildStackB: { - Type: 'AWS::CloudFormation::Stack', - Properties: { - TemplateURL: 'https://www.magic-url.com', - NestedTemplate: { - Resources: { - SomeResource: { - Type: 'AWS::Something', - Properties: { - Property: 'old-value', - }, - }, - }, - }, - }, - Metadata: { - 'aws:asset:path': 'one-resource-stack.nested.template.json', - }, - }, - SomeResource: { - Type: 'AWS::Something', - Properties: { - Property: 'old-value', - }, - }, - }, - }, }, Metadata: { 'aws:asset:path': 'one-resource-two-stacks-stack.nested.template.json', @@ -526,59 +483,123 @@ test('readCurrentTemplateWithNestedStacks() with a 3-level nested + sibling stru Type: 'AWS::CloudFormation::Stack', Properties: { TemplateURL: 'https://www.magic-url.com', - NestedTemplate: { + }, + Metadata: { + 'aws:asset:path': 'one-resource-two-stacks-stack.nested.template.json', + }, + }, + }, + }); + + expect(nestedStacks).toEqual({ + NestedStack: { + deployedTemplate: { + Resources: { + GrandChildStackA: { + Metadata: { + 'aws:asset:path': 'one-resource-stack.nested.template.json', + }, + Properties: { + TemplateURL: 'https://www.magic-url.com', + }, + Type: 'AWS::CloudFormation::Stack', + }, + GrandChildStackB: { + Metadata: { + 'aws:asset:path': 'one-resource-stack.nested.template.json', + }, + Properties: { + TemplateURL: 'https://www.magic-url.com', + }, + Type: 'AWS::CloudFormation::Stack', + }, + SomeResource: { + Properties: { + Property: 'old-value', + }, + Type: 'AWS::Something', + }, + }, + }, + generatedTemplate: { + Resources: { + GrandChildStackA: { + Metadata: { + 'aws:asset:path': 'one-resource-stack.nested.template.json', + }, + Properties: { + TemplateURL: 'https://www.magic-url.com', + }, + Type: 'AWS::CloudFormation::Stack', + }, + GrandChildStackB: { + Metadata: { + 'aws:asset:path': 'one-resource-stack.nested.template.json', + }, + Properties: { + TemplateURL: 'https://www.magic-url.com', + }, + Type: 'AWS::CloudFormation::Stack', + }, + SomeResource: { + Properties: { + Property: 'new-value', + }, + Type: 'AWS::Something', + }, + }, + }, + nestedStackTemplates: { + GrandChildStackA: { + deployedTemplate: { Resources: { - GrandChildStackA: { - Type: 'AWS::CloudFormation::Stack', + SomeResource: { Properties: { - TemplateURL: 'https://www.magic-url.com', - NestedTemplate: { - Resources: { - SomeResource: { - Type: 'AWS::Something', - Properties: { - Property: 'new-value', - }, - }, - }, - }, - }, - Metadata: { - 'aws:asset:path': 'one-resource-stack.nested.template.json', + Property: 'old-value', }, + Type: 'AWS::Something', }, - GrandChildStackB: { - Type: 'AWS::CloudFormation::Stack', + }, + }, + generatedTemplate: { + Resources: { + SomeResource: { Properties: { - TemplateURL: 'https://www.magic-url.com', - NestedTemplate: { - Resources: { - SomeResource: { - Type: 'AWS::Something', - Properties: { - Property: 'new-value', - }, - }, - }, - }, - }, - Metadata: { - 'aws:asset:path': 'one-resource-stack.nested.template.json', + Property: 'new-value', }, + Type: 'AWS::Something', }, + }, + }, + nestedStackTemplates: {}, + physicalName: 'GrandChildStackA', + }, + GrandChildStackB: { + deployedTemplate: { + Resources: { SomeResource: { + Properties: { + Property: 'old-value', + }, Type: 'AWS::Something', + }, + }, + }, + generatedTemplate: { + Resources: { + SomeResource: { Properties: { Property: 'new-value', }, + Type: 'AWS::Something', }, }, }, - }, - Metadata: { - 'aws:asset:path': 'one-resource-two-stacks-stack.nested.template.json', + nestedStackTemplates: {}, + physicalName: 'GrandChildStackB', }, }, + physicalName: 'NestedStack', }, }); }); @@ -612,26 +633,47 @@ test('readCurrentTemplateWithNestedStacks() on an undeployed parent stack with a }); // WHEN - const nestedStackCount = (await deployments.readCurrentTemplateWithNestedStacks(rootStack)).nestedStackCount; - const deployedTemplate = (await deployments.readCurrentTemplateWithNestedStacks(rootStack)).deployedTemplate; + const deployedTemplate = (await deployments.readCurrentTemplateWithNestedStacks(rootStack)).deployedRootTemplate; + const nestedStacks = (await deployments.readCurrentTemplateWithNestedStacks(rootStack)).nestedStacks; // THEN - expect(nestedStackCount).toEqual(2); - expect(deployedTemplate).toEqual({ - Resources: { - NestedStack: { - Type: 'AWS::CloudFormation::Stack', - Properties: { - NestedTemplate: { + expect(deployedTemplate).toEqual({}); + expect(nestedStacks).toEqual({ + NestedStack: { + deployedTemplate: {}, + generatedTemplate: { + Resources: { + SomeResource: { + Type: 'AWS::Something', + Properties: { + Property: 'new-value', + }, + }, + NestedStack: { + Type: 'AWS::CloudFormation::Stack', + Properties: { + TemplateURL: 'https://www.magic-url.com', + }, + Metadata: { + 'aws:asset:path': 'one-resource-stack.nested.template.json', + }, + }, + }, + }, + nestedStackTemplates: { + NestedStack: { + deployedTemplate: {}, + generatedTemplate: { Resources: { - NestedStack: { - Type: 'AWS::CloudFormation::Stack', + SomeResource: { + Type: 'AWS::Something', Properties: { - NestedTemplate: {}, + Property: 'new-value', }, }, }, }, + nestedStackTemplates: {}, }, }, }, @@ -787,27 +829,16 @@ test('readCurrentTemplateWithNestedStacks() succesfully ignores stacks without m )); // WHEN - const nestedStackCount = (await deployments.readCurrentTemplateWithNestedStacks(rootStack)).nestedStackCount; - const deployedTemplate = (await deployments.readCurrentTemplateWithNestedStacks(rootStack)).deployedTemplate; + const deployedTemplate = (await deployments.readCurrentTemplateWithNestedStacks(rootStack)).deployedRootTemplate; + const nestedStacks = (await deployments.readCurrentTemplateWithNestedStacks(rootStack)).nestedStacks; // THEN - expect(nestedStackCount).toEqual(1); expect(deployedTemplate).toEqual({ Resources: { WithMetadata: { Type: 'AWS::CloudFormation::Stack', Properties: { TemplateURL: 'https://www.magic-url.com', - NestedTemplate: { - Resources: { - SomeResource: { - Type: 'AWS::Something', - Properties: { - Property: 'old-value', - }, - }, - }, - }, }, Metadata: { 'aws:asset:path': 'one-resource-stack.nested.template.json', @@ -835,16 +866,6 @@ test('readCurrentTemplateWithNestedStacks() succesfully ignores stacks without m Type: 'AWS::CloudFormation::Stack', Properties: { TemplateURL: 'https://www.magic-url.com', - NestedTemplate: { - Resources: { - SomeResource: { - Type: 'AWS::Something', - Properties: { - Property: 'new-value', - }, - }, - }, - }, }, Metadata: { 'aws:asset:path': 'one-resource-stack.nested.template.json', @@ -852,6 +873,32 @@ test('readCurrentTemplateWithNestedStacks() succesfully ignores stacks without m }, }, }); + expect(nestedStacks).toEqual({ + WithMetadata: { + deployedTemplate: { + Resources: { + SomeResource: { + Properties: { + Property: 'old-value', + }, + Type: 'AWS::Something', + }, + }, + }, + generatedTemplate: { + Resources: { + SomeResource: { + Properties: { + Property: 'new-value', + }, + Type: 'AWS::Something', + }, + }, + }, + physicalName: 'one-resource-stack', + nestedStackTemplates: {}, + }, + }); }); function pushStackResourceSummaries(stackName: string, ...items: CloudFormation.StackResourceSummary[]) { diff --git a/packages/aws-cdk/test/api/hotswap/nested-stacks-hotswap.test.ts b/packages/aws-cdk/test/api/hotswap/nested-stacks-hotswap.test.ts index cb499f24bd3b7..318638b72000c 100644 --- a/packages/aws-cdk/test/api/hotswap/nested-stacks-hotswap.test.ts +++ b/packages/aws-cdk/test/api/hotswap/nested-stacks-hotswap.test.ts @@ -8,7 +8,6 @@ let mockUpdateLambdaCode: (params: Lambda.Types.UpdateFunctionCodeRequest) => La let mockPublishVersion: jest.Mock<Lambda.FunctionConfiguration, Lambda.PublishVersionRequest[]>; let hotswapMockSdkProvider: setup.HotswapMockSdkProvider; -// TODO: more tests for parent vs child containing hotswappable changes describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hotswapMode) => { test('can hotswap a lambda function in a 1-level nested stack', async () => { // GIVEN @@ -18,14 +17,14 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot updateFunctionCode: mockUpdateLambdaCode, }); - const rootStack = testStack({ + const oldRootStack = testStack({ stackName: 'LambdaRoot', template: { Resources: { NestedStack: { Type: 'AWS::CloudFormation::Stack', Properties: { - TemplateURL: 'https://www.magic-url.com', + TemplateURL: 'https://www.amazoff.com', }, Metadata: { 'aws:asset:path': 'one-lambda-stack.nested.template.json', @@ -35,7 +34,7 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot }, }); - setup.addTemplateToCloudFormationLookupMock(rootStack); + setup.addTemplateToCloudFormationLookupMock(oldRootStack); setup.addTemplateToCloudFormationLookupMock(testStack({ stackName: 'NestedStack', template: { @@ -50,7 +49,7 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot FunctionName: 'my-function', }, Metadata: { - 'aws:asset:path': 'old-path', + 'aws:asset:path': 'old-lambda-path', }, }, }, @@ -63,10 +62,10 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot ), ); - const cdkStackArtifact = testStack({ stackName: 'LambdaRoot', template: rootStack.template }); - // WHEN - const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact); + oldRootStack.template.Resources.NestedStack.Properties.TemplateURL = 'https://www.amazon.com'; + const newRootStack = testStack({ stackName: 'LambdaRoot', template: oldRootStack.template }); + const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, newRootStack); // THEN expect(deployStackResult).not.toBeUndefined(); @@ -85,14 +84,14 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot updateFunctionCode: mockUpdateLambdaCode, }); - const rootStack = testStack({ + const oldRootStack = testStack({ stackName: 'TwoLevelLambdaRoot', template: { Resources: { ChildStack: { Type: 'AWS::CloudFormation::Stack', Properties: { - TemplateURL: 'https://www.magic-url.com', + TemplateURL: 'https://www.amazoff.com', }, Metadata: { 'aws:asset:path': 'one-lambda-one-stack-stack.nested.template.json', @@ -102,8 +101,9 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot }, }); - setup.addTemplateToCloudFormationLookupMock(rootStack); - setup.addTemplateToCloudFormationLookupMock(testStack({ + setup.addTemplateToCloudFormationLookupMock(oldRootStack); + + const oldChildStack = testStack({ stackName: 'ChildStack', template: { Resources: { @@ -117,13 +117,13 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot FunctionName: 'child-function', }, Metadata: { - 'aws:asset:path': 'old-path', + 'aws:asset:path': 'old-lambda-path', }, }, GrandChildStack: { Type: 'AWS::CloudFormation::Stack', Properties: { - TemplateURL: 'https://www.magic-url.com', + TemplateURL: 'https://www.amazoff.com', }, Metadata: { 'aws:asset:path': 'one-lambda-stack.nested.template.json', @@ -131,7 +131,9 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot }, }, }, - })); + }); + setup.addTemplateToCloudFormationLookupMock(oldChildStack); + setup.addTemplateToCloudFormationLookupMock(testStack({ stackName: 'GrandChildStack', template: { @@ -146,7 +148,7 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot FunctionName: 'my-function', }, Metadata: { - 'aws:asset:path': 'old-path', + 'aws:asset:path': 'old-lambda-path', }, }, }, @@ -164,10 +166,15 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot ), ); - const cdkStackArtifact = testStack({ stackName: 'TwoLevelLambdaRoot', template: rootStack.template }); - // WHEN - const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact); + oldRootStack.template.Resources.ChildStack.Properties.TemplateURL = 'https://www.amazon.com'; + oldChildStack.template.Resources.GrandChildStack.Properties.TemplateURL = 'https://www.amazon.com'; + + // write the new templates to disk + const newRootStack = testStack({ stackName: oldRootStack.stackName, template: oldRootStack.template }); + testStack({ stackName: oldChildStack.stackName, template: oldChildStack.template }); + + const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, newRootStack); // THEN expect(deployStackResult).not.toBeUndefined(); @@ -191,7 +198,7 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot updateFunctionCode: mockUpdateLambdaCode, }); - const rootStack = testStack({ + const oldRootStack = testStack({ stackName: 'SiblingLambdaRoot', template: { Resources: { @@ -214,14 +221,14 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot FunctionName: 'root-function', }, Metadata: { - 'aws:asset:path': 'old-path', + 'aws:asset:path': 'old-lambda-path', }, }, }, }, }); - setup.addTemplateToCloudFormationLookupMock(rootStack); + setup.addTemplateToCloudFormationLookupMock(oldRootStack); setup.addTemplateToCloudFormationLookupMock(testStack({ stackName: 'NestedStack', template: { @@ -236,7 +243,7 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot FunctionName: 'my-function', }, Metadata: { - 'aws:asset:path': 'old-path', + 'aws:asset:path': 'old-lambda-path', }, }, }, @@ -249,11 +256,12 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot ), ); - rootStack.template.Resources.Func.Properties.Code.S3Bucket = 'new-bucket'; - const cdkStackArtifact = testStack({ stackName: 'SiblingLambdaRoot', template: rootStack.template }); - // WHEN - const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact); + oldRootStack.template.Resources.Func.Properties.Code.S3Bucket = 'new-bucket'; + oldRootStack.template.Resources.NestedStack.Properties.TemplateURL = 'https://www.amazon.com'; + // write the updated templates to disk + const newRootStack = testStack({ stackName: oldRootStack.stackName, template: oldRootStack.template }); + const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, newRootStack); // THEN expect(deployStackResult).not.toBeUndefined(); @@ -279,7 +287,7 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot updateFunctionCode: mockUpdateLambdaCode, }); - const rootStack = testStack({ + const oldRootStack = testStack({ stackName: 'NonHotswappableRoot', template: { Resources: { @@ -302,14 +310,14 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot FunctionName: 'root-function', }, Metadata: { - 'aws:asset:path': 'old-path', + 'aws:asset:path': 'old-lambda-path', }, }, }, }, }); - setup.addTemplateToCloudFormationLookupMock(rootStack); + setup.addTemplateToCloudFormationLookupMock(oldRootStack); setup.addTemplateToCloudFormationLookupMock(testStack({ stackName: 'NestedStack', template: { @@ -325,7 +333,7 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot FunctionName: 'my-function', }, Metadata: { - 'aws:asset:path': 'old-path', + 'aws:asset:path': 'old-lambda-path', }, }, }, @@ -338,12 +346,13 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot ), ); - rootStack.template.Resources.Func.Properties.Code.S3Bucket = 'new-bucket'; - const cdkStackArtifact = testStack({ stackName: 'NonHotswappableRoot', template: rootStack.template }); + oldRootStack.template.Resources.Func.Properties.Code.S3Bucket = 'new-bucket'; + oldRootStack.template.Resources.NestedStack.Properties.TemplateURL = 'https://www.amazon.com'; + const newRootStack = testStack({ stackName: oldRootStack.stackName, template: oldRootStack.template }); if (hotswapMode === HotswapMode.FALL_BACK) { // WHEN - const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact); + const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, newRootStack); // THEN expect(deployStackResult).toBeUndefined(); @@ -351,7 +360,7 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot } else if (hotswapMode === HotswapMode.HOTSWAP_ONLY) { // WHEN - const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact); + const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, newRootStack); // THEN expect(deployStackResult).not.toBeUndefined(); @@ -373,7 +382,7 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot updateFunctionCode: mockUpdateLambdaCode, }); - const rootStack = testStack({ + const oldRootStack = testStack({ stackName: 'NestedStackDeletionRoot', template: { Resources: { @@ -396,14 +405,14 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot FunctionName: 'root-function', }, Metadata: { - 'aws:asset:path': 'old-path', + 'aws:asset:path': 'old-lambda-path', }, }, }, }, }); - setup.addTemplateToCloudFormationLookupMock(rootStack); + setup.addTemplateToCloudFormationLookupMock(oldRootStack); setup.addTemplateToCloudFormationLookupMock(testStack({ stackName: 'NestedStack', template: { @@ -418,7 +427,7 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot FunctionName: 'my-function', }, Metadata: { - 'aws:asset:path': 'old-path', + 'aws:asset:path': 'old-lambda-path', }, }, }, @@ -431,20 +440,20 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot ), ); - rootStack.template.Resources.Func.Properties.Code.S3Bucket = 'new-bucket'; - delete rootStack.template.Resources.NestedStack; - const cdkStackArtifact = testStack({ stackName: 'NestedStackDeletionRoot', template: rootStack.template }); + oldRootStack.template.Resources.Func.Properties.Code.S3Bucket = 'new-bucket'; + delete oldRootStack.template.Resources.NestedStack; + const newRootStack = testStack({ stackName: oldRootStack.stackName, template: oldRootStack.template }); if (hotswapMode === HotswapMode.FALL_BACK) { // WHEN - const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact); + const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, newRootStack); // THEN expect(deployStackResult).toBeUndefined(); expect(mockUpdateLambdaCode).not.toHaveBeenCalled(); } else if (hotswapMode === HotswapMode.HOTSWAP_ONLY) { // WHEN - const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact); + const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, newRootStack); // THEN expect(deployStackResult).not.toBeUndefined(); @@ -466,7 +475,7 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot updateFunctionCode: mockUpdateLambdaCode, }); - const rootStack = testStack({ + const oldRootStack = testStack({ stackName: 'NestedStackCreationRoot', template: { Resources: { @@ -480,30 +489,31 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot FunctionName: 'root-function', }, Metadata: { - 'aws:asset:path': 'old-path', + 'aws:asset:path': 'old-lambda-path', }, }, }, }, }); - setup.addTemplateToCloudFormationLookupMock(rootStack); + setup.addTemplateToCloudFormationLookupMock(oldRootStack); - rootStack.template.Resources.Func.Properties.Code.S3Bucket = 'new-bucket'; - rootStack.template.Resources.NestedStack = { + oldRootStack.template.Resources.Func.Properties.Code.S3Bucket = 'new-bucket'; + oldRootStack.template.Resources.NestedStack = { Type: 'AWS::CloudFormation::Stack', Properties: { - TemplateURL: 'https://www.magic-url.com', + TemplateURL: 'https://www.amazon.com', }, Metadata: { 'aws:asset:path': 'one-lambda-stack.nested.template.json', }, }; - const cdkStackArtifact = testStack({ stackName: 'NestedStackCreationRoot', template: rootStack.template }); + // we need this because testStack() immediately writes the template to disk, so changing the template afterwards is not going to update the file. + const newRootStack = testStack({ stackName: oldRootStack.stackName, template: oldRootStack.template }); if (hotswapMode === HotswapMode.FALL_BACK) { // WHEN - const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact); + const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, newRootStack); // THEN expect(deployStackResult).toBeUndefined(); @@ -511,7 +521,7 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot } else if (hotswapMode === HotswapMode.HOTSWAP_ONLY) { // WHEN - const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact); + const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, newRootStack); // THEN expect(deployStackResult).not.toBeUndefined(); @@ -533,7 +543,7 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot updateFunctionCode: mockUpdateLambdaCode, }); - const rootStack = testStack({ + const oldRootStack = testStack({ stackName: 'NestedStackTypeChangeRoot', template: { Resources: { @@ -547,7 +557,7 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot FunctionName: 'root-function', }, Metadata: { - 'aws:asset:path': 'old-path', + 'aws:asset:path': 'old-lambda-path', }, }, FutureNestedStack: { @@ -560,30 +570,31 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot FunctionName: 'spooky-function', }, Metadata: { - 'aws:asset:path': 'old-path', + 'aws:asset:path': 'old-lambda-path', }, }, }, }, }); - setup.addTemplateToCloudFormationLookupMock(rootStack); + setup.addTemplateToCloudFormationLookupMock(oldRootStack); - rootStack.template.Resources.Func.Properties.Code.S3Bucket = 'new-bucket'; - rootStack.template.Resources.FutureNestedStack = { + oldRootStack.template.Resources.Func.Properties.Code.S3Bucket = 'new-bucket'; + oldRootStack.template.Resources.FutureNestedStack = { Type: 'AWS::CloudFormation::Stack', Properties: { - TemplateURL: 'https://www.magic-url.com', + TemplateURL: 'https://www.amazon.com', }, Metadata: { 'aws:asset:path': 'one-lambda-stack.nested.template.json', }, }; - const cdkStackArtifact = testStack({ stackName: 'NestedStackTypeChangeRoot', template: rootStack.template }); + // write the updated template to disk + const newRootStack = testStack({ stackName: oldRootStack.stackName, template: oldRootStack.template }); if (hotswapMode === HotswapMode.FALL_BACK) { // WHEN - const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact); + const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, newRootStack); // THEN expect(deployStackResult).toBeUndefined(); @@ -591,7 +602,7 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot } else if (hotswapMode === HotswapMode.HOTSWAP_ONLY) { // WHEN - const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact); + const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, newRootStack); // THEN expect(deployStackResult).not.toBeUndefined(); @@ -620,11 +631,11 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot }, }, Metadata: { - 'aws:asset:path': 'old-path', + 'aws:asset:path': 'old-lambda-path', }, }; - const rootStack = testStack({ + const oldRootStack = testStack({ stackName: 'MultiLayerRoot', template: { Resources: { @@ -642,8 +653,9 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot }, }); - setup.addTemplateToCloudFormationLookupMock(rootStack); - setup.addTemplateToCloudFormationLookupMock(testStack({ + setup.addTemplateToCloudFormationLookupMock(oldRootStack); + + const oldChildStack = testStack({ stackName: 'ChildStack', template: { Resources: { @@ -668,7 +680,9 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot Func: lambdaFunctionResource, }, }, - })); + }); + setup.addTemplateToCloudFormationLookupMock(oldChildStack); + setup.addTemplateToCloudFormationLookupMock(testStack({ stackName: 'GrandChildStackA', template: { @@ -708,11 +722,16 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot setup.stackSummaryOf('Func', 'AWS::Lambda::Function', 'grandchild-B-function'), ); - rootStack.template.Resources.Func.Properties.Code.S3Key = 'new-key'; - const cdkStackArtifact = testStack({ stackName: 'MultiLayerRoot', template: rootStack.template }); - // WHEN - const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact); + oldRootStack.template.Resources.Func.Properties.Code.S3Key = 'new-key'; + oldRootStack.template.Resources.ChildStack.Properties.TemplateURL = 'https://www.amazon.com'; + oldChildStack.template.Resources.GrandChildStackA.Properties.TemplateURL = 'https://www.amazon.com'; + oldChildStack.template.Resources.GrandChildStackB.Properties.TemplateURL = 'https://www.amazon.com'; + + const newRootStack = testStack({ stackName: oldRootStack.stackName, template: oldRootStack.template }); + //testStack({ stackName: oldChildStack.stackName, template: oldChildStack.template }); + + const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, newRootStack); // THEN expect(deployStackResult).not.toBeUndefined(); @@ -796,7 +815,7 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot FunctionName: 'my-function', }, Metadata: { - 'aws:asset:path': 'old-path', + 'aws:asset:path': 'old-lambda-path', }, }, }, @@ -809,10 +828,10 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot ), ); - const cdkStackArtifact = testStack({ stackName: 'LambdaRoot', template: rootStack.template }); + rootStack.template.Resources.NestedStack.Properties.TemplateURL = 'https://www.amazon.com'; // WHEN - const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact, { + const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, rootStack, { S3BucketParam: 'bucket-param-value', S3KeyParam: 'key-param-value', }); @@ -826,8 +845,8 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot }); }); - test('can hotswap a lambda function in a 2-level nested stack with dependency on a output of 2nd level sibling stack', async () => { - // GIVEN: RootStack has one child stack `FirstLevelRootStack` which further has two child stacks + test('can hotswap a lambda function in a 2-level nested stack with dependency on an output of 2nd level sibling stack', async () => { + // GIVEN: RootStack has one child stack `FirstLevelNestedStack` which further has two child stacks // `NestedLambdaStack` and `NestedSiblingStack`. `NestedLambdaStack` takes two parameters s3Key // and s3Bucket and use them for a Lambda function. // RootStack resolves s3Bucket from a root template parameter and passed to FirstLevelRootStack which @@ -838,11 +857,11 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot updateFunctionCode: mockUpdateLambdaCode, }); - const rootStack = testStack({ + const oldRootStack = testStack({ stackName: 'RootStack', template: { Resources: { - FirstLevelRootStack: { + FirstLevelNestedStack: { Type: 'AWS::CloudFormation::Stack', Properties: { TemplateURL: 'https://www.magic-url.com', @@ -866,8 +885,8 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot }, }); - const firstLevelRootStack = testStack({ - stackName: 'FirstLevelRootStack', + const oldFirstLevelNestedStack = testStack({ + stackName: 'FirstLevelNestedStack', template: { Resources: { NestedLambdaStack: { @@ -925,7 +944,7 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot }, }, Metadata: { - 'aws:asset:path': 'old-path', + 'aws:asset:path': 'old-lambda-path', }, }, }); @@ -936,40 +955,39 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot Outputs: { NestedOutput: { Value: 's3-key-value-from-output' }, }, - Metadata: { - 'aws:asset:path': 'old-path', - }, }, }); - setup.addTemplateToCloudFormationLookupMock(rootStack); - setup.addTemplateToCloudFormationLookupMock(firstLevelRootStack); + setup.addTemplateToCloudFormationLookupMock(oldRootStack); + setup.addTemplateToCloudFormationLookupMock(oldFirstLevelNestedStack); setup.addTemplateToCloudFormationLookupMock(nestedLambdaStack); setup.addTemplateToCloudFormationLookupMock(nestedSiblingStack); - setup.pushNestedStackResourceSummaries('RootStack', - setup.stackSummaryOf('FirstLevelRootStack', 'AWS::CloudFormation::Stack', - 'arn:aws:cloudformation:bermuda-triangle-1337:123456789012:stack/FirstLevelRootStack/abcd', + setup.pushNestedStackResourceSummaries(oldRootStack.stackName, + setup.stackSummaryOf(oldFirstLevelNestedStack.stackName, 'AWS::CloudFormation::Stack', + `arn:aws:cloudformation:bermuda-triangle-1337:123456789012:stack/${oldFirstLevelNestedStack.stackName}/abcd`, ), ); - - setup.pushNestedStackResourceSummaries('FirstLevelRootStack', - setup.stackSummaryOf('NestedLambdaStack', 'AWS::CloudFormation::Stack', - 'arn:aws:cloudformation:bermuda-triangle-1337:123456789012:stack/NestedLambdaStack/abcd', + setup.pushNestedStackResourceSummaries(oldFirstLevelNestedStack.stackName, + setup.stackSummaryOf(nestedLambdaStack.stackName, 'AWS::CloudFormation::Stack', + `arn:aws:cloudformation:bermuda-triangle-1337:123456789012:stack/${nestedLambdaStack.stackName}/abcd`, ), - setup.stackSummaryOf('NestedSiblingStack', 'AWS::CloudFormation::Stack', - 'arn:aws:cloudformation:bermuda-triangle-1337:123456789012:stack/NestedSiblingStack/abcd', + setup.stackSummaryOf(nestedSiblingStack.stackName, 'AWS::CloudFormation::Stack', + `arn:aws:cloudformation:bermuda-triangle-1337:123456789012:stack/${nestedSiblingStack.stackName}/abcd`, ), ); - setup.pushNestedStackResourceSummaries('NestedLambdaStack', + setup.pushNestedStackResourceSummaries(nestedLambdaStack.stackName, setup.stackSummaryOf('Func', 'AWS::Lambda::Function', 'nested-lambda-function'), ); - setup.pushNestedStackResourceSummaries('NestedSiblingStack'); - - const cdkStackArtifact = testStack({ stackName: 'RootStack', template: rootStack.template }); + setup.pushNestedStackResourceSummaries(nestedSiblingStack.stackName); + oldRootStack.template.Resources.FirstLevelNestedStack.Properties.TemplateURL = 'https://www.amazon.com'; + oldFirstLevelNestedStack.template.Resources.NestedLambdaStack.Properties.TemplateURL = 'https://www.amazon.com'; + oldFirstLevelNestedStack.template.Resources.NestedSiblingStack.Properties.TemplateURL = 'https://www.amazon.com'; + const newRootStack = testStack({ stackName: oldRootStack.stackName, template: oldRootStack.template }); + testStack({ stackName: oldFirstLevelNestedStack.stackName, template: oldFirstLevelNestedStack.template }); // WHEN - const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact, { + const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, newRootStack, { S3BucketParam: 'new-bucket', }); @@ -1046,7 +1064,7 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot FunctionName: 'my-function', }, Metadata: { - 'aws:asset:path': 'old-path', + 'aws:asset:path': 'old-lambda-path', }, }, }, @@ -1059,10 +1077,10 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot ), ); - const cdkStackArtifact = testStack({ stackName: 'LambdaRoot', template: rootStack.template }); + rootStack.template.Resources.NestedStack.Properties.TemplateURL = 'https://www.amazon.com'; // WHEN - const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact); + const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, rootStack); // THEN expect(deployStackResult).not.toBeUndefined(); @@ -1129,9 +1147,9 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot }, }, }); - setup.addTemplateToCloudFormationLookupMock(rootStack); - setup.addTemplateToCloudFormationLookupMock(testStack({ + + const childStack = testStack({ stackName: 'ChildStack', template: { Resources: { @@ -1145,7 +1163,7 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot FunctionName: 'my-function', }, Metadata: { - 'aws:asset:path': 'old-path', + 'aws:asset:path': 'old-lambda-path', }, }, GrandChildStack: { @@ -1159,7 +1177,9 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot }, }, }, - })); + }); + setup.addTemplateToCloudFormationLookupMock(childStack); + setup.addTemplateToCloudFormationLookupMock(testStack({ stackName: 'GrandChildStack', template: { @@ -1174,7 +1194,7 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot FunctionName: 'my-function', }, Metadata: { - 'aws:asset:path': 'old-path', + 'aws:asset:path': 'old-lambda-path', }, }, }, @@ -1192,10 +1212,12 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot 'arn:aws:cloudformation:bermuda-triangle-1337:123456789012:stack/GrandChildStack/abcd', ), ); - const cdkStackArtifact = testStack({ stackName: 'LambdaRoot', template: rootStack.template }); + + rootStack.template.Resources.ChildStack.Properties.TemplateURL = 'https://www.amazon.com'; + childStack.template.Resources.GrandChildStack.Properties.TemplateURL = 'https://www.amazon.com'; // WHEN - const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact, { + const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, rootStack, { GrandChildS3BucketParam: 'child-bucket-param-value', GrandChildS3KeyParam: 'child-key-param-value', ChildS3BucketParam: 'bucket-param-value', @@ -1232,7 +1254,7 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot NestedStack: { Type: 'AWS::CloudFormation::Stack', Properties: { - TemplateURL: 'https://www.magic-url.com', + TemplateURL: 'https://www.amazoff.com', }, Metadata: { 'aws:asset:path': 'one-lambda-version-stack.nested.template.json', @@ -1273,10 +1295,9 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot ), ); - const cdkStackArtifact = testStack({ stackName: 'LambdaRoot', template: rootStack.template }); - // WHEN - const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact); + rootStack.template.Resources.NestedStack.Properties.TemplateURL = 'https://www.amazon.com'; + const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, rootStack); // THEN expect(deployStackResult).not.toBeUndefined(); diff --git a/packages/aws-cdk/test/diff.test.ts b/packages/aws-cdk/test/diff.test.ts index a7b5905e12f87..8dd11bb0b5fce 100644 --- a/packages/aws-cdk/test/diff.test.ts +++ b/packages/aws-cdk/test/diff.test.ts @@ -7,11 +7,119 @@ import { instanceMockFrom, MockCloudExecutable } from './util'; import { Deployments } from '../lib/api/deployments'; import { CdkToolkit } from '../lib/cdk-toolkit'; import * as cfn from '../lib/api/util/cloudformation'; +import { NestedStackTemplates } from '../lib/api/nested-stack-helpers'; +import * as fs from 'fs'; let cloudExecutable: MockCloudExecutable; let cloudFormation: jest.Mocked<Deployments>; let toolkit: CdkToolkit; +describe('imports', () => { + beforeEach(() => { + const outputToJson = { + '//': 'This file is generated by cdk migrate. It will be automatically deleted after the first successful deployment of this app to the environment of the original resources.', + 'Source': 'localfile', + 'Resources': [], + }; + fs.writeFileSync('migrate.json', JSON.stringify(outputToJson, null, 2)); + + jest.spyOn(cfn, 'createDiffChangeSet').mockImplementationOnce(async () => { + return { + Changes: [ + { + ResourceChange: { + Action: 'Import', + LogicalResourceId: 'Queue', + }, + }, + { + ResourceChange: { + Action: 'Import', + LogicalResourceId: 'Bucket', + }, + }, + { + ResourceChange: { + Action: 'Import', + LogicalResourceId: 'Queue2', + }, + }, + ], + }; + }); + cloudExecutable = new MockCloudExecutable({ + stacks: [{ + stackName: 'A', + template: { + Resources: { + Queue: { + Type: 'AWS::SQS::Queue', + }, + Queue2: { + Type: 'AWS::SQS::Queue', + }, + Bucket: { + Type: 'AWS::S3::Bucket', + }, + }, + }, + }], + }); + + cloudFormation = instanceMockFrom(Deployments); + + toolkit = new CdkToolkit({ + cloudExecutable, + deployments: cloudFormation, + configuration: cloudExecutable.configuration, + sdkProvider: cloudExecutable.sdkProvider, + }); + + // Default implementations + cloudFormation.readCurrentTemplateWithNestedStacks.mockImplementation((_stackArtifact: CloudFormationStackArtifact) => { + return Promise.resolve({ + deployedRootTemplate: {}, + nestedStacks: {}, + }); + }); + cloudFormation.deployStack.mockImplementation((options) => Promise.resolve({ + noOp: true, + outputs: {}, + stackArn: '', + stackArtifact: options.stack, + })); + }); + + afterEach(() => { + fs.rmSync('migrate.json'); + }); + + test('imports', async () => { + // GIVEN + const buffer = new StringWritable(); + + // WHEN + const exitCode = await toolkit.diff({ + stackNames: ['A'], + stream: buffer, + changeSet: true, + }); + + // THEN + const plainTextOutput = buffer.data.replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, ''); + expect(plainTextOutput).toContain(`Stack A +Parameters and rules created during migration do not affect resource configuration. +Resources +[←] AWS::SQS::Queue Queue import +[←] AWS::SQS::Queue Queue2 import +[←] AWS::S3::Bucket Bucket import +`); + + expect(buffer.data.trim()).toContain('✨ Number of stacks with differences: 1'); + expect(exitCode).toBe(0); + }); +}); + describe('non-nested stacks', () => { beforeEach(() => { cloudExecutable = new MockCloudExecutable({ @@ -56,13 +164,13 @@ describe('non-nested stacks', () => { cloudFormation.readCurrentTemplateWithNestedStacks.mockImplementation((stackArtifact: CloudFormationStackArtifact) => { if (stackArtifact.stackName === 'D') { return Promise.resolve({ - deployedTemplate: { resource: 'D' }, - nestedStackCount: 0, + deployedRootTemplate: { resource: 'D' }, + nestedStacks: {}, }); } return Promise.resolve({ - deployedTemplate: {}, - nestedStackCount: 0, + deployedRootTemplate: {}, + nestedStacks: {}, }); }); cloudFormation.deployStack.mockImplementation((options) => Promise.resolve({ @@ -223,48 +331,82 @@ describe('nested stacks', () => { stackArtifact.template.Resources = { AdditionChild: { Type: 'AWS::CloudFormation::Stack', - Resources: { - SomeResource: { - Type: 'AWS::Something', - Properties: { - Prop: 'added-value', - }, - }, + Properties: { + TemplateURL: 'addition-child-url-old', }, }, DeletionChild: { Type: 'AWS::CloudFormation::Stack', - Resources: { - SomeResource: { - Type: 'AWS::Something', - }, + Properties: { + TemplateURL: 'deletion-child-url-old', }, }, ChangedChild: { Type: 'AWS::CloudFormation::Stack', - Resources: { - SomeResource: { - Type: 'AWS::Something', - Properties: { - Prop: 'new-value', - }, - }, + Properties: { + TemplateURL: 'changed-child-url-old', + }, + }, + UnchangedChild: { + Type: 'AWS::CloudFormation::Stack', + Properties: { + TemplateURL: 'changed-child-url-constant', }, }, }; return Promise.resolve({ - deployedTemplate: { + deployedRootTemplate: { Resources: { AdditionChild: { Type: 'AWS::CloudFormation::Stack', + Properties: { + TemplateURL: 'addition-child-url-new', + }, + }, + DeletionChild: { + Type: 'AWS::CloudFormation::Stack', + Properties: { + TemplateURL: 'deletion-child-url-new', + }, + }, + ChangedChild: { + Type: 'AWS::CloudFormation::Stack', + Properties: { + TemplateURL: 'changed-child-url-new', + }, + }, + UnchangedChild: { + Type: 'AWS::CloudFormation::Stack', + Properties: { + TemplateURL: 'changed-child-url-constant', + }, + }, + }, + }, + nestedStacks: { + AdditionChild: { + deployedTemplate: { Resources: { SomeResource: { Type: 'AWS::Something', }, }, }, - DeletionChild: { - Type: 'AWS::CloudFormation::Stack', + generatedTemplate: { + Resources: { + SomeResource: { + Type: 'AWS::Something', + Properties: { + Prop: 'added-value', + }, + }, + }, + }, + nestedStackTemplates: {}, + physicalName: 'AdditionChild', + }, + DeletionChild: { + deployedTemplate: { Resources: { SomeResource: { Type: 'AWS::Something', @@ -274,8 +416,18 @@ describe('nested stacks', () => { }, }, }, - ChangedChild: { - Type: 'AWS::CloudFormation::Stack', + generatedTemplate: { + Resources: { + SomeResource: { + Type: 'AWS::Something', + }, + }, + }, + nestedStackTemplates: {}, + physicalName: 'DeletionChild', + }, + ChangedChild: { + deployedTemplate: { Resources: { SomeResource: { Type: 'AWS::Something', @@ -285,19 +437,61 @@ describe('nested stacks', () => { }, }, }, + generatedTemplate: { + Resources: { + SomeResource: { + Type: 'AWS::Something', + Properties: { + Prop: 'new-value', + }, + }, + }, + }, + nestedStackTemplates: {}, + physicalName: 'ChangedChild', + }, + newChild: { + deployedTemplate: {}, + generatedTemplate: { + Resources: { + SomeResource: { + Type: 'AWS::Something', + Properties: { + Prop: 'new-value', + }, + }, + }, + }, + nestedStackTemplates: { + newGrandChild: { + deployedTemplate: {}, + generatedTemplate: { + Resources: { + SomeResource: { + Type: 'AWS::Something', + Properties: { + Prop: 'new-value', + }, + }, + }, + }, + physicalName: undefined, + nestedStackTemplates: {}, + } as NestedStackTemplates, + }, + physicalName: undefined, }, }, - nestedStackCount: 3, }); } return Promise.resolve({ - deployedTemplate: {}, - nestedStackCount: 0, + deployedRootTemplate: {}, + nestedStacks: {}, }); }); }); - test('diff can diff nested stacks', async () => { + test('diff can diff nested stacks and display the nested stack logical ID if has not been deployed or otherwise has no physical name', async () => { // GIVEN const buffer = new StringWritable(); @@ -305,6 +499,7 @@ describe('nested stacks', () => { const exitCode = await toolkit.diff({ stackNames: ['Parent'], stream: buffer, + changeSet: false, }); // THEN @@ -313,23 +508,47 @@ describe('nested stacks', () => { expect(plainTextOutput.trim()).toEqual(`Stack Parent Resources [~] AWS::CloudFormation::Stack AdditionChild - └─ [~] Resources - └─ [~] .SomeResource: - └─ [+] Added: .Properties + └─ [~] TemplateURL + ├─ [-] addition-child-url-new + └─ [+] addition-child-url-old [~] AWS::CloudFormation::Stack DeletionChild - └─ [~] Resources - └─ [~] .SomeResource: - └─ [-] Removed: .Properties + └─ [~] TemplateURL + ├─ [-] deletion-child-url-new + └─ [+] deletion-child-url-old [~] AWS::CloudFormation::Stack ChangedChild - └─ [~] Resources - └─ [~] .SomeResource: - └─ [~] .Properties: - └─ [~] .Prop: - ├─ [-] old-value - └─ [+] new-value + └─ [~] TemplateURL + ├─ [-] changed-child-url-new + └─ [+] changed-child-url-old + +Stack AdditionChild +Resources +[~] AWS::Something SomeResource + └─ [+] Prop + └─ added-value + +Stack DeletionChild +Resources +[~] AWS::Something SomeResource + └─ [-] Prop + └─ value-to-be-removed + +Stack ChangedChild +Resources +[~] AWS::Something SomeResource + └─ [~] Prop + ├─ [-] old-value + └─ [+] new-value +Stack newChild +Resources +[+] AWS::Something SomeResource + +Stack newGrandChild +Resources +[+] AWS::Something SomeResource -✨ Number of stacks with differences: 4`); + +✨ Number of stacks with differences: 6`); expect(exitCode).toBe(0); }); @@ -352,23 +571,47 @@ Resources expect(plainTextOutput.trim()).toEqual(`Stack Parent Resources [~] AWS::CloudFormation::Stack AdditionChild - └─ [~] Resources - └─ [~] .SomeResource: - └─ [+] Added: .Properties + └─ [~] TemplateURL + ├─ [-] addition-child-url-new + └─ [+] addition-child-url-old [~] AWS::CloudFormation::Stack DeletionChild - └─ [~] Resources - └─ [~] .SomeResource: - └─ [-] Removed: .Properties + └─ [~] TemplateURL + ├─ [-] deletion-child-url-new + └─ [+] deletion-child-url-old [~] AWS::CloudFormation::Stack ChangedChild - └─ [~] Resources - └─ [~] .SomeResource: - └─ [~] .Properties: - └─ [~] .Prop: - ├─ [-] old-value - └─ [+] new-value + └─ [~] TemplateURL + ├─ [-] changed-child-url-new + └─ [+] changed-child-url-old + +Stack AdditionChild +Resources +[~] AWS::Something SomeResource + └─ [+] Prop + └─ added-value + +Stack DeletionChild +Resources +[~] AWS::Something SomeResource + └─ [-] Prop + └─ value-to-be-removed + +Stack ChangedChild +Resources +[~] AWS::Something SomeResource + └─ [~] Prop + ├─ [-] old-value + └─ [+] new-value + +Stack newChild +Resources +[+] AWS::Something SomeResource + +Stack newGrandChild +Resources +[+] AWS::Something SomeResource -✨ Number of stacks with differences: 4`); +✨ Number of stacks with differences: 6`); expect(exitCode).toBe(0); expect(changeSetSpy).not.toHaveBeenCalled(); diff --git a/packages/aws-cdk/test/nested-stack-templates/one-stack-with-two-nested-stacks-stack.template.json b/packages/aws-cdk/test/nested-stack-templates/one-stack-with-two-nested-stacks-stack.template.json index bdf9ed5cb2c51..e0bcd1cb3ab43 100644 --- a/packages/aws-cdk/test/nested-stack-templates/one-stack-with-two-nested-stacks-stack.template.json +++ b/packages/aws-cdk/test/nested-stack-templates/one-stack-with-two-nested-stacks-stack.template.json @@ -3,7 +3,7 @@ "NestedLambdaStack": { "Type": "AWS::CloudFormation::Stack", "Properties": { - "TemplateURL": "https://www.magic-url.com", + "TemplateURL": "https://www.amazon.com", "Parameters": { "referenceToS3BucketParam": { "Ref": "S3BucketParam" @@ -23,7 +23,7 @@ "NestedSiblingStack": { "Type": "AWS::CloudFormation::Stack", "Properties": { - "TemplateURL": "https://www.magic-url.com" + "TemplateURL": "https://www.amazon.com" }, "Metadata": { "aws:asset:path": "one-output-stack.nested.template.json" @@ -32,7 +32,8 @@ }, "Parameters": { "S3BucketParam": { - "Type": "String" + "Type": "String", + "Description": "S3 bucket for asset" } } } diff --git a/packages/aws-cdk/test/nested-stack-templates/one-unnamed-lambda-two-stacks-stack.nested.template.json b/packages/aws-cdk/test/nested-stack-templates/one-unnamed-lambda-two-stacks-stack.nested.template.json index 2ac6271955cf4..a1adcd776d469 100644 --- a/packages/aws-cdk/test/nested-stack-templates/one-unnamed-lambda-two-stacks-stack.nested.template.json +++ b/packages/aws-cdk/test/nested-stack-templates/one-unnamed-lambda-two-stacks-stack.nested.template.json @@ -15,7 +15,7 @@ "GrandChildStackA": { "Type": "AWS::CloudFormation::Stack", "Properties": { - "TemplateURL": "https://www.magic-url.com" + "TemplateURL": "https://www.amazon.com" }, "Metadata": { "aws:asset:path": "one-unnamed-lambda-stack.nested.template.json" @@ -24,7 +24,7 @@ "GrandChildStackB": { "Type": "AWS::CloudFormation::Stack", "Properties": { - "TemplateURL": "https://www.magic-url.com" + "TemplateURL": "https://www.amazon.com" }, "Metadata": { "aws:asset:path": "one-unnamed-lambda-stack.nested.template.json" diff --git a/packages/aws-cdk/test/util.ts b/packages/aws-cdk/test/util.ts index ecc262e948153..879d6572f369b 100644 --- a/packages/aws-cdk/test/util.ts +++ b/packages/aws-cdk/test/util.ts @@ -37,9 +37,9 @@ export class MockCloudExecutable extends CloudExecutable { public readonly configuration: Configuration; public readonly sdkProvider: MockSdkProvider; - constructor(assembly: TestAssembly) { + constructor(assembly: TestAssembly, sdkProviderArg?: MockSdkProvider) { const configuration = new Configuration(); - const sdkProvider = new MockSdkProvider(); + const sdkProvider = sdkProviderArg ?? new MockSdkProvider(); super({ configuration, From 6fef789e14a0f53317da9ca4f319950d33f86ed1 Mon Sep 17 00:00:00 2001 From: Michael Sambol <sambol.michael@gmail.com> Date: Thu, 14 Mar 2024 09:01:26 -0600 Subject: [PATCH 4/4] feat(rds): proxy for mariadb (#29412) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MariaDB is grouped with MySQL, hence adding `MYSQL` as the engine family for MariaDB. <img width="749" alt="Screenshot 2024-03-08 at 12 02 36 PM" src="https://github.com/aws/aws-cdk/assets/3310356/382d5561-6115-4bc1-a7e7-fb4deda57c73"> Closes #29402. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../aws-cdk-rds-proxy-mariadb.assets.json | 19 + .../aws-cdk-rds-proxy-mariadb.template.json | 816 ++++++++++ .../integ.proxy-mariadb.js.snapshot/cdk.out | 1 + ...efaultTestDeployAssert9D9B7FF3.assets.json | 19 + ...aultTestDeployAssert9D9B7FF3.template.json | 36 + .../integ.json | 20 + .../manifest.json | 317 ++++ .../integ.proxy-mariadb.js.snapshot/tree.json | 1329 +++++++++++++++++ .../test/aws-rds/test/integ.proxy-mariadb.ts | 45 + packages/aws-cdk-lib/aws-rds/README.md | 4 +- .../aws-rds/lib/instance-engine.ts | 1 + .../aws-cdk-lib/aws-rds/test/proxy.test.ts | 67 +- 12 files changed, 2670 insertions(+), 4 deletions(-) create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.proxy-mariadb.js.snapshot/aws-cdk-rds-proxy-mariadb.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.proxy-mariadb.js.snapshot/aws-cdk-rds-proxy-mariadb.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.proxy-mariadb.js.snapshot/cdk.out create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.proxy-mariadb.js.snapshot/databaseproxymariadbintegtestDefaultTestDeployAssert9D9B7FF3.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.proxy-mariadb.js.snapshot/databaseproxymariadbintegtestDefaultTestDeployAssert9D9B7FF3.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.proxy-mariadb.js.snapshot/integ.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.proxy-mariadb.js.snapshot/manifest.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.proxy-mariadb.js.snapshot/tree.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.proxy-mariadb.ts diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.proxy-mariadb.js.snapshot/aws-cdk-rds-proxy-mariadb.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.proxy-mariadb.js.snapshot/aws-cdk-rds-proxy-mariadb.assets.json new file mode 100644 index 0000000000000..09256e0b2b86d --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.proxy-mariadb.js.snapshot/aws-cdk-rds-proxy-mariadb.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "f0fff306f7e137bbf1a002fa55210d252f5846dc8e753143422b180e68469441": { + "source": { + "path": "aws-cdk-rds-proxy-mariadb.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "f0fff306f7e137bbf1a002fa55210d252f5846dc8e753143422b180e68469441.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.proxy-mariadb.js.snapshot/aws-cdk-rds-proxy-mariadb.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.proxy-mariadb.js.snapshot/aws-cdk-rds-proxy-mariadb.template.json new file mode 100644 index 0000000000000..df22dfd1df50b --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.proxy-mariadb.js.snapshot/aws-cdk-rds-proxy-mariadb.template.json @@ -0,0 +1,816 @@ +{ + "Resources": { + "vpcA2121C38": { + "Type": "AWS::EC2::VPC", + "Properties": { + "CidrBlock": "10.0.0.0/16", + "EnableDnsHostnames": true, + "EnableDnsSupport": true, + "InstanceTenancy": "default", + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-rds-proxy-mariadb/vpc" + } + ] + } + }, + "vpcPublicSubnet1Subnet2E65531E": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "AvailabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "" + } + ] + }, + "CidrBlock": "10.0.0.0/18", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "Name", + "Value": "aws-cdk-rds-proxy-mariadb/vpc/PublicSubnet1" + } + ], + "VpcId": { + "Ref": "vpcA2121C38" + } + } + }, + "vpcPublicSubnet1RouteTable48A2DF9B": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-rds-proxy-mariadb/vpc/PublicSubnet1" + } + ], + "VpcId": { + "Ref": "vpcA2121C38" + } + } + }, + "vpcPublicSubnet1RouteTableAssociation5D3F4579": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "vpcPublicSubnet1RouteTable48A2DF9B" + }, + "SubnetId": { + "Ref": "vpcPublicSubnet1Subnet2E65531E" + } + } + }, + "vpcPublicSubnet1DefaultRoute10708846": { + "Type": "AWS::EC2::Route", + "Properties": { + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "vpcIGWE57CBDCA" + }, + "RouteTableId": { + "Ref": "vpcPublicSubnet1RouteTable48A2DF9B" + } + }, + "DependsOn": [ + "vpcVPCGW7984C166" + ] + }, + "vpcPublicSubnet1EIPDA49DCBE": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc", + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-rds-proxy-mariadb/vpc/PublicSubnet1" + } + ] + } + }, + "vpcPublicSubnet1NATGateway9C16659E": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "AllocationId": { + "Fn::GetAtt": [ + "vpcPublicSubnet1EIPDA49DCBE", + "AllocationId" + ] + }, + "SubnetId": { + "Ref": "vpcPublicSubnet1Subnet2E65531E" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-rds-proxy-mariadb/vpc/PublicSubnet1" + } + ] + }, + "DependsOn": [ + "vpcPublicSubnet1DefaultRoute10708846", + "vpcPublicSubnet1RouteTableAssociation5D3F4579" + ] + }, + "vpcPublicSubnet2Subnet009B674F": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "AvailabilityZone": { + "Fn::Select": [ + 1, + { + "Fn::GetAZs": "" + } + ] + }, + "CidrBlock": "10.0.64.0/18", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "Name", + "Value": "aws-cdk-rds-proxy-mariadb/vpc/PublicSubnet2" + } + ], + "VpcId": { + "Ref": "vpcA2121C38" + } + } + }, + "vpcPublicSubnet2RouteTableEB40D4CB": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-rds-proxy-mariadb/vpc/PublicSubnet2" + } + ], + "VpcId": { + "Ref": "vpcA2121C38" + } + } + }, + "vpcPublicSubnet2RouteTableAssociation21F81B59": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "vpcPublicSubnet2RouteTableEB40D4CB" + }, + "SubnetId": { + "Ref": "vpcPublicSubnet2Subnet009B674F" + } + } + }, + "vpcPublicSubnet2DefaultRouteA1EC0F60": { + "Type": "AWS::EC2::Route", + "Properties": { + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "vpcIGWE57CBDCA" + }, + "RouteTableId": { + "Ref": "vpcPublicSubnet2RouteTableEB40D4CB" + } + }, + "DependsOn": [ + "vpcVPCGW7984C166" + ] + }, + "vpcPublicSubnet2EIP9B3743B1": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc", + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-rds-proxy-mariadb/vpc/PublicSubnet2" + } + ] + } + }, + "vpcPublicSubnet2NATGateway9B8AE11A": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "AllocationId": { + "Fn::GetAtt": [ + "vpcPublicSubnet2EIP9B3743B1", + "AllocationId" + ] + }, + "SubnetId": { + "Ref": "vpcPublicSubnet2Subnet009B674F" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-rds-proxy-mariadb/vpc/PublicSubnet2" + } + ] + }, + "DependsOn": [ + "vpcPublicSubnet2DefaultRouteA1EC0F60", + "vpcPublicSubnet2RouteTableAssociation21F81B59" + ] + }, + "vpcPrivateSubnet1Subnet934893E8": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "AvailabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "" + } + ] + }, + "CidrBlock": "10.0.128.0/18", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + }, + { + "Key": "Name", + "Value": "aws-cdk-rds-proxy-mariadb/vpc/PrivateSubnet1" + } + ], + "VpcId": { + "Ref": "vpcA2121C38" + } + } + }, + "vpcPrivateSubnet1RouteTableB41A48CC": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-rds-proxy-mariadb/vpc/PrivateSubnet1" + } + ], + "VpcId": { + "Ref": "vpcA2121C38" + } + } + }, + "vpcPrivateSubnet1RouteTableAssociation67945127": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "vpcPrivateSubnet1RouteTableB41A48CC" + }, + "SubnetId": { + "Ref": "vpcPrivateSubnet1Subnet934893E8" + } + } + }, + "vpcPrivateSubnet1DefaultRoute1AA8E2E5": { + "Type": "AWS::EC2::Route", + "Properties": { + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "vpcPublicSubnet1NATGateway9C16659E" + }, + "RouteTableId": { + "Ref": "vpcPrivateSubnet1RouteTableB41A48CC" + } + } + }, + "vpcPrivateSubnet2Subnet7031C2BA": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "AvailabilityZone": { + "Fn::Select": [ + 1, + { + "Fn::GetAZs": "" + } + ] + }, + "CidrBlock": "10.0.192.0/18", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + }, + { + "Key": "Name", + "Value": "aws-cdk-rds-proxy-mariadb/vpc/PrivateSubnet2" + } + ], + "VpcId": { + "Ref": "vpcA2121C38" + } + } + }, + "vpcPrivateSubnet2RouteTable7280F23E": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-rds-proxy-mariadb/vpc/PrivateSubnet2" + } + ], + "VpcId": { + "Ref": "vpcA2121C38" + } + } + }, + "vpcPrivateSubnet2RouteTableAssociation007E94D3": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "vpcPrivateSubnet2RouteTable7280F23E" + }, + "SubnetId": { + "Ref": "vpcPrivateSubnet2Subnet7031C2BA" + } + } + }, + "vpcPrivateSubnet2DefaultRouteB0E07F99": { + "Type": "AWS::EC2::Route", + "Properties": { + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "vpcPublicSubnet2NATGateway9B8AE11A" + }, + "RouteTableId": { + "Ref": "vpcPrivateSubnet2RouteTable7280F23E" + } + } + }, + "vpcIGWE57CBDCA": { + "Type": "AWS::EC2::InternetGateway", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-rds-proxy-mariadb/vpc" + } + ] + } + }, + "vpcVPCGW7984C166": { + "Type": "AWS::EC2::VPCGatewayAttachment", + "Properties": { + "InternetGatewayId": { + "Ref": "vpcIGWE57CBDCA" + }, + "VpcId": { + "Ref": "vpcA2121C38" + } + } + }, + "SecretEncryptionKey40C82244": { + "Type": "AWS::KMS::Key", + "Properties": { + "KeyPolicy": { + "Statement": [ + { + "Action": "kms:*", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + }, + { + "Action": [ + "kms:CreateGrant", + "kms:Decrypt", + "kms:DescribeKey", + "kms:Encrypt", + "kms:GenerateDataKey*", + "kms:ReEncrypt*" + ], + "Condition": { + "StringEquals": { + "kms:ViaService": { + "Fn::Join": [ + "", + [ + "secretsmanager.", + { + "Ref": "AWS::Region" + }, + ".amazonaws.com" + ] + ] + } + } + }, + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + }, + { + "Action": "kms:Decrypt", + "Condition": { + "StringEquals": { + "kms:ViaService": { + "Fn::Join": [ + "", + [ + "secretsmanager.", + { + "Ref": "AWS::Region" + }, + ".amazonaws.com" + ] + ] + } + } + }, + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "mariaDBProxyIAMRoleB91DE271", + "Arn" + ] + } + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "mariaDBInstanceSubnetGroupA17E5651": { + "Type": "AWS::RDS::DBSubnetGroup", + "Properties": { + "DBSubnetGroupDescription": "Subnet group for mariaDBInstance database", + "SubnetIds": [ + { + "Ref": "vpcPrivateSubnet1Subnet934893E8" + }, + { + "Ref": "vpcPrivateSubnet2Subnet7031C2BA" + } + ] + } + }, + "mariaDBInstanceSecurityGroup1C05EFED": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "Security group for mariaDBInstance database", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "VpcId": { + "Ref": "vpcA2121C38" + } + } + }, + "mariaDBInstanceSecurityGroupfromawscdkrdsproxymariadbmariaDBProxyProxySecurityGroup4F55583FIndirectPort44A97795": { + "Type": "AWS::EC2::SecurityGroupIngress", + "Properties": { + "Description": "Allow connections to the database Instance from the Proxy", + "FromPort": { + "Fn::GetAtt": [ + "mariaDBInstance7DFDB9B1", + "Endpoint.Port" + ] + }, + "GroupId": { + "Fn::GetAtt": [ + "mariaDBInstanceSecurityGroup1C05EFED", + "GroupId" + ] + }, + "IpProtocol": "tcp", + "SourceSecurityGroupId": { + "Fn::GetAtt": [ + "mariaDBProxyProxySecurityGroupFD730755", + "GroupId" + ] + }, + "ToPort": { + "Fn::GetAtt": [ + "mariaDBInstance7DFDB9B1", + "Endpoint.Port" + ] + } + } + }, + "mariaDBInstanceSecretED322D85": { + "Type": "AWS::SecretsManager::Secret", + "Properties": { + "Description": { + "Fn::Join": [ + "", + [ + "Generated by the CDK for stack: ", + { + "Ref": "AWS::StackName" + } + ] + ] + }, + "GenerateSecretString": { + "ExcludeCharacters": "\"@/\\", + "GenerateStringKey": "password", + "PasswordLength": 30, + "SecretStringTemplate": "{\"username\":\"master\"}" + }, + "KmsKeyId": { + "Fn::GetAtt": [ + "SecretEncryptionKey40C82244", + "Arn" + ] + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "mariaDBInstanceSecretAttachment900E3C52": { + "Type": "AWS::SecretsManager::SecretTargetAttachment", + "Properties": { + "SecretId": { + "Ref": "mariaDBInstanceSecretED322D85" + }, + "TargetId": { + "Ref": "mariaDBInstance7DFDB9B1" + }, + "TargetType": "AWS::RDS::DBInstance" + } + }, + "mariaDBInstance7DFDB9B1": { + "Type": "AWS::RDS::DBInstance", + "Properties": { + "AllocatedStorage": "100", + "CopyTagsToSnapshot": true, + "DBInstanceClass": "db.t3.medium", + "DBSubnetGroupName": { + "Ref": "mariaDBInstanceSubnetGroupA17E5651" + }, + "Engine": "mariadb", + "EngineVersion": "10.6.16", + "MasterUserPassword": { + "Fn::Join": [ + "", + [ + "{{resolve:secretsmanager:", + { + "Ref": "mariaDBInstanceSecretED322D85" + }, + ":SecretString:password::}}" + ] + ] + }, + "MasterUsername": { + "Fn::Join": [ + "", + [ + "{{resolve:secretsmanager:", + { + "Ref": "mariaDBInstanceSecretED322D85" + }, + ":SecretString:username::}}" + ] + ] + }, + "StorageType": "gp2", + "VPCSecurityGroups": [ + { + "Fn::GetAtt": [ + "mariaDBInstanceSecurityGroup1C05EFED", + "GroupId" + ] + } + ] + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "mariaDBProxyIAMRoleB91DE271": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "rds.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "mariaDBProxyIAMRoleDefaultPolicyF4BE519B": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "secretsmanager:DescribeSecret", + "secretsmanager:GetSecretValue" + ], + "Effect": "Allow", + "Resource": { + "Ref": "mariaDBInstanceSecretAttachment900E3C52" + } + }, + { + "Action": "kms:Decrypt", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "SecretEncryptionKey40C82244", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "mariaDBProxyIAMRoleDefaultPolicyF4BE519B", + "Roles": [ + { + "Ref": "mariaDBProxyIAMRoleB91DE271" + } + ] + } + }, + "mariaDBProxyProxySecurityGroupFD730755": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "SecurityGroup for Database Proxy", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "VpcId": { + "Ref": "vpcA2121C38" + } + } + }, + "mariaDBProxy23E8B2BB": { + "Type": "AWS::RDS::DBProxy", + "Properties": { + "Auth": [ + { + "AuthScheme": "SECRETS", + "IAMAuth": "DISABLED", + "SecretArn": { + "Ref": "mariaDBInstanceSecretAttachment900E3C52" + } + } + ], + "DBProxyName": "awscdkrdsproxymariadbmariaDBProxyB0A701B2", + "EngineFamily": "MYSQL", + "RequireTLS": true, + "RoleArn": { + "Fn::GetAtt": [ + "mariaDBProxyIAMRoleB91DE271", + "Arn" + ] + }, + "VpcSecurityGroupIds": [ + { + "Fn::GetAtt": [ + "mariaDBProxyProxySecurityGroupFD730755", + "GroupId" + ] + } + ], + "VpcSubnetIds": [ + { + "Ref": "vpcPrivateSubnet1Subnet934893E8" + }, + { + "Ref": "vpcPrivateSubnet2Subnet7031C2BA" + } + ] + } + }, + "mariaDBProxyProxyTargetGroup960ACB39": { + "Type": "AWS::RDS::DBProxyTargetGroup", + "Properties": { + "ConnectionPoolConfigurationInfo": { + "ConnectionBorrowTimeout": 30, + "MaxConnectionsPercent": 50 + }, + "DBInstanceIdentifiers": [ + { + "Ref": "mariaDBInstance7DFDB9B1" + } + ], + "DBProxyName": { + "Ref": "mariaDBProxy23E8B2BB" + }, + "TargetGroupName": "default" + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value<String>", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.proxy-mariadb.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.proxy-mariadb.js.snapshot/cdk.out new file mode 100644 index 0000000000000..1f0068d32659a --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.proxy-mariadb.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"36.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.proxy-mariadb.js.snapshot/databaseproxymariadbintegtestDefaultTestDeployAssert9D9B7FF3.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.proxy-mariadb.js.snapshot/databaseproxymariadbintegtestDefaultTestDeployAssert9D9B7FF3.assets.json new file mode 100644 index 0000000000000..1d4ef8153edea --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.proxy-mariadb.js.snapshot/databaseproxymariadbintegtestDefaultTestDeployAssert9D9B7FF3.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "databaseproxymariadbintegtestDefaultTestDeployAssert9D9B7FF3.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.proxy-mariadb.js.snapshot/databaseproxymariadbintegtestDefaultTestDeployAssert9D9B7FF3.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.proxy-mariadb.js.snapshot/databaseproxymariadbintegtestDefaultTestDeployAssert9D9B7FF3.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.proxy-mariadb.js.snapshot/databaseproxymariadbintegtestDefaultTestDeployAssert9D9B7FF3.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value<String>", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.proxy-mariadb.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.proxy-mariadb.js.snapshot/integ.json new file mode 100644 index 0000000000000..227e6f88af581 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.proxy-mariadb.js.snapshot/integ.json @@ -0,0 +1,20 @@ +{ + "version": "36.0.0", + "testCases": { + "database-proxy-mariadb-integ-test/DefaultTest": { + "stacks": [ + "aws-cdk-rds-proxy-mariadb" + ], + "diffAssets": true, + "cdkCommandOptions": { + "deploy": { + "args": { + "rollback": true + } + } + }, + "assertionStack": "database-proxy-mariadb-integ-test/DefaultTest/DeployAssert", + "assertionStackName": "databaseproxymariadbintegtestDefaultTestDeployAssert9D9B7FF3" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.proxy-mariadb.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.proxy-mariadb.js.snapshot/manifest.json new file mode 100644 index 0000000000000..8b423ee7ee9ca --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.proxy-mariadb.js.snapshot/manifest.json @@ -0,0 +1,317 @@ +{ + "version": "36.0.0", + "artifacts": { + "aws-cdk-rds-proxy-mariadb.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "aws-cdk-rds-proxy-mariadb.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "aws-cdk-rds-proxy-mariadb": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "aws-cdk-rds-proxy-mariadb.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}/f0fff306f7e137bbf1a002fa55210d252f5846dc8e753143422b180e68469441.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "aws-cdk-rds-proxy-mariadb.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "aws-cdk-rds-proxy-mariadb.assets" + ], + "metadata": { + "/aws-cdk-rds-proxy-mariadb/vpc/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "vpcA2121C38" + } + ], + "/aws-cdk-rds-proxy-mariadb/vpc/PublicSubnet1/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "vpcPublicSubnet1Subnet2E65531E" + } + ], + "/aws-cdk-rds-proxy-mariadb/vpc/PublicSubnet1/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "vpcPublicSubnet1RouteTable48A2DF9B" + } + ], + "/aws-cdk-rds-proxy-mariadb/vpc/PublicSubnet1/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "vpcPublicSubnet1RouteTableAssociation5D3F4579" + } + ], + "/aws-cdk-rds-proxy-mariadb/vpc/PublicSubnet1/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "vpcPublicSubnet1DefaultRoute10708846" + } + ], + "/aws-cdk-rds-proxy-mariadb/vpc/PublicSubnet1/EIP": [ + { + "type": "aws:cdk:logicalId", + "data": "vpcPublicSubnet1EIPDA49DCBE" + } + ], + "/aws-cdk-rds-proxy-mariadb/vpc/PublicSubnet1/NATGateway": [ + { + "type": "aws:cdk:logicalId", + "data": "vpcPublicSubnet1NATGateway9C16659E" + } + ], + "/aws-cdk-rds-proxy-mariadb/vpc/PublicSubnet2/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "vpcPublicSubnet2Subnet009B674F" + } + ], + "/aws-cdk-rds-proxy-mariadb/vpc/PublicSubnet2/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "vpcPublicSubnet2RouteTableEB40D4CB" + } + ], + "/aws-cdk-rds-proxy-mariadb/vpc/PublicSubnet2/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "vpcPublicSubnet2RouteTableAssociation21F81B59" + } + ], + "/aws-cdk-rds-proxy-mariadb/vpc/PublicSubnet2/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "vpcPublicSubnet2DefaultRouteA1EC0F60" + } + ], + "/aws-cdk-rds-proxy-mariadb/vpc/PublicSubnet2/EIP": [ + { + "type": "aws:cdk:logicalId", + "data": "vpcPublicSubnet2EIP9B3743B1" + } + ], + "/aws-cdk-rds-proxy-mariadb/vpc/PublicSubnet2/NATGateway": [ + { + "type": "aws:cdk:logicalId", + "data": "vpcPublicSubnet2NATGateway9B8AE11A" + } + ], + "/aws-cdk-rds-proxy-mariadb/vpc/PrivateSubnet1/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "vpcPrivateSubnet1Subnet934893E8" + } + ], + "/aws-cdk-rds-proxy-mariadb/vpc/PrivateSubnet1/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "vpcPrivateSubnet1RouteTableB41A48CC" + } + ], + "/aws-cdk-rds-proxy-mariadb/vpc/PrivateSubnet1/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "vpcPrivateSubnet1RouteTableAssociation67945127" + } + ], + "/aws-cdk-rds-proxy-mariadb/vpc/PrivateSubnet1/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "vpcPrivateSubnet1DefaultRoute1AA8E2E5" + } + ], + "/aws-cdk-rds-proxy-mariadb/vpc/PrivateSubnet2/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "vpcPrivateSubnet2Subnet7031C2BA" + } + ], + "/aws-cdk-rds-proxy-mariadb/vpc/PrivateSubnet2/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "vpcPrivateSubnet2RouteTable7280F23E" + } + ], + "/aws-cdk-rds-proxy-mariadb/vpc/PrivateSubnet2/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "vpcPrivateSubnet2RouteTableAssociation007E94D3" + } + ], + "/aws-cdk-rds-proxy-mariadb/vpc/PrivateSubnet2/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "vpcPrivateSubnet2DefaultRouteB0E07F99" + } + ], + "/aws-cdk-rds-proxy-mariadb/vpc/IGW": [ + { + "type": "aws:cdk:logicalId", + "data": "vpcIGWE57CBDCA" + } + ], + "/aws-cdk-rds-proxy-mariadb/vpc/VPCGW": [ + { + "type": "aws:cdk:logicalId", + "data": "vpcVPCGW7984C166" + } + ], + "/aws-cdk-rds-proxy-mariadb/SecretEncryptionKey/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "SecretEncryptionKey40C82244" + } + ], + "/aws-cdk-rds-proxy-mariadb/mariaDBInstance/SubnetGroup/Default": [ + { + "type": "aws:cdk:logicalId", + "data": "mariaDBInstanceSubnetGroupA17E5651" + } + ], + "/aws-cdk-rds-proxy-mariadb/mariaDBInstance/SecurityGroup/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "mariaDBInstanceSecurityGroup1C05EFED" + } + ], + "/aws-cdk-rds-proxy-mariadb/mariaDBInstance/SecurityGroup/from awscdkrdsproxymariadbmariaDBProxyProxySecurityGroup4F55583F:{IndirectPort}": [ + { + "type": "aws:cdk:logicalId", + "data": "mariaDBInstanceSecurityGroupfromawscdkrdsproxymariadbmariaDBProxyProxySecurityGroup4F55583FIndirectPort44A97795" + } + ], + "/aws-cdk-rds-proxy-mariadb/mariaDBInstance/Secret/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "mariaDBInstanceSecretED322D85" + } + ], + "/aws-cdk-rds-proxy-mariadb/mariaDBInstance/Secret/Attachment/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "mariaDBInstanceSecretAttachment900E3C52" + } + ], + "/aws-cdk-rds-proxy-mariadb/mariaDBInstance/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "mariaDBInstance7DFDB9B1" + } + ], + "/aws-cdk-rds-proxy-mariadb/mariaDBProxy/IAMRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "mariaDBProxyIAMRoleB91DE271" + } + ], + "/aws-cdk-rds-proxy-mariadb/mariaDBProxy/IAMRole/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "mariaDBProxyIAMRoleDefaultPolicyF4BE519B" + } + ], + "/aws-cdk-rds-proxy-mariadb/mariaDBProxy/ProxySecurityGroup/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "mariaDBProxyProxySecurityGroupFD730755" + } + ], + "/aws-cdk-rds-proxy-mariadb/mariaDBProxy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "mariaDBProxy23E8B2BB" + } + ], + "/aws-cdk-rds-proxy-mariadb/mariaDBProxy/ProxyTargetGroup": [ + { + "type": "aws:cdk:logicalId", + "data": "mariaDBProxyProxyTargetGroup960ACB39" + } + ], + "/aws-cdk-rds-proxy-mariadb/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/aws-cdk-rds-proxy-mariadb/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "aws-cdk-rds-proxy-mariadb" + }, + "databaseproxymariadbintegtestDefaultTestDeployAssert9D9B7FF3.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "databaseproxymariadbintegtestDefaultTestDeployAssert9D9B7FF3.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "databaseproxymariadbintegtestDefaultTestDeployAssert9D9B7FF3": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "databaseproxymariadbintegtestDefaultTestDeployAssert9D9B7FF3.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}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "databaseproxymariadbintegtestDefaultTestDeployAssert9D9B7FF3.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "databaseproxymariadbintegtestDefaultTestDeployAssert9D9B7FF3.assets" + ], + "metadata": { + "/database-proxy-mariadb-integ-test/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/database-proxy-mariadb-integ-test/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "database-proxy-mariadb-integ-test/DefaultTest/DeployAssert" + }, + "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-rds/test/integ.proxy-mariadb.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.proxy-mariadb.js.snapshot/tree.json new file mode 100644 index 0000000000000..b0c1987a150b8 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.proxy-mariadb.js.snapshot/tree.json @@ -0,0 +1,1329 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "aws-cdk-rds-proxy-mariadb": { + "id": "aws-cdk-rds-proxy-mariadb", + "path": "aws-cdk-rds-proxy-mariadb", + "children": { + "vpc": { + "id": "vpc", + "path": "aws-cdk-rds-proxy-mariadb/vpc", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-rds-proxy-mariadb/vpc/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::VPC", + "aws:cdk:cloudformation:props": { + "cidrBlock": "10.0.0.0/16", + "enableDnsHostnames": true, + "enableDnsSupport": true, + "instanceTenancy": "default", + "tags": [ + { + "key": "Name", + "value": "aws-cdk-rds-proxy-mariadb/vpc" + } + ] + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "PublicSubnet1": { + "id": "PublicSubnet1", + "path": "aws-cdk-rds-proxy-mariadb/vpc/PublicSubnet1", + "children": { + "Subnet": { + "id": "Subnet", + "path": "aws-cdk-rds-proxy-mariadb/vpc/PublicSubnet1/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "availabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "" + } + ] + }, + "cidrBlock": "10.0.0.0/18", + "mapPublicIpOnLaunch": true, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Public" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Public" + }, + { + "key": "Name", + "value": "aws-cdk-rds-proxy-mariadb/vpc/PublicSubnet1" + } + ], + "vpcId": { + "Ref": "vpcA2121C38" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "Acl": { + "id": "Acl", + "path": "aws-cdk-rds-proxy-mariadb/vpc/PublicSubnet1/Acl", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "aws-cdk-rds-proxy-mariadb/vpc/PublicSubnet1/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "aws-cdk-rds-proxy-mariadb/vpc/PublicSubnet1" + } + ], + "vpcId": { + "Ref": "vpcA2121C38" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "aws-cdk-rds-proxy-mariadb/vpc/PublicSubnet1/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "vpcPublicSubnet1RouteTable48A2DF9B" + }, + "subnetId": { + "Ref": "vpcPublicSubnet1Subnet2E65531E" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "aws-cdk-rds-proxy-mariadb/vpc/PublicSubnet1/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "destinationCidrBlock": "0.0.0.0/0", + "gatewayId": { + "Ref": "vpcIGWE57CBDCA" + }, + "routeTableId": { + "Ref": "vpcPublicSubnet1RouteTable48A2DF9B" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "EIP": { + "id": "EIP", + "path": "aws-cdk-rds-proxy-mariadb/vpc/PublicSubnet1/EIP", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::EIP", + "aws:cdk:cloudformation:props": { + "domain": "vpc", + "tags": [ + { + "key": "Name", + "value": "aws-cdk-rds-proxy-mariadb/vpc/PublicSubnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "NATGateway": { + "id": "NATGateway", + "path": "aws-cdk-rds-proxy-mariadb/vpc/PublicSubnet1/NATGateway", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::NatGateway", + "aws:cdk:cloudformation:props": { + "allocationId": { + "Fn::GetAtt": [ + "vpcPublicSubnet1EIPDA49DCBE", + "AllocationId" + ] + }, + "subnetId": { + "Ref": "vpcPublicSubnet1Subnet2E65531E" + }, + "tags": [ + { + "key": "Name", + "value": "aws-cdk-rds-proxy-mariadb/vpc/PublicSubnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "PublicSubnet2": { + "id": "PublicSubnet2", + "path": "aws-cdk-rds-proxy-mariadb/vpc/PublicSubnet2", + "children": { + "Subnet": { + "id": "Subnet", + "path": "aws-cdk-rds-proxy-mariadb/vpc/PublicSubnet2/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "availabilityZone": { + "Fn::Select": [ + 1, + { + "Fn::GetAZs": "" + } + ] + }, + "cidrBlock": "10.0.64.0/18", + "mapPublicIpOnLaunch": true, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Public" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Public" + }, + { + "key": "Name", + "value": "aws-cdk-rds-proxy-mariadb/vpc/PublicSubnet2" + } + ], + "vpcId": { + "Ref": "vpcA2121C38" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "Acl": { + "id": "Acl", + "path": "aws-cdk-rds-proxy-mariadb/vpc/PublicSubnet2/Acl", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "aws-cdk-rds-proxy-mariadb/vpc/PublicSubnet2/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "aws-cdk-rds-proxy-mariadb/vpc/PublicSubnet2" + } + ], + "vpcId": { + "Ref": "vpcA2121C38" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "aws-cdk-rds-proxy-mariadb/vpc/PublicSubnet2/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "vpcPublicSubnet2RouteTableEB40D4CB" + }, + "subnetId": { + "Ref": "vpcPublicSubnet2Subnet009B674F" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "aws-cdk-rds-proxy-mariadb/vpc/PublicSubnet2/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "destinationCidrBlock": "0.0.0.0/0", + "gatewayId": { + "Ref": "vpcIGWE57CBDCA" + }, + "routeTableId": { + "Ref": "vpcPublicSubnet2RouteTableEB40D4CB" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "EIP": { + "id": "EIP", + "path": "aws-cdk-rds-proxy-mariadb/vpc/PublicSubnet2/EIP", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::EIP", + "aws:cdk:cloudformation:props": { + "domain": "vpc", + "tags": [ + { + "key": "Name", + "value": "aws-cdk-rds-proxy-mariadb/vpc/PublicSubnet2" + } + ] + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "NATGateway": { + "id": "NATGateway", + "path": "aws-cdk-rds-proxy-mariadb/vpc/PublicSubnet2/NATGateway", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::NatGateway", + "aws:cdk:cloudformation:props": { + "allocationId": { + "Fn::GetAtt": [ + "vpcPublicSubnet2EIP9B3743B1", + "AllocationId" + ] + }, + "subnetId": { + "Ref": "vpcPublicSubnet2Subnet009B674F" + }, + "tags": [ + { + "key": "Name", + "value": "aws-cdk-rds-proxy-mariadb/vpc/PublicSubnet2" + } + ] + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "PrivateSubnet1": { + "id": "PrivateSubnet1", + "path": "aws-cdk-rds-proxy-mariadb/vpc/PrivateSubnet1", + "children": { + "Subnet": { + "id": "Subnet", + "path": "aws-cdk-rds-proxy-mariadb/vpc/PrivateSubnet1/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "availabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "" + } + ] + }, + "cidrBlock": "10.0.128.0/18", + "mapPublicIpOnLaunch": false, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Private" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Private" + }, + { + "key": "Name", + "value": "aws-cdk-rds-proxy-mariadb/vpc/PrivateSubnet1" + } + ], + "vpcId": { + "Ref": "vpcA2121C38" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "Acl": { + "id": "Acl", + "path": "aws-cdk-rds-proxy-mariadb/vpc/PrivateSubnet1/Acl", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "aws-cdk-rds-proxy-mariadb/vpc/PrivateSubnet1/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "aws-cdk-rds-proxy-mariadb/vpc/PrivateSubnet1" + } + ], + "vpcId": { + "Ref": "vpcA2121C38" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "aws-cdk-rds-proxy-mariadb/vpc/PrivateSubnet1/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "vpcPrivateSubnet1RouteTableB41A48CC" + }, + "subnetId": { + "Ref": "vpcPrivateSubnet1Subnet934893E8" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "aws-cdk-rds-proxy-mariadb/vpc/PrivateSubnet1/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "destinationCidrBlock": "0.0.0.0/0", + "natGatewayId": { + "Ref": "vpcPublicSubnet1NATGateway9C16659E" + }, + "routeTableId": { + "Ref": "vpcPrivateSubnet1RouteTableB41A48CC" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "PrivateSubnet2": { + "id": "PrivateSubnet2", + "path": "aws-cdk-rds-proxy-mariadb/vpc/PrivateSubnet2", + "children": { + "Subnet": { + "id": "Subnet", + "path": "aws-cdk-rds-proxy-mariadb/vpc/PrivateSubnet2/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "availabilityZone": { + "Fn::Select": [ + 1, + { + "Fn::GetAZs": "" + } + ] + }, + "cidrBlock": "10.0.192.0/18", + "mapPublicIpOnLaunch": false, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Private" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Private" + }, + { + "key": "Name", + "value": "aws-cdk-rds-proxy-mariadb/vpc/PrivateSubnet2" + } + ], + "vpcId": { + "Ref": "vpcA2121C38" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "Acl": { + "id": "Acl", + "path": "aws-cdk-rds-proxy-mariadb/vpc/PrivateSubnet2/Acl", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "aws-cdk-rds-proxy-mariadb/vpc/PrivateSubnet2/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "aws-cdk-rds-proxy-mariadb/vpc/PrivateSubnet2" + } + ], + "vpcId": { + "Ref": "vpcA2121C38" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "aws-cdk-rds-proxy-mariadb/vpc/PrivateSubnet2/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "vpcPrivateSubnet2RouteTable7280F23E" + }, + "subnetId": { + "Ref": "vpcPrivateSubnet2Subnet7031C2BA" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "aws-cdk-rds-proxy-mariadb/vpc/PrivateSubnet2/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "destinationCidrBlock": "0.0.0.0/0", + "natGatewayId": { + "Ref": "vpcPublicSubnet2NATGateway9B8AE11A" + }, + "routeTableId": { + "Ref": "vpcPrivateSubnet2RouteTable7280F23E" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "IGW": { + "id": "IGW", + "path": "aws-cdk-rds-proxy-mariadb/vpc/IGW", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::InternetGateway", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "aws-cdk-rds-proxy-mariadb/vpc" + } + ] + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "VPCGW": { + "id": "VPCGW", + "path": "aws-cdk-rds-proxy-mariadb/vpc/VPCGW", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::VPCGatewayAttachment", + "aws:cdk:cloudformation:props": { + "internetGatewayId": { + "Ref": "vpcIGWE57CBDCA" + }, + "vpcId": { + "Ref": "vpcA2121C38" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "SecretEncryptionKey": { + "id": "SecretEncryptionKey", + "path": "aws-cdk-rds-proxy-mariadb/SecretEncryptionKey", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-rds-proxy-mariadb/SecretEncryptionKey/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::KMS::Key", + "aws:cdk:cloudformation:props": { + "keyPolicy": { + "Statement": [ + { + "Action": "kms:*", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + }, + { + "Action": [ + "kms:CreateGrant", + "kms:Decrypt", + "kms:DescribeKey", + "kms:Encrypt", + "kms:GenerateDataKey*", + "kms:ReEncrypt*" + ], + "Condition": { + "StringEquals": { + "kms:ViaService": { + "Fn::Join": [ + "", + [ + "secretsmanager.", + { + "Ref": "AWS::Region" + }, + ".amazonaws.com" + ] + ] + } + } + }, + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + }, + { + "Action": "kms:Decrypt", + "Condition": { + "StringEquals": { + "kms:ViaService": { + "Fn::Join": [ + "", + [ + "secretsmanager.", + { + "Ref": "AWS::Region" + }, + ".amazonaws.com" + ] + ] + } + } + }, + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "mariaDBProxyIAMRoleB91DE271", + "Arn" + ] + } + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "mariaDBInstance": { + "id": "mariaDBInstance", + "path": "aws-cdk-rds-proxy-mariadb/mariaDBInstance", + "children": { + "SubnetGroup": { + "id": "SubnetGroup", + "path": "aws-cdk-rds-proxy-mariadb/mariaDBInstance/SubnetGroup", + "children": { + "Default": { + "id": "Default", + "path": "aws-cdk-rds-proxy-mariadb/mariaDBInstance/SubnetGroup/Default", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::RDS::DBSubnetGroup", + "aws:cdk:cloudformation:props": { + "dbSubnetGroupDescription": "Subnet group for mariaDBInstance database", + "subnetIds": [ + { + "Ref": "vpcPrivateSubnet1Subnet934893E8" + }, + { + "Ref": "vpcPrivateSubnet2Subnet7031C2BA" + } + ] + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "SecurityGroup": { + "id": "SecurityGroup", + "path": "aws-cdk-rds-proxy-mariadb/mariaDBInstance/SecurityGroup", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-rds-proxy-mariadb/mariaDBInstance/SecurityGroup/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SecurityGroup", + "aws:cdk:cloudformation:props": { + "groupDescription": "Security group for mariaDBInstance database", + "securityGroupEgress": [ + { + "cidrIp": "0.0.0.0/0", + "description": "Allow all outbound traffic by default", + "ipProtocol": "-1" + } + ], + "vpcId": { + "Ref": "vpcA2121C38" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "from awscdkrdsproxymariadbmariaDBProxyProxySecurityGroup4F55583F:{IndirectPort}": { + "id": "from awscdkrdsproxymariadbmariaDBProxyProxySecurityGroup4F55583F:{IndirectPort}", + "path": "aws-cdk-rds-proxy-mariadb/mariaDBInstance/SecurityGroup/from awscdkrdsproxymariadbmariaDBProxyProxySecurityGroup4F55583F:{IndirectPort}", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SecurityGroupIngress", + "aws:cdk:cloudformation:props": { + "description": "Allow connections to the database Instance from the Proxy", + "fromPort": { + "Fn::GetAtt": [ + "mariaDBInstance7DFDB9B1", + "Endpoint.Port" + ] + }, + "groupId": { + "Fn::GetAtt": [ + "mariaDBInstanceSecurityGroup1C05EFED", + "GroupId" + ] + }, + "ipProtocol": "tcp", + "sourceSecurityGroupId": { + "Fn::GetAtt": [ + "mariaDBProxyProxySecurityGroupFD730755", + "GroupId" + ] + }, + "toPort": { + "Fn::GetAtt": [ + "mariaDBInstance7DFDB9B1", + "Endpoint.Port" + ] + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "Secret": { + "id": "Secret", + "path": "aws-cdk-rds-proxy-mariadb/mariaDBInstance/Secret", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-rds-proxy-mariadb/mariaDBInstance/Secret/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::SecretsManager::Secret", + "aws:cdk:cloudformation:props": { + "description": { + "Fn::Join": [ + "", + [ + "Generated by the CDK for stack: ", + { + "Ref": "AWS::StackName" + } + ] + ] + }, + "generateSecretString": { + "passwordLength": 30, + "secretStringTemplate": "{\"username\":\"master\"}", + "generateStringKey": "password", + "excludeCharacters": "\"@/\\" + }, + "kmsKeyId": { + "Fn::GetAtt": [ + "SecretEncryptionKey40C82244", + "Arn" + ] + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "Attachment": { + "id": "Attachment", + "path": "aws-cdk-rds-proxy-mariadb/mariaDBInstance/Secret/Attachment", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-rds-proxy-mariadb/mariaDBInstance/Secret/Attachment/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::SecretsManager::SecretTargetAttachment", + "aws:cdk:cloudformation:props": { + "secretId": { + "Ref": "mariaDBInstanceSecretED322D85" + }, + "targetId": { + "Ref": "mariaDBInstance7DFDB9B1" + }, + "targetType": "AWS::RDS::DBInstance" + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-cdk-rds-proxy-mariadb/mariaDBInstance/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::RDS::DBInstance", + "aws:cdk:cloudformation:props": { + "allocatedStorage": "100", + "copyTagsToSnapshot": true, + "dbInstanceClass": "db.t3.medium", + "dbSubnetGroupName": { + "Ref": "mariaDBInstanceSubnetGroupA17E5651" + }, + "engine": "mariadb", + "engineVersion": "10.6.16", + "masterUsername": { + "Fn::Join": [ + "", + [ + "{{resolve:secretsmanager:", + { + "Ref": "mariaDBInstanceSecretED322D85" + }, + ":SecretString:username::}}" + ] + ] + }, + "masterUserPassword": { + "Fn::Join": [ + "", + [ + "{{resolve:secretsmanager:", + { + "Ref": "mariaDBInstanceSecretED322D85" + }, + ":SecretString:password::}}" + ] + ] + }, + "storageType": "gp2", + "vpcSecurityGroups": [ + { + "Fn::GetAtt": [ + "mariaDBInstanceSecurityGroup1C05EFED", + "GroupId" + ] + } + ] + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "mariaDBProxy": { + "id": "mariaDBProxy", + "path": "aws-cdk-rds-proxy-mariadb/mariaDBProxy", + "children": { + "IAMRole": { + "id": "IAMRole", + "path": "aws-cdk-rds-proxy-mariadb/mariaDBProxy/IAMRole", + "children": { + "ImportIAMRole": { + "id": "ImportIAMRole", + "path": "aws-cdk-rds-proxy-mariadb/mariaDBProxy/IAMRole/ImportIAMRole", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-cdk-rds-proxy-mariadb/mariaDBProxy/IAMRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "rds.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "aws-cdk-rds-proxy-mariadb/mariaDBProxy/IAMRole/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-rds-proxy-mariadb/mariaDBProxy/IAMRole/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": [ + "secretsmanager:DescribeSecret", + "secretsmanager:GetSecretValue" + ], + "Effect": "Allow", + "Resource": { + "Ref": "mariaDBInstanceSecretAttachment900E3C52" + } + }, + { + "Action": "kms:Decrypt", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "SecretEncryptionKey40C82244", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "policyName": "mariaDBProxyIAMRoleDefaultPolicyF4BE519B", + "roles": [ + { + "Ref": "mariaDBProxyIAMRoleB91DE271" + } + ] + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "ProxySecurityGroup": { + "id": "ProxySecurityGroup", + "path": "aws-cdk-rds-proxy-mariadb/mariaDBProxy/ProxySecurityGroup", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-rds-proxy-mariadb/mariaDBProxy/ProxySecurityGroup/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SecurityGroup", + "aws:cdk:cloudformation:props": { + "groupDescription": "SecurityGroup for Database Proxy", + "securityGroupEgress": [ + { + "cidrIp": "0.0.0.0/0", + "description": "Allow all outbound traffic by default", + "ipProtocol": "-1" + } + ], + "vpcId": { + "Ref": "vpcA2121C38" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-cdk-rds-proxy-mariadb/mariaDBProxy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::RDS::DBProxy", + "aws:cdk:cloudformation:props": { + "auth": [ + { + "authScheme": "SECRETS", + "iamAuth": "DISABLED", + "secretArn": { + "Ref": "mariaDBInstanceSecretAttachment900E3C52" + } + } + ], + "dbProxyName": "awscdkrdsproxymariadbmariaDBProxyB0A701B2", + "engineFamily": "MYSQL", + "requireTls": true, + "roleArn": { + "Fn::GetAtt": [ + "mariaDBProxyIAMRoleB91DE271", + "Arn" + ] + }, + "vpcSecurityGroupIds": [ + { + "Fn::GetAtt": [ + "mariaDBProxyProxySecurityGroupFD730755", + "GroupId" + ] + } + ], + "vpcSubnetIds": [ + { + "Ref": "vpcPrivateSubnet1Subnet934893E8" + }, + { + "Ref": "vpcPrivateSubnet2Subnet7031C2BA" + } + ] + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "ProxyTargetGroup": { + "id": "ProxyTargetGroup", + "path": "aws-cdk-rds-proxy-mariadb/mariaDBProxy/ProxyTargetGroup", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::RDS::DBProxyTargetGroup", + "aws:cdk:cloudformation:props": { + "connectionPoolConfigurationInfo": { + "connectionBorrowTimeout": 30, + "maxConnectionsPercent": 50 + }, + "dbInstanceIdentifiers": [ + { + "Ref": "mariaDBInstance7DFDB9B1" + } + ], + "dbProxyName": { + "Ref": "mariaDBProxy23E8B2BB" + }, + "targetGroupName": "default" + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "aws-cdk-rds-proxy-mariadb/BootstrapVersion", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "aws-cdk-rds-proxy-mariadb/CheckBootstrapVersion", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "database-proxy-mariadb-integ-test": { + "id": "database-proxy-mariadb-integ-test", + "path": "database-proxy-mariadb-integ-test", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "database-proxy-mariadb-integ-test/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "database-proxy-mariadb-integ-test/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "database-proxy-mariadb-integ-test/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "database-proxy-mariadb-integ-test/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "database-proxy-mariadb-integ-test/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.proxy-mariadb.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.proxy-mariadb.ts new file mode 100644 index 0000000000000..c49d0a53ca66c --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.proxy-mariadb.ts @@ -0,0 +1,45 @@ +import * as ec2 from 'aws-cdk-lib/aws-ec2'; +import * as cdk from 'aws-cdk-lib'; +import { RemovalPolicy } from 'aws-cdk-lib'; +import * as integ from '@aws-cdk/integ-tests-alpha'; +import * as kms from 'aws-cdk-lib/aws-kms'; +import * as rds from 'aws-cdk-lib/aws-rds'; + +const app = new cdk.App(); +const stack = new cdk.Stack(app, 'aws-cdk-rds-proxy-mariadb'); + +const vpc = new ec2.Vpc(stack, 'vpc', { maxAzs: 2, restrictDefaultSecurityGroup: false }); +const kmsKey = new kms.Key(stack, 'SecretEncryptionKey'); + +const mariaDBInstance = new rds.DatabaseInstance(stack, 'mariaDBInstance', { + engine: rds.DatabaseInstanceEngine.mariaDb({ + version: rds.MariaDbEngineVersion.VER_10_6_16, + }), + instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE3, ec2.InstanceSize.MEDIUM), + credentials: rds.Credentials.fromUsername('master', { + encryptionKey: kmsKey, + excludeCharacters: '"@/\\', + }), + vpc, + removalPolicy: RemovalPolicy.DESTROY, +}); + +new rds.DatabaseProxy(stack, 'mariaDBProxy', { + borrowTimeout: cdk.Duration.seconds(30), + maxConnectionsPercent: 50, + secrets: [mariaDBInstance.secret!], + proxyTarget: rds.ProxyTarget.fromInstance(mariaDBInstance), + vpc, +}); + +new integ.IntegTest(app, 'database-proxy-mariadb-integ-test', { + testCases: [stack], + diffAssets: true, + cdkCommandOptions: { + deploy: { + args: { + rollback: true, + }, + }, + }, +}); diff --git a/packages/aws-cdk-lib/aws-rds/README.md b/packages/aws-cdk-lib/aws-rds/README.md index 20605a5463abd..7ee7a8df813dc 100644 --- a/packages/aws-cdk-lib/aws-rds/README.md +++ b/packages/aws-cdk-lib/aws-rds/README.md @@ -972,7 +972,9 @@ new rds.DatabaseCluster(this, 'dbcluster', { ## Creating a Database Proxy Amazon RDS Proxy sits between your application and your relational database to efficiently manage -connections to the database and improve scalability of the application. Learn more about at [Amazon RDS Proxy](https://aws.amazon.com/rds/proxy/) +connections to the database and improve scalability of the application. Learn more about at [Amazon RDS Proxy](https://aws.amazon.com/rds/proxy/). + +RDS Proxy is supported for MySQL, MariaDB, Postgres, and SQL Server. The following code configures an RDS Proxy for a `DatabaseInstance`. diff --git a/packages/aws-cdk-lib/aws-rds/lib/instance-engine.ts b/packages/aws-cdk-lib/aws-rds/lib/instance-engine.ts index 3774347e21e61..d705b9494a4fd 100644 --- a/packages/aws-cdk-lib/aws-rds/lib/instance-engine.ts +++ b/packages/aws-cdk-lib/aws-rds/lib/instance-engine.ts @@ -570,6 +570,7 @@ class MariaDbInstanceEngine extends InstanceEngineBase { majorVersion: version.mariaDbMajorVersion, } : undefined, + engineFamily: 'MYSQL', }); } diff --git a/packages/aws-cdk-lib/aws-rds/test/proxy.test.ts b/packages/aws-cdk-lib/aws-rds/test/proxy.test.ts index 970f13b09908d..7c1d76a68a416 100644 --- a/packages/aws-cdk-lib/aws-rds/test/proxy.test.ts +++ b/packages/aws-cdk-lib/aws-rds/test/proxy.test.ts @@ -79,6 +79,67 @@ describe('proxy', () => { }); }); + test('create a DB proxy for a MariaDB instance', () => { + // GIVEN + const instance = new rds.DatabaseInstance(stack, 'Instance', { + engine: rds.DatabaseInstanceEngine.mariaDb({ + version: rds.MariaDbEngineVersion.VER_10_6_16, + }), + vpc, + }); + + // WHEN + new rds.DatabaseProxy(stack, 'Proxy', { + proxyTarget: rds.ProxyTarget.fromInstance(instance), + secrets: [instance.secret!], + vpc, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBProxy', { + Auth: [ + { + AuthScheme: 'SECRETS', + IAMAuth: 'DISABLED', + SecretArn: { + Ref: 'InstanceSecretAttachment83BEE581', + }, + }, + ], + DBProxyName: 'Proxy', + EngineFamily: 'MYSQL', + RequireTLS: true, + RoleArn: { + 'Fn::GetAtt': [ + 'ProxyIAMRole2FE8AB0F', + 'Arn', + ], + }, + VpcSubnetIds: [ + { + Ref: 'VPCPrivateSubnet1Subnet8BCA10E0', + }, + { + Ref: 'VPCPrivateSubnet2SubnetCFCDAA7A', + }, + ], + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBProxyTargetGroup', { + DBProxyName: { + Ref: 'ProxyCB0DFB71', + }, + ConnectionPoolConfigurationInfo: {}, + DBInstanceIdentifiers: [ + { + Ref: 'InstanceC1063A87', + }, + ], + TargetGroupName: 'default', + }); + }); + test('create a DB proxy from a cluster', () => { // GIVEN const cluster = new rds.DatabaseCluster(stack, 'Database', { @@ -192,8 +253,8 @@ describe('proxy', () => { instanceEndpointAddress: 'instance-address', port: 5432, securityGroups: [], - engine: rds.DatabaseInstanceEngine.mariaDb({ - version: rds.MariaDbEngineVersion.VER_10_0_24, + engine: rds.DatabaseInstanceEngine.oracleEe({ + version: rds.OracleEngineVersion.VER_21, }), }); @@ -203,7 +264,7 @@ describe('proxy', () => { vpc, secrets: [new secretsmanager.Secret(stack, 'Secret')], }); - }).toThrow(/RDS proxies require an engine family to be specified on the database cluster or instance. No family specified for engine 'mariadb-10\.0\.24'/); + }).toThrow(/RDS proxies require an engine family to be specified on the database cluster or instance. No family specified for engine 'oracle-ee-21'/); }); test('correctly creates a proxy for an imported Cluster if its engine is known', () => {