Templates

Template merging

StackFormation allows you to configure more than one template:

  blueprints:
    - stackname: iam
      template:
        - iam_role_jenkins.template
        - iam_user_inspector.template
      description: 'IAM users and roles'

The template files cannot have duplicate keys in any of the top level attributes. StackFormation will then merge them into a single CloudFormation template and deploy this one instead. This feature helps you to structure your template logically without having to deploy and manage them separatly. Also with this you can choose which template to include in case you’re pulling in a StackFormation module like https://github.com/AOEpeople/cfn-lambdahelper.

You can always inspect the final merged and preprocessed template:

$ vendor/bin/stackformation.php stack:template iam

Prefixed template merging

If you list your templates with attributes instead of a plain list, the attribute keys will be used to prefix every element of that template. This way you can use the same template with different input parameters instead of duplicating resources. This comes in handy for VPC setups.

blueprints:
  - stackname: vpc-subnets
    template:
      ZoneA: az.template
      ZoneB: az.template
    parameters:
      ZoneAVpc: MyVPC
      ZoneAPublicSubnetCidrBlock: '10.0.0.0/24'
      ZoneAPrivateSubnetCidrBlock: '10.0.10.0/24'
      ZoneAAZ: 'eu-west-1a'
      ZoneBVpc: MyVPC
      ZoneBAPublicSubnetCidrBlock: '10.0.1.0/24'
      ZoneBPrivateSubnetCidrBlock: '10.0.11.0/24'
      ZoneBAZ: 'eu-west-1b'
      [...]

If you have a parameter that needs to be passed to all templates you can prefix it with ‘*’ (make sure you add quotes around that key since JSON will consider this a reference instead) and StackFormation will replace ‘*’ with each prefix used in the template: section.

  blueprints:
    - stackname: vpc-subnets
      template:
        ZoneA: az.template
        ZoneB: az.template
      parameters:
        '*Vpc': MyVPC # Will automatically be expanded to 'ZoneAVpc: MyVPC' and 'ZoneBVpc: MyVPC'
        '*Igw': MyInternetGateway
        ZoneAPublicSubnetCidrBlock: '10.0.0.0/24'
        ZoneAPrivateSubnetCidrBlock: '10.0.10.0/24'
        ZoneAAZ: 'eu-west-1a'
        ZoneBVpc: MyVPC
        ZoneBAPublicSubnetCidrBlock: '10.0.1.0/24'
        ZoneBPrivateSubnetCidrBlock: '10.0.11.0/24'
        ZoneBAZ: 'eu-west-1b'
        [...]

Inject Parameters

The scripts (included via Fn::FileContent) may contain references to other CloudFormation resources or parameters. Part of the pre-processing is to convert snippets like {Ref:MagentoWaitConditionHandle} or {Ref:AWS::Region} or {Fn::GetAtt:[resource,attribute]} (note the missing quotes!) into correct JSON snippets and embed them into the Fn::Join array.

Usage Example:

#!/usr/bin/env bash
/usr/local/bin/cfn-signal --exit-code $? '{Ref:WaitConditionHandle}'

will be converted to:

{"Fn::Join": ["", [
"#!\/usr\/bin\/env bash\n",
"\/usr\/local\/bin\/cfn-signal --exit-code $? '", {"Ref": "WaitConditionHandle"}, "'"
]]}

Usage Example:

#!/usr/bin/env bash
EIP="{Fn::GetAtt:[NatIp,AllocationId]}"

will be converted to:

{"Fn::Join": ["", [
"#!\/usr\/bin\/env bash\n",
"EIP=\"",
{
    "Fn::GetAtt": [
        "NatIp",
        "AllocationId"
    ]
},
"\"\n",
]]}

Include file content

You can include content from a different file into a script. Use this is you have duplicate code that you need to embed into multiple resource’s UserData:

Example:

  #!/usr/bin/env bash

  ###INCLUDE:../generic/includes/base.sh
  [...]

Inject raw Json

###JSON###
{ "hello": "world" }
######

Using composer

You can pull in StackFormation modules via composer. Look at the cfn-lambdahelper for an example. A custom composer installer (configured as require dependency) will take care of putting all the module files in your blueprints/ directory. This way you can have project specific and generic modules next to each other.

Please note that a “StackFormation module” will probably not come with a blueprints.yml file since this (and especially the stack parameter configuration) is project specific.

You will need to create the stack configuration for the parts you want to use. A good place would be blueprints/blueprints.yml where you reference the imported module.

Example:

  blueprints:
    - stackname: 'lambdacfnhelpers-stack'
      template: 'cfn-lambdahelper/lambda_cfn_helpers.template'
      Capabilities: CAPABILITY_IAM

Comments

You can add comments to your JSON file. Due to a current bug you can’t have double quotes in your comment block.

Example:

{"IpProtocol": "tcp", "FromPort": "80", "ToPort": "80", "CidrIp": "1.2.3.4/32"}, /* Office */
{"IpProtocol": "tcp", "FromPort": "80", "ToPort": "80", "CidrIp": "5.6.7.8/32"}, /* Max Musterman HomeOffice */

Port

"Port":"..." will automatically expanded to "FromPort": "...", "ToPort": "...". So if you’re specifying a single port instead of a range of ports you can reduce the redundancy:

Example:

{"IpProtocol": "tcp", "Port": "80", "CidrIp": "1.2.3.4/32"},

/* expands to: */
{"IpProtocol": "tcp", "FromPort": "80", "ToPort": "80", "CidrIp": "1.2.3.4/32"},

Expand strings with {Ref:…}

Tired of concatenating strings with {"Fn::Join": ["", [ manually? Just add the references in a string and StackFormation will expand this for you:

Example:

"Key": "Name", "Value": "magento-{Ref:Environment}-{Ref:Build}-instance"

/* will be replaced with: */
"Key": "Name", "Value": {"Fn::Join": ["", ["magento-", {"Ref":"Environment"}, "-", {"Ref":"Build"}, "-instance"]]}