diff --git a/src/content/_index.html b/src/content/_index.html index 253e406..dcf6cd7 100644 --- a/src/content/_index.html +++ b/src/content/_index.html @@ -1,22 +1,23 @@ --- title: "Enroll" -html_title: "Enroll - Reverse-engineering servers into Ansible" -description: "Enroll inspects Debian-like and RedHat-like Linux hosts and generates Ansible roles/playbooks from what it finds. Harvest → Manifest → Manage." -og_title: "Enroll - Reverse-engineering servers into Ansible" -og_description: "Harvest a host's real configuration and turn it into Ansible roles/playbooks. Safe-by-default, with optional SOPS encryption." +html_title: "Enroll - Reverse-engineering servers into configuration management" +description: "Enroll inspects Debian-like and RedHat-like Linux hosts and generates Ansible, Puppet, or Salt manifests from what it finds. Harvest → Manifest → Manage." +og_title: "Enroll - Reverse-engineering servers into configuration management" +og_description: "Harvest a host's real configuration and turn it into Ansible, Puppet, or Salt code. Safe-by-default, with optional SOPS encryption." og_type: "website" ---
-
Reverse-engineering servers into Ansible
-

Get an existing Linux host into Ansible in seconds.

-

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.

+
Reverse-engineering servers into Ansible, Puppet, or Salt
+

Get an existing Linux host into configuration management in seconds.

+

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.

Super fast @@ -29,24 +30,18 @@ og_type: "website"
Config in the blink of an eye
-
single-shot → ansible-playbook
+
harvest once → render to your CM tool
-
$ enroll single-shot --harvest ./harvest --out ./ansible
-
$ cd ./ansible && tree -L 2
-
.
-├── ansible.cfg
-├── playbook.yml
-├── roles/
-│   ├── cron/
-│   ├── etc_custom/
-│   ├── firewall/
-│   ├── nginx/
-│   ├── openssh-server/
-│   ├── users/
-└── README.md
+
$ enroll harvest --out ./harvest
+
$ enroll manifest --harvest ./harvest --target ansible --out ./ansible
+
$ enroll manifest --harvest ./harvest --target puppet --out ./puppet
+
$ enroll 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/...
-
Tip: for multiple hosts, use --fqdn to generate inventory-driven, data-driven roles.
+
Tip: for multiple hosts, use --fqdn to generate target-native host data: Ansible inventory, Puppet Hiera, or Salt pillar.
@@ -71,7 +66,7 @@ og_type: "website"
Manifest
-
Render Ansible roles & playbooks from the harvest.
+
Render Ansible roles/playbooks, Puppet modules/Hiera data, or Salt states/pillar from the harvest.
@@ -107,11 +102,27 @@ og_type: "website"
+
+
+
+
Ansible, Puppet, or Salt
+
The harvest bundle is renderer-neutral. Choose --target ansible, --target puppet, or --target salt at manifest time.
+
+
+
+
+
+
+
Fewer roles/modules where possible
+
By default, package/service snapshots are grouped by package Section or equivalent metadata to reduce role/module sprawl. Use --no-common-roles, or --fqdn, to keep output more host-specific.
+
+
+
Multi-site without "shared role broke host2"
-
In --fqdn mode, roles are data-driven and host inventory decides what gets managed per host.
+
In --fqdn mode, host-specific data moves into the target's native host-data layer: Ansible inventory, Puppet Hiera, or Salt pillar.
@@ -119,7 +130,7 @@ og_type: "website"
Remote over SSH
-
Harvest a remote host from your workstation, then manifest Ansible output locally.
+
Harvest a remote host from your workstation, then render Ansible, Puppet, or Salt output locally.
@@ -171,19 +182,28 @@ og_type: "website"
-
# Harvest → Manifest in one go
-enroll single-shot --harvest ./harvest --out ./ansible
+              
# Harvest once
+enroll harvest --out ./harvest
 
-# Then run Ansible locally
-ansible-playbook -i "localhost," -c local ./ansible/playbook.yml
+# 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
Good for
-
Disaster recovery snapshots, "make this one host reproducible", and carving a golden role set you'll refine over time.
+
Disaster recovery snapshots, "make this one host reproducible", and comparing how the same harvest looks in Ansible, Puppet, or Salt before you commit to a workflow.

-
Want templates for structured configs? Install JinjaTurtle and use --jinjaturtle (or let it auto-detect).
+
Using Ansible and want templates for structured configs? Install JinjaTurtle and use --jinjaturtle (or let it auto-detect).
@@ -197,6 +217,7 @@ enroll single-shot \ --remote-host myhost.example.com \ --remote-user myuser \ --harvest /tmp/enroll-harvest \ + --target ansible \ --out ./ansible \ --fqdn myhost.example.com @@ -206,14 +227,23 @@ enroll single-shot \
-
# Multi-site mode: shared roles, host-specific state in inventory
+          
# Multi-site mode: one harvest, target-native host data
+fqdn="$(hostname -f)"
 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
