Skip to content

Commit a870549

Browse files
authored
CloudFormation best practices for LocalStack<>AWS compatibility (#570)
1 parent 874efa1 commit a870549

1 file changed

Lines changed: 81 additions & 0 deletions

File tree

src/content/docs/aws/services/cloudformation.mdx

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,65 @@ The following code snippets and sample applications provide practical examples o
144144
- [Deploying containers on ECS clusters using ECR and Fargate](/aws/tutorials/ecs-ecr-container-app/)
145145
- [Messaging Processing application with SQS, DynamoDB, and Fargate](https://github.com/localstack/sqs-fargate-ddb-cdk-go)
146146

147+
## Best practices
148+
149+
CloudFormation templates that target both real AWS and LocalStack should avoid hardcoded values that differ between the two environments.
150+
Using [pseudo parameters](#pseudo-parameters) and [intrinsic functions](#intrinsic-functions) keeps a single template portable without conditional logic or environment-specific parameter overrides.
151+
152+
### Use `AWS::URLSuffix` for service domain names
153+
154+
Hardcoding `amazonaws.com` (or conversely `localhost.localstack.cloud`) when building service URLs is one of the most common causes of templates that deploy on AWS but fail on LocalStack, or vice versa.
155+
This typically shows up in API Gateway invoke URLs, Step Functions API integration targets, and other places where a template constructs a fully qualified endpoint.
156+
157+
The `AWS::URLSuffix` pseudo parameter resolves to `amazonaws.com` on AWS (or `amazonaws.com.cn` in China Regions) and to the configured [`LOCALSTACK_HOST`](/aws/capabilities/config/configuration/) on LocalStack, which defaults to `localhost.localstack.cloud`.
158+
Referencing it lets the same template produce a valid URL in either environment.
159+
160+
The following snippet shows the anti-pattern to avoid, where `amazonaws.com` is hardcoded into the output URL.
161+
A template written this way deploys on AWS but produces a non-resolvable URL on LocalStack:
162+
163+
```yaml
164+
Outputs:
165+
ApiUrl:
166+
Value: !Sub "https://${MyApi}.execute-api.${AWS::Region}.amazonaws.com/${StageName}"
167+
```
168+
169+
Reference `AWS::URLSuffix` instead so the same template resolves to `amazonaws.com` on AWS and to the LocalStack host locally:
170+
171+
```yaml
172+
Outputs:
173+
ApiUrl:
174+
Value: !Sub "https://${MyApi}.execute-api.${AWS::Region}.${AWS::URLSuffix}/${StageName}"
175+
```
176+
177+
The same pattern applies when wiring an API Gateway stage into a Step Functions task, when building a WebSocket invoke URL, or any other integration `Uri` that embeds a service domain.
178+
The LocalStack team contributed this practice upstream to the [AWS SAM application templates](https://github.com/aws/aws-sam-cli-app-templates/pull/525) and to the [AWS serverless patterns collection](https://github.com/aws-samples/serverless-patterns) that backs [serverlessland.com/patterns](https://serverlessland.com/patterns) and the VS Code Application Builder.
179+
180+
:::caution
181+
`AWS::URLSuffix` is the right tool for endpoints your template constructs, such as API Gateway URLs or service domain joins.
182+
Avoid substituting it into URIs that AWS resolves to fixed production hostnames, for example:
183+
184+
- ECR image URIs such as `<account>.dkr.ecr.<region>.amazonaws.com/<image>`
185+
- SageMaker built-in image URIs
186+
- AppSync `HttpConfig` endpoints for Bedrock or Step Functions data sources
187+
188+
In those cases, the `amazonaws.com` suffix is part of a registry or service endpoint that is not served by LocalStack, so rewriting it can break the template on AWS without making it work on LocalStack.
189+
:::
190+
191+
### Use `AWS::Partition` when building ARNs
192+
193+
Prefer composing ARNs with `AWS::Partition`, `AWS::Region`, and `AWS::AccountId` rather than embedding a literal `arn:aws:...` prefix.
194+
The resulting template also works on AWS GovCloud and AWS China without changes:
195+
196+
```yaml
197+
ManagedPolicyArns:
198+
- !Sub "arn:${AWS::Partition}:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs"
199+
```
200+
201+
### Reference resources with `!Ref` and `Fn::GetAtt`
202+
203+
When one resource needs the address of another, read it from the resource itself with `!Ref` or `!GetAtt` rather than constructing the URL from service domains.
204+
For example, use `!GetAtt MyQueue.QueueUrl` or `!GetAtt MyBucket.DomainName` so LocalStack returns the local endpoint while AWS returns the real one.
205+
147206
## Feature coverage
148207

149208
:::tip
@@ -203,6 +262,28 @@ Please exercise caution when using parameters with `NoEcho`.
203262
| `Fn::Cidr` | No | Generates a CIDR block from the inputs. |
204263
| `Fn::GetAZs` | No | Returns a list of the Availability Zones of a region. |
205264

265+
### Pseudo Parameters
266+
267+
[Pseudo parameters](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/pseudo-parameter-reference.html) are built-in variables that CloudFormation resolves at deployment time.
268+
You can reference them with the `Ref` intrinsic function (for example, `!Ref AWS::Region`) or with `Fn::Sub` (for example, `!Sub "${AWS::Region}"`).
269+
LocalStack resolves each pseudo parameter to the equivalent value for the local environment, which lets the same template deploy against both AWS and LocalStack.
270+
271+
| Pseudo Parameter | Supported | Value in LocalStack | Value in AWS |
272+
| ----------------------- | --------- | ------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
273+
| `AWS::AccountId` | Yes | The account ID used by the stack (default: `000000000000`) | The AWS account ID of the account deploying the stack |
274+
| `AWS::NotificationARNs` | Partial | Empty list | The list of SNS topic ARNs passed to the stack via `--notification-arns` |
275+
| `AWS::NoValue` | Yes | Removes the corresponding property when used as a return value in `Fn::If` | Same |
276+
| `AWS::Partition` | Yes | `aws` | `aws`, `aws-cn`, or `aws-us-gov` depending on the Region |
277+
| `AWS::Region` | Yes | The Region of the encompassing resource | Same |
278+
| `AWS::StackId` | Yes | The ARN of the stack | Same |
279+
| `AWS::StackName` | Yes | The name of the stack | Same |
280+
| `AWS::URLSuffix` | Yes | The configured [`LOCALSTACK_HOST`](/aws/capabilities/config/configuration/) (default: `localhost.localstack.cloud`) | `amazonaws.com`, or `amazonaws.com.cn` in China Regions |
281+
282+
:::tip
283+
Reach for `AWS::URLSuffix` and `AWS::Partition` instead of hardcoding `amazonaws.com` or `arn:aws:...` in templates.
284+
See [Best practices](#best-practices) for details.
285+
:::
286+
206287
### Resources
207288

208289
<CloudFormationCoverage client:load />

0 commit comments

Comments
 (0)