Policy as Code: Automating Compliance Requirements with Polycrate
Fabian Peter 11 Minuten Lesezeit

Policy as Code: Automating Compliance Requirements with Polycrate

Automate compliance requirements: Policy as Code with Ansible and Polycrate
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

  • Manual compliance checking with Excel lists is slow, error-prone, and hardly reproducible – with Policy as Code, you describe your requirements once and then have them automatically checked.
  • With Polycrate, you encapsulate Ansible entirely in a container: no local installation, no Python version conflicts, the same tooling version for everyone involved – ideal for repeatable compliance checks.
  • A dedicated “Compliance Block” separates checking (check) and correcting (remediate), generates audit reports as JSON/CSV, and stores them structured in the workspace – including Git history as an audit trail.
  • NIS-2 (measures applicable from 18.10.2024) and GDPR (effective since 25.05.2018) require demonstrable technical and organizational measures. With Policy as Code, you can automatically check specific controls (e.g., CIS Benchmarks).
  • ayedo supports you with open-source tooling, Platform Engineering, and specially tailored workshops to bring compliance automation safely and understandably into your organization.

Why Manual Compliance Checking Hits Its Limits

Many compliance teams still work with Excel checklists or Word documents:

  • Column A: Requirement (e.g., “Disable SSH root login”)
  • Column B: Status (“OK” / “not OK”)
  • Column C: Comment (“checked on 12.02.2026 by …”)

This works as long as you have few systems and infrequent audits. At the latest with:

  • hundreds of servers (Linux and Windows),
  • recurring audits (internal, external, customers),
  • new requirements due to NIS-2 or stricter internal policies

the model becomes fragile:

  • Time-consuming: Every check is manual work.
  • Error-prone: Typos, copy & paste, forgotten steps.
  • Not reproducible: Two people check the same system – results may vary.
  • Weak in audit: “How exactly did you check this?” can often only be answered verbally.

Policy as Code turns this around: The checklist becomes executable code. The question is no longer “Who checked what and how?”, but:

  • “Which version of the check logic ran when against which systems?”

This is where Polycrate with Ansible comes in – without you needing to be a developer.


Policy as Code: Compliance as a Repeatable Process

Policy as Code means: Compliance requirements are described in a machine-readable form. For our purposes:

  • YAML files as playbooks (Ansible),
  • clearly defined tasks per requirement,
  • automated execution against defined systems.

Example: A CIS benchmark requires that /etc/passwd is readable but not writable for “Others”. Instead of ticking “OK” in Excel, you define a task:

  • Check file permissions
  • Compare with target value
  • Report “compliant” or “non-compliant”

The advantage:

  • Reproducible: The same logic runs every week, every month, in every audit.
  • Traceable: Changes to the policy are visible as code changes.
  • Automatable: Hundreds of hosts are not much more expensive than one host.

With Polycrate, you link these playbooks to a structured environment (workspace), container-based tooling, and Git – making Policy as Code manageable for compliance officers.


Polycrate as a Foundation for Automated Compliance

What distinguishes Polycrate from “plain” Ansible from a compliance perspective?

No Dependencies on Laptops

With classic Ansible, you often have to:

  • Install Python in the correct version,
  • Synchronize Ansible versions within the team,
  • Manually maintain additional tools (kubectl, Helm, Python libraries).

The “dependency problem” leads to:

  • Different results depending on the laptop,
  • Security risks due to outdated packages.

With Polycrate:

  • Ansible always runs in a container.
  • Comes with a complete toolchain (Ansible, Python, possibly kubectl/Helm).
  • The environment is identical on all workstations.

You start a compliance check with one command, without worrying about the local technology. Details on Ansible integration can be found in the documentation on Ansible and Polycrate.

Guardrails Instead of Playbook Sprawl

Polycrate forces your automation into a clear structure:

  • Workspace (e.g., acme-corp-automation)
  • Blocks (e.g., cis-linux-baseline)
  • Actions per block (e.g., check, remediate)

This prevents loose playbooks from lying around everywhere that no one can assign anymore. More on this in the Best Practices.

Encrypted Workspace for Sensitive Data

Compliance checks often work with:

  • Server addresses,
  • Service accounts,
  • Configuration details.

The Polycrate workspace can be encrypted with age – without an additional tool like Vault. This keeps sensitive information protected in the repository. The details are in the documentation on Workspace Encryption.

Git as an Audit Trail

A central component for audits: every change, every check, every report is traceable in Git. Polycrate works ideally with Git, for example:

  • Playbooks and block configurations in the repository,
  • Generated reports in the artifacts/ directory,
  • Commits at the time of the check.

Polycrate’s Git integration is described in the documentation on Git Workflows.


A Compliance Block for CIS Checks on Linux Servers

In the following, we build a simple block that does two things:

  • check: Perform CIS-like checks on Linux servers.
  • remediate: Automatically correct some deviations.

Target audience: Linux servers (e.g., Ubuntu 22.04) in a domain acme-corp.com.

Create Workspace and Inventory

In the workspace root, you create the workspace.poly:

