reference-ansible/docs/inventories.md
Simon Bärlocher 2ba0c07cd3
docs(reference-ansible): add docs/ tree and document repo, playbooks, Makefile
Addresses the WKS PoC review (Notion 2026-05-26). All docs in English.
- README: purpose, docs table of contents, annotated repo tree
- docs/getting_started.md: prerequisites (WKS account, OIDC, SSH, VPN) + first deploy
- docs/ansible.md: playbook table, "Running Ansible", service parameters, cheatsheet
- docs/secrets.md: canonical Bao login (moved out of README) + demo defaults
- docs/operations.md: full Makefile reference
- docs/inventories.md: repo layout, topology, standard folder structure, walkthrough
- docs/testing.md: static checks, inventory resolution, smoke test / dry run
- remove ARCHITECTURE.md (architecture docs live externally)

Also includes the gymburgdorf inventory build-out (bookstack, homarr,
opnform, send) and scripts/bao-seed.sh. site.yml keeps a third traefik
play (traefik_servers minus the vagrant _dmz/_backend split) so the demo
inventories still configure their reverse proxy after the rebase onto main.
2026-05-28 11:20:54 +02:00

179 lines
8.4 KiB
Markdown

<!-- markdownlint-disable MD013 MD060 MD051 -->
# Repo layout & inventories
[← Documentation index](README.md)
## Repo layout and role origin
```text
reference-ansible/
├── Makefile # deploy targets, OIDC login, OBJC fork workaround
├── ansible.cfg # collections_path, remote_user=root, hashi_vault auth_method=token
├── requirements.yml # community.hashi_vault + digitalboard.core (Git)
├── playbooks/site.yml # play sequence (see ansible.md)
├── scripts/bao-seed.sh # seed/merge OpenBao secrets per inventory
├── docs/ # this documentation
├── collections/ # ← installed by `make install`, gitignored
│ └── ansible_collections/
│ └── digitalboard/core/
│ └── roles/ # 🔑 THE ROLES LIVE HERE, NOT in the repo root
└── inventories/
├── demo-gymburgdorf/ # reference inventory of this documentation
├── demo-mbazürich/
├── demo-phbern/
└── vagrant/ # local test inventory with its own topology
```
> **Important:** There is **no** `roles/` directory in the repo root. All
> roles come from the `digitalboard.core` collection (see
> [requirements.yml](../requirements.yml)), installed via
> `make install` into `./collections/`. Plays reference them by
> FQCN `digitalboard.core.<role>`.
## Available inventories
| Inventory | Purpose |
| --- | --- |
| [`demo-gymburgdorf/`](../inventories/demo-gymburgdorf/) | Demo tenant — **recommended as the template for new tenants** |
| [`demo-mbazürich/`](../inventories/demo-mbazürich/) | Demo tenant |
| [`demo-phbern/`](../inventories/demo-phbern/) | Demo tenant |
| [`vagrant/`](../inventories/vagrant/) | local test VMs; **incompatible group topology** with the demo inventories |
## Inventory topology (`demo-gymburgdorf`)
```mermaid
flowchart LR
classDef dmz fill:#fee2e2,stroke:#991b1b,color:#000
classDef app fill:#dcfce7,stroke:#166534,color:#000
classDef stor fill:#dbeafe,stroke:#1e40af,color:#000
classDef turn fill:#fef9c3,stroke:#854d0e,color:#000
subgraph ALL["group: all_servers"]
direction LR
subgraph DMZ["DMZ 172.16.9.0/24"]
RP["<b>reverseproxy</b><br/>172.16.9.111<br/>traefik_mode: dmz"]:::dmz
TURN["<b>turn</b><br/>172.16.9.112<br/>(no role in site.yml yet)"]:::turn
end
subgraph BE["Backend 172.16.19.0/24<br/>group: backend_servers"]
APP["<b>application</b><br/>172.16.19.101<br/>traefik_mode: backend<br/>+ authentik, authentik_outpost_ldap,<br/> nextcloud, collabora, drawio, …"]:::app
ST["<b>storage</b><br/>172.16.19.102<br/>traefik_mode: backend<br/>+ garage (S3)"]:::stor
end
end
RP -.HTTPS in, HTTP out.-> APP
RP -.HTTPS in, HTTP out.-> ST
```
**Group memberships (from [hosts.yml](../inventories/demo-gymburgdorf/hosts.yml)):**
| Group | Members | Purpose |
| --- | --- | --- |
| `all_servers` | `reverseproxy`, `application`, `storage`, `turn` | base role for all hosts |
| `traefik_servers` | `children: all_servers` (all 4 hosts) | Traefik everywhere; DMZ/backend via `traefik_mode` |
| `backend_servers` | `application`, `storage` | sets `traefik_mode: backend` via group_var |
| `garage_servers` | `storage` | single-host wrapper for the Garage role |
| `nextcloud_servers`, `collabora_servers`, `drawio_servers`, `authentik_servers`, `authentik_outpost_ldap_servers`, `send_servers`, `opnform_servers`, `homarr_servers`, `bookstack_servers` | only `application` each | single-host wrappers |
> **Difference from the `vagrant` inventory:** `vagrant` structures
> Traefik differently — via the children groups `traefik_servers_dmz` and
> `traefik_servers_backend` instead of via `backend_servers` +
> `host_vars` override. The two topologies are **structurally
> incompatible**; a 1:1 mapping is not possible. For new tenants, therefore,
> take `demo-gymburgdorf` as the template.
## Standard folder structure of an inventory entry
A fully built-out inventory follows this layout (example
`demo-gymburgdorf`). Currently only this inventory is built out;
`demo-mbazürich` and `demo-phbern` so far contain only `hosts.yml`.
```text
inventories/demo-<kunde>/
├── hosts.yml # REQUIRED — hosts, IPs, group topology
├── group_vars/
│ ├── all/
│ │ ├── vault.yml # REQUIRED — vault_addr, vault_mount (Bao)
│ │ ├── ansible.yml # ansible_python_interpreter etc.
│ │ └── docker.yml # docker_registry_mirrors
│ ├── traefik_servers/
│ │ └── traefik.yml # ACME/TSIG, TLS — applies to ALL Traefik instances
│ └── backend_servers/
│ └── traefik.yml # traefik_mode: backend (default for app + storage)
└── host_vars/
├── reverseproxy/
│ └── traefik.yml # traefik_mode: dmz + DMZ-specific ACME overrides
├── application/
│ ├── main.yml # comment only: which services run here
│ ├── traefik.yml # traefik_dmz_exposed_services (what the DMZ routes)
│ └── <service>.yml # one file per service (nextcloud, authentik, …)
└── storage/
├── main.yml # same as above
├── traefik.yml # traefik_extra_hosts + traefik_dmz_exposed_services
└── garage.yml # service vars for garage
```
**Conventions:**
- **`hosts.yml` is the only hard required file.** Vars are
optional — if one is missing, the role defaults from
`digitalboard.core` take effect. A new inventory therefore starts minimally with
only `hosts.yml` (just like `demo-mbazürich`/`demo-phbern`).
- **`group_vars/all/vault.yml`** is effectively required as soon as
Bao lookups are supposed to work — without `vault_mount`/`vault_addr` the
secret lookups fail.
- **One file per service** under `host_vars/<host>/<service>.yml`. The
file name is free (Ansible loads all YAMLs in the directory); by
convention it is named like the role. Which variables belong where:
[ansible.md § Where parameters belong](ansible.md#where-parameters-belong).
- **`main.yml` per host** is pure documentation — a comment indicating which
services run on the host. Carries no productive vars.
- **`host_vars/<host>/traefik.yml`** declares via
`traefik_dmz_exposed_services` which local services the
DMZ Traefik should make reachable from outside. The DMZ reads this
list via `hostvars[<backend>]` and renders its routers from it. A new
service exposed externally = a new entry here. Mechanics:
[ansible.md § traefik](ansible.md#traefik).
## Walkthrough: Creating a new demo tenant
Recommended template: **`demo-gymburgdorf`** (not `vagrant`, because its
group topology is incompatible).
1. **Copy the inventory:**
```bash
cp -r inventories/demo-gymburgdorf inventories/demo-<kunde>
```
2. **Adjust `hosts.yml`:** IPs, hostnames per host.
3. **`group_vars/all/vault.yml`** — set `vault_mount` to the new
tenant mount (`demo-<kunde>`).
4. **`group_vars/traefik_servers/traefik.yml`** —
point `traefik_acme_dns_zone` and the `acme-tsig` lookup paths to the
new zone / the new Bao path.
5. Go through **`host_vars/application/*.yml`** and **`host_vars/storage/*.yml`**:
FQDNs to the new domain pattern (e.g.
`*.<kunde>.souveredu.ch`), Bao lookup paths to `demo-<kunde>/data/…`.
6. **Prepare OpenBao** (out-of-band, not via Ansible):
- Create a new KV-v2 mount `demo-<kunde>`.
- Write secrets: `acme-tsig`, `authentik`, `nextcloud`,
`garage`, … — conveniently via `make seed_bao_<kunde>` (see
[scripts/bao-seed.sh](../scripts/bao-seed.sh) and
[secrets.md § Demo-Only-Defaults](secrets.md#demo-only-defaults--must-be-overridden)).
- Policy for the deploy token: read on `demo-<kunde>/data/*`.
7. **DNS:** Create the TSIG update zone (`demo-<kunde>._acme.digitalboard.ch`) at
`ns1.digitalboard.ch`, CNAMEs
`_acme-challenge.*.<kunde>.<tld>` pointing there.
8. **Makefile** — add a new target modeled on
`deploy_site_demo_gymburgdorf` and add it to `deploy_site_demo`;
likewise a `seed_bao_<kunde>` target.
9. **Smoke test:** `ansible all -i inventories/demo-<kunde>/hosts.yml -m ping`.
10. **Deploy:** Bao login + `make deploy_site_demo_<kunde>`.