DEV Community

Cover image for Hands-on AWS CloudFormation - Part 2. Into to Intrinsic functions
Samira Yusifova
Samira Yusifova

Posted on

Hands-on AWS CloudFormation - Part 2. Into to Intrinsic functions

This is Part 2 of Hands-on AWS CloudFormation series, you can find Part 1 here.

This article will walk you through the basic Intrinsic functions of AWS CloudFormation, which are simply built-in functions that can help you manage your stacks.

Fn::Ref

What if you need to use dynamically generated values, the values that are required but are only available at run time? For example, let’s say that you need to create a new VPC and a new Security group in the same stack; and you need to pass VPC's ID to Security group. Here is how you can do that:

Resources: 
  mySecurityGroup: # Logical ID
    Type: 'AWS::EC2::SecurityGroup'
    Properties:
        GroupDescription: My description here
        VpcId:
          Ref: myVPC # ref to VPC that will be created by CloudFormation
          # etc.  
  myVPC: # Logical ID
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      # etc. 
Enter fullscreen mode Exit fullscreen mode

Another way to use Ref is with Parameters. Let’s say that you need to create a new EC2 instance and you need to pass EC2 key pairs and instance’s tag during a stack creation.

Parameters:
  paramTagValue: # declare paramTagValue as a parameter
    Description: Value of tag for EC2 instance
    Type: String
  paramKeyName: # declare paramKeyName as a parameter
    Description: Name of an existing EC2 KeyPair to enable SSH access into the server
    Type: 'AWS::EC2::KeyPair::KeyName'
Resources:
  myEc2Instance: # Logical ID
    Type: 'AWS::EC2::Instance'
    Properties:
      InstanceType: t2.micro
      ImageId: ami-5b41123e
      Tags:
        - Key: CloudFormationLab
          Value: !Ref paramTagValue # ref to Parameters
      KeyName: !Ref paramKeyName # ref to Parameters
Enter fullscreen mode Exit fullscreen mode

Note, that Parameters allow you to pass user inputs at the time of stack creation or update (it will be discussed in the next parts of the series).

The third way to use Ref is as a pseudo parameter. Pseudo parameters are parameters that are predefined by AWS CloudFormation. For example, what if you need to get a region where your resources are being created, e.g. us-west-1? In that case use AWS::Region:

      Tags:
        - Key: CloudFormationLab
          Value: !Ref AWS::Region # use pseudo parameter to get a region
Enter fullscreen mode Exit fullscreen mode

Other pseudo parameters:

  • AWS::AccountId (returns AWS account ID of the account in which the stack is being created)
  • AWS::StackId (returns arn name of the stack)
  • AWS::StackName (returns friendly name of the stack)
  • AWS::NoValue (acts like the null value)

See the whole list by vising AWS's Pseudo parameters reference documentation.

Fn::Select

Select allows you to select one item from a list. Assume that you have a list of CIDR blocks and you need to create three subnets by passing the first CIDR block to the first subnet, the second CIDR block to the second subnet, so on. Here is how you can do that:

Parameters: 
  paramDbSubnetIpBlocks: 
    Description: 'Comma-delimited list of three CIDR blocks'
    Type: CommaDelimitedList
    Default: '10.0.48.0/24, 10.0.112.0/24, 10.0.176.0/24'
Resources:  
  myFirstSubnet:  # Logical ID
    Type: 'AWS::EC2::Subnet'
    Properties: 
      VpcId: !Ref myVPC
      CidrBlock: !Select [ 0, !Ref paramDbSubnetIpBlocks ] # output is 10.0.48.0/24
    # etc. 
  mySecondSubnet:  # Logical ID
    Type: 'AWS::EC2::Subnet'
    Properties: 
      VpcId: !Ref myVPC
      CidrBlock: !Select [ 1, !Ref paramDbSubnetIpBlocks ] # output is 10.0.112.0/24
    # etc.
  myVPC:
    Type: 'AWS::EC2::VPC'
    Properties:
    # etc
Enter fullscreen mode Exit fullscreen mode

Fn::Join

With Join you can take a set of values and append it into a single value, separated by the specified delimiter. Here is an example:

Tags:
  - Key: CloudFormationLab
    Value: !Join [ ' ', [ 'SecurityGroup', 'for', !Ref AWS::Region ] ]
# Output is “SecurityGroup for us-west-1”
Enter fullscreen mode Exit fullscreen mode

Fn::Split

Split is the opposite of Join. It splits a string into a list of string values. Commonly used with Select. Assume that the parameter's value is a comma-delimited string instead of list. You can still easily convert this string into a list.

Parameters: 
  paramDbSubnetIpBlocks: 
    Description: 'Comma-delimited string of three CIDR blocks'
    Type: CommaDelimitedList
    Default: '10.0.48.0/24, 10.0.112.0/24, 10.0.176.0/24'
Resources:  
  myFirstSubnet:  # Logical ID
    Type: 'AWS::EC2::Subnet'
    Properties: 
      VpcId: !Ref VPC
      CidrBlock: !Select [ 0, !Split [',', !Ref paramDbSubnetIpBlocks]] # output is 10.0.48.0/24
    # etc. 
  mySecondSubnet:  # Logical ID
    Type: 'AWS::EC2::Subnet'
    Properties: 
      VpcId: !Ref VPC
      CidrBlock: !Select [ 1, !Split [',', !Ref paramDbSubnetIpBlocks]] # output is 10.0.112.0/24
    # etc.
