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.jsonis not enough; I would also need to create aDockerfileand 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 3Dockerfileand 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
ubuntuto SSH for provisioning. - Ansible was configuring
ubuntuuser according to my instructions, e.g. changing its shell preference frombashtozshand configuring.zshrcso when you SSH, you get connected to the runningtmuxsession 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
ubuntuuser 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 π -
devuser to havezshby default,tmuxhook 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)