!Sub substitution function to inject parameter values into ARNs and other strings, avoiding hard-coded resource names. Using !Sub makes templates reusable, easier to manage across environments, and helps eliminate brittle string literals.
Why use !Sub instead of hard-coded names
- Keeps templates environment-agnostic and reusable.
- Lets you supply values at stack creation time via Parameters.
- Reduces copy/paste errors when reusing templates across accounts, regions, or partitions.
Example: Hard-coded bucket name
Here is an example policy where the bucket name is hard-coded inside the ARN:eden-kodekloud-bncv-bkt segment is fixed in the template. To make this template parameter-driven, replace the literal bucket name in the ARN with a !Sub expression that references a parameter.
Dynamic resource ARN with !Sub
Use !Sub with a placeholder like ${InputBucketName}. You can keep the Bucket property as !Ref (or use !Sub there as well). This example keeps Bucket: !Ref and uses !Sub only in the ARN:
${InputBucketName} placeholder is replaced with the value provided to the InputBucketName parameter when the stack is created.
Parameters and mappings (example)
Below is an exampleMappings and Parameters section that pairs a bucket parameter with a developer selection. This demonstrates how !Sub works together with template inputs:
Using
!Sub improves template readability and reusability. You can reference parameters in any string or ARN using the same ${ParamName} pattern, and the substitution happens at deploy time.cfn-lint warning about hard-coded partition
Tooling like cfn-lint may warn about a hard-coded partition when an ARN includesarn:aws:.... That literal uses the aws partition and reduces portability if you plan to deploy to specialized partitions (for example, aws-us-gov or aws-cn).
To make an ARN partition-agnostic, include the AWS::Partition pseudo parameter inside !Sub:
aws, aws-cn, aws-us-gov, etc.) is chosen automatically based on where the stack is deployed.
If you do not plan to deploy to AWS GovCloud or China partitions, keeping
arn:aws:s3::: is acceptable in many cases. Use ${AWS::Partition} when you need true portability across partitions.Linter severities (quick reference)
| Severity | Meaning | Action |
|---|---|---|
| Red (Error) | Will prevent deployment | Fix immediately |
| Yellow (Warning) | Likely a configuration or portability issue | Evaluate and address as needed |
| Blue / Info | Best-practice suggestion | Optional; follow if applicable |
Best practices & checklist
- Replace hard-coded resource names in ARNs with
!Suband${ParameterName}to support parameterized deployments. - Use
AWS::Partitioninside!Subif you require portability across AWS partitions. - Prefer
!Subfor any string that includes parameters or pseudo parameters (for example,AWS::AccountId,AWS::Region,AWS::Partition). - Keep
Bucket: !Ref InputBucketName(orBucket: !Sub "${InputBucketName}") and use!Subonly where interpolation is required. - Run
cfn-lintto catch portability and formatting issues, and apply linter guidance based on your deployment targets.
Summary
- Use
!Subwith${ParameterName}to inject parameter values into ARNs and other strings. - Add
${AWS::Partition}inside!Subwhen cross-partition portability is needed. - Treat
cfn-lintmessages as guidance: prioritize errors, evaluate warnings, and apply best-practice suggestions where beneficial.
Links and references
- AWS CloudFormation pseudo parameters — AWS::Partition
- cfn-lint GitHub repository
- AWS CloudFormation User Guide