Enroll inspects a Debian-like or RedHat-like system, harvests the state that matters, and generates Ansible roles, Puppet modules, or Salt states so you can bring snowflakes under management fast.
enroll harvest --out ./harvestenroll manifest --harvest ./harvest --target ansible --out ./ansibleenroll manifest --harvest ./harvest --target puppet --out ./puppetenroll manifest --harvest ./harvest --target salt --out ./salt./ansible → playbook.yml, roles/, inventory/... ./puppet → manifests/site.pp, modules/, data/... ./salt → states/top.sls, states/roles/, pillar/...
--fqdn to generate target-native host data: Ansible inventory, Puppet Hiera, or Salt pillar.Enroll is built around two phases, plus optional drift and reporting tools:
--target ansible, --target puppet, or --target salt at manifest time.Section or equivalent metadata to reduce role/module sprawl. Use --no-common-roles, or --fqdn, to keep output more host-specific.--fqdn mode, host-specific data moves into the target's native host-data layer: Ansible inventory, Puppet Hiera, or Salt pillar.--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 once
enroll harvest --out ./harvest
# Render and noop-test Ansible
enroll manifest --harvest ./harvest --target ansible --out ./ansible
ansible-playbook -i "localhost," -c local ./ansible/playbook.yml --check --diff
# Render and noop-test Puppet
enroll manifest --harvest ./harvest --target puppet --out ./puppet
puppet apply --modulepath ./puppet/modules ./puppet/manifests/site.pp --noop
# Render and noop-test Salt
enroll manifest --harvest ./harvest --target salt --out ./salt
salt-call --local --file-root ./salt/states state.apply test=True
--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 \
--target ansible \
--out ./ansible \
--fqdn myhost.example.com
--no-sudo (expect a less complete harvest). For remote sudo prompts use --ask-become-pass/-K. If your SSH private key is encrypted, use --ask-key-passphrase (interactive) or --ssh-key-passphrase-env ENV_VAR (non-interactive/CI).# Multi-site mode: one harvest, target-native host data
fqdn="$(hostname -f)"
enroll harvest --out /tmp/enroll-harvest
# Ansible: inventory/host_vars and per-host playbook
enroll manifest --harvest /tmp/enroll-harvest --target ansible --out ./ansible --fqdn "$fqdn"
ansible-playbook ./ansible/playbooks/${fqdn}.yml -i ./ansible/inventory/hosts.ini -c local --check --diff
# Puppet: Hiera node data, certname selects the host
enroll manifest --harvest /tmp/enroll-harvest --target puppet --out ./puppet --fqdn "$fqdn"
puppet apply --modulepath ./puppet/modules --hiera_config ./puppet/hiera.yaml --certname "$fqdn" ./puppet/manifests/site.pp --noop
# Salt: pillar node data, minion id selects the host
enroll manifest --harvest /tmp/enroll-harvest --target salt --out ./salt --fqdn "$fqdn"
salt-call --local --id "$fqdn" --file-root ./salt/states --pillar-root ./salt/pillar state.apply test=True
--fqdn for "many servers, host-specific data, 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
The same harvest bundle can now render Ansible, Puppet, or Salt output, with target-native multi-host layouts and package-section grouping where possible.
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