diff --git a/docs/globals.rst b/docs/globals.rst index ed83e6490..466d684ee 100644 --- a/docs/globals.rst +++ b/docs/globals.rst @@ -99,6 +99,7 @@ Currently, the following resources and properties are being supported: OpenApiVersion: Domain: SecurityPolicy: + EndpointAccessMode: HttpApi: # Properties of AWS::Serverless::HttpApi diff --git a/integration/resources/expected/single/api_with_custom_domain_security_policy_edge.json b/integration/resources/expected/single/api_with_custom_domain_security_policy_edge.json new file mode 100644 index 000000000..d45f76399 --- /dev/null +++ b/integration/resources/expected/single/api_with_custom_domain_security_policy_edge.json @@ -0,0 +1,26 @@ +[ + { + "LogicalResourceId": "MyApi", + "ResourceType": "AWS::ApiGateway::RestApi" + }, + { + "LogicalResourceId": "MyApiDeployment", + "ResourceType": "AWS::ApiGateway::Deployment" + }, + { + "LogicalResourceId": "MyApiProdStage", + "ResourceType": "AWS::ApiGateway::Stage" + }, + { + "LogicalResourceId": "ApiGatewayDomainName", + "ResourceType": "AWS::ApiGateway::DomainName" + }, + { + "LogicalResourceId": "MyApiBasePathMapping", + "ResourceType": "AWS::ApiGateway::BasePathMapping" + }, + { + "LogicalResourceId": "RecordSetGroup", + "ResourceType": "AWS::Route53::RecordSetGroup" + } +] diff --git a/integration/resources/expected/single/api_with_custom_domain_security_policy_regional.json b/integration/resources/expected/single/api_with_custom_domain_security_policy_regional.json new file mode 100644 index 000000000..d45f76399 --- /dev/null +++ b/integration/resources/expected/single/api_with_custom_domain_security_policy_regional.json @@ -0,0 +1,26 @@ +[ + { + "LogicalResourceId": "MyApi", + "ResourceType": "AWS::ApiGateway::RestApi" + }, + { + "LogicalResourceId": "MyApiDeployment", + "ResourceType": "AWS::ApiGateway::Deployment" + }, + { + "LogicalResourceId": "MyApiProdStage", + "ResourceType": "AWS::ApiGateway::Stage" + }, + { + "LogicalResourceId": "ApiGatewayDomainName", + "ResourceType": "AWS::ApiGateway::DomainName" + }, + { + "LogicalResourceId": "MyApiBasePathMapping", + "ResourceType": "AWS::ApiGateway::BasePathMapping" + }, + { + "LogicalResourceId": "RecordSetGroup", + "ResourceType": "AWS::Route53::RecordSetGroup" + } +] diff --git a/integration/resources/expected/single/api_with_endpoint_access_mode.json b/integration/resources/expected/single/api_with_endpoint_access_mode.json new file mode 100644 index 000000000..30b334c23 --- /dev/null +++ b/integration/resources/expected/single/api_with_endpoint_access_mode.json @@ -0,0 +1,14 @@ +[ + { + "LogicalResourceId": "MyApi", + "ResourceType": "AWS::ApiGateway::RestApi" + }, + { + "LogicalResourceId": "MyApiDeployment", + "ResourceType": "AWS::ApiGateway::Deployment" + }, + { + "LogicalResourceId": "MyApiProdStage", + "ResourceType": "AWS::ApiGateway::Stage" + } +] diff --git a/integration/resources/templates/single/api_with_custom_domain_security_policy_edge.yaml b/integration/resources/templates/single/api_with_custom_domain_security_policy_edge.yaml new file mode 100644 index 000000000..b12ed721f --- /dev/null +++ b/integration/resources/templates/single/api_with_custom_domain_security_policy_edge.yaml @@ -0,0 +1,27 @@ +Parameters: + DomainName: + Type: String + CertificateArn: + Type: String + HostedZoneId: + Type: String + +Resources: + MyApi: + Type: AWS::Serverless::Api + Properties: + StageName: Prod + DefinitionUri: ${definitionuri} + EndpointConfiguration: + Type: EDGE + Domain: + DomainName: !Ref DomainName + CertificateArn: !Ref CertificateArn + EndpointConfiguration: EDGE + SecurityPolicy: SecurityPolicy_TLS13_2025_EDGE + EndpointAccessMode: STRICT + Route53: + HostedZoneId: !Ref HostedZoneId + +Metadata: + SamTransformTest: true diff --git a/integration/resources/templates/single/api_with_custom_domain_security_policy_regional.yaml b/integration/resources/templates/single/api_with_custom_domain_security_policy_regional.yaml new file mode 100644 index 000000000..4799cc90f --- /dev/null +++ b/integration/resources/templates/single/api_with_custom_domain_security_policy_regional.yaml @@ -0,0 +1,27 @@ +Parameters: + DomainName: + Type: String + CertificateArn: + Type: String + HostedZoneId: + Type: String + +Resources: + MyApi: + Type: AWS::Serverless::Api + Properties: + StageName: Prod + DefinitionUri: ${definitionuri} + EndpointConfiguration: + Type: REGIONAL + Domain: + DomainName: !Ref DomainName + CertificateArn: !Ref CertificateArn + EndpointConfiguration: REGIONAL + SecurityPolicy: SecurityPolicy_TLS13_1_3_2025_09 + EndpointAccessMode: STRICT + Route53: + HostedZoneId: !Ref HostedZoneId + +Metadata: + SamTransformTest: true diff --git a/integration/resources/templates/single/api_with_endpoint_access_mode.yaml b/integration/resources/templates/single/api_with_endpoint_access_mode.yaml new file mode 100644 index 000000000..40b1cb327 --- /dev/null +++ b/integration/resources/templates/single/api_with_endpoint_access_mode.yaml @@ -0,0 +1,20 @@ +Parameters: + SecurityPolicyValue: + Type: String + Default: SecurityPolicy_TLS13_1_3_2025_09 + EndpointAccessModeValue: + Type: String + Default: STRICT + +Resources: + MyApi: + Type: AWS::Serverless::Api + Properties: + StageName: Prod + DefinitionUri: ${definitionuri} + SecurityPolicy: !Ref SecurityPolicyValue + EndpointAccessMode: !Ref EndpointAccessModeValue + EndpointConfiguration: + Type: REGIONAL +Metadata: + SamTransformTest: true diff --git a/integration/single/test_api_with_custom_domain_security_policy.py b/integration/single/test_api_with_custom_domain_security_policy.py new file mode 100644 index 000000000..e71e4c9f9 --- /dev/null +++ b/integration/single/test_api_with_custom_domain_security_policy.py @@ -0,0 +1,39 @@ +from unittest.case import skipIf + +from integration.config.service_names import CUSTOM_DOMAIN +from integration.helpers.base_internal_test import BaseInternalTest +from integration.helpers.base_test import nonblocking +from integration.helpers.resource import current_region_not_included + + +@skipIf( + current_region_not_included([CUSTOM_DOMAIN]), + "Custom domain is not supported in this testing region", +) +@nonblocking +class TestApiWithCustomDomainSecurityPolicy(BaseInternalTest): + """ + Test AWS::Serverless::Api with SecurityPolicy and EndpointAccessMode in Domain configuration + """ + + def test_api_with_custom_domain_security_policy_regional(self): + self.create_and_verify_stack("single/api_with_custom_domain_security_policy_regional") + + domain_name_id = self.get_physical_id_by_type("AWS::ApiGateway::DomainName") + result = self.client_provider.api_client.get_domain_name(domainName=domain_name_id) + + end_point_config = result["endpointConfiguration"] + self.assertEqual(["REGIONAL"], end_point_config["types"]) + self.assertEqual("SecurityPolicy_TLS13_1_3_2025_09", result["securityPolicy"]) + self.assertEqual("STRICT", result["endpointAccessMode"]) + + def test_api_with_custom_domain_security_policy_edge(self): + self.create_and_verify_stack("single/api_with_custom_domain_security_policy_edge") + + domain_name_id = self.get_physical_id_by_type("AWS::ApiGateway::DomainName") + result = self.client_provider.api_client.get_domain_name(domainName=domain_name_id) + + end_point_config = result["endpointConfiguration"] + self.assertEqual(["EDGE"], end_point_config["types"]) + self.assertEqual("SecurityPolicy_TLS13_2025_EDGE", result["securityPolicy"]) + self.assertEqual("STRICT", result["endpointAccessMode"]) diff --git a/integration/single/test_api_with_endpoint_access_mode.py b/integration/single/test_api_with_endpoint_access_mode.py new file mode 100644 index 000000000..dc2ce6a95 --- /dev/null +++ b/integration/single/test_api_with_endpoint_access_mode.py @@ -0,0 +1,38 @@ +from unittest.case import skipIf + +from integration.config.service_names import REST_API +from integration.helpers.base_test import BaseTest +from integration.helpers.resource import current_region_does_not_support + + +@skipIf(current_region_does_not_support([REST_API]), "Rest API is not supported in this testing region") +class TestApiWithEndpointAccessMode(BaseTest): + """ + Tests for AWS::Serverless::Api with EndpointAccessMode property + """ + + def test_api_with_endpoint_access_mode(self): + # Create stack with STRICT + parameters = [ + {"ParameterKey": "SecurityPolicyValue", "ParameterValue": "SecurityPolicy_TLS13_1_3_2025_09"}, + {"ParameterKey": "EndpointAccessModeValue", "ParameterValue": "STRICT"}, + ] + self.create_and_verify_stack("single/api_with_endpoint_access_mode", parameters) + + rest_api_id = self.get_physical_id_by_type("AWS::ApiGateway::RestApi") + rest_api = self.client_provider.api_client.get_rest_api(restApiId=rest_api_id) + + self.assertEqual(rest_api["securityPolicy"], "SecurityPolicy_TLS13_1_3_2025_09") + self.assertEqual(rest_api["endpointAccessMode"], "STRICT") + + # Update stack with BASIC + update_parameters = [ + {"ParameterKey": "SecurityPolicyValue", "ParameterValue": "SecurityPolicy_TLS13_1_3_2025_09"}, + {"ParameterKey": "EndpointAccessModeValue", "ParameterValue": "BASIC"}, + ] + self.update_stack(parameters=update_parameters) + + rest_api = self.client_provider.api_client.get_rest_api(restApiId=rest_api_id) + + self.assertEqual(rest_api["securityPolicy"], "SecurityPolicy_TLS13_1_3_2025_09") + self.assertEqual(rest_api["endpointAccessMode"], "BASIC") diff --git a/samtranslator/internal/schema_source/aws_serverless_api.py b/samtranslator/internal/schema_source/aws_serverless_api.py index c478a3c3a..f0dfc7320 100644 --- a/samtranslator/internal/schema_source/aws_serverless_api.py +++ b/samtranslator/internal/schema_source/aws_serverless_api.py @@ -196,6 +196,11 @@ class Domain(BaseModel): EndpointConfiguration: Optional[SamIntrinsicable[Literal["REGIONAL", "EDGE", "PRIVATE"]]] = domain( "EndpointConfiguration" ) + EndpointAccessMode: Optional[PassThroughProp] = passthrough_prop( + DOMAIN_STEM, + "EndpointAccessMode", + ["AWS::ApiGateway::DomainName", "Properties", "EndpointAccessMode"], + ) IpAddressType: Optional[PassThroughProp] # TODO: add documentation; currently unavailable MutualTlsAuthentication: Optional[PassThroughProp] = passthrough_prop( DOMAIN_STEM, @@ -306,6 +311,11 @@ class Properties(BaseModel): ) DisableExecuteApiEndpoint: Optional[PassThroughProp] = properties("DisableExecuteApiEndpoint") Domain: Optional[Domain] = properties("Domain") + EndpointAccessMode: Optional[PassThroughProp] = passthrough_prop( + PROPERTIES_STEM, + "EndpointAccessMode", + ["AWS::ApiGateway::RestApi", "Properties", "EndpointAccessMode"], + ) EndpointConfiguration: Optional[EndpointConfigurationType] = properties("EndpointConfiguration") FailOnWarnings: Optional[PassThroughProp] = passthrough_prop( PROPERTIES_STEM, @@ -419,6 +429,11 @@ class Globals(BaseModel): "SecurityPolicy", ["AWS::ApiGateway::RestApi", "Properties", "SecurityPolicy"], ) + EndpointAccessMode: Optional[PassThroughProp] = passthrough_prop( + PROPERTIES_STEM, + "EndpointAccessMode", + ["AWS::ApiGateway::RestApi", "Properties", "EndpointAccessMode"], + ) class Resource(ResourceAttributes): diff --git a/samtranslator/internal/schema_source/sam-docs.json b/samtranslator/internal/schema_source/sam-docs.json index edd52e62d..3b90cc728 100644 --- a/samtranslator/internal/schema_source/sam-docs.json +++ b/samtranslator/internal/schema_source/sam-docs.json @@ -49,6 +49,7 @@ "CertificateArn": "The Amazon Resource Name (https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/ARN.html) of an AWS managed certificate this domain name's endpoint. AWS Certificate Manager is the only supported source. \n*Type*: String \n*Required*: Yes \n*CloudFormation compatibility*: This property is similar to the [`CertificateArn`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-domainname.html#cfn-apigateway-domainname-certificatearn) property of an `AWS::ApiGateway::DomainName` resource. If `EndpointConfiguration` is set to `REGIONAL` (the default value), `CertificateArn` maps to [RegionalCertificateArn](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-domainname.html#cfn-apigateway-domainname-regionalcertificatearn) in `AWS::ApiGateway::DomainName`. If the `EndpointConfiguration` is set to `EDGE`, `CertificateArn` maps to [CertificateArn](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-domainname.html#cfn-apigateway-domainname-certificatearn) in `AWS::ApiGateway::DomainName`. If `EndpointConfiguration` is set to `PRIVATE`, this property is passed to the [AWS::ApiGateway::DomainNameV2](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-domainnamev2) resource. \n*Additional notes*: For an `EDGE` endpoint, you must create the certificate in the `us-east-1` AWS Region.", "DomainName": "The custom domain name for your API Gateway API. Uppercase letters are not supported. \nAWS SAM generates an [https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-domainname.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-domainname.html) resource when this property is set. For information about this scenario, see [DomainName property is specified](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-specification-generated-resources-api.html#sam-specification-generated-resources-api-domain-name). For information about generated CloudFormation resources, see [Generated CloudFormation resources for AWS SAM](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-specification-generated-resources.html). \n*Type*: String \n*Required*: Yes \n*CloudFormation compatibility*: This property is passed directly to the [`DomainName`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-domainname.html#cfn-apigateway-domainname-domainname) property of an `AWS::ApiGateway::DomainName` resource, or to [https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-domainnamev2](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-domainnamev2) when EndpointConfiguration is set to `PRIVATE`.", "EndpointConfiguration": "Defines the type of API Gateway endpoint to map to the custom domain. The value of this property determines how the `CertificateArn` property is mapped in CloudFormation. \n*Valid values*: `EDGE`, `REGIONAL`, or `PRIVATE` \n*Type*: String \n*Required*: No \n*Default*: `REGIONAL` \n*CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an CloudFormation equivalent.", + "EndpointAccessMode": "The endpoint access mode for the custom domain name. \n*Type*: String \n*Required*: No \n*CloudFormation compatibility*: This property is passed directly to the [`EndpointAccessMode`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-domainname.html#cfn-apigateway-domainname-endpointaccessmode) property of an `AWS::ApiGateway::DomainName` resource, or to `AWS::ApiGateway::DomainNameV2` when `EndpointConfiguration` is set to `PRIVATE`.", "MutualTlsAuthentication": "The mutual Transport Layer Security (https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/TLS.html) authentication configuration for a custom domain name. \n*Type*: [MutualTlsAuthentication](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-domainname.html#cfn-apigateway-domainname-mutualtlsauthentication) \n*Required*: No \n*CloudFormation compatibility*: This property is passed directly to the [`MutualTlsAuthentication`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-domainname.html#cfn-apigateway-domainname-mutualtlsauthentication) property of an `AWS::ApiGateway::DomainName` resource.", "NormalizeBasePath": "Indicates whether non-alphanumeric characters are allowed in basepaths defined by the `BasePath` property. When set to `True`, non-alphanumeric characters are removed from basepaths. \nUse `NormalizeBasePath` with the `BasePath` property. \n*Type*: Boolean \n*Required*: No \n*Default*: True \n*CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an CloudFormation equivalent.", "OwnershipVerificationCertificateArn": "The ARN of the public certificate issued by ACM to validate ownership of your custom domain. Required only when you configure mutual TLS and you specify an ACM imported or private CA certificate ARN for the `CertificateArn`. \n*Type*: String \n*Required*: No \n*CloudFormation compatibility*: This property is passed directly to the [`OwnershipVerificationCertificateArn`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-domainname.html#cfn-apigateway-domainname-ownershipverificationcertificatearn) property of an `AWS::ApiGateway::DomainName` resource.", @@ -723,6 +724,7 @@ "Description": "A description of the Api resource. \n*Type*: String \n*Required*: No \n*CloudFormation compatibility*: This property is passed directly to the [`Description`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-restapi.html#cfn-apigateway-restapi-description) property of an `AWS::ApiGateway::RestApi` resource.", "DisableExecuteApiEndpoint": "Specifies whether clients can invoke your API by using the default `execute-api` endpoint. By default, clients can invoke your API with the default `https://{api_id}.execute-api.{region}.amazonaws.com`. To require that clients use a custom domain name to invoke your API, specify `True`. \n*Type*: Boolean \n*Required*: No \n*CloudFormation compatibility*: This property is similar to the `[ DisableExecuteApiEndpoint](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-restapi.html#cfn-apigateway-restapi-disableexecuteapiendpoint)` property of an `AWS::ApiGateway::RestApi` resource. It is passed directly to the `disableExecuteApiEndpoint` property of an `[ x-amazon-apigateway-endpoint-configuration](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-swagger-extensions-endpoint-configuration.html)` extension, which gets added to the ` [ Body](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-restapi.html#cfn-apigateway-restapi-body)` property of an `AWS::ApiGateway::RestApi` resource.", "Domain": "Configures a custom domain for this API Gateway API. \n*Type*: [DomainConfiguration](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-api-domainconfiguration.html) \n*Required*: No \n*CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an CloudFormation equivalent.", + "EndpointAccessMode": "The endpoint access mode for the RestApi. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`EndpointAccessMode`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-restapi.html#cfn-apigateway-restapi-endpointaccessmode) property of an `AWS::ApiGateway::RestApi` resource.", "EndpointConfiguration": "The endpoint type of a REST API. \n*Type*: [EndpointConfiguration](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-api-endpointconfiguration.html) \n*Required*: No \n*CloudFormation compatibility*: This property is similar to the [`EndpointConfiguration`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-restapi.html#cfn-apigateway-restapi-endpointconfiguration) property of an `AWS::ApiGateway::RestApi` resource. The nested configuration properties are named differently.", "FailOnWarnings": "Specifies whether to roll back the API creation (`true`) or not (`false`) when a warning is encountered. The default value is `false`. \n*Type*: Boolean \n*Required*: No \n*CloudFormation compatibility*: This property is passed directly to the [`FailOnWarnings`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-restapi.html#cfn-apigateway-restapi-failonwarnings) property of an `AWS::ApiGateway::RestApi` resource.", "GatewayResponses": "Configures Gateway Responses for an API. Gateway Responses are responses returned by API Gateway, either directly or through the use of Lambda Authorizers. For more information, see the documentation for the [Api Gateway OpenApi extension for Gateway Responses](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-swagger-extensions-gateway-responses.html). \n*Type*: Map \n*Required*: No \n*CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an CloudFormation equivalent.", diff --git a/samtranslator/model/api/api_generator.py b/samtranslator/model/api/api_generator.py index 10af5ed82..a37247907 100644 --- a/samtranslator/model/api/api_generator.py +++ b/samtranslator/model/api/api_generator.py @@ -222,6 +222,7 @@ def __init__( # noqa: PLR0913 feature_toggle: Optional[FeatureToggle] = None, policy: Optional[Union[Dict[str, Any], Intrinsicable[str]]] = None, security_policy: Optional[Intrinsicable[str]] = None, + endpoint_access_mode: Optional[Intrinsicable[str]] = None, ): """Constructs an API Generator class that generates API Gateway resources @@ -281,8 +282,9 @@ def __init__( # noqa: PLR0913 self.feature_toggle = feature_toggle self.policy = policy self.security_policy = security_policy + self.endpoint_access_mode = endpoint_access_mode - def _construct_rest_api(self) -> ApiGatewayRestApi: + def _construct_rest_api(self) -> ApiGatewayRestApi: # noqa: PLR0912 """Constructs and returns the ApiGateway RestApi. :returns: the RestApi to which this SAM Api corresponds @@ -340,6 +342,9 @@ def _construct_rest_api(self) -> ApiGatewayRestApi: if self.security_policy: rest_api.SecurityPolicy = self.security_policy + if self.endpoint_access_mode: + rest_api.EndpointAccessMode = self.endpoint_access_mode + return rest_api def _validate_properties(self) -> None: @@ -739,6 +744,8 @@ def _set_optional_domain_properties(self, domain: Union[ApiGatewayDomainName, Ap return if self.domain.get("SecurityPolicy", None): domain.SecurityPolicy = self.domain["SecurityPolicy"] + if self.domain.get("EndpointAccessMode", None): + domain.EndpointAccessMode = self.domain["EndpointAccessMode"] if self.domain.get("Policy", None): domain.Policy = self.domain["Policy"] if self.domain.get("OwnershipVerificationCertificateArn", None): diff --git a/samtranslator/model/apigateway.py b/samtranslator/model/apigateway.py index 73966d645..96470722d 100644 --- a/samtranslator/model/apigateway.py +++ b/samtranslator/model/apigateway.py @@ -31,6 +31,7 @@ class ApiGatewayRestApi(Resource): "Tags": GeneratedProperty(), "Policy": GeneratedProperty(), "SecurityPolicy": GeneratedProperty(), + "EndpointAccessMode": GeneratedProperty(), } Body: Optional[Dict[str, Any]] @@ -48,6 +49,7 @@ class ApiGatewayRestApi(Resource): Tags: Optional[PassThrough] Policy: Optional[PassThrough] SecurityPolicy: Optional[PassThrough] + EndpointAccessMode: Optional[PassThrough] runtime_attrs = {"rest_api_id": lambda self: ref(self.logical_id)} @@ -219,6 +221,7 @@ class ApiGatewayDomainName(Resource): "EndpointConfiguration": GeneratedProperty(), "MutualTlsAuthentication": GeneratedProperty(), "SecurityPolicy": GeneratedProperty(), + "EndpointAccessMode": GeneratedProperty(), "CertificateArn": GeneratedProperty(), "Tags": GeneratedProperty(), "OwnershipVerificationCertificateArn": GeneratedProperty(), @@ -229,6 +232,7 @@ class ApiGatewayDomainName(Resource): EndpointConfiguration: Optional[PassThrough] MutualTlsAuthentication: Optional[Dict[str, Any]] SecurityPolicy: Optional[PassThrough] + EndpointAccessMode: Optional[PassThrough] CertificateArn: Optional[PassThrough] Tags: Optional[PassThrough] OwnershipVerificationCertificateArn: Optional[PassThrough] @@ -240,6 +244,7 @@ class ApiGatewayDomainNameV2(Resource): "DomainName": GeneratedProperty(), "EndpointConfiguration": GeneratedProperty(), "SecurityPolicy": GeneratedProperty(), + "EndpointAccessMode": GeneratedProperty(), "CertificateArn": GeneratedProperty(), "Tags": GeneratedProperty(), "Policy": GeneratedProperty(), @@ -248,6 +253,7 @@ class ApiGatewayDomainNameV2(Resource): DomainName: PassThrough EndpointConfiguration: Optional[PassThrough] SecurityPolicy: Optional[PassThrough] + EndpointAccessMode: Optional[PassThrough] CertificateArn: Optional[PassThrough] Tags: Optional[PassThrough] Policy: Optional[PassThrough] diff --git a/samtranslator/model/sam_resources.py b/samtranslator/model/sam_resources.py index d58642828..93511b9ec 100644 --- a/samtranslator/model/sam_resources.py +++ b/samtranslator/model/sam_resources.py @@ -1648,6 +1648,7 @@ class SamApi(SamResourceMacro): "AlwaysDeploy": Property(False, IS_BOOL), "Policy": PropertyType(False, one_of(IS_STR, IS_DICT)), "SecurityPolicy": PropertyType(False, IS_STR), + "EndpointAccessMode": PropertyType(False, IS_STR), } Name: Optional[Intrinsicable[str]] @@ -1681,6 +1682,7 @@ class SamApi(SamResourceMacro): AlwaysDeploy: Optional[bool] Policy: Optional[Union[Dict[str, Any], Intrinsicable[str]]] SecurityPolicy: Optional[Intrinsicable[str]] + EndpointAccessMode: Optional[Intrinsicable[str]] referable_properties = { "Stage": ApiGatewayStage.resource_type, @@ -1750,6 +1752,7 @@ def to_cloudformation(self, **kwargs) -> List[Resource]: # type: ignore[no-unty feature_toggle=feature_toggle, policy=self.Policy, security_policy=self.SecurityPolicy, + endpoint_access_mode=self.EndpointAccessMode, ) generated_resources = api_generator.to_cloudformation(redeploy_restapi_parameters, route53_record_set_groups) diff --git a/samtranslator/plugins/globals/globals.py b/samtranslator/plugins/globals/globals.py index 9defaeaf9..092f4486b 100644 --- a/samtranslator/plugins/globals/globals.py +++ b/samtranslator/plugins/globals/globals.py @@ -89,6 +89,7 @@ class Globals: "AlwaysDeploy", "PropagateTags", "SecurityPolicy", + "EndpointAccessMode", ], SamResourceType.HttpApi.value: [ "Auth", diff --git a/samtranslator/schema/schema.json b/samtranslator/schema/schema.json index cfbcf14ca..f1d991248 100644 --- a/samtranslator/schema/schema.json +++ b/samtranslator/schema/schema.json @@ -359482,6 +359482,11 @@ "title": "DomainName", "type": "string" }, + "EndpointAccessMode": { + "markdownDescription": "The endpoint access mode for the custom domain name. \n*Type*: String \n*Required*: No \n*CloudFormation compatibility*: This property is passed directly to the [`EndpointAccessMode`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-domainname.html#cfn-apigateway-domainname-endpointaccessmode) property of an `AWS::ApiGateway::DomainName` resource, or to `AWS::ApiGateway::DomainNameV2` when `EndpointConfiguration` is set to `PRIVATE`.", + "title": "EndpointAccessMode", + "type": "string" + }, "EndpointConfiguration": { "anyOf": [ { @@ -359621,6 +359626,11 @@ "markdownDescription": "Configures a custom domain for this API Gateway API. \n*Type*: [DomainConfiguration](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-api-domainconfiguration.html) \n*Required*: No \n*CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an CloudFormation equivalent.", "title": "Domain" }, + "EndpointAccessMode": { + "markdownDescription": "The endpoint access mode for the RestApi. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`EndpointAccessMode`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-restapi.html#cfn-apigateway-restapi-endpointaccessmode) property of an `AWS::ApiGateway::RestApi` resource.", + "title": "EndpointAccessMode", + "type": "string" + }, "EndpointConfiguration": { "allOf": [ { @@ -359807,6 +359817,11 @@ "markdownDescription": "Configures a custom domain for this API Gateway API. \n*Type*: [DomainConfiguration](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-api-domainconfiguration.html) \n*Required*: No \n*CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an CloudFormation equivalent.", "title": "Domain" }, + "EndpointAccessMode": { + "markdownDescription": "The endpoint access mode for the RestApi. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`EndpointAccessMode`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-restapi.html#cfn-apigateway-restapi-endpointaccessmode) property of an `AWS::ApiGateway::RestApi` resource.", + "title": "EndpointAccessMode", + "type": "string" + }, "EndpointConfiguration": { "anyOf": [ { diff --git a/schema_source/sam.schema.json b/schema_source/sam.schema.json index 5cbca5201..32c8ae5e9 100644 --- a/schema_source/sam.schema.json +++ b/schema_source/sam.schema.json @@ -4387,6 +4387,25 @@ ], "title": "DomainName" }, + "EndpointAccessMode": { + "__samPassThrough": { + "markdownDescriptionOverride": "The endpoint access mode for the custom domain name. \n*Type*: String \n*Required*: No \n*CloudFormation compatibility*: This property is passed directly to the [`EndpointAccessMode`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-domainname.html#cfn-apigateway-domainname-endpointaccessmode) property of an `AWS::ApiGateway::DomainName` resource, or to `AWS::ApiGateway::DomainNameV2` when `EndpointConfiguration` is set to `PRIVATE`.", + "schemaPath": [ + "definitions", + "AWS::ApiGateway::DomainName", + "properties", + "Properties", + "properties", + "EndpointAccessMode" + ] + }, + "allOf": [ + { + "$ref": "#/definitions/PassThroughProp" + } + ], + "title": "EndpointAccessMode" + }, "EndpointConfiguration": { "anyOf": [ { @@ -4624,6 +4643,25 @@ "markdownDescription": "Configures a custom domain for this API Gateway API. \n*Type*: [DomainConfiguration](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-api-domainconfiguration.html) \n*Required*: No \n*CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an CloudFormation equivalent.", "title": "Domain" }, + "EndpointAccessMode": { + "__samPassThrough": { + "markdownDescriptionOverride": "The endpoint access mode for the RestApi. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`EndpointAccessMode`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-restapi.html#cfn-apigateway-restapi-endpointaccessmode) property of an `AWS::ApiGateway::RestApi` resource.", + "schemaPath": [ + "definitions", + "AWS::ApiGateway::RestApi", + "properties", + "Properties", + "properties", + "EndpointAccessMode" + ] + }, + "allOf": [ + { + "$ref": "#/definitions/PassThroughProp" + } + ], + "title": "EndpointAccessMode" + }, "EndpointConfiguration": { "allOf": [ { @@ -4958,6 +4996,25 @@ "markdownDescription": "Configures a custom domain for this API Gateway API. \n*Type*: [DomainConfiguration](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-api-domainconfiguration.html) \n*Required*: No \n*CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an CloudFormation equivalent.", "title": "Domain" }, + "EndpointAccessMode": { + "__samPassThrough": { + "markdownDescriptionOverride": "The endpoint access mode for the RestApi. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`EndpointAccessMode`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-restapi.html#cfn-apigateway-restapi-endpointaccessmode) property of an `AWS::ApiGateway::RestApi` resource.", + "schemaPath": [ + "definitions", + "AWS::ApiGateway::RestApi", + "properties", + "Properties", + "properties", + "EndpointAccessMode" + ] + }, + "allOf": [ + { + "$ref": "#/definitions/PassThroughProp" + } + ], + "title": "EndpointAccessMode" + }, "EndpointConfiguration": { "anyOf": [ { diff --git a/tests/model/api/test_api_generator_endpoint_access_mode.py b/tests/model/api/test_api_generator_endpoint_access_mode.py new file mode 100644 index 000000000..47e4e42c1 --- /dev/null +++ b/tests/model/api/test_api_generator_endpoint_access_mode.py @@ -0,0 +1,43 @@ +from unittest import TestCase +from unittest.mock import Mock + +from samtranslator.model.api.api_generator import ApiGenerator + + +class TestApiGeneratorEndpointAccessMode(TestCase): + def setUp(self): + self.logical_id = "MyApi" + self.default_args = { + "logical_id": self.logical_id, + "cache_cluster_enabled": None, + "cache_cluster_size": None, + "variables": None, + "depends_on": None, + "definition_body": {"swagger": "2.0"}, + "definition_uri": None, + "name": None, + "stage_name": "Prod", + "shared_api_usage_plan": Mock(), + "template_conditions": Mock(), + "method_settings": None, + "endpoint_configuration": {"Type": "REGIONAL"}, + "access_log_setting": None, + "canary_setting": None, + "tracing_enabled": None, + "open_api_version": None, + "always_deploy": None, + } + + def test_endpoint_access_mode_set(self): + api_generator = ApiGenerator(**self.default_args, endpoint_access_mode="STRICT") + + rest_api = api_generator._construct_rest_api() + + self.assertEqual(rest_api.EndpointAccessMode, "STRICT") + + def test_no_endpoint_access_mode(self): + api_generator = ApiGenerator(**self.default_args, endpoint_access_mode=None) + + rest_api = api_generator._construct_rest_api() + + self.assertIsNone(rest_api.EndpointAccessMode) diff --git a/tests/translator/input/api_with_custom_domain_security_policy_edge.yaml b/tests/translator/input/api_with_custom_domain_security_policy_edge.yaml new file mode 100644 index 000000000..4b4d73aee --- /dev/null +++ b/tests/translator/input/api_with_custom_domain_security_policy_edge.yaml @@ -0,0 +1,45 @@ +Parameters: + MyDomainName: + Type: String + MyCertificateArn: + Type: String + HostedZoneId: + Type: String + +Resources: + Api: + Type: AWS::Serverless::Api + Properties: + StageName: Prod + EndpointConfiguration: + Type: EDGE + Domain: + DomainName: !Ref MyDomainName + CertificateArn: !Ref MyCertificateArn + EndpointConfiguration: EDGE + SecurityPolicy: SecurityPolicy_TLS13_2025_EDGE + EndpointAccessMode: STRICT + Route53: + HostedZoneId: !Ref HostedZoneId + DefinitionBody: + swagger: '2.0' + info: + title: MockApi + version: '1.0' + paths: + /get: + get: + responses: + '200': + description: 200 response + x-amazon-apigateway-integration: + type: mock + requestTemplates: + application/json: '{"statusCode": 200}' + responses: + default: + statusCode: '200' + responseTemplates: + application/json: '{"message": "Hello World"}' +Metadata: + SamTransformTest: true diff --git a/tests/translator/input/api_with_custom_domain_security_policy_private.yaml b/tests/translator/input/api_with_custom_domain_security_policy_private.yaml new file mode 100644 index 000000000..871b640fe --- /dev/null +++ b/tests/translator/input/api_with_custom_domain_security_policy_private.yaml @@ -0,0 +1,78 @@ +Parameters: + MyDomainName: + Type: String + MyCertificateArn: + Type: String + HostedZoneId: + Type: String + VpcEndpointId: + Type: String + VpcEndpointDomainName: + Type: String + VpcEndpointHostedZoneId: + Type: String + +Resources: + Api: + Type: AWS::Serverless::Api + Properties: + StageName: Prod + Auth: + ResourcePolicy: + CustomStatements: + - Effect: Allow + Action: execute-api:Invoke + Resource: + - execute-api:/*/*/* + Principal: '*' + Condition: + StringEquals: + aws:SourceVpce: !Ref VpcEndpointId + EndpointConfiguration: + Type: PRIVATE + VPCEndpointIds: + - !Ref VpcEndpointId + Domain: + DomainName: !Ref MyDomainName + CertificateArn: !Ref MyCertificateArn + EndpointConfiguration: PRIVATE + SecurityPolicy: SecurityPolicy_TLS13_1_3_2025_09 + EndpointAccessMode: STRICT + Route53: + HostedZoneId: !Ref HostedZoneId + VpcEndpointDomainName: !Ref VpcEndpointDomainName + VpcEndpointHostedZoneId: !Ref VpcEndpointHostedZoneId + AccessAssociation: + VpcEndpointId: !Ref VpcEndpointId + Policy: + Version: '2012-10-17' + Statement: + - Effect: Allow + Principal: '*' + Action: execute-api:Invoke + Resource: execute-api:/*/*/* + Condition: + StringEquals: + aws:SourceVpce: !Ref VpcEndpointId + DefinitionBody: + swagger: '2.0' + info: + title: MockApi + version: '1.0' + paths: + /get: + get: + responses: + '200': + description: 200 response + x-amazon-apigateway-integration: + type: mock + requestTemplates: + application/json: '{"statusCode": 200}' + responses: + default: + statusCode: '200' + responseTemplates: + application/json: '{"message": "Hello World"}' +Metadata: + SamTransformTest: true diff --git a/tests/translator/input/api_with_custom_domain_security_policy_regional.yaml b/tests/translator/input/api_with_custom_domain_security_policy_regional.yaml new file mode 100644 index 000000000..bbd9d7189 --- /dev/null +++ b/tests/translator/input/api_with_custom_domain_security_policy_regional.yaml @@ -0,0 +1,45 @@ +Parameters: + MyDomainName: + Type: String + MyCertificateArn: + Type: String + HostedZoneId: + Type: String + +Resources: + Api: + Type: AWS::Serverless::Api + Properties: + StageName: Prod + EndpointConfiguration: + Type: REGIONAL + Domain: + DomainName: !Ref MyDomainName + CertificateArn: !Ref MyCertificateArn + EndpointConfiguration: REGIONAL + SecurityPolicy: SecurityPolicy_TLS13_1_3_2025_09 + EndpointAccessMode: STRICT + Route53: + HostedZoneId: !Ref HostedZoneId + DefinitionBody: + swagger: '2.0' + info: + title: MockApi + version: '1.0' + paths: + /get: + get: + responses: + '200': + description: 200 response + x-amazon-apigateway-integration: + type: mock + requestTemplates: + application/json: '{"statusCode": 200}' + responses: + default: + statusCode: '200' + responseTemplates: + application/json: '{"message": "Hello World"}' +Metadata: + SamTransformTest: true diff --git a/tests/translator/input/api_with_endpoint_access_mode.yaml b/tests/translator/input/api_with_endpoint_access_mode.yaml new file mode 100644 index 000000000..dbd0e995c --- /dev/null +++ b/tests/translator/input/api_with_endpoint_access_mode.yaml @@ -0,0 +1,19 @@ +Globals: + Api: + EndpointAccessMode: BASIC + SecurityPolicy: SecurityPolicy_TLS13_1_3_2025_09 + +Resources: + # Inherits Globals + ApiInheritGlobals: + Type: AWS::Serverless::Api + Properties: + StageName: Prod + + # Top-level overrides Globals + ApiTopLevelOverride: + Type: AWS::Serverless::Api + Properties: + StageName: Prod + EndpointAccessMode: STRICT + SecurityPolicy: SecurityPolicy_TLS13_1_3_2025_09 diff --git a/tests/translator/output/api_with_custom_domain_security_policy_edge.json b/tests/translator/output/api_with_custom_domain_security_policy_edge.json new file mode 100644 index 000000000..67b32d5b9 --- /dev/null +++ b/tests/translator/output/api_with_custom_domain_security_policy_edge.json @@ -0,0 +1,142 @@ +{ + "Metadata": { + "SamTransformTest": true + }, + "Parameters": { + "HostedZoneId": { + "Type": "String" + }, + "MyCertificateArn": { + "Type": "String" + }, + "MyDomainName": { + "Type": "String" + } + }, + "Resources": { + "Api": { + "Properties": { + "Body": { + "info": { + "title": "MockApi", + "version": "1.0" + }, + "paths": { + "/get": { + "get": { + "responses": { + "200": { + "description": "200 response" + } + }, + "x-amazon-apigateway-integration": { + "requestTemplates": { + "application/json": "{\"statusCode\": 200}" + }, + "responses": { + "default": { + "responseTemplates": { + "application/json": "{\"message\": \"Hello World\"}" + }, + "statusCode": "200" + } + }, + "type": "mock" + } + } + } + }, + "swagger": "2.0" + }, + "EndpointConfiguration": { + "Types": [ + "EDGE" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "EDGE" + } + }, + "Type": "AWS::ApiGateway::RestApi" + }, + "ApiBasePathMapping": { + "Properties": { + "DomainName": { + "Ref": "ApiGatewayDomainNamee12ae193a4" + }, + "RestApiId": { + "Ref": "Api" + }, + "Stage": { + "Ref": "ApiProdStage" + } + }, + "Type": "AWS::ApiGateway::BasePathMapping" + }, + "ApiDeploymentb4212ace49": { + "Properties": { + "Description": "RestApi deployment id: b4212ace4953ed4f1debfa33eda1b29707d537e1", + "RestApiId": { + "Ref": "Api" + }, + "StageName": "Stage" + }, + "Type": "AWS::ApiGateway::Deployment" + }, + "ApiGatewayDomainNamee12ae193a4": { + "Properties": { + "CertificateArn": { + "Ref": "MyCertificateArn" + }, + "DomainName": { + "Ref": "MyDomainName" + }, + "EndpointAccessMode": "STRICT", + "EndpointConfiguration": { + "Types": [ + "EDGE" + ] + }, + "SecurityPolicy": "SecurityPolicy_TLS13_2025_EDGE" + }, + "Type": "AWS::ApiGateway::DomainName" + }, + "ApiProdStage": { + "Properties": { + "DeploymentId": { + "Ref": "ApiDeploymentb4212ace49" + }, + "RestApiId": { + "Ref": "Api" + }, + "StageName": "Prod" + }, + "Type": "AWS::ApiGateway::Stage" + }, + "RecordSetGroup1194dea82a": { + "Properties": { + "HostedZoneId": { + "Ref": "HostedZoneId" + }, + "RecordSets": [ + { + "AliasTarget": { + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainNamee12ae193a4", + "DistributionDomainName" + ] + }, + "HostedZoneId": "Z2FDTNDATAQYW2" + }, + "Name": { + "Ref": "MyDomainName" + }, + "Type": "A" + } + ] + }, + "Type": "AWS::Route53::RecordSetGroup" + } + } +} diff --git a/tests/translator/output/api_with_custom_domain_security_policy_private.json b/tests/translator/output/api_with_custom_domain_security_policy_private.json new file mode 100644 index 000000000..163ca73df --- /dev/null +++ b/tests/translator/output/api_with_custom_domain_security_policy_private.json @@ -0,0 +1,205 @@ +{ + "Metadata": { + "SamTransformTest": true + }, + "Parameters": { + "HostedZoneId": { + "Type": "String" + }, + "MyCertificateArn": { + "Type": "String" + }, + "MyDomainName": { + "Type": "String" + }, + "VpcEndpointDomainName": { + "Type": "String" + }, + "VpcEndpointHostedZoneId": { + "Type": "String" + }, + "VpcEndpointId": { + "Type": "String" + } + }, + "Resources": { + "Api": { + "Properties": { + "Body": { + "info": { + "title": "MockApi", + "version": "1.0" + }, + "paths": { + "/get": { + "get": { + "responses": { + "200": { + "description": "200 response" + } + }, + "x-amazon-apigateway-integration": { + "requestTemplates": { + "application/json": "{\"statusCode\": 200}" + }, + "responses": { + "default": { + "responseTemplates": { + "application/json": "{\"message\": \"Hello World\"}" + }, + "statusCode": "200" + } + }, + "type": "mock" + } + } + } + }, + "swagger": "2.0", + "x-amazon-apigateway-policy": { + "Statement": [ + { + "Action": "execute-api:Invoke", + "Condition": { + "StringEquals": { + "aws:SourceVpce": { + "Ref": "VpcEndpointId" + } + } + }, + "Effect": "Allow", + "Principal": "*", + "Resource": [ + "execute-api:/*/*/*" + ] + } + ], + "Version": "2012-10-17" + } + }, + "EndpointConfiguration": { + "Types": [ + "PRIVATE" + ], + "VpcEndpointIds": [ + { + "Ref": "VpcEndpointId" + } + ] + }, + "Parameters": { + "endpointConfigurationTypes": "PRIVATE" + } + }, + "Type": "AWS::ApiGateway::RestApi" + }, + "ApiBasePathMapping": { + "Properties": { + "DomainNameArn": { + "Ref": "ApiGatewayDomainNameV2e12ae193a4" + }, + "RestApiId": { + "Ref": "Api" + }, + "Stage": { + "Ref": "ApiProdStage" + } + }, + "Type": "AWS::ApiGateway::BasePathMappingV2" + }, + "ApiDeployment201b5a52ed": { + "Properties": { + "Description": "RestApi deployment id: 201b5a52ed6f40089c7e4d46db54f63bdd4dd157", + "RestApiId": { + "Ref": "Api" + }, + "StageName": "Stage" + }, + "Type": "AWS::ApiGateway::Deployment" + }, + "ApiGatewayDomainNameV2e12ae193a4": { + "Properties": { + "CertificateArn": { + "Ref": "MyCertificateArn" + }, + "DomainName": { + "Ref": "MyDomainName" + }, + "EndpointAccessMode": "STRICT", + "EndpointConfiguration": { + "Types": [ + "PRIVATE" + ] + }, + "Policy": { + "Statement": [ + { + "Action": "execute-api:Invoke", + "Condition": { + "StringEquals": { + "aws:SourceVpce": { + "Ref": "VpcEndpointId" + } + } + }, + "Effect": "Allow", + "Principal": "*", + "Resource": "execute-api:/*/*/*" + } + ], + "Version": "2012-10-17" + }, + "SecurityPolicy": "SecurityPolicy_TLS13_1_3_2025_09" + }, + "Type": "AWS::ApiGateway::DomainNameV2" + }, + "ApiProdStage": { + "Properties": { + "DeploymentId": { + "Ref": "ApiDeployment201b5a52ed" + }, + "RestApiId": { + "Ref": "Api" + }, + "StageName": "Prod" + }, + "Type": "AWS::ApiGateway::Stage" + }, + "DomainNameAccessAssociationf4f910dee7": { + "Properties": { + "AccessAssociationSource": { + "Ref": "VpcEndpointId" + }, + "AccessAssociationSourceType": "VPCE", + "DomainNameArn": { + "Ref": "ApiGatewayDomainNameV2e12ae193a4" + } + }, + "Type": "AWS::ApiGateway::DomainNameAccessAssociation" + }, + "RecordSetGroup1194dea82a": { + "Properties": { + "HostedZoneId": { + "Ref": "HostedZoneId" + }, + "RecordSets": [ + { + "AliasTarget": { + "DNSName": { + "Ref": "VpcEndpointDomainName" + }, + "HostedZoneId": { + "Ref": "VpcEndpointHostedZoneId" + } + }, + "Name": { + "Ref": "MyDomainName" + }, + "Type": "A" + } + ] + }, + "Type": "AWS::Route53::RecordSetGroup" + } + } +} diff --git a/tests/translator/output/api_with_custom_domain_security_policy_regional.json b/tests/translator/output/api_with_custom_domain_security_policy_regional.json new file mode 100644 index 000000000..ca4762151 --- /dev/null +++ b/tests/translator/output/api_with_custom_domain_security_policy_regional.json @@ -0,0 +1,147 @@ +{ + "Metadata": { + "SamTransformTest": true + }, + "Parameters": { + "HostedZoneId": { + "Type": "String" + }, + "MyCertificateArn": { + "Type": "String" + }, + "MyDomainName": { + "Type": "String" + } + }, + "Resources": { + "Api": { + "Properties": { + "Body": { + "info": { + "title": "MockApi", + "version": "1.0" + }, + "paths": { + "/get": { + "get": { + "responses": { + "200": { + "description": "200 response" + } + }, + "x-amazon-apigateway-integration": { + "requestTemplates": { + "application/json": "{\"statusCode\": 200}" + }, + "responses": { + "default": { + "responseTemplates": { + "application/json": "{\"message\": \"Hello World\"}" + }, + "statusCode": "200" + } + }, + "type": "mock" + } + } + } + }, + "swagger": "2.0" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + }, + "Type": "AWS::ApiGateway::RestApi" + }, + "ApiBasePathMapping": { + "Properties": { + "DomainName": { + "Ref": "ApiGatewayDomainNamee12ae193a4" + }, + "RestApiId": { + "Ref": "Api" + }, + "Stage": { + "Ref": "ApiProdStage" + } + }, + "Type": "AWS::ApiGateway::BasePathMapping" + }, + "ApiDeployment84a6e2abbb": { + "Properties": { + "Description": "RestApi deployment id: 84a6e2abbbe8b085641de8b944a7dd72a4d01764", + "RestApiId": { + "Ref": "Api" + }, + "StageName": "Stage" + }, + "Type": "AWS::ApiGateway::Deployment" + }, + "ApiGatewayDomainNamee12ae193a4": { + "Properties": { + "DomainName": { + "Ref": "MyDomainName" + }, + "EndpointAccessMode": "STRICT", + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "RegionalCertificateArn": { + "Ref": "MyCertificateArn" + }, + "SecurityPolicy": "SecurityPolicy_TLS13_1_3_2025_09" + }, + "Type": "AWS::ApiGateway::DomainName" + }, + "ApiProdStage": { + "Properties": { + "DeploymentId": { + "Ref": "ApiDeployment84a6e2abbb" + }, + "RestApiId": { + "Ref": "Api" + }, + "StageName": "Prod" + }, + "Type": "AWS::ApiGateway::Stage" + }, + "RecordSetGroup1194dea82a": { + "Properties": { + "HostedZoneId": { + "Ref": "HostedZoneId" + }, + "RecordSets": [ + { + "AliasTarget": { + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainNamee12ae193a4", + "RegionalDomainName" + ] + }, + "HostedZoneId": { + "Fn::GetAtt": [ + "ApiGatewayDomainNamee12ae193a4", + "RegionalHostedZoneId" + ] + } + }, + "Name": { + "Ref": "MyDomainName" + }, + "Type": "A" + } + ] + }, + "Type": "AWS::Route53::RecordSetGroup" + } + } +} diff --git a/tests/translator/output/api_with_endpoint_access_mode.json b/tests/translator/output/api_with_endpoint_access_mode.json new file mode 100644 index 000000000..c906946b2 --- /dev/null +++ b/tests/translator/output/api_with_endpoint_access_mode.json @@ -0,0 +1,82 @@ +{ + "Resources": { + "ApiInheritGlobals": { + "Properties": { + "Body": { + "info": { + "title": { + "Ref": "AWS::StackName" + }, + "version": "1.0" + }, + "paths": {}, + "swagger": "2.0" + }, + "EndpointAccessMode": "BASIC", + "SecurityPolicy": "SecurityPolicy_TLS13_1_3_2025_09" + }, + "Type": "AWS::ApiGateway::RestApi" + }, + "ApiInheritGlobalsDeployment5332c373d4": { + "Properties": { + "Description": "RestApi deployment id: 5332c373d45c69e6c0f562b4a419aa8eb311adc7", + "RestApiId": { + "Ref": "ApiInheritGlobals" + }, + "StageName": "Stage" + }, + "Type": "AWS::ApiGateway::Deployment" + }, + "ApiInheritGlobalsProdStage": { + "Properties": { + "DeploymentId": { + "Ref": "ApiInheritGlobalsDeployment5332c373d4" + }, + "RestApiId": { + "Ref": "ApiInheritGlobals" + }, + "StageName": "Prod" + }, + "Type": "AWS::ApiGateway::Stage" + }, + "ApiTopLevelOverride": { + "Properties": { + "Body": { + "info": { + "title": { + "Ref": "AWS::StackName" + }, + "version": "1.0" + }, + "paths": {}, + "swagger": "2.0" + }, + "EndpointAccessMode": "STRICT", + "SecurityPolicy": "SecurityPolicy_TLS13_1_3_2025_09" + }, + "Type": "AWS::ApiGateway::RestApi" + }, + "ApiTopLevelOverrideDeployment5332c373d4": { + "Properties": { + "Description": "RestApi deployment id: 5332c373d45c69e6c0f562b4a419aa8eb311adc7", + "RestApiId": { + "Ref": "ApiTopLevelOverride" + }, + "StageName": "Stage" + }, + "Type": "AWS::ApiGateway::Deployment" + }, + "ApiTopLevelOverrideProdStage": { + "Properties": { + "DeploymentId": { + "Ref": "ApiTopLevelOverrideDeployment5332c373d4" + }, + "RestApiId": { + "Ref": "ApiTopLevelOverride" + }, + "StageName": "Prod" + }, + "Type": "AWS::ApiGateway::Stage" + } + } +} diff --git a/tests/translator/output/aws-cn/api_with_custom_domain_security_policy_edge.json b/tests/translator/output/aws-cn/api_with_custom_domain_security_policy_edge.json new file mode 100644 index 000000000..67b32d5b9 --- /dev/null +++ b/tests/translator/output/aws-cn/api_with_custom_domain_security_policy_edge.json @@ -0,0 +1,142 @@ +{ + "Metadata": { + "SamTransformTest": true + }, + "Parameters": { + "HostedZoneId": { + "Type": "String" + }, + "MyCertificateArn": { + "Type": "String" + }, + "MyDomainName": { + "Type": "String" + } + }, + "Resources": { + "Api": { + "Properties": { + "Body": { + "info": { + "title": "MockApi", + "version": "1.0" + }, + "paths": { + "/get": { + "get": { + "responses": { + "200": { + "description": "200 response" + } + }, + "x-amazon-apigateway-integration": { + "requestTemplates": { + "application/json": "{\"statusCode\": 200}" + }, + "responses": { + "default": { + "responseTemplates": { + "application/json": "{\"message\": \"Hello World\"}" + }, + "statusCode": "200" + } + }, + "type": "mock" + } + } + } + }, + "swagger": "2.0" + }, + "EndpointConfiguration": { + "Types": [ + "EDGE" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "EDGE" + } + }, + "Type": "AWS::ApiGateway::RestApi" + }, + "ApiBasePathMapping": { + "Properties": { + "DomainName": { + "Ref": "ApiGatewayDomainNamee12ae193a4" + }, + "RestApiId": { + "Ref": "Api" + }, + "Stage": { + "Ref": "ApiProdStage" + } + }, + "Type": "AWS::ApiGateway::BasePathMapping" + }, + "ApiDeploymentb4212ace49": { + "Properties": { + "Description": "RestApi deployment id: b4212ace4953ed4f1debfa33eda1b29707d537e1", + "RestApiId": { + "Ref": "Api" + }, + "StageName": "Stage" + }, + "Type": "AWS::ApiGateway::Deployment" + }, + "ApiGatewayDomainNamee12ae193a4": { + "Properties": { + "CertificateArn": { + "Ref": "MyCertificateArn" + }, + "DomainName": { + "Ref": "MyDomainName" + }, + "EndpointAccessMode": "STRICT", + "EndpointConfiguration": { + "Types": [ + "EDGE" + ] + }, + "SecurityPolicy": "SecurityPolicy_TLS13_2025_EDGE" + }, + "Type": "AWS::ApiGateway::DomainName" + }, + "ApiProdStage": { + "Properties": { + "DeploymentId": { + "Ref": "ApiDeploymentb4212ace49" + }, + "RestApiId": { + "Ref": "Api" + }, + "StageName": "Prod" + }, + "Type": "AWS::ApiGateway::Stage" + }, + "RecordSetGroup1194dea82a": { + "Properties": { + "HostedZoneId": { + "Ref": "HostedZoneId" + }, + "RecordSets": [ + { + "AliasTarget": { + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainNamee12ae193a4", + "DistributionDomainName" + ] + }, + "HostedZoneId": "Z2FDTNDATAQYW2" + }, + "Name": { + "Ref": "MyDomainName" + }, + "Type": "A" + } + ] + }, + "Type": "AWS::Route53::RecordSetGroup" + } + } +} diff --git a/tests/translator/output/aws-cn/api_with_custom_domain_security_policy_private.json b/tests/translator/output/aws-cn/api_with_custom_domain_security_policy_private.json new file mode 100644 index 000000000..163ca73df --- /dev/null +++ b/tests/translator/output/aws-cn/api_with_custom_domain_security_policy_private.json @@ -0,0 +1,205 @@ +{ + "Metadata": { + "SamTransformTest": true + }, + "Parameters": { + "HostedZoneId": { + "Type": "String" + }, + "MyCertificateArn": { + "Type": "String" + }, + "MyDomainName": { + "Type": "String" + }, + "VpcEndpointDomainName": { + "Type": "String" + }, + "VpcEndpointHostedZoneId": { + "Type": "String" + }, + "VpcEndpointId": { + "Type": "String" + } + }, + "Resources": { + "Api": { + "Properties": { + "Body": { + "info": { + "title": "MockApi", + "version": "1.0" + }, + "paths": { + "/get": { + "get": { + "responses": { + "200": { + "description": "200 response" + } + }, + "x-amazon-apigateway-integration": { + "requestTemplates": { + "application/json": "{\"statusCode\": 200}" + }, + "responses": { + "default": { + "responseTemplates": { + "application/json": "{\"message\": \"Hello World\"}" + }, + "statusCode": "200" + } + }, + "type": "mock" + } + } + } + }, + "swagger": "2.0", + "x-amazon-apigateway-policy": { + "Statement": [ + { + "Action": "execute-api:Invoke", + "Condition": { + "StringEquals": { + "aws:SourceVpce": { + "Ref": "VpcEndpointId" + } + } + }, + "Effect": "Allow", + "Principal": "*", + "Resource": [ + "execute-api:/*/*/*" + ] + } + ], + "Version": "2012-10-17" + } + }, + "EndpointConfiguration": { + "Types": [ + "PRIVATE" + ], + "VpcEndpointIds": [ + { + "Ref": "VpcEndpointId" + } + ] + }, + "Parameters": { + "endpointConfigurationTypes": "PRIVATE" + } + }, + "Type": "AWS::ApiGateway::RestApi" + }, + "ApiBasePathMapping": { + "Properties": { + "DomainNameArn": { + "Ref": "ApiGatewayDomainNameV2e12ae193a4" + }, + "RestApiId": { + "Ref": "Api" + }, + "Stage": { + "Ref": "ApiProdStage" + } + }, + "Type": "AWS::ApiGateway::BasePathMappingV2" + }, + "ApiDeployment201b5a52ed": { + "Properties": { + "Description": "RestApi deployment id: 201b5a52ed6f40089c7e4d46db54f63bdd4dd157", + "RestApiId": { + "Ref": "Api" + }, + "StageName": "Stage" + }, + "Type": "AWS::ApiGateway::Deployment" + }, + "ApiGatewayDomainNameV2e12ae193a4": { + "Properties": { + "CertificateArn": { + "Ref": "MyCertificateArn" + }, + "DomainName": { + "Ref": "MyDomainName" + }, + "EndpointAccessMode": "STRICT", + "EndpointConfiguration": { + "Types": [ + "PRIVATE" + ] + }, + "Policy": { + "Statement": [ + { + "Action": "execute-api:Invoke", + "Condition": { + "StringEquals": { + "aws:SourceVpce": { + "Ref": "VpcEndpointId" + } + } + }, + "Effect": "Allow", + "Principal": "*", + "Resource": "execute-api:/*/*/*" + } + ], + "Version": "2012-10-17" + }, + "SecurityPolicy": "SecurityPolicy_TLS13_1_3_2025_09" + }, + "Type": "AWS::ApiGateway::DomainNameV2" + }, + "ApiProdStage": { + "Properties": { + "DeploymentId": { + "Ref": "ApiDeployment201b5a52ed" + }, + "RestApiId": { + "Ref": "Api" + }, + "StageName": "Prod" + }, + "Type": "AWS::ApiGateway::Stage" + }, + "DomainNameAccessAssociationf4f910dee7": { + "Properties": { + "AccessAssociationSource": { + "Ref": "VpcEndpointId" + }, + "AccessAssociationSourceType": "VPCE", + "DomainNameArn": { + "Ref": "ApiGatewayDomainNameV2e12ae193a4" + } + }, + "Type": "AWS::ApiGateway::DomainNameAccessAssociation" + }, + "RecordSetGroup1194dea82a": { + "Properties": { + "HostedZoneId": { + "Ref": "HostedZoneId" + }, + "RecordSets": [ + { + "AliasTarget": { + "DNSName": { + "Ref": "VpcEndpointDomainName" + }, + "HostedZoneId": { + "Ref": "VpcEndpointHostedZoneId" + } + }, + "Name": { + "Ref": "MyDomainName" + }, + "Type": "A" + } + ] + }, + "Type": "AWS::Route53::RecordSetGroup" + } + } +} diff --git a/tests/translator/output/aws-cn/api_with_custom_domain_security_policy_regional.json b/tests/translator/output/aws-cn/api_with_custom_domain_security_policy_regional.json new file mode 100644 index 000000000..ca4762151 --- /dev/null +++ b/tests/translator/output/aws-cn/api_with_custom_domain_security_policy_regional.json @@ -0,0 +1,147 @@ +{ + "Metadata": { + "SamTransformTest": true + }, + "Parameters": { + "HostedZoneId": { + "Type": "String" + }, + "MyCertificateArn": { + "Type": "String" + }, + "MyDomainName": { + "Type": "String" + } + }, + "Resources": { + "Api": { + "Properties": { + "Body": { + "info": { + "title": "MockApi", + "version": "1.0" + }, + "paths": { + "/get": { + "get": { + "responses": { + "200": { + "description": "200 response" + } + }, + "x-amazon-apigateway-integration": { + "requestTemplates": { + "application/json": "{\"statusCode\": 200}" + }, + "responses": { + "default": { + "responseTemplates": { + "application/json": "{\"message\": \"Hello World\"}" + }, + "statusCode": "200" + } + }, + "type": "mock" + } + } + } + }, + "swagger": "2.0" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + }, + "Type": "AWS::ApiGateway::RestApi" + }, + "ApiBasePathMapping": { + "Properties": { + "DomainName": { + "Ref": "ApiGatewayDomainNamee12ae193a4" + }, + "RestApiId": { + "Ref": "Api" + }, + "Stage": { + "Ref": "ApiProdStage" + } + }, + "Type": "AWS::ApiGateway::BasePathMapping" + }, + "ApiDeployment84a6e2abbb": { + "Properties": { + "Description": "RestApi deployment id: 84a6e2abbbe8b085641de8b944a7dd72a4d01764", + "RestApiId": { + "Ref": "Api" + }, + "StageName": "Stage" + }, + "Type": "AWS::ApiGateway::Deployment" + }, + "ApiGatewayDomainNamee12ae193a4": { + "Properties": { + "DomainName": { + "Ref": "MyDomainName" + }, + "EndpointAccessMode": "STRICT", + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "RegionalCertificateArn": { + "Ref": "MyCertificateArn" + }, + "SecurityPolicy": "SecurityPolicy_TLS13_1_3_2025_09" + }, + "Type": "AWS::ApiGateway::DomainName" + }, + "ApiProdStage": { + "Properties": { + "DeploymentId": { + "Ref": "ApiDeployment84a6e2abbb" + }, + "RestApiId": { + "Ref": "Api" + }, + "StageName": "Prod" + }, + "Type": "AWS::ApiGateway::Stage" + }, + "RecordSetGroup1194dea82a": { + "Properties": { + "HostedZoneId": { + "Ref": "HostedZoneId" + }, + "RecordSets": [ + { + "AliasTarget": { + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainNamee12ae193a4", + "RegionalDomainName" + ] + }, + "HostedZoneId": { + "Fn::GetAtt": [ + "ApiGatewayDomainNamee12ae193a4", + "RegionalHostedZoneId" + ] + } + }, + "Name": { + "Ref": "MyDomainName" + }, + "Type": "A" + } + ] + }, + "Type": "AWS::Route53::RecordSetGroup" + } + } +} diff --git a/tests/translator/output/aws-cn/api_with_endpoint_access_mode.json b/tests/translator/output/aws-cn/api_with_endpoint_access_mode.json new file mode 100644 index 000000000..f0c892e72 --- /dev/null +++ b/tests/translator/output/aws-cn/api_with_endpoint_access_mode.json @@ -0,0 +1,98 @@ +{ + "Resources": { + "ApiInheritGlobals": { + "Properties": { + "Body": { + "info": { + "title": { + "Ref": "AWS::StackName" + }, + "version": "1.0" + }, + "paths": {}, + "swagger": "2.0" + }, + "EndpointAccessMode": "BASIC", + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + }, + "SecurityPolicy": "SecurityPolicy_TLS13_1_3_2025_09" + }, + "Type": "AWS::ApiGateway::RestApi" + }, + "ApiInheritGlobalsDeployment5332c373d4": { + "Properties": { + "Description": "RestApi deployment id: 5332c373d45c69e6c0f562b4a419aa8eb311adc7", + "RestApiId": { + "Ref": "ApiInheritGlobals" + }, + "StageName": "Stage" + }, + "Type": "AWS::ApiGateway::Deployment" + }, + "ApiInheritGlobalsProdStage": { + "Properties": { + "DeploymentId": { + "Ref": "ApiInheritGlobalsDeployment5332c373d4" + }, + "RestApiId": { + "Ref": "ApiInheritGlobals" + }, + "StageName": "Prod" + }, + "Type": "AWS::ApiGateway::Stage" + }, + "ApiTopLevelOverride": { + "Properties": { + "Body": { + "info": { + "title": { + "Ref": "AWS::StackName" + }, + "version": "1.0" + }, + "paths": {}, + "swagger": "2.0" + }, + "EndpointAccessMode": "STRICT", + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + }, + "SecurityPolicy": "SecurityPolicy_TLS13_1_3_2025_09" + }, + "Type": "AWS::ApiGateway::RestApi" + }, + "ApiTopLevelOverrideDeployment5332c373d4": { + "Properties": { + "Description": "RestApi deployment id: 5332c373d45c69e6c0f562b4a419aa8eb311adc7", + "RestApiId": { + "Ref": "ApiTopLevelOverride" + }, + "StageName": "Stage" + }, + "Type": "AWS::ApiGateway::Deployment" + }, + "ApiTopLevelOverrideProdStage": { + "Properties": { + "DeploymentId": { + "Ref": "ApiTopLevelOverrideDeployment5332c373d4" + }, + "RestApiId": { + "Ref": "ApiTopLevelOverride" + }, + "StageName": "Prod" + }, + "Type": "AWS::ApiGateway::Stage" + } + } +} diff --git a/tests/translator/output/aws-us-gov/api_with_custom_domain_security_policy_edge.json b/tests/translator/output/aws-us-gov/api_with_custom_domain_security_policy_edge.json new file mode 100644 index 000000000..67b32d5b9 --- /dev/null +++ b/tests/translator/output/aws-us-gov/api_with_custom_domain_security_policy_edge.json @@ -0,0 +1,142 @@ +{ + "Metadata": { + "SamTransformTest": true + }, + "Parameters": { + "HostedZoneId": { + "Type": "String" + }, + "MyCertificateArn": { + "Type": "String" + }, + "MyDomainName": { + "Type": "String" + } + }, + "Resources": { + "Api": { + "Properties": { + "Body": { + "info": { + "title": "MockApi", + "version": "1.0" + }, + "paths": { + "/get": { + "get": { + "responses": { + "200": { + "description": "200 response" + } + }, + "x-amazon-apigateway-integration": { + "requestTemplates": { + "application/json": "{\"statusCode\": 200}" + }, + "responses": { + "default": { + "responseTemplates": { + "application/json": "{\"message\": \"Hello World\"}" + }, + "statusCode": "200" + } + }, + "type": "mock" + } + } + } + }, + "swagger": "2.0" + }, + "EndpointConfiguration": { + "Types": [ + "EDGE" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "EDGE" + } + }, + "Type": "AWS::ApiGateway::RestApi" + }, + "ApiBasePathMapping": { + "Properties": { + "DomainName": { + "Ref": "ApiGatewayDomainNamee12ae193a4" + }, + "RestApiId": { + "Ref": "Api" + }, + "Stage": { + "Ref": "ApiProdStage" + } + }, + "Type": "AWS::ApiGateway::BasePathMapping" + }, + "ApiDeploymentb4212ace49": { + "Properties": { + "Description": "RestApi deployment id: b4212ace4953ed4f1debfa33eda1b29707d537e1", + "RestApiId": { + "Ref": "Api" + }, + "StageName": "Stage" + }, + "Type": "AWS::ApiGateway::Deployment" + }, + "ApiGatewayDomainNamee12ae193a4": { + "Properties": { + "CertificateArn": { + "Ref": "MyCertificateArn" + }, + "DomainName": { + "Ref": "MyDomainName" + }, + "EndpointAccessMode": "STRICT", + "EndpointConfiguration": { + "Types": [ + "EDGE" + ] + }, + "SecurityPolicy": "SecurityPolicy_TLS13_2025_EDGE" + }, + "Type": "AWS::ApiGateway::DomainName" + }, + "ApiProdStage": { + "Properties": { + "DeploymentId": { + "Ref": "ApiDeploymentb4212ace49" + }, + "RestApiId": { + "Ref": "Api" + }, + "StageName": "Prod" + }, + "Type": "AWS::ApiGateway::Stage" + }, + "RecordSetGroup1194dea82a": { + "Properties": { + "HostedZoneId": { + "Ref": "HostedZoneId" + }, + "RecordSets": [ + { + "AliasTarget": { + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainNamee12ae193a4", + "DistributionDomainName" + ] + }, + "HostedZoneId": "Z2FDTNDATAQYW2" + }, + "Name": { + "Ref": "MyDomainName" + }, + "Type": "A" + } + ] + }, + "Type": "AWS::Route53::RecordSetGroup" + } + } +} diff --git a/tests/translator/output/aws-us-gov/api_with_custom_domain_security_policy_private.json b/tests/translator/output/aws-us-gov/api_with_custom_domain_security_policy_private.json new file mode 100644 index 000000000..163ca73df --- /dev/null +++ b/tests/translator/output/aws-us-gov/api_with_custom_domain_security_policy_private.json @@ -0,0 +1,205 @@ +{ + "Metadata": { + "SamTransformTest": true + }, + "Parameters": { + "HostedZoneId": { + "Type": "String" + }, + "MyCertificateArn": { + "Type": "String" + }, + "MyDomainName": { + "Type": "String" + }, + "VpcEndpointDomainName": { + "Type": "String" + }, + "VpcEndpointHostedZoneId": { + "Type": "String" + }, + "VpcEndpointId": { + "Type": "String" + } + }, + "Resources": { + "Api": { + "Properties": { + "Body": { + "info": { + "title": "MockApi", + "version": "1.0" + }, + "paths": { + "/get": { + "get": { + "responses": { + "200": { + "description": "200 response" + } + }, + "x-amazon-apigateway-integration": { + "requestTemplates": { + "application/json": "{\"statusCode\": 200}" + }, + "responses": { + "default": { + "responseTemplates": { + "application/json": "{\"message\": \"Hello World\"}" + }, + "statusCode": "200" + } + }, + "type": "mock" + } + } + } + }, + "swagger": "2.0", + "x-amazon-apigateway-policy": { + "Statement": [ + { + "Action": "execute-api:Invoke", + "Condition": { + "StringEquals": { + "aws:SourceVpce": { + "Ref": "VpcEndpointId" + } + } + }, + "Effect": "Allow", + "Principal": "*", + "Resource": [ + "execute-api:/*/*/*" + ] + } + ], + "Version": "2012-10-17" + } + }, + "EndpointConfiguration": { + "Types": [ + "PRIVATE" + ], + "VpcEndpointIds": [ + { + "Ref": "VpcEndpointId" + } + ] + }, + "Parameters": { + "endpointConfigurationTypes": "PRIVATE" + } + }, + "Type": "AWS::ApiGateway::RestApi" + }, + "ApiBasePathMapping": { + "Properties": { + "DomainNameArn": { + "Ref": "ApiGatewayDomainNameV2e12ae193a4" + }, + "RestApiId": { + "Ref": "Api" + }, + "Stage": { + "Ref": "ApiProdStage" + } + }, + "Type": "AWS::ApiGateway::BasePathMappingV2" + }, + "ApiDeployment201b5a52ed": { + "Properties": { + "Description": "RestApi deployment id: 201b5a52ed6f40089c7e4d46db54f63bdd4dd157", + "RestApiId": { + "Ref": "Api" + }, + "StageName": "Stage" + }, + "Type": "AWS::ApiGateway::Deployment" + }, + "ApiGatewayDomainNameV2e12ae193a4": { + "Properties": { + "CertificateArn": { + "Ref": "MyCertificateArn" + }, + "DomainName": { + "Ref": "MyDomainName" + }, + "EndpointAccessMode": "STRICT", + "EndpointConfiguration": { + "Types": [ + "PRIVATE" + ] + }, + "Policy": { + "Statement": [ + { + "Action": "execute-api:Invoke", + "Condition": { + "StringEquals": { + "aws:SourceVpce": { + "Ref": "VpcEndpointId" + } + } + }, + "Effect": "Allow", + "Principal": "*", + "Resource": "execute-api:/*/*/*" + } + ], + "Version": "2012-10-17" + }, + "SecurityPolicy": "SecurityPolicy_TLS13_1_3_2025_09" + }, + "Type": "AWS::ApiGateway::DomainNameV2" + }, + "ApiProdStage": { + "Properties": { + "DeploymentId": { + "Ref": "ApiDeployment201b5a52ed" + }, + "RestApiId": { + "Ref": "Api" + }, + "StageName": "Prod" + }, + "Type": "AWS::ApiGateway::Stage" + }, + "DomainNameAccessAssociationf4f910dee7": { + "Properties": { + "AccessAssociationSource": { + "Ref": "VpcEndpointId" + }, + "AccessAssociationSourceType": "VPCE", + "DomainNameArn": { + "Ref": "ApiGatewayDomainNameV2e12ae193a4" + } + }, + "Type": "AWS::ApiGateway::DomainNameAccessAssociation" + }, + "RecordSetGroup1194dea82a": { + "Properties": { + "HostedZoneId": { + "Ref": "HostedZoneId" + }, + "RecordSets": [ + { + "AliasTarget": { + "DNSName": { + "Ref": "VpcEndpointDomainName" + }, + "HostedZoneId": { + "Ref": "VpcEndpointHostedZoneId" + } + }, + "Name": { + "Ref": "MyDomainName" + }, + "Type": "A" + } + ] + }, + "Type": "AWS::Route53::RecordSetGroup" + } + } +} diff --git a/tests/translator/output/aws-us-gov/api_with_custom_domain_security_policy_regional.json b/tests/translator/output/aws-us-gov/api_with_custom_domain_security_policy_regional.json new file mode 100644 index 000000000..ca4762151 --- /dev/null +++ b/tests/translator/output/aws-us-gov/api_with_custom_domain_security_policy_regional.json @@ -0,0 +1,147 @@ +{ + "Metadata": { + "SamTransformTest": true + }, + "Parameters": { + "HostedZoneId": { + "Type": "String" + }, + "MyCertificateArn": { + "Type": "String" + }, + "MyDomainName": { + "Type": "String" + } + }, + "Resources": { + "Api": { + "Properties": { + "Body": { + "info": { + "title": "MockApi", + "version": "1.0" + }, + "paths": { + "/get": { + "get": { + "responses": { + "200": { + "description": "200 response" + } + }, + "x-amazon-apigateway-integration": { + "requestTemplates": { + "application/json": "{\"statusCode\": 200}" + }, + "responses": { + "default": { + "responseTemplates": { + "application/json": "{\"message\": \"Hello World\"}" + }, + "statusCode": "200" + } + }, + "type": "mock" + } + } + } + }, + "swagger": "2.0" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + }, + "Type": "AWS::ApiGateway::RestApi" + }, + "ApiBasePathMapping": { + "Properties": { + "DomainName": { + "Ref": "ApiGatewayDomainNamee12ae193a4" + }, + "RestApiId": { + "Ref": "Api" + }, + "Stage": { + "Ref": "ApiProdStage" + } + }, + "Type": "AWS::ApiGateway::BasePathMapping" + }, + "ApiDeployment84a6e2abbb": { + "Properties": { + "Description": "RestApi deployment id: 84a6e2abbbe8b085641de8b944a7dd72a4d01764", + "RestApiId": { + "Ref": "Api" + }, + "StageName": "Stage" + }, + "Type": "AWS::ApiGateway::Deployment" + }, + "ApiGatewayDomainNamee12ae193a4": { + "Properties": { + "DomainName": { + "Ref": "MyDomainName" + }, + "EndpointAccessMode": "STRICT", + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "RegionalCertificateArn": { + "Ref": "MyCertificateArn" + }, + "SecurityPolicy": "SecurityPolicy_TLS13_1_3_2025_09" + }, + "Type": "AWS::ApiGateway::DomainName" + }, + "ApiProdStage": { + "Properties": { + "DeploymentId": { + "Ref": "ApiDeployment84a6e2abbb" + }, + "RestApiId": { + "Ref": "Api" + }, + "StageName": "Prod" + }, + "Type": "AWS::ApiGateway::Stage" + }, + "RecordSetGroup1194dea82a": { + "Properties": { + "HostedZoneId": { + "Ref": "HostedZoneId" + }, + "RecordSets": [ + { + "AliasTarget": { + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainNamee12ae193a4", + "RegionalDomainName" + ] + }, + "HostedZoneId": { + "Fn::GetAtt": [ + "ApiGatewayDomainNamee12ae193a4", + "RegionalHostedZoneId" + ] + } + }, + "Name": { + "Ref": "MyDomainName" + }, + "Type": "A" + } + ] + }, + "Type": "AWS::Route53::RecordSetGroup" + } + } +} diff --git a/tests/translator/output/aws-us-gov/api_with_endpoint_access_mode.json b/tests/translator/output/aws-us-gov/api_with_endpoint_access_mode.json new file mode 100644 index 000000000..f0c892e72 --- /dev/null +++ b/tests/translator/output/aws-us-gov/api_with_endpoint_access_mode.json @@ -0,0 +1,98 @@ +{ + "Resources": { + "ApiInheritGlobals": { + "Properties": { + "Body": { + "info": { + "title": { + "Ref": "AWS::StackName" + }, + "version": "1.0" + }, + "paths": {}, + "swagger": "2.0" + }, + "EndpointAccessMode": "BASIC", + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + }, + "SecurityPolicy": "SecurityPolicy_TLS13_1_3_2025_09" + }, + "Type": "AWS::ApiGateway::RestApi" + }, + "ApiInheritGlobalsDeployment5332c373d4": { + "Properties": { + "Description": "RestApi deployment id: 5332c373d45c69e6c0f562b4a419aa8eb311adc7", + "RestApiId": { + "Ref": "ApiInheritGlobals" + }, + "StageName": "Stage" + }, + "Type": "AWS::ApiGateway::Deployment" + }, + "ApiInheritGlobalsProdStage": { + "Properties": { + "DeploymentId": { + "Ref": "ApiInheritGlobalsDeployment5332c373d4" + }, + "RestApiId": { + "Ref": "ApiInheritGlobals" + }, + "StageName": "Prod" + }, + "Type": "AWS::ApiGateway::Stage" + }, + "ApiTopLevelOverride": { + "Properties": { + "Body": { + "info": { + "title": { + "Ref": "AWS::StackName" + }, + "version": "1.0" + }, + "paths": {}, + "swagger": "2.0" + }, + "EndpointAccessMode": "STRICT", + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + }, + "SecurityPolicy": "SecurityPolicy_TLS13_1_3_2025_09" + }, + "Type": "AWS::ApiGateway::RestApi" + }, + "ApiTopLevelOverrideDeployment5332c373d4": { + "Properties": { + "Description": "RestApi deployment id: 5332c373d45c69e6c0f562b4a419aa8eb311adc7", + "RestApiId": { + "Ref": "ApiTopLevelOverride" + }, + "StageName": "Stage" + }, + "Type": "AWS::ApiGateway::Deployment" + }, + "ApiTopLevelOverrideProdStage": { + "Properties": { + "DeploymentId": { + "Ref": "ApiTopLevelOverrideDeployment5332c373d4" + }, + "RestApiId": { + "Ref": "ApiTopLevelOverride" + }, + "StageName": "Prod" + }, + "Type": "AWS::ApiGateway::Stage" + } + } +} diff --git a/tests/translator/output/error_globals_api_with_stage_name.json b/tests/translator/output/error_globals_api_with_stage_name.json index 570d065c7..775a5a483 100644 --- a/tests/translator/output/error_globals_api_with_stage_name.json +++ b/tests/translator/output/error_globals_api_with_stage_name.json @@ -4,7 +4,7 @@ "Number of errors found: 1. ", "'Globals' section is invalid. ", "'StageName' is not a supported property of 'Api'. ", - "Must be one of the following values - ['Auth', 'Name', 'DefinitionUri', 'CacheClusterEnabled', 'CacheClusterSize', 'MergeDefinitions', 'Variables', 'EndpointConfiguration', 'MethodSettings', 'BinaryMediaTypes', 'MinimumCompressionSize', 'Cors', 'GatewayResponses', 'AccessLogSetting', 'CanarySetting', 'TracingEnabled', 'OpenApiVersion', 'Domain', 'AlwaysDeploy', 'PropagateTags', 'SecurityPolicy']" + "Must be one of the following values - ['Auth', 'Name', 'DefinitionUri', 'CacheClusterEnabled', 'CacheClusterSize', 'MergeDefinitions', 'Variables', 'EndpointConfiguration', 'MethodSettings', 'BinaryMediaTypes', 'MinimumCompressionSize', 'Cors', 'GatewayResponses', 'AccessLogSetting', 'CanarySetting', 'TracingEnabled', 'OpenApiVersion', 'Domain', 'AlwaysDeploy', 'PropagateTags', 'SecurityPolicy', 'EndpointAccessMode']" ], - "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. 'Globals' section is invalid. 'StageName' is not a supported property of 'Api'. Must be one of the following values - ['Auth', 'Name', 'DefinitionUri', 'CacheClusterEnabled', 'CacheClusterSize', 'MergeDefinitions', 'Variables', 'EndpointConfiguration', 'MethodSettings', 'BinaryMediaTypes', 'MinimumCompressionSize', 'Cors', 'GatewayResponses', 'AccessLogSetting', 'CanarySetting', 'TracingEnabled', 'OpenApiVersion', 'Domain', 'AlwaysDeploy', 'PropagateTags', 'SecurityPolicy']" + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. 'Globals' section is invalid. 'StageName' is not a supported property of 'Api'. Must be one of the following values - ['Auth', 'Name', 'DefinitionUri', 'CacheClusterEnabled', 'CacheClusterSize', 'MergeDefinitions', 'Variables', 'EndpointConfiguration', 'MethodSettings', 'BinaryMediaTypes', 'MinimumCompressionSize', 'Cors', 'GatewayResponses', 'AccessLogSetting', 'CanarySetting', 'TracingEnabled', 'OpenApiVersion', 'Domain', 'AlwaysDeploy', 'PropagateTags', 'SecurityPolicy', 'EndpointAccessMode']" }