diff --git a/moto/ec2/models.py b/moto/ec2/models.py
index d754fd4bf..4a9ba1460 100644
--- a/moto/ec2/models.py
+++ b/moto/ec2/models.py
@@ -131,6 +131,7 @@ from .utils import (
random_internet_gateway_id,
random_egress_only_internet_gateway_id,
random_ip,
+ random_mac_address,
random_ipv6_cidr,
random_transit_gateway_attachment_id,
random_transit_gateway_route_table_id,
@@ -277,15 +278,16 @@ class NetworkInterface(TaggedEC2Resource, CloudFormationModel):
private_ip_address,
private_ip_addresses=None,
device_index=0,
- public_ip_auto_assign=True,
+ public_ip_auto_assign=False,
group_ids=None,
description=None,
+ tags=None,
):
self.ec2_backend = ec2_backend
self.id = random_eni_id()
self.device_index = device_index
- self.private_ip_address = private_ip_address or random_private_ip()
- self.private_ip_addresses = private_ip_addresses
+ self.private_ip_address = private_ip_address or None
+ self.private_ip_addresses = private_ip_addresses or []
self.subnet = subnet
self.instance = None
self.attachment_id = None
@@ -295,13 +297,35 @@ class NetworkInterface(TaggedEC2Resource, CloudFormationModel):
self.public_ip = None
self.public_ip_auto_assign = public_ip_auto_assign
self.start()
-
+ self.add_tags(tags or {})
+ self.status = "available"
self.attachments = []
-
+ self.mac_address = random_mac_address()
+ self.interface_type = "interface"
# Local set to the ENI. When attached to an instance, @property group_set
# returns groups for both self and the attached instance.
self._group_set = []
+ if not self.private_ip_address:
+ if self.private_ip_addresses:
+ for ip in self.private_ip_addresses:
+ if isinstance(ip, list) and ip.get("Primary", False) in [
+ "true",
+ True,
+ "True",
+ ]:
+ self.private_ip_address = ip.get("PrivateIpAddress")
+ if isinstance(ip, str):
+ self.private_ip_address = self.private_ip_addresses[0]
+ break
+ else:
+ self.private_ip_address = random_private_ip()
+
+ if not self.private_ip_addresses and self.private_ip_address:
+ self.private_ip_addresses.append(
+ {"Primary": True, "PrivateIpAddress": self.private_ip_address}
+ )
+
group = None
if group_ids:
for group_id in group_ids:
@@ -319,6 +343,21 @@ class NetworkInterface(TaggedEC2Resource, CloudFormationModel):
if group:
self._group_set.append(group)
+ @property
+ def owner_id(self):
+ return ACCOUNT_ID
+
+ @property
+ def association(self):
+ association = {}
+ if self.public_ip:
+ eips = self.ec2_backend.address_by_ip([self.public_ip])
+ eip = eips[0] if len(eips) > 0 else None
+ if eip:
+ association["allocationId"] = eip.allocation_id or None
+ association["associationId"] = eip.association_id or None
+ return association
+
@staticmethod
def cloudformation_name_type():
return None
@@ -430,10 +469,9 @@ class NetworkInterfaceBackend(object):
private_ip_addresses,
group_ids=group_ids,
description=description,
+ tags=tags,
**kwargs
)
- if tags:
- eni.add_tags(tags)
self.enis[eni.id] = eni
return eni
@@ -4031,9 +4069,7 @@ class Subnet(TaggedEC2Resource, CloudFormationModel):
for eni in self.ec2_backend.get_all_network_interfaces()
if eni.subnet.id == self.id
]
- addresses_taken = [
- eni.private_ip_address for eni in enis if eni.private_ip_address
- ]
+ addresses_taken = []
for eni in enis:
if eni.private_ip_addresses:
addresses_taken.extend(eni.private_ip_addresses)
@@ -7704,8 +7740,9 @@ class NatGateway(CloudFormationModel, TaggedEC2Resource):
@property
def public_ip(self):
- eips = self._backend.address_by_allocation([self.allocation_id])
- return eips[0].public_ip
+ if self.allocation_id:
+ eips = self._backend.address_by_allocation([self.allocation_id])
+ return eips[0].public_ip if self.allocation_id else None
@staticmethod
def cloudformation_name_type():
diff --git a/moto/ec2/responses/elastic_network_interfaces.py b/moto/ec2/responses/elastic_network_interfaces.py
index f4dce86c7..409951f98 100644
--- a/moto/ec2/responses/elastic_network_interfaces.py
+++ b/moto/ec2/responses/elastic_network_interfaces.py
@@ -1,6 +1,6 @@
from __future__ import unicode_literals
from moto.core.responses import BaseResponse
-from moto.ec2.utils import filters_from_querystring
+from moto.ec2.utils import filters_from_querystring, add_tag_specification
class ElasticNetworkInterfaces(BaseResponse):
@@ -11,9 +11,9 @@ class ElasticNetworkInterfaces(BaseResponse):
groups = self._get_multi_param("SecurityGroupId")
subnet = self.ec2_backend.get_subnet(subnet_id)
description = self._get_param("Description")
- tags = self._parse_tag_specification("TagSpecification").get(
- "network-interface"
- )
+ tags = self._get_multi_param("TagSpecification")
+ tags = add_tag_specification(tags)
+
if self.is_not_dryrun("CreateNetworkInterface"):
eni = self.ec2_backend.create_network_interface(
subnet,
@@ -84,19 +84,20 @@ CREATE_NETWORK_INTERFACE_RESPONSE = """
2c6021ec-d705-445a-9780-420d0c7ab793
+
+
{{ eni.id }}
{{ eni.subnet.id }}
{{ eni.subnet.vpc_id }}
- us-west-2a
+ {{ eni.subnet.availability_zone }}
{% if eni.description %}
{{ eni.description }}
- {% else %}
-
{% endif %}
- 498654062920
- false
- pending
- 02:07:a9:b6:12:51
+ {{ eni.owner_id }}
+ AIDARCSPW2WNREUEN7XFM
+ False
+ {{ eni.status }}
+ {{ eni.mac_address }}
{% if eni.private_ip_address %}
{{ eni.private_ip_address }}
{% endif %}
@@ -109,17 +110,36 @@ CREATE_NETWORK_INTERFACE_RESPONSE = """
{% endfor %}
-
- {% if eni.private_ip_address %}
-
- -
- {{ eni.private_ip_address }}
- true
-
-
- {% else %}
-
+ {% if eni.association %}
+
+ {{ eni.public_ip }}
+ {{ eni.owner_id }}
+ {{ eni.association.allocationId }}
+ {{ eni.association.associationId }}
+ true
+
{% endif %}
+
+ {% for tag in eni.get_tags() %}
+ -
+ {{ tag.key }}
+ {{ tag.value }}
+
+ {% endfor %}
+
+
+ {% for address in eni.private_ip_addresses %}
+ -
+ {{ address.PrivateIpAddress }}
+ {% if address.privateDnsName %}
+ {{ address.privateDnsName }}
+ {% endif %}
+ {{ address.Primary }}
+
+ {% endfor %}
+
+
+ {{ eni.interface_type }}
"""
@@ -129,74 +149,60 @@ DESCRIBE_NETWORK_INTERFACES_RESPONSE = """
{% for eni in enis %}
-
- {{ eni.id }}
- {{ eni.subnet.id }}
- {{ eni.subnet.vpc_id }}
- us-west-2a
- {{ eni.description or "" }}
- 190610284047
- false
- {% if eni.attachment_id %}
- in-use
- {% else %}
- available
- {% endif %}
- 0e:a3:a7:7b:95:a7
- {% if eni.private_ip_address %}
- {{ eni.private_ip_address }}
- {% endif %}
- ip-10-0-0-134.us-west-2.compute.internal
- {{ eni.source_dest_check }}
-
- {% for group in eni.group_set %}
-
-
- {{ group.id }}
- {{ group.name }}
+ {{ eni.id }}
+ {{ eni.subnet.id }}
+ {{ eni.subnet.vpc_id }}
+ {{ eni.subnet.availability_zone }}
+ {% if eni.description %}
+ {{ eni.description }}
+ {% endif %}
+ {{ eni.owner_id }}
+ AIDARCSPW2WNREUEN7XFM
+ False
+ {{ eni.status }}
+ {{ eni.mac_address }}
+ {% if eni.private_ip_address %}
+ {{ eni.private_ip_address }}
+ {% endif %}
+ {{ eni.source_dest_check }}
+
+ {% for group in eni.group_set %}
+
-
+ {{ group.id }}
+ {{ group.name }}
+
+ {% endfor %}
+
+ {% if eni.association %}
+
+ {{ eni.public_ip }}
+ {{ eni.owner_id }}
+ {{ eni.association.allocationId }}
+ {{ eni.association.associationId }}
+ true
+
+ {% endif %}
+
+ {% for tag in eni.get_tags() %}
+ -
+ {{ tag.key }}
+ {{ tag.value }}
{% endfor %}
-
-
- {% for tag in eni.get_tags() %}
- -
- {{ tag.key }}
- {{ tag.value }}
-
- {% endfor %}
-
- {% if eni.instance %}
-
- {{ eni.attachment_id }}
- {{ eni.instance.id }}
- 190610284047
- {{ eni.device_index }}
- attached
- 2013-10-04T17:38:53.000Z
- true
-
- {% endif %}
-
- {{ eni.public_ip }}
- ec2-54-200-86-47.us-west-2.compute.amazonaws.com
- amazon
-
- {% if eni.private_ip_address %}
-
- -
- {{ eni.private_ip_address }}
- ip-10-0-0-134.us-west-2.compute.internal
- true
- {% if eni.public_ip %}
-
- {{ eni.public_ip }}
- ec2-54-200-86-47.us-west-2.compute.amazonaws.com
- amazon
-
- {% endif %}
-
-
- {% else %}
-
- {% endif %}
+
+
+ {% for address in eni.private_ip_addresses %}
+ -
+ {{ address.PrivateIpAddress }}
+ {% if address.privateDnsName %}
+ {{ address.privateDnsName }}
+ {% endif %}
+ {{ address.Primary }}
+
+ {% endfor %}
+
+
+ {{ eni.interface_type }}
{% endfor %}
diff --git a/moto/ec2/utils.py b/moto/ec2/utils.py
index d230af37b..8ceeb1e16 100644
--- a/moto/ec2/utils.py
+++ b/moto/ec2/utils.py
@@ -241,6 +241,14 @@ def random_ip():
)
+def random_mac_address():
+ return "02:00:00:%02x:%02x:%02x" % (
+ random.randint(0, 255),
+ random.randint(0, 255),
+ random.randint(0, 255),
+ )
+
+
def randor_ipv4_cidr():
return "10.0.{}.{}/16".format(random.randint(0, 255), random.randint(0, 255))
diff --git a/tests/terraform-tests.failures.txt b/tests/terraform-tests.failures.txt
index b3d78799f..5f8cb73a3 100644
--- a/tests/terraform-tests.failures.txt
+++ b/tests/terraform-tests.failures.txt
@@ -7,3 +7,4 @@ TestAccAWSIAMRolePolicy
TestAccAWSSecurityGroup_forceRevokeRules_
TestAccAWSSSMDocument_package
TestAccAWSDefaultSecurityGroup_Classic_
+TestAccDataSourceAwsNetworkInterface_CarrierIPAssociation
diff --git a/tests/terraform-tests.success.txt b/tests/terraform-tests.success.txt
index 96a9c59f6..83394e12e 100644
--- a/tests/terraform-tests.success.txt
+++ b/tests/terraform-tests.success.txt
@@ -124,4 +124,5 @@ TestAccAWSSecurityGroupRule_
TestAccAWSVpnGateway
TestAccAWSVpnGatewayAttachment
TestAccAWSEc2CarrierGateway
+TestAccDataSourceAwsNetworkInterface_
TestAccAWSNatGateway
diff --git a/tests/test_ec2/test_subnets.py b/tests/test_ec2/test_subnets.py
index 3f439a5c9..c1bcefa1f 100644
--- a/tests/test_ec2/test_subnets.py
+++ b/tests/test_ec2/test_subnets.py
@@ -618,14 +618,14 @@ def validate_subnet_details_after_creating_eni(
],
)["NetworkInterface"]
enis_created.append(eni)
- ip_addresses_assigned = ip_addresses_assigned + nr_of_ip_addresses + 1 #
+ ip_addresses_assigned = ip_addresses_assigned + nr_of_ip_addresses #
+
# Verify that the nr of available IP addresses takes these ENIs into account
updated_subnet = client.describe_subnets(SubnetIds=[subnet["SubnetId"]])["Subnets"][
0
]
- private_addresses = [
- eni["PrivateIpAddress"] for eni in enis_created if eni["PrivateIpAddress"]
- ]
+
+ private_addresses = []
for eni in enis_created:
private_addresses.extend(
[address["PrivateIpAddress"] for address in eni["PrivateIpAddresses"]]