Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .cfnlintrc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@ ignore_templates:
- tests/translator/output/**/function_with_metrics_config.json
- tests/translator/output/**/function_with_self_managed_kafka_and_schema_registry.json # cfnlint is not updated to recognize the SchemaRegistryConfig property
- tests/translator/output/**/function_with_msk_with_schema_registry_config.json # cfnlint is not updated to recognize the SchemaRegistryConfig property
- tests/translator/output/**/function_with_tenancy_config.json # cfnlint is not updated to recognize the TenancyConfig property
- tests/translator/output/**/function_with_tenancy_and_api_event.json # cfnlint is not updated to recognize the TenancyConfig property
- tests/translator/output/**/function_with_tenancy_and_httpapi_event.json # cfnlint is not updated to recognize the TenancyConfig property
- tests/translator/output/**/function_with_tenancy_config_global.json # cfnlint is not updated to recognize the TenancyConfig property

ignore_checks:
- E2531 # Deprecated runtime; not relevant for transform tests
Expand Down
1 change: 1 addition & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

### Checklist

- [ ] Review the [generative AI contribution guidelines](https://github.com/aws/serverless-application-model/blob/develop/CONTRIBUTING.md#ai-usage)
- [ ] Adheres to the [development guidelines](https://github.com/aws/serverless-application-model/blob/develop/DEVELOPMENT_GUIDE.md#development-guidelines)
- [ ] Add/update [transform tests](https://github.com/aws/serverless-application-model/blob/develop/DEVELOPMENT_GUIDE.md#unit-testing-with-multiple-python-versions)
- [ ] Using correct values
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
- "3.11"
steps:
- uses: actions/checkout@v5
- uses: actions/setup-python@v5
- uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python }}
- run: make init
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/check_compatibility.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
- name: Checkout the PR
uses: actions/checkout@v5

- uses: actions/setup-python@v5
- uses: actions/setup-python@v6
with:
python-version: "3.10"

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:

steps:
- uses: actions/checkout@v5
- uses: actions/setup-python@v5
- uses: actions/setup-python@v6
with:
python-version: "3.10"

Expand Down
11 changes: 11 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,17 @@ transparent and open process for evolving AWS SAM.

Please read through this document before submitting any issues or pull requests to ensure we have all the necessary information to effectively respond to your bug report or contribution.

## AI Usage

While using generative AI is allowed when contributing to this project, please keep the following points in mind:

* Review all code yourself before you submit it.
* Understand all the code you have submitted in order to answer any questions the maintainers could have when reviewing your PR.
* Avoid being overly verbose in code and testing - extra code can be hard to review.
* For example, avoid writing unit tests that duplicate existing ones, or test libraries that you're using.
* Keep PR descriptions, comments, and follow ups concise.
* Ensure AI-generated code meets the same quality standards as human-written code.

## Integrating AWS SAM into your tool

We encourage you to modify SAM to integrate it with other frameworks and deployment providers from the community for building serverless applications. If you're building a new tool that will use AWS SAM, let us know how we can help!
Expand Down
20 changes: 18 additions & 2 deletions bin/run_cfn_lint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,22 @@ if [ ! -d "${VENV}" ]; then
fi

"${VENV}/bin/python" -m pip install cfn-lint --upgrade --quiet
# update cfn schema
"${VENV}/bin/cfn-lint" -u
# update cfn schema with retry logic (can fail due to network issues)
MAX_RETRIES=3
RETRY_COUNT=0
while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
if "${VENV}/bin/cfn-lint" -u; then
echo "Successfully updated cfn-lint schema"
break
else
RETRY_COUNT=$((RETRY_COUNT + 1))
if [ $RETRY_COUNT -lt $MAX_RETRIES ]; then
echo "cfn-lint schema update failed, retrying... (attempt $RETRY_COUNT of $MAX_RETRIES)"
sleep 2
else
echo "cfn-lint schema update failed after $MAX_RETRIES attempts"
exit 1
fi
fi
done
"${VENV}/bin/cfn-lint" --format parseable
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[
{
"LogicalResourceId": "MyLambdaFunction",
"ResourceType": "AWS::Lambda::Function"
},
{
"LogicalResourceId": "MyLambdaFunctionRole",
"ResourceType": "AWS::IAM::Role"
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,14 @@ Resources:
Type: AWS::Serverless::Function
Properties:
InlineCode: |
exports.handler = async (event, context, callback) => {
exports.handler = async (event, context) => {
return {
statusCode: 200,
body: 'Success'
}
}
Handler: index.handler
Runtime: nodejs16.x
Runtime: nodejs22.x
Events:
LambdaRequest:
Type: Api
Expand Down Expand Up @@ -94,9 +94,9 @@ Resources:
Type: AWS::Serverless::Function
Properties:
Handler: index.handler
Runtime: nodejs16.x
Runtime: nodejs22.x
InlineCode: |
exports.handler = async (event, context, callback) => {
exports.handler = async (event, context) => {
const auth = event.queryStringParameters.authorization
const policyDocument = {
Version: '2012-10-17',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ Resources:
API_KEY: !GetAtt ApiKey.ApiKey
GRAPHQL_URL: !GetAtt AppSyncApi.GraphQLUrl
EventBusName: !Ref EventBus
Runtime: nodejs16.x
Runtime: nodejs22.x
Handler: index.handler
InlineCode: |
const https = require("https");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ Resources:
MyMskEvent:
Type: MSK
Properties:
Enabled: false
StartingPosition: LATEST
Stream:
Ref: MyMskCluster
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ Resources:
MyMskEvent:
Type: MSK
Properties:
Enabled: false
StartingPosition: LATEST
Stream:
Ref: MyMskCluster
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ Resources:
logs:CreateLogStream, logs:PutLogEvents, s3:ListBucket]
Effect: Allow
Resource: '*'
- Action: [s3:PutObject, s3:ListBucket]
Effect: Allow
Resource:
- arn:aws:s3:::*/*
- arn:aws:s3:::*
ManagedPolicyArns:
- !Sub arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Tags:
Expand Down Expand Up @@ -60,6 +65,7 @@ Resources:
MyMskEvent:
Type: MSK
Properties:
Enabled: false
StartingPosition: LATEST
Stream:
Ref: MyMskCluster
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Resources:
MyMskEvent:
Type: MSK
Properties:
Enabled: false
StartingPosition: LATEST
Stream:
Ref: MyMskCluster
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Resources:
Fn::Sub: ['${filename}.handler', filename: index]

Runtime:
Fn::Join: ['', [nodejs, 16.x]]
Fn::Join: ['', [nodejs, 22.x]]

Role:
Fn::GetAtt: [MyNewRole, Arn]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Resources:
MyLambdaFunction:
Type: AWS::Serverless::Function
Properties:
Handler: index.handler
Runtime: nodejs18.x
CodeUri: ${codeuri}
MemorySize: 128
TenancyConfig:
TenantIsolationMode: PER_TENANT
Policies:
- AWSLambdaRole
- AmazonS3ReadOnlyAccess
Metadata:
SamTransformTest: true
21 changes: 21 additions & 0 deletions integration/single/test_basic_function.py
Original file line number Diff line number Diff line change
Expand Up @@ -392,3 +392,24 @@ def _assert_invoke(self, lambda_client, function_name, qualifier=None, expected_
def _verify_get_request(self, url, expected_text):
response = self.verify_get_request_response(url, 200)
self.assertEqual(response.text, expected_text)

# @skipIf(current_region_does_not_support([MULTI_TENANCY]), "Multi-tenancy is not supported in this testing region")
@skipIf(True, "Multi-tenancy feature is not available to test yet")
def test_basic_function_with_tenancy_config(self):
"""
Creates a basic lambda function with TenancyConfig
"""
self.create_and_verify_stack("single/basic_function_with_tenancy_config")

lambda_client = self.client_provider.lambda_client
function_name = self.get_physical_id_by_type("AWS::Lambda::Function")

# Get function configuration
function_config = lambda_client.get_function_configuration(FunctionName=function_name)

# Verify TenancyConfig is set correctly
self.assertEqual(
function_config["TenancyConfig"]["TenantIsolationMode"],
"PER_TENANT",
"Expected TenantIsolationMode to be PER_TENANT",
)
2 changes: 2 additions & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,5 @@ filterwarnings =
ignore::DeprecationWarning:urllib3.*:
# https://github.com/boto/boto3/issues/3889
ignore:datetime.datetime.utcnow
# Boto3 will stop supporting Python3.9 starting April 29, 2026
ignore:Boto3 will no longer support Python 3.9:boto3.exceptions.PythonDeprecationWarning
2 changes: 1 addition & 1 deletion samtranslator/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "1.101.0"
__version__ = "1.102.0"
8 changes: 4 additions & 4 deletions samtranslator/internal/data/aws_managed_policies.json
Original file line number Diff line number Diff line change
Expand Up @@ -756,7 +756,7 @@
"AmazonGrafanaCloudWatchAccess": "arn:aws:iam::aws:policy/service-role/AmazonGrafanaCloudWatchAccess",
"AmazonGrafanaRedshiftAccess": "arn:aws:iam::aws:policy/service-role/AmazonGrafanaRedshiftAccess",
"AmazonGrafanaServiceLinkedRolePolicy": "arn:aws:iam::aws:policy/aws-service-role/AmazonGrafanaServiceLinkedRolePolicy",
"AmazonGuardDutyFullAccess": "arn:aws:iam::aws:policy/AmazonGuardDutyFullAccess",
"AmazonGuardDutyFullAccess_v2": "arn:aws:iam::aws:policy/AmazonGuardDutyFullAccess_v2",
"AmazonGuardDutyMalwareProtectionServiceRolePolicy": "arn:aws:iam::aws:policy/aws-service-role/AmazonGuardDutyMalwareProtectionServiceRolePolicy",
"AmazonGuardDutyReadOnlyAccess": "arn:aws:iam::aws:policy/AmazonGuardDutyReadOnlyAccess",
"AmazonGuardDutyServiceRolePolicy": "arn:aws:iam::aws:policy/aws-service-role/AmazonGuardDutyServiceRolePolicy",
Expand Down Expand Up @@ -1577,7 +1577,7 @@
"AmazonFreeRTOSOTAUpdate": "arn:aws-cn:iam::aws:policy/service-role/AmazonFreeRTOSOTAUpdate",
"AmazonGlacierFullAccess": "arn:aws-cn:iam::aws:policy/AmazonGlacierFullAccess",
"AmazonGlacierReadOnlyAccess": "arn:aws-cn:iam::aws:policy/AmazonGlacierReadOnlyAccess",
"AmazonGuardDutyFullAccess": "arn:aws-cn:iam::aws:policy/AmazonGuardDutyFullAccess",
"AmazonGuardDutyFullAccess_v2": "arn:aws-cn:iam::aws:policy/AmazonGuardDutyFullAccess_v2",
"AmazonGuardDutyMalwareProtectionServiceRolePolicy": "arn:aws-cn:iam::aws:policy/aws-service-role/AmazonGuardDutyMalwareProtectionServiceRolePolicy",
"AmazonGuardDutyReadOnlyAccess": "arn:aws-cn:iam::aws:policy/AmazonGuardDutyReadOnlyAccess",
"AmazonGuardDutyServiceRolePolicy": "arn:aws-cn:iam::aws:policy/aws-service-role/AmazonGuardDutyServiceRolePolicy",
Expand Down Expand Up @@ -2200,7 +2200,7 @@
"AmazonFSxServiceRolePolicy": "arn:aws-us-gov:iam::aws:policy/aws-service-role/AmazonFSxServiceRolePolicy",
"AmazonGlacierFullAccess": "arn:aws-us-gov:iam::aws:policy/AmazonGlacierFullAccess",
"AmazonGlacierReadOnlyAccess": "arn:aws-us-gov:iam::aws:policy/AmazonGlacierReadOnlyAccess",
"AmazonGuardDutyFullAccess": "arn:aws-us-gov:iam::aws:policy/AmazonGuardDutyFullAccess",
"AmazonGuardDutyFullAccess_v2": "arn:aws-us-gov:iam::aws:policy/AmazonGuardDutyFullAccess_v2",
"AmazonGuardDutyReadOnlyAccess": "arn:aws-us-gov:iam::aws:policy/AmazonGuardDutyReadOnlyAccess",
"AmazonGuardDutyServiceRolePolicy": "arn:aws-us-gov:iam::aws:policy/aws-service-role/AmazonGuardDutyServiceRolePolicy",
"AmazonInspector2FullAccess": "arn:aws-us-gov:iam::aws:policy/AmazonInspector2FullAccess",
Expand Down Expand Up @@ -2608,7 +2608,7 @@
"AmazonGrafanaCloudWatchAccess": "arn:aws-eusc:iam::aws:policy/service-role/AmazonGrafanaCloudWatchAccess",
"AmazonGrafanaRedshiftAccess": "arn:aws-eusc:iam::aws:policy/service-role/AmazonGrafanaRedshiftAccess",
"AmazonGrafanaServiceLinkedRolePolicy": "arn:aws-eusc:iam::aws:policy/aws-service-role/AmazonGrafanaServiceLinkedRolePolicy",
"AmazonGuardDutyFullAccess": "arn:aws-eusc:iam::aws:policy/AmazonGuardDutyFullAccess",
"AmazonGuardDutyFullAccess_v2": "arn:aws-eusc:iam::aws:policy/AmazonGuardDutyFullAccess_v2",
"AmazonGuardDutyMalwareProtectionServiceRolePolicy": "arn:aws-eusc:iam::aws:policy/aws-service-role/AmazonGuardDutyMalwareProtectionServiceRolePolicy",
"AmazonGuardDutyReadOnlyAccess": "arn:aws-eusc:iam::aws:policy/AmazonGuardDutyReadOnlyAccess",
"AmazonGuardDutyServiceRolePolicy": "arn:aws-eusc:iam::aws:policy/aws-service-role/AmazonGuardDutyServiceRolePolicy",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,7 @@ class HttpApiEvent(BaseModel):

class MSKEventProperties(BaseModel):
ConsumerGroupId: Optional[PassThroughProp] = mskeventproperties("ConsumerGroupId")
Enabled: Optional[PassThroughProp] # TODO: it doesn't show up in docs yet
FilterCriteria: Optional[PassThroughProp] = mskeventproperties("FilterCriteria")
KmsKeyArn: Optional[PassThroughProp] # TODO: add documentation
MaximumBatchingWindowInSeconds: Optional[PassThroughProp] = mskeventproperties("MaximumBatchingWindowInSeconds")
Expand Down Expand Up @@ -521,6 +522,7 @@ class ScheduleV2Event(BaseModel):
LoggingConfig = Optional[PassThroughProp] # TODO: add documentation
RecursiveLoop = Optional[PassThroughProp]
SourceKMSKeyArn = Optional[PassThroughProp]
TenancyConfig = Optional[PassThroughProp]


class Properties(BaseModel):
Expand Down Expand Up @@ -649,6 +651,7 @@ class Properties(BaseModel):
LoggingConfig: Optional[PassThroughProp] # TODO: add documentation
RecursiveLoop: Optional[PassThroughProp] # TODO: add documentation
SourceKMSKeyArn: Optional[PassThroughProp] # TODO: add documentation
TenancyConfig: Optional[PassThroughProp] # TODO: add documentation


class Globals(BaseModel):
Expand Down Expand Up @@ -709,6 +712,7 @@ class Globals(BaseModel):
LoggingConfig: Optional[PassThroughProp] # TODO: add documentation
RecursiveLoop: Optional[PassThroughProp] # TODO: add documentation
SourceKMSKeyArn: Optional[PassThroughProp] # TODO: add documentation
TenancyConfig: Optional[PassThroughProp] # TODO: add documentation


class Resource(ResourceAttributes):
Expand Down
35 changes: 31 additions & 4 deletions samtranslator/intrinsics/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,31 @@
from samtranslator.model.exceptions import InvalidDocumentException, InvalidTemplateException


def _get_parameter_value(parameters: Dict[str, Any], param_name: str, default: Any = None) -> Any:
"""
Get parameter value from parameters dict, but return default (None) if
- it's a CloudFormation internal placeholder.
- param_name is not in the parameters.

CloudFormation internal placeholders are passed during changeset creation with --include-nested-stacks
when there are cross-references between nested stacks that don't exist yet.
These placeholders should not be resolved by SAM.

:param parameters: Dictionary of parameter values
:param param_name: Name of the parameter to retrieve
:param default: Default value to return if parameter not found or is a placeholder
:return: Parameter value, or default if not found or is a CloudFormation placeholder
"""
value = parameters.get(param_name, default)

# Check if the value is a CloudFormation internal placeholder
# E.g. {{IntrinsicFunction:api-xx/MyStack.Outputs.API/Fn::GetAtt}}
if isinstance(value, str) and value.startswith("{{IntrinsicFunction:"):
return default

return value


class Action(ABC):
"""
Base class for intrinsic function actions. Each intrinsic function must subclass this,
Expand Down Expand Up @@ -103,9 +128,9 @@ def resolve_parameter_refs(self, input_dict: Optional[Any], parameters: Dict[str
if not isinstance(param_name, str):
return input_dict

if param_name in parameters:
return parameters[param_name]
return input_dict
# Use the wrapper function to get parameter value
# It returns the original input unchanged if the parameter is a CloudFormation internal placeholder
return _get_parameter_value(parameters, param_name, input_dict)

def resolve_resource_refs(
self, input_dict: Optional[Any], supported_resource_refs: Dict[str, Any]
Expand Down Expand Up @@ -193,7 +218,9 @@ def do_replacement(full_ref: str, prop_name: str) -> Any:
:param prop_name: => logicalId.property
:return: Either the value it resolves to. If not the original reference
"""
return parameters.get(prop_name, full_ref)
# Use the wrapper function to get parameter value
# It returns the original input unchanged if the parameter is a CloudFormation internal placeholder
return _get_parameter_value(parameters, prop_name, full_ref)

return self._handle_sub_action(input_dict, do_replacement)

Expand Down
2 changes: 2 additions & 0 deletions samtranslator/model/lambda_.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class LambdaFunction(Resource):
"RuntimeManagementConfig": GeneratedProperty(),
"LoggingConfig": GeneratedProperty(),
"RecursiveLoop": GeneratedProperty(),
"TenancyConfig": GeneratedProperty(),
}

Code: Dict[str, Any]
Expand Down Expand Up @@ -64,6 +65,7 @@ class LambdaFunction(Resource):
RuntimeManagementConfig: Optional[Dict[str, Any]]
LoggingConfig: Optional[Dict[str, Any]]
RecursiveLoop: Optional[str]
TenancyConfig: Optional[Dict[str, Any]]

runtime_attrs = {"name": lambda self: ref(self.logical_id), "arn": lambda self: fnGetAtt(self.logical_id, "Arn")}

Expand Down
Loading
Loading