Turn Your Ansible Playbook into a Bash Command

In one of the previous blog posts I described the playbook I use to collect SSH keys from network devices. As I use it quite often, it became tedious to write ansible-playbook path-to-playbook every time I wanted to run the collection process.

Ansible playbooks are YAML documents, and YAML documents use # to start comments, so I thought “what if I’d use a YAML comment to add shebang and turn my YAML document into a script

TL&DR: It works. Now for the longer story…

Bash (or any other shell) will try to execute a file that has execute bit set. Use chmod +x playbook to make the playbook executable.

Text files with execute bit set are assumed to be shell scripts unless the first line in the file starts with #! sequence which specifies the absolute path to the script interpreter to use.

You can use which ansible-playbook to find path to ansible-playbook command on your system and add that as the first line of your playbook:

#!/usr/local/bin/ansible-playbook

It’s even better to use env command. It’s (almost) always in /usr/bin directory and the system call it uses to transfer control to the desired interpreter (execvp) uses PATH environment variable to find the command you want executed. The first line of your Ansible playbook should therefore be:

#!/usr/bin/env ansible-playbook

Cherry on the cake: add a symbolic link to your playbook into one of the directories in your search path. For example:

$ ln –s /vagrant/tools/ssh-keys/get-keys.yml ~/bin/get-ssh-keys

Now you can execute your Ansible playbook like any other Linux command.

No idea what I’m talking about? Check out the Ansible for Networking Engineers webinar and online course.

Adding Environment Variables

As Brian Coca explained in a comment, you have to use -S env parameter to add environment variables to the shebang line. When executing your script, the shell passes the rest of the shebang line as a single argument to the interpreter (env), and without the -S parameter env dutifully passes that argument to execvp as the file name to be executed.

-S parameter splits the rest of the string into multiple arguments making it all work:

#!/usr/bin/env -S ANSIBLE_STDOUT_CALLBACK=dense ansible-playbook

Revision History

2022-02-17
Added adding environment variables section

5 comments:

  1. export ANSIBLE_STDOUT_CALLBACK=dense; ansible-playbook [options]
    Replies
    1. Yeah, sure... have you tried adding that to the #! line? Did it work? If it did, under which shell?
  2. I'd actually preffer making aliases in bashrc. Makes it easier to manage and move around instead of creating loads of symlinks.
    Just add this:
    alias get-ssh-keys="/vagrant/tools/ssh-keys/get-keys.yml"
    inside ~./bashrc
    Reload bash by typing source ~./bashrc

    You could also be creative and make a functions and such
  3. Ivan - aren't we now moving the "CLI"[-like] approach, upstream (the one we are just trying to depart, via the more structured and robust approach of RESTAPI), to the Bash shell, by maintaining more complex scripts?

    Who is / how are we going to fix the portability of Bash vs other shells (e.g. ksh, zsh) when taking scripts and move them across *nix platforms, or with diff path(s) and env, troubleshoot shell exit codes, etc.? Or am I misunderstanding the intent here?
    Replies
    1. I would recommend anyone believing in the magic of whatever replacing CLI to read this (in particular #2):

      http://queue.acm.org/detail.cfm?id=1921361

      As for portability of shells, sysadmins might be yammering about many things, but the good ones seem to be doing a decent job:

      http://highscalability.com/blog/2013/11/19/we-finally-cracked-the-10k-problem-this-time-for-managing-se.html

      Obviously people who meet the requirements of RFC 1925 Rule #4 will tell you CLI is dead. Let's revisit this in 10 years ;)

    2. Didn't mean to deviate from the main topic too much, but when looking at some of the CLIs just front-ending RESTAPIs, I wonder if "survival" of CLI isn't just in the eyes of the beholder.
  4. This link explains the reason why the environment variable doesn't work in the shebang:

    https://stackoverflow.com/a/10217307

    Anyway, you can add that variable permanently to your shell http://www.linuxfromscratch.org/blfs/view/8.0/postlfs/profile.html:

    export ANSIBLE_STDOUT_CALLBACK=dense

    or run the script with the env variable in the same line:

    $ ANSIBLE_STDOUT_CALLBACK=dense ~/bin/get-ssh-keys
    Replies
    1. Thanks a million! Your Google-Fu is stronger than mine ;)
  5. #!/usr/bin/env -S ANSIBLE_STDOUT_CALLBACK=dense ansible-playbook

    'env' is MEANT to execute a command under a specific environment, many of those SO comments are dead wrong. While most people misuse env as they think it does some path magic .. .no, shell does that, env just uses it and we normalized on it since 'shebang' requires a full path.

    note: i have found systems with env outside /usr/bin/ but they are the exception, 99.99% of the time it is there

    Replies
    1. Thanks a million for the pointer. It sent me on a more focused search trip resulting in...

      https://en.wikipedia.org/wiki/Shebang_(Unix)#Character_interpretation

      ... which totally explains the challenge and why -S solves it -- because a string starting with -S is split into argv components. Checked the https://github.com/coreutils/coreutils/blob/master/src/env.c code just to be on the safe side ;)

Add comment
Sidebar