Sending SecurityGroupIngress to nested cloudformation stack

amazon-cloudformationamazon-web-services

Not sure if here or SO is the better place to ask this…

I'm trying to split up our CloudFormation templates to make them more usable and smaller.

I've hit an issue where I wish to use a "basic" security group template that is essentially empty, then reference that along with parameters for each security group I need to make.

My issue comes with filling in the "SecurityGroupIngress/Egress" parts, as these contain json arrays, and as far as I can see, you can only pass in strings or numbers via parameters.

Here's an example;

Parent Stack:

{
    "AWSTemplateFormatVersion": "2010-09-09",
    "Description": "CloudFormation template to create all Security Groups",  
    "Resources": {
        "CommonSecurityGroupStack" : {
            "Type" : "AWS::CloudFormation::Stack",
            "DependsOn": [
            ],
            "Properties" : {
                "TemplateURL" : "https://s3.template.url/template.name",
                "TimeoutInMinutes" : "60",
                "Parameters": {
                    "VPC" : { "Ref": "VPC" },
                    "VpcCidrRange": { "Ref": "VpcCidrRange" },
                    "SecurityGroupIngress": { "Something here" },
                    "SecurityGroupName": "CommonSecurityGroup"
                }
            }
        }     
    }
}

Nested Stack:

{
    "AWSTemplateFormatVersion": "2010-09-09",
    "Description": "CloudFormation template to create a security group",
    "Parameters": {
        "VPC": {
            "Description": "Name of the VPC",
            "Type": "String"
        },
        "GroupDescription": {
            "Description": "Description of Security Group",
            "Type": "String"
        },
        "SecurityGroupIngress" : {
            "Description": "List of rules for the Security Group Ingress"
        },
        "VpcCidrRange": {
            "Description": "CIDR IP range",
            "Type": "String",
            "MinLength": "9",
            "MaxLength": "18",
            "Default": "0.0.0.0/0",
            "AllowedPattern": "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2})",
            "ConstraintDescription": "must be a valid CIDR range of the form x.x.x.x/x."
        },
        "SecurityGroupName": {
            "Description": "Name of the Security Group",
            "Type": "String"
        }
    },
    "Resources": {
        "SecurityGroup": {
            "DependsOn": [],
            "Type": "AWS::EC2::SecurityGroup",
            "Properties": {
                "GroupDescription": { "Ref": "GroupDescription" },
                "VpcId": {
                    "Ref": "VPC"
                },
                "SecurityGroupIngress": {"Ref" : "SecurityGroupIngress"},
                "Tags": [
                    {
                        "Key": "Name",
                        "Value": { "Ref": "SecurityGroupName" }
                    }
                ]
            }
        }
    }
}

I need a way of passing in something to the equivalent of the following, into the stack as a parameter to fill in the SecurityGroupIngress property.

[
    {
        "IpProtocol": "tcp",
        "FromPort": "22",
        "ToPort": "22",
        "CidrIp": {
          "Ref": "VpcCidrRange"
        }
    },
    {
        "IpProtocol": "tcp",
        "FromPort": "443",
        "ToPort": "443",
        "CidrIp": {
          "Ref": "VpcCidrRange"
        }
    }
]

Best Answer

Unfortunately, CloudFormation is not that sophisticated. You're limited on the data types that can be used as parameters:

  • Strings
  • Comma separated list of strings
  • Various AWS resource types, none of which are ingress rules

Instead, try the following:

  • Add parameters to include/exclude your various ingress rules,
  • Move the ingress rules into the nested stack as AWS::EC2::SecurityGroupIngress resources, and
  • Use conditions based on your parameters to include/exclude the various ingress rules

References:

Related Topic