Pyinfra: getting started
10/11/2024Automation is a huge time-saver in managing servers and containers. If you’ve ever been bogged down by blocks of YAML in Ansible or complex setups in Terraform, read on! This starts a little series of small posts on pyinfra. I’m assuming you’re already somewhat familiar with things like ansible or terraform. Basically, we want to automate a few things on other linux boxes.
My specific use case is managing tailscale on LXC containers on a proxmox server. I want to make sure that all of the lxc containers have tailscale running and are connected to the network. I currently access my internal network using tailscale running on proxmox and advertising itself as a gateway to my internal subnet. It’s not ideal and it means I will have a few extra steps when trying to keep track of what boxes are running, and their IPs. So let’s install and configure tailscale on all of the lxc containers.
Here’s where pyinfra
is magical. With ansible, you’d need plugins, and blocks and blocks of yaml. Here is the file (the actual one minus
the keys) that I use to manage tailscale on my lxc containers:
}
from ipaddress import IPv4Address, IPv4Network
from unittest import TestCase
from proxmoxer import ProxmoxAPI
def get_running_nodes(net: IPv4Network) -> list[IPv4Address]:
proxmox = ProxmoxAPI(
'...',
user='...',
token_name="proxmoxer-1",
token_value='...',
verify_ssl=False)
lxc_ips: list[IPv4Address] = []
for node in proxmox.nodes.get():
for lxc in proxmox.nodes(node['node']).lxc.get():
if lxc['status'] == 'running':
lxc_interface_info: dict[str, str] = proxmox.nodes(node['node']).lxc(lxc['vmid']).interfaces.get()
for interface in lxc_interface_info:
if (parsed := IPv4Address(interface['inet'].rpartition('/')[0])) in net:
lxc_ips.append(parsed)
return lxc_ips
running_lxc = [(str(i), {"ssh_user": "root",}) for i in
get_running_nodes(IPv4Network('192.168.0.0/16'))]
That’s it! A uv init
, uv add pyinfra
, and uv run pyinfra inventory.py exec ip addr show
and you’re
running against all of the lxc containers you’ve got a copied ssh key on. Don’t have a key copied on your containers?
Just add "ssh_password": "..."
to the dictionary in running_lxc
. Imagine trying to do that in ansible.