DEV Community

Cover image for Game of Templates with AWS Cloud Formation: Simple and Templates with Intrinsic Functions, including ForEach and FindInMap

Game of Templates with AWS Cloud Formation: Simple and Templates with Intrinsic Functions, including ForEach and FindInMap

AWS CloudFormation

AWS CloudFormation is a service provided by Amazon Web Services (AWS) that allows you to define and manage your infrastructure as code.


Key Terms

CloudFormation templates

A CloudFormation template is used to define your AWS resources in a structured text format, either YAML or JSON.

CloudFormation stack

A "stack" is a collection of AWS resources that may be managed as a single unit in AWS CloudFormation.

StackSet

A named collection of stacks that have the same template but are applied to distinct accounts and regions.

Change Set

A Change Set is a list of proposed changes that will be applied to a stack when you run an update. It serves as a preview method, allowing you to review and validate potential modifications before they are implemented in your infrastructure.


Image description

You are only charged for the resources you create and the API calls that CloudFormation performs on your behalf.


The Template Study

Let's dive into the structure of an AWS CloudFormation template

The following example shows an AWS CloudFormation YAML template structure and its top-level objects:

AWSTemplateFormatVersion: 'version date' (optional) # version of the CloudFormation template. Only accepted value is '2010-09-09'

Description: 'String' (optional) # a text description of the Cloudformation template

Metadata: 'template metadata' (optional) # objects that provide additional information about the template

Parameters: 'set of parameters' (optional) # a set of inputs used to customize the template

Rules: 'set of rules' (optional) # a set of rules to validate the parameters provided at deployment/update

Mappings: 'set of mappings' (optional) # a mapping of keys and associated values

Conditions: 'set of conditions' (optional) # conditions that control whether certain resources are created

Transform: 'set of transforms' (optional) # for serverless applications

Resources: 'set of resources' (required) # a components of your infrastructure

Hooks: 'set of hooks' (optional) # Used for ECS Blue/Green Deployments

Outputs: 'set of outputs' (optional) # values that are returned whenever you view your stack's properties
Enter fullscreen mode Exit fullscreen mode

Image description


Now, Its time for building some CloudFormation Templates and explore various features.

Lets begin the game now


Image description


Building Cloud Formation Templates


Simple Templates

Template 1

A Simple CloudFormation Template that creates an S3 Bucket

AWSTemplateFormatVersion: '2010-09-09'
Resources:
  MyS3Bucket:
    Type: 'AWS::S3::Bucket'
    Properties:
      BucketName: templateonebucket

Enter fullscreen mode Exit fullscreen mode
  • Replace templateonebucket with a unique name for your S3 bucket. S3 bucket names are globally unique across all of AWS, so you may need to come up with a creative and unique name.
  • Save this template in a .yaml file and then use the AWS CloudFormation service to deploy it.

Lets Understand the above snippet

Certainly! Let me explain each part of the CloudFormation template:

  1. AWSTemplateFormatVersion: '2010-09-09':
    This specifies the version of the CloudFormation template format being used. In this case, it's set to the earliest version '2010-09-09'. This version indicates the overall structure and syntax of the template.

  2. Resources:
    The Resources section is where you define the AWS resources you want to create or manage using this CloudFormation template. Resources can include things like EC2 instances, S3 buckets, DynamoDB tables and more.

  3. MyS3Bucket:
    This is the logical name of the resource being defined. In this case, it's an S3 bucket, and the logical name is MyS3Bucket. You'll use this logical name later to refer to this resource in other parts of the template or when interacting with AWS services.

  4. Type: 'AWS::S3::Bucket':
    This line specifies the type of AWS resource you want to create. In this case, you're creating an S3 bucket, so the Type is set to AWS::S3::Bucket. This tells CloudFormation that you want to create an S3 bucket resource.

  5. Properties:
    The Properties section is where you provide the specific configuration for the resource you're creating. For an S3 bucket, there are various properties you can set, such as BucketName, AccessControl, VersioningConfiguration etc

  6. BucketName: templateonebucket:
    This is where you set the properties for the S3 bucket. In this example, you're setting the BucketName property to templateonebucket.
    Replace templateonebucket with a unique name for your S3 bucket. As mentioned before, S3 bucket names must be globally unique across AWS, so make sure to choose a unique name.

