> ## Documentation Index
> Fetch the complete documentation index at: https://certplane.kippel.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Manage Certplane with infrastructure as code

> Certplane is designed to be driven from your existing IaC pipeline. Render broker, agent, and policy configs from inventory and let your config management tool keep them in sync.

Certplane has no web UI and no runtime API for changing certificate policy or agent configuration. Every piece of configuration the broker and agent consume is a plain YAML file on disk. This is deliberate: it lets you treat certificate policy the same way you treat the rest of your infrastructure — rendered from a single source of truth, reviewed in version control, and applied with the same automation you already use to manage your servers.

## What this means in practice

There are three configuration files in a typical Certplane deployment, and each one is a natural fit for IaC:

| File         | Where it lives                                     | What renders it                                            |
| ------------ | -------------------------------------------------- | ---------------------------------------------------------- |
| `broker.yml` | Broker host, e.g. `/etc/certplane/broker.yml`      | Your config management tool, from broker-side variables    |
| `policy.yml` | Broker host, e.g. `/etc/certplane/policy.yml`      | Your config management tool, from a host/profile inventory |
| `agent.yml`  | Each managed host, e.g. `/etc/certplane/agent.yml` | Your config management tool, from per-host facts           |

Because the broker can hot-reload `policy.yml` (see [`policy.watch`](/policy/overview#hot-reload)), the entire lifecycle — add a host, grant it a new profile, rotate a DNS credential reference — can be driven by a single push to your IaC repo.

## Recommended layout

A simple, scalable pattern is to keep all Certplane configuration under one directory in your IaC repository, rendered from inventory:

```
infrastructure/
├── inventory/
│   ├── hosts.yml                  # source of truth: hosts and their roles
│   └── certplane.yml              # profiles and host → profile mappings
├── templates/
│   ├── broker.yml.j2
│   ├── policy.yml.j2
│   └── agent.yml.j2
└── playbooks/
    ├── broker.yml
    └── agent.yml
```

The templates render `broker.yml`, `policy.yml`, and `agent.yml` from inventory variables. Your playbook (or equivalent) places the rendered files on the right hosts, restarts the broker if `broker.yml` changes, and signals a reload if `policy.yml` or `agent.yml` changes.

## Rendering `policy.yml` from inventory

The policy file is the most common thing to render. Keep your profiles and host-to-profile mappings in inventory variables, then template `policy.yml`:

```yaml inventory/certplane.yml theme={null}
certplane_profiles:
  public_edge_main:
    type: wildcard
    dns_names:
      - "*.example.com"
    acme:
      challenge: dns-01
      credentials: cloudflare/example-com
    renew_before: 720h

certplane_hosts:
  edge01:
    identity: edge01.h.int.example.com
    profiles:
      - public_edge_main
  edge02:
    identity: edge02.h.int.example.com
    profiles:
      - public_edge_main
```

```jinja templates/policy.yml.j2 theme={null}
version: 1

profiles:
{{ certplane_profiles | to_nice_yaml(indent=2) | indent(2, true) }}

hosts:
{{ certplane_hosts | to_nice_yaml(indent=2) | indent(2, true) }}
```

With `policy.watch: true` set in `broker.yml`, the broker reloads automatically the moment your IaC tool writes the new file. No broker restart, no agent restart, no downtime.

## Keep secrets out of rendered files

The secrets provider (see [Secrets](/configuration/secrets)) is what makes the IaC-first model safe. Your rendered `broker.yml` and `policy.yml` only contain **secret references** — environment variable names, file paths, or Vault paths — never the secret values themselves.

That means the rendered files are safe to commit to version control, diff in code review, and ship through your normal pipeline. The actual secret values are injected at runtime by whichever secrets provider you configure (`env`, `file`, `vault`, or `openbao`).

## Official Ansible roles

The repo ships two ready-to-use Ansible roles under [`ansible/`](https://github.com/TaconeoMental/Certplane/tree/main/ansible):

* [`certplane_broker`](/guides/ansible#certplane-broker-role) — installs the broker binary, deploys TLS material and policy, renders `broker.yml`, manages the systemd service. Handlers restart the broker on TLS/policy/config changes.
* [`certplane_agent`](/guides/ansible#certplane-agent-role) — installs the agent binary, deploys CA bundles, renders `agent.yml`, performs first-time enrollment idempotently with a `no_log` bootstrap token, and manages the `systemd` timer.

Use them as-is or vendor them and tweak the templates. See the [Ansible roles guide](/guides/ansible) for variable references and play examples. If you prefer Salt/Chef/Puppet, the patterns on this page describe everything the roles do under the hood.

## Bootstrapping new hosts in your pipeline

Enrollment is the one part of the Certplane lifecycle that requires a short-lived secret (the bootstrap token). The recommended pattern in an IaC pipeline is:

<Steps>
  <Step title="Add the host to inventory">
    Append the new host to `inventory/hosts.yml` and add an entry under `certplane_hosts` with its identity CN and allowed profiles. Commit the change.
  </Step>

  <Step title="Render and ship policy.yml">
    Your IaC tool re-renders `policy.yml` and writes it to the broker host. With `policy.watch: true`, the broker picks up the new host registration immediately.
  </Step>

  <Step title="Generate a bootstrap token on step-ca">
    From your IaC controller, run `step ca token <identity-cn>` against your step-ca instance. The token is single-use and short-lived.
  </Step>

  <Step title="Place the token on the new host">
    Copy the token to the path referenced by `identity.bootstrap_token` in the host's `agent.yml`, then render and ship `agent.yml`.
  </Step>

  <Step title="Run enrollment">
    Invoke `certplane-agent -c /etc/certplane/agent.yml enroll` on the host as part of the same play. After enrollment, the agent's `systemd` timer keeps the certificates renewed (`certplane-agent -c /etc/certplane/agent.yml run`).
  </Step>
</Steps>

After the first successful enrollment, the bootstrap token is consumed and the host needs no further secrets — the identity certificate it just received is what authenticates every subsequent broker call.

## Drift detection

Because every effective change goes through your IaC tool, drift detection is straightforward: re-run the playbook in check mode. Any host whose `agent.yml`, broker host's `broker.yml`, or rendered `policy.yml` differs from what your inventory describes will show up as a diff. The broker itself enforces policy on every request, so even if a rendered file somehow drifts, an agent cannot request anything the canonical inventory does not allow.

## Next steps

* [Broker configuration reference](/configuration/broker) — every field the broker reads
* [Agent configuration reference](/configuration/agent) — every field the agent reads
* [Policy overview](/policy/overview) — the structure of `policy.yml`
* [Secrets](/configuration/secrets) — how secret references are resolved at runtime
