feat: Add CloudFormation Language Extensions support (Fn::ForEach)#8637
Open
feat: Add CloudFormation Language Extensions support (Fn::ForEach)#8637
Conversation
0be94d0 to
5d6cbf3
Compare
Contributor
Author
|
Integration test: https://github.com/aws/aws-sam-cli/actions/runs/21808626015 |
e68efa0 to
4ed8396
Compare
8 tasks
5324dad to
707baad
Compare
9baaa0d to
d322ba2
Compare
b2a44b9 to
6713896
Compare
Contributor
Author
|
Integration test run: https://github.com/aws/aws-sam-cli/actions/runs/22987281510 |
seshubaws
reviewed
Mar 13, 2026
seshubaws
reviewed
Mar 13, 2026
...tegration/testdata/buildcmd/language-extensions-nested-foreach-dynamic-codeuri/template.yaml
Show resolved
Hide resolved
seshubaws
reviewed
Mar 13, 2026
seshubaws
reviewed
Mar 13, 2026
seshubaws
reviewed
Mar 13, 2026
seshubaws
reviewed
Mar 13, 2026
seshubaws
reviewed
Mar 13, 2026
tests/integration/testdata/package/language-extensions-foreach/template.yaml
Show resolved
Hide resolved
9b224a7 to
e3ff752
Compare
Implement a local CloudFormation Language Extensions processor supporting: - Fn::ForEach loop expansion in Resources, Conditions, and Outputs - Fn::Length, Fn::ToJsonString intrinsic functions - Fn::FindInMap with DefaultValue support - Conditional DeletionPolicy/UpdateReplacePolicy - Nested ForEach depth validation (max 5 levels) - Partial resolution mode preserving unresolvable references Pipeline architecture: TemplateParsingProcessor -> ForEachProcessor -> IntrinsicResolverProcessor -> DeletionPolicyProcessor -> UpdateReplacePolicyProcessor Includes comprehensive unit tests and CloudFormation compatibility suite.
Wire the language extensions library into SAM CLI with two-phase architecture: - Phase 1: expand_language_extensions() -> LanguageExtensionResult - Phase 2: SamTranslatorWrapper.run_plugins() (SAM transform only) Key components: - expand_language_extensions() canonical entry point - SamTranslatorWrapper receives pre-expanded template (Phase 2 only) - SamLocalStackProvider.get_stacks() calls expand_language_extensions() - SamTemplateValidator calls expand_language_extensions() - DynamicArtifactProperty dataclass for Mappings transformation - Fn::ForEach guards in artifact_exporter, normalizer, cdk/utils
- _get_template_for_output() preserves Fn::ForEach in build output - _update_foreach_artifact_paths() generates Mappings for dynamic artifact properties with per-function build paths - Recursive nested Fn::ForEach support - ForEach-aware path resolution skips Docker image URIs Test templates: static CodeUri, dynamic CodeUri, parameter collections, nested stacks, nested ForEach, dynamic ImageUri, depth validation.
Package:
- _export() calls expand_language_extensions() for Phase 1
- Preserves Fn::ForEach in packaged template with S3 URIs
- Generates Mappings for dynamic artifact properties
- _find_artifact_uri_for_resource() handles all export formats:
string, {S3Bucket,S3Key}, {Bucket,Key}, {ImageUri}
- Recursive nested Fn::ForEach support
- Warning for parameter-based collections
Deploy:
- Uploads original unexpanded template to CloudFormation
- Clear error for missing Mapping keys
Integration tests for CodeUri, ContentUri, DefinitionUri, ImageUri,
BodyS3Location across all packageable resource types.
- sam validate: valid ForEach, invalid syntax, cloud-dependent collections, dynamic CodeUri, nested depth validation (5 valid, 6 invalid) - sam local invoke: expanded function names from ForEach - sam local start-api: ForEach-generated API endpoints
Track CFNLanguageExtensions as a UsedFeature event when templates with AWS::LanguageExtensions transform are expanded. Emitted once per expansion in expand_language_extensions().
Remove redundant and AWS-dependent integration tests, keeping 9 essential tests across build, package, validate, local invoke, and start-api. Delete 34 orphaned testdata directories.
YAML parsing produces Python booleans for bare true/false values, but parameter overrides from --parameter-overrides are always strings. Fn::Equals was using Python == which returns False for 'true' == True. CloudFormation Fn::Equals performs string comparison, so convert both operands to their string representations before comparing. Booleans are lowercased to produce 'true'/'false' matching CFN serialization.
… only Language extension functions are only supported in these three sections per AWS::LanguageExtensions transform documentation. Previously the intrinsic resolver also processed Parameters, Mappings, Metadata, etc.
The name iter_regular_resources better conveys that ForEach blocks are skipped. Removes the backward-compatible alias.
Extract duplicated _to_boolean logic from condition_resolver.py and fn_if.py into IntrinsicFunctionResolver.to_boolean() static method. Replace os.path.isfile() + os.path.getmtime() two-step check with a single try/except around getmtime() to eliminate the race condition.
Remove 9 integration tests whose test data directories were removed in
an earlier commit: validate/language-extensions/, buildcmd/language-
extensions-dynamic-imageuri/, language-extensions-foreach/, and
language-extensions-nested-foreach-{valid,invalid}/.
- Move inline imports to top level in sam_stack_provider.py and test_template.py - Add missing assertion in test_handles_empty_mappings - Uncomment Fn::ForEach::Topics block to test non-Lambda resource types - Update mock patch paths to match top-level import locations
e3ff752 to
75df927
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
This PR adds support for CloudFormation Language Extensions in SAM CLI, addressing GitHub issue #5647.
Features
Fn::Ifin resource policiesKey Design Decisions
Fn::ForEachblocks with dynamic artifact properties (e.g.,CodeUri: ./src/${Name}) are supported via a Mappings transformationFn::ForEachcollections must be resolvable locally; cloud-dependent values (Fn::GetAtt,Fn::ImportValue) are not supported with clear error messagesSupported Commands
sam build- Builds all expanded functions, preserves original templatesam package- PreservesFn::ForEachstructure with S3 URIssam deploy- Uploads original template for CloudFormation to processsam validate- Validates language extension syntaxsam local invoke- Invokes expanded functions by namesam local start-api- Serves ForEach-generated API endpointssam local start-lambda- Serves all expanded functionsExample
Resolves #5647
Testing
Checklist