Skip to content

Commit ff5fa33

Browse files
authored
feat: Support EndpointAccessMode property for AWS::Serverless::Api (#3898)
1 parent 0b243af commit ff5fa33

File tree

35 files changed

+2317
-3
lines changed

35 files changed

+2317
-3
lines changed

docs/globals.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ Currently, the following resources and properties are being supported:
9999
OpenApiVersion:
100100
Domain:
101101
SecurityPolicy:
102+
EndpointAccessMode:
102103
103104
HttpApi:
104105
# Properties of AWS::Serverless::HttpApi
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
[
2+
{
3+
"LogicalResourceId": "MyApi",
4+
"ResourceType": "AWS::ApiGateway::RestApi"
5+
},
6+
{
7+
"LogicalResourceId": "MyApiDeployment",
8+
"ResourceType": "AWS::ApiGateway::Deployment"
9+
},
10+
{
11+
"LogicalResourceId": "MyApiProdStage",
12+
"ResourceType": "AWS::ApiGateway::Stage"
13+
},
14+
{
15+
"LogicalResourceId": "ApiGatewayDomainName",
16+
"ResourceType": "AWS::ApiGateway::DomainName"
17+
},
18+
{
19+
"LogicalResourceId": "MyApiBasePathMapping",
20+
"ResourceType": "AWS::ApiGateway::BasePathMapping"
21+
},
22+
{
23+
"LogicalResourceId": "RecordSetGroup",
24+
"ResourceType": "AWS::Route53::RecordSetGroup"
25+
}
26+
]
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
[
2+
{
3+
"LogicalResourceId": "MyApi",
4+
"ResourceType": "AWS::ApiGateway::RestApi"
5+
},
6+
{
7+
"LogicalResourceId": "MyApiDeployment",
8+
"ResourceType": "AWS::ApiGateway::Deployment"
9+
},
10+
{
11+
"LogicalResourceId": "MyApiProdStage",
12+
"ResourceType": "AWS::ApiGateway::Stage"
13+
},
14+
{
15+
"LogicalResourceId": "ApiGatewayDomainName",
16+
"ResourceType": "AWS::ApiGateway::DomainName"
17+
},
18+
{
19+
"LogicalResourceId": "MyApiBasePathMapping",
20+
"ResourceType": "AWS::ApiGateway::BasePathMapping"
21+
},
22+
{
23+
"LogicalResourceId": "RecordSetGroup",
24+
"ResourceType": "AWS::Route53::RecordSetGroup"
25+
}
26+
]
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[
2+
{
3+
"LogicalResourceId": "MyApi",
4+
"ResourceType": "AWS::ApiGateway::RestApi"
5+
},
6+
{
7+
"LogicalResourceId": "MyApiDeployment",
8+
"ResourceType": "AWS::ApiGateway::Deployment"
9+
},
10+
{
11+
"LogicalResourceId": "MyApiProdStage",
12+
"ResourceType": "AWS::ApiGateway::Stage"
13+
}
14+
]
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
Parameters:
2+
DomainName:
3+
Type: String
4+
CertificateArn:
5+
Type: String
6+
HostedZoneId:
7+
Type: String
8+
9+
Resources:
10+
MyApi:
11+
Type: AWS::Serverless::Api
12+
Properties:
13+
StageName: Prod
14+
DefinitionUri: ${definitionuri}
15+
EndpointConfiguration:
16+
Type: EDGE
17+
Domain:
18+
DomainName: !Ref DomainName
19+
CertificateArn: !Ref CertificateArn
20+
EndpointConfiguration: EDGE
21+
SecurityPolicy: SecurityPolicy_TLS13_2025_EDGE
22+
EndpointAccessMode: STRICT
23+
Route53:
24+
HostedZoneId: !Ref HostedZoneId
25+
26+
Metadata:
27+
SamTransformTest: true
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
Parameters:
2+
DomainName:
3+
Type: String
4+
CertificateArn:
5+
Type: String
6+
HostedZoneId:
7+
Type: String
8+
9+
Resources:
10+
MyApi:
11+
Type: AWS::Serverless::Api
12+
Properties:
13+
StageName: Prod
14+
DefinitionUri: ${definitionuri}
15+
EndpointConfiguration:
16+
Type: REGIONAL
17+
Domain:
18+
DomainName: !Ref DomainName
19+
CertificateArn: !Ref CertificateArn
20+
EndpointConfiguration: REGIONAL
21+
SecurityPolicy: SecurityPolicy_TLS13_1_3_2025_09
22+
EndpointAccessMode: STRICT
23+
Route53:
24+
HostedZoneId: !Ref HostedZoneId
25+
26+
Metadata:
27+
SamTransformTest: true
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
Parameters:
2+
SecurityPolicyValue:
3+
Type: String
4+
Default: SecurityPolicy_TLS13_1_3_2025_09
5+
EndpointAccessModeValue:
6+
Type: String
7+
Default: STRICT
8+
9+
Resources:
10+
MyApi:
11+
Type: AWS::Serverless::Api
12+
Properties:
13+
StageName: Prod
14+
DefinitionUri: ${definitionuri}
15+
SecurityPolicy: !Ref SecurityPolicyValue
16+
EndpointAccessMode: !Ref EndpointAccessModeValue
17+
EndpointConfiguration:
18+
Type: REGIONAL
19+
Metadata:
20+
SamTransformTest: true
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
from unittest.case import skipIf
2+
3+
from integration.config.service_names import CUSTOM_DOMAIN
4+
from integration.helpers.base_internal_test import BaseInternalTest
5+
from integration.helpers.base_test import nonblocking
6+
from integration.helpers.resource import current_region_not_included
7+
8+
9+
@skipIf(
10+
current_region_not_included([CUSTOM_DOMAIN]),
11+
"Custom domain is not supported in this testing region",
12+
)
13+
@nonblocking
14+
class TestApiWithCustomDomainSecurityPolicy(BaseInternalTest):
15+
"""
16+
Test AWS::Serverless::Api with SecurityPolicy and EndpointAccessMode in Domain configuration
17+
"""
18+
19+
def test_api_with_custom_domain_security_policy_regional(self):
20+
self.create_and_verify_stack("single/api_with_custom_domain_security_policy_regional")
21+
22+
domain_name_id = self.get_physical_id_by_type("AWS::ApiGateway::DomainName")
23+
result = self.client_provider.api_client.get_domain_name(domainName=domain_name_id)
24+
25+
end_point_config = result["endpointConfiguration"]
26+
self.assertEqual(["REGIONAL"], end_point_config["types"])
27+
self.assertEqual("SecurityPolicy_TLS13_1_3_2025_09", result["securityPolicy"])
28+
self.assertEqual("STRICT", result["endpointAccessMode"])
29+
30+
def test_api_with_custom_domain_security_policy_edge(self):
31+
self.create_and_verify_stack("single/api_with_custom_domain_security_policy_edge")
32+
33+
domain_name_id = self.get_physical_id_by_type("AWS::ApiGateway::DomainName")
34+
result = self.client_provider.api_client.get_domain_name(domainName=domain_name_id)
35+
36+
end_point_config = result["endpointConfiguration"]
37+
self.assertEqual(["EDGE"], end_point_config["types"])
38+
self.assertEqual("SecurityPolicy_TLS13_2025_EDGE", result["securityPolicy"])
39+
self.assertEqual("STRICT", result["endpointAccessMode"])
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
from unittest.case import skipIf
2+
3+
from integration.config.service_names import REST_API
4+
from integration.helpers.base_test import BaseTest
5+
from integration.helpers.resource import current_region_does_not_support
6+
7+
8+
@skipIf(current_region_does_not_support([REST_API]), "Rest API is not supported in this testing region")
9+
class TestApiWithEndpointAccessMode(BaseTest):
10+
"""
11+
Tests for AWS::Serverless::Api with EndpointAccessMode property
12+
"""
13+
14+
def test_api_with_endpoint_access_mode(self):
15+
# Create stack with STRICT
16+
parameters = [
17+
{"ParameterKey": "SecurityPolicyValue", "ParameterValue": "SecurityPolicy_TLS13_1_3_2025_09"},
18+
{"ParameterKey": "EndpointAccessModeValue", "ParameterValue": "STRICT"},
19+
]
20+
self.create_and_verify_stack("single/api_with_endpoint_access_mode", parameters)
21+
22+
rest_api_id = self.get_physical_id_by_type("AWS::ApiGateway::RestApi")
23+
rest_api = self.client_provider.api_client.get_rest_api(restApiId=rest_api_id)
24+
25+
self.assertEqual(rest_api["securityPolicy"], "SecurityPolicy_TLS13_1_3_2025_09")
26+
self.assertEqual(rest_api["endpointAccessMode"], "STRICT")
27+
28+
# Update stack with BASIC
29+
update_parameters = [
30+
{"ParameterKey": "SecurityPolicyValue", "ParameterValue": "SecurityPolicy_TLS13_1_3_2025_09"},
31+
{"ParameterKey": "EndpointAccessModeValue", "ParameterValue": "BASIC"},
32+
]
33+
self.update_stack(parameters=update_parameters)
34+
35+
rest_api = self.client_provider.api_client.get_rest_api(restApiId=rest_api_id)
36+
37+
self.assertEqual(rest_api["securityPolicy"], "SecurityPolicy_TLS13_1_3_2025_09")
38+
self.assertEqual(rest_api["endpointAccessMode"], "BASIC")

samtranslator/internal/schema_source/aws_serverless_api.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,11 @@ class Domain(BaseModel):
196196
EndpointConfiguration: Optional[SamIntrinsicable[Literal["REGIONAL", "EDGE", "PRIVATE"]]] = domain(
197197
"EndpointConfiguration"
198198
)
199+
EndpointAccessMode: Optional[PassThroughProp] = passthrough_prop(
200+
DOMAIN_STEM,
201+
"EndpointAccessMode",
202+
["AWS::ApiGateway::DomainName", "Properties", "EndpointAccessMode"],
203+
)
199204
IpAddressType: Optional[PassThroughProp] # TODO: add documentation; currently unavailable
200205
MutualTlsAuthentication: Optional[PassThroughProp] = passthrough_prop(
201206
DOMAIN_STEM,
@@ -306,6 +311,11 @@ class Properties(BaseModel):
306311
)
307312
DisableExecuteApiEndpoint: Optional[PassThroughProp] = properties("DisableExecuteApiEndpoint")
308313
Domain: Optional[Domain] = properties("Domain")
314+
EndpointAccessMode: Optional[PassThroughProp] = passthrough_prop(
315+
PROPERTIES_STEM,
316+
"EndpointAccessMode",
317+
["AWS::ApiGateway::RestApi", "Properties", "EndpointAccessMode"],
318+
)
309319
EndpointConfiguration: Optional[EndpointConfigurationType] = properties("EndpointConfiguration")
310320
FailOnWarnings: Optional[PassThroughProp] = passthrough_prop(
311321
PROPERTIES_STEM,
@@ -419,6 +429,11 @@ class Globals(BaseModel):
419429
"SecurityPolicy",
420430
["AWS::ApiGateway::RestApi", "Properties", "SecurityPolicy"],
421431
)
432+
EndpointAccessMode: Optional[PassThroughProp] = passthrough_prop(
433+
PROPERTIES_STEM,
434+
"EndpointAccessMode",
435+
["AWS::ApiGateway::RestApi", "Properties", "EndpointAccessMode"],
436+
)
422437

423438

424439
class Resource(ResourceAttributes):

0 commit comments

Comments
 (0)