Blocks, Actions, and Workspaces: The Modular Principle of Polycrate
Fabian Peter 7 Minuten Lesezeit

Blocks, Actions, and Workspaces: The Modular Principle of Polycrate

The Modular Principle of Polycrate: Explained Blocks, Actions, and Workspaces
Ganze Serie lesen (24 Artikel)

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.

  1. Install Polycrate and Build Your First Ansible Block in 15 Minutes
  2. Blocks, Actions, and Workspaces: The Modular Principle of Polycrate
  3. Linux Servers on Autopilot: System Management with Polycrate and Ansible
  4. Nginx and Let's Encrypt as a Reusable Polycrate Block
  5. Managing Docker Stacks on Linux Servers with Polycrate
  6. Many Servers, One Truth: Multi-Server Management with Polycrate Inventories
  7. Windows Automation with Polycrate: Ansible and WinRM Without Pain
  8. Windows Software Deployment without SCCM: Chocolatey and Ansible
  9. Hybrid Automation: Windows and Linux in the Same Polycrate Workspace
  10. Deploy Kubernetes Apps from the PolyHub: From Idea to Deployment in Minutes
  11. Creating Your Own Kubernetes App as a Polycrate Block: A Step-by-Step Guide
  12. Multi-Cluster Kubernetes with Polycrate: Why One Cluster, One Workspace
  13. SSH Sessions and kubectl Debugging: Polycrate as an Operations Tool
  14. Helm Charts as a Polycrate Block: More Control Over Chart Deployments
  15. Policy as Code: Automating Compliance Requirements with Polycrate
  16. Workspace Encryption: Managing Secrets in GDPR Compliance – Without External Tooling
  17. Managing Raspberry Pi and Edge Nodes with Polycrate in IoT and Edge Computing
  18. Enterprise Automation: Building, Versioning, and Sharing Blocks Within Teams
  19. Polycrate MCP: Connecting AI Assistants with Live Infrastructure Context
  20. Polycrate vs. plain Ansible: What You Gain – and Why It's Worth It
  21. The Polycrate Ecosystem: PolyHub, API, MCP, and the Future of Automation
  22. Your First Productive Polycrate Workspace: A Checklist for Getting Started
  23. Auditable Operations: SSH Sessions and CLI Activities with Polycrate API
  24. Polycrate API for Teams: Centralized Monitoring and Remote Triggering

TL;DR

  • Polycrate structures Ansible automation into three building blocks: Blocks, Actions, and Workspaces – eliminating the classic playbook sprawl and making automation discoverable and shareable.
  • Blocks come with their own configuration, playbooks, templates, and artifacts. Workspace configuration overrides block defaults via deep-merge – ideal for reusable standards plus project-specific customizations.
  • All blocks run in containers: No local Ansible setup, no Python version turbulence, and a unified toolchain for the entire team. Distribution is via an OCI registry and the PolyHub .
  • Compared to Ansible Roles, Polycrate Blocks are self-contained, versionable, shareable via registries, and provide a clear execution and configuration framework – including workflows for more complex processes.
  • ayedo develops Polycrate and provides a practical platform with official block collections in the PolyHub and documentation on Polycrate Blocks that scales from individual admins to large platform teams.

Block, Action, Workspace: The Core Principle

Polycrate builds on Ansible but gives automation a clearer form. Three concepts are central:

  • Block: A self-contained module with block.poly, playbooks, templates, and artifacts.
  • Action: A named entry point into a block (e.g., configure, patch, deploy) that executes exactly one playbook.
  • Workspace: The project context in which blocks are instantiated, configured, combined, and orchestrated in workflows.

Important: Polycrate always runs Ansible in a container. This solves the classic dependency problem:

  • No local Ansible installation
  • No Python version conflicts
  • No heterogeneous tool setups in the team

Instead of complex Ansible CLI calls like:

ansible-playbook -i inventory.yml site.yml -e "env=prod" --tags "patch"

you start a defined action:

polycrate run linux-baseline patch

The action knows its playbook, configuration, and container context. This is good DX/UX – even colleagues without deep Ansible knowledge can safely execute automations.

Blocks are described in detail in the official documentation on Polycrate Blocks .


Block Anatomy in Detail

Let’s look at a specific block that deploys Linux baseline configuration and patches. Directory structure in the workspace:

