AWS for DevOps — CloudFormation Intrinsic Functions

3 minute read

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

Updated: