Repository: getmoto/moto

Issue:
modify_network_interface_attribute should support setting more than one security group
`modify_network_interface_attribute`  (EC2 client) should support setting more than one security group via the `Groups` parameter, but currently it just selects the first security group.

Example to reproduce:
```python
from moto import mock_ec2
import boto3

@mock_ec2
def test_example():
	client = boto3.client("ec2")

	vpcResponse = client.describe_vpcs(
		Filters=[{'Name': 'isDefault', 'Values': ['true']}]
	)
	vpc = vpcResponse['Vpcs'][0]

	subnetResponse = client.describe_subnets(
		Filters=[{'Name': 'vpc-id', 'Values': [vpc['VpcId']]}]
	)
	subnet = subnetResponse['Subnets'][0]

	interfaceResponse = client.create_network_interface(
		SubnetId=subnet['SubnetId']
	)
	interface = interfaceResponse['NetworkInterface']

	sg1 = client.create_security_group(
		Description="testing",
		GroupName="test-group-1",
		VpcId=vpc['VpcId']
	)
	sg2 = client.create_security_group(
		Description="testing",
		GroupName="test-group-2",
		VpcId=vpc['VpcId']
	)

	expectedGroups = [
		{
			'GroupName': 'test-group-1',
			'GroupId': sg1['GroupId']
		},
		{
			'GroupName': 'test-group-2',
			'GroupId': sg2['GroupId']
		}
	]

	modifyResponse = client.modify_network_interface_attribute(
		NetworkInterfaceId=interface['NetworkInterfaceId'],
		Groups=[
			sg1['GroupId'],
			sg2['GroupId']
		]
	)

	describeInterfaceResponse = client.describe_network_interfaces(
		NetworkInterfaceIds=[interface['NetworkInterfaceId']]
	)
	updatedInterface = describeInterfaceResponse['NetworkInterfaces'][0]
	assert updatedInterface['NetworkInterfaceId'] == interface['NetworkInterfaceId']

	assert len(updatedInterface['Groups']) == 2
	assert updatedInterface['Groups'] == expectedGroups
```


Expected:
The updated interface should have 2 security groups, which should match the 2 groups created.

Observed:
The updated interface has a single security group, matching whichever was passed in first in the `Groups` parameter.


Moto version: 2.0.6
Using python mocks, boto3 version 1.17.64, botocore version 1.20.64
Python: 3.8.9




Discussion:

Resolving patch:
diff --git a/moto/ec2/models.py b/moto/ec2/models.py
--- a/moto/ec2/models.py
+++ b/moto/ec2/models.py
@@ -479,10 +479,10 @@ def detach_network_interface(self, attachment_id):
 
         found_eni.instance.detach_eni(found_eni)
 
-    def modify_network_interface_attribute(self, eni_id, group_id):
+    def modify_network_interface_attribute(self, eni_id, group_ids):
         eni = self.get_network_interface(eni_id)
-        group = self.get_security_group_from_id(group_id)
-        eni._group_set = [group]
+        groups = [self.get_security_group_from_id(group_id) for group_id in group_ids]
+        eni._group_set = groups
 
     def get_all_network_interfaces(self, eni_ids=None, filters=None):
         enis = self.enis.values()
diff --git a/moto/ec2/responses/elastic_network_interfaces.py b/moto/ec2/responses/elastic_network_interfaces.py
--- a/moto/ec2/responses/elastic_network_interfaces.py
+++ b/moto/ec2/responses/elastic_network_interfaces.py
@@ -56,11 +56,10 @@ def detach_network_interface(self):
             return template.render()
 
     def modify_network_interface_attribute(self):
-        # Currently supports modifying one and only one security group
         eni_id = self._get_param("NetworkInterfaceId")
-        group_id = self._get_param("SecurityGroupId.1")
+        group_ids = self._get_multi_param("SecurityGroupId")
         if self.is_not_dryrun("ModifyNetworkInterface"):
-            self.ec2_backend.modify_network_interface_attribute(eni_id, group_id)
+            self.ec2_backend.modify_network_interface_attribute(eni_id, group_ids)
             return MODIFY_NETWORK_INTERFACE_ATTRIBUTE_RESPONSE
 
     def reset_network_interface_attribute(self):


Test patch:
diff --git a/tests/test_ec2/test_elastic_network_interfaces.py b/tests/test_ec2/test_elastic_network_interfaces.py
--- a/tests/test_ec2/test_elastic_network_interfaces.py
+++ b/tests/test_ec2/test_elastic_network_interfaces.py
@@ -133,7 +133,7 @@ def test_elastic_network_interfaces_modify_attribute():
 
     with pytest.raises(EC2ResponseError) as ex:
         conn.modify_network_interface_attribute(
-            eni.id, "groupset", [security_group2.id], dry_run=True
+            eni.id, "groupset", [security_group1.id, security_group2.id], dry_run=True
         )
     ex.value.error_code.should.equal("DryRunOperation")
     ex.value.status.should.equal(400)
@@ -141,14 +141,17 @@ def test_elastic_network_interfaces_modify_attribute():
         "An error occurred (DryRunOperation) when calling the ModifyNetworkInterface operation: Request would have succeeded, but DryRun flag is set"
     )
 
-    conn.modify_network_interface_attribute(eni.id, "groupset", [security_group2.id])
+    conn.modify_network_interface_attribute(
+        eni.id, "groupset", [security_group1.id, security_group2.id]
+    )
 
     all_enis = conn.get_all_network_interfaces()
     all_enis.should.have.length_of(1)
 
     eni = all_enis[0]
-    eni.groups.should.have.length_of(1)
-    eni.groups[0].id.should.equal(security_group2.id)
+    eni.groups.should.have.length_of(2)
+    eni.groups[0].id.should.equal(security_group1.id)
+    eni.groups[1].id.should.equal(security_group2.id)
 
 
 @mock_ec2_deprecated