Once you have this CloudFormation template ready, you can use the AWS CloudFormation service to deploy it. This will create an S3 bucket with the bucket name specified


Template 2

Create another s3 bucket with versioning enabled

AWSTemplateFormatVersion: '2010-09-09'
Resources:
  MyS3Bucket:
    Type: 'AWS::S3::Bucket'
    Properties:
     BucketName: mybucketname
     VersioningConfiguration:
        Status: Enabled
Enter fullscreen mode Exit fullscreen mode
  • Replace mybucketname with a unique name for your S3 bucket. S3 bucket names are globally unique across all of AWS, so you may need to come up with a creative and unique name.
  • Save this template in a .yaml file and then use the AWS CloudFormation service to deploy it.

VersioningConfiguration is a property that sets the versioning configuration for the bucket.
Statusis set to Enabled to enable versioning for the bucket.


Intrinsic functions

Intrinsic functions are AWS CloudFormation built-in functions that you may use within CloudFormation templates to dynamically produce values, apply conditionals, and control resource characteristics.

You can use intrinsic functions in resource properties, outputs, metadata attributes, and update policy attributes.


Image description

Lets dive into some commonly used intrinsic functions in CloudFormation


Ref

This function is used to reference a resource declared within the same CloudFormation stack. It returns the value of the specified resource's logical name.

Template 3

A simple S3 bucket with the name of the bucket getting displayed in output tab using Ref

AWSTemplateFormatVersion: '2010-09-09'
Resources:
  MyS3Bucket:
    Type: 'AWS::S3::Bucket'
    Properties:
      BucketName: my-unique-bucket-name

Outputs:
  BucketNameOutput:
    Description: "Name of the S3 bucket"
    Value: !Ref MyS3Bucket

Enter fullscreen mode Exit fullscreen mode
  • Replace my-unique-bucket-name with a unique name for your S3 bucket. S3 bucket names are globally unique across all of AWS, so you may need to come up with a creative and unique name.
  • Save this template in a .yaml file and then use the AWS CloudFormation service to deploy it.
  • We define an output named BucketNameOutput to display the name of the created S3 bucket.
  • We use the !Ref function to reference the MyS3Bucket resource, which returns the logical name of the resource, in this case, MyS3Bucket

Outputs in CloudFormation provide a way to make important information available after your infrastructure has been deployed. This can include resource identifiers, endpoints, URLs, and more. By defining outputs, you can make your CloudFormation stacks more flexible and interconnected, enabling you to build complex and dynamic infrastructures.

  • In Summary, When you deploy this CloudFormation template, it will create an S3 bucket with the specified name, and the output BucketNameOutput will display the logical name of the bucket resource (MyS3Bucket).

You can View the output value on the AWS CloudFormation console , in the Outputs tab.

Image description


Fn::Join

This function joins values together using a delimiter to create a single string. It's often used to construct resource names or ARNs

Every Fn::Join object requires two parameters,

  1. a string delimiter
  2. a list of strings to be joined or a function that returns a list of strings to be joined.

Template 4

A simple S3 bucket with bucket name getting generated using Fn:Join

AWSTemplateFormatVersion: '2010-09-09'
Resources:
  MyS3Bucket:
    Type: 'AWS::S3::Bucket'
    Properties:
      BucketName:
        Fn::Join:
          - '-'
          - - 'my-bucket-prefix'
            -  Ref: 'AWS::Region'
            - 'unique-id'

