It’s been ages since I posted anything, so here’s something that is probably not very useful. OpenStack Heat has reosurce groups which allow you to stamp out the same resource multiple times. Better yet, it provides a magic token %index% by default for which copy of a resource your template is run for.

But %index% doesn’t get substituted by intrinsic functions. This unfortunately means you can’t use it in really obvious places like str_replace or use it to lookup a list by index with get_param. This would be useful for figuring out which AZ we want to launch an instance in by being passed a list of AZs and mangling the index from the resource group.

However, all is not lost. There’s a way to do this. You just need to use nested stacks.

Nested stacks in Heat allow you to define a psuedo-resource which is itself another Heat stack, complete with parameters. Effectively, the properties of the resource become paramaters in the nested stack.

For example, we can write:

main.yaml:

resources:
  NestedStack:
    type: innerstack.yaml
    properties:
      some_param: value1
      another_param: value2

innerstack.yaml:

 
parameters:
  some_param:
    type: string
  another_param:
    type: string

resources:
  SomeResource:
    type: OS::Nova::Server
    properties:
       flavor: { get_param: some_param }
       image: { get_param: another_param }
       [...]

In this case, when we call the child stack we are not just doing a simple #include-like practice, but creating an new child stack with parameters and reosurces like any other stack. The parameters are passed to it from the parent stack.

Obvious use cases for this include having a canoncial version of what say an app server looks like, and passing in just the variables you need for a specific implementation. A canonical version might have metadata that must be applied, and you can turn those into parameters passed into the child stack.

Why is this useful beyond just a standard version of a resource? Well Heat includes an interator to stamp out multiple copies of a resource from a single resource definition in the template.

The resource type is OS::Heat::ResourceGroup, and it accepts a resource to stamp out, in the same format as a ‘top level’ resource (or whatever you want to call them). It has %index% by default that will be substituded for the index into the count you provide. So:

parameters:
  Hostnames:
    type: comma_delimited_list
    default: "first,second,third,forth,fifth"

resources:
  MyResourceGroup:
    type: OS::Heat::ResourceGroup
    properties:
      count: 5
      template:
        type: OS::Nova::Server
        properties:
          flavour: abcd1
          namea: { get_param [ Hostnames, '%index%' ] }

Oh, but THIS DOES NOT WORK. You can’t use %index% in the function like that. You can use it as literal string-sub values, but not inside a function. There’s a bug which discusses this problem.

Here come nested stacks. Instead of stamping the actual resource, you stamp out a nested stack, and can pass the index (and say, in the example above, the list which is being indexed) into the child stack:

main.yaml:

parameters:
  count:
    type: number
  names:
    type: comma_delimited_list
resources:
  ServerCluster:
    type: OS::Heat::ResourceGroup
    paramters:
      count: { get_param: count }
      template:
        type: appserver.yaml
        properties:
          names: { get_param: names }
          index: '%index%'

appserver.yaml:

parameters:
  names:
    type: comma_delimited_list
  index:
    type: number
resources:
  AnAppServer:
    type: OS::Nova::Server
    properties:
      flavor: abcd1
      name: { get_param [ names, index ] }
      [...]

This can be used to index any list as a paramter to the parent stack, and it’s a useful trick for reducing the number of resources that need to be defined, and provides some flexibility.

In another post, I’ll discuss how to abuse this to assign servers to availability zones in a balanced distribution. Hint: YAQL.