The Problem
Whenever I'm building a server or simple local infrastructure for testing code, I start by looking at the available ansible roles, hoping to find one that JustWorks for my use-case right out of the box. One thing that annoys me though is that even for roles without extra variables, a playbook is still required. Sure it's a small file, but creating it still annoys me when I'm just testing stuff.
Typically these one-off playbooks look something like this:
- hosts: all
become: yes
become_method: sudo
roles:
- {role: username.rolename}
There's a regular need for "anonymous roles", ie role application without using playbooks, even if only for simple use-cases.
Prior Art
There's already a few projects that work around this, but none of them fit my use-case exactly (see the spec below).
All implementations seem to agree with my conclusion on one point though: while it's very attractive to consider using the ansible API directly (if only to have a practical task in mind while you get to know the API), doing this is awkward. The simplest approach is to just create a temporary playbook.yml
file, then run an appropriate ansible-playbook
command on that file and get rid of it afterwards.
The Specification
I specifically wanted the following behaviour from the missing ansible-role
command:
-
The
ansible-role
invocation should respect data intended for theansible-playbook
command, i.e.ANSIBLE_*
shell variables should be passed through, and the unparsed aspect of theansible-role
command line also should be passed through toansible-playbook
. -
Despite pass-through, the
ansible-role
command should also parse and understand the --module-path=FOO argument if it is given, and by default look for roles insideFOO/roles
. -
In the event no matching role is available locally then the role should be downloaded for me automatically using the
ansible-galaxy
command. -
In case a galaxy download is necessary and
--modulepath=FOO
is specified, it is downloaded toFOO/roles
and not cleaned afterwards. File-system caching makes sense here because theansible-role
invocation is supposed to be quick and easy, but the usage of the role itself is less experimental. -
In case a galaxy download is necessary and
--modulepath
is NOT given, then there is no caching. File system cleaning for the role download makes sense here because this is a one-off experiment and it's no use cluttering the file system. Thus the role will be downloaded to a temporary directory and will be deleted afterwards (regardless of whether the application of the role succeeds). -
Apart from the standard
ansible-playbook
arguments, theansible-role
command line understands exactly 2 other arguments: the primary positional argumentrole_name
(which is required and which specifies an ansible-galaxy role), and thehost
argument (which defaults tolocalhost
). Again, everything else on the command line will be passed through to theansible-playbook
invocation. -
After cleanup, the
ansible-role
command should return the same exit code as the impliedansible-playbook
invocation. -
Finally, the
ansible-role
command should be available on pypi so it can be installed with pip.
Examples
Most of the requirements above are straightforward, but let's look at a few examples. The invocation ansible-role username.rolename --module-path ./ansible --become
will download the role if it doesn't exist, and result in the command ansible-playbook --module-path ./ansible --become $random_tmp_playbook.yml
, and the role will not be cleaned. The temporary playbook will be cleaned after it is used, and the contents of it are:
- hosts: localhost
roles:
- {role: ./ansible/roles/username.rolename}
The invocation ansible-role username.rolename test_server
will definitely download the role, and result in the command ansible-playbook $random_tmp_playbook.yml
, and the role will be deleted after it is used. The temporary playbook will also be cleaned after it is used, and the contents of the playbook are:
- hosts: test_server
roles:
- {role: $some_tmpdir/roles/username.rolename}
Overrides
If you need to override variables using this approach to "anonymous" roles, then honestly it might be time to consider writing and maintaining an actual playbook file. But if you insist, then you can always take advantage of ansible variable preference precedence and pass --extra-vars "var=val"
to your ansible-role
command.