<< Blog Index

Jinja and YAML
February 6, 2023

You'd think that Jinja2 templates for Python would be a little more friendly towards markup that enforces spaces, e.g. YAML.

I was working with AWS CloudFormation templates today and wanted to do some convenient things like this:

AWSTemplateFormatVersion: "2010-09-09"
Resources:
  {% include "ec2-iam-profile.yml" %}
  {% include "ec2-iam-role.yml" %}

  ec2:
    Type: "AWS::EC2::Instance"
    Properties:
      AvailabilityZone: "us-gov-west-1a"
      ImageId: "{{ami_latest}}"
      InstanceType: "t3a.micro"

      Tags:
      {% include "ec2-std-tags.yml" %}
      # Can add additional tags here! Much more flexible
      than a YAML custom loader.

      BlockDeviceMappings:
      - DeviceName: "/dev/sda1"
        Ebs:
          DeleteOnTermination: "true"
          VolumeType: "gp3"

      {% include "ec2-network-interface.yml" %}

The main issue is that the default "include" will not preserve indentation, so that breaks the YAML. Luckily, Jinja2 provides an "indent" filter that can add indentation when including things. You can wrap your includes with that to have proper indentation.

{% filter indent(6) %}
{% include "ec2-network-interface.yml" %}
{% endfilter}

Having to specify the indentation amount is cumbersome though, and, to be honest, it looks quite ugly. So, I added a little magic to my host program. I simply look for include tags and then copy the indentation on the left, passing that to the indent filter.

def filterer(m):
   return f"{m[1]}{{% filter indent('{m[1]}') %}}{m[2]}{{% endfilter %}}"

template = re.sub(r"^( *)({%\s*include.*?%})", filterer, template, flags=re.MULTILINE)

Great! It's simple and effective. Now I can build YAML structures with all the fun features of a powerful templating engine and reusable subsections.

Another option is using JSON for the CloudFormation templates, since that doesn't require indentation, but, honestly, I hate the commas.

<< Blog Index