Your First Productive Polycrate Workspace: A Checklist for Getting Started
TL;DR A well-named, clearly structured Polycrate workspace is half the battle: a consistent name …
Diese Serie zeigt Schritt für Schritt, wie Ansible mit Polycrate zu einer strukturierten, teilbaren und compliance-fähigen Automatisierungsplattform wird – von den Grundlagen bis zu Enterprise-Szenarien.
linux_group and windows_group, encapsulate Linux and Windows playbooks in their own blocks, and manage everything from one workspace.config in workspace.poly – explicitly per block (in the hybrid example the same values twice, without YAML anchors). There is no Jinja templating in .poly files.artifacts/secrets/ and are encrypted with the workspace; sensitive block configuration such as win_admin_password belongs in secrets.poly (not as artifacts/secrets/win_admin_password) – see Workspace encryption.polycrate workflows run … commands, without needing to install Ansible, Python, or pywinrm locally.If you work in a larger company, your world usually looks like this:
In many teams, these worlds end up in separate silos:
Polycrate turns this around: one workspace, two blocks (Linux and Windows), one inventory, one workflow. And everything runs in a container, which takes away the classic dependency problem with Ansible:
In this post, we build just such a hybrid workspace – including inventory, blocks, playbooks, secrets, and workflow.
The workspace is the central hub. Here you define:
configconfig in workspace.poly (e.g. container image – see Configuration), not as a free-form bag for arbitrary automation variablesThere is no template substitution in workspace.poly or block.poly (no Jinja2); values are literal YAML or merged from secrets.poly. See Important limitations.
workspace.poly – set DNS/NTP as literal values in each block’s config (here the same values in linux-mgmt and windows-mgmt – no YAML anchors). SSH: Polycrate configures the Ansible environment in the action container (including ANSIBLE_PRIVATE_KEY_FILE) – you do not put ssh_private_key in block config or ansible_ssh_private_key_file in the playbook.
name: acme-corp-automation
organization: acme
blocks:
- name: linux-mgmt
from: registry.acme-corp.com/acme/infra/linux-mgmt:0.1.0
config:
dns_server: "10.0.0.10"
ntp_server: "time.acme-corp.com"
- name: windows-mgmt
from: registry.acme-corp.com/acme/infra/windows-mgmt:0.1.0
config:
dns_server: "10.0.0.10"
ntp_server: "time.acme-corp.com"
win_admin_user: "ACME\\ansible-svc"
workflows:
- name: hybrid-maintenance
steps:
- name: update-linux
block: linux-mgmt
action: update-linux
- name: update-windows
block: windows-mgmt
action: update-windowssecrets.poly (sensitive block configuration, merged with workspace.poly and overrides on key clash):
blocks:
- name: windows-mgmt
config:
win_admin_password: "…"The WinRM password is not an artifact path: it lives under blocks[].config in secrets.poly, not as a file artifacts/secrets/win_admin_password. That matches Workspace encryption for secrets.poly (sensitive block configuration) versus files under artifacts/secrets/.
Important:
workspace.poly does have a config key, but it is meant for workspace/toolchain settings (e.g. config.image), not for arbitrary global variables such as DNS or inventory parameters – those belong in per-block config (and secrets.poly when sensitive).block.poly → workspace.poly → secrets.poly – see Configuration.from: contains the full OCI registry reference including the version tag (like container images); registry.acme-corp.com is an example (not a reference to any specific production registry).blocks/registry.acme-corp.com/acme/infra/linux-mgmt/ and …/windows-mgmt/ respectively (directory tree mirrors the registry path).config merge and inheritance: inheritance and workspaces.hybrid-maintenance sequentially calls Linux and Windows actions.Polycrate always uses a YAML inventory in the workspace root under inventory.yml. No INI files, no per-block inventory.
As in the multi-server article, every host appears once under all.hosts; under children you only assign them to groups so Ansible and polycrate ssh share the same canonical host list. Put shared Linux defaults under all.vars; WinRM settings belong on the Windows group because they apply only there.
Our hybrid inventory with two groups:
all:
vars:
ansible_ssh_common_args: "-o StrictHostKeyChecking=no"
ansible_python_interpreter: /usr/bin/python3
hosts:
linux01.acme-corp.com:
ansible_user: ubuntu
linux02.acme-corp.com:
ansible_user: ubuntu
win01.acme-corp.com: {}
win02.acme-corp.com: {}
children:
linux_group:
hosts:
linux01.acme-corp.com:
linux02.acme-corp.com:
windows_group:
hosts:
win01.acme-corp.com:
win02.acme-corp.com:
vars:
ansible_connection: winrm
ansible_winrm_transport: credssp
ansible_winrm_server_cert_validation: ignoreA few points about this:
all.hosts (or as a default in all.vars). Polycrate supplies the private key to Ansible via container environment variables (ANSIBLE_PRIVATE_KEY_FILE, etc.) – no ssh_private_key in workspace.poly and no ansible_ssh_private_key_file in the playbook.ansible_connection: winrm etc.) are in the vars of group windows_group.block.config.win_admin_user and block.config.win_admin_password.Polycrate automatically sets the environment variable ANSIBLE_INVENTORY when starting the action, so you don’t need to append -i inventory.yml to the Ansible CLI – Polycrate takes care of that for you. More on this in the Ansible integration.
Hybrid environments also mean hybrid authentication:
artifacts/secrets/ (e.g. id_rsa) and is encrypted with the workspace. For Ansible, Polycrate wires access in the action container via environment (ANSIBLE_PRIVATE_KEY_FILE) – no explicit ssh_private_key in workspace.poly and no ansible_ssh_private_key_file in the playbook.config in workspace.poly; put the password in secrets.poly under the matching block (windows-mgmt), not in workspace.poly, so it merges into block.config and is not committed in plain text (or use certificates instead of a password, depending on your setup).polycrate workspace encrypt encrypts secrets.poly and files under artifacts/secrets/ (see Workspace encryption). You activate and manage this via the CLI, for example:
# Encrypt secrets (e.g., before a Git commit)
polycrate workspace encrypt
# Decrypt for working
polycrate workspace decryptIn Ansible playbooks (not in .poly files), you use block.config.* – values come from the merged block.poly + workspace.poly + secrets.poly.
This achieves two important things:
Let’s start with the Linux part. The block encapsulates everything you want to do for Linux servers – here as an example:
In block.poly, name must be the full registry URL – the same string as in from: in workspace.poly, without the version tag (:0.1.0). In short: from: = name + : + tag. That is not the same identifier as the block instance linux-mgmt in workspace.poly. The file lives at blocks/registry.acme-corp.com/acme/infra/linux-mgmt/block.poly (directory tree mirrors the registry path).
# name = from: without tag (full registry path)
name: "registry.acme-corp.com/acme/infra/linux-mgmt"
version: 0.1.0
kind: generic
config:
dns_server: ""
ntp_server: ""
actions:
- name: update-linux
description: Update Linux servers and set basic configuration
playbook: linux_update.ymlThe associated Ansible playbook blocks/registry.acme-corp.com/acme/infra/linux-mgmt/linux_update.yml:
- name: Update Linux servers and set basic config
hosts: linux_group
become: true
gather_facts: true
vars:
dns_server: "{{ block.config.dns_server }}"
ntp_server: "{{ block.config.ntp_server }}"
tasks:
- name: Update package list (Debian family)
ansible.builtin.apt:
update_cache: true
when: ansible_os_family == 'Debian'
- name: Upgrade packages (Debian family)
ansible.builtin.apt:
upgrade: dist
when: ansible_os_family == 'Debian'
- name: Upgrade packages (RedHat family)
ansible.builtin.yum:
name: "*"
state: latest
when: ansible_os_family == 'RedHat'
- name: Set DNS server in /etc/resolv.conf
ansible.builtin.lineinfile:
path: /etc/resolv.conf
regexp: '^nameserver'
line: "nameserver {{ dns_server }}"
when: ansible_os_family in ['Debian', 'RedHat']
- name: Install NTP package (Debian family)
ansible.builtin.package:
name: chrony
state: present
when: ansible_os_family == 'Debian'
- name: Configure NTP server in chrony.conf
ansible.builtin.lineinfile:
path: /etc/chrony/chrony.conf
regexp: '^server'
line: "server {{ ntp_server }} iburst"
when: ansible_os_family == 'Debian'
- name: Restart chrony
ansible.builtin.service:
name: chrony
state: restarted
enabled: true
when: ansible_os_family == 'Debian'Key points:
hosts: linux_group – we work on the Linux hosts from the shared inventory, not on localhost.ansible_os_family controls OS-specific tasks (when: conditions). This allows you to handle Debian, RedHat, and others in the same playbook.block.config.*. SSH: Polycrate sets ANSIBLE_PRIVATE_KEY_FILE in the container – no ssh_private_key in workspace.poly and no ansible_ssh_private_key_file in the playbook. See Ansible integration.You can execute the action with:
polycrate run linux-mgmt update-linuxYou don’t have to worry about the local installation of Ansible, Python, or SSH client – Polycrate starts a container with a complete toolchain (including Ansible, Python, SSH), as described in the Best Practices.
The second block takes care of Windows hosts:
blocks/registry.acme-corp.com/acme/infra/windows-mgmt/block.poly – name again as the full registry path without tag, analogous to the Linux block:
# name = from: without tag (full registry path)
name: "registry.acme-corp.com/acme/infra/windows-mgmt"
version: 0.1.0
kind: generic
config:
dns_server: ""
ntp_server: ""
win_admin_user: ""
win_admin_password: ""
actions:
- name: update-windows
description: Update Windows servers and set basic configuration
playbook: windows_update.ymlThe playbook blocks/registry.acme-corp.com/acme/infra/windows-mgmt/windows_update.yml:
- name: Update Windows hosts and set basic config
hosts: windows_group
gather_facts: true
vars:
ansible_user: "{{ block.config.win_admin_user }}"
ansible_password: "{{ block.config.win_admin_password }}"
dns_server: "{{ block.config.dns_server }}"
ntp_server: "{{ block.config.ntp_server }}"
tasks:
- name: Check reachability
ansible.windows.win_ping: {}
- name: Install Windows updates
ansible.windows.win_updates:
category_names:
- CriticalUpdates
- SecurityUpdates
reboot: yes
- name: Set DNS server on network adapter
ansible.windows.win_dns_client:
adapter_names: "*"
ipv4_addresses:
- "{{ dns_server }}"
when: ansible_os_family == 'Windows'
- name: Configure Windows Time Service
ansible.windows.win_shell: |
w32tm /config /manualpeerlist:"{{ ntp_server }}" /syncfromflags:manual /reliable:yes /update
when: ansible_os_family == 'Windows'
- name: Restart Windows Time Service
ansible.windows.win_service:
name: W32Time
state: restarted
when: ansible_os_family == 'Windows'Again:
hosts: windows_group uses the same inventory.yml as the Linux block.ansible_user and ansible_password come from the encrypted workspace configuration via block.config, not in plain text from the inventory.ansible_os_family == 'Windows' guards OS-specific tasks if you later extend the group (e.g. with appliances).Run the action with:
polycrate run windows-mgmt update-windowsBecause execution runs in a container, dependencies such as pywinrm are already included. With plain Ansible you would need to ensure on every admin machine:
ansible.windows collection installed,Polycrate removes that setup overhead – the strength of the container model with Ansible, as described in the Ansible integration.
The benefit of a shared workspace shows in the workflow. We already defined hybrid-maintenance in workspace.poly:
workflows:
- name: hybrid-maintenance
steps:
- name: update-linux
block: linux-mgmt
action: update-linux
- name: update-windows
block: windows-mgmt
action: update-windowsRun a full maintenance cycle with:
polycrate workflows run hybrid-maintenanceWhat happens:
update-linux action of block linux-mgmt.update-windows on block windows-mgmt.You can extend workflows with pre-checks (e.g. free disk space), maintenance windows, or post-checks (e.g. service health). Workflows connect blocks and help avoid unstructured playbook sprawl. See Workflows.
Compared to a classic setup, with plain Ansible you would typically:
ansible-playbook commands to run in which order.With Polycrate you get:
kubectl or helm ship in a defined toolchain – no workstation setup drift.linux-mgmt and windows-mgmt blocks can be shared via an OCI registry – internally or through PolyHub (reference scheme like registry.acme-corp.com/acme/infra/windows-mgmt:0.1.0; see PolyHub).block.poly, workspace.poly, and workflows (Best Practices).polycrate run windows-mgmt update-windows or polycrate workflows run hybrid-maintenance instead of raw Ansible CLI.Ansible remains the automation engine; Polycrate adds structure, reuse, and team focus.
Yes. The inventory can have any number of groups, and playbooks can branch on ansible_os_family or other facts. For macOS or appliances, add groups and either new blocks or more actions on existing blocks. Keep the pattern: clear blocks, clear inventory, clear workflows.
Polycrate workspace encryption uses age and encrypts sensitive files (SSH keys, passwords) in the repository. That gives you separation of config and plaintext secrets, traceable key rotation via Git history and CLI, and technical controls that map well to ISO 27001 and GDPR expectations (e.g. protecting access to production systems). Details: Workspace encryption.
We often work with teams that have grown Windows domains, new Linux workloads, and compliance requirements. In a hybrid workshop we shape:
On request we integrate monitoring, CMDB, ticketing, and help you build your own block registry.
More questions? See our FAQ.
With the hybrid workspace in this article you gain three things:
linux-mgmt, windows-mgmt) wrap OS-specific playbooks; workflow hybrid-maintenance defines the sequence.workspace.poly (and secrets.poly for passwords); the SSH key as a file under artifacts/secrets/, WinRM password in secrets.poly – with encryption enabled you only commit .age artifacts.That turns the classic patch day – often a mix of manual steps, RDP, and ad-hoc scripts – into a reproducible, documented flow: a clear entry point (polycrate workflows run hybrid-maintenance), traceable changes (Git history plus Ansible logs), and reusable blocks for other workspaces or teams (internal registry or PolyHub).
At ayedo we help teams move from isolated scripts to reusable building blocks. Whether you come from Linux, Windows, or compliance – a shared hybrid workspace is a solid foundation for policies, hardening, monitoring, or self-service for other teams.
If you want to structure your hybrid infrastructure this way and learn from other environments, our Hybrid Infrastructure Workshop is a good starting point.
TL;DR A well-named, clearly structured Polycrate workspace is half the battle: a consistent name …
TL;DR Plain Ansible is a powerful tool for ad-hoc automation, quick scripts, and simple setups – but …
TL;DR Set up WinRM properly once with HTTPS, certificate, and firewall rules, and you’ll have …