I’m trying to create a CloudFormation script that allows the user to select a Linux distribution from three options (ubuntu, redhat, centos). Based on the option the user selects I then want to select the right AMI for the region in which the CloudFormation stack is being created, from an existing mapping. Finally I want to use that region-specific AMI as the Image ID for an EC2 instance.
I have mappings for each distribution with the AMI for that region mapped.
I would like to have this all automated, so I can use just one CloudFormation script, and not one for each OS.
I have a web application that depending on client preferences will run on Ubuntu, CentOS or Red Hat. I don’t want to have to maintain three separate CloudFormation scripts for each distribution, especially considering that the only real thing that would be different on each is the ImageId
for the EC2 Instance.
I am aware that I should probably be making use of CloudFormation’s condition functions, but I haven’t yet really figured out a way to do it.
I haven’t been able to find anything on the web to help that much. That’s either because it is really easy nobody has posted anything about it, or it’s not possible, I feel like it may be the latter, but would like to see some other views.
Here’s an excerpt of what I am currently working with.
Parameters:
ApplicationServerLinuxDistribution:
Type: "String"
AllowedValues:
- ubuntu
- redhat
- centos
Mappings:
# Ubuntu 18.04 AMIs
AWSUbuntuAMIRegionMap:
ap-northeast-1:
HVM64: "ami-0cd744adeca97abb1"
ap-northeast-2:
HVM64: "ami-00379ec40a3e30f87"
# Red Hat 8 AMIs
AWSRedHatAMIRegionMap:
ap-northeast-1:
HVM64: "ami-0c45b9b8b241f629f"
ap-northeast-2:
HVM64: "ami-090f71670acf741d8"
# CentOS 7 AMIs
AWSCentOSAMIRegionMap:
ap-northeast-1:
HVM64: "ami-045f38c93733dd48d"
ap-northeast-2:
HVM64: "ami-06cf2a72dadf92410"
Resources:
ApplicationServer:
Type: AWS::EC2::Instance
Properties:
ImageId:
So to be clear what I want is if the user is in eu-west-1 and selects Ubuntu, I want to query my Ubuntu Region/AMI map for the eu-west-1 AMI, and use that AMI to create the EC2 instance, and if the user selects Red Hat or CentOS do the same, but for those maps instead.
2
Answers
This was actually a lot simpler to accomplish than I initially thought it might be, and can be done without using conditionals at all.
Solution
Why does this work?
This works, because you can have multiple
SecondLevelKey
's underneath aTopLevelKey
as theFn::FindInMap
documentation states: here. So the AMIs for each distribution in each region can just be key/value pairs underneath the region key. Example below.CloudFormation can then work through things logically. So when you need to get an AMI for the
ImageId
parameter of an EC2 instance based on what a user picks, you tell CloudFormation to query your region map with!FindInMap
(the shorthand forFn::FindInMap
). Then CloudFormation will reference the region you are running the script in by using!Ref "AWS::Region"
, and finally CloudFormation is able to reference the selected parameter with!Ref LinuxDistributionSelection
.I hope my answer is sufficient and explains it well enough for anybody struggling with the same issue. It's my first answer so please be nice, and feel free to critique it if it's misleading, or if I have misunderstood what is actually happening here.
You’re nearly there, but because Fn::FindInMap can only return the value corresponding to keys in a two-level map I’d remove the HVM64 property from the map:
Then in the Resources section you can use the
AWS::Region
pseudo parameter along with yourApplicationServerLinuxDistribution
parameter to access the AMI you need:The region must be used as the first level key, if used as a second level key you will encounter
Mappings attribute name 'ap-northeast-1' must contain only alphanumeric characters.
The documentation states that:But it isn’t clear that this only applies to second level keys!