Home > Software engineering >  How to declare multiple and similar groups of resources in the same CloudFormation template?
How to declare multiple and similar groups of resources in the same CloudFormation template?

Time:03-20

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.

  • Related