Compliance as Code
Turning controls into automated tests and audit evidence into a build artifact, so compliance becomes continuous rather than a quarterly scramble.
Traditional compliance is periodic and manual: an auditor asks for evidence, a team spends three weeks assembling screenshots and spreadsheets, and everyone learns whether a control was working last quarter. Compliance as code transforms that into a continuous property of the system, with evidence produced automatically as a by-product of normal operation.
From control statement to executable check
Every control in a framework is a statement about how the system should behave. Most of them can be expressed as an assertion that a machine can evaluate.
| Control statement | Executable form | Evidence produced |
|---|---|---|
| “Data at rest is encrypted” | Query every storage resource for encryption configuration | Timestamped, signed inventory with per-resource status |
| “Access is reviewed periodically” | Compare granted permissions against the approved entitlement source | Diff report plus revocation records |
| “Changes are reviewed before production” | Every production deployment traces to a merged, approved pull request | Change record linked to commit, reviewer, and pipeline run |
| “Systems are patched within N days” | Compare deployed component versions against advisory dates | Age distribution of outstanding vulnerabilities |
| “Backups are tested” | Automated restore drill with verification | Restore log with integrity check results |
| “Least privilege is enforced” | Compare granted against used permissions over a window | Over-permission report per identity |
| “Logs are retained for N days” | Verify retention configuration and sample availability | Configuration snapshot plus retrieval proof |
Not every control automates — physical security and training records do not. But in a cloud-native estate, the large majority of technical controls do, and the remainder shrink to a manageable manual set.
The continuous compliance loop
flowchart TD
A[Control catalogue<br/>frameworks mapped to internal controls] --> B[Each control implemented<br/>as an automated check]
B --> C[Checks run continuously<br/>against the live estate]
C --> D[(Evidence store<br/>timestamped, immutable)]
D --> E[Compliance dashboard<br/>current posture per control]
C --> F{Check fails?}
F -->|Yes| G[Ticket routed to the owning team<br/>with the specific finding]
G --> H[Remediation]
H --> C
D --> I[Audit request:<br/>export evidence for a period]
Two properties make this materially better than the periodic model. Drift is caught in hours, not at the next audit — which is the actual security benefit, independent of any auditor. And evidence is contemporaneous: it records that the control was working continuously, which is a stronger claim than a snapshot taken on the day the auditor visited.
Designing the evidence store
Evidence is only useful if an auditor accepts it. That imposes requirements the engineering instinct tends to skip:
- Immutable and timestamped. Write-once storage, or at minimum version-locked with access logging. Evidence that could have been edited is not evidence.
- Attributable. Which check produced it, running as what identity, against which resources.
- Complete for the period. Gaps are the first thing an auditor asks about. A missing week reads as “the control was off”, so the collection job needs its own dead-man’s-switch alert.
- Exportable in a human-readable form. Auditors do not query your database. A generated report per control per period, with the raw data attached, is the deliverable.
- Retained for the required period, which is often longer than your default log retention.
Mapping frameworks without duplicating work
Organizations subject to several frameworks discover the controls overlap heavily. Encryption at rest appears in almost every one.
flowchart LR
subgraph "Internal controls — implemented once"
C1[Encryption at rest]
C2[Change approval]
C3[Access review]
C4[Vulnerability SLA]
end
subgraph "Framework A"
A1[Control 3.4] --- C1
A2[Control 8.2] --- C2
end
subgraph "Framework B"
B1[CC6.1] --- C1
B2[CC8.1] --- C2
B3[CC6.3] --- C3
end
subgraph "Framework C"
D1[Art. 32] --- C1
D2[A.9.2.5] --- C3
end
Implement each internal control once, then maintain a mapping table from framework requirements to internal controls. Adding a new framework becomes a mapping exercise plus a gap analysis, rather than a new implementation programme.
Change management as evidence
The control auditors probe hardest is usually change management: can you show that every production change was authorised, reviewed, and traceable?
In a mature pipeline this is nearly free, because the pipeline already records it:
Change record CR-2026-08-4471
Artifact: payments-api@sha256:9f2c…
Source: github.com/example/payments-api, commit abc123def
Author: (developer identity)
Reviewers: (approver identities), approved 2026-08-19T08:41Z
Checks passed: unit, integration, SAST, SCA, IaC policy, image scan
Approval: release-managers, 2026-08-19T09:02Z
Deployed: 2026-08-19T09:07Z by pipeline run #8842
Verification: canary analysis passed, SLIs within bounds for 30 min
Rollback: available — previous artifact sha256:7a1b…
Generated automatically from pipeline metadata, this is a stronger change record than any manually maintained ticket, and it costs nothing per change. Teams that maintain a separate change-management ticket alongside a pipeline that already records all of this are paying twice for one control — and the manual record is the less reliable of the two.
Where automation stops
Be explicit about the boundary, because overclaiming damages credibility with auditors:
- Automation proves configuration, not intent. A check confirms encryption is enabled; it does not confirm the classification decision that said this data needed it.
- Some controls are inherently human — training, vendor due diligence, physical access, incident communication decisions.
- Evidence of a check running is not evidence the check is correct. The checks themselves need review, and their test cases are part of the audit scope.
A good programme automates what it can, tracks the manual remainder explicitly as a much shorter list, and is honest about which is which.
Adoption checklist
- Controls are catalogued internally and mapped to each applicable framework.
- Each automatable control is implemented as a continuous check.
- Evidence is immutable, timestamped, attributable, and gap-free.
- Evidence collection jobs have dead-man’s-switch alerts.
- Failures generate tickets routed to owning teams, not reports nobody reads.
- Change records are generated from pipeline metadata, not maintained by hand.
- Checks have their own tests, and those tests are in audit scope.
- The manual control remainder is explicitly listed and tracked.
Last updated 19 Aug 2026, 00:00 UTC.