Blogs

AWS CloudFormation Intrinsic Functions

AWS CloudFormation is a tool to create a template for your infrastructure. You can define AWS constructs using JSON or YAML. AWS’s documentation is well-suited for explaining the basic functionality and simple resource creation. After creating a few stacks, a newcomer would likely want to learn how to increase template portability and re-use with intrinsic functions. At this point, the reference material may be a bit daunting. This article will introduce CloudFormation intrinsic functions by using the creation of a simple VPC as an example.

AWS CloudFormation intrinsic functions help you manage cloud formation stacks by assigning values that are required but are only available at run time (i.e., when the stack is launched). For example, let’s say that one of your resources depends on another resource’s attributes, which are not defined at the time the template is written. We can use CloudFormation to dynamically access those attributes at run time.

Here’s a list of the intrinsic functions we’ll cover in this post.

  • Fn::GetAZs – returns an array that lists Availability Zones for a specified region

  • Fn::Select – returns a single object from a list of objects by index

  • Fn::Ref – returns the value of the specified parameter or resource

  • Fn::GetAtt – returns the value of an attribute from a resource in the template

  • Fn::Sub – substitutes variables in an input string with values that you specify

  • Fn::Join – appends a set of values into a single value, separated by the specified delimiter

Some of Konekti’s customers have expressed frustration in writing CloudFormation templates in JSON. We tend to agree as we find reading and writing CloudFormation templates in YAML to be an easier method. The examples in this post will use YAML.

Like JSON, intrinsic functions in YAML can be expressed in templates using the format “Fn::function_name”. YAML has the advantage of using the short form, “!function_name”. This means rather than writing “Fn::Sub”, you can write “!Sub”. Note that the short form notation will indicate invalid when using YAML validators. The AWS CLI command aws cloudformation validate-template –template-body file://your_template.yaml can be executed to validate a template.

Our stack will create a VPC with two subnets, one per AZ. The stack also creates an Internet Gateway and two Routing Tables (one per subnet).

!GetAZs

For portability and re-use, we want to create a template that will deploy infrastructure in any region. Hard coding ‘us-east-1a’ links the template to this AZ. Launching the stack in another region will result in an error. To resolve this issue, we’ll look to !GetAZs. This function returns an array. We need a way to access an individual element in the area.

!Select

The !Select function identifies a single object in an array. We want to launch one subnet in the first AZ and another in the second. The expression AvailabilityZone: !Select [ 0, !GetAZs ” ] returns the first element of the availability zones array. For us-east-1, the first AZ is us-east-1a.

!Ref

The !Ref function gives the author of the template access to variables in the Parameters section of the template.

Let’s look at an example.

  1. Parameters:
    VpcCIDR:
    Description:Please enter the IP range (CIDR notation) for this VPC
    Type:String
    Default:172.25.0.0/16

Specifying “!Ref VpcCIDR” in a template will replace the aforementioned text with “172.25.0.0/16” or the CIDR range specified at run time.

!GetAtt

When we need to access Parameters, we use !Ref. !GetAtt provides similar functionality for accessing resource attributes in the template.

The !GetAtt documentation provides the attributes available for each resource. A VPC has a “DefaultSecurityGroup” attribute. We reference this security group using !GetAttr VPC.DefaultSecurityGroup.

!Sub

The !Sub function replaces a variable with its value. The function can be used with or without a mapping. We’ll use an example of basic substitution without a mapping.

The expression !Sub ${EnvironmentName} fills in the value of the EnvironmentName parameter. Placing the expression in the Outputs section would print “Konekti-prod” if the default environment name from the Parameters section is used.

!Join

If you are a python developer, the !Join function is reminiscent of the python join method. The result is the string “1,2,3”. The comma serves as a separater character. The same holds true for the expression !Join [ “,”, [ !Ref Subnet1, !Ref Subnet2 ]].

