diff --git a/moto/ec2/exceptions.py b/moto/ec2/exceptions.py
index ce68497a3..48607b770 100644
--- a/moto/ec2/exceptions.py
+++ b/moto/ec2/exceptions.py
@@ -398,6 +398,14 @@ class InvalidParameterValueErrorUnknownAttribute(EC2ClientError):
)
+class InvalidGatewayIDError(EC2ClientError):
+ def __init__(self, gateway_id):
+ super(InvalidGatewayIDError, self).__init__(
+ "InvalidGatewayID.NotFound",
+ "The eigw ID '{0}' does not exist".format(gateway_id),
+ )
+
+
class InvalidInternetGatewayIdError(EC2ClientError):
def __init__(self, internet_gateway_id):
super(InvalidInternetGatewayIdError, self).__init__(
diff --git a/moto/ec2/models.py b/moto/ec2/models.py
index f68f3b575..f674d91c9 100644
--- a/moto/ec2/models.py
+++ b/moto/ec2/models.py
@@ -111,6 +111,7 @@ from .exceptions import (
InvalidAssociationIDIamProfileAssociationError,
InvalidVpcEndPointIdError,
InvalidTaggableResourceType,
+ InvalidGatewayIDError,
)
from .utils import (
EC2_RESOURCE_TO_PREFIX,
@@ -123,6 +124,7 @@ from .utils import (
random_eni_id,
random_instance_id,
random_internet_gateway_id,
+ random_egress_only_internet_gateway_id,
random_ip,
random_ipv6_cidr,
random_transit_gateway_attachment_id,
@@ -4792,6 +4794,52 @@ class InternetGatewayBackend(object):
return self.describe_internet_gateways(internet_gateway_ids=igw_ids)[0]
+class EgressOnlyInternetGateway(TaggedEC2Resource):
+ def __init__(self, ec2_backend, vpc_id, tags=None):
+ self.id = random_egress_only_internet_gateway_id()
+ self.ec2_backend = ec2_backend
+ self.vpc_id = vpc_id
+ self.state = "attached"
+ self.add_tags(tags or {})
+
+ @property
+ def physical_resource_id(self):
+ return self.id
+
+
+class EgressOnlyInternetGatewayBackend(object):
+ def __init__(self):
+ self.egress_only_internet_gateway_backend = {}
+ super(EgressOnlyInternetGatewayBackend, self).__init__()
+
+ def create_egress_only_internet_gateway(self, vpc_id, tags=None):
+ vpc = self.get_vpc(vpc_id)
+ if not vpc:
+ raise InvalidVPCIdError(vpc_id)
+ egress_only_igw = EgressOnlyInternetGateway(self, vpc_id, tags)
+ self.egress_only_internet_gateway_backend[egress_only_igw.id] = egress_only_igw
+ return egress_only_igw
+
+ def describe_egress_only_internet_gateways(self, ids=None, filters=None):
+ # TODO: support filtering based on tag
+ egress_only_igws = list(self.egress_only_internet_gateway_backend.values())
+
+ if ids:
+ egress_only_igws = [
+ egress_only_igw
+ for egress_only_igw in egress_only_igws
+ if egress_only_igw.id in ids
+ ]
+ return egress_only_igws
+
+ def delete_egress_only_internet_gateway(self, id):
+ egress_only_igw = self.egress_only_internet_gateway_backend.get(id)
+ if not egress_only_igw:
+ raise InvalidGatewayIDError(id)
+ if egress_only_igw:
+ self.egress_only_internet_gateway_backend.pop(id)
+
+
class VPCGatewayAttachment(CloudFormationModel):
def __init__(self, gateway_id, vpc_id):
self.gateway_id = gateway_id
@@ -7308,6 +7356,7 @@ class EC2Backend(
RouteTableBackend,
RouteBackend,
InternetGatewayBackend,
+ EgressOnlyInternetGatewayBackend,
VPCGatewayAttachmentBackend,
SpotFleetBackend,
SpotRequestBackend,
diff --git a/moto/ec2/responses/__init__.py b/moto/ec2/responses/__init__.py
index 0f8e6d8ff..5368062b8 100644
--- a/moto/ec2/responses/__init__.py
+++ b/moto/ec2/responses/__init__.py
@@ -12,6 +12,7 @@ from .elastic_network_interfaces import ElasticNetworkInterfaces
from .general import General
from .instances import InstanceResponse
from .internet_gateways import InternetGateways
+from .egress_only_internet_gateways import EgressOnlyInternetGateway
from .ip_addresses import IPAddresses
from .key_pairs import KeyPairs
from .launch_templates import LaunchTemplates
@@ -53,6 +54,7 @@ class EC2Response(
General,
InstanceResponse,
InternetGateways,
+ EgressOnlyInternetGateway,
IPAddresses,
KeyPairs,
LaunchTemplates,
diff --git a/moto/ec2/responses/egress_only_internet_gateways.py b/moto/ec2/responses/egress_only_internet_gateways.py
new file mode 100644
index 000000000..f0615f5e6
--- /dev/null
+++ b/moto/ec2/responses/egress_only_internet_gateways.py
@@ -0,0 +1,83 @@
+from moto.core.responses import BaseResponse
+from moto.ec2.utils import filters_from_querystring, add_tag_specification
+
+
+class EgressOnlyInternetGateway(BaseResponse):
+ def create_egress_only_internet_gateway(self):
+ vpc_id = self._get_param("VpcId")
+ tags = self._get_multi_param("TagSpecification")
+ tags = add_tag_specification(tags)
+
+ egress_only_igw = self.ec2_backend.create_egress_only_internet_gateway(
+ vpc_id=vpc_id, tags=tags
+ )
+ template = self.response_template(CREATE_EGRESS_ONLY_IGW_RESPONSE)
+ return template.render(egress_only_igw=egress_only_igw)
+
+ def describe_egress_only_internet_gateways(self):
+ egress_only_igw_ids = self._get_multi_param("EgressOnlyInternetGatewayId")
+ filters = filters_from_querystring(self.querystring)
+ egress_only_igws = self.ec2_backend.describe_egress_only_internet_gateways(
+ egress_only_igw_ids, filters,
+ )
+ template = self.response_template(DESCRIBE_EGRESS_ONLY_IGW_RESPONSE)
+ return template.render(egress_only_igws=egress_only_igws)
+
+ def delete_egress_only_internet_gateway(self):
+ egress_only_igw_id = self._get_param("EgressOnlyInternetGatewayId")
+ self.ec2_backend.delete_egress_only_internet_gateway(id=egress_only_igw_id)
+ template = self.response_template(DELETE_EGRESS_ONLY_IGW_RESPONSE)
+ return template.render()
+
+
+CREATE_EGRESS_ONLY_IGW_RESPONSE = """
+ c617595f-6c29-4a00-a941-example
+
+
+ -
+ {{ egress_only_igw.state }}
+ {{ egress_only_igw.vpc_id }}
+
+
+ {{ egress_only_igw.id }}
+
+ {% for tag in egress_only_igw.get_tags() %}
+ -
+ {{ tag.key }}
+ {{ tag.value }}
+
+ {% endfor %}
+
+
+
+"""
+
+DESCRIBE_EGRESS_ONLY_IGW_RESPONSE = """
+ ec441b4c-357f-4483-b4a7-example
+
+ {% for egress_only_igw in egress_only_igws %}
+ -
+
+
-
+ {{ egress_only_igw.state }}
+ {{ egress_only_igw.vpc_id }}
+
+
+ {{ egress_only_igw.id }}
+
+ {% for tag in egress_only_igw.get_tags() %}
+ -
+ {{ tag.key }}
+ {{ tag.value }}
+
+ {% endfor %}
+
+
+ {% endfor %}
+
+"""
+
+DELETE_EGRESS_ONLY_IGW_RESPONSE = """
+ 59dbff89-35bd-4eac-99ed-be587EXAMPLE
+ true
+"""
diff --git a/moto/ec2/utils.py b/moto/ec2/utils.py
index b7b5c5654..328b77029 100644
--- a/moto/ec2/utils.py
+++ b/moto/ec2/utils.py
@@ -23,6 +23,7 @@ EC2_RESOURCE_TO_PREFIX = {
"image": "ami",
"instance": "i",
"internet-gateway": "igw",
+ "egress-only-internet-gateway": "eigw",
"launch-template": "lt",
"nat-gateway": "nat",
"network-acl": "acl",
@@ -153,6 +154,12 @@ def random_internet_gateway_id():
return random_id(prefix=EC2_RESOURCE_TO_PREFIX["internet-gateway"])
+def random_egress_only_internet_gateway_id():
+ return random_id(
+ prefix=EC2_RESOURCE_TO_PREFIX["egress-only-internet-gateway"], size=17
+ )
+
+
def random_route_table_id():
return random_id(prefix=EC2_RESOURCE_TO_PREFIX["route-table"])
diff --git a/tests/terraform-tests.success.txt b/tests/terraform-tests.success.txt
index e1da881ee..ccbc00f2b 100644
--- a/tests/terraform-tests.success.txt
+++ b/tests/terraform-tests.success.txt
@@ -86,4 +86,5 @@ TestAccAWSRouteTable_IPv4_To_NatGateway
TestAccAWSRouteTable_IPv4_To_TransitGateway
TestAccAWSRouteTable_disappears
TestAccAWSRouteTable_basic
-TestAccAwsEc2ManagedPrefixList
\ No newline at end of file
+TestAccAwsEc2ManagedPrefixList
+TestAccAWSEgressOnlyInternetGateway
\ No newline at end of file