Enter fullscreen mode Exit fullscreen mode
  • Save this template in a .yaml file and then use the AWS CloudFormation service to deploy it.
  • Under the Resources section, we define an S3 bucket resource named MyS3Bucket.
  • We use the Fn::Join intrinsic function to dynamically generate the bucket name.

  • As the Fn::Join function takes two arguments:

  1. The delimiter to use to join the elements (in this case, a hyphen -)

  2. A list of elements to join together

Inside the list of elements, we include:

  • A static string 'my-bucket-prefix'.
  • The Ref intrinsic function referencing the AWS region. This will be replaced with the actual AWS region where the stack is being created.
  • Another static string 'unique-id'.

    • The resulting bucket name will be something like: my-bucket-prefix-us-east-2-unique-id, where us-east-2 being the actual AWS region I use the CloudFormation service

Image description


Fn::Sub

The Fn::Sub intrinsic function in AWS CloudFormation is used for variable substitution in strings. It allows you to create dynamic strings by substituting variables with their corresponding values from resources, parameters, or intrinsic functions

Syntax:

Fn::Sub:
  - "string to substitute ${variable}"
  - { "variable": value }

Enter fullscreen mode Exit fullscreen mode

Template 5

A simple S3 bucket with a bucket name derived by the value of the Environment variable using Fn::Sub

AWSTemplateFormatVersion: '2010-09-09'
Resources:
  MyBucket:
    Type: 'AWS::S3::Bucket'
    Properties:
      BucketName:
        Fn::Sub:
          - "my-awesome-bucket-${Environment}"
          - { "Environment": "production" }

Enter fullscreen mode Exit fullscreen mode
  • Save this template in a .yaml file and then use the AWS CloudFormation service to deploy it.
  • Under the Resources section, we define an S3 bucket resource named MyBucket.

  • The BucketName property of the S3 bucket resource uses the Fn::Sub intrinsic function to create a dynamic bucket name.

  • The first argument is the string template with a variable ${Environment} that needs to be replaced.

  • The second argument to Fn::Sub is a JSON object that specifies the values of the variables used in the string template.

  • In this case, {"Environment": "production"} means that ${Environment} will be replaced with the value "production".

  • After deployment, the bucket name will be my-awesome-bucket-production.

Image description


Fn::GetAtt

The Fn::GetAtt intrinsic function returns the value of an attribute from a resource in the template.

Syntax

!GetAtt logicalNameOfResource.attributeName

Template 6

Create an S3 bucket resource, along with a few outputs being generated using Fn::GetAtt

AWSTemplateFormatVersion: '2010-09-09'
Resources:
  MyS3Bucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: my-unique-bucket-name

Outputs:
  BucketName:
    Value: !Ref MyS3Bucket
  BucketArn:
    Value: !GetAtt MyS3Bucket.Arn
  BucketDomainName:
    Value: !GetAtt MyS3Bucket.DomainName

Enter fullscreen mode Exit fullscreen mode
  • Replace my-unique-bucket-name with a unique name for your S3 bucket. S3 bucket names are globally unique across all of AWS, so you may need to come up with a creative and unique name.
  • Save this template in a .yaml file and then use the AWS CloudFormation service to deploy it.
  • MyS3Bucket is defined as an S3 bucket resource with a specified unique bucket name.
  • The BucketName output uses !Ref to reference the bucket resource, effectively providing the name of the bucket.
  • The BucketArn output uses !GetAtt to retrieve the Amazon Resource Name (ARN) of the bucket resource.
  • The BucketDomainName output uses !GetAtt to retrieve the domain name of the bucket.

You can View the output values on the AWS CloudFormation console , in the Outputs tab.

Image description


Fn::Select

The Fn::Select intrinsic function in AWS CloudFormation is used to select an element from a list based on its index. It's commonly used when you have a list of items and you want to retrieve a specific item from that list.

Syntax:

Fn::Select: [ index, listOfItems ]

Enter fullscreen mode Exit fullscreen mode
  • index: The zero-based index of the item you want to select from the list.
  • listOfItems: The list of items from which you want to select an item.

