Warning
This project is undergoing a fairly significant refactor. The documentation will probably lag behind a bit. Until this message is removed, some of the commands and setup instructions may no longer work.
My Ansible monorepo, with roles grouped into importable collections, playbooks, mise for tools installation, go-task/task for automations, and direnv for environment variables.
mise is really the only requirement for this repository. The included .mise.toml handles installing all the other tools.
If you are not using mise, the requirements are:
Pythonuv-uvis used to manage the MkDocs site's dependencies.ansibledirenv- For setting environment variables any time youcdinto this repository.- This is optional, but makes using the repository locally much easier.
go-task/task- For repository automations
If you are using mise, just run:
mise trust
mise install
direnv allow
task ansible-requirementsIf you are not using mise, make sure you install all of the requirements. You can use a Python virtualenv to install Ansible, if you want:
direnv allow
python -m pip install -U virtualenv
python -m virtualenv .venv
./.venv/bin/activate
pip install -U ansible
task ansible-requirementsNote
You can install Ansible's requirements manually with ansible-galaxy install -r .config/ansible/requirements.yml.
Now you're ready to create an inventory and run some playbooks.
Note
If you are using direnv, the .envrc file sets the ANSIBLE_SSH_DIR variable for you.
This repository uses a .ssh/ directory to define SSH sessions. This keeps Ansible-specific SSH sessions isolated from your ~/.ssh/config, and your Ansible keys contained in this repository. You can tell Ansible to look in another directory for SSH configuration using the ANSIBLE_SSH_DIR environment variable.
On a new machine, if you are setting up fresh managed infrastructure and do not already have an SSH keypair, start by generating an ansible_svc key (you can use whatever name you want for the keys, I just use ansible_svc/ansible_svc.pub):
ssh-keygen -t ed25519 -b 4096 -f .ssh/ansible_svc -N ""Copy this key to the host(s) you want to manage with Ansible. Then, copy .ssh/example_config to .ssh/config and edit with your host(s) you want to manage with Ansible.
Tip
You can use the run-onboarding.yml playbook to automatically copy your ansible_svc SSH key to the remote during onboarding. This assumes the remote host has password connections enabled so Ansible can prompt you for the remote connection's password.
The onboarding playbook requires a user with root or sudo privileges. If you provision a machine with just a root account, use the root user in your .ssh/config file and pass -k to your commands.
Example (assumes you have created an onboarding inventory):
ansible-playbook -i inventories/onboard/inventory.yml [--limit <limit-name>] plays/onboard/run-onboarding.yml -kYou can also tell Ansible to prompt you for sudo password when required by passing an uppercase -K along with -k (prompt for SSH connection password).
The onboarding playbook will create an ansible_svc user on the remote. You cannot use a password to authenticate as this user, you must use the ansible_svc SSH key. After running the onboarding playbook, you can run ssh -i .ssh/ansible_svc ansible_svc@<your-hostname-or-ip> to ensure connectivity.
To validate Ansible's SSH connection, run ansible <hostname-or-inventory-group> -m ansible.builtin.ping, or run the ping playbook.
The .envrc file sets default environment variables to configure the repository. You can create a .envrc.local (copy the contents at the bottom of the .envrc file into the .envrc.local file) and override individual settings.
Whenever you make a change to either .envrc or .envrc.local, you will need to re-run direnv allow.
Whenever you cd into this repository after allowing the .envrc file, direnv will automatically source the file and set your environment variables, and when you leave the repository path it will unset them.
If you're editing in VSCode, you need to tell Code where to find your python, ansible, and ansible-config executables. If you are using mise, you can copy the .vscode/example.settings.json file to .vscode/settings.json and use mise's shim paths for the executables.
Links to the binaries for mise-managed tools are in ~/.local/share/mise/shims, but VSCode doesn't expand ~, $HOME, or ${env:HOME}/${userHome} vars in settings.json. That's why you need to create your own settings.json, to paste the path to your mise/shims/ directory:
{
// ---- Python (used by language server, linting, etc.) ----
"python.defaultInterpreterPath": "/home/YOUR-USERNAME/.local/share/mise/shims/python",
// ---- Ansible extension (actual executables) ----
"ansible.ansible.path": "/home/YOUR-USERNAME/.local/share/mise/shims/ansible",
"ansible.ansibleConfig.path": "/home/YOUR-USERNAME/.local/share/mise/shims/ansible-config",
"ansible.python.interpreterPath": "/home/YOUR-USERNAME/.local/share/mise/shims/python",
// ---- Project defaults ----
"ansible.inventory": "inventories/homelab/inventory.yml",
"ansible.playbookDir": "plays",
"ansible.useFullyQualifiedCollectionNames": true
}- Run
task -lto see predefined Ansible tasks. - Run individual playbooks with
ansible-playbook [--limit <hostname-in-inventory>] plays/path/to/playbook-name.yml- By default, the
homelabinventory is used. - You can target a different inventory with
ansible-playbook -i inventories/inventoryName/inventory.yml. - Instead of passing
-i path/to/inventory.yml, you can also set theANSIBLE_INVENTORYenvironment variable. - You can also set the value of
ANSIBLE_INVENTORYin a.envrc.localfile to override the default inventory.
- By default, the
The inventories/ path is where server infrastructure can be configured. Use an inventory's group_vars/all.yml to set variables for the inventory group, then use ansible-playbook -i inventories/<inventory_name>/inventory.yml ....
To create a new inventory, create a new directory and add inventory and vars files:
mkdir inventories/new_inventory/group_vars- Create the inventory directory, "new_inventory/", and the
group_vars/directory, where variables for the inventory group are set.
- Create the inventory directory, "new_inventory/", and the
touch inventories/new_inventory/inventory.yml- The inventory file where hosts are declared and grouped.
- When setting a variable for an inventory, check the Ansible documentation to determine where to set and override variables: Variable precedence: Where should I put a variable?.
touch inventories/new_inventory/group_vars/all.yml- Create the group variables file, where you can define variables that apply to the whole inventory.
Tip
If you use an existing directory in inventories/, you should be able to just copy the example.inventory.yml to inventory.yml and edit it.
Example inventory.yml
---
## Define a cluster group, with 2 agents and 2 servers
all:
hosts:
## Add localhost to allow running plays against this machine
localhost:
ansible_connection: local
ansible_python_interpreter: "{{ ansible_playbook_python }}"
host1:
ansible_host: "192.168.1.15"
host2:
ansible_host: "192.168.1.16"
## Remote has SSH on a different port than 22
ansible_ssh_port: 222
host3:
ansible_host: "192.168.1.60"
## Login as the root user on this machine
ansible_user: root
host4:
ansible_host: "192.168.1.64"
## One of the roles can enable passwordless sudo
setup_user_passwordless_sudo: true
## This host defines ports to allow through a UFW firewall
ufw_tcp_allowed_ports: ["80", "443", "29281"]
host5:
## This machine was configured before and can use an SSH key for connection.
# Tell Ansible where to find that key.
ansible_ssh_private_key_file: "/home/username/.ssh/id_rsa"
## Variables set here apply to the hosts above across all inventory groups
vars:
## Assumes the SSH user for setup is 'root', and that the playbook
# will disable SSH login as root
ansible_user: "root"
## Create a group specifically for onboarding into Ansible management.
onboard:
hosts:
## Re-declare hosts from above. Variables like ansible_host and ansible_user are inherited from the "all" group.
host1:
host2:
host3:
host4:
## Vars set here will only apply when ansible-playbook -i ... --limit onboard is used
vars:
## NOTE: Both of these are set with direnv. They are only here as an example.
## Set private key to use for connections.
ansible_ssh_private_key_file: ".ssh/ansible_svc"
## Set public key variable, which is used in the onboard play
ansible_ssh_public_key_file: ".ssh/ansible_svc.pub"
Playbooks join collections and roles into repeatable steps/tasks that can be applied to an inventory.
For example, the plays/maint/update-system.yml playbook will run apt or dnf updates/upgrades, and reboot the remote host if required after an update. The playbook uses variables from the inventory's group_vars/all.yml file, and pulls in the update_system role from the my.homelab collection.
Collections are a distribution format for Ansible content that can include playbooks, roles, modules, and plugins. You can install and use collections through a distribution server, such as Ansible Galaxy, or a Pulp 3 Galaxy server.
Collections in ./ansible_collections/my/ are built & installed using the `requirements.yml file. Any time a collection changes, it must be rebuilt.
Encrypting secret variables with Ansible Vault allows for passing secrets like passwords to playbooks/roles. Your vault is encrypted with a vault password, which can be read a number of ways. This project stores the password in a file.
This project is relatively small, and with only a single user (me). I've opted to store my password in a file (option 2, 'configuring vault_password_file' in ansible.cfg).
Steps:
- Edit
ansible.cfgand specify thevault_password_filein the[defaults]section:
[defaults]
vault_password_file = /path/to/vault_password_file- (Optional) You can also set your vault password as an environment variable, and set
vault_password_fileto a Bash script that echoes your Ansible vault password:
#!/bin/bash
#
# Assumes you have set an env variable $ANSIBLE_VAULT_PASS to a value matching your vault password
#
echo $ANSIBLE_VAULT_PASS
Each time you run your playbook, Ansible will unlock the vault by reading from the vault_password_file, or echoing the value from the environment (if you set the value to a Bash script).
This repository includes a devcontainer.json. This file is compatible with VSCode Devcontainers and Devpod. The devcontainer builds an environment from the included Dockerfile, then runs it in an isolated environment.
If you're using Devpod, you can setup the environment with the following steps:
- Add the Docker provider:
devpod provider add docker - Build/run the workspace:
devpod up . --id devpod-ansible- To build the workspace & open in VSCode:
devpod up . --id devpod-ansible --ide vscode
- To build the workspace & open in VSCode:
You can also open the repository in the Devpod GUI, if you've installed that.