AWS for DevOps — CloudFormation Intrinsic Functions
8.2 — Intrinsic Functions
Intrinsic functions are built-in CloudFormation functions that let you dynamically reference values, concatenate strings, and apply conditional logic within templates.
Function Overview
| Function | Short Form | Purpose |
|---|---|---|
Fn::Ref |
!Ref |
Reference a parameter or resource |
Fn::GetAtt |
!GetAtt |
Get an attribute of a resource |
Fn::Sub |
!Sub |
String substitution (like template literals) |
Fn::Join |
!Join |
Join strings with a delimiter |
Fn::Select |
!Select |
Pick an item from a list |
Fn::Split |
!Split |
Split a string into a list |
Fn::If |
!If |
Conditional value |
Fn::Equals |
!Equals |
Compare two values |
Fn::And / Fn::Or / Fn::Not |
!And / !Or / !Not |
Logical operators |
Fn::ImportValue |
!ImportValue |
Import from another stack’s output |
Fn::FindInMap |
!FindInMap |
Lookup value from Mappings |
Fn::Base64 |
— | Encode string to Base64 |
Fn::Cidr |
!Cidr |
Generate CIDR blocks |
Ref
Returns the value of a parameter or the physical ID of a resource:
Resources:
MyVPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref VpcCidr # parameter value
MySubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref MyVPC # returns VPC ID (vpc-abc123)
GetAtt
Gets a specific attribute from a resource. Different resources expose different attributes:
Outputs:
VpcId:
Value: !Ref MyVPC # vpc-abc123
VpcCidr:
Value: !GetAtt MyVPC.CidrBlock # 10.0.0.0/16
InstancePublicIp:
Value: !GetAtt WebServer.PublicIp # 54.123.45.67
LoadBalancerDns:
Value: !GetAtt MyALB.DNSName # my-alb-123.ap-south-1.elb.amazonaws.com
Sub (String Substitution)
The most commonly used function — substitutes variables in a string:
# Simple substitution with parameters and pseudo parameters
Name: !Sub "web-server-${EnvironmentType}-${AWS::Region}"
# Result: "web-server-production-ap-south-1"
# Substitution with resource references
Description: !Sub "Security group ${WebSG} in VPC ${MyVPC}"
# Result: "Security group sg-abc123 in VPC vpc-def456"
# Multi-line with explicit variable map
UserData:
Fn::Base64:
!Sub
- |
#!/bin/bash
echo "Region: ${AWS::Region}"
echo "Stack: ${AWS::StackName}"
echo "DB Endpoint: ${DBEndpoint}"
- DBEndpoint: !GetAtt MyDatabase.Endpoint.Address
Join
Concatenates values with a delimiter:
# Join with comma
Value: !Join [",", ["subnet-abc", "subnet-def", "subnet-ghi"]]
# Result: "subnet-abc,subnet-def,subnet-ghi"
# Join with colon
Value: !Join [":", [!Ref "AWS::StackName", !Ref "AWS::Region", "web"]]
# Result: "my-stack:ap-south-1:web"
Select
Pick an item from a list (zero-indexed):
Value: !Select [0, !Split [",", "a,b,c"]]
# Result: "a"
# Pick first AZ in the region
AvailabilityZone: !Select [0, !GetAZs ""]
# Result: "ap-south-1a"
Conditionals
Conditions:
IsProduction: !Equals [!Ref EnvironmentType, production]
IsNotDev: !Not [!Equals [!Ref EnvironmentType, dev]]
IsLargeAndProd: !And
- !Equals [!Ref InstanceSize, large]
- !Condition IsProduction
Resources:
# Create resource only if condition is true
ProdAlarm:
Type: AWS::CloudWatch::Alarm
Condition: IsProduction
Properties:
AlarmName: "prod-alarm"
# Conditional property value
WebServer:
Type: AWS::EC2::Instance
Properties:
InstanceType: !If [IsProduction, t3.large, t3.micro]
# production → t3.large, otherwise → t3.micro
ImportValue (Cross-Stack References)
Stack A exports a value:
# Stack A
Outputs:
VpcId:
Value: !Ref MyVPC
Export:
Name: "shared-vpc-id"
Stack B imports it:
# Stack B
Resources:
MySubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId: !ImportValue "shared-vpc-id"
FindInMap
Look up values from the Mappings section:
Mappings:
RegionAMI:
ap-south-1:
HVM64: ami-0abcdef1234567890
us-east-1:
HVM64: ami-0fedcba9876543210
Resources:
WebServer:
Type: AWS::EC2::Instance
Properties:
ImageId: !FindInMap [RegionAMI, !Ref "AWS::Region", HVM64]
Combining Functions
Functions can be nested:
# Sub + If
Name: !Sub
- "db-${Env}-${AWS::Region}"
- Env: !If [IsProduction, "prod", "dev"]
# Result: "db-prod-ap-south-1"
# Join + Ref + GetAtt
ConnectionString: !Join
- ""
- - "postgresql://"
- !Ref DBUsername
- ":"
- !Ref DBPassword
- "@"
- !GetAtt MyDB.Endpoint.Address
- ":5432/"
- !Ref DBName
# Result: "postgresql://admin:secret@mydb.abc123.ap-south-1.rds.amazonaws.com:5432/myapp"
Common Mistakes
| Mistake | Why It Hurts | Fix |
|---|---|---|
!Ref on a resource expecting an attribute |
Returns ID instead of DNS name, IP, etc. | Use !GetAtt Resource.Attribute |
Circular !ImportValue references |
Stack B depends on A, A depends on B | Design one-way dependency chains |
Using !Sub without ${} |
Just a plain string, no substitution | Wrap variables in ${} |
!Select with out-of-bounds index |
Stack creation fails | Verify list length matches index |
Quick Reference
| Need | Function | Example |
|---|---|---|
| Parameter value | !Ref ParamName |
!Ref InstanceType |
| Resource ID | !Ref ResourceName |
!Ref MyVPC |
| Resource attribute | !GetAtt Resource.Attr |
!GetAtt MyALB.DNSName |
| String template | !Sub "text-${Var}" |
!Sub "app-${AWS::Region}" |
| Concatenate | !Join [delim, list] |
!Join [",", [a, b]] |
| Conditional value | !If [Cond, TrueVal, FalseVal] |
!If [IsProd, t3.large, t3.micro] |
| Cross-stack import | !ImportValue Name |
!ImportValue shared-vpc-id |
Next → Stacks & Nested Stacks