The !Select function allows you to dynamically select values based on their indices, enabling you to parameterize and customize your resource properties within the template.

Template 7

Creates an AWS S3 bucket resource named MyBucket with a specific bucket name generated using Fn::Select function

AWSTemplateFormatVersion: '2010-09-09'
Resources:
  MyBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Select [1, ["my-bucket-1", "my-bucket-2", "my-bucket-3"]]

Enter fullscreen mode Exit fullscreen mode
  • Replace my-bucket-1,my-bucket-2,my-bucket-3 with a unique name for your S3 bucket. S3 bucket names are globally unique across all of AWS, so you may need to come up with a creative and unique name.
  • Save this template in a .yaml file and then use the AWS CloudFormation service to deploy it.
  • AWSTemplateFormatVersion: '2010-09-09': This indicates the version of the CloudFormation template format being used. In this case, the template is using the 2010-09-09 version.

  • Resources: This section defines the AWS resources that you want to create or manage using the CloudFormation template.

  • MyBucket: This is the logical ID of the AWS S3 bucket resource being created.

  • Type: AWS::S3::Bucket:This specifies the resource type as an S3 bucket. It indicates that CloudFormation should create an S3 bucket resource.

Properties: This section contains the properties specific to the resource being created. In this case, it's the properties of the S3 bucket resource.

BucketName: This property specifies the name of the S3 bucket. The value is retrieved using the !Select intrinsic function.

-!Select [1, ["my-bucket-1", "my-bucket-2", "my-bucket-3"]]: This uses the !Select function to select an element from a list.

  • The arguments are as follows:
    • 1: This is the index of the element to be selected. Since indices are zero-based, 1 corresponds to the second element in the list.
    • ["my-bucket-1", "my-bucket-2", "my-bucket-3"]: This is the list of bucket names. The second element in this list is "my-bucket-2".

When this CloudFormation template is executed, it will create an S3 bucket resource named MyBucket with the bucket name "my-bucket-2".

Image description


Fn::Split

The Fn::Split intrinsic function in AWS CloudFormation is used to split a string into a list of substrings based on a specified delimiter.

Syntax:

Fn::Split: [ delimiter, sourceString ]

Enter fullscreen mode Exit fullscreen mode

Template 8

Add a tag for the S3 bucket resource MyBucket

AWSTemplateFormatVersion: '2010-09-09'
Resources:
  MyS3Bucket:
    Type: 'AWS::S3::Bucket'
    Properties:
      BucketName: my-bucket-name
      Tags:
        - Key: Name
          Value:
            'Fn::Select':
              - 1
              - 'Fn::Split':
                  - '-'
                  - my-bucket-name


Enter fullscreen mode Exit fullscreen mode
  • Replace my-bucket-name with a unique name for your S3 bucket. S3 bucket names are globally unique across all of AWS, so you may need to come up with a creative and unique name.
  • Save this template in a .yaml file and then use the AWS CloudFormation service to deploy it.
  • AWSTemplateFormatVersion: '2010-09-09': This line specifies the version of the CloudFormation template format being used. In this case, it's the 2010-09-09 version.

  • Resources: This section defines the AWS resources that you want to create or manage using the CloudFormation template.

  • MyBucket: This is the logical ID of the AWS S3 bucket resource being created.

  • Type: AWS::S3::Bucket: This line indicates that the resource type being created is an S3 bucket. The CloudFormation template will create an S3 bucket resource.

  • Properties: This section contains the properties specific to the S3 bucket resource.

  • BucketName: my-bucket-name: This sets the name of the S3 bucket to "my-bucket-name".

  • Tags: This property allows you to assign tags to the S3 bucket.

Tags are key-value pairs used to label and categorize resources for better organization and management.

