I have a service that has three layers with each layer having an auto scaling group with some instances. The layers differ (in launch configuration, number of instances, etc.) in a way that is controlled by a single parameter ("role"). Thus, I call the template with the parameter as follows:
{
...
"Parameters": {
...
"Role": {
"AllowedValues": [
"inbound",
"filter",
"outbound"
],
...
},
},
"Conditions" : {
"IsStaging" : { "Fn::Equals" : [ { "Ref": "Environment" }, "eu-staging"] },
"IsInbound" : { "Fn::Equals" : [ { "Ref": "Role" }, "inbound"] },
"IsFilter" : { "Fn::Equals" : [ { "Ref": "Role" }, "filter"] },
"IsOutbound" : { "Fn::Equals" : [ { "Ref": "Role" }, "outbound"] }
},
"Resources": {
...
"SecurityGroup": {
"Type": "AWS::EC2::SecurityGroup",
"Properties": {
...
"SecurityGroupEgress": [
{"Fn::If": ["IsFilter", { "CidrIp": "10.243.74.0/24", "IpProtocol": "tcp", "FromPort": "9094", "ToPort": "9094" }, { "Ref": "AWS::NoValue" } ]}
],
"Tags": [
{
"Key": "Name",
"Value": { "Fn::Join" : [ "-", [ "smtp-service-security-group", {"Ref": "Role"} ] ] }
},
...
],
}
},
}
}
Now I would like to declare these three layers in the same CloudFormation template (so that it integrates transparently with an existing pipeline job, and benefits from CloudFormation features, e.g. rollback). Furthermore, I would like to avoid rewriting three times the resources, one time for each layer as follows:
{
...
"Parameters": {
...
"Role": {
"AllowedValues": [
"inbound",
"filter",
"outbound"
],
...
},
},
"Conditions" : {
"IsStaging" : { "Fn::Equals" : [ { "Ref": "Environment" }, "eu-staging"] },
"IsInbound" : { "Fn::Equals" : [ { "Ref": "Role" }, "inbound"] },
"IsFilter" : { "Fn::Equals" : [ { "Ref": "Role" }, "filter"] },
"IsOutbound" : { "Fn::Equals" : [ { "Ref": "Role" }, "outbound"] }
},
"Resources": {
...
"SecurityGroup": {
"Type": "AWS::EC2::SecurityGroup",
"Properties": {
...
"SecurityGroupEgress": [
{"Fn::If": ["IsInbound", { "CidrIp": "10.243.74.0/24", "IpProtocol": "tcp", "FromPort": "9094", "ToPort": "9094" }, { "Ref": "AWS::NoValue" } ]}
],
"Tags": [
{
"Key": "Name",
"Value": { "Fn::Join" : [ "-", [ "smtp-service-security-group", {"Ref": "Role"} ] ] }
},
...
],
}
},
...
"SecurityGroup": {
"Type": "AWS::EC2::SecurityGroup",
"Properties": {
...
"SecurityGroupEgress": [
{"Fn::If": ["IsFilter", { "CidrIp": "10.243.74.0/24", "IpProtocol": "tcp", "FromPort": "9094", "ToPort": "9094" }, { "Ref": "AWS::NoValue" } ]}
],
"Tags": [
{
"Key": "Name",
"Value": { "Fn::Join" : [ "-", [ "smtp-service-security-group", {"Ref": "Role"} ] ] }
},
...
],
}
},
...
"SecurityGroup": {
"Type": "AWS::EC2::SecurityGroup",
"Properties": {
...
"SecurityGroupEgress": [
{"Fn::If": ["IsOutbound", { "CidrIp": "10.243.74.0/24", "IpProtocol": "tcp", "FromPort": "9094", "ToPort": "9094" }, { "Ref": "AWS::NoValue" } ]}
],
"Tags": [
{
"Key": "Name",
"Value": { "Fn::Join" : [ "-", [ "smtp-service-security-group", {"Ref": "Role"} ] ] }
},
...
],
}
},
}
}
How can I do this in a way that does not require me to rewrite the same code for the three layers?
CodePudding user response:
You can't do that without CloudFormation {CFN) macros if you wish to use only a single template.
The usual way to deal with repetition is the use of nested stacks. This would require you to put the common code for the three services in a separate template, and them include them three times in a parent template with their specific parameters.