Making Your Templates More Versatile
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:
- Mappings: It matches keys to a set of values "Key:Value" dictionaries to use in resource declarations.
- Conditionals let you add logical statements to add or alter values in your resources.
What is Mappings?
- Mappings are fixed variables within your CloudFormation template which consists of name-value pair.
- 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.
- Conditions are used to control the creation of resources or outputs based on a given condition.
- Conditions can be anything you want, few common ones are:
- Environment(dev/test)
- AWS Region
- Parameter Value
- Conditions can refer to another condition, parameter values or mapping.
- 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