TL;DR
- You can implement standardized Windows software deployment without expensive SCCM infrastructure – using Chocolatey as a package manager and Ansible as the control center.
- Polycrate packages Ansible, Python, and the entire toolchain into a container, solving the dependency problem and bringing structure to your automation through reusable blocks.
- A software baseline is modeled as a Polycrate block: Office, Developer, and Server profiles are versioned, reproducible, and executable via
polycrate run– without a local Ansible installation. - Through groups in the inventory, you can implement a rolling rollout: first test workstations, then broad deployment into production.
- ayedo supports teams in modeling such scenarios in a practical software deployment workshop and sustainably bringing them into operation.
The SCCM Problem: When Overhead Outweighs Benefits
Many Windows admins know the dilemma: Microsoft Endpoint Configuration Manager (formerly SCCM) is powerful, but:
- Licensing costs are high – particularly hard to justify for small and medium environments.
- The infrastructure is complex (SQL Server, Site Server, distribution points).
- Changes require long lead times because everything must be "properly" modeled in SCCM first.
- Scripts and small automation components disappear in the GUI or in individual scripts and are hard to version.
At the same time, the need is clear:
- Standard software like 7-Zip, browsers, PDF viewers, admin tools.
- Different baselines: Office users vs. developers vs. servers.
- Reproducibility: A new machine should be at the same state as existing ones in 30–60 minutes.
This is where Chocolatey, Ansible, and Polycrate form an exciting combination: You achieve many of the benefits of SCCM (standardized, repeatable, auditable) without its costs and complexity.
Chocolatey as a Windows Package Manager
Chocolatey is – simply put – apt or yum for Windows:
- Packages are installed from repositories (e.g., the public Chocolatey repository or an internal one).
- Installation, update, and uninstallation are handled via a consistent CLI command (
choco install,choco upgrade,choco uninstall). - Many standard tools are already available as ready-made packages: browsers, tools, runtime environments, etc.
For admins, this means:
- No more manual MSI downloads and click installers.
- Version pinning is trivial:
choco install 7zip --version 22.1.0. - Packages are scriptable and thus ideal for Ansible.
Important: In production environments, you usually use an internal Chocolatey repository. The principle remains the same – only the source URL changes.
win_chocolatey in Ansible: Installing, Updating, Removing Packages
Ansible comes with its own module, win_chocolatey, which encapsulates exactly this Chocolatey functionality.
A minimal example playbook (without Polycrate, just for context):
- name: Windows Baseline with Chocolatey
hosts: windows_office
gather_facts: false
tasks:
- name: Ensure Chocolatey is installed
ansible.windows.win_chocolatey:
name: chocolatey
state: present
- name: Install 7zip
ansible.windows.win_chocolatey:
name: 7zip
version: '22.1.0'
state: present
- name: Install Google Chrome
ansible.windows.win_chocolatey:
name: googlechrome
version: '118.0.5993.90'
state: present
With plain Ansible, this means for you:
- You need a working Python and Ansible installation locally.
- Everyone on the team must use compatible versions, otherwise "works on my machine" effects occur.
- You must organize and document Ansible configuration (inventory,
ansible.cfg, collections) yourself.
This is where Polycrate comes into play.
Why Polycrate Comes into Play
Polycrate does not replace Ansible – it provides a clean, reusable packaging for Ansible.
The relevant advantages in this scenario:
- Dependency Problem Solved
- Ansible runs in a container provided by Polycrate.
- No local Python chaos, no different Ansible versions in the team.
- Additional tools (e.g.,
choco,pwsh,kubectl) are consistently added via aDockerfile.polyor a setup script. - All colleagues use exactly the same toolchain – whether on a laptop, VDI, or terminal server.
More on this in the Ansible Integration of Polycrate.
- Guardrails and Sharable Automation
- Instead of individual playbooks, there are blocks that are clearly structured (with
block.poly). - These blocks can be versioned and shared in an OCI registry.
- A block
registry.acme-corp.com/acme/windows/windows-software-baseline:0.1.0is a "product" that your team can use, test, and share – internally or via the PolyHub.
Best practices can be found under Polycrate Best Practices.
- Secure Configuration
- License keys, internal repository URLs, or admin credentials are stored in the workspace and encrypted with
polycrate workspace encrypt. - The built-in age encryption facilitates compliance without additional tooling.
- Details on this: Workspace Encryption in Polycrate.
For you as a Windows admin, this means: You focus on the playbooks and software baselines – Polycrate takes care of the toolchain, structure, and reuse.
The Workspace: acme-corp-automation
We build an example for the fictional acme-corp.com:
- Three target groups:
office-workstationdeveloper-workstationserver- Each with its own host groups in the Ansible inventory.
- A shared software baseline as a Polycrate block.
A minimal inventory.yml in the workspace root might look like this:
all:
hosts:
win-office-01.acme-corp.com: {}
win-office-10.acme-corp.com: {}
win-dev-01.acme-corp.com: {}
win-srv-01.acme-corp.com: {}
children:
windows_office_test:
hosts:
win-office-01.acme-corp.com:
vars:
ansible_user: Administrator
ansible_connection: winrm
ansible_winrm_server_cert_validation: ignore
windows_office_prod:
hosts:
win-office-10.acme-corp.com:
vars:
ansible_user: Administrator
ansible_connection: winrm
ansible_winrm_server_cert_validation: ignore
windows_developer:
hosts:
win-dev-01.acme-corp.com:
vars:
ansible_user: Administrator
ansible_connection: winrm
ansible_winrm_server_cert_validation: ignore
windows_servers:
hosts:
win-srv-01.acme-corp.com:
vars:
ansible_user: Administrator
ansible_connection: winrm
ansible_winrm_server_cert_validation: ignore
No passwords in the inventory—set ansible_password in playbooks from secrets.poly / workspace.secrets.* (see Workspace encryption).
Polycrate automatically sets ANSIBLE_INVENTORY to this file. You don't need an additional ansible.cfg.
Our workspace.poly defines which blocks exist:
name: acme-corp-automation
organization: acme
blocks:
- name: windows-office-baseline
from: registry.acme-corp.com/acme/windows/windows-software-baseline:0.1.0
config:
profile: office
target_group: windows_office_test
- name: windows-developer-baseline
from: registry.acme-corp.com/acme/windows/windows-software-baseline:0.1.0
config:
base_profile: office
profile: developer-extra
target_group: windows_developer
- name: windows-server-baseline
from: registry.acme-corp.com/acme/windows/windows-software-baseline:0.1.0
config:
profile: server
target_group: windows_servers
allow_reboot: false
from: is the full OCI registry reference including the version tag; registry.acme-corp.com is a fictional example (not tied to a specific production registry).
Important:
- All three entries use the same block version; unpacked it lives under
blocks/registry.acme-corp.com/acme/windows/windows-software-baseline/. - Differences lie only in the configuration (
profile,base_profile, etc.). - This already gives you a form of block inheritance through configuration: Developer "inherits" Office plus additional tools.
More on generic inheritance in Polycrate can be found in the documentation on Inheritance.
The Polycrate Block: name as full registry reference in block.poly
Now we define the block itself under blocks/registry.acme-corp.com/acme/windows/windows-software-baseline/block.poly (directory layout = registry path). In block.poly, name must be the full registry URL—the same string as from: in workspace.poly, without the version tag (no :0.1.0 suffix). In short: from: = name + : + tag. That is not the same identifier as the block instances in workspace.poly (windows-office-baseline, windows-developer-baseline, …).
# name = from: without tag (full registry path)
name: "registry.acme-corp.com/acme/windows/windows-software-baseline"
version: 0.1.0
kind: generic
config:
chocolatey_source: "https://community.chocolatey.org/api/v2/"
# Central definition of software profiles
software_profiles:
office:
- name: 7zip
version: "22.1.0"
- name: googlechrome
version: "118.0.5993.90"
- name: sumatrapdf
version: "3.4.6"
developer-extra:
- name: vscode
version: "1.84.0"
- name: git
version: "2.44.0"
- name: postman
version: "10.21.0"
server:
- name: sysinternals
version: "2023.9.23"
- name: notepadplusplus
version: "8.6.2"
actions:
- name: baseline
playbook: baseline.yml
A few points about this:
polycrate run windows-office-baseline …and similar commands use the instance name fromworkspace.poly, not the full registry reference inname(block.poly).- Version pinning: All packages have specific version specifications.
latestwould be more convenient, but you lose all reproducibility. Your goal: "A workstation in April 2026 looks the same as one in June – as long as you don't intentionally increase the block version." - The profiles
office,developer-extra, andserverare centrally defined. Workspace-specific, you only decide which profile is used.
Once this block runs stably, you can push it to your OCI registry—for example (fictional registry, same path scheme as above):
registry.acme-corp.com/acme/windows/windows-software-baseline:0.1.0
This makes your software baseline a sharable automation for other teams.
The Ansible Playbook: Combine and Deploy Profiles
Now we need the playbook blocks/registry.acme-corp.com/acme/windows/windows-software-baseline/baseline.yml, which uses Chocolatey and combines profiles:
- name: Windows Software Baseline with Chocolatey
hosts: "{{ block.config.target_group }}"
gather_facts: false
vars:
choco_source: "{{ block.config.chocolatey_source }}"
base_profile_name: "{{ block.config.base_profile | default(None) }}"
profile_name: "{{ block.config.profile }}"
tasks:
- name: Ensure Chocolatey is installed
ansible.windows.win_chocolatey:
name: chocolatey
state: present
- name: Load base packages (e.g., office)
ansible.builtin.set_fact:
base_packages: "{{ block.config.software_profiles[base_profile_name] | default([]) }}"
- name: Load profile packages (e.g., developer-extra or server)
ansible.builtin.set_fact:
profile_packages: "{{ block.config.software_profiles[profile_name] }}"
- name: Merge final list of packages
ansible.builtin.set_fact:
final_packages: "{{ base_packages + profile_packages }}"
- name: Install/update all packages to desired version
ansible.windows.win_chocolatey:
name: "{{ item.name }}"
version: "{{ item.version }}"
state: present
source: "{{ choco_source }}"
loop: "{{ final_packages }}"
- name: Optional: Allow reboot after installation
ansible.windows.win_reboot:
msg: "Reboot after baseline installation by Polycrate/Ansible"
pre_reboot_delay: 60
when: block.config.allow_reboot | default(true)
Important details:
hosts: "{{ block.config.target_group }}"ensures that you can control which host group is addressed per block instance – ideal for rolling rollouts.base_profileandprofileimplement our configuration inheritance:- Office workstations use
profile: officewithoutbase_profile. - Developer workstations use
base_profile: officeandprofile: developer-extra—that is “developer inherits office plus extra tools”. - The
ansible.windows.win_chocolateyandansible.windows.win_rebootmodules come from the Windows collections; Polycrate provides a suitable Ansible environment in the container.
With plain Ansible you would have to orchestrate all variable sources (e.g. group_vars, host_vars, extra_vars) and CLI calls yourself. With Polycrate, these parameters become part of the block and are versioned.
Rolling rollout: test group first, then production
The rolling rollout follows naturally from inventory groups:
- Define the test group
In the inventory.yml above, windows_office_test and windows_office_prod exist. In workspace.poly, the office baseline’s target_group is initially windows_office_test.
- Run the baseline on the test group
polycrate run windows-office-baseline baseline
Polycrate:
- Starts a container with Ansible and the required collections.
- Loads the workspace (including inventory) into the container.
- Runs
baseline.ymlwith the parameters fromwindows-office-baseline.
- After tests: switch target to production
Change workspace.poly:
- name: windows-office-baseline
from: registry.acme-corp.com/acme/windows/windows-software-baseline:0.1.0
config:
profile: office
target_group: windows_office_prod
Save the workspace and commit to Git—so you record from which workspace definition version the baseline rolled out to production.
- Roll out to production
polycrate run windows-office-baseline baseline
You can bundle these steps in a Polycrate workflow later if you want. For many teams it is already a big win that test vs. production is clear from inventory groups and Git history.
Frequently asked questions
Do I need a central Chocolatey repository for this setup?
No—you can use the public Chocolatey repository as in the example (https://community.chocolatey.org/api/v2/). Many organizations still prefer an internal repository:
- Control over which packages and versions are allowed.
- Better reproducibility because you are not surprised by upstream changes.
- Less reliance on external services (offline / restricted internet).
In the Polycrate block, the source is just a config variable (chocolatey_source); you can switch from public to internal without rewriting the playbook.
How does Polycrate help with compliance and audits?
Two points matter most:
- Versioned blocks and workspaces: You can always see which block version (e.g.
registry.acme-corp.com/acme/windows/windows-software-baseline:0.1.0) ran with which workspace configuration against which systems—supporting audit traceability. - Workspace encryption: Sensitive data such as credentials, license keys, or internal URLs can live in the workspace and be encrypted with
polycrate workspace encrypt, which helps meet requirements from standards such as ISO 27001 or internal policies without an external secrets manager.
More in the documentation on workspace encryption.
How is this different from “Ansible only”—is Polycrate worth it?
Plain Ansible can implement everything in this article. The difference is everything around it:
- You must standardize Ansible, Python, collections, inventory layout, and playbooks yourself and enforce them in the team.
- Sharing automation is mostly Git repos, folder conventions, and docs—without a clear block/versioning model.
- New colleagues need more setup time before everything works.
Polycrate adds:
- A predefined Ansible toolchain container that is the same everywhere.
- The block model as a guardrail against playbook sprawl.
- Registry integration to distribute automation as reusable building blocks (e.g. via PolyHub).
In short: Polycrate pays off when you have more than a few ad-hoc playbooks and want automation as a “product” in the team.
More questions? See our FAQ
From theory to practice
With Chocolatey, Ansible, and Polycrate you have a practical path to Windows software deployment without SCCM infrastructure:
- You have seen how a Windows software baseline is modeled as a Polycrate block—including clearly versioned package lists.
- You use configuration to serve different targets (office, developer, server) from the same block and apply inheritance in daily use.
- Modules such as
win_chocolateykeep automation idempotent and traceable, while Polycrate handles the container toolchain, workspace structure, and reuse. - Rolling rollouts across test and production groups become a
target_groupchange and a Git commit—not a new SCCM deployment.
We often see Windows teams start with building blocks like these and then add patch management, local admin groups, browser hardening, and combined workspaces for Linux, Windows, and edge environments.
If you want to walk through this approach in your environment—from the first workspace structure to a tested rollout to your test group—we are happy to join you in a shared software deployment workshop.