Enter fullscreen mode Exit fullscreen mode

Fn::GetAtt

What if you need to get an Availability Zone of your EC2 instance specified in your template? Or maybe public DNS? Well, you can use GetAtt which returns the value of an attribute from a resource in the template.

Resources:
  myEC2Instance: # Logical ID
    Type: 'AWS::EC2::Instance'
    # etc. 
  MountPoint:
    # etc. 
  myInstanceVolume: # Logical ID
    Type: 'AWS::EC2::Volume'
    Properties:
      Size: 100
      AvailabilityZone: !GetAtt myEC2Instance.AvailabilityZone # use GetAtt to get AZ of your EC2 instance
Outputs:
  PublicDNS:                                    
    Description: EC2 instance public DNS name   
    Value: !GetAtt myEC2Instance.PublicDnsName # use GetAtt to get public DNS of your EC2 instance
Enter fullscreen mode Exit fullscreen mode

Note, that Outputs allow you to declare output values that you can return in response to view on console or import into other stacks (it will be discussed in the next parts of the series).

But how would you know which attributes each resource has? You need to navigate to AWS CloudFormation's documentation and search for
"AWS resource and property types reference". Find a target resource from the list, e.g. EC2, then search by resource types, e.g. AWS::EC2::Instance. Scroll down and look for "Return values" section. Under "Fn::GetAtt" you can find the list of all available attributes for selected resource type:

Alt Text

Fn::GetAZs

You don't want to hard-code a full list of AZs for a specified region, do you? In that case use GetAZs. It returns a list of AZs for a target region in alphabetical order.

!GetAZs us-east-1  # output is [ 'us-east-1a', 'us-east-1b', 'us-east-1c', 'us-east-1d' ]
Enter fullscreen mode Exit fullscreen mode

You can use GetAZs with Ref and Select:

Resources:
  myInstanceVolume: # Logical ID
    Type: 'AWS::EC2::Volume'
    Properties:
      Size: 100
      AvailabilityZone: !Select [0, !GetAZs !Ref 'AWS::Region']
# !Ref 'AWS::Region' returns a region where your volume is being created, e.g. us-east-1 
# !GetAZs 'us-east-1' returns the list of AZs for 'us-east-1' region, which are ['us-east-1a', 'us-east-1b', 'us-east-1c', 'us-east-1d']
# !Select ['us-east-1a', 'us-east-1b', 'us-east-1c', 'us-east-1d'] returns the first value from the list, which is 'us-east-1a'
Enter fullscreen mode Exit fullscreen mode

Fn::FindInMap

Let's start with Mappings. Why would you need it? Let’s say that you want to provision a new EC2 instance and you want to specify image ID (AMI) based on a region where your instance is being created. E.g. for North Virginia ('us-east-1') region you need to use 'ami-1853ac65' image id and for Singapore ('ap-southeast-1') - 'ami-e2adf99e'. In this case you can use Mappings which allows you to create simple "key:value" dictionaries for use in your resource declarations.

Mappings: # map image ids with regions
  mapRegion:
    us-east-1:
      AMI: ami-1853ac65
    us-west-1:
      AMI: ami-bf5540df
    eu-west-1:
      AMI: ami-3bfab942
    ap-southeast-1:
      AMI: ami-e2adf99e
    ap-southeast-2:
      AMI: ami-43874721
Enter fullscreen mode Exit fullscreen mode

Now, in order to call your map and get a value based on a key, you need to use FindInMap function:

Mappings: # map image ids with regions
  mapRegion:
    us-east-1:
      AMI: ami-1853ac65
    us-west-1:
      AMI: ami-bf5540df
    # etc.
Resources:
  myEc2Instance:  # Logical ID
    Type: 'AWS::EC2::Instance'
    Properties:
      InstanceType: t2.micro
      ImageId: !FindInMap # define imageId based on region
        - mapRegion # map's name
        - !Ref 'AWS::Region' # top level key which is a region where your instance is being created
        - AMI # second level key - e.g. for 'us-east-1' the value for ImageId is 'ami-1853ac65' 
Enter fullscreen mode Exit fullscreen mode

Other intrinsic functions

Alt Text

Now, that was a whole lot to cover but if you read it, kudos! So far we have covered the most crucial intrinsic functions. The rest might be discussed later, here is a brief list:

  • Fn::Base64 - returns the Base64 representation of the input string (details)
  • Fn::Cidr - returns an array of CIDR address blocks (details)
  • Fn::ImportValue - returns the value of an output exported by another stack (details)
  • Fn::Sub - substitutes variables in an input string with values that you specify (details)
  • Fn::Transform - specifies a macro to perform custom processing on part of a stack template (details)

Conclusion

Intrinsic (i.e. build-in) functions are extremely useful and efficient if you'd like to start writing your templates. There is one more group of intrinsic functions left, named Condition functions, which I’m going to cover in Part 3 of Hands-on AWS CloudFormation series. Once we are done with functions we can start using them in custom templates.

Top comments (0)