Skip to content

Commit 4643b6c

Browse files
ec2: Add support for security groups from NetworkInterfaces in RunInstances
1 parent 1f8a506 commit 4643b6c

2 files changed

Lines changed: 135 additions & 3 deletions

File tree

moto/ec2/models/instances.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -773,6 +773,7 @@ def run_instances(
773773
The KeyPair-parameter can be validated, to see if it is a known key-pair.
774774
Enable this validation by setting the environment variable `MOTO_ENABLE_KEYPAIR_VALIDATION=true`
775775
"""
776+
template_nics = []
776777
if launch_template := kwargs.get("launch_template"):
777778
tmpl = self._get_template_from_args(launch_template).data
778779

@@ -795,6 +796,15 @@ def run_instances(
795796
template_sgs := tmpl.get("SecurityGroups")
796797
):
797798
security_group_names = template_sgs
799+
template_nics = tmpl.get("NetworkInterfaces") or []
800+
801+
if not kwargs.get("security_group_ids") and not security_group_names:
802+
if nic_groups := [
803+
g
804+
for nic in (kwargs.get("nics") or template_nics)
805+
for g in (nic.get("Groups") or [])
806+
]:
807+
kwargs["security_group_ids"] = nic_groups
798808

799809
location_type = "availability-zone" if kwargs.get("placement") else "region"
800810
default_region = "us-east-1"

tests/test_ec2/test_instances.py

Lines changed: 125 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3124,6 +3124,64 @@ def test_block_device_status_conversion():
31243124
assert Instance.get_block_device_status("deleting") == "deleting"
31253125

31263126

3127+
@ec2_aws_verified()
3128+
@pytest.mark.aws_verified
3129+
def test_run_instances__security_groups_from_request_network_interfaces(
3130+
valid_ami, cleanups, ec2_client=None
3131+
):
3132+
vpc_id = ec2_client.create_vpc(CidrBlock="10.0.0.0/16")["Vpc"]["VpcId"]
3133+
cleanups.append(lambda: ec2_client.delete_vpc(VpcId=vpc_id))
3134+
3135+
subnet_id = ec2_client.create_subnet(
3136+
VpcId=vpc_id,
3137+
CidrBlock="10.0.1.0/24",
3138+
AvailabilityZone=ec2_client.meta.region_name + "a",
3139+
)["Subnet"]["SubnetId"]
3140+
cleanups.append(lambda: ec2_client.delete_subnet(SubnetId=subnet_id))
3141+
3142+
sg_id = ec2_client.create_security_group(
3143+
GroupName=f"test-sg-{str(uuid4())[0:6]}",
3144+
Description="test",
3145+
VpcId=vpc_id,
3146+
)["GroupId"]
3147+
cleanups.append(lambda: ec2_client.delete_security_group(GroupId=sg_id))
3148+
3149+
instance = ec2_client.run_instances(
3150+
MinCount=1,
3151+
MaxCount=1,
3152+
ImageId=valid_ami,
3153+
NetworkInterfaces=[
3154+
{"DeviceIndex": 0, "SubnetId": subnet_id, "Groups": [sg_id]}
3155+
],
3156+
)["Instances"][0]
3157+
instance_id = instance["InstanceId"]
3158+
3159+
def terminate_and_wait():
3160+
ec2_client.terminate_instances(InstanceIds=[instance_id])
3161+
ec2_client.get_waiter("instance_terminated").wait(InstanceIds=[instance_id])
3162+
3163+
cleanups.append(terminate_and_wait)
3164+
3165+
sg_ids = [g["GroupId"] for g in instance["SecurityGroups"]]
3166+
assert sg_id in sg_ids
3167+
nic_sg_ids = [g["GroupId"] for g in instance["NetworkInterfaces"][0]["Groups"]]
3168+
assert sg_id in nic_sg_ids
3169+
3170+
described = ec2_client.describe_instances(InstanceIds=[instance_id])
3171+
sg_ids = [
3172+
g["GroupId"]
3173+
for g in described["Reservations"][0]["Instances"][0]["SecurityGroups"]
3174+
]
3175+
assert sg_id in sg_ids
3176+
nic_sg_ids = [
3177+
g["GroupId"]
3178+
for g in described["Reservations"][0]["Instances"][0]["NetworkInterfaces"][0][
3179+
"Groups"
3180+
]
3181+
]
3182+
assert sg_id in nic_sg_ids
3183+
3184+
31273185
class TestCreateInstanceFromLaunchTemplate:
31283186
_LT_USER_DATA_SCRIPT = b"#!/bin/bash\necho from-template"
31293187
_LT_USER_DATA_B64 = base64.b64encode(_LT_USER_DATA_SCRIPT).decode()
@@ -3383,9 +3441,12 @@ def test_run_instances__security_group_ids_from_launch_template(
33833441
ec2_client, template_name=lt_name, ImageId=valid_ami, SubnetId=subnet_id
33843442
)
33853443
instance_id = instance["InstanceId"]
3386-
cleanups.append(
3387-
lambda: ec2_client.terminate_instances(InstanceIds=[instance_id])
3388-
)
3444+
3445+
def terminate_and_wait():
3446+
ec2_client.terminate_instances(InstanceIds=[instance_id])
3447+
ec2_client.get_waiter("instance_terminated").wait(InstanceIds=[instance_id])
3448+
3449+
cleanups.append(terminate_and_wait)
33893450

33903451
instance_sg_ids = [g["GroupId"] for g in instance["SecurityGroups"]]
33913452
assert sg_id in instance_sg_ids
@@ -3405,6 +3466,67 @@ def test_run_instances__security_group_ids_from_launch_template(
34053466
]
34063467
assert sg_id in instance_sg_ids
34073468

3469+
@ec2_aws_verified()
3470+
@pytest.mark.aws_verified
3471+
def test_run_instances__security_groups_from_launch_template_network_interfaces(
3472+
self, valid_ami, cleanups, ec2_client=None
3473+
):
3474+
"""
3475+
SecurityGroups specified in NetworkInterfaces[].Groups in a launch template
3476+
must appear in DescribeInstances SecurityGroups.
3477+
"""
3478+
vpc_id = ec2_client.create_vpc(CidrBlock="10.0.0.0/16")["Vpc"]["VpcId"]
3479+
cleanups.append(lambda: ec2_client.delete_vpc(VpcId=vpc_id))
3480+
3481+
subnet_id = ec2_client.create_subnet(
3482+
VpcId=vpc_id,
3483+
CidrBlock="10.0.1.0/24",
3484+
AvailabilityZone=ec2_client.meta.region_name + "a",
3485+
)["Subnet"]["SubnetId"]
3486+
cleanups.append(lambda: ec2_client.delete_subnet(SubnetId=subnet_id))
3487+
3488+
sg_id = ec2_client.create_security_group(
3489+
GroupName=f"test-sg-{str(uuid4())[0:6]}",
3490+
Description="test",
3491+
VpcId=vpc_id,
3492+
)["GroupId"]
3493+
cleanups.append(lambda: ec2_client.delete_security_group(GroupId=sg_id))
3494+
3495+
lt_name = str(uuid4())
3496+
ec2_client.create_launch_template(
3497+
LaunchTemplateName=lt_name,
3498+
LaunchTemplateData={
3499+
"InstanceType": "t2.micro",
3500+
"NetworkInterfaces": [
3501+
{"DeviceIndex": 0, "SubnetId": subnet_id, "Groups": [sg_id]}
3502+
],
3503+
},
3504+
)
3505+
cleanups.append(
3506+
lambda: ec2_client.delete_launch_template(LaunchTemplateName=lt_name)
3507+
)
3508+
3509+
instance = self._run_instance_from_template(
3510+
ec2_client, template_name=lt_name, ImageId=valid_ami
3511+
)
3512+
instance_id = instance["InstanceId"]
3513+
3514+
def terminate_and_wait():
3515+
ec2_client.terminate_instances(InstanceIds=[instance_id])
3516+
ec2_client.get_waiter("instance_terminated").wait(InstanceIds=[instance_id])
3517+
3518+
cleanups.append(terminate_and_wait)
3519+
3520+
sg_ids = [g["GroupId"] for g in instance["SecurityGroups"]]
3521+
assert sg_id in sg_ids
3522+
3523+
described = ec2_client.describe_instances(InstanceIds=[instance_id])
3524+
sg_ids = [
3525+
g["GroupId"]
3526+
for g in described["Reservations"][0]["Instances"][0]["SecurityGroups"]
3527+
]
3528+
assert sg_id in sg_ids
3529+
34083530
@ec2_aws_verified()
34093531
@pytest.mark.aws_verified
34103532
def test_create_instance_from_launch_template_single_template_version(

0 commit comments

Comments
 (0)