Documenting this here, because it’s a thing I keep forgetting is possible.
This post assumes you have a working knowledge of Ansible - what it is, the difference between a playbook and a role, dynamic vs static inventories, how collections work, etc.
The Setup - Virtual Machines
I keep the configuration for my homelab/home-prod environment as code using Ansible. Previously I had the entirety of my infrastructure as virtual machines within Proxmox, and while I could reference each individual host in the hosts entry for each playbook this is a practice that doesn’t scale well if you have multiple hosts performing similar functions. It’s much better to use Ansible groups.
Instead of keeping a static list of all of my hosts in an inventory text file (because I can add or remove VMs basically at any time), I use a dynamic inventory file powered by the community.proxmox collection.
(I actually learned while writing this blog post that this collection, which was previously part of the community.general collection, was separated out into its own collection - cool stuff!)
Slightly edited for length and content, my proxmox dynamic inventory file looks like this:
1 | plugin: community.proxmox.proxmox |
I’m not going to go into too much detail about how this file works and what happens under the hood - the important part to know for this particular blog post is that you can add in tags in Proxmox and use the dynamic inventory file to turn those tags into Ansible groups. For instance, anything with the tag of dns-primary within Proxmox will wind up in the dns_primary group within this Ansible inventory.
In my Ansible project, this dynamic inventory file lives in the location inventory/homelab.proxmox.yml at the root of the repository. (Yes, of course this project and file are in Git!) If I run ansible-inventory -i inventory/homelab.proxmox.yml --graph, here’s a snippet of what that looks like:
1 | user@host > ansible-inventory -i inventory/homelab.proxmox.yml --graph |
You can see that the dynamic inventory automatically creates some groups based on the running state of the machines and whether or not they’re QEMU (ie, virtualized) resources vs LXC resources, as well as breaking down hosts based on the tags I’ve set.
Neat!
The Setup - Physical Machines
Recently, however, I’ve been starting to use a few more pieces of physical hardware in my lab. Proxmox is great, but for some services you really want to have some redundancy and/or fault tolerance in the case of your primary hypervisor suffering catastrophic hardware failure. (or, y’know… for software maintenance. Hypervisor software has to get updated too!)
I’m also using Ansible to manage these machines. Because I don’t routinely add new pieces of physical hardware, I’m fine with using a static inventory file. Here’s what that looks like for me currently:
1 | [jellyfin] |
Here you can see groups for the physical hardware I’m managing through Ansible - my Jellyfin server, a Minecraft server for my friends and family, my NAS boxes, and a secondary physical machine I’ve provisioned to serve as a secondary DNS server (the primary still lives as a virtual machine on my Proxmox hypervisor).
This file lives at the location inventory/physical-machines within my Ansible project.
The Payoff
Note that I have groups called dns within each inventory - the virtual machine named dns01 in the Proxmox dynamic inventory as well as the physical machine named dns02 in the static inventory.
I want to be able to manage both sets of machines using the same Ansible playbook. They both perform the same function, so using the same playbook makes sense. I also want to be able to only reference the single dns group within my playbook, instead of manually specifying hosts: dns01.example.com, dns02.example.com. For instance, the playbook for my DNS servers might look like this:
1 | --- |
Turns out, getting this to work is stupid-simple. All you have to do is, instead of specifying the individual inventory file on the command line, specify the containing folder of all of the inventory files you want to use. Ansible will automagically combine them into a single inventory and then run the playbook on that combined inventory.
If we run ansible-inventory -i inventory --graph, we can see that both of our DNS servers show up in the dns group:
1 | -snip snip snip- |
And if we then run the playbook against this inventory directory from above with just the dns group specified, we see that the playbook does indeed run against both the virtualized and the physical DNS machines:
1 | user@host > ansible-playbook -i inventory playbooks/dns.yml |
Super handy for managing machines in different locations, on different hypervisor technologies, in the cloud vs on-prem (there are dynamic inventories for Amazon AWS, Google Cloud Platform, etc - I want to dig deeper into those in a future blog post), etc. You’re only limited by your imagination!