Parsing playbooks with full Jinja templating

Hi Ansible developers,

I’ve been wondering about something. Ansible’s syntax for module arguments is key=value parameters, all on a single line. This then needs to be parsed, and the key=value elements properly separated.

Wouldn’t it be much nicer if the module parameters were also just written in YAML syntax? For example:

  • hosts: all
    tasks:
  • name: blah
    yum:
  • name: package
  • state: installed

And it might even be an idea to pass the playbook through Jinja first, allowing things like:

  • name: blah
    yum:
    {% for pkg in packages %}
  • name: “{{ pkg }}”
  • state: installed
    {% endfor %}

We would not need the “when:” conditional, or the shlex module to split key=value pairs. This would allow taking full advantage of Jinja’s templating features.

Could this be an idea for Ansible 2.0?

Regards,
Anand

Hi Anand

You actually can (and I prefer to) write your playbooks with that kind of plain YAML syntax. e.g.

- hosts: localhost
  tasks:
    - name: blah
      yum:
        name:
          - mypackage
          - otherpackage
        state: present
    - name: blurh
      copy:
        src: myfile
        dest: /tmp/myfile
        mode: 0640
        owner: me

It gets more interesting with the shell and command modules

- hosts: localhost
  tasks:
    - name: run this
      shell: mycommand
      args:
        creates: /tmp/artefact
        chdir: /path/to/dir
        executable: /bin/bash

Thanks Tom! I didn’t realise it was already possible! So I can also use Jinja functions in a playbook to include/exclude some parameters based on facts?

Same here. Otherwise I've found sometimes I have to decide which one to
use, where to put quotes, etc. I just go 100% YAML and the INI files.

Ansible's syntax with the equal signs is more concise but I don't mind
the extra lines with YAML.

Giovanni

Hi Tom,

I just tried the syntax you suggested.

My very simple playbook is:

  • hosts: myhost
    tasks:
  • name: install packages
    yum:
    name:
  • screen
  • tmux
    state: installed

However, when I run this, I get an errors:

SUDO-SUCCESS-ziubbkxxzglhrtsmdmekoixffubflanc
Traceback (most recent call last):
File “/tmp/ansible-tmp-1422870182.84-7050423984136/yum”, line 2402, in
main()
File “/tmp/ansible-tmp-1422870182.84-7050423984136/yum”, line 832, in main
disablerepo, disable_gpg_check)
File “/tmp/ansible-tmp-1422870182.84-7050423984136/yum”, line 717, in ensure
items = pkgspec.split(‘,’)
AttributeError: ‘list’ object has no attribute ‘split’

Are you certain that plain YAML syntax works for modules?

- hosts: myhost
  tasks:
    - name: install packages
      yum:
        name: "{{ item }}"
        state: latest
      with_items:
        - screen
        - tmux

Useful reading:
http://docs.ansible.com/playbooks_loops.html

Giovanni

Hi Giovanni,

Your suggestion works. But I think what I was trying to say is that Ansible’s playbook syntax is mixed. Wouldn’t it be cool if it were pure YAML, with Jinja templating? Then it would allow something like:

  • hosts: myhost
    tasks:
  • name: install packages
    yum:
    name:
  • tmux
  • screen
    {% if ansible_distribution_major_version == ‘6’ %}
  • lsof
    {% endif %}

Instead of inventing ansible-specific loops, we could just use the power of Jinja templating.

As an Ansible newbie, I have struggled with understanding where it's
Ansible or Jinja2 doing the parsing. So I can surely related to that. On
the other hand, Ansible tries to be simple and the looping constructs,
IMHO, are easy to work with and have a concise syntax.

I don't have enough experience with Ansible to suggest where it should
go next.

In my so far very limited knowledge of Ansible, I would suggest
encapsulating these things in different roles (e.g. common_debian,
common_centos6, common_centos7). It seems more idiomatic in Ansible,
based on the examples I've seen so far.

Giovanni

Hi Anand

Are you sure your indenting is correct? Here’s an example from one of my role task files, known working with Ansible v1.8.2 (packages trimmed for brevity)

---
- name: Install packages (apt)
  apt:
    pkg:
      - bash-completion
      - bind9utils
      - vim-nox
      - zsh
    state: present
  when: ansible_os_family == 'Debian'

- name: Install packages (yum)
  yum:
    name:
      - bind-utils
      - byobu
      - vim-enhanced
      - zsh
    state: present
  when: ansible_os_family == 'RedHat'

Jinja templating doesn’t work in playbooks in that way. Lots of variable constructs are actually parsed with jinja2, so you can do stuff like

- set_fact:
    myvar: "{% if something %}this{% else %}that{% endif %}"

As Giovanni mentioned, there are numerous looping constructs for things like task repetition. Playbooks are declarative, so it wouldn’t really be right to build them from a template.

Hello Tom,

I am certain that my syntax and indentation is correct. I have created a test role, called packages, and in the main.yml file of the tasks directory of this role, I have:

As an Ansible newbie, I have struggled with understanding where it's

Ansible or Jinja2 doing the parsing. So I can surely related to that. On
the other hand, Ansible tries to be simple and the looping constructs,
IMHO, are easy to work with and have a concise syntax.

Hi Giovanni,

My thinking is similar. Ansible playbooks are parsed in a strange way. Some
parts of it are plain YAML, whereas other parts are run through the Jinja2
templating engine. This is confusing.

I was reading Salt documentation, and saw that Salt's state files are just
plain YAML, but the entire YAML file is passed through Jinja2, so that any
loops and other variable things are done by Jinja2, and this is
conceptually neater than Ansible's current mix of Jinja2 and Ansible's own
declarative language.

Hi Tom,

Jinja templating doesn’t work in playbooks in that way. Lots of variable
constructs are actually parsed with jinja2, so you can do stuff like

- set_fact:
    myvar: "{% if something %}this{% else %}that{% endif %}"

As Giovanni mentioned, there are numerous looping constructs for things
like task repetition. Playbooks are declarative, so it wouldn’t really be
right to build them from a template.

I understand this, and it is exactly what I am trying to discuss here. Why
can't one build a playbook from a template? I think plain YAML is just fine
for declarative statements. Or am I missing a use case?