Skip to content

feat: Add CloudFormation Language Extensions support (Fn::ForEach)#8637

Open
bnusunny wants to merge 10 commits intodevelopfrom
feat-language-extension
Open

feat: Add CloudFormation Language Extensions support (Fn::ForEach)#8637
bnusunny wants to merge 10 commits intodevelopfrom
feat-language-extension

Conversation

@bnusunny
Copy link
Contributor

@bnusunny bnusunny commented Feb 9, 2026

Description

This PR adds support for CloudFormation Language Extensions in SAM CLI, addressing GitHub issue #5647.

Features

  • Fn::ForEach - Iterate over collections to generate resources
  • Fn::Length - Get the length of an array
  • Fn::ToJsonString - Convert objects to JSON strings
  • Fn::FindInMap with DefaultValue - Map lookups with fallback values
  • Conditional DeletionPolicy/UpdateReplacePolicy - Use intrinsic functions like Fn::If in resource policies

Key Design Decisions

  1. In-Memory Expansion Only - Templates are expanded in memory for SAM CLI operations, but the original unexpanded template is preserved for CloudFormation deployment
  2. Dynamic Artifact Properties via Mappings - Fn::ForEach blocks with dynamic artifact properties (e.g., CodeUri: ./src/${Name}) are supported via a Mappings transformation
  3. Locally Resolvable Collections Only - Fn::ForEach collections must be resolvable locally; cloud-dependent values (Fn::GetAtt, Fn::ImportValue) are not supported with clear error messages

Supported Commands

  • sam build - Builds all expanded functions, preserves original template
  • sam package - Preserves Fn::ForEach structure with S3 URIs
  • sam deploy - Uploads original template for CloudFormation to process
  • sam validate - Validates language extension syntax
  • sam local invoke - Invokes expanded functions by name
  • sam local start-api - Serves ForEach-generated API endpoints
  • sam local start-lambda - Serves all expanded functions

Example

Transform:
  - AWS::LanguageExtensions
  - AWS::Serverless-2016-10-31

Resources:
  Fn::ForEach::Functions:
    - Name
    - [Alpha, Beta, Gamma]
    - ${Name}Function:
        Type: AWS::Serverless::Function
        Properties:
          Handler: ${Name}.handler
          CodeUri: ./src
          Runtime: python3.9

Resolves #5647

Testing

  • Comprehensive unit tests for the language extensions engine
  • Property-based tests using Hypothesis
  • Integration tests for all supported commands
  • Test templates covering static/dynamic CodeUri, nested stacks, parameter collections

Checklist

  • Unit tests added
  • Integration tests added
  • Documentation in code comments
  • Error messages include actionable workarounds

@bnusunny bnusunny requested a review from a team as a code owner February 9, 2026 00:16
@github-actions github-actions bot added area/package sam package command area/deploy sam deploy command area/build sam build command pr/internal labels Feb 9, 2026
Add core library for processing CFN Language Extensions including:
- Fn::ForEach processor for iterating over collections
- Fn::Length, Fn::ToJsonString intrinsic function resolvers
- Fn::FindInMap with DefaultValue support
- Conditional DeletionPolicy/UpdateReplacePolicy support
- SAM CLI integration module with partial resolution mode

Includes comprehensive unit tests and property-based tests.

Addresses GitHub issue #5647
- Add in-memory template expansion with original template preservation
- Add Stack.original_template_dict for CloudFormation deployment
- Add PACKAGEABLE_RESOURCE_ARTIFACT_PROPERTIES and DynamicArtifactProperty
- Add _process_language_extensions() to SamTranslatorWrapper
- Add ForEach guards to validator and warning system
- Support locally resolvable collections with clear error messages

Templates are expanded in memory for local operations while preserving
the original Fn::ForEach structure for CloudFormation deployment.
- Expand Fn::ForEach in memory to build all generated functions
- Preserve original template structure in .aws-sam/build/template.yaml
- Generate Mappings for dynamic artifact properties (CodeUri: ./${Name})
- Replace dynamic CodeUri with Fn::FindInMap referencing generated Mappings
- Support --resource-id with expanded function names
- Preserve Fn::ForEach structure in packaged template
- Generate Mappings with S3 URIs for dynamic artifact properties
- Use content-based S3 hashing for each expanded artifact
- Emit warning for parameter-based collections with dynamic CodeUri
- Validate collection values for CloudFormation Mapping key compatibility
- Upload original unexpanded template to CloudFormation
- Add clear error message for missing Mapping keys at deploy time
- Suggest re-running sam package when collection values change
- Test sam build with static and dynamic CodeUri
- Test sam package preserves Fn::ForEach with Mappings transformation
- Test sam validate with valid/invalid language extension templates
- Test sam local invoke with expanded function names
- Test sam local start-api with ForEach-generated API endpoints
- Test nested stacks with language extensions
@bnusunny bnusunny force-pushed the feat-language-extension branch from 0be94d0 to 5d6cbf3 Compare February 9, 2026 00:33
@bnusunny
Copy link
Contributor Author

bnusunny commented Feb 9, 2026

The integration tests incorrectly expected "${Name}" and "${FunctionName}"
as the second argument to Fn::FindInMap, but the implementation correctly
uses {"Ref": "Name"} and {"Ref": "FunctionName"} because bare ${Var}
strings are not resolved by Fn::ForEach inside Fn::FindInMap arguments.
- Add _validate_foreach_nesting_depth() and _calculate_max_foreach_depth()
  methods to ForEachProcessor to validate nesting depth before expansion
- Define MAX_FOREACH_NESTING_DEPTH = 5 constant per CloudFormation limits
- Integrate validation into process_template() to fail early with clear error
- Add unit tests for depth calculation and validation (14 new tests)
- Add integration tests for sam validate and sam build with nested loops
- Create test templates for valid (5 levels) and invalid (6 levels) nesting
- Fix mypy type annotation for empty dict in test file

Validates Requirements: 18.1-18.7
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/build sam build command area/deploy sam deploy command area/package sam package command pr/internal

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature request: Support LanguageExtensions feature Fn::ForEach

1 participant