Today I learned… Ansible Include Templates

I am building Openstack Servers with the ansible os_server module. One of these fields will accept a very long string (userdata). Typically, I end up with a giant blob of unreadable build script in this field…

Today I learned that I can use this:

---
- name: "Create Server"
  os_server:
    name: "{{ item.value.name }}"
    state: present
    availability_zone: "{{ item.value.az.name }}"
    flavor: "{{ item.value.flavor }}"
    key_name: "{{ item.value.az.keypair }}"
    nics: "[{%- for nw in item.value.ports -%}{'port-name': '{{ ProjectPrefix }}{{ item.value.name }}-Port-{{nw.network.name}}'}{%- if not loop.last -%}, {%- endif -%} {%- endfor -%}]" # Ignore this line - it's complicated for a reason
    boot_volume: "{{ ProjectPrefix }}{{ item.value.name }}-OS-Volume" # Ignore this line also :)
    terminate_volume: yes
    volumes: "{%- if item.value.log_size is defined -%}[{{ ProjectPrefix }}{{ item.value.name }}-Log-Volume]{%- else -%}{{ omit }}{%- endif -%}"
    userdata: "{% include 'templates/userdata.j2' %}"
    auto_ip: no
    timeout: 65535
    cloud: "{{ cloud }}"
  with_dict: "{{ Servers }}"

This file (/path/to/ansible/playbooks/servers.yml) is referenced by my play.yml (/path/to/ansible/play.yml) via an include, so the template reference there is in my templates directory (/path/to/ansible/templates/userdata.j2).

That template can also then reference other template files itself (using {% include 'templates/some_other_file.extension' %}) so you can have nicely complex userdata fields with loads and loads of detail, and not make the actual play complicated (or at least, no more than it already needs to be!)

One thought to “Today I learned… Ansible Include Templates”

  1. Because of templates I was building in my post “Today I learned… Ansible Include Templates”, I thought you could repeat the same sections over again. Here’s a snippet of something like what I’d built (after combining lots of templates together):
    Note this is a non-working code sample!

    #cloud-config
    packages:
    - iperf
    - git

    write_files:
    – content: {% include ‘files/public_key.j2’ %}
      path: /root/.ssh/authorized_keys
      owner: root:root
      permission: ‘0600’
    – content: {% include ‘files/private_key.j2’ %}
      path: /root/.ssh/id_rsa
      owner: root:root
      permission: ‘0600’
    packages:
    – byobu
    write_files:
    – content: |
        #!/bin/bash
        git clone {{ test_scripts }} /root/iperf_scripts
        bash /root/iperf_scripts/run_test.sh
      path: /root/run_test
      owner: root:root
      permission: ‘0700’
    runcmd:
    – /root/run_test
    I’d get *bits* of it to run – basically, the last file, the last package and the last runcmd… but not all of it.
    Turns out, cloud-init doesn’t like having to rebuild all the fragments together. Instead, you need to put them all together, so the write_files items, and the packages items all live in the same area.
    Which, when you think about what it’s doing, which is that the parent lines are defining a variable called… well, whatever that line is, and if you replace it, it’s only going to keep the last one, then it all makes sense really!

    Share this:

    Click to share on Twitter (Opens in new window)
    Click to share on Facebook (Opens in new window)
    Click to share on Google+ (Opens in new window)
    Click to share on LinkedIn (Opens in new window)
    Click to share on Pocket (Opens in new window)
    Click to share on Reddit (Opens in new window)
    Click to share on Tumblr (Opens in new window)

    Because of templates I was building in my post “Today I learned… Ansible Include Templates”, I thought you could repeat the same sections over again. Here’s a snippet of something…

Leave a Reply