-Key: Name: This defines a tag with the key "Name" The key represents the label or category of the tag.

  • Value: The value of the tag is determined using the Fn::Select and Fn::Split intrinsic functions.

  • Fn::Split: This function is used to split the string "my-bucket-name" into a list of substrings using the "-" delimiter. The result is a list of substrings: ["my", "bucket", "name"].

  • Fn::Select: This function is used to select an element from the list of substrings. In this case, the second element ("bucket") is selected using an index of 1.

As a result, the S3 bucket resource MyBucket will have a tag with the key "Name" and the value "bucket."

You can view the tags on the properties tab of your bucket in S3 Console

Image description

The dynamic tagging is achieved by using Fn::Split to manipulate the string "my-bucket-name" and Fn::Select to select the desired substring for the tag value.


Fn::ForEach

The Fn::ForEach intrinsic function takes a collection and a fragment, and applies the items in the collection to the identifier in the provided fragment

Syntax:

Fn::ForEach::UniqueLoopName:
    - Identifier
    - - Value1 # Collection
      - Value2
    - OutputKey:
        OutputValue

Enter fullscreen mode Exit fullscreen mode
  • UniqueLoopName A name for this loop.
  • Identifier The identifier you want to replace in the OutputKey and OutputValue parameters that represent the template fragment that is replicated
  • Collection
    The collection of values to iterate over. This can be an array in this parameter, or it can be a Ref to a CommaDelimitedList.

  • OutputKey
    The key in the transformed template. ${Identifier} must be included within the OutputKey parameter.

  • OutputValue
    The value that is replicated in the transformed template for each item in the Collection parameter


You must use the AWS::LanguageExtensions transform to use the Fn::ForEach intrinsic function.

Template 9

Using the Fn::ForEach intrinsic function, create S3 buckets with versioning enabled, by looping through a list of bucket names and building S3 buckets with those names

AWSTemplateFormatVersion: 2010-09-09
Transform: 'AWS::LanguageExtensions'
Resources:
  Fn::ForEach::Buckets:
    - BucketName
    - - my-unique-bucket-name-one
      - my-unique-bucket-name-two
    - ${BucketName}:
        Type: AWS::S3::Bucket
        Properties:
          BucketName: !Ref BucketName
          VersioningConfiguration:
             Status: Enabled
Enter fullscreen mode Exit fullscreen mode
  • Replace my-unique-bucket-name-one and my-unique-bucket-name-two with a unique names for your S3 buckets. S3 bucket names are globally unique across all of AWS, so you may need to come up with a creative and unique name.
  • Save this template in a .yaml file and then use the AWS CloudFormation service to deploy it.
  • Transform: AWS::LanguageExtensions: This indicates that you're using a custom CloudFormation transform named AWS::LanguageExtensions. This transform extends the template syntax with additional capabilities, and in this case, it's used to enable the Fn::ForEach functionality.

  • Resources: This is where you define your CloudFormation resources.

  • Fn::ForEach::Buckets: This is the resource defined with the Fn::ForEach intrinsic function. It iterates over the list of bucket names and creates S3 bucket resources for each name.

    • BucketName: This is the logical ID for the iteration. This is the parameter for the iteration, representing the list of bucket names that you want to iterate over.

my-unique-bucket-name-one and my-unique-bucket-name-two : These are the actual bucket names specified in the list. Each name represents an iteration.

${BucketName}: This is the dynamic reference to the iteration variable. It's used to create the logical IDs for the S3 buckets.

Type: AWS::S3::Bucket: This specifies that the resource type is an S3 bucket.

BucketName: !Ref BucketName: This sets the name of the S3 bucket based on the value of the BucketName parameter.

VersioningConfiguration: This defines the bucket's versioning configuration.

Status: Enabled: This enables versioning for the S3 bucket.

Image description

  • With Fn::ForEach, you can replicate parts of your templates with minimal lines of code.
  • You can use Fn::ForEach to simplify your template layout and make it easier and faster for you and your peers to review your code.
  • Fn::ForEach helps reduce human errors such as updating wrong properties or missing out on updating multiple target properties in your template.