Let’s use what we’ve learned to instantiate a VPC.


  1. AWSTemplateFormatVersion: “2010-09-09″
    Description:|
    This template deploys a VPC with two Availability Zones, each with
    one subnet. It also deploys an Internet Gateway and a Route Table.Parameters:
    EnvironmentName:
    Description: Please enter a name for this VPC
    Type: String
    Default: Konekti-prodVpcCIDR:
    Description: Please enter the IP range in CIDR notation for this VPC
    Type: String
    Default: 172.25.0.0/16Subnet1CIDR:
    Description:
    Please enter the IP range in CIDR notation for the
    subnet in the first Availability Zone
    Type: String
    Default: 172.25.30.0/24Subnet2CIDR:
    Description: |
    Please enter the IP range (CIDR notation) for the
    subnet in the second Availability Zone
    Type: String
    Default: 172.25.32.0/24Resources:VPC:
    Type: AWS::EC2::VPC
    Properties:
    CidrBlock: !Ref VpcCIDR
    Tags:
    – Key: Name
    Value: !Ref EnvironmentNameInternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
    Tags:
    – Key: Name
    Value: !Ref EnvironmentName

    InternetGatewayAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
    InternetGatewayId: !Ref InternetGateway
    VpcId: !Ref VPC

    Subnet1:
    Type: AWS::EC2::Subnet
    Properties:
    VpcId: !Ref VPC
    AvailabilityZone: !Select [ 0, !GetAZs ” ]
    CidrBlock: !Ref Subnet1CIDR
    MapPublicIpOnLaunch: false
    Tags:
    – Key: Name
    Value: !Sub ${EnvironmentName} AZ1

    Subnet2:
    Type: AWS::EC2::Subnet
    Properties:
    VpcId: !Ref VPC
    AvailabilityZone: !Select [ 1, !GetAZs ” ]
    CidrBlock: !Ref Subnet2CIDR
    MapPublicIpOnLaunch: false
    Tags:
    – Key: Name
    Value: !Sub ${EnvironmentName} AZ2

    RouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
    VpcId: !Ref VPC
    Tags:
    – Key: Name
    Value: !Sub ${EnvironmentName}

    DefaultRoute:
    Type: AWS::EC2::Route
    DependsOn: InternetGatewayAttachment
    Properties:
    RouteTableId: !Ref RouteTable
    DestinationCidrBlock: 0.0.0.0/0
    GatewayId: !Ref InternetGateway

    Subnet1RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
    RouteTableId: !Ref RouteTable
    SubnetId: !Ref Subnet1

    Subnet2RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
    RouteTableId: !Ref RouteTable
    SubnetId: !Ref Subnet2

    Outputs:

    VPC:
    Description: A reference to the created VPC
    Value: !Ref VPC

    Subnets:
    Description: A list of the subnets
    Value: !Join [ “,”, [ !Ref Subnet1, !Ref Subnet2 ]]

    Subnet1:
    Description: |
    A reference to the subnet in the 1st Availability Zone
    Value: !Ref Subnet1

    Subnet2:
    Description: |
    A reference to the subnet in the 2nd Availability Zone
    Value: !Ref Subnet2

    SecurityGroup:
    Description: VPC Default Security Group
    Value: !GetAtt VPC.DefaultSecurityGroup

Creating the Stack

Let’s demonstrate how the template can be used to build a stack. Copy the text from the template or download it here. Validate it using aws cloudformation validate-template –template-body file://vpc.yaml.

The command will return a ValidationError if the template fails validation.

Let’s create our stack and examine the outputs.

  1. $ aws cloudformation create-stack –stack-name Konekti –template-body file://vpc.yaml
    { “StackId”: “arn:aws:cloudformation:us-east-1:097996670471:stack/Konekti/b9a63580-0235-11e8-a218-50d5ca6326ba”
    }# # wait a few minutes, query the stack, and wait for it to complete if needed
    $ aws cloudformation describe-stacks –stack-name Konekti | grep StackStatus “StackStatus”: “CREATE_COMPLETE”,
    $ aws cloudformation describe-stacks –stack-name Konekti –output text
    STACKS 2018-01-26T01:10:40.614Z This template deploys a VPC with two Availability Zones, each with one subnet. It also deploys an Internet Gateway and a Route Table. False arn:aws:cloudformation:us-east-1:097996670471:stack/Konekti/b9a63580-0235-11e8-a218-50d5ca6326ba Konekti CREATE_COMPLETE OUTPUTS A reference to the subnet in the 1st Availability Zone Subnet1 subnet-fd2842d2
    OUTPUTS A reference to the subnet in the 2nd Availability Zone Subnet2 subnet-8789b9cc
    OUTPUTS A list of the subnets Subnets subnet-fd2842d2,subnet-8789b9cc OUTPUTS A reference to the created VPC VPC vpc-5c203324
    OUTPUTS VPC Default Security Group SecurityGroup sg-cc3fd0bb PARAMETERS VpcCIDR 172.25.0.0/16
    PARAMETERS EnvironmentName Konekti-prod
    PARAMETERS Subnet2CIDR 172.25.32.0/24
    PARAMETERS Subnet1CIDR 172.25.30.0/24 #

Look! We have a stack. We used the OUTPUTS section to display the text related to resource creation. I encourage readers to experiment with intrinsic functions using OUTPUTS. If you use Resources such as VPCs, Route Tables and Subnets, no costs will be incurred. When you want to delete the stack, execute aws cloudformation delete-stack –stack-name Konekti. Remember that you have a limit of five VPCs per region.

Conclusion

The most important take-away from this article is that you should associate CloudFormation intrinsic functions with run time intelligence. The awareness CloudFormation has about the resources being created increases the portability of the templates. We didn’t cover all functions nor describe other critical components of portability such as pseudo parameters. I’ll describe this information in a future post.

 

Author

Jeff Loughridge

Leave a comment

Your email address will not be published.