Conditions - !If function
Everything you need to know about the !If
function
Introduction
We are going to explore:
Inner workings of the
!If
functionHow they work in conjunction with the
!Equals
functionHow they work with the pseudo parameter
!NoValue
With !If
functions, a value is returned if a certain condition evaluates to true
while another value is returned when the condition evaluates to false.
If a condition evaluates to false
, any Resource that depends on that condition will not be created in the stack.
If the condition evaluates to true
, any resource that depends on that condition will be created in the stack.
Template Sample
Let's explore the template below and examine how Conditions are utilized in the template
AWSTemplateFormatVersion: 2010-09-09
Description: Creating an ec2 instance
Mappings: # Allows cloudformation to use only these AMIs in the following regions. Always update the latest AMI IDs
MapEc2Region:
us-east-2:
HVM64: ami-0c0d141edc4f470cc
us-west-2:
HVM64: ami-093467ec28ae4fe03
MapEnvironmentType: # Allows cloudformation to only use these instance type depending on the Env type
Prod:
InstanceType: t3.small
Dev:
InstanceType: t3.micro
Parameters:
ParamEnvironmentType:
Description: Select the Environment Type from the list
Type: String
Default: Dev
AllowedValues:
- Prod
- Dev
ParamKeyName:
Description: Select the key name from the list
Type: AWS::EC2::KeyPair::KeyName
ParamAZName:
Description: Select the Avaiability Zone name from the list
Type: AWS::EC2::AvailabilityZone::Name
Conditions:
ConditionForProdEIP: !Equals [!Ref ParamEnvironmentType, Prod]
ConditionForProdSG: !Equals [!Ref ParamEnvironmentType, Prod]
ConditionForDevSG: !Equals [!Ref ParamEnvironmentType, Dev]
Resources:
ProdSecurityGroup:
Type: AWS::EC2::SecurityGroup
Condition: ConditionForProdSG
Properties:
GroupDescription: Allow HTTP traffic
GroupName: Prod SecurityGroup
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: '80'
ToPort: '80'
CidrIp: 0.0.0.0/0
DevSecurityGroup:
Type: AWS::EC2::SecurityGroup
Condition: ConditionForDevSG
Properties:
GroupDescription: Allow HTTP and SSH traffic
GroupName: Dev SecurityGroup
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: '80'
ToPort: '80'
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: '22'
ToPort: '22'
CidrIp: 0.0.0.0/0
MyEIP:
Type: AWS::EC2::EIP
Condition: ConditionForProdEIP
Properties:
InstanceId: !Ref EC2Instance
EC2Instance:
Type: AWS::EC2::Instance
Properties:
ImageId: !FindInMap
- MapEc2Region
- !Ref AWS::Region
- HVM64
KeyName: !Ref ParamKeyName
AvailabilityZone: !Ref ParamAZName
InstanceType: !FindInMap
- MapEnvironmentType
- !Ref ParamEnvironmentType
- InstanceType
Tags:
- Key: Name
Value: !Ref ParamEnvironmentType
UserData:
Fn::Base64: |
#!/bin/bash -xe
yum update -y
yum install -y httpd
systemctl start httpd
systemctl enable httpd
echo '<html><h1>Hello From Your Restart Web Server!</h1></html>' > /var/www/html/index.html
SecurityGroups:
- !If [ConditionForProdSG, !Ref ProdSecurityGroup, !Ref AWS::NoValue]
With Conditions, you can deploy both / either the Dev and / or Prod environments using the same template. You can achieve this with the help of Parameters, Mappings and Resources.
Let's explore how Parameters and the other sections assist in deploying different environments.
Step 1
Parameters
Let's extract the Parameter section of our template
ParamEnvironmentType:
Description: Select the Environment Type from the list
Type: String
Default: Dev
AllowedValues:
- Prod
- Dev
The ParamEnvironmentType
parameter tells CloudFormation which environment to be created during the stack creation process.
This is where you choose either Dev
or Prod
Step 2
Conditions
Conditions:
ConditionForProdEIP: !Equals [!Ref ParamEnvironmentType, Prod]
ConditionForProdSG: !Equals [!Ref ParamEnvironmentType, Prod]
ConditionForDevSG: !Equals [!Ref ParamEnvironmentType, Dev]
Conditions evaluate the selected environment type (by using !Ref ParamEnvironmentType) and decides which resources are going to be created.
ConditionForDevSG
evaluate to true if the environment type selected isDev
.ConditionForProdSG
andConditionForProdEIP
evaluate to true if the environment type selected isProd
.
Resource Creation
Notice that Condition: ConditionForProdSG
is now added to the following Resources
ProdSecurityGroup
MyEIP
We first evaluate that Resource and confirm whether the condition associated with it evaluates to true
or false
.
If the condition
ConditionForProdSG
is true, theProdSecurityGroup
Resource will be created. If the condition is false, the Resource is not created. Same goes forMyEIP
Resource.If the condition
ConditionForDevSG
is true, theDevSecurityGroup
Resource will be created
Resources:
ProdSecurityGroup:
Type: AWS::EC2::SecurityGroup
Condition: ConditionForProdSG
Properties:
GroupDescription: Allow HTTP traffic
GroupName: Prod SecurityGroup
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: '80'
ToPort: '80'
CidrIp: 0.0.0.0/0
DevSecurityGroup:
Type: AWS::EC2::SecurityGroup
Condition: ConditionForDevSG
Properties:
GroupDescription: Allow HTTP and SSH traffic
GroupName: Dev SecurityGroup
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: '80'
ToPort: '80'
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: '22'
ToPort: '22'
CidrIp: 0.0.0.0/0
MyEIP:
Type: AWS::EC2::EIP
Condition: ConditionForProdEIP
Properties:
InstanceId: !Ref EC2Instance
EC2Instance:
Type: AWS::EC2::Instance
Properties:
ImageId: !FindInMap
- MapEc2Region
- !Ref AWS::Region
- HVM64
KeyName: !Ref ParamKeyName
AvailabilityZone: !Ref ParamAZName
InstanceType: !FindInMap
- MapEnvironmentType
- !Ref ParamEnvironmentType
- InstanceType
Tags:
- Key: Name
Value: !Ref ParamEnvironmentType
UserData:
Fn::Base64: |
#!/bin/bash -xe
yum update -y
yum install -y httpd
systemctl start httpd
systemctl enable httpd
echo '<html><h1>Hello From Your Restart Web Server!</h1></html>' > /var/www/html/index.html
SecurityGroups:
- !If [ConditionForProdSG, !Ref ProdSecurityGroup, !Ref AWS::NoValue]
How the !If function
works with Conditions
If we intend to deploy a Dev environment, we expect to attach a Dev security group to the instance and vice versa.
Since the EC2 Instance does not rely on any condition evaluating to true or false, it will be created regardless.
But what is more important to note is the security group that is going to be attached to the instance.
EC2Instance:
Type: AWS::EC2::Instance
Properties:
ImageId: !FindInMap
- MapEc2Region
- !Ref AWS::Region
- HVM64
KeyName: !Ref ParamKeyName
AvailabilityZone: !Ref ParamAZName
InstanceType: !FindInMap
- MapEnvironmentType
- !Ref ParamEnvironmentType
- InstanceType
Tags:
- Key: Name
Value: !Ref ParamEnvironmentType
UserData:
Fn::Base64: |
#!/bin/bash -xe
yum update -y
yum install -y httpd
systemctl start httpd
systemctl enable httpd
echo '<html><h1>Hello From Your Restart Web Server!</h1></html>' > /var/www/html/index.html
SecurityGroups:
- !If [ConditionForProdSG, !Ref ProdSecurityGroup, !Ref AWS::NoValue]
By using the !If function
on the security group property, we are explicitly telling CloudFormation
To use the
!If
intrinsic function to check ifConditionForProdSG
is true.If
ConditionForProdSG
is true (meaning the environment isProd
), it referencesProdSecurityGroup
thus it will be attached to the instance.If
ConditionForProdSG
is false (meaning the environment isDev
), then no security group will be attached to the instance. This is due to the pseudo parameterAWS::NoValue
.Note. You can adjust the template using another
!If function
to reference the correct security group when the environment selected isDev
How AWS::NoValue works with Conditions
SecurityGroups:
- !If [ConditionForProdSG, !Ref ProdSecurityGroup, !Ref AWS::NoValue]
We have concluded that the pseudo parameter AWS::NoValue
ensures a Resource is not created if ConditionForProdSG
evaluates to false
.
When you choose the Dev
environment, a Dev
security group will be created but because of the pseudo parameter AWS::NoValue
, no security group will attached to the instance.
Summary
You can update the template to include Resources such as databases and load balancers with similar conditional logic.
This technique follows architectural best practices by separating environment-specific configurations thus resulting in a more cleaner, readable and flexible template.