When developing your Infrastructure as Code CloudFormation templates, you sometimes need to associate your resources with a list of Security Groups (SG) which may need to be configurable. For example, your resource may have a primary SG, and a list of optional SGs that can be specified at template deployment time.
I recently went through a somewhat painful exercise figuring out how to implement this, and that’s what I’m here to share with you.
As they say – “failure is mother of all learning”, or something on those lines (actually I just made it up), but anyway.
What I failed to achieve is make it work with optional SGs Parameter type List<AWS::EC2::SecurityGroup::Id>
, that displays a nice drop-down of all your existing SGs when deploying through UI. Instead, I have to use CommaDelimitedList
, which isn’t nearly as nice, but hey – at least it works.
The sticker was in the validation logic (see Gist below): when user doesn’t make any selection for Parameter with type List<AWS::EC2::SecurityGroup::Id>
, CloudFormation declares this Parameter as “unknown”, so it can’t be used for things like Fn::Equals
and such. That’s what I learned (and proudly twitted about with the #awswishlist tag, asking for a way to validate if such Parameter is empty) 🙂
So, enough talking, off to show the goods.
{ | |
"AWSTemplateFormatVersion" : "2010-09-09", | |
"Parameters": { | |
"PrimarySG": { | |
"Description": "Primary Security Group for the instance", | |
"Type": "String", | |
"Default": "sg-863ab0e0", | |
"ConstraintDescription": "must be an EC2 security group id" | |
}, | |
"AdditionalSGs": { | |
"Description": "Additional security groups for the instance", | |
"Type": "CommaDelimitedList", | |
"Default": "sg-e23ab084,sg-bb27addd", | |
"ConstraintDescription": "must be a list of EC2 security group ids" | |
} | |
}, | |
"Conditions" : { | |
"MoreSGs": { "Fn::Not": [ | |
{ "Fn::Equals": [ "", | |
{ "Fn::Join": [ "", { "Ref": "AdditionalSGs" } ] } | |
] } | |
] } | |
}, | |
"Resources": { | |
"InstanceLaunchConfig": { | |
"Type": "AWS::AutoScaling::LaunchConfiguration", | |
"Properties": { | |
"InstanceType": "t2.nano", | |
"KeyName": "SecretKey", | |
"ImageId": "ami-deadbeef", | |
"SecurityGroups": { | |
"Fn::If": [ | |
"MoreSGs", | |
{ "Fn::Split": [ ",", | |
{ "Fn::Join": [ ",", [ | |
{ "Ref": "PrimarySG" }, | |
{ "Fn::Join": [ ",", { "Ref": "AdditionalSGs" } ] } | |
] ] } | |
] }, | |
{ "Fn::Split": [ ",", | |
{ "Ref": "PrimarySG" } | |
] } | |
] | |
} | |
} | |
} | |
} | |
} |
Notes:
- Yes, the logic spider nest in
SecurityGroup
attribute ofInstanceLaunchConfig
looks, um, unattractive; but it works. - You may probably want to make sure the value you supply for AdditionalSGs doesn’t have any spaces in it.
- I declare
PrimarySG
as a Parameter here – it’s just for brevity so I don’t have to include an actualAWS::EC2::SecurityGroup
Resource that in turn requires a VPC, and so on. - What’s with all Splits and Joins? Well,
SecurityGroups
is a List, which means that we first must make a String from all our SGs, and then Split the result. SinceAdditionalSGs
is aCommaDelimitedList
(and not a String), it needs to be run throughFn::Join
(line 46), as well. Yeah, ugly.
I am making a buch of assumptions about what you, dear reader, already know – so shout out in comments if something is still muddy.
November 9th, 2018 at 6:27 pm
So helpful .. thank youl
April 18th, 2023 at 5:31 pm
Dmitri this was super helpful, thank you so much!!