name: acme-corp-automation
organization: acme

blocks:
  - name: cis-linux-baseline
    from: registry.acme-corp.com/acme/compliance/cis-linux-baseline:0.1.0
    config:
      report_format: "json"

The block is pulled via from: from an OCI registry (here fictional registry.acme-corp.com/...; public examples include cargo.ayedo.cloud or PolyHub). The version is pinned in from: (:0.1.0). Output paths for reports do not belong in workspace.poly / block.poly; derive them in the playbook from block.artifacts.path – see Artifacts.

The YAML inventory is also located in the workspace root as inventory.yml:

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

Polycrate automatically sets ANSIBLE_INVENTORY to this file. The playbooks can simply use hosts: all – they run in the container but access your servers via SSH.

Define Block for Compliance Check

After polycrate blocks pull, the block lives under blocks/registry.acme-corp.com/acme/compliance/cis-linux-baseline/. There you create the block.poly (name = full registry path without tag):

name: registry.acme-corp.com/acme/compliance/cis-linux-baseline
version: 0.1.0
kind: generic

config:
  report_format: "json"

actions:
  - name: check
    playbook: check.yml
  - name: remediate
    playbook: remediate.yml

Key points:

  • version pins the block state – important for traceability in audits.
  • You can override e.g. report_format per instance in workspace.polyliteral YAML values only, no Jinja2 expressions. There is no template substitution in workspace.poly and block.poly (no Jinja2); set dynamic paths in Ansible, e.g. via block.artifacts.path. Details: Configuration – limitations and Blocks.
  • Two actions separate clear responsibilities: checking vs. correcting.

Ansible Playbook: CIS Checks (check-action)

Now the check.yml in the same directory:

- name: CIS Linux Baseline Check
  hosts: all
  become: true
  gather_facts: false

  vars:
    cis_results: []
    report_dir: "{{ block.artifacts.path }}/reports"

  tasks:
    - name: Check 1 – /etc/passwd permissions
      ansible.builtin.stat:
        path: /etc/passwd
      register: passwd_stat

    - name: Evaluate 1 – /etc/passwd should be 0644
      ansible.builtin.assert:
        that:
          - passwd_stat.stat.mode == "0644"
        fail_msg: "/etc/passwd permissions are not 0644"
        success_msg: "/etc/passwd permissions are 0644"
      register: passwd_check

    - name: Check 2 – SSH root login disabled
      ansible.builtin.command: "sshd -T"
      register: sshd_config
      changed_when: false

    - name: Evaluate 2 – PermitRootLogin should be no
      ansible.builtin.assert:
        that:
          - "'permitrootlogin no' in sshd_config.stdout_lines"
        fail_msg: "SSH root login is not disabled"
        success_msg: "SSH root login is disabled"
      register: ssh_root_check

    - name: Build CIS result structure
      ansible.builtin.set_fact:
        cis_results: >-
          {{
            cis_results + [
              {
                "control": "CIS-5.4.1",
                "description": "/etc/passwd permissions 0644",
                "passed": passwd_check is succeeded
              },
              {
                "control": "CIS-5.2.8",
                "description": "SSH root login disabled",
                "passed": ssh_root_check is succeeded
              }
            ]
          }}

    - name: Ensure report directory exists
      ansible.builtin.file:
        path: "{{ report_dir }}"
        state: directory
        mode: "0750"

    - name: Write JSON report
      ansible.builtin.copy:
        dest: "{{ report_dir }}/cis-linux-{{ inventory_hostname }}.json"
        content: "{{ cis_results | to_nice_json }}"
        mode: "0640"

What happens here:

  • ansible.builtin.stat checks file permissions.
  • ansible.builtin.command reads the current SSHD configuration.
  • ansible.builtin.assert evaluates whether the target state is met.
  • ansible.builtin.file creates the report directory under {{ block.artifacts.path }}/reports (not as a fixed path in block.poly – see Artifacts).
  • ansible.builtin.copy writes a JSON report per host into that directory.

The modules are deliberately kept simple – compliance officers can easily understand the text in description and fail_msg.

Ansible Playbook: Remediation (remediate-action)

Now the remediate.yml:

- name: CIS Linux Baseline Remediation
  hosts: all
  become: true
  gather_facts: false

  tasks:
    - name: Fix 1 – Set /etc/passwd permissions to 0644
      ansible.builtin.file:
        path: /etc/passwd
        mode: "0644"

    - name: Fix 2 – Disable SSH root login in sshd_config
      ansible.builtin.lineinfile:
        path: /etc/ssh/sshd_config
        regexp: '^PermitRootLogin'
        line: 'PermitRootLogin no'
        create: no
        backup: yes

    - name: Reload SSH service
      ansible.builtin.command: "systemctl reload sshd"
      changed_when: true

This playbook uses:

  • ansible.builtin.file to set file permissions.
  • ansible.builtin.lineinfile (in addition to the required module set) to adjust the SSH configuration.
  • ansible.builtin.command to reload the service.

Important: Idempotency. If a system is already compliant, files do not change unnecessarily – a core principle of Ansible.

