Making Your Templates More Versatile

2021.10.08

The notion of "infrastructure as code" is one of the most important aspect in Cloud DevOps and Cloud Architecture in general. This turns into a programmatic method of instantiating and configuring environments that is not only self-documenting but also simply repeatable. Coding the parts of a cloud deployment enables the development of a procedure that can predictably construct the infrastructure numerous times while removing the possibility of human mistake resulting in misconfiguration or failure.

In AWS, CloudFormation is the primary tool for achieving these goals. Engineers use CloudFormation to construct templates that can create "stacks" of resources in AWS that are linked together.

So, with that introduction, let's get down to business.

Parameters

Parameters are commonly used to give flexibility to CloudFormation templates and to be able to reuse them in multiple contexts. But what if you want a template that can be reused not just by you but by anyone, Perhaps you need distinct configuration settings for each environment for security reasons, but you don't want the end-user to alter the values for the items in Template

For that, CloudFormation has two features:

  1. Mappings: It matches keys to a set of values "Key:Value" dictionaries to use in resource declarations.
  2. Conditionals let you add logical statements to add or alter values in your resources.

What is Mappings?

  1. Mappings are fixed variables within your CloudFormation template which consists of name-value pair.
  2. They are very useful when you want to differentiate between different environments like test, production or different regions, different AMI types.

Example Mappings:

Mappings:
  RegionEC2ImageMap:
    ap-northeast-3: 
      HVM64: "ami-09ec82600a05bc23a"
    ap-northeast-1: 
      HVM64: "ami-09c4848c1c6c4b09b"
    ap-southeast-1: 
      HVM64: "ami-082105f875acab993"
  InstanceType:
    test:
      EnvInstanceType: t2.micro
    prod:
      instanceType: t2.small

In above example we have 2 top level value "RegionEC2ImageMap" and "InstanceType", and we have multiple second key like Regions in "RegionEC2ImageMap" and "test","prod" in the "InstanceType".

To access the above Mapping values we need to use a function "FN::FindInMap".

Resources:
  EC2Instance:
    Type: "AWS::EC2::Instance"
    Properties:
      ImageId!FindInMap[RegionMap, !Ref "AWS::Region::HVM64"]
      InstanceType: t2.micro

Lets have a look at a sample template:

Parameters: 
    EnvironmentType: 
      Description: The environment type
      Type: String
      Default: testing
      AllowedValues: 
        - testing
        - prod
      ConstraintDescription: must be a testing or prod
Mappings:
  RegionEC2ImageMap:
    ap-northeast-3: 
      HVM64: "ami-09ec82600a05bc23a"
    ap-northeast-1: 
      HVM64: "ami-09c4848c1c6c4b09b"
    ap-southeast-1: 
      HVM64: "ami-082105f875acab993"
  EnvToInstanceType:
    testing:
      EnvInstanceType: t2.micro
    prod:
      instanceType: t2.small    

Resources:
  Ec2Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !FindInMap
        - RegionEC2ImageMap
        - !Ref 'AWS::Region'
        - HVM64
      InstanceType: !FindInMap
        - EnvToInstanceType
        - !Ref 'EnvironmentType'
        - EnvInstanceType

Mappings are mostly used when you know all the values that can be used in your environment such as:

  • Region
  • AWS Account
  • Availability Zone
  • Environment
  • etc...

With help of mapping you have some control over allowed values within the template, which helps in sharing the template to other users.

Conditions

Conditions let you add logical statements to add or alter values in your resources.

  1. Conditions are used to control the creation of resources or outputs based on a given condition.
  2. Conditions can be anything you want, few common ones are:
    • Environment(dev/test)
    • AWS Region
    • Parameter Value
  3. Conditions can refer to another condition, parameter values or mapping.
  4. Logical function can be any of the following:
    • Fn::Equals
    • Fn::And
    • Fn::If
    • Fn::Not
    • Fn::Or

Example Condition:

Conditions:
  CreateTestRes: !Equals [!Ref EnvType, test ]

Using a condition:

Resources:
  MountPoint:
    Type: AWS::EC2::VolumeAttachment
    Condition::CreateTestRes

Above Condition is an example of "Resources" which has a mount point which has a EC2 volume attachment and the condition is CreateTestResources, so if the condition is true, it will create this mount point of false it wont.

For more understanding lets have a look at a sample template:

Parameters:  
  ImageId:
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2

  EnvType:
    Description: Environment type.
    Default: test
    Type: String
    AllowedValues:
      - prod
      - test
    ConstraintDescription: must specify prod or test.

Conditions:
  CreateProdRes: !Equals [ !Ref EnvType, prod ]

Resources:
  EC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref ImageId
      InstanceType: t2.micro

  MountPoint:
    Type: AWS::EC2::VolumeAttachment
    Condition: CreateProdRes
    Properties:
      InstanceId:
        !Ref EC2Instance
      VolumeId:
        !Ref NewVolume
      Device: /dev/sdh

  NewVolume:
    Type: AWS::EC2::Volume
    Condition: CreateProdRes
    Properties:
      Size: 1
      AvailabilityZone:
        !GetAtt EC2Instance.AvailabilityZone

Outputs:
  VolumeId:
    Condition: CreateProdRes
    Value:
      !Ref NewVolume

In above template there are 2 conditions "prod" and "test" environments, so depending on what parameter you choose while executing your template it will execute corresponding condition.

Conclusion

By combining the power of Mappings and Conditions of CloudFormation, you can make templates which are more general, and you can do rid of the for heavily parameterised non shareable templates for different environment situations.

Mappings and Conditions can also be used together for better control over the allowed values or resources, which will make a template shareable to various teams or your customers residing in different regions with different workloads requirements.

References

https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/mappings-section-structure.html https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/conditions-section-structure.html