How unauthorized changes are detected

Per change source (cloud audit log, Kubernetes audit, GitOps, ITSM), what counts as an authorized change, what raises an unauthorized change, and a sandbox demo.

Concept · Updated 2026-10-26 · Describes release 0.1.164

An unauthorized change is a write the platform saw applied in your estate that nothing accounts for: no approved change request, no approved ITSM change ticket, no automation identity you trust. It is different from unauthorized access, a write the cloud refused: nothing changed, so there is nothing to roll back, and it goes to SecOps as a report.

The rule is the same for every source that can see who made a write. What differs is what each source can see. The field-by-field facts (notes, audit actions, caps) are in the Changes reference.

The switches

Setting Default What it does
aiops_unauthorized_change_incidents (Guardrails → Unmanaged changes, Open an incident for every change with no reference) off An applied write from the cloud audit log or the Kubernetes audit log needs a reference; one without opens an incident that waits for a person. Off by default because a busy account records thousands of outside writes a day: fill the allowlist first.
aiops_denied_access_to_secops on A refused write opens a SecOps report (one per principal, account and hour), with the SecOps agent enabled.
aiops_change_principal_check on A write made with the platform's own cloud credential that no platform change, approved action or source-side suppression explains is flagged.
change_freeze none While the declared freeze window matches, any observed change with no platform change in flight for its service is flagged.
change_authorized_principals [] The automation identities (globs) whose writes are authorized: CI/CD roles, pipeline service accounts.
itsm_change_match per ticket system The ticket fields read for state, CI and planned window.

With the first switch off, the platform still records every captured write with its principal, and the freeze window, the principal check and denied attempts still flag rows on the Changes page; only the reference rule and its incidents are off.

What counts as authorized

A captured, applied write is authorized when one of these accounts for it, checked in this order. The first match is stored on the change as its reference.

  1. The platform made it. The platform's own credential, with a receipt: a typed platform step on the same resource within 10 minutes, a route change naming the resource or its service, an approved action inside an analysis, or a source-side suppression.
  2. An allowlisted principal. The AWS role or user, the Azure caller or the Kubernetes user matches a glob in change_authorized_principals (case-insensitive). Use it for deployment pipelines and for in-cluster CI/CD service accounts, for example system:serviceaccount:flux-system:*.
  3. An approved ITSM change ticket. A ServiceNow change request or a Jira change issue the platform has received, in an approved state, whose CI names the resource or a service the write is linked to, and whose planned window covers the time of the write. A ticket without a planned start and end never matches.
  4. A change request a person approved on the platform. Its typed steps, reviewed action or operator commands name the resource itself (a whole service is not enough), it was opened before the write, and it moved within 24 hours of it. One the platform executed covers writes only until its execution ended.

A write with none of these is flagged "no reference found (no ticket, approval or signed change request)". A freeze window or the principal check can still flag a write that has a reference.

Method per source

Cloud audit log: AWS CloudTrail and the Azure activity log

Kubernetes audit log

GitOps: repositories, Argo CD and the change webhook

ITSM: ServiceNow and Jira change tickets

What a person does

An unauthorized-change incident never fixes or rolls back on its own, whatever runbook is certified. On its Unauthorized change card, a person with the approver permission either:

Run the sandbox demo

scripts/demo-unauthorized-change.py shows the cloud audit log path end to end in a sandbox account. The script is in the platform's source repository, not on this installation: ask your platform administrator or the vendor for it. It turns aiops_unauthorized_change_incidents on for the demo window only and puts the workspace's own value back on the way out, also when the run fails or is stopped with Ctrl+C, SIGTERM or a closed terminal. If putting it back fails, the script says so in a line that starts !!! and names the value to set by hand; a SIGKILL skips the restore.

Before you start: a workspace whose AWS read connection covers the sandbox account and region; a security group there, tagged aiops-managed=true so the platform's write connection can run the drafted rollback; a personal access token with members.manage, pillars.manage and incidents.view; the AWS CLI signed in to the sandbox account with an identity that is not on change_authorized_principals (the script refuses to start otherwise).

AIOPS_BASE=https://aiops.example.com AIOPS_TOKEN=<token> TENANT_ID=<workspace id> \
SG_ID=sg-0123456789abcdef0 AWS_PROFILE=<sandbox profile> AWS_REGION=ap-southeast-1 \
  python3 scripts/demo-unauthorized-change.py
  1. The script adds one ingress rule to the security group: TCP 8443 from 203.0.113.0/24, a documentation range that is never routed on the internet.
  2. It pulls the audit log through the platform every minute until the write is flagged; CloudTrail takes about 5 to 15 minutes to deliver an event. You should see the note "no reference found (no ticket, approval or signed change request)" and the incident's address.
  3. Open the incident and answer on its Unauthorized change card. The script waits up to an hour (DECIDE_WAIT_S, 3600 seconds) for the incident to close: a rollback is released by a person and then validated in rounds.
  4. The script restores the switch, then removes the rule if no rollback removed it (CLEANUP=0 keeps it). While the incident's rollback is requested, approved or running, it leaves the rule to the rollback and prints the command that removes it later.

To show an authorized write instead, raise an approved change ticket naming the security group with a planned window around the demo, or add your identity to change_authorized_principals (the script then refuses, because nothing would be flagged).

Next steps