Execute Polycrate Commands

From the workspace root, you can start the actions directly:

polycrate run cis-linux-baseline check

After completion, you will find a JSON report for each host under {{ block.artifacts.path }}/reports/ (typically artifacts/blocks/<registry-path>/reports/).

If you want to correct deviations:

polycrate run cis-linux-baseline remediate

And then check again:

polycrate run cis-linux-baseline check

With “plain” Ansible, you would have to:

  • Install Ansible locally,
  • Manually set inventories and paths,
  • Call playbooks with correct parameters.

With Polycrate, this is reduced to clear, non-technical commands – well-suited to give, for example, an ISO its own “compliance console.”

Audit Report and Git as Evidence

Typical procedure for a documented check:

# Decrypt workspace if necessary
polycrate workspace decrypt

# Execute compliance check
polycrate run cis-linux-baseline check

# View results (path = block artifacts + /reports)
ls artifacts/blocks/registry.acme-corp.com/acme/compliance/cis-linux-baseline/reports/
cat artifacts/blocks/registry.acme-corp.com/acme/compliance/cis-linux-baseline/reports/cis-linux-server01.acme-corp.com.json

# Commit changes to the Git repository
git add artifacts/blocks/
git commit -m "CIS Linux baseline check 2026-03-25"
git push

In an audit you can then show:

  • which block (including version) was used,
  • which hosts were checked (inventory),
  • which results were produced (reports),
  • and when execution happened (commit timestamp).

Policy as Code in the Context of NIS-2 and GDPR

NIS-2 (Directive (EU) 2022/2555) entered into force on 16.01.2023; member states had to transpose measures by 17.10.2024, with application from 18.10.2024. GDPR has been in effect since 25.05.2018.

Both regimes require, in simplified terms:

  • Appropriate technical and organizational measures,
  • Regular review and evaluation of those measures,
  • Demonstrability to supervisory authorities and auditors.

With Policy as Code you can automatically check, for example:

  • whether logging services are active (CIS controls),
  • whether unnecessary services are disabled,
  • whether SSH and remote access are hardened,
  • whether system files and configurations have correct permissions,
  • whether encryption is active for certain paths/files (depending on module choice).

These technical controls support:

  • NIS-2 requirements for incident prevention and network security,
  • GDPR articles on integrity and confidentiality and security of processing.

Polycrate helps because:

  • checks live as code in the repository,
  • the execution environment (container) is reproducible,
  • sensitive data can be encrypted in the workspace,
  • results can be archived long-term in Git.

For more complex environments (e.g. mixed Linux/Windows or OT/IoT), you can build more blocks or pull them from PolyHub or your own registries and share them in a versioned way (“sharable automation”).


Frequently asked questions

Do I need to program to use Policy as Code with Polycrate?

No. You should be able to:

  • read basic YAML structures,
  • understand conceptually what a “task” or “action” does,
  • classify the described controls from a domain perspective.

Polycrate encapsulates the actual technology (container, Ansible, Python). Many compliance teams work like this: a technically savvy colleague or external partner builds the blocks; the compliance team runs them with simple commands and interprets the reports.

How does Policy as Code fit our existing governance and documentation?

Policy as Code complements your existing policies:

  • The “big” IT security policy stays as it is.
  • Concrete technical controls get a machine-readable counterpart (playbooks).
  • In your policies you reference the relevant block version and execution cadence.

Git history and Polycrate workspaces yield consistent evidence that integrates well with GRC tools or audit documentation.

Is this not too technical for a compliance team?

Experience shows: once the first two or three blocks exist, the barrier drops sharply. Clear roles matter:

  • Engineering (e.g. a Platform Engineering team or ayedo) owns blocks and infrastructure.
  • Compliance defines requirements, scenarios, and evaluation logic.
  • Together you decide which checks may be auto-remediated and where manual approval is still required.

Polycrate supports this split through clear actions and a simple CLI. In addition, the Polycrate API provides higher-level interfaces: workspaces, blocks, and actions can be invoked from other tools or self-service front ends – useful when not all stakeholders use the command line.

More questions? See our FAQ.


Compliance in practice

With the example above you have seen:

  • how to turn a manual CIS checklist into an Ansible playbook,
  • how Polycrate turns that into a structured compliance block with check and remediate actions,
  • how reports land as JSON files under block.artifacts.path and become a solid audit trail with Git,
  • and how this fits NIS-2 and GDPR.

For many organizations the decisive step is not a single playbook but building a repeatable platform for compliance automation:

  • containerized toolchain instead of individual admin laptops,
  • encrypted workspaces for sensitive data,
  • clearly defined blocks per topic (e.g. “Linux CIS”, “Windows hardening”, “database security”),
  • clean Git processes and traceable history.

This is where ayedo fits in: we combine open-source tools like Polycrate with proven platform engineering approaches and help you introduce Policy as Code so it stays understandable for compliance teams, operable for IT, and defensible for auditors.

If you want to take the next step and shape a solution for your environment, we are happy to start with a hands-on format – overview and booking:

Workshops

Ähnliche Artikel