## Why
netbox_user_management authenticates to NetBox to reconcile service users + permissions on every apply. It must not depend on a second static admin token, and it must not break when the engine rotates its admin seed (`netbox/config/rotate` mints a fresh admin token and deletes the old one). The durable shape: keep exactly ONE static admin token, and have the netbox engine mint an ephemeral, user-admin-capable token that the e-breuninger provider uses to manage users.
## How
- `module.netbox_user_mgmt_role` creates `netbox/roles/vault-user-mgmt`, a write-enabled role for a pre-existing NetBox superuser named by `user_mgmt_username`. Minted tokens authenticate AS that superuser (NetBox tokens carry no scope beyond `write_enabled`; the user's permissions apply), so they can create users.
- `netbox_user_management` reads `netbox/creds/vault-user-mgmt` and configures the netbox provider with the minted token. When `user_mgmt_username` is unset it falls back to the single static `admin_token` (a `check` block warns that rotation would then break it) - a bootstrap/degraded path, never a second static token.
- Grant the deployer `read` on `netbox/creds/vault-user-mgmt` (the one deliberate exception to the admin policy's `netbox/creds/*` exclusion).
- Keep the bare-token + `token_version`-match postconditions on the single static admin token.
## Feasibility constraints (worked through, documented in-module)
1. **The engine CAN mint a user-admin token** - roles map to a pre-existing user with only a `write_enabled` gate (`vault-plugin-secrets-netbox` `path_roles.go`, `client.go` `MintToken`); point it at a superuser and minted tokens can manage users.
2. **Token transits state.** The hashicorp/vault provider (5.6.0) exposes ephemeral resources for KV only, not dynamic engine creds, so the mint is read via the `vault_generic_secret` DATA source: the short-lived token is written to state (sensitive, lease-revoked) and re-minted each plan. Migrate to an ephemeral resource once the vault provider ships a dynamic-secret one.
3. **A clean single fresh apply is not possible.** A provider cannot be configured from a role created in the same run (data sources don't defer; OpenTofu 1.11 defers only ephemeral resources, which the vault provider doesn't offer here). So enabling the dynamic path on a backend needs a one-time targeted bootstrap of the mount + role, then normal applies. Documented in `config/netbox_secret_backend/netbox.yaml`.
## Operator follow-up
- Repair the live mount first (unchanged): `vault write netbox/config token=<BARE>` (the mount uses `ignore_changes=[token]`), keep `token_version=2`.
- To enable dynamic minting: set `user_mgmt_username` to the pre-existing superuser, apply the deployer creds policy, then bootstrap once: `tofu apply -target=...netbox_secret_backend -target=...netbox_user_mgmt_role`, then apply normally. Until then user management stays on the static token (non-breaking, with a warning).
Reviewed-on: #119
Co-authored-by: Ben Vincent <ben@unkin.net>
Co-committed-by: Ben Vincent <ben@unkin.net>
## Why
- The netbox engine modules stand ready but mount nothing and create no identity until backend and role data exist, so terraform-infra still reads a static NetBox token instead of minting ephemeral scoped tokens.
## How
- Add `config/netbox_secret_backend/netbox.yaml` to mount the engine at `netbox` and point it at the syd1 NetBox URL; the admin token is read from KV, not stored here.
- Add `config/netbox_secret_backend_role/netbox/terraform-infra.yaml` as the single declarative source for the terraform-infra identity: filename-derived role name and NetBox username, write access, short TTLs, and an inline permissions block. Nothing in the file repeats the filename.
- Scope terraform-infra to view/add/change/delete on the IPAM/DCIM objects it manages: prefixes, ip-addresses, ip-ranges, devices, interfaces, mac addresses.
- Add `policies/netbox/creds/terraform-infra.yaml` letting the terraform-infra AppRole and its Woodpecker k8s role read `netbox/creds/terraform-infra`; it attaches to nothing until the separate terraform-infra Vault onboarding lands.
## Dependency order
- Stacked on the modules PR (#115), which stacks on the plugin registration PR. Merge order: plugin -> #115 -> this.
## CI note
- The plan step is red only on the external admin_token KV seed at `kv/data/service/vault/au/syd1/secret_backend/netbox/config` (a NetBox token with add_token + grant_token / superuser). Seeding that path is an environmental prerequisite, not a code defect; everything else validates.
---------
Co-authored-by: BenVincent <benvin@main.unkin.net>
Reviewed-on: #117
Co-authored-by: Ben Vincent <ben@unkin.net>
Co-committed-by: Ben Vincent <ben@unkin.net>