Skip to main content

An audit is not a photo — it's an auditor that teaches itself between runs

A cloud is never set up "insecurely" — it becomes insecure gradually. An instance gets added, a port is opened "just to test," a bucket appears, a service ships under a key nobody ever rotates. A year or two later you have 130 servers, thousands of snapshots, and nobody can tell you whether root is protected, whether the disks are encrypted, or whether you'd even notice someone getting in. We ran a read-only security audit of one mid-sized company's AWS account — through a read-only role, with not a single change to the account — and found exactly this quiet debt: root without MFA, 100% of disks and snapshots unencrypted, threat detection switched off in every region.

But an audit as a one-off PDF report is stale the day it ships — a week later you add a service, AWS ships a new feature, a new CVE lands. So we didn't build a report; we built an auditor that keeps living: every run it measures the same thing deterministically, and between runs it grows its own rule set — proving each new rule before it trusts it.

The problem: gaps you can't see — and an audit that ages before you finish reading it

Security debt in the cloud isn't one big blunder. It's the sum of small things that never landed on a single screen together — in a state that changes faster than you can review it. That cost concentrates in three problems.

1. The configuration drifts over time and nobody sees the whole of it

Who has it: any company whose AWS account grew faster than its security practices — root is used "only occasionally" (and has no MFA), the default security group has had SSH and the Docker API open to the internet for years across a hundred network interfaces at once, and disk encryption was never turned on, so 100% of volumes and snapshots are unencrypted.

Each of these is "just a setting" on its own. Together they're an open door: one guessed root password = loss of the whole account; one compromised host = lateral movement through an open Docker API to dozens more. And for an EU company, not encrypting is also a GDPR exposure (Art. 32).

How we solve it: the audit walks service by service across every region and brings all of this onto one page — not a "feeling," but a list with evidence attached to each finding.

2. Without detection and logs, you don't know if you've already been breached

Who has it: an operation where GuardDuty (threat detection) is off in every region, and VPC flow logs and load-balancer access logs are missing. The servers run, the apps work — but if someone were mining crypto on your instances right now or exfiltrating data, you wouldn't find out.

This is the most insidious part: the absence of a problem looks exactly like safety. As long as nothing happens, nobody misses something they never had.

How we solve it: the audit explicitly checks what you'd actually be able to catch — detection, network and application logs, vulnerability-scan coverage — and where there's a blind spot, it names it as a finding with a concrete fix.

3. A one-off scan is an unranked pile — and it's stale the day it ships

Who has it: anyone who has ever run a scanner and gotten back 200 "critical" rows with no order — and a month later it's out of date anyway, because both the account and AWS have moved on. An unprioritized list is as useless as no list; and a report that never refreshes is just a snapshot of the past.

How we solve it: every finding gets a severity and an effort estimate and is sorted by benefit/effort (reversible steps first) — and the auditor runs repeatedly, so instead of a snapshot you get a living state that fills itself in between runs (more below).

The idea: a controlled, read-only audit that learns

The problems share one cause: nobody sees the account's state whole, at once, and continuously. The fix is a controlled auditor — and the same principle holds as for every solution we build: the audit proposes; you decide.

How a single run works

  1. Read-only — the audit runs through read-only access (an SSO role with no write permissions). Nothing is created, changed or deleted. Data itself (e.g. S3 object contents) is never touched — only metadata and configuration are read.
  2. A scan across every region — not just the one where the workload runs. "Forgotten" resources in unused regions are a favorite hiding spot for attackers precisely because nobody looks there.
  3. Deterministic analysis — findings are computed by ordinary, repeatable code against the CIS AWS Benchmark and AWS Foundational Security Best Practices. Same data → same result, every time. No model "improvising" on the spot.
  4. Severity and impact ordering — each finding gets a severity (🔴 critical → 🔵 cleanup) and an effort estimate. The order is by benefit/effort, not alphabetical.
  5. A plan, not a list of faults — each finding comes with concrete steps (copy-paste commands), ✅ how to verify and ⚠️ rollback. Reversible steps first.
  6. A local dashboard — the output is a local HTML dashboard; nothing is published to the cloud. Accepted risks are edited right there (ADRs, see below).
  7. Re-scan and measure — the next run compares the delta (new/resolved) and a falling finding count is a measurable KPI. And the most interesting part happens between runs ⤵

It's not a photo — the auditor teaches itself between runs

This is the core. The auditor isn't a frozen set of checks: it grows its own rules. On a set cadence (say every 30 days, with no cron at all) a discovery agent runs, uses the current official AWS documentation to find new best practices relevant to your account, and tries to turn them into new checks. But — and this is the whole point — it never just adds one.

The AI writes data, not code

The key decision everything else rests on: the learning agent never writes code into the audit core. It writes only a rule as data (a declarative statement: "in this file, look at this field; it must have this value"). The measuring stays the same deterministic, tested code.

This separates the "brain" (discovering a new best practice) from the "ruler" (evaluating it). The audit stays repeatable and testable even though an AI extends it. That's the difference between "an audit with AI" (unsettling) and an auditor that proves every rule before it trusts it (trustworthy).

Every new rule is tested first

A new rule is born with two test fixtures — one that must fail and one that must pass. Before the rule is admitted, a test-gate runs: the bad fixture has to actually fail, the good one has to pass, and the whole test suite has to stay green. Only then is the rule allowed in. On the last run the suite was 17/17.

Better nothing than a wrong rule

The agent adversarially checks itself — for each candidate it asks: does the documentation really say this? does the check match the shape of our data? could it produce a false "PASS"? If any of that fails, the candidate is thrown out.

In one real learning run the agent considered 4 candidates and rejected all 4 for the right reasons:

  • a check already covered by an existing CIS rule (no gain),
  • a check that would return a false "pass" on an empty list (false-pass),
  • a check that blurred with a collection error (false-positive),
  • a check over an untrustworthy raw input.

