Getting started with Ansible on localhost
These last few days I’ve been working hard to use Ansible to manage my localhost configuration, and since Ansible is really built for big server setups I didn’t find very many guides that were helpful.
I want to write this as a help to other people who are in the same situation as me--wanting to use Ansible to manage their own computer, but not knowing where to start.
Table of contents
First, my scope
My scope for this is pretty small, because I needed to keep things under control.
- Machine: My laptop
- Linux distro: Arch linux
- Desktop environment: GNOME
- What to manage: Things that aren’t already managed by chezmoi (see this other post), including:
- packages
- /etc configurations
- installing chezmoi
If you want to look at all my files, they’re on my github: https://github.com/almondheil/ansible-arch
Files to run Ansible
Once you’ve installed Ansible, it needs some setup to tell it what machines it is managing and how.
These are the files that I needed to make my setup work. Click one to jump to that header.
-
The most important files:
-
The less-but-still-pretty-important files:
-
ansible.cfg - configuration for ansible overall
-
inventory.ini - a file that tells Ansible what machines it manages
-
host_vars - for each machine Ansible manages, variables specific to that machine
-
roles (directory)
At minimum, a role looks like this.
roles
└── role_name
└── tasks
└── main.yml
There are a LOT of other things that can go under the role_name directory, but the only one I used while setting up my computer was the files subdirectory. This lets you have files that can get copied to a location on your computer. which was often helpful with /etc configuration.
main.yml is the most important part -- it gives a list of tasks that need to be run to complete the role. These include things like running commands, installing packages, and a whole lot more.
Every task is made up of a module (which can be built into Ansible or installed from an external source), plus some arguments along with it.
Here’s an example task that installs a package:
- name: Install some package
become: true
community.general.pacman:
name: <package-name>
state: present
The important bits here are:
- The
become: true
line makes this command run as sudo. I am going to run Ansible with my user account, so I need to do this in order to get elevated permissions. - The
community.general.pacman
line tells Ansible to use that as a module. See the module documentation for more if you want.
setup.yml
The setup.yml file is called a playbook, and it collects together all your roles.
You can also put tasks directly in the playbook, but I feel like this looks sorta bad. With that in mind, here’s a basic way to set up a playbook:
---
- hosts: laptop
roles:
- { role: role1, tags: ['role1'] }
- { role: role2, tags: ['role2'] }
- { role: role3, tags: ['role3'] }
- { role: role4, tags: ['role4'] }
Aside: Pre-prompting chezmoi file decryption
I encrypt some of my files with AGE in my chezmoi repo, and I wanted to make sure these files could be decrypted right away when I started up my computer. The thing is, the decryption asks for a passphrase. I didn’t want to store this in a file, so my best option was making Ansible prompt me for my passkey.
I did this by using the vars_prompt
directive in my playbook, where I could
give prompts and variables to store the responses in:
---
- hosts: laptop
vars_prompt:
- name: chezmoi_passphrase
prompt: Chezmoi encrpyion passphrase
roles:
- { role: chezmoi, tags: ['chezmoi'] }
- other roles...
Once you do that, you can use chezmoi_passphrase
as a variable wherever you need to. In my case,
this was part of an ansible.builtin.expect
module in order to respond with the appropriate passphrase.
ansible.cfg
This file has very little going on. Basically, I just used it to silence a warning about the python version on my system. Here are the file contents:
[defaults]
interpreter_python = auto_silent
I did this because when Ansible is running locally, it realizes that you could use it to install a new version of python--which would then change the version of python it is relying on to run. By setting the interpreter to be silent, I just make it not print this warning. I don’t change the python version in my setup, after all.
inventory.ini
The inventory can also be stored as a directory, but since we’re doing only one machine it’s fine to just do a single file.
The file has pretty simple contents:
[laptop]
127.0.0.1 ansible_connection=local
Here, we define a group called “laptop” with a single host (127.0.0.1) inside.
We also specify ansible_connection=local, which tells Ansible not to try to SSH into this host since it’s running on that host.
host_vars (directory)
In our case, this directory is set up like so. The only file inside is a YAML file with the name of our only host, 127.0.0.1.
host_vars
└── 127.0.0.1.yml
Inside that file, you can set variables that will be used when Ansible is working with that host, and you can sub them in with Jinja2 formatting
I just made mine a handful of really simple key:value pairs like my home directory, but you could do a lot more here if you needed to.
Running Ansible
To run Ansible, we’re going to run the setup.yml playbook that we made. It’s necessary to provide the following info:
- Inventory to use
- Password to use for any sudo operations (or, as Ansible puts it, BECOME)
- Machine(s) to run on
- Playbook to run
These are included in the following command:
ansible-playbook -i inventory.ini -K -l laptop setup.yml
-i specifies the inventory, -K tells Ansible to prompt for the BECOME password, -l specifies which hosts to run on, and we give the playbook as the last argument.
We can also optionally specify which tags to run, which narrows down which roles will happen. This can be with one tag:
ansible-playbook -i inventory.ini -K -l laptop setup.yml -t tag
Or with multiple:
ansible-playbook -i inventory.ini -K -l laptop setup.yml -t tag1,tag2