acme-corp-automation/
  workspace.poly
  inventory.yml
  blocks/
    linux-baseline/
      block.poly
      patch.yml
      harden.yml
      templates/
        motd.j2
      artifacts/
        baseline-report.md

block.poly: Definition, Configuration, and Actions

# blocks/linux-baseline/block.poly
name: linux-baseline
version: 0.1.0
kind: generic

config:
  os:
    family: linux
    reboot_after_patch: true
  packages:
    common:
      - vim
      - curl
      - htop
  motd:
    enabled: true
    text: "Managed by Polycrate – ACME Corp"

actions:
  - name: patch
    playbook: patch.yml
    description: "Installs updates and optionally reboots"
  - name: harden
    playbook: harden.yml
    description: "Applies basic hardening (SSH, Sysctl, MOTD)"

Key points:

  • config: Default values of the block. These are later overridden or supplemented in the workspace.
  • actions: Each action refers to exactly one playbook in the same directory.
  • version: Version of the block template – important once the block is shared via a registry.

Inventory in the Workspace

The inventory is always in the workspace root as a YAML file inventory.yml. Polycrate automatically sets ANSIBLE_INVENTORY.

# inventory.yml
all:
  hosts:
    server01.acme-corp.com:
      ansible_user: ubuntu
    server02.acme-corp.com:
      ansible_user: ubuntu

The inventory is YAML in the workspace root (not a classic INI-style hosts file).

Ansible Playbook: Using block.config.*

The patch.yml accesses the block configuration:

# blocks/linux-baseline/patch.yml
- name: Patch Linux with Polycrate
  hosts: all
  become: true
  gather_facts: true

  vars:
    reboot_after_patch: "{{ block.config.os.reboot_after_patch }}"

  tasks:
    - name: Update APT Cache
      ansible.builtin.apt:
        update_cache: true
      when: ansible_facts.os_family == "Debian"

    - name: Upgrade all packages (Debian/Ubuntu)
      ansible.builtin.apt:
        upgrade: dist
        autoremove: true
      when: ansible_facts.os_family == "Debian"

    - name: Install YUM/DNF updates (RHEL/CentOS)
      ansible.builtin.yum:
        name: "*"
        state: latest
      when: ansible_facts.os_family in ["RedHat", "Rocky", "AlmaLinux", "CentOS"]

    - name: Optional reboot after patches
      ansible.builtin.reboot:
        msg: "Reboot triggered by Polycrate linux-baseline.patch"
        reboot_timeout: 600
      when: reboot_after_patch | bool

The magic happens at block.config.os.reboot_after_patch: This variable does not come from vars_files or -e, but directly from block.poly or workspace configuration.


Configuration Inheritance and Deep-Merge

For a block to be truly reusable, it needs meaningful defaults – but also the ability to be overridden project-specifically. This is where Polycrate’s config inheritance (deep-merge) comes in.

Block Defaults in block.poly

As seen above:

config:
  os:
    family: linux
    reboot_after_patch: true
  packages:
    common:
      - vim
      - curl
      - htop
  motd:
    enabled: true
    text: "Managed by Polycrate – ACME Corp"

Workspace Instance with Overrides

In the workspace, define how the block should look specifically for ACME Corp:

# workspace.poly
name: acme-corp-automation
organization: acme

blocks:
  - name: linux-baseline
    from: cargo.ayedo.cloud/ayedo/infra/linux-baseline:0.3.1
    config:
      os:
        reboot_after_patch: false
      packages:
        common:
          - vim
          - curl
          - htop
          - jq
      motd:
        text: "ACME Corp – Managed Linux Server"

Important:

  • from: is the OCI registry reference including the version tag (:0.3.1). Once the block is available locally (on the first polycrate run … you can confirm automatic installation from the registry; polycrate blocks pull remains optional), it lives under blocks/cargo.ayedo.cloud/ayedo/infra/linux-baseline/ (the path under blocks/ mirrors the URL).
  • Under config:, you override and extend the block defaults.

Deep-Merge Principle

Polycrate deeply merges block config and workspace config. This means:

  • Maps are recursively merged.
  • Scalar values are overwritten.
  • Lists are replaced by default, not appended (best practices see Best Practices for Blocks ).

For our example, the effective configuration at runtime is:

