Enroll inspects a Debian-like or RedHat-like system, harvests the state that matters, and generates Ansible roles/playbooks so you can bring snowflakes under management fast.
enroll single-shot --harvest ./harvest --out ./ansiblecd ./ansible && tree -L 2. ├── ansible.cfg ├── playbook.yml ├── roles/ │ ├── cron/ │ ├── etc_custom/ │ ├── firewall/ │ ├── nginx/ │ ├── openssh-server/ │ ├── users/ └── README.md
--fqdn to generate inventory-driven, data-driven roles.Enroll is built around two phases, plus optional drift and reporting tools:
--fqdn mode, roles are data-driven and host inventory decides what gets managed per host.--sops to store harvests/manifests as a single encrypted .tar.gz.sops file (GPG) for safer long-term storage as a DR strategy.diff mode detects and alerts about drift# Harvest → Manifest in one go
enroll single-shot --harvest ./harvest --out ./ansible
# Then run Ansible locally
ansible-playbook -i "localhost," -c local ./ansible/playbook.yml
--jinjaturtle (or let it auto-detect).# Remote harvest over SSH, then manifest locally
enroll single-shot \
--remote-host myhost.example.com \
--remote-user myuser \
--harvest /tmp/enroll-harvest \
--out ./ansible \
--fqdn myhost.example.com
--no-sudo (expect a less complete harvest).# Multi-site mode: shared roles, host-specific state in inventory
enroll harvest --out /tmp/enroll-harvest
enroll manifest --harvest /tmp/enroll-harvest --out ./ansible --fqdn "$(hostname -f)"
# Run the per-host playbook
ansible-playbook ./ansible/playbooks/"$(hostname -f)".yml
--fqdn for "many servers, high abstraction, fast adoption".# Compare two harvests and get a human-friendly report (ignoring noise)
enroll diff --old /path/to/harvestA --new /path/to/harvestB --format markdown \
--exclude-path /var/anacron \
--ignore-package-versions
# Send a webhook when differences are detected
enroll diff \
--old /path/to/harvestA \
--new /path/to/harvestB \
--webhook https://example.net/webhook \
--webhook-format json \
--webhook-header 'X-Enroll-Secret: ...' \
--ignore-package-versions \
--exit-code
# Ignore a path and changes to package versions, and optionally
# enforce the old state locally (requires ansible-playbook)
enroll diff --old /path/to/harvestA --new /path/to/harvestB \
--exclude-path /var/anacron \
--ignore-package-versions \
--enforce
# Explain what's in a harvest
enroll explain /path/to/harvest
# JSON format, and using a SOPS-encrypted harvest
enroll explain /path/to/harvest.sops \
--sops \
--format json
# Validate a harvest is correct.
enroll validate /path/to/harvest
# Check against the latest published version of the state schema specification
enroll validate /path/to/harvest --schema https://enroll.sh/schema/state.schema.json
Use your preferred packaging. An AppImage is also available.
sudo mkdir -p /usr/share/keyrings
curl -fsSL https://mig5.net/static/mig5.asc | sudo gpg --dearmor -o /usr/share/keyrings/mig5.gpg
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/mig5.gpg] https://apt.mig5.net $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/mig5.list
sudo apt update
sudo apt install enroll
sudo rpm --import https://mig5.net/static/mig5.asc
sudo tee /etc/yum.repos.d/mig5.repo > /dev/null << 'EOF'
[mig5]
name=mig5 Repository
baseurl=https://rpm.mig5.net/$releasever/rpm/$basearch
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://mig5.net/static/mig5.asc
EOF
sudo dnf upgrade --refresh
sudo dnf install enroll
pip install enroll
# or: pipx install enroll