Mental model
Enroll has two main phases:
- Harvest collects host facts and selected files into a harvest bundle containing
state.jsonandartifacts/. - Manifest converts that bundle into Ansible configuration-management output.
Additional commands help you compare harvests (diff), explain why things were included or skipped (explain), and validate bundle structure (validate).
enroll harvest
Harvest writes a local bundle. If --out is omitted, Enroll creates a private randomized cache directory under the user’s Enroll cache. If --out is supplied for plaintext output, the final output directory must be new and safe; Enroll creates it with private permissions.
High-level capture scope
- Manual packages and installed package inventory
- Enabled systemd services and relevant package ownership
- Modified package-owned config where the backend can prove drift
- Service-relevant unowned files under matching
/etctrees - Central cron and logrotate snippets
- Non-system users and SSH public/authorized key material
- Package-manager config:
apt_configordnf_config - Miscellaneous
/etcand/usr/localcustom files - Runtime sysctl and firewall fallback state where appropriate
- Flatpak, Snap, Docker, and Podman image evidence
Path selection
--include-path adds extra files or directories to the extra_paths role. --exclude-path removes matching paths from all harvesting, including defaults. Excludes win over includes.
enroll harvest --out ./harvest \
--include-path '/home/*/.profile' \
--exclude-path '/home/*/.ssh/**' \
--exclude-path 're:^/var/cache/myapp/.*$'Pattern styles are plain paths, inferred or explicit globs such as glob:/path/**/*.conf, and regexes prefixed with re: or regex:. Include expansion ignores symlinks, respects caps, and reports unmatched/capped paths as notes.
Remote harvesting
Remote mode runs the harvest over SSH and stores the bundle locally. The remote host does not need Enroll installed in the same way you would for local operation.
enroll harvest --remote-host app01.example.net --remote-user mig --out ./harvest-app01--remote-ssh-config [path]reads OpenSSH-style settings. With no value, it defaults to~/.ssh/config.- Supported ssh config fields include
HostName,User,Port,IdentityFile,ProxyCommand, andHostKeyAliaswhere supported. -K/--ask-become-passprompts for remote sudo.--no-sudoavoids sudo and produces a more limited harvest.--ask-key-passphraseprompts for an encrypted SSH key.--ssh-key-passphrase-env ENV_VARreads it from an environment variable for CI.
enroll manifest
Manifest validates the harvest, freezes directory bundles into a private temporary copy, prints the semantic-safety warning, and writes Ansible output.
enroll validate ./harvest
enroll manifest --harvest ./harvest --out ./ansible
cd ./ansible
ansible-galaxy collection install -r requirements.yml
ansible-playbook -i localhost, -c local playbook.yml --check --diffOutput contents
playbook.ymlin single-site mode, orplaybooks/<fqdn>.ymlplus inventory in multi-site mode.roles/<role>/tasks/main.yml, with handlers where needed.- Harvested files in role
files/, generated templates in roletemplates/, and variables indefaults/main.ymlor host vars. requirements.ymlfor Ansible collections such as container/Flatpak/Snap support when required.- A generated
README.mdsummarising layout, roles, excluded paths, notes, and dry-run commands.
Every generated role has a tag named role_<role_name>. Use tags to dry-run or apply a narrow slice, for example --tags role_users,role_services.
Output modes
Single-site: default
Use this for one server or a “golden” role set. Roles are more self-contained; files live under the role; variables live in role defaults.
Multi-site: --fqdn
Use this for several existing servers. Generic shared roles read host-specific variables and per-host files under inventory/host_vars/<fqdn>.
By default, Enroll groups many package and systemd-unit roles into common Debian Section/RPM Group roles. --no-common-roles preserves one generated role per package/unit. --fqdn implies this behavior to avoid cross-host coupling.
SOPS encrypted bundles
--sops <GPG_FINGERPRINT...> writes a single encrypted binary tarball instead of a plaintext directory. This is storage-at-rest encryption for harvest/manifest bundles, not an Ansible inventory-vault workflow.
enroll harvest --out ./harvest --dangerous --sops 54A91143AE0AB4F7743B01FE888ED1B423A3BC99
enroll manifest --harvest ./harvest/harvest.tar.gz.sops \
--out ./ansible-secure --sops 54A91143AE0AB4F7743B01FE888ED1B423A3BC99
cd ./ansible-secure
sops -d manifest.tar.gz.sops | tar -xzvf -
cd manifestWhen manifest output is encrypted, decrypt and extract it before running ansible-playbook.
enroll diff
Diff compares two harvests and reports package, service, user, and managed-file drift. Inputs may be directories, state.json paths, tarballs, or SOPS bundles when --sops is set.
enroll diff --old ./harvest-a --new ./harvest-b --format markdown \
--ignore-package-versions --exclude-path /var/anacron --exit-codeNotification options include --webhook, --webhook-format json|text|markdown, repeated --webhook-header 'K: V', repeated --email-to, SMTP settings, and --notify-always.
--exit-code returns status 2 when differences are detected, which is useful for cron, CI, or monitoring wrappers.
Explain and validate
enroll explain
Summarises inventory, roles, included reasons, excluded reasons, and examples. Supports --format text|json, --max-examples, tarballs, directories, state.json, and SOPS bundles.
enroll explain ./harvest --format json --max-examples 20enroll validate
Checks JSON validity, schema compliance, missing referenced artifacts, and orphaned artifact files. Supports --schema, --allow-remote-schema, --no-schema, --fail-on-warnings, JSON output, and report files.
enroll validate ./harvest --format json --out validate.jsonConfiguration file
Enroll can read INI-style defaults from an explicit --config path, ENROLL_CONFIG, $XDG_CONFIG_HOME/enroll/enroll.ini, or ~/.config/enroll/enroll.ini. It no longer reads .enroll.ini from the current working directory.
Precedence is: explicit CLI flags, subcommand/global INI defaults, then argparse defaults. Use underscores for INI keys that are hyphenated on the CLI.
[harvest]
include_path =
/home/*/.bashrc
/home/*/.profile
exclude_path = /usr/local/bin/docker-*, /usr/local/bin/some-tool
[manifest]
no_jinjaturtle = true
[diff]
exclude_path = /var/anacron
ignore_package_versions = trueInstall
Enroll requires Python 3.10 or newer. The project’s README documents Debian/Ubuntu and Fedora repositories, AppImage, pip/pipx, and Poetry development installs.
pip install enroll
# or
pipx install enrollpoetry install
poetry run enroll --helpFor a beta release, your package manager may require an explicit pre-release opt-in depending on how you install and what stable version is already available.