From d43834d441ae8eb0192df45c1cfa0101e5533e4e Mon Sep 17 00:00:00 2001 From: Colin Francis <131073567+colifran@users.noreply.github.com> Date: Tue, 23 May 2023 05:43:36 -0700 Subject: [PATCH] feat(ec2): added support for network interfaces on ec2 instances by providing an associatePublicIpAddress property (#25441) ## Motivation When creating and launching an EC2 instance, a public IPv4 address will be assigned by default for any instances being launched into a default public subnet. Conversely, any EC2 instance being launched into a nondefault public subnet will not be automatically assigned a public IPv4 address. The decision to automatically assign or not assign a public IPv4 address is based on a subnet property which is true by default for default public subnets and false by default for nondefault public subnets. This property can be controlled by specifying that the 'associatePublicIpAddress' be true for an EC2 instance. This property can be exposed via the 'networkInterfaces' property on the underlying L1 CfnInstance construct. Furthermore, any network interface that has an 'associatePublicIpAddress' set to true must also be the primary network interface for the EC2 instance and a primary network interface will always have a device index of 0. The work in this PR will allow a user to automatically have a public IPv4 address assigned to an EC2 instance that they are launching into a nondefault public subnet or stop the default subnet behavior of automatically assigning a public IPv4 address. ## Important Changes The changes made in this PR start by exposing the 'networkInterfaces' property on the underlying L1 CfnInstance. Next, I added 'associatePublicIpAddress' as an optional boolean property that is part of the 'InstanceProps' interface. Importantly, if 'associatePublicIpAddress' is set to true or false, then this means we need to launch the EC2 instance with a configured primary network interface. If 'associatePublicIpAddress' is set to true or false, a network interfaces array is created with the specified network interface configuration for the primary network interface. The subnetId and securityGroupIds are also configured for the network interface since they must be defined on the network interface level when launching an EC2 instance with a configured network interface. I updated the L1 CfnInstance to set subnetId and securityGroupIds to undefined in the event that the network interfaces array is defined. Closes #17127 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../integ.instance-public.js.snapshot/cdk.out | 1 + ...efaultTestDeployAssert5516EAF1.assets.json | 19 + ...aultTestDeployAssert5516EAF1.template.json | 36 + .../integ-ec2-instance.assets.json | 19 + .../integ-ec2-instance.template.json | 362 ++++++++++ .../integ.json | 12 + .../manifest.json | 207 ++++++ .../tree.json | 645 ++++++++++++++++++ .../aws-ec2/test/integ.instance-public.ts | 57 ++ packages/aws-cdk-lib/aws-ec2/README.md | 28 + packages/aws-cdk-lib/aws-ec2/lib/instance.ts | 31 +- .../aws-cdk-lib/aws-ec2/test/instance.test.ts | 93 +++ 12 files changed, 1507 insertions(+), 3 deletions(-) create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-ec2/test/integ.instance-public.js.snapshot/cdk.out create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-ec2/test/integ.instance-public.js.snapshot/instancetestDefaultTestDeployAssert5516EAF1.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-ec2/test/integ.instance-public.js.snapshot/instancetestDefaultTestDeployAssert5516EAF1.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-ec2/test/integ.instance-public.js.snapshot/integ-ec2-instance.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-ec2/test/integ.instance-public.js.snapshot/integ-ec2-instance.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-ec2/test/integ.instance-public.js.snapshot/integ.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-ec2/test/integ.instance-public.js.snapshot/manifest.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-ec2/test/integ.instance-public.js.snapshot/tree.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-ec2/test/integ.instance-public.ts diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ec2/test/integ.instance-public.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-ec2/test/integ.instance-public.js.snapshot/cdk.out new file mode 100644 index 0000000000000..7925065efbcc4 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ec2/test/integ.instance-public.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"31.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ec2/test/integ.instance-public.js.snapshot/instancetestDefaultTestDeployAssert5516EAF1.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ec2/test/integ.instance-public.js.snapshot/instancetestDefaultTestDeployAssert5516EAF1.assets.json new file mode 100644 index 0000000000000..92c11858a9333 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ec2/test/integ.instance-public.js.snapshot/instancetestDefaultTestDeployAssert5516EAF1.assets.json @@ -0,0 +1,19 @@ +{ + "version": "31.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "instancetestDefaultTestDeployAssert5516EAF1.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-ec2/test/integ.instance-public.js.snapshot/instancetestDefaultTestDeployAssert5516EAF1.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ec2/test/integ.instance-public.js.snapshot/instancetestDefaultTestDeployAssert5516EAF1.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ec2/test/integ.instance-public.js.snapshot/instancetestDefaultTestDeployAssert5516EAF1.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "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-ec2/test/integ.instance-public.js.snapshot/integ-ec2-instance.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ec2/test/integ.instance-public.js.snapshot/integ-ec2-instance.assets.json new file mode 100644 index 0000000000000..24fa7fa28b059 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ec2/test/integ.instance-public.js.snapshot/integ-ec2-instance.assets.json @@ -0,0 +1,19 @@ +{ + "version": "31.0.0", + "files": { + "488d9cf540c6790fc09af871e06438e043f47d03101ef192131f1dafbbb434cb": { + "source": { + "path": "integ-ec2-instance.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "488d9cf540c6790fc09af871e06438e043f47d03101ef192131f1dafbbb434cb.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-ec2/test/integ.instance-public.js.snapshot/integ-ec2-instance.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ec2/test/integ.instance-public.js.snapshot/integ-ec2-instance.template.json new file mode 100644 index 0000000000000..4026ad159f736 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ec2/test/integ.instance-public.js.snapshot/integ-ec2-instance.template.json @@ -0,0 +1,362 @@ +{ + "Resources": { + "VPCB9E5F0B4": { + "Type": "AWS::EC2::VPC", + "Properties": { + "CidrBlock": "10.0.0.0/16", + "EnableDnsHostnames": true, + "EnableDnsSupport": true, + "InstanceTenancy": "default", + "Tags": [ + { + "Key": "Name", + "Value": "integ-ec2-instance/VPC" + } + ] + } + }, + "VPCpublicsubnet1Subnet1Subnet39B927A0": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "AvailabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "" + } + ] + }, + "CidrBlock": "10.0.0.0/24", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "public-subnet-1" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "Name", + "Value": "integ-ec2-instance/VPC/public-subnet-1Subnet1" + } + ] + } + }, + "VPCpublicsubnet1Subnet1RouteTable1127E157": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "integ-ec2-instance/VPC/public-subnet-1Subnet1" + } + ] + } + }, + "VPCpublicsubnet1Subnet1RouteTableAssociation99DE76A6": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VPCpublicsubnet1Subnet1RouteTable1127E157" + }, + "SubnetId": { + "Ref": "VPCpublicsubnet1Subnet1Subnet39B927A0" + } + } + }, + "VPCpublicsubnet1Subnet1DefaultRouteEFD0DA69": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VPCpublicsubnet1Subnet1RouteTable1127E157" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VPCIGWB7E252D3" + } + }, + "DependsOn": [ + "VPCVPCGW99B986DC" + ] + }, + "VPCpublicsubnet1Subnet2Subnet1B74FFEC": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "AvailabilityZone": { + "Fn::Select": [ + 1, + { + "Fn::GetAZs": "" + } + ] + }, + "CidrBlock": "10.0.1.0/24", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "public-subnet-1" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "Name", + "Value": "integ-ec2-instance/VPC/public-subnet-1Subnet2" + } + ] + } + }, + "VPCpublicsubnet1Subnet2RouteTable6613D6DE": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "integ-ec2-instance/VPC/public-subnet-1Subnet2" + } + ] + } + }, + "VPCpublicsubnet1Subnet2RouteTableAssociation4859253B": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VPCpublicsubnet1Subnet2RouteTable6613D6DE" + }, + "SubnetId": { + "Ref": "VPCpublicsubnet1Subnet2Subnet1B74FFEC" + } + } + }, + "VPCpublicsubnet1Subnet2DefaultRoute3D53F956": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VPCpublicsubnet1Subnet2RouteTable6613D6DE" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VPCIGWB7E252D3" + } + }, + "DependsOn": [ + "VPCVPCGW99B986DC" + ] + }, + "VPCIGWB7E252D3": { + "Type": "AWS::EC2::InternetGateway", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "integ-ec2-instance/VPC" + } + ] + } + }, + "VPCVPCGW99B986DC": { + "Type": "AWS::EC2::VPCGatewayAttachment", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "InternetGatewayId": { + "Ref": "VPCIGWB7E252D3" + } + } + }, + "IntegSg68DC2C7E": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "integ-ec2-instance/IntegSg", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + }, + { + "CidrIpv6": "::/0", + "Description": "Allow all outbound ipv6 traffic by default", + "IpProtocol": "-1" + } + ], + "SecurityGroupIngress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "from 0.0.0.0/0:ICMP Type 8", + "FromPort": 8, + "IpProtocol": "icmp", + "ToPort": -1 + } + ], + "VpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "InstanceInstanceRoleE9785DE5": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "ec2.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "Tags": [ + { + "Key": "Name", + "Value": "integ-ec2-instance/Instance" + } + ] + } + }, + "InstanceInstanceRoleDefaultPolicy4ACE9290": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "ssm:*", + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "InstanceInstanceRoleDefaultPolicy4ACE9290", + "Roles": [ + { + "Ref": "InstanceInstanceRoleE9785DE5" + } + ] + } + }, + "InstanceInstanceProfileAB5AEF02": { + "Type": "AWS::IAM::InstanceProfile", + "Properties": { + "Roles": [ + { + "Ref": "InstanceInstanceRoleE9785DE5" + } + ] + } + }, + "InstanceC1063A87": { + "Type": "AWS::EC2::Instance", + "Properties": { + "AvailabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "" + } + ] + }, + "IamInstanceProfile": { + "Ref": "InstanceInstanceProfileAB5AEF02" + }, + "ImageId": { + "Ref": "SsmParameterValueawsserviceamiamazonlinuxlatestamzn2amihvmx8664gp2C96584B6F00A464EAD1953AFF4B05118Parameter" + }, + "InstanceType": "t3.nano", + "Monitoring": true, + "NetworkInterfaces": [ + { + "AssociatePublicIpAddress": true, + "DeviceIndex": "0", + "GroupSet": [ + { + "Fn::GetAtt": [ + "IntegSg68DC2C7E", + "GroupId" + ] + } + ], + "SubnetId": { + "Ref": "VPCpublicsubnet1Subnet1Subnet39B927A0" + } + } + ], + "Tags": [ + { + "Key": "Name", + "Value": "integ-ec2-instance/Instance" + } + ], + "UserData": { + "Fn::Base64": "#!/bin/bash\nyum install -y" + } + }, + "DependsOn": [ + "InstanceInstanceRoleDefaultPolicy4ACE9290", + "InstanceInstanceRoleE9785DE5", + "VPCpublicsubnet1Subnet1DefaultRouteEFD0DA69", + "VPCpublicsubnet1Subnet1RouteTableAssociation99DE76A6", + "VPCpublicsubnet1Subnet2DefaultRoute3D53F956", + "VPCpublicsubnet1Subnet2RouteTableAssociation4859253B" + ] + } + }, + "Parameters": { + "SsmParameterValueawsserviceamiamazonlinuxlatestamzn2amihvmx8664gp2C96584B6F00A464EAD1953AFF4B05118Parameter": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2" + }, + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "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-ec2/test/integ.instance-public.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ec2/test/integ.instance-public.js.snapshot/integ.json new file mode 100644 index 0000000000000..f7b474f3d5a35 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ec2/test/integ.instance-public.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "31.0.0", + "testCases": { + "instance-test/DefaultTest": { + "stacks": [ + "integ-ec2-instance" + ], + "assertionStack": "instance-test/DefaultTest/DeployAssert", + "assertionStackName": "instancetestDefaultTestDeployAssert5516EAF1" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ec2/test/integ.instance-public.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ec2/test/integ.instance-public.js.snapshot/manifest.json new file mode 100644 index 0000000000000..aa23896e254e6 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ec2/test/integ.instance-public.js.snapshot/manifest.json @@ -0,0 +1,207 @@ +{ + "version": "31.0.0", + "artifacts": { + "integ-ec2-instance.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "integ-ec2-instance.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "integ-ec2-instance": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "integ-ec2-instance.template.json", + "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}/488d9cf540c6790fc09af871e06438e043f47d03101ef192131f1dafbbb434cb.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "integ-ec2-instance.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": [ + "integ-ec2-instance.assets" + ], + "metadata": { + "/integ-ec2-instance/VPC/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCB9E5F0B4" + } + ], + "/integ-ec2-instance/VPC/public-subnet-1Subnet1/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCpublicsubnet1Subnet1Subnet39B927A0" + } + ], + "/integ-ec2-instance/VPC/public-subnet-1Subnet1/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCpublicsubnet1Subnet1RouteTable1127E157" + } + ], + "/integ-ec2-instance/VPC/public-subnet-1Subnet1/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCpublicsubnet1Subnet1RouteTableAssociation99DE76A6" + } + ], + "/integ-ec2-instance/VPC/public-subnet-1Subnet1/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCpublicsubnet1Subnet1DefaultRouteEFD0DA69" + } + ], + "/integ-ec2-instance/VPC/public-subnet-1Subnet2/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCpublicsubnet1Subnet2Subnet1B74FFEC" + } + ], + "/integ-ec2-instance/VPC/public-subnet-1Subnet2/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCpublicsubnet1Subnet2RouteTable6613D6DE" + } + ], + "/integ-ec2-instance/VPC/public-subnet-1Subnet2/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCpublicsubnet1Subnet2RouteTableAssociation4859253B" + } + ], + "/integ-ec2-instance/VPC/public-subnet-1Subnet2/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCpublicsubnet1Subnet2DefaultRoute3D53F956" + } + ], + "/integ-ec2-instance/VPC/IGW": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCIGWB7E252D3" + } + ], + "/integ-ec2-instance/VPC/VPCGW": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCVPCGW99B986DC" + } + ], + "/integ-ec2-instance/IntegSg/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "IntegSg68DC2C7E" + } + ], + "/integ-ec2-instance/Instance/InstanceRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "InstanceInstanceRoleE9785DE5" + } + ], + "/integ-ec2-instance/Instance/InstanceRole/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "InstanceInstanceRoleDefaultPolicy4ACE9290" + } + ], + "/integ-ec2-instance/Instance/InstanceProfile": [ + { + "type": "aws:cdk:logicalId", + "data": "InstanceInstanceProfileAB5AEF02" + } + ], + "/integ-ec2-instance/Instance/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "InstanceC1063A87" + } + ], + "/integ-ec2-instance/SsmParameterValue:--aws--service--ami-amazon-linux-latest--amzn2-ami-hvm-x86_64-gp2:C96584B6-F00A-464E-AD19-53AFF4B05118.Parameter": [ + { + "type": "aws:cdk:logicalId", + "data": "SsmParameterValueawsserviceamiamazonlinuxlatestamzn2amihvmx8664gp2C96584B6F00A464EAD1953AFF4B05118Parameter" + } + ], + "/integ-ec2-instance/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/integ-ec2-instance/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "integ-ec2-instance" + }, + "instancetestDefaultTestDeployAssert5516EAF1.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "instancetestDefaultTestDeployAssert5516EAF1.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "instancetestDefaultTestDeployAssert5516EAF1": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "instancetestDefaultTestDeployAssert5516EAF1.template.json", + "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": [ + "instancetestDefaultTestDeployAssert5516EAF1.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": [ + "instancetestDefaultTestDeployAssert5516EAF1.assets" + ], + "metadata": { + "/instance-test/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/instance-test/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "instance-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-ec2/test/integ.instance-public.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ec2/test/integ.instance-public.js.snapshot/tree.json new file mode 100644 index 0000000000000..87142e2d428c9 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ec2/test/integ.instance-public.js.snapshot/tree.json @@ -0,0 +1,645 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "integ-ec2-instance": { + "id": "integ-ec2-instance", + "path": "integ-ec2-instance", + "children": { + "VPC": { + "id": "VPC", + "path": "integ-ec2-instance/VPC", + "children": { + "Resource": { + "id": "Resource", + "path": "integ-ec2-instance/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": "integ-ec2-instance/VPC" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnVPC", + "version": "0.0.0" + } + }, + "public-subnet-1Subnet1": { + "id": "public-subnet-1Subnet1", + "path": "integ-ec2-instance/VPC/public-subnet-1Subnet1", + "children": { + "Subnet": { + "id": "Subnet", + "path": "integ-ec2-instance/VPC/public-subnet-1Subnet1/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "VPCB9E5F0B4" + }, + "availabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "" + } + ] + }, + "cidrBlock": "10.0.0.0/24", + "mapPublicIpOnLaunch": true, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "public-subnet-1" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Public" + }, + { + "key": "Name", + "value": "integ-ec2-instance/VPC/public-subnet-1Subnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnet", + "version": "0.0.0" + } + }, + "Acl": { + "id": "Acl", + "path": "integ-ec2-instance/VPC/public-subnet-1Subnet1/Acl", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "integ-ec2-instance/VPC/public-subnet-1Subnet1/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "VPCB9E5F0B4" + }, + "tags": [ + { + "key": "Name", + "value": "integ-ec2-instance/VPC/public-subnet-1Subnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRouteTable", + "version": "0.0.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "integ-ec2-instance/VPC/public-subnet-1Subnet1/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VPCpublicsubnet1Subnet1RouteTable1127E157" + }, + "subnetId": { + "Ref": "VPCpublicsubnet1Subnet1Subnet39B927A0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnetRouteTableAssociation", + "version": "0.0.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "integ-ec2-instance/VPC/public-subnet-1Subnet1/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VPCpublicsubnet1Subnet1RouteTable1127E157" + }, + "destinationCidrBlock": "0.0.0.0/0", + "gatewayId": { + "Ref": "VPCIGWB7E252D3" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRoute", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.PublicSubnet", + "version": "0.0.0" + } + }, + "public-subnet-1Subnet2": { + "id": "public-subnet-1Subnet2", + "path": "integ-ec2-instance/VPC/public-subnet-1Subnet2", + "children": { + "Subnet": { + "id": "Subnet", + "path": "integ-ec2-instance/VPC/public-subnet-1Subnet2/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "VPCB9E5F0B4" + }, + "availabilityZone": { + "Fn::Select": [ + 1, + { + "Fn::GetAZs": "" + } + ] + }, + "cidrBlock": "10.0.1.0/24", + "mapPublicIpOnLaunch": true, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "public-subnet-1" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Public" + }, + { + "key": "Name", + "value": "integ-ec2-instance/VPC/public-subnet-1Subnet2" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnet", + "version": "0.0.0" + } + }, + "Acl": { + "id": "Acl", + "path": "integ-ec2-instance/VPC/public-subnet-1Subnet2/Acl", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "integ-ec2-instance/VPC/public-subnet-1Subnet2/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "VPCB9E5F0B4" + }, + "tags": [ + { + "key": "Name", + "value": "integ-ec2-instance/VPC/public-subnet-1Subnet2" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRouteTable", + "version": "0.0.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "integ-ec2-instance/VPC/public-subnet-1Subnet2/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VPCpublicsubnet1Subnet2RouteTable6613D6DE" + }, + "subnetId": { + "Ref": "VPCpublicsubnet1Subnet2Subnet1B74FFEC" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnetRouteTableAssociation", + "version": "0.0.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "integ-ec2-instance/VPC/public-subnet-1Subnet2/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VPCpublicsubnet1Subnet2RouteTable6613D6DE" + }, + "destinationCidrBlock": "0.0.0.0/0", + "gatewayId": { + "Ref": "VPCIGWB7E252D3" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRoute", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.PublicSubnet", + "version": "0.0.0" + } + }, + "IGW": { + "id": "IGW", + "path": "integ-ec2-instance/VPC/IGW", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::InternetGateway", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "integ-ec2-instance/VPC" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnInternetGateway", + "version": "0.0.0" + } + }, + "VPCGW": { + "id": "VPCGW", + "path": "integ-ec2-instance/VPC/VPCGW", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::VPCGatewayAttachment", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "VPCB9E5F0B4" + }, + "internetGatewayId": { + "Ref": "VPCIGWB7E252D3" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnVPCGatewayAttachment", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.Vpc", + "version": "0.0.0" + } + }, + "IntegSg": { + "id": "IntegSg", + "path": "integ-ec2-instance/IntegSg", + "children": { + "Resource": { + "id": "Resource", + "path": "integ-ec2-instance/IntegSg/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SecurityGroup", + "aws:cdk:cloudformation:props": { + "groupDescription": "integ-ec2-instance/IntegSg", + "securityGroupEgress": [ + { + "cidrIp": "0.0.0.0/0", + "description": "Allow all outbound traffic by default", + "ipProtocol": "-1" + }, + { + "ipProtocol": "-1", + "cidrIpv6": "::/0", + "description": "Allow all outbound ipv6 traffic by default" + } + ], + "securityGroupIngress": [ + { + "cidrIp": "0.0.0.0/0", + "ipProtocol": "icmp", + "fromPort": 8, + "toPort": -1, + "description": "from 0.0.0.0/0:ICMP Type 8" + } + ], + "vpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSecurityGroup", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.SecurityGroup", + "version": "0.0.0" + } + }, + "Instance": { + "id": "Instance", + "path": "integ-ec2-instance/Instance", + "children": { + "InstanceRole": { + "id": "InstanceRole", + "path": "integ-ec2-instance/Instance/InstanceRole", + "children": { + "ImportInstanceRole": { + "id": "ImportInstanceRole", + "path": "integ-ec2-instance/Instance/InstanceRole/ImportInstanceRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "integ-ec2-instance/Instance/InstanceRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "ec2.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "tags": [ + { + "key": "Name", + "value": "integ-ec2-instance/Instance" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "integ-ec2-instance/Instance/InstanceRole/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "integ-ec2-instance/Instance/InstanceRole/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": "ssm:*", + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "policyName": "InstanceInstanceRoleDefaultPolicy4ACE9290", + "roles": [ + { + "Ref": "InstanceInstanceRoleE9785DE5" + } + ] + } + }, + "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" + } + }, + "InstanceProfile": { + "id": "InstanceProfile", + "path": "integ-ec2-instance/Instance/InstanceProfile", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::InstanceProfile", + "aws:cdk:cloudformation:props": { + "roles": [ + { + "Ref": "InstanceInstanceRoleE9785DE5" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnInstanceProfile", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "integ-ec2-instance/Instance/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Instance", + "aws:cdk:cloudformation:props": { + "availabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "" + } + ] + }, + "iamInstanceProfile": { + "Ref": "InstanceInstanceProfileAB5AEF02" + }, + "imageId": { + "Ref": "SsmParameterValueawsserviceamiamazonlinuxlatestamzn2amihvmx8664gp2C96584B6F00A464EAD1953AFF4B05118Parameter" + }, + "instanceType": "t3.nano", + "monitoring": true, + "networkInterfaces": [ + { + "deviceIndex": "0", + "associatePublicIpAddress": true, + "subnetId": { + "Ref": "VPCpublicsubnet1Subnet1Subnet39B927A0" + }, + "groupSet": [ + { + "Fn::GetAtt": [ + "IntegSg68DC2C7E", + "GroupId" + ] + } + ] + } + ], + "tags": [ + { + "key": "Name", + "value": "integ-ec2-instance/Instance" + } + ], + "userData": { + "Fn::Base64": "#!/bin/bash\nyum install -y" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnInstance", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.Instance", + "version": "0.0.0" + } + }, + "SsmParameterValue:--aws--service--ami-amazon-linux-latest--amzn2-ami-hvm-x86_64-gp2:C96584B6-F00A-464E-AD19-53AFF4B05118.Parameter": { + "id": "SsmParameterValue:--aws--service--ami-amazon-linux-latest--amzn2-ami-hvm-x86_64-gp2:C96584B6-F00A-464E-AD19-53AFF4B05118.Parameter", + "path": "integ-ec2-instance/SsmParameterValue:--aws--service--ami-amazon-linux-latest--amzn2-ami-hvm-x86_64-gp2:C96584B6-F00A-464E-AD19-53AFF4B05118.Parameter", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "SsmParameterValue:--aws--service--ami-amazon-linux-latest--amzn2-ami-hvm-x86_64-gp2:C96584B6-F00A-464E-AD19-53AFF4B05118": { + "id": "SsmParameterValue:--aws--service--ami-amazon-linux-latest--amzn2-ami-hvm-x86_64-gp2:C96584B6-F00A-464E-AD19-53AFF4B05118", + "path": "integ-ec2-instance/SsmParameterValue:--aws--service--ami-amazon-linux-latest--amzn2-ami-hvm-x86_64-gp2:C96584B6-F00A-464E-AD19-53AFF4B05118", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "integ-ec2-instance/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "integ-ec2-instance/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "instance-test": { + "id": "instance-test", + "path": "instance-test", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "instance-test/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "instance-test/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.9" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "instance-test/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "instance-test/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "instance-test/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.2.9" + } + } + }, + "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-ec2/test/integ.instance-public.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-ec2/test/integ.instance-public.ts new file mode 100644 index 0000000000000..63b910e6e4fbd --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ec2/test/integ.instance-public.ts @@ -0,0 +1,57 @@ +import { PolicyStatement } from 'aws-cdk-lib/aws-iam'; +import * as cdk from 'aws-cdk-lib'; +import { IntegTest } from '@aws-cdk/integ-tests-alpha'; +import * as ec2 from 'aws-cdk-lib/aws-ec2'; +import { EC2_RESTRICT_DEFAULT_SECURITY_GROUP } from 'aws-cdk-lib/cx-api'; + +const app = new cdk.App(); + +class TestStack extends cdk.Stack { + constructor(scope: cdk.App, id: string, props?: cdk.StackProps) { + super(scope, id, props); + this.node.setContext(EC2_RESTRICT_DEFAULT_SECURITY_GROUP, false); + + const vpc = new ec2.Vpc(this, 'VPC', { + cidr: '10.0.0.0/16', + natGateways: 0, + maxAzs: 3, + subnetConfiguration: [ + { + name: 'public-subnet-1', + subnetType: ec2.SubnetType.PUBLIC, + cidrMask: 24, + }, + ], + }); + + const securityGroup = new ec2.SecurityGroup(this, 'IntegSg', { + vpc, + allowAllIpv6Outbound: true, + }); + + const instance = new ec2.Instance(this, 'Instance', { + vpc, + vpcSubnets: { subnetGroupName: 'public-subnet-1' }, + securityGroup, + instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.NANO), + machineImage: new ec2.AmazonLinuxImage({ generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2 }), + detailedMonitoring: true, + associatePublicIpAddress: true, + }); + + instance.addToRolePolicy(new PolicyStatement({ + actions: ['ssm:*'], + resources: ['*'], + })); + + instance.connections.allowFromAnyIpv4(ec2.Port.icmpPing()); + + instance.addUserData('yum install -y'); + } +} + +const testCase = new TestStack(app, 'integ-ec2-instance'); + +new IntegTest(app, 'instance-test', { + testCases: [testCase], +}); diff --git a/packages/aws-cdk-lib/aws-ec2/README.md b/packages/aws-cdk-lib/aws-ec2/README.md index 84a4b63436246..4dde61bfa3fa8 100644 --- a/packages/aws-cdk-lib/aws-ec2/README.md +++ b/packages/aws-cdk-lib/aws-ec2/README.md @@ -1553,6 +1553,34 @@ const aspect = new ec2.InstanceRequireImdsv2Aspect(); Aspects.of(this).add(aspect); ``` +### Associating a Public IP Address with an Instance + +All subnets have an attribute that determines whether instances launched into that subnet are assigned a public IPv4 address. This attribute is set to true by default for default public subnets. Thus, an EC2 instance launched into a default public subnet will be assigned a public IPv4 address. Nondefault public subnets have this attribute set to false by default and any EC2 instance launched into a nondefault public subnet will not be assigned a public IPv4 address automatically. To automatically assign a public IPv4 address to an instance launched into a nondefault public subnet, you can set the `associatePublicIpAddress` property on the `Instance` construct to true. Alternatively, to not automatically assign a public IPv4 address to an instance launched into a default public subnet, you can set `associatePublicIpAddress` to false. Including this property, removing this property, or updating the value of this property on an existing instance will result in replacement of the instance. + +```ts +const vpc = new ec2.Vpc(this, 'VPC', { + cidr: '10.0.0.0/16', + natGateways: 0, + maxAzs: 3, + subnetConfiguration: [ + { + name: 'public-subnet-1', + subnetType: ec2.SubnetType.PUBLIC, + cidrMask: 24, + }, + ], +}); + +const instance = new ec2.Instance(this, 'Instance', { + vpc, + vpcSubnets: { subnetGroupName: 'public-subnet-1' }, + instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.NANO), + machineImage: new ec2.AmazonLinuxImage({ generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2 }), + detailedMonitoring: true, + associatePublicIpAddress: true, +}); +``` + ## VPC Flow Logs VPC Flow Logs is a feature that enables you to capture information about the IP traffic going to and from network interfaces in your VPC. Flow log data can be published to Amazon CloudWatch Logs and Amazon S3. After you've created a flow log, you can retrieve and view its data in the chosen destination. (). diff --git a/packages/aws-cdk-lib/aws-ec2/lib/instance.ts b/packages/aws-cdk-lib/aws-ec2/lib/instance.ts index e9f877cff1558..b18911161a0ee 100644 --- a/packages/aws-cdk-lib/aws-ec2/lib/instance.ts +++ b/packages/aws-cdk-lib/aws-ec2/lib/instance.ts @@ -271,6 +271,13 @@ export interface InstanceProps { * @default false */ readonly ssmSessionPermissions?: boolean; + + /** + * Whether to associate a public IP address to the primary network interface attached to this instance. + * + * @default - public IP address is automatically assigned based on default behavior + */ + readonly associatePublicIpAddress?: boolean; } /** @@ -373,7 +380,7 @@ export class Instance extends Resource implements IInstance { const userDataToken = Lazy.string({ produce: () => Fn.base64(this.userData.render()) }); const securityGroupsToken = Lazy.list({ produce: () => this.securityGroups.map(sg => sg.securityGroupId) }); - const { subnets } = props.vpc.selectSubnets(props.vpcSubnets); + const { subnets, hasPublic } = props.vpc.selectSubnets(props.vpcSubnets); let subnet; if (props.availabilityZone) { const selected = subnets.filter(sn => sn.availabilityZone === props.availabilityZone); @@ -398,14 +405,22 @@ export class Instance extends Resource implements IInstance { }); } + // network interfaces array is set to configure the primary network interface if associatePublicIpAddress is true or false + const networkInterfaces = props.associatePublicIpAddress !== undefined + ? [{ deviceIndex: '0', associatePublicIpAddress: props.associatePublicIpAddress, subnetId: subnet.subnetId, groupSet: securityGroupsToken }] + : undefined; + + // if network interfaces array is configured then subnetId and securityGroupIds are configured on the network interface + // level and there is no need to configure them on the instance level this.instance = new CfnInstance(this, 'Resource', { imageId: imageConfig.imageId, keyName: props.keyName, instanceType: props.instanceType.toString(), - securityGroupIds: securityGroupsToken, + subnetId: networkInterfaces ? undefined : subnet.subnetId, + securityGroupIds: networkInterfaces ? undefined : securityGroupsToken, + networkInterfaces, iamInstanceProfile: iamProfile.ref, userData: userDataToken, - subnetId: subnet.subnetId, availabilityZone: subnet.availabilityZone, sourceDestCheck: props.sourceDestCheck, blockDeviceMappings: props.blockDevices !== undefined ? instanceBlockDeviceMappings(this, props.blockDevices) : undefined, @@ -415,6 +430,16 @@ export class Instance extends Resource implements IInstance { }); this.instance.node.addDependency(this.role); + // if associatePublicIpAddress is true, then there must be a dependency on internet connectivity + if (props.associatePublicIpAddress !== undefined && props.associatePublicIpAddress) { + const internetConnected = props.vpc.selectSubnets(props.vpcSubnets).internetConnectivityEstablished; + this.instance.node.addDependency(internetConnected); + } + + if (!hasPublic && props.associatePublicIpAddress) { + throw new Error("To set 'associatePublicIpAddress: true' you must select Public subnets (vpcSubnets: { subnetType: SubnetType.PUBLIC })"); + } + this.osType = imageConfig.osType; this.node.defaultChild = this.instance; diff --git a/packages/aws-cdk-lib/aws-ec2/test/instance.test.ts b/packages/aws-cdk-lib/aws-ec2/test/instance.test.ts index 16ccd3bd75f21..42de157891c8c 100644 --- a/packages/aws-cdk-lib/aws-ec2/test/instance.test.ts +++ b/packages/aws-cdk-lib/aws-ec2/test/instance.test.ts @@ -19,6 +19,8 @@ import { LaunchTemplate, UserData, Vpc, + SubnetType, + SecurityGroup, } from '../lib'; let stack: Stack; @@ -644,3 +646,94 @@ test('sameInstanceClassAs compares different InstanceTypes correctly', () => { expect(instanceType.sameInstanceClassAs(comparitor)).toBeFalsy(); }); +test('associate public IP address with instance', () => { + // GIVEN + const securityGroup = new SecurityGroup(stack, 'SecurityGroup', { vpc }); + + // WHEN + new Instance(stack, 'Instance', { + vpc, + vpcSubnets: { subnetType: SubnetType.PUBLIC }, + securityGroup, + machineImage: new AmazonLinuxImage(), + instanceType: InstanceType.of(InstanceClass.T3, InstanceSize.LARGE), + sourceDestCheck: false, + associatePublicIpAddress: true, + }); + + // THEN + Template.fromStack(stack).hasResource('AWS::EC2::Instance', { + Properties: { + NetworkInterfaces: [{ + AssociatePublicIpAddress: true, + DeviceIndex: '0', + GroupSet: [ + { + 'Fn::GetAtt': [ + 'SecurityGroupDD263621', + 'GroupId', + ], + }, + ], + SubnetId: { + Ref: 'VPCPublicSubnet1SubnetB4246D30', + }, + }], + }, + DependsOn: [ + 'InstanceInstanceRoleE9785DE5', + 'VPCPublicSubnet1DefaultRoute91CEF279', + 'VPCPublicSubnet1RouteTableAssociation0B0896DC', + 'VPCPublicSubnet2DefaultRouteB7481BBA', + 'VPCPublicSubnet2RouteTableAssociation5A808732', + ], + }); +}); + +test('do not associate public IP address with instance', () => { + // GIVEN + const securityGroup = new SecurityGroup(stack, 'SecurityGroup', { vpc }); + + // WHEN + new Instance(stack, 'Instance', { + vpc, + vpcSubnets: { subnetType: SubnetType.PUBLIC }, + securityGroup, + machineImage: new AmazonLinuxImage(), + instanceType: InstanceType.of(InstanceClass.T3, InstanceSize.LARGE), + sourceDestCheck: false, + associatePublicIpAddress: false, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::EC2::Instance', { + NetworkInterfaces: [{ + AssociatePublicIpAddress: false, + DeviceIndex: '0', + GroupSet: [ + { + 'Fn::GetAtt': [ + 'SecurityGroupDD263621', + 'GroupId', + ], + }, + ], + SubnetId: { + Ref: 'VPCPublicSubnet1SubnetB4246D30', + }, + }], + }); +}); + +test('associate public IP address with instance and no public subnet', () => { + // WHEN/THEN + expect(() => { + new Instance(stack, 'Instance', { + vpc, + machineImage: new AmazonLinuxImage(), + instanceType: InstanceType.of(InstanceClass.T3, InstanceSize.LARGE), + sourceDestCheck: false, + associatePublicIpAddress: true, + }); + }).toThrow("To set 'associatePublicIpAddress: true' you must select Public subnets (vpcSubnets: { subnetType: SubnetType.PUBLIC })"); +});