Ansible configuration exists in YAML files which look static but are deceptively dynamic: values in these files get parsed as static values, Python expressions, or Jinja2 templates that may themselves contain Jinja2 filters and Python snippets. This mish-mash of conceptual models leads to awkward syntax that requires careful quoting and a constant mental effort to separate each of the three layers (static YAML, Jinja2 interpolation/filtering, and Python evaluation).
Some example sources of discomfort:
- The context-dependent need to quote Jinja2 syntax interpolation ({{ ... }}) in some places but not others, due to conflict with YAML syntax rules.
- Undifferentiated mixing of Python evaluation (eg. list concatenation with +) and Jinja2 filtering/transformtion (eg. `| zip` and `| list`) in a single value.
- Awkward encoding of imperative programming patterns using YAML keys (eg. `loop` and `loop_control` to describe a loop; `when` to describe a conditional).
- Context-specific embedding of Python expressions (eg. raw Python code being passed as a string in the `when` property, but elsewhere being interpolated in Jinja2 interpolation).
- Implicit/magical variable naming conventions (eg. use of loop implies the existence of an `item` variable).
- No obvious scoping rules eg. variables magically available with no obvious source (they are defined in another file, or defined in a prior task).
That's from an example I wrote up over on my dotfiles repo:
https://github.com/wincent/wincent/tree/master/fig#on-ansibl... - I used Ansible happily enough for years there, but concluded that at least on a little toy project like that it was more fun/pleasant/simpler to just embody the imperative, procedural work in an actual programming language. I still use Ansible in other places, to manage remote hosts, but I can't shake off the discomfort I feel about how it uses YAML.