The result of that run: 0 rules added, 0 noise — and that's the correct behavior. An auditor that would rather add nothing than add an unreliable rule is an auditor you can trust.

Gates that hold even without a human

Even when a candidate holds up, it must pass three gates: a mandatory citation of authoritative AWS documentation (no source = rejected), add-only (never silently softens an existing rule or severity), and the test-gate above. Every rule therefore carries its provenance — source, doc URL, date.

What can't be safely automated → a proposal for a human

Sometimes there's a real gap the agent can't safely add on its own (the check is outside the declarative form, or new data has to be collected). Then it commits nothing — it writes a proposal for a human. That's exactly what happened once: the agent discovered that vulnerability scanning for container images and Lambda was turned off, even though the account uses them — but the check didn't fit the declarative form. It wrote a proposal; a human collected the missing data; and the same check then passed the same gates and has been measured automatically ever since.

★ This is the only part left to a human: the agent proposes → a human extends the collection → the rule passes the same machine gates → the audit measures it deterministically from then on.

What triggers a fresh look

The auditor doesn't look again "just because the calendar says so." A fresh look is triggered by two real changes:

  • A service appeared in your account. The auditor works out for itself which services the account actually uses and compares them against what it already checks. When you start using something new — say Cognito or EKS — it flags it as an uncovered service and, on the next learning run, looks up best practices for it and proposes a check.
  • AWS itself changed something. The auditor watches AWS "What's New" and Security Bulletins (RSS, over verified TLS), filters them down to your services + security keywords, and takes only what's new since last time. That turns "review once a month" into something changed → look at it now. In a baseline run it pulled dozens of relevant items out of ~170 — including fresh CVEs in services the account actually uses (e.g. an HTTP/2 vulnerability in WAF, a containerd CVE affecting ECS).

Decisions the system remembers (ADR)

Not every finding is a bug to fix — some are consciously accepted risks. So the auditor stops alarming on them but doesn't forget them, they get a record: an ADR (Architecture/Accepted Decision Record). You record the decision — what you're accepting, why, who approved it, the compensating control, and until when it holds — right in the dashboard. From then on the auditor doesn't show it as an alarm… but once the review date passes, the finding resurfaces on its own for reassessment.

This gives the system three clean layers of trust:

LayerWho holds itExample
Deterministic measurementrepeatable coderoot MFA, encryption, open ports, flow logs…
Severity judgmenta tunable policy"public bucket with data = critical"
Override (ADR)a human, with a deadline"we consciously accept this risk through Q4"

Why read-only — and why you can trust it

The audit changes nothing in the account. That's not a limitation, it's a feature: you get the full picture of risk without risking anything. You run the remediation commands yourself (or we do it together, with admin access) only once you understand the impact — and we do it reversibly first (a key is deactivated before it's deleted, once you've confirmed nothing broke).

It's the same principle as in all our solutions: the system prepares the work; a person decides and runs it. With security that goes double — nobody wants an "automatic fix" cutting off SSH to production at midnight. That's why the learning itself may only add checks, never turn anything off.

What the audit typically finds

From a real audit (anonymized) — patterns that recur in almost every account no one has walked through in a long time:

FindingSeverityTypical fix
Root account without MFA🔴 CriticalEnable MFA (FIDO2) / centralized root access
Default security group opens SSH and Docker API to the internet (~120 interfaces)🟠 HighRemove 0.0.0.0/0, move to dedicated SGs + SSM
100% of disks and snapshots unencrypted🟠 HighEnable EBS encryption by default + migrate existing
Threat detection (GuardDuty) off in every region🟠 HighEnable org-wide via delegated admin
Static access keys never rotated (18 of 23 older than a year)🟠 HighRotate ≤ 90 days; replace with roles / OIDC
Publicly readable S3 bucket + no account-level Block Public Access🟠 HighTurn on account BPA
100+ critical vulnerabilities with a fix available🟠 HighIntroduce a Patch Manager cycle
Vulnerability scanning for containers/Lambda turned off🟡 MediumEnable enhanced scanning (a finding from learning)

Just as important: an audit should be balanced. The output also includes what's set up well (a central CloudTrail with log validation, locked-down buckets, strong TLS policies, service accounts with least privilege) — so you don't break it during remediation.

What we measure

Security can be measured — and without measurement "improvement" can't be proven:

What we measureWhy it matters
Open critical findings with a fix availableReal, immediately exploitable risk — a number that should fall
% of disks and snapshots encryptedDirect data exposure (and GDPR)
Detection coverage (GuardDuty / Inspector / flow logs)Whether you'd catch an incident at all
Age of access keysThe older the key, the higher the chance of a leak
Uncovered services in the accountWhere the audit can't see yet — input for more learning
New rules / relevant AWS changes per periodThat the auditor grows with your account and with AWS

Who it's for

It makes sense for any company on AWS whose account hasn't been systematically reviewed in a while — especially if:

  • the account grew fast and security practices couldn't keep up,
  • it runs across multiple accounts / via Control Tower and lacks a central overview,
  • it handles personal or regulated data (GDPR, customers, healthcare, energy),
  • it has no dedicated security team to check it continuously,
  • you want a continuous picture of risk — one that doesn't refresh once a year, but grows with the account.

The audit changes nothing on its own. It walks the account read-only, finds and evidences the issues, ranks them by impact, grows its own rules between runs (proving each one first), and hands you a living remediation plan — instead of a PDF that's stale the day it ships.

Want to know where your account stands? Get a free diagnostic — we run a read-only scan of your AWS account and show you the top findings, their severity, and a prioritized remediation plan. We change nothing, you risk nothing. No obligation.