cloudtools / troposphere

troposphere - Python library to create AWS CloudFormation descriptions
BSD 2-Clause "Simplified" License
4.93k stars 1.45k forks source link

No Example for ParameterGroups #367

Open zachzeid opened 8 years ago

zachzeid commented 8 years ago

Would be nice to see if Troposphere supports this, in particular the AWS::CloudFormation::Interface functionality in CloudFormation.

benbridts commented 8 years ago

Leaving this here, in case someone has time to test and add this to the examples:

from troposphere import Template, Parameter

t.add_metadata({
    'AWS::CloudFormation::Interface': {
        'ParameterGroups': [
            {
                'Label': {'default': 'AWS Configuration'},
                'Parameters': ['AccountId']
            },
        ],
        'ParameterLabels': {
            'AccountId': {'default': 'AWS Account Id'},
        }
    }
})

t.add_parameter(Parameter(
    'AccountId',
    Type='Number',
    Default="000000000000",
    Description="The AWS account id"
))

print(t.to_json())

IIRC, you can be a bit more clever by defining the parameter first and using paramter.title instead of AccountId.

zachzeid commented 8 years ago

I've confirmed this works, thanks!

tobiasmcnulty commented 6 years ago

I ended up creating a wrapper class for Template that lets me define the group and label at the time the parameter is added to the template, e.g.:

domain_name = template.add_parameter(
    Parameter(
        "DomainName",
        Description="The fully-qualified domain name for the application.",
        Type="String",
    ),
    group="Global",
    label="Domain Name",
)

This way, I don't have to worry about keeping the interface and the overall template in sync. Adding the code for the Template subclass below in case this is useful to anyone else and/or could be incorporated into Troposphere proper some day.

from collections import OrderedDict

from troposphere import Template

class InterfaceTemplate(Template):
    """
    Custom Template class that allows us to optionally define groups and labels for
    CloudFormation Parameters at the time they're added to the template. Groups and
    labels specified, if any, will be added to a custom AWS::CloudFormation::Interface
    """

    def __init__(self, *args, **kwargs):
        super(InterfaceTemplate, self).__init__(*args, **kwargs)
        # use OrderedDict() so we can keep track of the order in which groups are added
        self.parameter_groups = OrderedDict()
        self.parameter_labels = {}
        self.group_order = []

    def add_parameter(self, parameter, group=None, label=None):
        """
        Save group and/or label, if specified, for later generation of
        'AWS::CloudFormation::Interface' in to_dict().
        """
        parameter = super(InterfaceTemplate, self).add_parameter(parameter)
        if group:
            if group not in self.parameter_groups:
                self.parameter_groups[group] = []
            self.parameter_groups[group].append(parameter.title)
        if label:
            self.parameter_labels[parameter.title] = label
        return parameter

    def set_group_order(self, group_order):
        """
        Set an ordered list of all known, possible parameter groups in this stack.
        If none is provided, groups will appear in the order they were first passed
        to add_parameter().
        """
        self.group_order = group_order

    def to_dict(self):
        """
        Overwrite 'AWS::CloudFormation::Interface' key in self.metadata (if any)
        with the groups and labels defined via add_parameter(), and then call
        super().to_dict().
        """
        # create an ordered list of parameter groups for our interface
        ordered_groups = list(self.group_order)
        groups_in_stack = list(self.parameter_groups.keys())
        # add any groups specified in the stack that we didn't know about in advance
        ordered_groups += [g for g in groups_in_stack if g not in ordered_groups]
        # remove any groups NOT specified in the stack
        ordered_groups = [g for g in ordered_groups if g in groups_in_stack]
        # update metadata with our interface
        self.metadata.update({
            'AWS::CloudFormation::Interface': {
                'ParameterGroups': [
                    {
                        'Label': {'default': group},
                        'Parameters': self.parameter_groups[group],
                    }
                    for group in ordered_groups
                ],
                'ParameterLabels': dict([
                    (parameter, {'default': label})
                    for parameter, label in self.parameter_labels.items()
                ]),
            }
        })
        return super(InterfaceTemplate, self).to_dict()