diff --git a/moto/ec2/exceptions.py b/moto/ec2/exceptions.py
index 4811a9f8b..bec3ffc41 100644
--- a/moto/ec2/exceptions.py
+++ b/moto/ec2/exceptions.py
@@ -703,3 +703,11 @@ class InvalidSubnetCidrBlockAssociationID(EC2ClientError):
association_id
),
)
+
+
+class InvalidCarrierGatewayID(EC2ClientError):
+ def __init__(self, carrier_gateway_id):
+ super(InvalidCarrierGatewayID, self).__init__(
+ "InvalidCarrierGatewayID.NotFound",
+ "The CarrierGateway ID '{0}' does not exist".format(carrier_gateway_id),
+ )
diff --git a/moto/ec2/models.py b/moto/ec2/models.py
index 56514ed6f..65d89a2bd 100644
--- a/moto/ec2/models.py
+++ b/moto/ec2/models.py
@@ -116,6 +116,7 @@ from .exceptions import (
InvalidVpcEndPointIdError,
InvalidTaggableResourceType,
InvalidGatewayIDError,
+ InvalidCarrierGatewayID,
)
from .utils import (
EC2_RESOURCE_TO_PREFIX,
@@ -160,6 +161,7 @@ from .utils import (
random_vpc_cidr_association_id,
random_vpc_peering_connection_id,
random_iam_instance_profile_association_id,
+ random_carrier_gateway_id,
generic_filter,
is_valid_resource_id,
get_prefix,
@@ -5344,6 +5346,66 @@ class InternetGatewayBackend(object):
return self.describe_internet_gateways(internet_gateway_ids=igw_ids)[0]
+class CarrierGateway(TaggedEC2Resource):
+ def __init__(self, ec2_backend, vpc_id, tags=None):
+ self.id = random_carrier_gateway_id()
+ self.ec2_backend = ec2_backend
+ self.vpc_id = vpc_id
+ self.state = "available"
+ self.add_tags(tags or {})
+
+ @property
+ def physical_resource_id(self):
+ return self.id
+
+ @property
+ def owner_id(self):
+ return ACCOUNT_ID
+
+
+class CarrierGatewayBackend(object):
+ def __init__(self):
+ self.carrier_gateways = {}
+ super(CarrierGatewayBackend, self).__init__()
+
+ def create_carrier_gateway(self, vpc_id, tags=None):
+ vpc = self.get_vpc(vpc_id)
+ if not vpc:
+ raise InvalidVPCIdError(vpc_id)
+ carrier_gateway = CarrierGateway(self, vpc_id, tags)
+ self.carrier_gateways[carrier_gateway.id] = carrier_gateway
+ return carrier_gateway
+
+ def delete_carrier_gateway(self, id):
+ if not self.carrier_gateways.get(id):
+ raise InvalidCarrierGatewayID(id)
+ carrier_gateway = self.carrier_gateways.pop(id)
+ carrier_gateway.state = "deleted"
+ return carrier_gateway
+
+ def describe_carrier_gateways(self, ids=None, filters=None):
+ carrier_gateways = list(self.carrier_gateways.values())
+
+ if ids:
+ carrier_gateways = [
+ carrier_gateway
+ for carrier_gateway in carrier_gateways
+ if carrier_gateway.id in ids
+ ]
+
+ attr_pairs = (
+ ("carrier-gateway-id", "id"),
+ ("state", "state"),
+ ("vpc-id", "vpc_id"),
+ ("owner-id", "owner_id"),
+ )
+
+ result = carrier_gateways
+ if filters:
+ result = filter_resources(carrier_gateways, filters, attr_pairs)
+ return result
+
+
class EgressOnlyInternetGateway(TaggedEC2Resource):
def __init__(self, ec2_backend, vpc_id, tags=None):
self.id = random_egress_only_internet_gateway_id()
@@ -7962,6 +8024,7 @@ class EC2Backend(
TransitGatewayRelationsBackend,
LaunchTemplateBackend,
IamInstanceProfileAssociationBackend,
+ CarrierGatewayBackend,
):
def __init__(self, region_name):
self.region_name = region_name
diff --git a/moto/ec2/responses/__init__.py b/moto/ec2/responses/__init__.py
index 5368062b8..008b81f2a 100644
--- a/moto/ec2/responses/__init__.py
+++ b/moto/ec2/responses/__init__.py
@@ -39,6 +39,7 @@ from .transit_gateways import TransitGateways
from .transit_gateway_route_tables import TransitGatewayRouteTable
from .transit_gateway_attachments import TransitGatewayAttachment
from .iam_instance_profiles import IamInstanceProfiles
+from .carrier_gateways import CarrierGateway
class EC2Response(
@@ -81,6 +82,7 @@ class EC2Response(
TransitGatewayRouteTable,
TransitGatewayAttachment,
IamInstanceProfiles,
+ CarrierGateway,
):
@property
def ec2_backend(self):
diff --git a/moto/ec2/responses/carrier_gateways.py b/moto/ec2/responses/carrier_gateways.py
new file mode 100644
index 000000000..f55467825
--- /dev/null
+++ b/moto/ec2/responses/carrier_gateways.py
@@ -0,0 +1,94 @@
+from moto.core.responses import BaseResponse
+from moto.ec2.utils import filters_from_querystring, add_tag_specification
+
+
+class CarrierGateway(BaseResponse):
+ def create_carrier_gateway(self):
+ vpc_id = self._get_param("VpcId")
+ tags = self._get_multi_param("TagSpecification")
+ tags = add_tag_specification(tags)
+
+ carrier_gateway = self.ec2_backend.create_carrier_gateway(
+ vpc_id=vpc_id, tags=tags
+ )
+ template = self.response_template(CREATE_CARRIER_GATEWAY_RESPONSE)
+ return template.render(carrier_gateway=carrier_gateway)
+
+ def delete_carrier_gateway(self):
+ carrier_gateway_id = self._get_param("CarrierGatewayId")
+
+ carrier_gateway = self.ec2_backend.delete_carrier_gateway(carrier_gateway_id)
+ template = self.response_template(DELETE_CARRIER_GATEWAY_RESPONSE)
+ return template.render(carrier_gateway=carrier_gateway)
+
+ def describe_carrier_gateways(self):
+ carrier_gateway_ids = self._get_multi_param("CarrierGatewayId")
+ filters = filters_from_querystring(self.querystring)
+
+ carrier_gateways = self.ec2_backend.describe_carrier_gateways(
+ carrier_gateway_ids, filters
+ )
+ template = self.response_template(DESCRIBE_CARRIER_GATEWAYS_RESPONSE)
+ return template.render(carrier_gateways=carrier_gateways)
+
+
+CREATE_CARRIER_GATEWAY_RESPONSE = """
+ c617595f-6c29-4a00-a941-example
+
+ {{ carrier_gateway.state }}
+ {{ carrier_gateway.vpc_id }}
+ {{ carrier_gateway.id }}
+ {{ carrier_gateway.owner_id }}
+
+ {% for tag in carrier_gateway.get_tags() %}
+ -
+ {{ tag.key }}
+ {{ tag.value }}
+
+ {% endfor %}
+
+
+
+"""
+
+DELETE_CARRIER_GATEWAY_RESPONSE = """
+ c617595f-6c29-4a00-a941-example
+
+ {{ carrier_gateway.state }}
+ {{ carrier_gateway.vpc_id }}
+ {{ carrier_gateway.id }}
+ {{ carrier_gateway.owner_id }}
+
+ {% for tag in carrier_gateway.get_tags() %}
+ -
+ {{ tag.key }}
+ {{ tag.value }}
+
+ {% endfor %}
+
+
+
+"""
+
+DESCRIBE_CARRIER_GATEWAYS_RESPONSE = """
+ 151283df-f7dc-4317-89b4-01c9888b1d45
+
+ {% for carrier_gateway in carrier_gateways %}
+ -
+ {{ carrier_gateway.state }}
+ {{ carrier_gateway.vpc_id }}
+ {{ carrier_gateway.id }}
+ {{ carrier_gateway.owner_id }}
+
+ {% for tag in carrier_gateway.get_tags() %}
+
-
+ {{ tag.key }}
+ {{ tag.value }}
+
+ {% endfor %}
+
+
+ {% endfor %}
+
+
+"""
diff --git a/moto/ec2/utils.py b/moto/ec2/utils.py
index 89c28ce29..d230af37b 100644
--- a/moto/ec2/utils.py
+++ b/moto/ec2/utils.py
@@ -52,6 +52,7 @@ EC2_RESOURCE_TO_PREFIX = {
"vpn-connection": "vpn",
"vpn-gateway": "vgw",
"iam-instance-profile-association": "iip-assoc",
+ "carrier-gateway": "cagw",
}
@@ -220,6 +221,10 @@ def random_iam_instance_profile_association_id():
return random_id(prefix=EC2_RESOURCE_TO_PREFIX["iam-instance-profile-association"])
+def random_carrier_gateway_id():
+ return random_id(prefix=EC2_RESOURCE_TO_PREFIX["carrier-gateway"], size=17)
+
+
def random_public_ip():
return "54.214.{0}.{1}".format(random.choice(range(255)), random.choice(range(255)))
diff --git a/tests/terraform-tests.success.txt b/tests/terraform-tests.success.txt
index 4a8aa6bdd..18080f276 100644
--- a/tests/terraform-tests.success.txt
+++ b/tests/terraform-tests.success.txt
@@ -123,3 +123,4 @@ TestAccAWSInternetGateway
TestAccAWSSecurityGroupRule_
TestAccAWSVpnGateway
TestAccAWSVpnGatewayAttachment
+TestAccAWSEc2CarrierGateway
\ No newline at end of file