Trying to pass parameters from Master to child template

13,037

Solution 1

The issue is that when using Ref on a CommaDelimitedList, you pass a list, so it passes "[a,b,c]" rather than "a,b,c"

So on the master template I've used Join(",") to pass lists, and Join(" ") to pass strings. This way they're Referenced as "a,b,c" and " String"

On the child template I've set the parameters as CommaDelimitedLists for the lists and String from the String. This is because of the way they're being passed from the master template.

I then used Ref on the Child template without the use of Join() on the child template. This created the CommaDelimitedLists into Lists and the Strings that were joined with Join(" ") passed as strings.

Here is my parameter declaration on the master Template.

"Parameters": {
    "ELBSubnets": {
        "Default": "subnet-5d8fea67,subnet-3e35cf15",
        "Type": "CommaDelimitedList"
    },
    "LCKeyPair": {
        "Default": "key-master",
        "Type": "CommaDelimitedList"
    },
    "LCSecurityGroups": {
        "Default": "sg-10a15c74,sg-880e5fec",
        "Type": "CommaDelimitedList"
    }
},

Here is how I used Join(","), Join(" ") and Ref. Since Ref converts the CommaDelimitedLists into Lists, I used Join(",") to keep them as CommaDelimitedLists and pass them on to the child template.

As for the KeyPair String, I made sure that it was declared as a CommaDelimitedList on the parent template and joined with Join(" "), this effectively made it into a string when referencing to the child template.

            "Parameters": {
                "ELBSubnets": {
                    "Fn::Join": [
                        ",",
                        {
                            "Ref": "ELBSubnets"
                        }
                    ]
                },
                "LCKeyPair": {
                    "Fn::Join": [
                        " ",
                        {
                            "Ref": "LCKeyPair"
                        }
                    ]
                },
                "LCSecurityGroups": {
                    "Fn::Join": [
                        ",",
                        {
                            "Ref": "LCSecurityGroups"
                        }
                    ]
                }
            },

On the child template, they're declared like so.

 "Parameters": {
    "ELBSubnets": {
        "Type": "CommaDelimitedList"
    },
    "LCKeyPair": {
        "Type": "String"
    },
    "LCSecurityGroups": {
        "Type": "CommaDelimitedList"
    }
},

And they are all referenced normally without the use of Join on the child template.

Subnets": {
                "Ref": "ELBSubnets"
            }

There could have been many different ways to do this. I could have had the joins on the child template rather than the parent template. However I prefer to keep one template complicated and the rest as clean as possible. Hope this helps others down the line.

I also should have been able to pass the list as a list on the child template, however then I received the error "Unknown parameter type: List"

Solution 2

CommaDelimtedList can only be parsed once, so in the parent template, you need to set the type as "String", then in the child template, keep it as CommaDelimitedList, as before.

The problem is happening, because CloudFormation is parsing the CommaDelimitedList in the parent template, and then it's trying to parse it again in the child template.

Share:
13,037

Related videos on Youtube

Mo Ali
Author by

Mo Ali

Updated on June 04, 2022