Fn::FindInMap

The Fn::FindInMap intrinsic function is used in AWS CloudFormation templates to retrieve a value from a predefined mapping. This function is particularly useful when you want to parameterize or customize resources based on certain conditions, such as regions or environments.

A Mappings section is a top level section of a CloudFormation template. It is used to define maps, their keys and values which can be then referenced in your template.

When you include the AWS::LanguageExtensions transform in an AWS CloudFormation template, you can define the fields of Fn::FindInMap using intrinsic functions. so that, If no mapping is detected, you can utilize a new optional field to return a default value.

Template 10

Create S3 buckets with names based on AWS regions, while also providing a default bucket name in case a specific region mapping is not defined. using !FindInMap

AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::LanguageExtensions'
Mappings:
  S3BucketNames:
    us-east-1:
      BucketName: my-bucket-us-east-1
    us-west-1:
      BucketName: my-bucket-us-west-1

Resources:
  MyS3Bucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName:
        Fn::FindInMap:
          - S3BucketNames
          - !Ref AWS::Region
          - BucketName
          - DefaultValue: my-default-bucket
Enter fullscreen mode Exit fullscreen mode
  • Replace my-default-bucket, my-bucket-us-east-1 and my-bucket-us-west-1 with a unique names for your S3 buckets.

  • S3 bucket names are globally unique across all of AWS
    so you may need to come up with a creative and unique name.

  • Save this template in a .yaml file and then use the AWS CloudFormation service to deploy it.

  • Try deploying the template in us-east-1, us-west-1 and us-west-2 regions.

The above CloudFormation template showcases the usage of the AWS::LanguageExtensions transform along with the Fn::FindInMap function and a default value extension.

  • Transform: 'AWS::LanguageExtensions': This line indicates that the CloudFormation template is using the AWS::LanguageExtensions transform, which allows the use of language extensions like default values for intrinsic functions.

  • Mappings: Defines a mapping named S3BucketNames, which associates different S3 bucket names with different AWS regions.

  • S3BucketNames: This key under the Mappings section contains the mapping for S3 bucket names based on regions.

  • us-east-1 and us-west-1: These region keys under the S3BucketNames mapping correspond to different AWS regions. For each region, there's a sub-key BucketName that holds the corresponding bucket name.

  • Resources: Specifies the AWS resources created as part of this CloudFormation stack.

  • MyS3Bucket: Defines an S3 bucket resource.

  • Type: AWS::S3::Bucket: Indicates that this resource represents an S3 bucket.

  • Properties: Sets the properties for the MyS3Bucket resource.

BucketName: This property designates the bucket name of the S3 bucket.

  • The Fn::FindInMap function is used to dynamically retrieve the bucket name based on the AWS region of the stack ( !Ref AWS::Region).

There is a twist here,

DefaultValue: my-default-bucket: This extension, facilitated by the AWS::LanguageExtensions transform, allows specification of a fallback value if the specific mapping key isn't found.

In this scenario, if the stack is deployed in a region not included in the S3BucketNames mapping, such as a region other than us-east-1 or us-west-1, the bucket name will default to
my-default-bucket.

If you try to deploy the CloudFormation Template in regions say
US West (Oregon), us-west-2 (which is not mentioned in mapping-S3BucketNames) The name of the bucket would be my-default-bucket

If you try to deploy the cloudformation Template in regions say
US East (N. Virginia) us-east-1 The name of the bucket would be my-bucket-us-east-1

If you try to deploy the cloudformation Template in regions say
US West (N. California) us-west-1 The name of the bucket would be my-bucket-us-west-1


Create CloudFormation stacks using the AWS Management Console

The following YouTube video illustrate how you can create a CloudFormation stack using the console.


Deleting a CloudFormation stack will remove all resources created by that stack. So once you have finished learning, You can delete the stacks

Top comments (0)