User
Manage Horizon users and roles declaratively (kind: User) and imperatively via the read / rotate / delete verbs.
Targets the users REST surface on Horizon 35+.
YAML
apiVersion: onmsctl.no42.org/v1alpha1
kind: User
metadata:
name: jdoe
spec:
fullName: Jane Doe
email: jane@example.com
roles: [ROLE_USER]
passwordRef: # passwords are create-only and never inline
fromEnv: JDOE_PASSWORD
Roles reconcile as a set. roles: [B, C] against a server holding [A, B] grants C, revokes A, and keeps B.
Omitted scalar fields never clear the server value: this is a merge, not a replace.
passwordRef: passwords are create-only and never inline. A literal password: is rejected at parse (PR-IAM-001); reference an external secret (fromFile/fromEnv/fromKeyring).
It is honored on create only; apply never rotates a password (it can't read the current password to diff), so use iam user set-password.
dutySchedule is create-only (PR-IAM-004 warns on change); a purely numeric metadata.name is refused (PR-IAM-003).
A context may tune defaults under an iam: block (protected-roles, known-roles, allow-admin-lockout).
Apply
JDOE_PASSWORD=… onmsctl apply -f user.yaml --dry-run --diff
JDOE_PASSWORD=… onmsctl apply -f user.yaml
onmsctl apply -f ./users/ --dry-run --diff # preview
onmsctl apply -f ./users/ # apply
Lockout protection. Apply refuses a plan that would empty a protected role (IAM-001, exit 13; override per-context with allow-admin-lockout: true) or strip or delete the calling user's own protected role (IAM-002, exit 14, no override).
If whoami is unavailable for a self-affecting change, apply refuses (exit 15) rather than skip the check.
Inspect and delete
onmsctl iam whoami # the calling user
onmsctl iam user list|get|export # reads
onmsctl iam user delete alice --yes
printf %s "$NEW_PW" | onmsctl iam user set-password alice --password-stdin
iam user export snapshots all users as YAML.
Rotate a password (pick exactly one source):
onmsctl iam user set-password jdoe --password-stdin # read one line from stdin
onmsctl iam user set-password jdoe --from-env JDOE_PASSWORD
onmsctl iam user set-password jdoe --from-file ./pw
onmsctl iam user set-password jdoe --from-keyring myservice/jdoe
The legacy users.xml migration is covered in the migration guide.
Example
# yaml-language-server: $schema=https://raw.githubusercontent.com/no42-org/onmsctl/main/schemas/iam-user.schema.json
#
# kind: User example covering every modeled field
# (per task 10.5 of the add-iam-capability change).
#
# Pair with `onmsctl apply -f examples/iam-user.yaml --dry-run --diff`
# to see the per-user plan against a live Horizon.
apiVersion: onmsctl.no42.org/v1alpha1
kind: User
metadata:
name: alice
spec:
fullName: Alice Example
email: alice@example.org
comments: Created via onmsctl GitOps
# Roles are a set: duplicates are rejected at parse time, and apply
# computes exact add/remove deltas against the server. Roles outside the
# built-in known set emit a PR-IAM-006 warning (not a refusal).
roles:
- ROLE_USER
- ROLE_PROVISION
# Create-only: settable on the initial POST, but never emitted on a
# form-encoded update (§D11.5). A dutySchedule change on an existing user
# warns (PR-IAM-004) instead of mutating.
dutySchedule: "MoTuWeThFr0800-1700"
# The password is NEVER stored inline — a literal `password:` key is
# rejected at parse time (PR-IAM-001). Reference an external secret
# instead; exactly one source. passwordRef is honored on Create only.
#
# passwordRef: { fromEnv: ALICE_PW }
# passwordRef: { fromKeyring: { service: onmsctl, account: alice } }
passwordRef:
fromFile: /run/secrets/alice.pw