Skip to content

Commit c4640d7

Browse files
committed
docs: add CloudFormation Language Extensions support documentation
Covers: - How the two-phase expansion works (LE then SAM transform) - Fn::ForEach usage with static and parameter-based collections - Dynamic artifact properties and SAM-generated Mappings - Nested stacks with parent-supplied parameters - Nested Fn::ForEach (up to 5 levels) - &{identifier} syntax for logical ID sanitization - Supported intrinsic functions table - Limitations (collection resolvability, package-time fixation) - Telemetry
1 parent 7e23978 commit c4640d7

1 file changed

Lines changed: 165 additions & 0 deletions

File tree

docs/cfn-language-extensions.md

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
# CloudFormation Language Extensions Support
2+
3+
SAM CLI now supports templates that use the `AWS::LanguageExtensions` transform, including `Fn::ForEach`, `Fn::Length`, `Fn::ToJsonString`, and `Fn::FindInMap` with `DefaultValue`.
4+
5+
## How it works
6+
7+
When SAM CLI detects `AWS::LanguageExtensions` in a template's `Transform` section, it expands language extension constructs locally before running SAM transforms. This enables `sam build`, `sam package`, `sam deploy`, `sam validate`, `sam local invoke`, and `sam local start-api` to work with templates that use these constructs.
8+
9+
The expansion happens in two phases:
10+
11+
1. **Phase 1 (Language Extensions)**`Fn::ForEach` loops are expanded, intrinsic functions are resolved where possible, and the template is converted to standard CloudFormation.
12+
2. **Phase 2 (SAM Transform)** — The expanded template is processed by the SAM Translator as usual.
13+
14+
The original template (with `Fn::ForEach` intact) is preserved for CloudFormation deployment, since CloudFormation processes the `AWS::LanguageExtensions` transform server-side.
15+
16+
## Fn::ForEach
17+
18+
`Fn::ForEach` generates multiple resources, conditions, or outputs from a single template definition:
19+
20+
```yaml
21+
Transform: AWS::LanguageExtensions
22+
23+
Parameters:
24+
ServiceNames:
25+
Type: CommaDelimitedList
26+
Default: "Users,Orders,Products"
27+
28+
Resources:
29+
Fn::ForEach::Services:
30+
- Name
31+
- !Ref ServiceNames
32+
- ${Name}Function:
33+
Type: AWS::Serverless::Function
34+
Properties:
35+
Handler: index.handler
36+
Runtime: python3.12
37+
CodeUri: ./services/${Name}
38+
```
39+
40+
Running `sam build` expands this into `UsersFunction`, `OrdersFunction`, and `ProductsFunction`, each built from its respective source directory.
41+
42+
### Dynamic artifact properties
43+
44+
When a packageable property (like `CodeUri`, `ContentUri`, `ImageUri`) uses a loop variable (e.g., `./services/${Name}`), SAM CLI generates a CloudFormation `Mappings` section that maps each collection value to its S3 URI. The `Fn::ForEach` body is rewritten to use `Fn::FindInMap` so CloudFormation can resolve the correct artifact at deploy time.
45+
46+
For example, after `sam package`:
47+
48+
```yaml
49+
Mappings:
50+
SAMCodeUriServices:
51+
Users:
52+
CodeUri: s3://my-bucket/abc123
53+
Orders:
54+
CodeUri: s3://my-bucket/def456
55+
Products:
56+
CodeUri: s3://my-bucket/ghi789
57+
58+
Resources:
59+
Fn::ForEach::Services:
60+
- Name
61+
- !Ref ServiceNames
62+
- ${Name}Function:
63+
Type: AWS::Serverless::Function
64+
Properties:
65+
Handler: index.handler
66+
Runtime: python3.12
67+
CodeUri: !FindInMap [SAMCodeUriServices, !Ref Name, CodeUri]
68+
```
69+
70+
### Parameter-based collections
71+
72+
When the `Fn::ForEach` collection is a parameter reference (`!Ref ServiceNames`), the collection values are resolved at package time from:
73+
74+
1. `--parameter-overrides` passed to `sam build` or `sam package`
75+
2. The parameter's `Default` value in the template
76+
77+
**Important:** If you change the parameter value at deploy time (e.g., adding a new service), you must re-package first so the Mappings include entries for the new values.
78+
79+
```bash
80+
# Package with the values you intend to deploy with
81+
sam package --parameter-overrides ServiceNames="Users,Orders,Products"
82+
83+
# Deploy with the same values
84+
sam deploy --parameter-overrides ServiceNames="Users,Orders,Products"
85+
```
86+
87+
### Nested stacks
88+
89+
`Fn::ForEach` in nested stack templates (`AWS::CloudFormation::Stack`) is supported. SAM CLI passes the parent stack's `Parameters` property to the child template expansion, so child `Fn::ForEach` collections that reference parent-supplied parameters resolve correctly.
90+
91+
```yaml
92+
# parent.yaml
93+
Resources:
94+
ChildStack:
95+
Type: AWS::CloudFormation::Stack
96+
Properties:
97+
TemplateURL: ./child.yaml
98+
Parameters:
99+
ServiceNames: "Users,Orders,Products"
100+
```
101+
102+
### Nested Fn::ForEach
103+
104+
Up to 5 levels of nesting are supported, matching CloudFormation's limit:
105+
106+
```yaml
107+
Resources:
108+
Fn::ForEach::Envs:
109+
- Env
110+
- [Dev, Staging, Prod]
111+
- Fn::ForEach::Services:
112+
- Svc
113+
- [Users, Orders]
114+
- ${Env}${Svc}Function:
115+
Type: AWS::Serverless::Function
116+
Properties:
117+
CodeUri: ./services/${Svc}
118+
Environment:
119+
Variables:
120+
STAGE: !Ref Env
121+
```
122+
123+
### &{identifier} syntax
124+
125+
The `&{identifier}` syntax strips non-alphanumeric characters from the substituted value, useful for generating valid logical IDs from values like IP addresses:
126+
127+
```yaml
128+
Fn::ForEach::Hosts:
129+
- IP
130+
- ["10.0.0.1", "10.0.0.2"]
131+
- Host&{IP}:
132+
Type: AWS::EC2::Instance
133+
# Expands to Host10001, Host10002
134+
```
135+
136+
## Supported intrinsic functions
137+
138+
The following intrinsic functions are resolved locally during expansion:
139+
140+
| Function | Description |
141+
|----------|-------------|
142+
| `Fn::ForEach` | Loop expansion |
143+
| `Fn::Length` | Returns count of list elements |
144+
| `Fn::ToJsonString` | Converts value to JSON string |
145+
| `Fn::FindInMap` | Map lookup (with optional `DefaultValue`) |
146+
| `Fn::If` | Conditional value selection |
147+
| `Fn::Sub` | String substitution |
148+
| `Fn::Join` | String concatenation |
149+
| `Fn::Split` | String splitting |
150+
| `Fn::Select` | List element selection |
151+
| `Fn::Base64` | Base64 encoding |
152+
| `Fn::Equals` / `Fn::And` / `Fn::Or` / `Fn::Not` | Condition evaluation |
153+
| `Ref` | Parameter and pseudo-parameter references |
154+
155+
Functions that require deployed resources (`Fn::GetAtt`, `Fn::ImportValue`, `Fn::GetAZs`) are preserved for CloudFormation to resolve at deploy time.
156+
157+
## Limitations
158+
159+
- **Collections must be resolvable at build/package time.** `Fn::ForEach` collections that use `Fn::GetAtt`, `Fn::ImportValue`, or SSM/Secrets Manager dynamic references cannot be expanded locally. Use a parameter with `--parameter-overrides` instead.
160+
- **Parameter values are fixed at package time.** If you change `--parameter-overrides` at deploy time without re-packaging, the Mappings won't include entries for new values and deployment will fail.
161+
- **`DeletionPolicy` and `UpdateReplacePolicy`** are validated and resolved during expansion. They support `Ref` to parameters but not other intrinsic functions.
162+
163+
## Telemetry
164+
165+
SAM CLI tracks usage of `AWS::LanguageExtensions` via the `CFNLanguageExtensions` telemetry feature flag. No template content is transmitted.

0 commit comments

Comments
 (0)