3 Merging
Adam Pippin edited this page 3 years ago

Merging

cfnpp adds the ability to have multiple YAML files merged to provide a sort of inheritance/re-use.

How Merging is Performed

Given a list of YAML files, cfnpp will start from a blank document and merge each one into it one by one until we're done and have our final result. The merging follows a pretty simple set of rules:

  • Scalars overwrite scalars.
  • Arrays are merged.
  • If a key doesn't exist in the current document or its type is different than the target, it is overwritten.

That is, given:

Resources:
    MyResource:
        Name: BestResource
        VpcId: vpc-1234
        AvailabilityZones:
            - us-east-1a
            - us-east-2a
        AllowedPorts:
            - 80
            - 443

and merging in:

Resources:
    MyResource:
        Name: NewBestResource
        AvailabilityZones:
            - us-east-1c
        AllowedPorts: False
        EnableSuperSecurity: True

will result in:

Resources:
    MyResource:
        Name: NewBestResource
        VpcId: vpc-1234
        AvailabilityZones:
            - us-east-1a
            - us-east-1b
            - us-east-1c
        AllowedPorts: False
        EnableSuperSecurity: True

Dependencies

In a YAML file, you can add a cfnpp block to make use of some of this additional functionality. The stack property under that can be an array of additional YAML files (with paths relative to the current file). Each file referenced will be merged before the current document in the order given. Each of those included documents will also have its dependencies examined and those merged before the included documents (with paths relative to those documents), and so on.

Specify the documents your stack depends on in the cfnpp block of your file:

cfnpp:
    stack:
        - ../base.yaml
        - ../component/test.yaml
        - app_base.yaml

Under the Hood

All of your stack's dependencies will be loaded, their dependencies loaded, etc until we reach files with no more dependencies. All of these relationships are then built into a dependency graph which is then flattened to retrieve the actual processing order. This guarantees that all of your declared dependencies will be processed before your stack is processed in the order you have provided them, but it does not guarantee that other stacks won't be processed in there as well which may also mutate them.

Each stack referenced will only be processed once per invocation, so if multiple stacks all depend on the same parent stack, that parent stack will be processed exactly once before all of the depending stacks are processed.

Functions

Sometimes the default merge rules aren't exactly what you want, so additional functions exist to allow you to control the merging process in a more fine-grained manner.

!replace

This signals that this node in the stack should be completely replaced by the value given, and no attempt should be made to merge or otherwise retain the value from the base stack.

That is, given:

Resources:
    MyResource:
        Name: My Resource
        AvailabilityZones:
            - us-east-1a
            - us-east-1b
        Users:
            Alice: 'aws:arn:1234:alice'
            Bob: 'aws:arn:1234:bob'

and merging in:

Resources:
    MyResource:
        AvailabilityZones: !replace
            - us-east-1c
        Users: !replace
            Charlie: 'aws:arn:1234:charlie'

will result in:

Resources:
    MyResource:
        Name: My Resource
        AvailabilityZones:
            - us-east-1c
        Users:
            Charlie: 'aws:arn:1234:charlie'