Comments

  • Mo Ali
    Mo Ali almost 2 years

    I'm trying to pass list parameters from master to child template, however I'm running into two errors.. These are my current parameters on the master template.

    "Parameters": {
        "ELBSubnets": {
            "Default": "subnet-5d8fea67,subnet-3e35cf15",
            "Type": "CommaDelimitedList"
        },
        "LCKeyPair": {
            "Default": "key-master",
            "Type": "String"
        },
        "LCSecurityGroups": {
            "Default": "sg-10a15c74,sg-880e5fec",
            "Type": "CommaDelimitedList"
        }
    },
    

    They are being referenced in this method on the same template when passing on to the child template.

        "ChildTempate1": {
            "Properties": {
                "Parameters": {
                    "ELBSubnets": {
                        "Ref": "ELBSubnets"
                    },
                    "KeyPair": {
                        "Ref": "LCKeyPair"
                    },
                    "LCSecurityGroups": {
                        "Ref": "LCSecurityGroups"
                    }
                },
    

    On the child template, they are declared exactly the same.

    "Parameters": {
        "ELBSubnets": {
            "Type": "CommaDelimitedList"
        },
        "LCKeyPair": {
            "Type": "String"
        },
        "LCSecurityGroups": {
            "Type": "CommaDelimitedList"
        }
    },
    

    And they're being referenced in this method in the child template.

                "KeyName": {
                    "Ref": "LCKeyPair"
                },
                "SecurityGroups": {
                    "Fn::Join": [
                        ",",
                        [
                            {
                                "Ref": "LCSecurityGroups"
                            }
                        ]
                    ]
                }
            },
    

    This is another part of the template.

                "Subnets": {
                    "Fn::Join": [
                        ",",
                        [
                            {
                                "Ref": "ELBSubnets"
                            }
                        ]
                    ]
                }
            },
    

    When I attempt to use the fn::join on the master template, it says

    "Template validation error: Template error: every Fn::Join object requires two parameters, (1) a string delimiter and (2) a list of strings to be joined or a function that returns a list of strings (such as Fn::GetAZs) to be joined."

    When I don't use fn::join on the master template the error is

    Value of property Parameters must be an object with String (or simple type) properties

    Regardless of whether I have fn::join on the same parameters in the child template.

    Both templates can be found here: https://github.com/slimg00dy/Troposphere-CloudformationTests

  • Mo Ali
    Mo Ali almost 9 years
    I've done as you say. I've removed all of the "Join" as you stated, they're not needed, which is what I thought in the beginning. However I get "Value of property Parameters must be an object with String (or simple type) properties" again. I've removed them on the child template and on the master template.
  • phobologic
    phobologic almost 9 years
    Ok - is that happening when you try to compile the template, or when you try to push it into CloudFormation?
  • phobologic
    phobologic almost 9 years
    Did some quick testing with the code in your repo, and I can see that the template compiles fine, so this is an issue w/ CloudFormation. Give me a bit, I'll try and reproduce an environment that I can successfully launch these templates in, and then I'll see if I can figure out what the issue is here.
  • phobologic
    phobologic almost 9 years
    Ok, I've messed with it a bit, and I've gotten you past some of your initial issues - you can see my diff from your source here: gist.github.com/phobologic/d4e4368e4befa0121240 Now, it still fails, but this error is a lot easier to deal with: Template error: Mapping named 'AMIMap' is not present in the 'Mappings' section of template. That was on the creation of the Child Template - you most likely just need to clean up some mis-named values. Remember - the name of the Resource & the python object name can be different things. See the change I made dealing with your KeyPair.
  • phobologic
    phobologic almost 9 years
    BTW, the key to the 'when to use Join' bit here is that when the Parent accepts a Parameter as a CommaDelimitedList, it converts the Parameter into a List. When you pass that to the child it gets the list- but the parameter expects a CommaDelimitedList, which is a String that it can turn into the list - so the Parent needs to re-join the list to pass it on. It's all pretty confusing, and honestly it's one of many reasons I've stopped using Nested stacks, and instead try to keep my stacks separate as far as CloudFormation is concerned, but deal with relationships with code (like stacker).
  • Mo Ali
    Mo Ali almost 9 years
    Thank you so much. You've pointed me in the right direction with the parameter CommaDelimitedList. I've set the parent to CommaDelimitedList for all of them and when sending them to the child template I've used Join(",") to keep them a CommaDelimitedList because Ref changes them to a list, which for some reason I cannot pass. So I've changed the child template parameters to CommaDelimitedLists too for all but KeyPair. Originally on the master template I joined the KeyPair parameter with Join(" ") that was able to pass as a string rather than a list when using Ref.
  • Nigel Sheridan-Smith
    Nigel Sheridan-Smith over 7 years
    This worked for me, but only when creating two new stacks (not using an existing one specified by template S3 URL) and/or when the default value was not set on the child template. Might have to refer it to AWS as a bug.