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