TL;DR
Use ansible_user
ONLY in inventory
/ group_vars
/ host_vars
files
Problem: ansible_user
in my playbooks
I wanted Ansible to setup Docker with the geerlingguy/ansible-role-docker:
playbook.yml
π
...
roles:
- role: geerlingguy.docker
docker_users: ["{{ ansible_user }}"]
...
Ran vagrant provision
: no errors π
Connected to a VM with vagrant ssh
:
-
docker ps
: no permission denied errors π -
docker run hello-world
: no errors π
git commit
, git push
, done... β
I have wanted to try out dev containers for a long time, and my repository with Ansible stuff was a great choice.
While dev containers have a lot of benefits, there are also some limitations. The one I faced was broken Vagrant integration.
Before migration to dev containers: Ansible and Vagrant live on a host and integrate perfectly.
After the migration: Ansible lives in a dev container and Vagrant lives on the host. If you run vagrant provision
, you will see the error about missing ansible-playbook
executable on your PATH.
While installing Vagrant in a Docker container is easy, it's not with VirtualBox. I found some threads on StackOverflow about getting VirtualBox up and running, but...
-
It requires sticking to an image with
systemd
. On Docker Hub you can find somesystemd-*
images provided by jrei, but...- I have no idea who jrei is and how long they will maintain the images (e. g. rebasing onto the latest Debian/Ubuntu/etc. to grab all the security patches). I wouldn't like it resting on my shoulders one day.
- Just using one of these images in
.devcontainer/devcontainer.json
is not enough; I would also need to create aDockerfile
and copy everything from the Microsoft's Python 3 one to make it suitable for development. We need a dev container, remember? This way it starts resting on my shoulders immediately: periodically I need to check the changes commited to the Microsoft's Python 3Dockerfile
and apply them to myDockerfile
.
The fact that there is almost no info on the web about the setup like this says it's uncommon practice, and if I have any problems, there is nobody to help me.
OK, but except the Ansible provisioner Vagrant has the Ansible Local one!
In a Vagrantfile
I changed one line:
- config.vm.provision 'ansible' do |ansible|
+ config.vm.provision 'ansible_local' do |ansible|
... and thought it was a quick win π.
I ran vagrant provision
and got saddened by the error:
... {{ ansible_user }}: 'ansible_user' is undefined ...
Hmm, where is ansible_user
? π€
Let's take a step back and look at how Ansible Local provisioning works under the hood. Vagrant generates an inventory and passes it to ansible-playbook
:
<your-project>/.vagrant/provisioners/ansible/inventory/vagrant_ansible_inventory
# Generated by Vagrant
default ansible_ssh_host=127.0.0.1 ansible_ssh_port=2222 ansible_ssh_user='vagrant' ansible_ssh_private_key_file='<your-project>/.vagrant/machines/default/virtualbox/private_key'
^^^^^^^^^^^^^^^^^^^^^^^^^^
The ansible_ssh_user
you can see here is the ansible_user
at the time of Ansible <2. You can find the deprecation notice here.
I wish this deprecation didn't happen. So now I would not write these lines π
Let's get back to the Ansible Local provisioning. Ansible doesn't connect to any machine with SSH β‘οΈ there is no need for an inventory β‘οΈ there is no ansible_ssh_user
/ansible_user
π
OK, googling for undefined ansible_user gave me the solution:
...
roles:
- role: geerlingguy.docker
- docker_users: ["{{ ansible_user }}"]
+ docker_users: ["{{ ansible_env.USER }}"]
...
that makes my vagrant provision
successful again π
git commit
, git push
, done... β
It's time for production. I run vagrant provision
to provision my Raspberry Pi 4 (where my pet projects are hosted) and see how docker_users
variable evaluates to [root]
instead of [pi]
π€¦ββοΈ
The easy solution that can be found in some open-sourced Ansible roles:
...
roles:
- role: geerlingguy.docker
- docker_users: ["{{ ansible_env.USER }}"]
+ docker_users: ["{{ ansible_user | default(ansible_env.USER) }}"]
...
The root cause
I specified ansible_user
only in the inventory and used {{ ansible_user }}
to refer to it in my playbooks, in my roles, in variables to 3rd party roles (like in the one described above)... everywhere.
Let's say the user is ubuntu
. Use cases:
- Ansible used
ubuntu
to SSH for provisioning. - Ansible was configuring
ubuntu
user according to my instructions, e.g. changing its shell preference frombash
tozsh
and configuring.zshrc
so when you SSH, you get connected to the runningtmux
session or a new one created for you π Did you get it? Ansible is configuring the user it's using to SSH... What if it gets broken as a result of provisioning? π - I used
ubuntu
user to SSH...
At that time I thought: wow, how flexible it is πͺ
Now I think: how stupid and fragile it is π€¦ββοΈπ
The root cause is that I forgot about the single-responsibility principle, overloaded the user and thought it was cool. ansible_user
is just the possible implementation. The way I will re-write this Ansible setup and will use for all my future ones:
-
vagrant
/ubuntu
/pi
/whatever is used only by Ansible for SSHing, installing packages, blacklisting ports, etc., but it stays pristine because it creates and configures π -
dev
user to havezsh
by default,tmux
hook described above, etc.
vars/main.yml
π
---
dev_user: dev
I am using the variable for the case I find a better name one day... before the very first provisioning on a new project of course... changing this in-between would be too risky π£ Also it simplifies searching across the project.
playbook.yml
π
...
roles:
- role: geerlingguy.docker
- docker_users: ["{{ ansible_user | default(ansible_env.USER) }}"]
+ docker_users: ["{{ dev_user }}"]
...
This way you are in a safe place. No matter you are running Ansible Remote or Ansible Local provisioner. Also migration from one OS to another is an easy task:
inventory
π
-some-host ansible_user=ubuntu
+some-host ansible_user=ec2-user
... with everything else being familiar: the same ssh dev@some-host
command, the same dev
user, etc.
The less complex interpolations, conditions, loops you have - the better. These guys must be tested. Is it easy to test Ansible code? Would you like to test your Ansible code? I guess, no π
Top comments (0)