# Effective block.config.* in the action linux-baseline.patch
os:
  family: linux              # from block.poly
  reboot_after_patch: false  # overridden in workspace
packages:
  common:                    # list completely replaced
    - vim
    - curl
    - htop
    - jq
motd:
  enabled: true              # from block.poly
  text: "ACME Corp – Managed Linux Server"  # overridden in workspace

This achieves:

  • A reusable standard block (e.g., maintained by a platform team)
  • Fine tenant/project-specific customizations in the workspaces
  • Clear separation between block definition and usage

More details on inheritance can be found in the documentation on Inheritance in Polycrate .


Polycrate Variables in the Ansible Playbook

Polycrate provides a range of variables in every action without you having to explicitly pass them:

  • block.config.* – the merged block configuration (as shown above)
  • workspace.* – information and configuration of your workspace
  • action.* – metadata about the currently running action

An extended example:

# blocks/linux-baseline/harden.yml
- name: Linux Baseline Hardening with Polycrate
  hosts: all
  become: true
  gather_facts: false

  tasks:
    - name: Disable SSH Root Login
      ansible.builtin.lineinfile:
        path: /etc/ssh/sshd_config
        regexp: '^PermitRootLogin'
        line: 'PermitRootLogin no'
        create: false
        backup: true

    - name: Set MOTD if enabled
      ansible.builtin.copy:
        content: "{{ block.config.motd.text }}"
        dest: /etc/motd
      when: block.config.motd.enabled | bool

    - name: Log Workspace Name
      ansible.builtin.lineinfile:
        path: /var/log/polycrate.log
        line: "Hardened by workspace {{ workspace.name }} via action {{ action.name }}"
        create: yes

Here, all three levels come together:

  • block.config.motd.* – block or workspace-specific configuration
  • workspace.name – e.g., acme-corp-automation
  • action.name – e.g., harden

When working with secrets (e.g., SSH keys, certificates), these typically reside under artifacts/secrets/ in the workspace and are protected by the integrated Workspace Encryption with age. In the playbook, they are available via workspace.secrets['filename'].


From Action to Command: Execution with polycrate run

You can start the harden action above simply by:

cd acme-corp-automation

polycrate run linux-baseline harden

Polycrate ensures:

  • A container with Ansible and the defined toolchain is started
  • The workspace is mounted into the container
  • Environment variables (e.g., ANSIBLE_INVENTORY) are set
  • Variables block, workspace, action are provided in the play
  • The action’s playbook (e.g. harden.yml in the block directory, typically blocks/cargo.ayedo.cloud/ayedo/infra/linux-baseline/ once the block is available locally) is executed against the defined inventory

With plain Ansible, you would need to:

  • Provide Ansible and dependencies locally
  • Manually manage ansible.cfg and inventory paths
  • Assemble and pass extra variables yourself

With Polycrate, this context is modeled in block and workspace.

More on Ansible integration is described in the official Ansible Integration of Polycrate .


Versioning and Distribution via OCI Registry

You use the same registry reference (cargo.ayedo.cloud/...) to share and version blocks—in your team or via PolyHub .

Polycrate uses OCI registries—the same protocol as Docker/OCI container images. The from line in the workspace is the canonical source; the version is always a tag at the end (:0.3.1).

Key points:

  • Never use :latest—you want reproducible automation and controlled updates.
  • For local development (before push), you create the block under blocks/<name>/—once published, you still reference it in workspace.poly with the full registry URL (see Inheritance ).

Pushing and Pulling Blocks from the Registry

As a block author, you want to share your work:

# In the block directory
cd acme-corp-automation/blocks/linux-baseline

# Push to the registry (version is in block.poly)
polycrate blocks push cargo.ayedo.cloud/acme/infra/linux-baseline

On another system or in another workspace, you can then use the same block:

# Make block available locally from registry (optional – often the first polycrate run is enough)
polycrate blocks pull cargo.ayedo.cloud/acme/infra/linux-baseline:0.1.0

You often do not need an explicit polycrate blocks pull: if the block is missing locally, Polycrate detects that on the first polycrate run … and asks whether to install the block automatically. Then reference it in workspace.poly with this URL. This is shareable automation in its purest form: built once, usable many times – in your team.

Ähnliche Artikel

Kontakt aufnehmen