If there is some work-flow, configuration or tweak to a system to be made you best believe it’s going into a playbook because otherwise I will forget. Becoming a yaml-farmer is not what I envisioned for myself but here we are. Almost every time I use Ansible to implement something I learn some new trick, which can be annoying or cool depending on my mood. Tools from Redhat tend to be pretty bleeding-edge so this is no exception. When I first started writing simple playbooks ansible-galaxy had just become a thing, but now there is so much more, sheesh.
Here’s my setup, which you can easily repurpose for your own projects.
ansible-navigator + ansible-builder
It feels a little silly that now there are even more cli tools to juggle, instead of the usual ansible-playbook task.yml
. Now we have… ’execution environments’? I LOVE learning new jargon, I LOVE having to memorize words for things that already exist.
Anyway, an execution environment is just a container image which comes with all the ansible-core and python dependencies. Much in the same way that it doesn’t really make sense to run builds or batch jobs on your own development laptop, ansible-builder
allows you to create an execution environment for a control node on a remote server with all the ansible-galaxy collections and python dependencies setup and ready to go. Share with other yaml-farmers on your team as you please. Python dependencies for me have always been pure pain to configure so I am willing to take the assist.
ansible-navigator
is the UI that wraps everything together and becomes the new entrypoint for executing commands against the execution environment. For example:
ansible-vault encrypt group_vars/all/vault_passwords.yml
becomes
ansible-navigator exec -- ansible-vault encrypt group_vars/all/vault_passwords.yml
or alternatively
ansible-navigator exec "ansible-vault encrypt group_vars/all/vault_passwords.yml"
and
ansible-playbook website-deploy.yml
becomes
ansible-navigator run website-deploy.yml
How delightfully madenning it is having to memorize even more wrappers and abstractions. Furthermore, one annoying detail I discovered is that you have to escape whitespace if you’re running ad-hoc commands.
So
ansible-navigator exec -- ansible gwt -m file -a "path=/path/to/some/file.txt state=absent"
will error out but
ansible-navigator exec -- ansible gwt -m file -a "path=/path/to/some/file.txt\ state=absent"
succeeds. Most uncool.
Configuring the configurators
You can get a good overview of what exactly navigator is capable of doing by checking out its interactive mode ansible-navigator --mode interactive
via the :settings
.
Name Default Source Current
0│Ansible runner artifact dir False Settings file /run/user/1000
1│Ansible runner rotate artifacts count False Settings file 10
2│Ansible runner timeout True Not set Not set
3│Ansible runner write job events True Defaults False
4│App False Command line collections
5│Cmdline True Not set Not set
6│Collection doc cache path True Defaults /home/programmist/.cache/ansible-navigator/collection_doc_cache.db
7│Config True Not set Not set
8│Container engine False Settings file podman
9│Container options False Settings file ['--user=0', '--user=root']
10│Current settings file False Search path /home/programmist/.ansible-navigator.yml
11│Display color True Defaults True
12│Editor command True Defaults nvim {filename}
13│Editor console True Defaults True
14│Enable prompts True Defaults False
15│Exec command True Defaults /bin/bash
16│Exec shell True Defaults True
17│Execution environment True Settings file True
18│Execution environment image False Settings file custom-ee:latest
19│Execution environment volume mounts True Not set Not set
20│Format True Settings file yaml
21│Help builder True Defaults False
22│Help config True Defaults False
23│Help doc True Defaults False
24│Help inventory True Defaults False
25│Help playbook True Defaults False
26│Images details True Defaults ['everything']
27│Inventory False Ansible configuration file ['/home/programmist/projects/mine/generic-wizard-tower/hosts.yml']
28│Inventory column True Not set Not set
29│Lint config True Not set Not set
30│Lintables True Not set Not set
31│Log append True Defaults True
32│Log file False Settings file /home/programmist/projects/mine/ansible-navigator.log
33│Log level False Settings file critical
34│Mode True Command line interactive
35│Osc4 True Defaults True
36│Pass environment variable False Settings file ['ANSIBLE_VAULT_PASSWORD_FILE']
37│Playbook True Not set Not set
38│Playbook artifact enable True Settings file True
39│Playbook artifact replay False Settings file /tmp/test_artifact.json
40│Playbook artifact save as False Settings file /tmp/test_artifact.json
41│Plugin name True Not set Not set
42│Plugin type True Defaults module
43│Pull arguments True Not set Not set
44│Pull policy False Settings file missing
45│Set environment variable False Settings file {'ANSIBLE_STDOUT_CALLBACK': 'yaml'}
46│Settings effective True Defaults False
47│Settings sample True Defaults False
48│Settings schema True Defaults json
49│Settings sources True Defaults False
50│Time zone True Defaults UTC
51│Workdir True Defaults /home/programmist/projects/mine/generic-wizard-tower
As you can see its a mix of config values from differents sources; environment variables, defaults and files. Note the execution environment image, which is created with ansible-builder build -t custom-ee:latest --prune-images -v3
. The config for the image references the requirements.yml and requirements.txt for any collections from galaxy you might want to use in your playbooks, such as Podman modules:
~/projects/mine/gwt/execution-environment.yml
version: 3
images:
base_image:
name: ghcr.io/ansible/community-ansible-dev-tools:latest
dependencies:
ansible_core:
package_pip: ansible-core
ansible_runner:
package_pip: ansible-runner
galaxy: requirements.yml
python: requirements.txt
system:
- openssh-clients
- sshpass
options:
package_manager_path: /usr/bin/dnf5
Of course, its own config is also found at ~/.ansible-navigator.yml
. Other important things you’ll probably want to set are your vault password file, log levels and log file location. I like to see prettier yaml so I use the stdout callback function.
---
ansible-navigator:
ansible-runner:
artifact-dir: /run/user/1000
rotate-artifacts-count: 10
execution-environment:
container-engine: podman
container-options: ["--user=0"]
enabled: true
environment-variables:
pass:
- ANSIBLE_VAULT_PASSWORD_FILE
set:
ANSIBLE_STDOUT_CALLBACK: yaml
image: custom-ee:latest
pull:
policy: missing
logging:
file: /home/programmist/projects/mine/ansible-navigator.log
level: critical
playbook-artifact:
enable: True
replay: /tmp/test_artifact.json
save-as: /tmp/test_artifact.json
mode: stdout
format: yaml
Project directory structure
There are a multitude of ways to structure an Ansible project but here is what works for me.