Passing free-form commands to Ansible using complex-args form

ansible

I'm using programatically generated Ansible playbooks. In general, because playbooks are just YAML, this is straightforward. However, when using the "simple" key=value form, playbooks aren't pure YAML — they include content embedded a shlex-parsable form.

To avoid the ambiguity in this form (is that key=value pair an argument to the command or an argument for ansible?), and have only a single format to parse and generate, I'm unconditionally using the complex args mechanism demonstrated by example in the ansible-examples repository.

This uses a syntax of the following kind:

action: module-name
args:
  key1: value1
  key2: value2

…which is well and good. However, when trying to use this form for the shell or command modules (whose documentation describes the actual command as being passed in an argument named free_form), this doesn't work so well:

action: shell
args:
  free_form: echo hello_world >/tmp/something
  creates: /tmp/something

When invoked, this runs the following:

/bin/sh -c " free_form='echo hello_world >/tmp/something'  "

…which is very much not what I'm trying to accomplish.

What's the right way to use Ansible modules taking "free-form" commands using pure YAML syntax?

Best Answer

Short answer: Don't use the command, raw, script, or shell modules. Write your own module that accepts the command as a "normal" argument.

Long answer:

In most cases, you can do this:

- shell: echo hello_world > /tmp/something
  args:
    creates: /tmp/something

However, this fails in some edge cases:

- shell: echo hello_world > creates=something
  args:
    creates: creates=something  # The file is named "creates=something"

I don't know of a general way to handle this, but a bash-specific solution is:

- shell: echo hello_world > "creates=something"
  args:
    creates: creates=something