+# 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
-
Rule of thumb: single-site for "one server, easy-to-read roles"; --fqdn for "many servers, high abstraction, fast adoption".
+
Rule of thumb: single-site for "one server, easy-to-read roles/modules/states"; --fqdn for "many servers, host-specific data, fast adoption".
@@ -260,14 +290,14 @@ enroll explain /path/to/harvest.sops \
-
# Validate a harvest is correct.
+          
# 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
 
-
'validate' makes sure the harvest's state confirms to Enroll's state schema, doesn't contain orphaned artifacts and isn't missing any artifacts needed by the state. By default, it checks against the schema packaged with Enroll, but you can also check against the latest version on this site.
+
'validate' makes sure the harvest's state conforms to Enroll's state schema, doesn't contain orphaned artifacts and isn't missing any artifacts needed by the state. By default, it checks against the schema packaged with Enroll, but you can also check against the latest version on this site.
@@ -295,7 +325,7 @@ enroll validate /path/to/harvest --schema https://enroll.sh/schema/state.schema.
Manifest
-
Render Ansible roles/playbooks.
+
Render Ansible roles/playbooks, Puppet modules, or Salt states.
@@ -325,6 +355,23 @@ enroll validate /path/to/harvest --schema https://enroll.sh/schema/state.schema. +
+
+
+
+
+
News
+

Enroll 0.7.0 adds Puppet and Salt rendering

+

The same harvest bundle can now render Ansible, Puppet, or Salt output, with target-native multi-host layouts and package-section grouping where possible.

+
+ +
+
+
+
+
diff --git a/src/content/docs.html b/src/content/docs.html index d6dbb48..5e6812d 100644 --- a/src/content/docs.html +++ b/src/content/docs.html @@ -18,6 +18,7 @@ description: "How Enroll works: harvest, manifest, modes, and configuration."
Mental model + Targets and grouping How harvesting works State schema Single-site vs multi-site @@ -38,7 +39,7 @@ description: "How Enroll works: harvest, manifest, modes, and configuration."

Mental model

-

Enroll is intentionally simple: it collects facts first, then renders Ansible from those facts.

+

Enroll is intentionally simple: it collects facts first, then renders target-native configuration-management output from those facts.

