from botocore.exceptions import ClientError from moto import mock_cloudformation, mock_ec2 from tests import EXAMPLE_AMI_ID from tests.test_cloudformation.fixtures import ec2_classic_eip from tests.test_cloudformation.fixtures import single_instance_with_ebs_volume from tests.test_cloudformation.fixtures import vpc_eip from tests.test_cloudformation.fixtures import vpc_eni from tests.test_cloudformation.fixtures import vpc_single_instance_in_subnet from uuid import uuid4 import boto3 import json import sure # noqa # pylint: disable=unused-import import pytest template_vpc = { "AWSTemplateFormatVersion": "2010-09-09", "Description": "Create VPC", "Resources": { "VPC": {"Properties": {"CidrBlock": "192.168.0.0/16"}, "Type": "AWS::EC2::VPC"} }, } @mock_ec2 @mock_cloudformation def test_vpc_single_instance_in_subnet(): template_json = json.dumps(vpc_single_instance_in_subnet.template) cf = boto3.client("cloudformation", region_name="us-west-1") stack_name = str(uuid4())[0:6] cf.create_stack( StackName=stack_name, TemplateBody=template_json, Parameters=[{"ParameterKey": "KeyName", "ParameterValue": "my_key"}], ) ec2 = boto3.client("ec2", region_name="us-west-1") stack = cf.describe_stacks(StackName=stack_name)["Stacks"][0] resources = cf.list_stack_resources(StackName=stack_name)["StackResourceSummaries"] vpc_id = [ resource for resource in resources if resource["ResourceType"] == "AWS::EC2::VPC" ][0]["PhysicalResourceId"] vpc = ec2.describe_vpcs(VpcIds=[vpc_id])["Vpcs"][0] vpc["CidrBlock"].should.equal("10.0.0.0/16") vpc["Tags"].should.contain({"Key": "Application", "Value": stack["StackId"]}) security_group = ec2.describe_security_groups( Filters=[{"Name": "vpc-id", "Values": [vpc["VpcId"]]}] )["SecurityGroups"][0] security_group["VpcId"].should.equal(vpc["VpcId"]) subnet_id = [ resource for resource in resources if resource["ResourceType"] == "AWS::EC2::Subnet" ][0]["PhysicalResourceId"] subnet = ec2.describe_subnets(SubnetIds=[subnet_id])["Subnets"][0] subnet["VpcId"].should.equal(vpc["VpcId"]) instance_id = [ resource for resource in resources if resource["ResourceType"] == "AWS::EC2::Instance" ][0]["PhysicalResourceId"] res = ec2.describe_instances(InstanceIds=[instance_id])["Reservations"][0] instance = res["Instances"][0] instance["Tags"].should.contain({"Key": "Foo", "Value": "Bar"}) eip_id = [ resource for resource in resources if resource["ResourceType"] == "AWS::EC2::EIP" ][0]["PhysicalResourceId"] eip = ec2.describe_addresses(PublicIps=[eip_id])["Addresses"][0] eip["Domain"].should.equal("vpc") eip["InstanceId"].should.equal(instance["InstanceId"]) @mock_cloudformation @mock_ec2 def test_delete_stack_with_resource_missing_delete_attr(): cf = boto3.client("cloudformation", region_name="us-east-1") ec2 = boto3.client("ec2", region_name="us-east-1") name = str(uuid4())[0:6] cf.create_stack(StackName=name, TemplateBody=json.dumps(template_vpc)) cf.describe_stacks(StackName=name)["Stacks"].should.have.length_of(1) resources = cf.list_stack_resources(StackName=name)["StackResourceSummaries"] vpc_id = resources[0]["PhysicalResourceId"] cf.delete_stack( StackName=name ) # should succeed, despite the fact that the resource itself cannot be deleted with pytest.raises(ClientError) as exc: cf.describe_stacks(StackName=name) err = exc.value.response["Error"] err.should.have.key("Code").equals("ValidationError") err.should.have.key("Message").equals(f"Stack with id {name} does not exist") # We still have our VPC, as the VPC-object does not have a delete-method yet ec2.describe_vpcs(VpcIds=[vpc_id])["Vpcs"].should.have.length_of(1) @mock_ec2 @mock_cloudformation def test_elastic_network_interfaces_cloudformation_boto3(): template = vpc_eni.template template_json = json.dumps(template) cf = boto3.client("cloudformation", region_name="us-west-1") stack_name = str(uuid4())[0:6] cf.create_stack(StackName=stack_name, TemplateBody=template_json) ec2 = boto3.client("ec2", region_name="us-west-1") all_enis = ec2.describe_network_interfaces()["NetworkInterfaces"] all_eni_ids = [eni["NetworkInterfaceId"] for eni in all_enis] all_ips = [eni["PrivateIpAddresses"][0]["PrivateIpAddress"] for eni in all_enis] resources = cf.list_stack_resources(StackName=stack_name)["StackResourceSummaries"] cfn_eni = [ resource for resource in resources if resource["ResourceType"] == "AWS::EC2::NetworkInterface" ][0] all_eni_ids.should.contain(cfn_eni["PhysicalResourceId"]) outputs = cf.describe_stacks(StackName=stack_name)["Stacks"][0]["Outputs"] received_ip = [ o["OutputValue"] for o in outputs if o["OutputKey"] == "ENIIpAddress" ][0] all_ips.should.contain(received_ip) @mock_ec2 @mock_cloudformation def test_volume_size_through_cloudformation(): ec2 = boto3.client("ec2", region_name="us-east-1") cf = boto3.client("cloudformation", region_name="us-east-1") tag_value = str(uuid4()) volume_template = { "AWSTemplateFormatVersion": "2010-09-09", "Resources": { "testInstance": { "Type": "AWS::EC2::Instance", "Properties": { "ImageId": EXAMPLE_AMI_ID, "KeyName": "dummy", "InstanceType": "t2.micro", "BlockDeviceMappings": [ {"DeviceName": "/dev/sda2", "Ebs": {"VolumeSize": "50"}} ], "Tags": [ {"Key": "foo", "Value": "bar"}, {"Key": "blah", "Value": tag_value}, ], }, } }, } template_json = json.dumps(volume_template) stack_name = str(uuid4())[0:6] cf.create_stack(StackName=stack_name, TemplateBody=template_json) resource = cf.list_stack_resources(StackName=stack_name)["StackResourceSummaries"][ 0 ] resource.should.have.key("LogicalResourceId").being.equal("testInstance") resource.should.have.key("PhysicalResourceId").shouldnt.be.none resource.should.have.key("ResourceType").being.equal("AWS::EC2::Instance") instances = ec2.describe_instances(InstanceIds=[resource["PhysicalResourceId"]]) volume = instances["Reservations"][0]["Instances"][0]["BlockDeviceMappings"][0][ "Ebs" ] volumes = ec2.describe_volumes(VolumeIds=[volume["VolumeId"]]) volumes["Volumes"][0]["Size"].should.equal(50) @mock_ec2 @mock_cloudformation def test_attach_internet_gateway(): ec2 = boto3.client("ec2", region_name="us-east-1") cf = boto3.client("cloudformation", region_name="us-east-1") volume_template = { "AWSTemplateFormatVersion": "2010-09-09", "Resources": { "DEVLAB1": { "Type": "AWS::EC2::VPC", "Properties": {"CidrBlock": "10.0.0.0/16"}, }, "internetgateway": {"Type": "AWS::EC2::InternetGateway"}, "DEVLAB1VPGAttaching": { "Type": "AWS::EC2::VPCGatewayAttachment", "Properties": { "VpcId": {"Ref": "DEVLAB1"}, "InternetGatewayId": {"Ref": "internetgateway"}, }, }, }, } template_json = json.dumps(volume_template) stack_name = str(uuid4())[0:6] cf.create_stack(StackName=stack_name, TemplateBody=template_json) stack_resources = cf.list_stack_resources(StackName=stack_name)[ "StackResourceSummaries" ] # Verify VPC is created vpc = [r for r in stack_resources if r["ResourceType"] == "AWS::EC2::VPC"][0] vpc["LogicalResourceId"].should.equal("DEVLAB1") vpc_id = vpc["PhysicalResourceId"] # Verify Internet Gateway is created gateway_id = get_resource_id("AWS::EC2::InternetGateway", stack_resources) gateway = ec2.describe_internet_gateways(InternetGatewayIds=[gateway_id])[ "InternetGateways" ][0] gateway["Attachments"].should.contain({"State": "available", "VpcId": vpc_id}) gateway["Tags"].should.contain( {"Key": "aws:cloudformation:logical-id", "Value": "internetgateway"} ) @mock_ec2 @mock_cloudformation def test_attach_vpn_gateway(): ec2 = boto3.client("ec2", region_name="us-east-1") cf = boto3.client("cloudformation", region_name="us-east-1") vpn_gateway_template = { "AWSTemplateFormatVersion": "2010-09-09", "Resources": { "DEVLAB1": { "Type": "AWS::EC2::VPC", "Properties": {"CidrBlock": "10.0.0.0/16"}, }, "DEVLAB1DCGateway": { "Type": "AWS::EC2::VPNGateway", "Properties": {"Type": "ipsec.1"}, }, "DEVLAB1VPGAttaching": { "Type": "AWS::EC2::VPCGatewayAttachment", "Properties": { "VpcId": {"Ref": "DEVLAB1"}, "VpnGatewayId": {"Ref": "DEVLAB1DCGateway"}, }, "DependsOn": ["DEVLAB1DCGateway"], }, }, } template_json = json.dumps(vpn_gateway_template) stack_name = str(uuid4())[0:6] cf.create_stack(StackName=stack_name, TemplateBody=template_json) stack_resources = cf.list_stack_resources(StackName=stack_name)[ "StackResourceSummaries" ] gateway_id = get_resource_id("AWS::EC2::VPNGateway", stack_resources) vpc_id = get_resource_id("AWS::EC2::VPC", stack_resources) gateway = ec2.describe_vpn_gateways(VpnGatewayIds=[gateway_id])["VpnGateways"][0] gateway["VpcAttachments"].should.contain({"State": "attached", "VpcId": vpc_id}) def get_resource_id(resource_type, stack_resources): r = [r for r in stack_resources if r["ResourceType"] == resource_type][0] return r["PhysicalResourceId"] @mock_ec2 @mock_cloudformation def test_subnet_tags_through_cloudformation_boto3(): ec2 = boto3.client("ec2", region_name="us-west-1") ec2_res = boto3.resource("ec2", region_name="us-west-1") vpc = ec2_res.create_vpc(CidrBlock="10.0.0.0/16") subnet_template = { "AWSTemplateFormatVersion": "2010-09-09", "Resources": { "testSubnet": { "Type": "AWS::EC2::Subnet", "Properties": { "VpcId": vpc.id, "CidrBlock": "10.0.0.0/24", "AvailabilityZone": "us-west-1b", "Tags": [ {"Key": "foo", "Value": "bar"}, {"Key": "blah", "Value": "baz"}, ], }, } }, } template_json = json.dumps(subnet_template) cf = boto3.client("cloudformation", region_name="us-west-1") stack_name = str(uuid4())[0:6] cf.create_stack(StackName=stack_name, TemplateBody=template_json) resources = cf.list_stack_resources(StackName=stack_name)["StackResourceSummaries"] subnet_id = resources[0]["PhysicalResourceId"] subnet = ec2.describe_subnets(SubnetIds=[subnet_id])["Subnets"][0] subnet["CidrBlock"].should.equal("10.0.0.0/24") subnet["Tags"].should.contain({"Key": "foo", "Value": "bar"}) subnet["Tags"].should.contain({"Key": "blah", "Value": "baz"}) @mock_ec2 @mock_cloudformation def test_single_instance_with_ebs_volume(): template_json = json.dumps(single_instance_with_ebs_volume.template) cf = boto3.client("cloudformation", region_name="us-west-1") stack_name = str(uuid4())[0:6] cf.create_stack( StackName=stack_name, TemplateBody=template_json, Parameters=[{"ParameterKey": "KeyName", "ParameterValue": "key_name"}], ) resources = cf.list_stack_resources(StackName=stack_name)["StackResourceSummaries"] instance_id = [ r["PhysicalResourceId"] for r in resources if r["ResourceType"] == "AWS::EC2::Instance" ][0] volume_id = [ r["PhysicalResourceId"] for r in resources if r["ResourceType"] == "AWS::EC2::Volume" ][0] ec2 = boto3.client("ec2", region_name="us-west-1") ec2_instance = ec2.describe_instances(InstanceIds=[instance_id])["Reservations"][0][ "Instances" ][0] volumes = ec2.describe_volumes(VolumeIds=[volume_id])["Volumes"] # Grab the mounted drive volume = [ volume for volume in volumes if volume["Attachments"][0]["Device"] == "/dev/sdh" ][0] volume["State"].should.equal("in-use") volume["Attachments"][0]["InstanceId"].should.equal(ec2_instance["InstanceId"]) @mock_ec2 @mock_cloudformation def test_classic_eip(): template_json = json.dumps(ec2_classic_eip.template) cf = boto3.client("cloudformation", region_name="us-west-1") stack_name = str(uuid4())[0:6] cf.create_stack(StackName=stack_name, TemplateBody=template_json) ec2 = boto3.client("ec2", region_name="us-west-1") all_ips = [eip["PublicIp"] for eip in ec2.describe_addresses()["Addresses"]] resources = cf.list_stack_resources(StackName=stack_name)["StackResourceSummaries"] cfn_eip = [ resource for resource in resources if resource["ResourceType"] == "AWS::EC2::EIP" ][0] all_ips.should.contain(cfn_eip["PhysicalResourceId"]) @mock_ec2 @mock_cloudformation def test_vpc_eip(): template_json = json.dumps(vpc_eip.template) cf = boto3.client("cloudformation", region_name="us-west-1") stack_name = str(uuid4())[0:6] cf.create_stack(StackName=stack_name, TemplateBody=template_json) ec2 = boto3.client("ec2", region_name="us-west-1") all_ips = [eip["PublicIp"] for eip in ec2.describe_addresses()["Addresses"]] resources = cf.list_stack_resources(StackName=stack_name)["StackResourceSummaries"] cfn_eip = [ resource for resource in resources if resource["ResourceType"] == "AWS::EC2::EIP" ][0] all_ips.should.contain(cfn_eip["PhysicalResourceId"]) @mock_cloudformation @mock_ec2 def test_vpc_gateway_attachment_creation_should_attach_itself_to_vpc(): template = { "AWSTemplateFormatVersion": "2010-09-09", "Resources": { "internetgateway": {"Type": "AWS::EC2::InternetGateway"}, "testvpc": { "Type": "AWS::EC2::VPC", "Properties": { "CidrBlock": "10.0.0.0/16", "EnableDnsHostnames": "true", "EnableDnsSupport": "true", "InstanceTenancy": "default", }, }, "vpcgatewayattachment": { "Type": "AWS::EC2::VPCGatewayAttachment", "Properties": { "InternetGatewayId": {"Ref": "internetgateway"}, "VpcId": {"Ref": "testvpc"}, }, }, }, } template_json = json.dumps(template) cf = boto3.client("cloudformation", region_name="us-west-1") stack_name = str(uuid4())[0:6] cf.create_stack(StackName=stack_name, TemplateBody=template_json) resources = cf.list_stack_resources(StackName=stack_name)["StackResourceSummaries"] vpc_id = resources[1]["PhysicalResourceId"] ec2 = boto3.client("ec2", region_name="us-west-1") vpc = ec2.describe_vpcs(VpcIds=[vpc_id])["Vpcs"][0] vpc["CidrBlock"].should.equal("10.0.0.0/16") igws = ec2.describe_internet_gateways( Filters=[{"Name": "attachment.vpc-id", "Values": [vpc["VpcId"]]}] )["InternetGateways"] igws.should.have.length_of(1) @mock_cloudformation @mock_ec2 def test_vpc_peering_creation(): ec2 = boto3.resource("ec2", region_name="us-west-1") ec2_client = boto3.client("ec2", region_name="us-west-1") vpc_source = ec2.create_vpc(CidrBlock="10.0.0.0/16") peer_vpc = ec2.create_vpc(CidrBlock="10.1.0.0/16") template = { "AWSTemplateFormatVersion": "2010-09-09", "Resources": { "vpcpeeringconnection": { "Type": "AWS::EC2::VPCPeeringConnection", "Properties": {"PeerVpcId": peer_vpc.id, "VpcId": vpc_source.id}, } }, } template_json = json.dumps(template) cf = boto3.client("cloudformation", region_name="us-west-1") stack_name = str(uuid4())[0:6] cf.create_stack(StackName=stack_name, TemplateBody=template_json) resources = cf.list_stack_resources(StackName=stack_name)["StackResourceSummaries"] our_vpx_id = resources[0]["PhysicalResourceId"] peering_connections = ec2_client.describe_vpc_peering_connections( VpcPeeringConnectionIds=[our_vpx_id] )["VpcPeeringConnections"] peering_connections.should.have.length_of(1) @mock_cloudformation @mock_ec2 def test_multiple_security_group_ingress_separate_from_security_group_by_id(): sg1 = str(uuid4())[0:6] sg2 = str(uuid4())[0:6] template = { "AWSTemplateFormatVersion": "2010-09-09", "Resources": { "test-security-group1": { "Type": "AWS::EC2::SecurityGroup", "Properties": { "GroupDescription": "test security group", "Tags": [{"Key": "sg-name", "Value": sg1}], }, }, "test-security-group2": { "Type": "AWS::EC2::SecurityGroup", "Properties": { "GroupDescription": "test security group", "Tags": [{"Key": "sg-name", "Value": sg2}], }, }, "test-sg-ingress": { "Type": "AWS::EC2::SecurityGroupIngress", "Properties": { "GroupId": {"Ref": "test-security-group1"}, "IpProtocol": "tcp", "FromPort": "80", "ToPort": "8080", "SourceSecurityGroupId": {"Ref": "test-security-group2"}, }, }, }, } template_json = json.dumps(template) cf = boto3.client("cloudformation", region_name="us-west-1") cf.create_stack(StackName=str(uuid4())[0:6], TemplateBody=template_json) ec2 = boto3.client("ec2", region_name="us-west-1") security_group1 = get_secgroup_by_tag(ec2, sg1) security_group2 = get_secgroup_by_tag(ec2, sg2) security_group1["IpPermissions"].should.have.length_of(1) security_group1["IpPermissions"][0]["UserIdGroupPairs"].should.have.length_of(1) security_group1["IpPermissions"][0]["UserIdGroupPairs"][0]["GroupId"].should.equal( security_group2["GroupId"] ) security_group1["IpPermissions"][0]["IpProtocol"].should.equal("tcp") security_group1["IpPermissions"][0]["FromPort"].should.equal(80) security_group1["IpPermissions"][0]["ToPort"].should.equal(8080) @mock_cloudformation @mock_ec2 def test_security_group_ingress_separate_from_security_group_by_id(): ec2 = boto3.client("ec2", region_name="us-west-1") sg_name = str(uuid4()) ec2.create_security_group(GroupName=sg_name, Description="test security group") sg_2 = str(uuid4())[0:6] template = { "AWSTemplateFormatVersion": "2010-09-09", "Resources": { "test-security-group2": { "Type": "AWS::EC2::SecurityGroup", "Properties": { "GroupDescription": "test security group", "Tags": [{"Key": "sg-name", "Value": sg_2}], }, }, "test-sg-ingress": { "Type": "AWS::EC2::SecurityGroupIngress", "Properties": { "GroupName": sg_name, "IpProtocol": "tcp", "FromPort": "80", "ToPort": "8080", "SourceSecurityGroupId": {"Ref": "test-security-group2"}, }, }, }, } template_json = json.dumps(template) cf = boto3.client("cloudformation", region_name="us-west-1") cf.create_stack(StackName=str(uuid4())[0:6], TemplateBody=template_json) security_group1 = ec2.describe_security_groups(GroupNames=[sg_name])[ "SecurityGroups" ][0] security_group2 = get_secgroup_by_tag(ec2, sg_2) security_group1["IpPermissions"].should.have.length_of(1) security_group1["IpPermissions"][0]["UserIdGroupPairs"].should.have.length_of(1) security_group1["IpPermissions"][0]["UserIdGroupPairs"][0]["GroupId"].should.equal( security_group2["GroupId"] ) security_group1["IpPermissions"][0]["IpProtocol"].should.equal("tcp") security_group1["IpPermissions"][0]["FromPort"].should.equal(80) security_group1["IpPermissions"][0]["ToPort"].should.equal(8080) @mock_cloudformation @mock_ec2 def test_security_group_ingress_separate_from_security_group_by_id_using_vpc(): ec2 = boto3.resource("ec2", region_name="us-west-1") ec2_client = boto3.client("ec2", region_name="us-west-1") vpc = ec2.create_vpc(CidrBlock="10.0.0.0/16") template = { "AWSTemplateFormatVersion": "2010-09-09", "Resources": { "test-security-group1": { "Type": "AWS::EC2::SecurityGroup", "Properties": { "GroupDescription": "test security group", "VpcId": vpc.id, "Tags": [{"Key": "sg-name", "Value": "sg1"}], }, }, "test-security-group2": { "Type": "AWS::EC2::SecurityGroup", "Properties": { "GroupDescription": "test security group", "VpcId": vpc.id, "Tags": [{"Key": "sg-name", "Value": "sg2"}], }, }, "test-sg-ingress": { "Type": "AWS::EC2::SecurityGroupIngress", "Properties": { "GroupId": {"Ref": "test-security-group1"}, "VpcId": vpc.id, "IpProtocol": "tcp", "FromPort": "80", "ToPort": "8080", "SourceSecurityGroupId": {"Ref": "test-security-group2"}, }, }, }, } template_json = json.dumps(template) cf = boto3.client("cloudformation", region_name="us-west-1") cf.create_stack(StackName=str(uuid4())[0:6], TemplateBody=template_json) security_group1 = get_secgroup_by_tag(ec2_client, "sg1") security_group2 = get_secgroup_by_tag(ec2_client, "sg2") security_group1["IpPermissions"].should.have.length_of(1) security_group1["IpPermissions"][0]["UserIdGroupPairs"].should.have.length_of(1) security_group1["IpPermissions"][0]["UserIdGroupPairs"][0]["GroupId"].should.equal( security_group2["GroupId"] ) security_group1["IpPermissions"][0]["IpProtocol"].should.equal("tcp") security_group1["IpPermissions"][0]["FromPort"].should.equal(80) security_group1["IpPermissions"][0]["ToPort"].should.equal(8080) @mock_cloudformation @mock_ec2 def test_security_group_with_update(): ec2 = boto3.resource("ec2", region_name="us-west-1") ec2_client = boto3.client("ec2", region_name="us-west-1") vpc1 = ec2.create_vpc(CidrBlock="10.0.0.0/16") vpc2 = ec2.create_vpc(CidrBlock="10.1.0.0/16") sg = str(uuid4())[0:6] template = { "AWSTemplateFormatVersion": "2010-09-09", "Resources": { "test-security-group": { "Type": "AWS::EC2::SecurityGroup", "Properties": { "GroupDescription": "test security group", "VpcId": vpc1.id, "Tags": [{"Key": "sg-name", "Value": sg}], }, } }, } template_json = json.dumps(template) cf = boto3.client("cloudformation", region_name="us-west-1") stack_name = str(uuid4())[0:6] cf.create_stack(StackName=stack_name, TemplateBody=template_json) security_group = get_secgroup_by_tag(ec2_client, sg) security_group["VpcId"].should.equal(vpc1.id) template["Resources"]["test-security-group"]["Properties"]["VpcId"] = vpc2.id template_json = json.dumps(template) cf.update_stack(StackName=stack_name, TemplateBody=template_json) security_group = get_secgroup_by_tag(ec2_client, sg) security_group["VpcId"].should.equal(vpc2.id) @mock_cloudformation @mock_ec2 def test_subnets_should_be_created_with_availability_zone(): ec2 = boto3.resource("ec2", region_name="us-west-1") ec2_client = boto3.client("ec2", region_name="us-west-1") vpc = ec2.create_vpc(CidrBlock="10.0.0.0/16") subnet_template = { "AWSTemplateFormatVersion": "2010-09-09", "Resources": { "testSubnet": { "Type": "AWS::EC2::Subnet", "Properties": { "VpcId": vpc.id, "CidrBlock": "10.0.0.0/24", "AvailabilityZone": "us-west-1b", }, } }, } cf = boto3.client("cloudformation", region_name="us-west-1") template_json = json.dumps(subnet_template) stack_name = str(uuid4())[0:6] cf.create_stack(StackName=stack_name, TemplateBody=template_json) resources = cf.list_stack_resources(StackName=stack_name)["StackResourceSummaries"] subnet_id = resources[0]["PhysicalResourceId"] subnet = ec2_client.describe_subnets(SubnetIds=[subnet_id])["Subnets"][0] subnet["CidrBlock"].should.equal("10.0.0.0/24") subnet["AvailabilityZone"].should.equal("us-west-1b") def get_secgroup_by_tag(ec2, sg_): return ec2.describe_security_groups( Filters=[{"Name": "tag:sg-name", "Values": [sg_]}] )["SecurityGroups"][0] @mock_cloudformation @mock_ec2 def test_vpc_endpoint_creation(): ec2 = boto3.resource("ec2", region_name="us-west-1") ec2_client = boto3.client("ec2", region_name="us-west-1") vpc = ec2.create_vpc(CidrBlock="10.0.0.0/16") subnet1 = ec2.create_subnet( VpcId=vpc.id, CidrBlock="10.0.0.0/24", AvailabilityZone="us-west-1a" ) subnet_template = { "AWSTemplateFormatVersion": "2010-09-09", "Parameters": { "EndpointSubnetId": {"Type": "String"}, "EndpointVpcId": {"Type": "String"}, "EndpointServiceName": {"Type": "String"}, }, "Resources": { "GwlbVpcEndpoint": { "Type": "AWS::EC2::VPCEndpoint", "Properties": { "ServiceName": {"Ref": "EndpointServiceName"}, "SubnetIds": [{"Ref": "EndpointSubnetId"}], "VpcEndpointType": "GatewayLoadBalancer", "VpcId": {"Ref": "EndpointVpcId"}, }, } }, "Outputs": { "EndpointId": { "Description": "Id of the endpoint created", "Value": {"Ref": "GwlbVpcEndpoint"}, }, }, } cf = boto3.client("cloudformation", region_name="us-west-1") template_json = json.dumps(subnet_template) stack_name = str(uuid4())[0:6] cf.create_stack( StackName=stack_name, TemplateBody=template_json, Parameters=[ {"ParameterKey": "EndpointSubnetId", "ParameterValue": subnet1.id}, {"ParameterKey": "EndpointVpcId", "ParameterValue": vpc.id}, {"ParameterKey": "EndpointServiceName", "ParameterValue": "serv_name"}, ], ) resources = cf.list_stack_resources(StackName=stack_name)["StackResourceSummaries"] resources.should.have.length_of(1) resources[0].should.have.key("LogicalResourceId").equals("GwlbVpcEndpoint") vpc_endpoint_id = resources[0]["PhysicalResourceId"] outputs = cf.describe_stacks(StackName=stack_name)["Stacks"][0]["Outputs"] outputs.should.have.length_of(1) outputs[0].should.equal({"OutputKey": "EndpointId", "OutputValue": vpc_endpoint_id}) endpoint = ec2_client.describe_vpc_endpoints(VpcEndpointIds=[vpc_endpoint_id])[ "VpcEndpoints" ][0] endpoint.should.have.key("VpcId").equals(vpc.id) endpoint.should.have.key("ServiceName").equals("serv_name") endpoint.should.have.key("State").equals("available") endpoint.should.have.key("SubnetIds").equals([subnet1.id]) endpoint.should.have.key("VpcEndpointType").equals("GatewayLoadBalancer") @mock_cloudformation @mock_ec2 def test_launch_template_create(): cf = boto3.client("cloudformation", region_name="us-west-1") ec2 = boto3.client("ec2", region_name="us-west-1") launch_template_name = str(uuid4())[0:6] logical_id = str(uuid4())[0:6] stack_name = str(uuid4())[0:6] lt_tags = [{"Key": "lt-tag-key", "Value": "lt-tag-value"}] template_json = json.dumps( { "AWSTemplateFormatVersion": "2010-09-09", "Resources": { logical_id: { "Type": "AWS::EC2::LaunchTemplate", "Properties": { "LaunchTemplateName": launch_template_name, "VersionDescription": "some template", "LaunchTemplateData": { "TagSpecifications": [ { "ResourceType": "instance", "Tags": [ {"Key": "i-tag-key", "Value": "i-tag-value"} ], } ] }, "TagSpecifications": [ { "ResourceType": "launch-template", "Tags": lt_tags, } ], }, } }, "Outputs": { "LaunchTemplateId": { "Description": "The ID of the created launch template", "Value": {"Ref": logical_id}, }, }, } ) cf.create_stack(StackName=stack_name, TemplateBody=template_json) resources = cf.list_stack_resources(StackName=stack_name)["StackResourceSummaries"] resources.should.have.length_of(1) resources[0].should.have.key("LogicalResourceId").equals(logical_id) launch_template_id = resources[0]["PhysicalResourceId"] outputs = cf.describe_stacks(StackName=stack_name)["Stacks"][0]["Outputs"] outputs.should.have.length_of(1) outputs[0].should.equal( {"OutputKey": "LaunchTemplateId", "OutputValue": launch_template_id} ) launch_template = ec2.describe_launch_templates( LaunchTemplateNames=[launch_template_name] )["LaunchTemplates"][0] launch_template.should.have.key("LaunchTemplateName").equals(launch_template_name) launch_template.should.have.key("LaunchTemplateId").equals(launch_template_id) launch_template.should.have.key("Tags").equals(lt_tags) launch_template_version = ec2.describe_launch_template_versions( LaunchTemplateName=launch_template_name )["LaunchTemplateVersions"][0] launch_template_version.should.have.key("LaunchTemplateName").equals( launch_template_name ) launch_template_version.should.have.key("LaunchTemplateId").equals( launch_template_id ) launch_template_version["LaunchTemplateData"]["TagSpecifications"][ 0 ].should.have.key("ResourceType").equals("instance") @mock_cloudformation @mock_ec2 def test_launch_template_update(): cf = boto3.client("cloudformation", region_name="us-west-1") ec2 = boto3.client("ec2", region_name="us-west-1") launch_template_name = str(uuid4())[0:6] logical_id = str(uuid4())[0:6] stack_name = str(uuid4())[0:6] template_json = json.dumps( { "AWSTemplateFormatVersion": "2010-09-09", "Resources": { logical_id: { "Type": "AWS::EC2::LaunchTemplate", "Properties": { "LaunchTemplateName": launch_template_name, "VersionDescription": "some template", "LaunchTemplateData": {"UserData": ""}, }, } }, } ) cf.create_stack(StackName=stack_name, TemplateBody=template_json) template_json = json.dumps( { "AWSTemplateFormatVersion": "2010-09-09", "Resources": { logical_id: { "Type": "AWS::EC2::LaunchTemplate", "Properties": { "LaunchTemplateName": launch_template_name, "VersionDescription": "a better template", "LaunchTemplateData": {"UserData": ""}, }, } }, } ) cf.update_stack(StackName=stack_name, TemplateBody=template_json) resources = cf.list_stack_resources(StackName=stack_name)["StackResourceSummaries"] resources.should.have.length_of(1) launch_template_versions = ec2.describe_launch_template_versions( LaunchTemplateName=launch_template_name )["LaunchTemplateVersions"] launch_template_versions.should.have.length_of(2) launch_template_versions[0].should.have.key("VersionDescription").equals( "some template" ) launch_template_versions[1].should.have.key("VersionDescription").equals( "a better template" ) @mock_cloudformation @mock_ec2 def test_launch_template_delete(): cf = boto3.client("cloudformation", region_name="us-west-1") ec2 = boto3.client("ec2", region_name="us-west-1") launch_template_name = str(uuid4())[0:6] logical_id = str(uuid4())[0:6] stack_name = str(uuid4())[0:6] template_json = json.dumps( { "AWSTemplateFormatVersion": "2010-09-09", "Resources": { logical_id: { "Type": "AWS::EC2::LaunchTemplate", "Properties": { "LaunchTemplateName": launch_template_name, "VersionDescription": "some template", "LaunchTemplateData": {"UserData": ""}, }, } }, } ) cf.create_stack(StackName=stack_name, TemplateBody=template_json) cf.delete_stack(StackName=stack_name) ec2.describe_launch_templates(LaunchTemplateNames=[launch_template_name])[ "LaunchTemplates" ].should.have.length_of(0)