moto/tests/test_ec2/test_ec2_cloudformation.py
2022-09-16 10:01:43 +00:00

944 lines
34 KiB
Python

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)