@@ -54,7 +55,7 @@ description: "How Enroll works: harvest, manifest, modes, and configuration."
  • Detect installed packages and services
  • Collect config that deviates from packaged defaults (where possible)
  • Grab relevant custom/unowned files in service dirs
  • -
  • Capture non-system users & SSH public keys, .bashrc files etc
  • +
  • Capture non-system users, group memberships, SSH public keys, and dangerous-mode shell dotfiles when requested
  • @@ -64,13 +65,13 @@ description: "How Enroll works: harvest, manifest, modes, and configuration."
    2) Manifest
    -
    Generate an Ansible repo structure
    +
    Generate Ansible, Puppet, or Salt output
      -
    • Roles with files/templates and defaults
    • -
    • Playbooks to apply the captured state
    • -
    • Optional inventory structure for multi-host runs: each host gets its own playbook
    • +
    • Ansible roles/playbooks, Puppet modules/Hiera data, or Salt states/pillar
    • +
    • Noop-friendly output you can review before applying
    • +
    • Optional target-native host data for multi-host runs
    @@ -79,8 +80,52 @@ description: "How Enroll works: harvest, manifest, modes, and configuration."
    Typical flow
    $ enroll harvest --out /tmp/enroll-harvest
    -$ enroll manifest --harvest /tmp/enroll-harvest --out /tmp/enroll-ansible
    -$ ansible-playbook -i "localhost," -c local /tmp/enroll-ansible/playbook.yml
    +$ enroll manifest --harvest /tmp/enroll-harvest --target ansible --out /tmp/enroll-ansible +$ ansible-playbook -i "localhost," -c local /tmp/enroll-ansible/playbook.yml --check --diff + +$ enroll manifest --harvest /tmp/enroll-harvest --target puppet --out /tmp/enroll-puppet +$ puppet apply --modulepath /tmp/enroll-puppet/modules /tmp/enroll-puppet/manifests/site.pp --noop + +$ enroll manifest --harvest /tmp/enroll-harvest --target salt --out /tmp/enroll-salt +$ salt-call --local --file-root /tmp/enroll-salt/states state.apply test=True
    +
    +
    + +
    +

    Targets and grouping

    +

    The harvest bundle is the contract. Manifesting is a separate step, so the same harvest can be rendered into Ansible, Puppet, or Salt without re-probing the host.

    + +
    +
    +
    +
    Ansible
    +

    Generates playbooks, roles, defaults, files/templates, inventory, and host vars.

    +
    $ enroll manifest --harvest ./harvest --target ansible --out ./ansible
    +$ ansible-playbook -i "localhost," -c local ./ansible/playbook.yml --check --diff
    +
    +
    +
    +
    +
    Puppet
    +

    Generates a control-repo-style tree with manifests/site.pp, modules, files, and Hiera data in --fqdn mode.

    +
    $ enroll manifest --harvest ./harvest --target puppet --out ./puppet
    +$ puppet apply --modulepath ./puppet/modules ./puppet/manifests/site.pp --noop
    +
    +
    +
    +
    +
    Salt
    +

    Generates Salt state trees under states/, with pillar data under pillar/ in --fqdn mode.

    +
    $ enroll manifest --harvest ./harvest --target salt --out ./salt
    +$ salt-call --local --file-root ./salt/states state.apply test=True
    +
    +
    +
    + +
    +
    Reducing role/module/state sprawl
    +

    When Enroll can see package metadata, it tries to combine related package/service snapshots by the package Section category, or equivalent backend metadata. On Debian-like systems this can turn many tiny package roles into broader groups such as admin, web, net, libs, or utils. On RPM-like systems it uses comparable package group metadata where available.

    +

    This grouping is enabled by default in single-site output. It is disabled by --no-common-roles when you want one role/module/state per discovered package/service, and it is also disabled by --fqdn because host-specific output should stay isolated in inventory/Hiera/pillar.

    @@ -92,19 +137,19 @@ description: "How Enroll works: harvest, manifest, modes, and configuration."
  • Detects the OS and its package backend (e.g dpkg vs rpm)
  • Detects what packages are installed
  • For each package, it tries to detect files in /etc that have been modified from the default that get shipped with the package.
  • -
  • It detects running/enabled services and timers via systemd. For each of these, it looks for the unit files, any 'drop-in' files, environment variable files, etc, as well as what executable it executes, and tries to map those systemd services to the packages it's already learned about earlier (that way, those 'packages' or future Ansible roles, can also be associated with 'handlers' in Ansible, to handle restart of the services if/when the configs change)
  • +
  • It detects running/enabled services and timers via systemd. For each of these, it looks for the unit files, any 'drop-in' files, environment variable files, etc, as well as what executable it executes, and tries to map those systemd services to the packages it's already learned about earlier (that way, those 'packages' or future manifest output can also be associated with service handling in the selected target, to handle restart of the services if/when the configs change)
  • Aside from known packages already learned, it optimistically tries to capture extra system configuration in /etc that is common for config management. This is stuff like the apt or dnf configuration, crons, logrotate configs, networking settings, hosts files, etc.
  • -
  • For applications that commonly make use of symlinks (think Apache2 or Nginx's sites-enabled or mods-enabled), it notes what symlinks exist so that it can capture those in Ansible
  • +
  • For applications that commonly make use of symlinks (think Apache2 or Nginx's sites-enabled or mods-enabled), it notes what symlinks exist so that it can capture those in the generated manifest
  • It also looks for other snowflake stuff in /etc not associated with packages/services or other typical system config, and will put these into an etc_custom role.
  • Likewise, it looks in /usr/local for stuff, on the assumption that this is an area that custom apps/configs might've been placed in. These go into a usr_local_custom role.
  • -
  • It captures non-system user accounts, their group memberships and files such as their .ssh/authorized_keys, and .bashrc, .profile, .bash_aliases, .bash_logout if these files differ from the skel defaults
  • +
  • It captures non-system user accounts, group memberships, and files such as .ssh/authorized_keys. In --dangerous mode it can also capture shell dotfiles such as .bashrc, .profile, .bash_aliases, and .bash_logout when they differ from the skel defaults.
  • It takes into account anything the user set with --exclude-path or --include-path. For anything extra that is included, it will put these into an 'extra_paths' role. The location could be anywhere e.g something in /opt, /srv, whatever you want.
  • It writes the state.json and captures the artifacts.

  • Other things to be aware of:

    -
    Does Enroll use Ansible community/galaxy roles?
    -
    No, Enroll doesn't have any knowledge of Ansible Galaxy roles or community plugins. It generates all the roles itself. If you really want to use roles from the community, Enroll may not be the tool for you, other than perhaps to help get you started.
    +
    Does Enroll use community roles/modules?
    +
    No, Enroll does not pull in Ansible Galaxy roles, Puppet Forge modules, or Salt formulas. It generates its own output from the harvest state. If you want to adopt community content later, Enroll can still help you discover and bootstrap the host-specific state you need to port.

    Keep in mind that a lot of software config files are also good candidates for being Jinja templates with abstracted vars for separate hosts.

    -
    Enroll does use my companion tool JinjaTurtle if it's installed, but JinjaTurtle only recognises certain types of files (.ini style, .json, .xml, .yaml, .toml, but not special ones like Nginx or Apache conf files which have their own special syntax). When Enroll can't turn a config file into a template, it copies the raw file instead and uses it with ansible.builtin.copy in role tasks.
    +
    Enroll does use my companion tool JinjaTurtle if it's installed, but JinjaTurtle only recognises certain types of files (.ini style, .json, .xml, .yaml, .toml, but not special ones like Nginx or Apache conf files which have their own special syntax). When Enroll cannot turn a config file into a template, it copies the raw file instead. JinjaTurtle templating is currently most useful for Ansible output; Puppet and Salt output stay closer to raw managed files and target-native data.
    @@ -125,7 +170,7 @@ description: "How Enroll works: harvest, manifest, modes, and configuration."

    State schema

    Enroll writes a state.json file describing what was harvested. The canonical definition of that file format is the JSON Schema below.

    -

    You can also validate a harvest state file against the schema by using enroll validate /path/to/harvest.

    +

    You can also validate a harvest state file against the schema by using enroll validate /path/to/harvest.

    @@ -137,17 +182,18 @@ description: "How Enroll works: harvest, manifest, modes, and configuration."

    Single-site vs multi-site

    -

    Manifest output has two styles. Choose based on how you'll use the result.

    +

    Manifest output has two styles. The exact files differ by target, but the intent is the same.

    Single-site (default)
    -

    Best when you are enrolling one host, or you're producing a reusable "golden" role set that could be applied anywhere.

    +

    Best when you are enrolling one host, or producing a reusable baseline that you will review and refine.

      -
    • Roles are self-contained
    • -
    • Raw files live in each role's files/
    • -
    • Template variables live in defaults/main.yml
    • +
    • Ansible: self-contained roles and playbook.yml
    • +
    • Puppet: manifests/site.pp plus modules under modules/
    • +
    • Salt: states/top.sls plus states under states/roles/
    • +
    • Common role grouping is enabled unless --no-common-roles is passed
    @@ -156,23 +202,31 @@ description: "How Enroll works: harvest, manifest, modes, and configuration."
    Multi-site (--fqdn)

    Best when you want to enroll several existing servers quickly, especially if they differ.

      -
    • Roles are shared; raw files live in host-specific inventory
    • -
    • Inventory decides what gets managed on each host (files/packages/services)
    • -
    • Non-templated files go under inventory/host_vars/<fqdn>/<role>/.files
    • +
    • Ansible: host-specific state in inventory/host_vars/<fqdn>/
    • +
    • Puppet: host-specific data in Hiera under data/nodes/<fqdn>.yaml
    • +
    • Salt: host-specific data in pillar under pillar/nodes/
    • +
    • Common grouping is disabled so each host's harvested state remains isolated
    -
    Multi-site example
    -
    $ enroll manifest --harvest /tmp/enroll-harvest --out /tmp/enroll-ansible --fqdn "$(hostname -f)"
    -$ ansible-playbook /tmp/enroll-ansible/playbooks/"$(hostname -f)".yml
    +
    Multi-site noop examples
    +
    $ fqdn="$(hostname -f)"
    +$ enroll manifest --harvest /tmp/enroll-harvest --target ansible --out /tmp/enroll-ansible --fqdn "$fqdn"
    +$ ansible-playbook /tmp/enroll-ansible/playbooks/${fqdn}.yml -i /tmp/enroll-ansible/inventory/hosts.ini -c local --check --diff
    +
    +$ enroll manifest --harvest /tmp/enroll-harvest --target puppet --out /tmp/enroll-puppet --fqdn "$fqdn"
    +$ puppet apply --modulepath /tmp/enroll-puppet/modules --hiera_config /tmp/enroll-puppet/hiera.yaml --certname "$fqdn" /tmp/enroll-puppet/manifests/site.pp --noop
    +
    +$ enroll manifest --harvest /tmp/enroll-harvest --target salt --out /tmp/enroll-salt --fqdn "$fqdn"
    +$ salt-call --local --id "$fqdn" --file-root /tmp/enroll-salt/states --pillar-root /tmp/enroll-salt/pillar state.apply test=True
    -
    Tip: role tags
    -
    Generated playbooks tag each role as role_<name> (e.g. role_users, role_services, role_other). You can target a subset with ansible-playbook ... --tags role_users.
    +
    Tip: targeting subsets
    +
    Ansible playbooks tag each role as role_<name>. Puppet and Salt output can be trimmed by editing generated class/state inclusion data. In every target, treat the first generated manifest as a reviewable bootstrap rather than magic.
    @@ -181,12 +235,12 @@ description: "How Enroll works: harvest, manifest, modes, and configuration."

    Run Enroll on your workstation, harvest a remote host over SSH. The harvest is pulled locally.

    $ enroll harvest --remote-host myhost.example.com --remote-user myuser --out /tmp/enroll-harvest
    -$ enroll manifest --harvest /tmp/enroll-harvest --out /tmp/enroll-manifest
    +$ enroll manifest --harvest /tmp/enroll-harvest --target ansible --out /tmp/enroll-ansible
     
     # Alternatively, run both commands combined together with the 'single-shot' mode:
     
     $ enroll single-shot --remote-host myhost.example.com --remote-user myuser \
    -  --harvest /tmp/enroll-harvest --out /tmp/enroll-ansible \
    +  --harvest /tmp/enroll-harvest --target ansible --out /tmp/enroll-ansible \
       --fqdn myhost.example.com
     
     # Encrypted SSH key examples:
    @@ -211,7 +265,7 @@ description: "How Enroll works: harvest, manifest, modes, and configuration."
     
             

    JinjaTurtle integration

    -

    If JinjaTurtle (one of my other projects) is installed, Enroll can also produce Jinja2 templates for ini/json/xml/toml-style config and extract variables cleanly into Ansible, instead of just storing the 'raw' files.

    +

    If JinjaTurtle (one of my other projects) is installed, Enroll can also produce Jinja2 templates for ini/json/xml/toml-style config and extract variables cleanly into Ansible, instead of just storing the 'raw' files. Puppet and Salt output currently focus on raw managed files plus target-native data.

    @@ -227,8 +281,8 @@ description: "How Enroll works: harvest, manifest, modes, and configuration."
    Where variables land
      -
    • Single-site: roles/<role>/defaults/main.yml
    • -
    • Multi-site: inventory/host_vars/<fqdn>/<role>.yml
    • +
    • Ansible single-site: roles/<role>/defaults/main.yml
    • +
    • Ansible multi-site: inventory/host_vars/<fqdn>/<role>.yml
    @@ -279,7 +333,7 @@ ignore_package_versions = true

    Drift detection with enroll diff

    -

    One of the things I miss from my Puppet days, was the way the Puppet 'agent' would check in with the server and realign itself to the declared desired state. With Ansible, it's easy for systems to fall 'out of date', especially if someone is doing the wrong thing and changing things on-the-fly instead of via config management!

    +

    One of the things I miss from my Puppet days, was the way the Puppet 'agent' would check in with the server and realign itself to the declared desired state. With any configuration-management system, it is easy for systems to fall out of date if someone changes things on-the-fly instead of via the desired-state workflow.

    The purpose of enroll diff is to compare two 'harvests' and detect what has changed - be it adding/removing of programs, change to systemd unit state, modifications, addition or removal of files, and so on.

    @@ -302,7 +356,7 @@ ignore_package_versions = true
    Optional: enforce the old harvest state (--enforce)
    -
    If drift exists and ansible-playbook is on PATH, Enroll can generate a manifest from the old harvest and apply it locally to restore expected state. It avoids package downgrades, and will often run Ansible with --tags role_... so only the roles implicated by the drift are applied. This is very much like a return to Puppet's agent mode!
    +
    If drift exists and ansible-playbook is on PATH, Enroll can generate a manifest from the old harvest and apply it locally to restore expected state. It avoids package downgrades, and will often run Ansible with --tags role_... so only the roles implicated by the drift are applied. This is intentionally Ansible-based today, even when you also use Puppet or Salt rendering for normal manifests.
    @@ -561,7 +615,7 @@ sudo journalctl -u enroll-harvest-diff.service -n 200 --no-pager
    Multi-host safety
    -

    For fleets, prefer multi-site output so roles stay generic and host inventory controls what is applied per host - reducing "shared role breaks other host" surprises.

    +

    For fleets, prefer multi-site output so target-native host data controls what is applied per host - reducing "shared role/module/state breaks another host" surprises.

    diff --git a/src/content/examples.html b/src/content/examples.html index 464eb1e..58e5ca5 100644 --- a/src/content/examples.html +++ b/src/content/examples.html @@ -1,13 +1,13 @@ --- title: "Examples" html_title: "Enroll Examples" -description: "Copy/paste recipes for Enroll: one host, fleets, drift detection, and safe storage." +description: "Copy/paste recipes for Enroll: Ansible, Puppet, Salt, fleets, drift detection, and safe storage." ---
    Examples

    Copy/paste recipes

    -

    Practical flows you can adapt to your environment.

    +

    Practical flows you can adapt to Ansible, Puppet, Salt, or all three.

    @@ -17,49 +17,85 @@ description: "Copy/paste recipes for Enroll: one host, fleets, drift detection,
    -
    Enroll a single host (local)
    +
    Harvest once, try each renderer locally
    $ enroll harvest --out /tmp/enroll-harvest
    -$ enroll manifest --harvest /tmp/enroll-harvest \
    -  --out /tmp/enroll-ansible
    -$ ansible-playbook -i "localhost," -c local \
    -  /tmp/enroll-ansible/playbook.yml --diff --check
    + +# Ansible noop +$ enroll manifest --harvest /tmp/enroll-harvest --target ansible --out /tmp/enroll-ansible +$ ansible-playbook -i "localhost," -c local /tmp/enroll-ansible/playbook.yml --diff --check + +# Puppet noop +$ enroll manifest --harvest /tmp/enroll-harvest --target puppet --out /tmp/enroll-puppet +$ puppet apply --modulepath /tmp/enroll-puppet/modules /tmp/enroll-puppet/manifests/site.pp --noop + +# Salt noop +$ enroll manifest --harvest /tmp/enroll-harvest --target salt --out /tmp/enroll-salt +$ salt-call --local --file-root /tmp/enroll-salt/states state.apply test=True
    -

    Great for "make this box reproducible" or building a golden role set.

    +

    Great for “make this box reproducible”, testing renderer output, or building a golden baseline you will refine.

    -
    Enroll a remote host (over SSH)
    +
    Enroll a remote host over SSH
    $ enroll harvest \
       --remote-host myhost.example.com \
       --remote-user myuser \
       --out /tmp/enroll-harvest
    +
     $ enroll manifest \
       --harvest /tmp/enroll-harvest \
    -  --out /tmp/enroll-ansible
    + --target puppet \ + --out /tmp/enroll-puppet
    -

    No need to manually run commands on the server - your bundle lands locally. If your remote user needs a password for sudo, pass in --ask-become-pass or -K, just like in Ansible. If you don't want to use sudo, pass --no-sudo, but your harvest may contain less data.

    +

    The bundle lands locally. Change --target puppet to ansible or salt without re-harvesting. For remote sudo prompts use --ask-become-pass/-K; use --no-sudo for a less complete non-root harvest.

    -
    Fleets: multi-site output
    +
    Fleets: --fqdn output
    $ fqdn="$(hostname -f)"
    -$ enroll single-shot --remote-host "$fqdn" \
    -  --remote-user myuser \
    -  --out /tmp/enroll-ansible \
    -  --fqdn "$fqdn"
    -$ ansible-playbook "/tmp/enroll-ansible/playbooks/${fqdn}.yml"
    +$ enroll harvest --out /tmp/enroll-harvest + +# Ansible: inventory/host_vars and a per-host playbook +$ enroll manifest --harvest /tmp/enroll-harvest --target ansible --out /tmp/enroll-ansible --fqdn "$fqdn" +$ ansible-playbook /tmp/enroll-ansible/playbooks/${fqdn}.yml -i /tmp/enroll-ansible/inventory/hosts.ini -c local --check --diff + +# Puppet: Hiera data keyed by certname +$ enroll manifest --harvest /tmp/enroll-harvest --target puppet --out /tmp/enroll-puppet --fqdn "$fqdn" +$ puppet apply --modulepath /tmp/enroll-puppet/modules --hiera_config /tmp/enroll-puppet/hiera.yaml --certname "$fqdn" /tmp/enroll-puppet/manifests/site.pp --noop + +# Salt: pillar data keyed by minion id +$ enroll manifest --harvest /tmp/enroll-harvest --target salt --out /tmp/enroll-salt --fqdn "$fqdn" +$ salt-call --local --id "$fqdn" --file-root /tmp/enroll-salt/states --pillar-root /tmp/enroll-salt/pillar state.apply test=True
    -

    Shared roles + host inventory keeps one host's differences from breaking another.

    +

    --fqdn disables common grouping and moves host-specific state into the target’s native host-data layer: inventory, Hiera, or pillar.

    +
    +
    + +
    +
    +
    Control role/module/state grouping
    +
    + +
    # Default single-site mode: combine packages/services by package Section where possible
    +$ enroll manifest --harvest /tmp/enroll-harvest --target ansible --out /tmp/enroll-ansible
    +
    +# Keep output split more literally by discovered package/service role
    +$ enroll manifest --harvest /tmp/enroll-harvest --target ansible --out /tmp/enroll-ansible-raw --no-common-roles
    +
    +# --fqdn also disables common grouping because host data must remain isolated
    +$ enroll manifest --harvest /tmp/enroll-harvest --target salt --out /tmp/enroll-salt --fqdn "$(hostname -f)"
    +
    +

    Grouping keeps first-pass output manageable. Use --no-common-roles when you prefer more direct one-role-per-package/service output.

    @@ -68,15 +104,19 @@ description: "Copy/paste recipes for Enroll: one host, fleets, drift detection,
    Drift detection with enroll diff
    -
    $ enroll diff --old /path/to/harvestA --new /path/to/harvestB --format markdown --exclude-path /var/anacron --ignore-package-versions
    +            
    $ enroll diff --old /path/to/harvestA --new /path/to/harvestB \
    +  --format markdown \
    +  --exclude-path /var/anacron \
    +  --ignore-package-versions
    +
     $ enroll diff --old /path/to/golden --new /path/to/current \
    -  --webhook https://example.net/webhook  \
    +  --webhook https://example.net/webhook \
       --webhook-format json \
    -  --webhook-header 'X-Enroll-Secret: ...'  \
    -   --ignore-package-versions --exit-code
    -            
    + --webhook-header 'X-Enroll-Secret: ...' \ + --ignore-package-versions \ + --exit-code
    -

    Use it in cron or CI to alert on change.

    +

    Use it in cron, systemd timers, or CI to alert on change.

    @@ -87,13 +127,13 @@ description: "Copy/paste recipes for Enroll: one host, fleets, drift detection,
    $ enroll explain /tmp/enroll-harvest
     
    -# machine-readable (reasons, examples, inventory breakdown)
    +# machine-readable reasons, examples, and inventory breakdown
     $ enroll explain /tmp/enroll-harvest --format json | jq .
     
     # encrypted bundle
     $ enroll explain /var/lib/enroll/harvest.tar.gz.sops --sops
    -

    Great for answering "why did it include/exclude that file?" before you generate a manifest.

    +

    Great for answering “why did it include/exclude that file?” before you generate a manifest.

    @@ -101,39 +141,31 @@ description: "Copy/paste recipes for Enroll: one host, fleets, drift detection,
    Enforce the previous state with enroll diff --enforce
    - -
    $ enroll diff \
    +            
    +            
    $ enroll diff \
       --old /path/to/harvestA \
       --new /path/to/harvestB \
       --enforce \
    -  --format json
    -	    
    + --format json
    -

    Enforcing the old harvest will restore its files/perms, missing packages, changed services or users, if ansible-playbook is on the PATH.

    +

    Enforcement currently uses generated Ansible and ansible-playbook on PATH, even if you also use Puppet or Salt for normal manifest output.

    - -
    - -
    -
    -
    Safe harvesting (default)
    -

    Enroll tries to avoid harvesting files that might contain secrets. If you need to capture "everything", pass --dangerous and treat the output as sensitive.

    -

    You can still control what gets collected and what doesn't by using --include and --exclude flags.

    -
    $ enroll harvest --dangerous --out /tmp/enroll-harvest
    -
    -
    -
    -
    -
    Encrypt bundles at rest (SOPS)
    -

    Produce a single encrypted file for harvest and/or manifest output (requires SOPS to be installed).

    -

    This is especially a good idea if you are using --dangerous, which might sweep up secrets (see above).

    -
    $ enroll harvest --dangerous --out /tmp/harvest \
    +        
    +
    Encrypt bundles at rest with SOPS
    +
    + +
    $ enroll harvest --dangerous --out /tmp/harvest \
       --sops <FINGERPRINT>
    +
     $ enroll manifest --harvest /tmp/harvest/harvest.tar.gz.sops \
    -  --out /tmp/enroll-ansible --sops <FINGERPRINT>
    + --target salt \ + --out /tmp/enroll-salt \ + --sops <FINGERPRINT>
    +
    +

    Use this if you intend to store harvests/manifests long term or if you harvested with --dangerous.

    diff --git a/src/content/news/0-7-0.html b/src/content/news/0-7-0.html new file mode 100644 index 0000000..f4c5f97 --- /dev/null +++ b/src/content/news/0-7-0.html @@ -0,0 +1,115 @@ +--- +title: "Enroll 0.7.0: Puppet and Salt renderer support, and more" +html_title: "Enroll 0.7.0 News" +description: "Enroll 0.7.0 adds first-class Puppet and Salt manifest rendering alongside Ansible, along with other improvements." +date: 2026-06-17 +summary: "Enroll can now render Puppet and Salt manifests alongside Ansible, detect Flatpak and Snap packages and other obscure runtime state, and produce better organised roles/modules." +--- +
    +
    +
    Release notes
    +

    Enroll 0.7.0: Puppet and Salt join Ansible

    +

    A single harvest can now be rendered into Ansible, Puppet, or Salt output.

    +
    +
    + +
    +
    +
    +
    +
    +

    Welcome to the first of Enroll's new, erm, news section! To celebrate, Enroll 0.7.0 has been released, and makes manifest rendering target-selectable. Ansible remains the default, but Puppet and Salt are now first-class manifest targets rather than ad-hoc exports.

    + +

    Highlights

    +
      +
    • --target puppet renders Puppet module/control-repo style output., and in --fqdn mode, renders per-host Hiera data.
    • +
    • --target salt renders Salt state trees and, in --fqdn mode, Salt pillar data.
    • +
    • Ansible works basically as it always did, and is the default, but you can specify --target ansible too. As usual, in --fqdn mode, specific artifacts end up in host_vars inventory folders rather than polluting the 'golden' roles.
    • +
    • All three config management manifest renderers derive from the same harvest state. You can rendered repeatedly into different config management tools without re-harvesting the host!
    • +
    • Single-site output tries to combine package/service data by their package manager's Section (or equivalent metadata), to reduce role/module/state sprawl.
    • +
    • Flatpak and Snap detection!
    • +
    + +

    Dry-run examples

    +
    + +
    $ enroll harvest --out ./harvest
    +
    +$ enroll manifest --harvest ./harvest --target ansible --out ./ansible
    +$ ansible-playbook -i "localhost," -c local ./ansible/playbook.yml --check --diff
    +
    +$ enroll manifest --harvest ./harvest --target puppet --out ./puppet
    +$ puppet apply --modulepath ./puppet/modules ./puppet/manifests/site.pp --noop
    +
    +$ enroll manifest --harvest ./harvest --target salt --out ./salt
    +$ salt-call --local --file-root ./salt/states state.apply test=True
    +
    + +

    New grouping behaviour in roles/modules

    +

    Did you find the number of manifested roles overwhelming?

    +

    Previously, Enroll created an Ansible role (or, now, a Puppet module or Salt role) for pretty much every 'package' it found. In some cases (especially on desktops) this could result in hundreds of roles. Technically fine, but overwhelming to look at!

    + +

    As of 0.7.0, where Enroll can read that package metadata, it groups related package and service snapshots by the package manager's Section category (or comparable backend metadata), to make it less noisy. For example, network-related packages and config files might end up in a role called net. Meanwhile, vim, nano might both appear in editors, and mutt and Thunderbird may be in mail.

    + +

    If you're not a fan of this new layout, you can pass --no-common-roles to enforce the previous behaviour. Also, if you use --fqdn for host-specific data-driven output, the 'common' roles are disabled automatically, because it's then safer to avoid 'bleed in' of unnecessary package installation on other hosts from a role that otherwise 'assumes too much' for all hosts.

    + +

    Flatpak and Snap detection

    +

    Beyond deb and rpm

    +

    When using Ansible, Enroll now attempts to detect Flatpak and Snaps present on the system. For Flatpaks, this includes user-specific Flatpaks as well as system-wide ones. Manifesting to Ansible will attempt to use the community.general collection to create Flatpak and Snap tasks to enforce the presence of those packages.

    +
    +
    $ sudo ansible-playbook playbook.yml -i localhost, -c local --tags role_snap --diff
    +
    +PLAY [Apply all roles on all hosts] *******************************************************************************************************************************************************************************
    +
    +TASK [Gathering Facts] ********************************************************************************************************************************************************************************************
    +ok: [localhost]
    +
    +TASK [snap : Install system-wide snaps with full detected attributes] *********************************************************************************************************************************************
    +ok: [localhost] => (item={'channel': 'latest/stable', 'classic': False, 'dangerous': False, 'devmode': False, 'install_revision': False, 'name': 'bare', 'notes': ['base'], 'revision': 5, 'source': 'snap-list'})
    +ok: [localhost] => (item={'channel': 'latest/stable', 'classic': False, 'dangerous': False, 'devmode': False, 'install_revision': False, 'name': 'core24', 'notes': ['base'], 'revision': 1643, 'source': 'snap-list'})
    +ok: [localhost] => (item={'channel': 'latest/stable', 'classic': False, 'dangerous': False, 'devmode': False, 'install_revision': False, 'name': 'gnome-46-2404', 'notes': [], 'revision': 153, 'source': 'snap-list'})
    +ok: [localhost] => (item={'channel': 'latest/stable', 'classic': False, 'dangerous': False, 'devmode': False, 'install_revision': False, 'name': 'gtk-common-themes', 'notes': [], 'revision': 1535, 'source': 'snap-list'})
    +ok: [localhost] => (item={'channel': 'latest/stable', 'classic': False, 'dangerous': False, 'devmode': False, 'install_revision': False, 'name': 'mesa-2404', 'notes': [], 'revision': 1165, 'source': 'snap-list'})
    +ok: [localhost] => (item={'channel': 'latest/stable', 'classic': False, 'dangerous': False, 'devmode': False, 'install_revision': False, 'name': 'onionshare', 'notes': [], 'revision': 212, 'source': 'snap-list'})
    +ok: [localhost] => (item={'channel': 'latest/stable', 'classic': False, 'dangerous': False, 'devmode': False, 'install_revision': False, 'name': 'snapd', 'notes': ['snapd'], 'revision': 26865, 'source': 'snap-list'})
    +            
    +
    + +

    Please be aware that you need version 13+ of the community.general collection for this to work properly. A requirements.yml gets created with your manifest to help you install it if necessary.

    + +

    Other smaller changes

    +
      +
    • sysctl runtime parameters are now detected and would be written to /etc/sysctl.d/99-enroll.conf. Not all runtime parameters are supported.
    • +
    • .bashrc and similar files are now only harvested from user directories when --dangerous is used, since this is a common place for sensitive environment variables to be set. As always, remember that --dangerous gives better harvest coverage, but you should use --sops or some other means of your own to encrypt the harvested data at rest safely!
    • +
    • Some output during an Ansible play is hidden with no_log to avoid potentially sensitive output, particularly of systemd unit state.
    • +
    • In case you missed it in version 0.6.0: Enroll now harvests runtime iptables and ipset rules!
    • +
    + +

    See you soon..

    +

    I'm off to try and write more tests - we're at 84% coverage in pytest, and we also run a stack of 'noop' executions for Ansible, Puppet and Salt too now, in CI.

    +

    Thanks to everyone who has reached out with suggestions, constructive criticism, and bug reports! You're helping make Enroll better for everyone.

    + + +
    +
    + +
    +
    +
    Targets
    +
      +
    • --target ansible (default)
    • +
    • --target puppet
    • +
    • --target salt
    • +
    +
    +
    +
    Useful links
    + +
    +
    +
    +
    +
    diff --git a/src/content/news/_index.html b/src/content/news/_index.html new file mode 100644 index 0000000..6958573 --- /dev/null +++ b/src/content/news/_index.html @@ -0,0 +1,12 @@ +--- +title: "News" +html_title: "Enroll News" +description: "Release notes and project updates for Enroll." +--- +
    +
    +
    News
    +

    What's new in Enroll

    +

    Release notes, tips and tricks, and whatever else we've been cooking up.

    +
    +
    diff --git a/src/content/security.html b/src/content/security.html index c2a812b..2218f29 100644 --- a/src/content/security.html +++ b/src/content/security.html @@ -40,12 +40,12 @@ description: "Security posture and safe workflows for Enroll outputs."
    $ enroll harvest --out /tmp/enroll-harvest --dangerous --sops <FINGERPRINT>
     $ enroll manifest --harvest /tmp/enroll-harvest/harvest.tar.gz.sops \
    -  --out /tmp/enroll-ansible --sops <FINGERPRINT>
    + --target ansible --out /tmp/enroll-ansible --sops <FINGERPRINT>
    Important
    -
    In manifest --sops mode, you'll need to decrypt and extract the bundle before running ansible-playbook.
    +
    In manifest --sops mode, you'll need to decrypt and extract the bundle before running ansible-playbook, puppet apply, or salt-call.
    @@ -92,7 +92,7 @@ description: "Security posture and safe workflows for Enroll outputs."