… updated on Thursday, February 17, 2022 16:27 UTC
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
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
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?
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 ;)
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
#!/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 thereThanks 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 ;)