Why You Should Use YAML as YAML with Ansible

As a software engineer I require a consistent style in any configuration or code. Static analysis and style checking are very important for project health. These procedures have a place in any project.

With Ansible, parameters can be written as fully expanded YAML dictionaries or as condensed key=value pairs. It is left up to the user to decide between the styles. Working on playbooks, roles, or tasks that use both styles can be tedious compared to just using expanded YAML dictionaries.

Something that I have recently decided on is to use YAML as YAML with Ansible. I use fully expanded YAML dictionaries everywhere now. My recommendation is that you try it, too.

Here is a task written with the two different styles. The general idea is from a real role except for the fact that a floppy disk is being mounted in the example. I have no idea why this would be needed.


# Condensed Parameters
---
- name: Ensure the floppy disk is mounted.
  mount: "name=/mnt/floppy src=/dev/fd0 fstype=fuse opts=allow_other,user=nobody,nonempty dump=0 passno=0 state=mounted"
# Dictionary Parameters
---
- name: Ensure the floppy disk is mounted.
  mount:
    name: /mnt/floppy
    src: /dev/fd0
    fstype: fuse
    opts: "allow_other,user=nobody,nonempty"
    dump: 0
    passno: 0
    state: mounted

The dictionary parameters are easier to read. Of course, this is clearly a biased example. Let’s look at a task with only a few parameters and a loop:

# Condensed Parameters
---
- name: Ensure common packages are installed.
  apt: "name={{ item }} state=present"
  with_items:
    - ufw
    - tcpdump
    - git
    - python-pip
    - python-dev
# Dictionary Parameters
---
- name: Ensure common packages are installed.
  apt:
    name: "{{ item }}"
    state: present
  with_items:
    - ufw
    - tcpdump
    - git
    - python-pip
    - python-dev

It’s easier to skim over the dictionary parameters vertically, so they still win for me. Now let’s look at a ridiculously simple task:

# Condensed Parameters
---
- name: Ensure Upstart script is in place.
  template: src=my-service.conf.j2 dest=/etc/init/my-service.conf
# Dictionary Parameters
---
- name: Ensure common packages are installed.
  template:
    src: my-service.conf.j2
    dest: /etc/init/my-service.conf

It’s not clear to me if the condensed parameters buy us anything here, except a lack of syntax highlighting.


Now that I have shown several example tasks using each style…

vars:
  reasons_to_expand_yaml:
    Skimmability:
      - parameters are unlikely to extend beyond 15 columns
      - no reading horizontally
    Readability:
      - structured variables fit in nicely
      - YAML expressiveness is utilized for parameters
      - easily fit in small terminals and GitHub / BitBucket UI with no line wraps
      - syntax highlighting!
    Consistency:
      - no more mixed style
      - no more refactoring tasks after parameter list grows
    Data types:
      - your parameter will have the data type you want

tasks:
  - name: Ensure reasons I prefer expanded YAML style are listed when a user logs in.
    copy:
      dest: /etc/update-motd.d/99-reasons
      content: >
        Reasons to use the more verbose expanded YAML style:
        "{{ reasons_to_expand_yaml | to_nice_yaml }}"

Try out using pure YAML and see if your productivity increases. Mine did as soon as I didn’t have to scan through dense playbooks to find a specific parameter. Let me know what you think.