From zero to a full AWS account health check with Claude Code
You give Claude Code broad visibility into an AWS account while limiting it to a read only IAM identity built from the ReadOnlyAccess and SecurityAudit managed policies, so it can describe and list resources but never create, modify, or delete anything. The agent runs AWS CLI commands, reads metrics, logs and configuration, follows its own hypotheses, and writes a report of findings ranked by production cost.
Most AWS accounts accumulate risk quietly. Nobody sets out to leave a broker with zero alarms, or to run two supposedly multi availability zone brokers in the same availability zone, or to leave statement level database logging writing customer data to plain text files. These things happen one reasonable seeming decision at a time, and they only surface when something breaks in production and someone spends a stressful afternoon working backward through CloudWatch, VPC flow logs, and six months of half remembered context.
This post is a practical guide to doing the opposite: a calm, read only sweep of an entire AWS account, run by an agent, that surfaces exactly this kind of thing before it becomes an incident. We will go from an empty terminal to a full account audit, using the AWS CLI for access and Claude Code as the agent that actually reads the metrics, logs, and configuration and writes the report. The goal is not “Claude looks around my AWS account.” It is a laptop, a read only identity, and an afternoon producing an evidence backed technology risk register that a CIO, a platform lead, or a consultant walking into an unfamiliar account can actually act on.
The approach in this post grew out of a real investigation into ActiveMQ reply path latency on a core banking platform. That investigation eliminated fourteen wrong hypotheses, cross checked its conclusion against two independent data sources, and along the way turned up sixteen separate secondary issues that had nothing to do with the original incident: no alarms on the message brokers, both brokers quietly sharing a single availability zone since a maintenance event weeks earlier, a database logging bind parameters for customer accounts, and more. None of that would have been found by looking for it directly. It surfaced because the investigation was thorough by default, and because every finding was backed by a number, not a hunch. That same discipline, evidence first, one queue or one alarm at a time, ranked by what it would actually cost you in production, is what the prompts in this post are built to reproduce.
A note on scope before we start. By full, this post means an account wide operational sweep across the major workload categories: compute and Kubernetes scaling, data stores, storage and data protection, messaging, networking, DNS, security posture, account level governance, lifecycle and patch exposure, and resilience. It is not a substitute for a formal AWS security assessment, a Well Architected review, or a compliance audit, and the prompts later in this post deliberately treat their own service lists as a starting point rather than an exhaustive one, since no single list covers every service a given account might lean on.
The idea that makes this whole approach work is simple enough to state once and hold onto for everything that follows. The agent is not allowed to declare a risk merely because a configuration looks unusual. It must produce the specific metric, log line, or configuration value that supports the conclusion, and it must record the evidence just as carefully when a hypothesis turns out to be wrong. Observation, hypothesis, evidence, an honest attempt to disprove it, conclusion, and only then a statement of business impact. Every prompt in this post exists to enforce that discipline, not just to ask a lot of AWS CLI questions.
One consequence of that discipline is worth naming up front, because it recurs constantly across every service in this account: the absence of a symptom in the data is not the same thing as a clean bill of health, and Claude Code needs to be told this explicitly rather than left to infer it. Every prompt in this post should have Claude sort its findings into three categories, not two. A confirmed risk is one the evidence directly demonstrates. A potential risk is one the configuration suggests, where more evidence would settle it either way. An observability gap is neither of those: it is a case where the telemetry needed to answer the question simply does not exist, EC2 memory with no CloudWatch agent installed, a VPC with no Resolver query logging, an EKS cluster with no Container Insights, an instance fleet SSM cannot see for patch compliance. Reporting an observability gap as if it were a clean result is how real problems stay invisible for years. Reporting it honestly as its own category is often the single most useful thing this whole exercise produces. It is worth going one step further than just naming the gap: because this whole exercise is read only, closing a gap means recommending the specific fix rather than applying it, enable flow logs on this VPC, install the CloudWatch agent on this instance, turn on Container Insights for this cluster, so every gap in the final report comes with a concrete next step attached, not just a description of the blind spot.
1. What “read only” buys you here
Before touching a terminal, it is worth being clear about the shape of the exercise. You are going to give an agent broad visibility into an AWS account and ask it to reason about risk across compute, networking, data stores, messaging, and security. That is a lot of surface area, and the natural instinct is caution. The right way to satisfy that instinct is not to limit what the agent can see, but to limit what it can do.
A read only IAM identity, built from AWS managed policies like ReadOnlyAccess and SecurityAudit, can describe, list, and get almost anything in an account. It cannot create, modify, or delete a single resource. There is no PutObject, no RunInstances, no ModifyDBInstance available to it. If the agent gets it wrong, misreads a metric, or goes down an unproductive path, the blast radius is a wasted API call, not a production change. This is what makes it safe to let the agent explore broadly and follow its own hypotheses, the same way a human investigator would, rather than scripting every step in advance.
2. Installing the AWS CLI
If you already have the AWS CLI installed and configured, skip ahead to section 3. If not, here is the quick version for each platform.
On macOS, the simplest path is Homebrew:
brew install awscli
aws --versionOn Linux, use the official installer rather than a distribution package, since AWS tends to update the CLI faster than most package repositories:
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
aws --versionOn Windows, download and run the MSI installer from https://awscli.amazonaws.com/AWSCLIV2.msi, then confirm with aws --version in a new terminal window.
Any of these should report AWS CLI version 2. Version 1 still works for most of what follows, but version 2 has better support for SSO login flows, which is worth having if your organization uses AWS IAM Identity Center.
3. Creating a read only identity
You have two reasonable options here, depending on how your account is set up.
3.1 If you use IAM Identity Center (AWS SSO)
This is the better option if it is available to you, since it avoids long lived credentials entirely. In the IAM Identity Center console, create a permission set that attaches the ReadOnlyAccess and SecurityAudit AWS managed policies, assign it to your user against the account you want to audit, then configure the CLI to use it:
aws configure ssoFollow the prompts for your SSO start URL and region, select the account and the read only permission set when offered, and give the resulting profile a clear name such as aws-audit. From then on, you authenticate with:
aws sso login --profile aws-auditwhich opens a browser, and issues short lived credentials that expire on their own.
3.2 If you use IAM users
If your account still relies on IAM users rather than Identity Center, create a dedicated user for this purpose rather than reusing an existing one. In the IAM console, create a user named something like account-audit-readonly, attach the AWS managed policies ReadOnlyAccess and SecurityAudit, and generate an access key for programmatic access only, with no console password.
Then configure a named CLI profile so this identity is never your default:
aws configure --profile aws-auditEnter the access key ID, secret access key, your default region, and json as the output format.
Either way, verify the identity and its permissions before moving on:
aws sts get-caller-identity --profile aws-audit
aws ec2 describe-regions --profile aws-audit --output tableIf both commands succeed, you have read access and are ready to go. Set AWS_PROFILE=aws-audit in your shell for the rest of this exercise, or pass --profile aws-audit explicitly, so there is no risk of accidentally running commands against a different, more privileged identity.
3.3 If EKS is in scope, add read only Kubernetes access
The identity from 3.1 or 3.2 governs the AWS control plane around EKS: it can describe a cluster, list its node groups, and read CloudWatch metrics about them. It has no say at all over the Kubernetes API running inside the cluster itself, and the EKS deep dive in section 8.2 needs to query that API directly, things like the current HorizontalPodAutoscaler objects, pod status, and PodDisruptionBudgets. If phase 1’s inventory turns up any EKS clusters, set this up before you get there.
Install kubectl for your platform, then point it at each cluster using the aws-audit identity:
aws eks update-kubeconfig --name <cluster-name> --profile aws-auditThat command only writes to your local kubeconfig file, it never touches the cluster itself, so it is worth explicitly exempting from the read only guardrail described in section 4, which is really about avoiding AWS control plane mutations rather than local configuration changes.
Getting an actual response from a cluster’s API still needs the identity to be authorized inside Kubernetes, and it is worth being precise about how, because AWS offers two distinct mechanisms here rather than one. An EKS access entry controls whether a principal can reach the cluster’s API at all, and can be bound either to an AWS managed EKS access policy, AmazonEKSViewPolicy is the closest fit for this exercise, or mapped into Kubernetes groups that ordinary RBAC then governs. Kubernetes’s own built in view ClusterRole is designed around namespaced resources within a single namespace, which is narrower than this audit needs: the EKS deep dive also reads cluster scoped objects like nodes and their labels and status, and potentially Karpenter or EKS Auto Mode custom resources, none of which the default view role reliably covers.
The more robust approach is a small, purpose built ClusterRole, something like aws-audit-view, that explicitly grants get, list, and watch on exactly what the audit needs: nodes, pods, deployments, StatefulSets, DaemonSets, ReplicaSets, HorizontalPodAutoscalers, PodDisruptionBudgets, events, the metrics API, and whatever autoscaler custom resources (Karpenter’s NodePools and NodeClasses, for example) actually exist in that cluster, while deliberately excluding Secrets. Bind that ClusterRole to the aws-audit identity’s access entry, either directly or through a matching Kubernetes group, and never anything resembling cluster admin. With that in place, kubectl get hpa -A, kubectl get deployments -A, kubectl get pods -A, and kubectl get pdb -A will all work under the same read only principle as everything else in this exercise.
4. Installing Claude Code
Claude Code is a command line agent that can run shell commands, read their output, and iterate, which makes it a good fit for this kind of investigation: it can run an AWS CLI command, read what comes back, decide the next command based on that, and keep going until it has enough evidence to write a finding.
Install it with npm:
npm install -g @anthropic-ai/claude-codeThen start it from the directory where you want your reports to land:
mkdir aws-account-audit && cd aws-account-audit
claudeOn first run it will ask you to authenticate. Once that is done, confirm it can see your AWS CLI profile by asking it directly, something as simple as “run aws sts get-caller-identity with the aws-audit profile and tell me what you see” is a fine first message. If it returns your read only identity’s ARN, you are ready to begin.
One setting worth changing before you start the real work: tell Claude Code, either in your first message or in a CLAUDE.md file in this directory, to always pass --profile aws-audit on every AWS CLI command. It is worth being precise about the rest of that instruction. Telling it to avoid anything that is not literally named describe, get, or list sounds safe, but it would also rule out perfectly normal audit operations, CloudWatch Logs Insights’ start-query, DynamoDB’s query and scan, or IAM’s generate-credential-report among them, and it would need an explicit exception for aws eks update-kubeconfig from section 3.3, which contains the word update but only ever touches a local file. It is more robust to tell Claude Code plainly what the exercise is for, an account wide read only audit, and let it reach for whatever AWS CLI and kubectl subcommands that implies, rather than hand it a verb pattern that is both too strict in places and beside the point in others. Claude Code also has its own permission system layered on top of this: --allowedTools and --disallowedTools flags, and permission modes governing what it can run without stopping to ask. That is worth setting up as a second, mechanical layer alongside the IAM and Kubernetes permissions themselves, rather than relying on either one alone.
5. Why one prompt does not work
The temptation is to open Claude Code and type something like “audit my entire AWS account for problems.” This does not work well, for the same reason a single engineer cannot usefully investigate an entire account in one unstructured pass. AWS accounts of any real size have dozens of services in play, each with its own metrics, its own failure modes, and its own idea of what “healthy” looks like. A single broad prompt tends to produce a shallow pass over everything, or a deep dive into whichever service happens to have the most obviously alarming looking metric, while everything else gets a cursory glance.
The fix, and the approach the rest of this post walks through, is to split the work into phases that mirror how a good human investigator actually works:
Phase 0, native evidence. Before Claude starts rediscovering anything from raw CLI calls, pull together what AWS itself has already concluded, and catalog what monitoring and logging telemetry actually exists in the account, whether or not it has raised anything yet. This turns Claude into a correlation engine that reasons over evidence AWS’s own controls have already produced, rather than a slower and more expensive replacement for them.
Phase 1, discovery. Build an inventory of the major workload services in the account. What is actually in use, what regions, what scale. This phase produces a map, not findings.
Phase 2, service deep dives. Run a focused investigation against each area found in phase 1, using a prompt written specifically for that area, so the agent can bring the right expertise and the right metrics to bear on each one. This is the largest phase of the exercise and, in the version this post walks through, spans compute and scaling, EKS workload and cluster scaling, data stores, storage and data protection, messaging, networking, DNS and name resolution, security and observability coverage, account level governance and blast radius, lifecycle and end of support exposure, patching and vulnerabilities, resilience and recoverability, and cost and housekeeping.
Phase 3, synthesis. Take every deep dive report and rank the findings across the whole account by actual risk, the same way the executive summary of a good incident report separates what genuinely matters from what is interesting but low stakes.
This mirrors the method that made the ActiveMQ investigation useful: fourteen hypotheses were tested and discarded, and the ones that survived were kept in proportion, a root cause section separated cleanly from sixteen secondary findings, and those secondary findings were themselves triaged into escalate, high, medium, and low priority rather than dumped in one undifferentiated list. Ranking findings by actual cost to the business is what turns a wall of observations into something someone can act on this week.
6. Phase 0: pull in what AWS already knows
AWS provides a set of controls that can evaluate an account continuously, when they are actually switched on: Security Hub evaluates configuration against known standards, Config tracks whether resources comply with rules you have enabled, GuardDuty watches for suspicious behavior, and several other services quietly accumulate findings whether or not anyone is looking at them, provided someone turned them on in the first place. It is wasteful to have an agent re-derive all of this from scratch before it has even looked at what AWS already knows, so make this the very first prompt, and use it to check what is actually enabled rather than assume.
This phase has two halves, and it is worth asking for both explicitly. The first half is findings, conclusions AWS’s own tooling has already reached. The second half is telemetry itself, a catalog of which monitoring and logging sources actually exist and are switched on in this account, so that every later phase knows in advance what evidence it will and will not have to work with, rather than each deep dive discovering the same gap independently.
You have read only access to an AWS account via the CLI profile
aws-audit. Before investigating anything yourself, collect two
things: the evidence AWS's own tools have already produced, and a
catalog of what monitoring and logging telemetry actually exists
in this account. Later phases should be able to correlate and
prioritize against this, rather than rediscover it from scratch or
repeatedly hit the same gap without realizing another phase already
found it.
First, pull together every AWS native finding source:
- AWS Security Hub: enabled or not, which standards are active,
and every finding currently at HIGH or CRITICAL severity
- AWS Config: whether it is recording, which managed or custom
rules are evaluating resources, and every rule currently
showing NON_COMPLIANT resources
- GuardDuty: enabled or not, and every finding from the last 90
days above LOW severity
- Inspector: enabled or not, and any HIGH or CRITICAL findings
on EC2, ECR images, or Lambda functions
- IAM Access Analyzer: any active findings, particularly
resources shared outside the account
- Trusted Advisor: every red and yellow check across all
categories (cost, performance, security, fault tolerance,
service limits), noting that some checks require a Business or
Enterprise support plan and may not be available on this account
- Compute Optimizer: any resources flagged as over provisioned
or under provisioned, if opted in
- AWS Backup: whether backup plans exist and what they cover,
and recent backup job success or failure counts
- AWS Health: open events on the account specific Personal Health
Dashboard, and separately, any open events on the public AWS
Service Health Dashboard for the regions and services this
account actually uses, since an ongoing AWS side incident can
easily be mistaken for a problem in your own configuration
Second, catalog the telemetry itself, independent of whether it
has raised any findings:
- CloudWatch: total alarm count, how many are currently in ALARM
state right now versus OK or INSUFFICIENT_DATA, and what
CloudWatch dashboards exist and what they cover
- VPC Flow Logs: which VPCs have flow logs enabled, at what
traffic type (ALL, ACCEPT, or REJECT only) and delivered to
what destination, and which VPCs have none at all. This is one
of the most common gaps in this whole exercise and one of the
cheapest to close, so flag every VPC without it by name
- X-Ray: enabled or not, and for which services or applications
- CloudWatch Application Insights: enabled or not, and for which
applications
- Cost Anomaly Detection: enabled or not, and any anomalies
currently open, since an unexpected cost spike is often the
earliest visible symptom of a scaling or configuration problem
turning up elsewhere in this audit
- Whether any third party observability agent (Datadog, Dynatrace,
Instana, Prometheus, New Relic, or similar) appears to be
present, based on IAM roles, installed agents, or sidecars, so
later phases do not treat an absence of CloudWatch coverage as
an absence of any coverage
Where a service or telemetry source is not enabled at all, say so
plainly rather than reporting an empty findings list as if it
were a clean result, since "not enabled" and "enabled with
nothing found" are very different situations and later phases
need to know which one they are looking at. Where something is
missing or only partially enabled, for example flow logs on some
VPCs but not others, note the specific fix, enable it for the
remaining VPCs, as the recommendation, without enabling it
yourself.
Save this as 00-native-evidence.md. Treat it as a starting
hypothesis list and a telemetry map for later phases to confirm,
extend, or occasionally find that AWS's own tooling missed, not
as the final word.7. Phase 1: discovery and inventory
With phase 0 done, the next step is a single prompt that asks Claude Code to build a map of the account before it starts investigating anything in depth.
You have read only access to an AWS account via the CLI profile
aws-audit. Before any deep analysis, build an inventory of the
major workload services in this account. This does not need to
be exhaustive against every AWS service that exists, but treat
any resource type you skip as a gap worth noting explicitly,
rather than a silent omission.
For every region with resources, list:
- Compute: EC2 instances, EKS clusters and node groups, Lambda
functions, ECS clusters and services, Auto Scaling groups
- Data stores: RDS instances and clusters, DynamoDB tables,
ElastiCache clusters, Redshift clusters, OpenSearch domains
- Messaging: SQS queues, SNS topics, Amazon MQ brokers, MSK
clusters, EventBridge buses, Step Functions state machines
- Networking: VPCs, Transit Gateways, NAT gateways, load
balancers (ALB/NLB/CLB), VPC endpoints, CloudFront distributions,
WAF web ACLs
- DNS: Route 53 public and private hosted zones, Resolver
endpoints, and Resolver query logging configurations
- Storage: S3 buckets, EFS file systems, EBS volumes, ECR
repositories
- Security and identity: IAM users, roles, and policies (count
and flag any wildcard permissions for a closer look later),
KMS keys, Secrets Manager secrets, ACM certificates, GuardDuty
and Security Hub status
- Governance: whether this account is part of an AWS Organization,
and if so, any Service Control Policies attached to it
- API and edge: API Gateway REST and HTTP APIs
- Observability: CloudWatch alarms (count, and which services
they cover versus which services have none), log groups and
their retention settings
For each EKS cluster specifically, also note whether Container
Insights or an equivalent metrics pipeline appears to be present,
since that determines how much the dedicated EKS deep dive in
phase 2 will actually be able to see.
For each item, note the region, approximate scale (instance
count, cluster size, table item count if cheap to get), and
whether it appears to be production based on naming, tags, or
account context. Cross reference against 00-native-evidence.md
from phase 0 to fill in anything Config, Security Hub, or
Trusted Advisor already has a count or status for.
Do not investigate any specific issue yet. The output of this
phase is a structured inventory document, saved as
01-inventory.md, that phase 2 will use to decide which deep
dive prompts to run and against which specific resources.
Flag anything where you could not get a complete picture due
to permissions, and note it rather than guessing.This phase is intentionally shallow. Its only job is to tell you what exists, so that the deep dive prompts in phase 2 are not run blind against resources that are not actually there, and so that a resource type with zero instances in this account does not waste a prompt.
8. Phase 2: service deep dive prompts
This is the core of the exercise, and the part worth reusing across accounts. Each prompt below is written to be generic: paste it into Claude Code, point it at the inventory from phase 1, and let it run. Each one should produce its own numbered markdown report, in the style of a real investigation report rather than a bullet dump, with an executive summary, evidence for every claim, and a section separating what was ruled out from what remains a genuine concern. Where a prompt asks Claude to classify a finding, use the three category system from the introduction throughout: confirmed risk, potential risk, or observability gap.
8.1 Compute and scaling risk
Using 01-inventory.md as your starting point, investigate compute
and scaling risk across every EC2, ECS, Lambda, and Auto Scaling
resource in this account, using the aws-audit CLI profile. EKS
workload and node scaling gets its own dedicated deep dive
immediately after this one, so this pass does not need to go
deeper than infrastructure level metrics for any EKS clusters
found in the inventory.
For each production workload, establish:
- Current CPU, memory, and network utilization over the last 14
days (CloudWatch metrics), and whether headroom is comfortable,
tight, or already exhausted at peak
- Whether Auto Scaling policies exist, whether they have actually
triggered in the last 30 days, and whether scaling limits (min,
max, desired) make sense against observed peak load
- Single points of failure: single AZ deployments, and Lambda
functions with no reserved concurrency sitting behind a shared
account level concurrency limit that other functions could
exhaust
- Instance types and generations that are old enough to matter
for cost or performance (previous generation instance families,
EOL AMIs, deprecated Lambda runtimes)
- Any CloudWatch alarms that exist for these resources, and
specifically which ones do NOT have alarms despite being
production
A note on what is actually available before you report a gap:
EC2 does not publish memory or disk metrics by default, that
needs the CloudWatch agent installed on the instance; Lambda's
own CPU, memory, and network metrics need Lambda Insights, though
memory usage can usually still be read from the MaxMemoryUsed
field in each invocation's REPORT log line. Where a metric
genuinely is not being published, report that as an observability
gap in its own right, distinct from and not evidence of the
underlying resource being fine or being a problem.
For anything you flag as a risk, state the specific evidence
(the actual metric, the actual configuration value) that
supports it, not a general impression. If you check a hypothesis
and it turns out fine, say so explicitly rather than omitting it,
the same way you would record a hypothesis that was ruled out.
Save this as 02-compute-scaling.md, with findings ranked by
severity (production risk now, risk under growth, cost or
housekeeping) and a one paragraph executive summary at the top.8.2 EKS workload and cluster scaling risk
An EKS cluster has at least three independent scaling layers stacked on top of each other: containers request CPU and memory, Kubernetes decides how many pod replicas to run, and node autoscaling decides how much EC2 capacity sits underneath all of it. A healthy looking node group does not prove that the applications running on it can actually scale, because any one of those layers can quietly cap the others while every individual dashboard reports green. This deep dive is built around one question: trace demand, through the HPA, through the pods, through the scheduler, through node autoscaling, through EC2, through Ready pods, to load balancer target registration, and find out where that chain actually breaks first, rather than just confirming that autoscaling is switched on at each layer separately.
For every EKS cluster in 01-inventory.md, run a read only
Kubernetes and AWS infrastructure investigation using the
aws-audit CLI profile and, where read only Kubernetes access has
been set up per section 3.3, kubectl against that cluster's API.
First establish whether workload level metrics exist at all.
Check for Container Insights, Prometheus, Metrics Server, or an
equivalent pipeline providing pod and container CPU and memory
data. If none of these are present, classify that as an
observability gap for this cluster and say so plainly, rather
than treating the workloads as healthy by default.
For every production Deployment and StatefulSet, establish:
- Current and configured replica count
- CPU and memory requests for every container, in millicores and
bytes, and limits where configured
- Actual CPU and memory utilization over the last 14 days,
including peak and, where telemetry allows it, p95 and p99
- Whether requests sit materially below or above actual observed
consumption
- CPU throttling, OOMKilled containers, pod evictions, container
restart counts, and node memory pressure, where measurable
- Pods that have sat Pending or Unschedulable, and the scheduler
reason recorded against them
- Whether startup, readiness, and liveness probes exist, and
whether slow or flaky startup behavior could itself interfere
with autoscaling decisions
Then inspect every HorizontalPodAutoscaler: scaleTargetRef,
configured metrics, target value, minReplicas, maxReplicas,
current and desired replicas, and recent scaling events and
conditions. Specifically determine:
- Whether the workload reached maxReplicas while its scaling
metric stayed above target during the observation window, and
for how long and how often. This is a scaling ceiling, not a
configuration detail, quantify it
- Whether minReplicas leaves meaningful redundancy for anything
that looks like a critical production service, since a minimum
of one pod means no pod level redundancy during a normal
deployment, let alone a node or availability zone failure
- Whether CPU or memory based HPAs actually have the resource
requests they depend on configured for every relevant
container, since utilization based scaling cannot function
correctly without them, and an HPA object existing is not
evidence that it is actually working
- Whether HPAs using custom or external metrics have recently hit
FailedGetResourceMetric, FailedGetExternalMetric, or similar
conditions
- Whether CPU or memory genuinely looks like the right scaling
signal for the workload, or whether request rate, queue depth,
concurrency, or latency would predict saturation better. Where
that looks likely, flag the current metric choice as a
potential risk rather than recommending a change outright
Next, look at node capacity underneath the workloads. For every
managed node group, record instance types, availability zone
placement, current node count, minSize, desiredSize, maxSize,
allocatable versus requested CPU and memory, actual utilization,
and pod density. Identify whether Cluster Autoscaler, Karpenter,
EKS Auto Mode, or something else is responsible for adding
capacity, then:
- For Cluster Autoscaler, determine whether any node group has
reached maxSize while pods remain Pending or demand keeps
climbing, and separately flag unnecessarily high minSize values
as a cost finding rather than an availability one
- For Karpenter, inspect NodePool and NodeClass CPU and memory
limits, instance type and availability zone restrictions,
architecture requirements, and Spot versus On Demand
constraints, and identify any limit that has been reached or
sits close enough to constrain credible peak demand
State every material scaling finding as which ceiling gets hit
first, for example: an HPA maxReplicas of 20 would limit this
application before node capacity runs out, or a node group
maxSize of 12 would prevent the 47 replicas needed at observed
peak from ever being scheduled, or free IP capacity in a subnet
would be exhausted before the node group reaches its configured
maximum. An autoscaler being enabled at every layer is not
evidence the system can scale, the whole chain needs headroom
together.
Also check pod level resilience: singleton production workloads,
PodDisruptionBudgets and their minAvailable/maxUnavailable
settings, topology spread constraints, pod anti affinity where it
would be appropriate, actual pod placement across nodes and
availability zones (specifically flag any deployment where every
replica currently sits in one availability zone), rollout
settings that could remove too much capacity during a deployment,
and PodDisruptionBudget settings that could block a node upgrade
or scale in indefinitely.
Finally, check cluster level health: the Kubernetes version and
whether it is on standard or extended support, managed add on
versions, node Kubernetes versions against the control plane
version, VPC CNI IP address capacity and subnet exhaustion risk,
relevant EC2 and service quota headroom, and any evidence of
control plane throttling or failed upgrades.
Save this as 03-eks-scaling.md, with separate sections for
workload sizing, pod autoscaling, node autoscaling, resilience,
observability gaps, and scaling chain bottlenecks. Rank findings
by production impact, and lead with the scaling chain findings
specifically, since those are the ones a per service dashboard
will not show on its own.This is deliberately the most detailed prompt in the whole audit, and it earns that. AWS treats HPA based replica scaling, right sizing requests and limits, and node autoscaling through Cluster Autoscaler or Karpenter as three separate concerns for good reason, and separately cautions that CPU and memory are not always reliable predictors of when an application is actually about to run out of capacity. The failure mode this prompt is built to catch, every individual layer reporting healthy while the combination cannot actually absorb a demand spike, is exactly the kind of thing a set of per service CloudWatch dashboards will never show you, because no single dashboard owns the whole chain.
8.3 Data stores
Using 01-inventory.md, investigate every RDS instance and
cluster, DynamoDB table, ElastiCache cluster, OpenSearch domain,
and Redshift cluster in this account, using the aws-audit CLI
profile.
For each one, establish:
- Current utilization: CPU, connections, storage, IOPS/throughput
against provisioned or burst limits, over the last 14 days
- Multi AZ configuration for anything that looks like production,
and whether it is actually multi AZ or only configured to
appear so
- Backup configuration: automated backup retention, whether a
recent restorable snapshot actually exists, point in time
recovery status for DynamoDB (the cross service resilience
deep dive later in this audit will build on this, so keep the
raw evidence here rather than a conclusion)
- Connection pool exhaustion risk: max connections configured
versus observed peak connection count
- Encryption at rest and in transit status
- Logging configuration, specifically whether verbose or
parameter level query logging is enabled on anything that could
contain personal or financial data, and where those logs are
written and who can read them
- Any read replica lag, if replicas exist, over the observation
window
Cross check anything that looks anomalous against a longer
baseline (30 days) before flagging it, the same way you would
rule out a diurnal pattern before calling something an incident.
Save this as 04-data-stores.md, ranked findings, with the
logging and data exposure check treated as its own clearly
separated section given its different kind of risk.8.4 Storage and data protection
Object storage and block storage rarely get the scrutiny the databases sitting on top of them do, and they carry a different kind of risk: exposure rather than downtime. A misconfigured bucket policy, a public snapshot, or a file system with no lifecycle management is not something CPU or connection metrics will ever surface, so it needs its own pass rather than a couple of bullets tacked onto the data stores prompt.
Using 01-inventory.md, investigate S3, EBS, EFS, and ECR in this
account, using the aws-audit CLI profile.
For S3, on every bucket:
- Block Public Access settings, both the account level defaults
and any bucket level overrides
- Bucket policies and ACLs, specifically anything granting access
to the public or to another AWS account, and whether that looks
intentional given the bucket's apparent purpose
- Default encryption, and whether it uses SSE-S3 or SSE-KMS
- Versioning, and Object Lock where the bucket appears to hold
backups, compliance data, or anything else where accidental or
malicious deletion would matter
- Cross region or cross account replication, where configured
- Lifecycle policies, or their absence, on buckets that look like
they hold logs or backups
- Server access logging or S3 data event logging through
CloudTrail, and whether it is enabled for buckets that hold
anything sensitive
- Buckets with no recent object activity that also do not appear
in any other report in this audit, flagged as candidates for
investigation rather than for deletion
For EBS, on every volume:
- Encryption status
- Unattached volumes, and how long they have been unattached
- Snapshot posture: whether recent snapshots exist, and whether
any snapshot is shared publicly or with another AWS account
- Volume type, flagging older types (gp2, io1) that a newer
equivalent (gp3, io2) would usually outperform or undercut on
cost
- IOPS and throughput utilization against the provisioned limit,
over the last 14 days, where CloudWatch metrics allow it
For EFS, on every file system:
- Encryption at rest
- Mount target coverage across availability zones, flagging any
file system whose mount targets sit in only one AZ
- Throughput mode, and, for bursting mode, burst credit balance
and any evidence of throughput being throttled
- Lifecycle management policies for transitioning infrequently
accessed data
- Whether the file system is covered by an AWS Backup plan
(cross reference 00-native-evidence.md rather than re-deriving
this)
For ECR, on every repository:
- Whether image scanning is enabled, and any unresolved HIGH or
CRITICAL findings
- Whether the repository is public, and if so, what it actually
contains
Save this as 05-storage.md, ranked findings, with anything
involving public or cross account exposure treated as its own
clearly separated section given how differently it should be
prioritized from a housekeeping issue like an unattached volume.8.5 Messaging and asynchronous systems
Using 01-inventory.md, investigate every SQS queue, SNS topic,
Amazon MQ broker, MSK cluster, EventBridge bus, and Step
Functions state machine in this account, using the aws-audit
CLI profile.
For each one, establish:
- Queue depth and age of oldest message over the last 14 days,
specifically looking for queues that are growing rather than
draining
- Dead letter queue configuration: does one exist, is it actually
receiving messages, and if so how many and since when
- For Amazon MQ and MSK: broker or cluster CPU, memory, heap
usage, connection counts, and whether CloudWatch alarms exist
covering broker health, not just application level metrics
- For MSK specifically: under replicated partitions, offline
partitions, and whether enhanced monitoring is enabled
- For Step Functions: execution failure rate over the same
window, and whether failures cluster around a specific state
- Consumer lag or processing latency where derivable from
available metrics, distinguishing request path health from
reply or response path health where the two are separate,
since these can diverge even when overall throughput looks
fine
- Availability zone placement of any broker or cluster claiming
multi AZ redundancy, confirmed against actual current node
placement, not just the configured deployment mode
- Message persistence settings, and whether that setting matches
what you would expect given the apparent sensitivity of the
workload
State every conclusion with the specific metric or configuration
value behind it. Where request and reply paths could plausibly
diverge, check both separately rather than assuming that healthy
send metrics mean the round trip is healthy.
Save this as 06-messaging.md, ranked findings.8.6 Networking
Using 01-inventory.md, investigate the networking layer of this
account: VPCs, Transit Gateways, NAT gateways, load balancers,
VPC endpoints, CloudFront distributions, WAF web ACLs, and
security groups and NACLs, using the aws-audit CLI profile. DNS
and Route 53 get their own dedicated investigation immediately
after this one, so this prompt can stay focused on transport
and connectivity.
For each one, establish:
- Transit Gateway and NAT gateway throughput and error metrics
(blackhole drops, PacketsDropCount, ErrorPortAllocation) against
their limits over the last 14 days
- Load balancer health: healthy target counts per availability
zone, specifically flagging any load balancer where one or more
configured availability zones currently has zero healthy
targets, since this is a silent single point of failure that
looks fine until failover is actually needed
- VPC flow log rejection rate and, for anything above a trivial
baseline, what is being rejected and why. Cross check
00-native-evidence.md for which VPCs actually have flow logs
enabled before reporting on this. If VPC flow logs are not
enabled for a given VPC at all, report that plainly as an
observability gap rather than reporting a zero rejection rate,
and recommend enabling flow logs (traffic type ALL, delivered
to CloudWatch Logs or S3) as the specific fix, since this is
usually the single highest value piece of telemetry missing
from a networking investigation and the cheapest to turn on
- Security groups with overly broad ingress rules (0.0.0.0/0 on
anything other than a load balancer's public listener)
- Whether any documented architecture (for example, traffic that
is supposed to route through a load balancer or gateway) is
actually being bypassed, by checking VPC flow logs for direct
connections that skip the intended path
Save this as 07-networking.md, ranked findings, and call out
explicitly anything that "carries zero traffic today" as
distinct from something actively causing problems, since a
non functional failover path is a real risk even with no
current symptom.8.7 DNS and name resolution
DNS is easy to leave out of an exercise like this, because a networking prompt can quietly wave at Route 53 and move on. That is a mistake. DNS sits underneath almost everything else in the account, it fails in ways that take down large parts of an organization at once, and, unlike most of the other categories here, it has its own distinct logging surface that most accounts never turn on. Give it a full prompt of its own.
Perform a DNS and Route 53 risk assessment for this AWS account,
using the aws-audit CLI profile. This is a read only investigation.
Start by discovering the full DNS estate. Do not assume public
hosted zones are the whole picture. Inspect:
- Route 53 public hosted zones
- Route 53 private hosted zones, and which VPCs they are
associated with
- Route 53 Resolver query logging configurations
- Resolver inbound and outbound endpoints
- Resolver rules and rule associations
- Route 53 Profiles, if present
- Route 53 health checks
- DNS Firewall configuration, if present
- DNSSEC configuration
- CloudWatch DNS related metrics and alarms
- CloudTrail evidence of DNS configuration changes, where
available
- Hosted zone delegation and authoritative name server
configuration
- Cross account or cross VPC DNS dependencies you can discover
- Any DNS related findings already captured in
00-native-evidence.md
Query logging coverage is a required part of this audit, not an
optional detail. For every public hosted zone, determine whether
Route 53 public query logging is enabled. For every VPC, determine
whether Resolver query logging is enabled and whether that
specific VPC is actually associated with a logging configuration.
Do not assume that one logging configuration existing means every
VPC is covered, list covered and uncovered VPCs explicitly. Where
a logging configuration exists, record its destination, retention
period, whether logs are currently being delivered, an approximate
volume, and the age of the oldest available data. Where query
logging is not enabled, report UNKNOWN, DNS QUERY TELEMETRY
UNAVAILABLE for that zone or VPC rather than inferring that DNS is
healthy simply because no errors are visible. Do not enable
logging yourself, recommend the change and stop there.
Where logging is available, analyze at least 30 days of history
where retention allows it, looking for: NXDOMAIN, SERVFAIL, and
REFUSED response trends; unusual spikes or sudden drops in query
volume; heavily queried records; unexpected or suspicious external
lookups; repeated failed lookups against names that look
decommissioned; dependencies on external DNS services; unexpected
dependencies between environments; DNS Firewall blocks; and any
pattern that would reasonably support a concern about command and
control or DNS tunnelling, stated conditionally rather than
asserted outright unless the evidence is strong. Remember that DNS
caching means query logs do not capture every lookup an
application performs, so state that limitation explicitly rather
than treating a query count as a request count.
Then review the configuration itself for each material hosted
zone: authoritative delegation is correct and NS records match;
no obviously abandoned or duplicate zones; no records pointing at
resources that no longer exist; alias and CNAME destinations still
resolve to something real, with an eye specifically for dangling
records that could allow a subdomain takeover; TTLs are reasonable
for the role of the record; routing policies (weighted, failover,
latency, geolocation, geoproximity) are intentional and, where
failover routing is used, backed by a genuinely healthy health
check; DNSSEC is configured where expected; public records do not
unintentionally expose internal naming; private zones are
associated with the VPCs you would expect and do not create
ambiguous resolution through overlapping namespaces; and Resolver
endpoints have sensible redundancy, span enough availability
zones, and sit behind appropriately restricted security groups.
Trace a handful of the most important production domain names all
the way through, DNS record, to alias target, to the CloudFront
distribution, load balancer, or API Gateway behind it, to the
application itself, and flag any case where the DNS layer looks
healthy but the destination is missing, unhealthy, or pointing at
an unexpected environment.
Where a record shows no observed queries, do not recommend
deleting it on that basis alone, logging may be incomplete, DNS
caching can hide real usage, and some records are only exercised
during failover or infrequent operational processes. Classify
these as candidates for further investigation instead.
Finally, produce a DNS risk summary covering: the number of public
and private hosted zones; the number and percentage of public
zones with query logging enabled; the number and percentage of
VPCs with Resolver query logging, and which VPCs have none; log
retention; NXDOMAIN and SERVFAIL rates where measurable; DNS
Firewall coverage; Resolver endpoint resilience; DNSSEC coverage;
the count of stale or dangling records flagged for investigation;
and the material DNS dependencies and single points of failure you
found. Rank findings the same way as every other report in this
audit, and use the confirmed risk, potential risk, and
observability gap classification throughout rather than reporting
an absence of visible problems as a clean result.
Save this as 08-dns.md.The two AWS logging surfaces behind this prompt are worth understanding before you read the report it produces. Public hosted zone query logging is delivered to CloudWatch Logs, and the log group for it has to sit in us-east-1 regardless of where the hosted zone itself lives, so an account with resources elsewhere can easily end up with this switched off simply because nobody thought to look in that region. Resolver query logging, the equivalent for DNS originating inside your VPCs, can be delivered to CloudWatch Logs, S3, or Firehose, and it can record the originating VPC, the originating IP address, and often the originating instance alongside the query itself, which is what actually lets Claude reconstruct dependencies between services rather than guessing at them from architecture diagrams. AWS Security Hub even ships a specific control, Route53.2, that fails when a public hosted zone has no query logging enabled, which is one more reason phase 0’s native evidence pass is worth cross referencing here rather than treating this as a fresh discovery every time.
8.8 Security, identity, and observability gaps
Using 01-inventory.md and 00-native-evidence.md, investigate
identity, security posture, and monitoring coverage across this
account, using the aws-audit CLI profile.
For each one, establish:
- IAM: users or roles with wildcard (*) resource or action
permissions, evaluated together with the specific actions
involved and any conditions or permission boundaries attached,
since a wildcard resource is unavoidable for some AWS actions
and is not automatically a finding on its own. Also note access
keys older than 90 days, users with console access but no MFA,
and unused roles and users (no activity in the last 90 days via
IAM access advisor)
- KMS: keys with rotation disabled, keys used across account
boundaries, and specifically note anything where a key
controlling a production resource lives in a different AWS
account than the resource itself
- Secrets Manager and Parameter Store: secrets not rotated within
their configured rotation window, plaintext parameters holding
values that look like credentials
- GuardDuty and Security Hub: enabled or not, and if enabled,
any high or critical findings currently open (cross check
against 00-native-evidence.md rather than re-pulling this)
- Monitoring coverage: cross reference against the full resource
inventory from phase 1 and list every service category that has
no CloudWatch alarms. Before treating that absence as a finding
on its own, check whether the account appears to use a third
party observability platform instead, Instana, Datadog,
Dynatrace, or Prometheus are common examples, by looking for
related IAM roles, agents, or sidecars turned up during the
compute deep dive. If no monitoring coverage exists anywhere,
CloudWatch or otherwise, that is often the single highest value
finding in this kind of review, since a service with no
effective monitoring fails silently until a person notices
- Any existing CloudWatch alarms that are configured against a
metric the underlying resource type does not actually publish
(these show as permanently INSUFFICIENT_DATA and give false
confidence)
Save this as 09-security-observability.md, ranked findings, with
the "which production services have no effective monitoring
coverage" list given its own clearly visible section near the
top, since this finding tends to explain why other findings in
the other reports went unnoticed for as long as they did.8.9 Governance and blast radius
Every other prompt in this audit asks some version of “can this individual resource fail.” This one asks a different question: what single credential, pipeline, or control plane action could take out a large number of resources at once. That is a distinct axis of risk, and it is the one a CIO facing review needs most, because it is the one no per service dashboard will ever surface on its own.
Using 01-inventory.md and 00-native-evidence.md, investigate
account level governance and blast radius risk, using the
aws-audit CLI profile.
Establish:
- Whether this account is part of an AWS Organization, and if so,
what Service Control Policies apply to it and what they
actually restrict or permit
- Whether CloudTrail is enabled across all regions, whether log
file validation is turned on, and whether trails deliver to a
centralized, separately controlled destination rather than a
bucket inside this same account
- Config recorder coverage: which regions and which resource
types it actually records, and where the gaps are
- Root account posture: whether MFA is enabled on the root user,
and any evidence of recent root account usage in CloudTrail
- Cross account trust relationships: roles that can be assumed
from other AWS accounts, and specifically any with wildcard or
otherwise overly broad trust policies
- Privileged deployment and CI/CD roles: roles used by deployment
pipelines (CodePipeline, CodeBuild, GitHub Actions or similar
OIDC federated roles, Terraform or CloudFormation execution
roles), how broad their permissions are, and specifically
whether any of them could delete or modify a large number of
resources in one action
- Whether termination protection or deletion protection is
enabled on the production resources that support it (RDS
instances, DynamoDB tables, CloudFormation stacks, EKS clusters
where available)
- AWS Resource Access Manager shares: what this account shares
out to other accounts or the wider organization
- Any AMIs or snapshots owned by this account that are public
- Account level service quotas that sit close enough to their
current usage that they would act as an effective ceiling on
production capacity, cross referencing anything already
surfaced as a scaling ceiling in the compute or EKS reports
For anything you cannot fully determine, for example because a
management account level check requires permissions this profile
does not have, report that as an observability gap and name the
specific permission or account context that would be needed
rather than leaving the question unanswered.
Save this as 10-governance-blast-radius.md, ranked findings, and
frame each one around what it would actually take to trigger it,
one compromised credential, one misconfigured pipeline run, one
accidental terraform apply against the wrong workspace, rather
than as an abstract best practice deviation.8.10 Lifecycle and end of support exposure
Using 01-inventory.md, investigate version and lifecycle exposure
across this account, using the aws-audit CLI profile. This prompt
is about things that are fine today and will not stay that way on
their own. Do not rely on model knowledge for support dates or
version lifecycles, since these tables change over time. Obtain
them from the current AWS API or official AWS documentation, and
record the source and the date you checked it alongside each
finding.
For each of the following, where present, identify the current
version in use and compare it against AWS's published support
timeline for that version:
- RDS and Aurora engine versions, and whether any are already on
or approaching an AWS Extended Support period, noting that
Extended Support carries its own additional cost
- EKS cluster versions, and the same standard versus extended
support distinction
- EC2 AMIs, particularly any built on an operating system version
that has reached or is approaching end of life
- Lambda runtimes that AWS has deprecated or scheduled for
deprecation
- ElastiCache, MSK, and Amazon MQ engine or broker versions
against their current supported versions
- ACM certificates approaching expiry, and whether renewal is
configured to be automatic or manual for each one
Where you cannot determine a version directly from the CLI, for
example because agent based reporting is not present, report that
as an observability gap rather than assuming the resource is
current.
For anything flagged, state plainly what happens if it is left
unaddressed (loss of standard support, forced move to a paid
extended support tier, an unpatched and unpatchable operating
system) rather than a generic "should be upgraded."
Save this as 11-lifecycle-eol.md, ranked by how close each item
is to its actual support boundary, not just by how old it looks.8.11 Patching and vulnerabilities
Using 01-inventory.md and 00-native-evidence.md, investigate
patch compliance and known vulnerabilities across this account,
using the aws-audit CLI profile.
Start from Inspector findings already captured in
00-native-evidence.md, and correlate them against the specific
resources in 01-inventory.md rather than restating them in
isolation. Then check:
- SSM Patch Manager compliance data, if patch baselines and
patch groups are configured, for how many managed instances are
compliant, non compliant, or missing scan data entirely
- Which EC2 instances are actually registered with SSM as managed
instances, and which are not, since an instance SSM cannot see
is an instance this whole check cannot say anything meaningful
about
- ECR image scan findings, if scanning is enabled on the
repositories found during discovery, for any HIGH or CRITICAL
vulnerabilities in images that are actually in use by a running
task or pod, not just sitting in the registry
Where patch or scan data does not exist for a resource, whether
because SSM is not installed, scanning is not enabled, or the
resource type is not covered by any of these tools, report that
explicitly as an observability gap. A fleet with no visible
vulnerabilities because nothing is scanning it is a materially
different, and generally worse, situation than a fleet that has
been scanned and found clean, and the report should never blur
that distinction.
Save this as 12-patching-vulnerabilities.md, ranked findings,
with the fraction of the fleet that has no patch or scan
visibility at all stated as its own headline number.8.12 Resilience, backup, and recoverability
Using 01-inventory.md, 04-data-stores.md, 05-storage.md, and
00-native-evidence.md, pull together a cross service view of
backup and disaster recovery posture for this account, using the
aws-audit CLI profile. The per resource backup configuration for
RDS, DynamoDB, and EFS was already captured in the data stores
and storage reports, do not re-derive that, build on it.
Establish:
- AWS Backup plan coverage: which resource types and which
specific resources are actually included in a backup plan,
versus which production resources (EC2, and anything else
found in phase 1) have no backup plan covering them at all
- Recent backup job success and failure counts from
00-native-evidence.md, and whether any resource has a
consistent pattern of failing backups rather than an isolated
blip
- What the current backup frequency and retention actually imply
for recovery point objective, in plain terms, for the most
important production data stores
- Whether any backups are copied to a second region or a second
account, and if not, what that means for recovery from a
region wide event rather than a single resource failure
- Whether this account, and the workloads in it, depend entirely
on a single AWS region, and if so, whether that appears to be a
deliberate architectural decision or simply how it grew
This report is explicitly about what would happen during recovery,
not just whether a checkbox is ticked, so where you cannot
determine whether a restore has ever actually been tested, say so
as an observability gap rather than assuming untested backups are
trustworthy.
Save this as 13-resilience-backup.md, ranked by how much of the
account's actual production data has no credible recovery path
today.8.13 Cost and housekeeping (optional but usually worthwhile)
Using 01-inventory.md, look for cost and housekeeping issues that
are not themselves production risks but are worth a mention:
unattached EBS volumes, old unused snapshots, idle load balancers
with zero requests over 30 days, oversized instances against
actual utilization, S3 buckets with no lifecycle policy holding
data that looks like logs or backups, and any Kubernetes jobs or
controllers that appear to be recreating themselves repeatedly
in a loop, which usually indicates something failing silently
underneath even when nothing user facing is affected.
Save this as 14-cost-housekeeping.md, kept separate from the
production risk reports so the two kinds of finding do not get
mixed together in the final ranking.9. Phase 3: synthesis and the risk register
Once every deep dive report exists as its own markdown file, the last step is to have Claude Code read all of them and produce a single ranked account risk register, the way an executive summary sits above the fourteen sections of a full investigation. This is the document that actually gets handed to someone: a CIO, a platform lead, a client, whoever needs the account level picture without reading thirteen separate reports first.
The register is only useful if it reads as a set of things to do, not a list of things to worry about, so ask Claude explicitly for the same four tier structure a real incident report uses: critical, high, medium, and low, each one grouped together rather than scattered through a single undifferentiated list.
Read every numbered report in this directory, 00-native-evidence.md
through 14-cost-housekeeping.md. Produce a single synthesis
document, 00-account-risk-register.md, structured as follows.
Section 1, executive summary: three to six sentences covering
the overall state of the account and the single most important
finding, written for someone who will only read this section.
Section 2, ranked findings, grouped by priority. Assign every
genuine risk finding across all reports one of four priority
levels: Critical, High, Medium, or Low. Base that assignment on
four things together, not any one of them alone: severity (would
this cause a production incident, and how bad), likelihood (is it
already happening intermittently, or is it latent and waiting for
the right conditions), imminence (is there a specific trigger, a
scheduled maintenance window, a growth trend, a support deadline,
that would set it off soon), and detection gap (if it happened
tonight, would anything actually alert a person, or would it fail
silently until someone noticed downstream). Two findings with the
same severity and likelihood are not equally urgent if one has a
working alarm and a clear recovery path and the other has
neither, so weigh that difference explicitly rather than only
scoring severity and likelihood.
Present the findings under four clearly labelled headings, in
that order, Critical first, so the register reads as an ordered
set of actions rather than a flat list a reader has to re-sort
themselves. Within each heading, order findings by how soon they
should be tackled. For every finding, write it as an action a
team could turn straight into a ticket: what it is, the specific
evidence behind it (cite the originating report), the
classification it was given there (confirmed risk, potential
risk, or observability gap), what would trigger it in production,
and a rough sense of effort to fix (one line config change versus
architectural work).
Section 3, observability gaps: gather every observability gap
called out across all reports into one place, since this list on
its own tells a reader where the account's blind spots are, even
before any specific finding behind them is resolved.
Section 4, cleared: a short section listing anything that was
investigated and found to be fine, so a future reader knows it
was checked and does not need to check it again.
Section 5, cost and housekeeping: the lower stakes findings from
14-cost-housekeeping.md, kept visibly separate from section 2.
Do not soften or hedge a finding that the evidence clearly
supports, and do not place something in Critical or High without
a specific number or configuration value behind it. If two
reports disagree or one contradicts an earlier finding, say so
explicitly rather than picking one silently. Do not turn generic
AWS best practice advice into a finding unless it concerns a
resource actually present in this account.This is the document you actually hand to a team. It reads the way a good incident report reads: a short summary anyone can act on, a ranked and tiered list backed by evidence rather than adjectives, a dedicated view of where the account simply cannot see, and an honest account of what was checked and cleared, so nobody re-investigates the same dead end twice.
10. What good output actually looks like
The bar to hold Claude Code to throughout this process is the same bar a strong human investigator holds themselves to. Every claim should be backed by a specific number: a p99 latency in milliseconds, a connection count, an alarm state, not a vague “seems high” or “could be an issue.” When a hypothesis is tested and does not hold up, that should be recorded rather than quietly dropped, since knowing what was ruled out is almost as valuable as knowing what was found, and it stops the next person from re-treading the same ground. Findings should be separated by what they actually mean for the business: a genuine production risk that could recur tonight is not the same category as an unused snapshot burning a few cents a month, and collapsing the two into one undifferentiated list is how a real risk gets lost in the noise.
If you want to sanity check the depth Claude Code is going to, a useful habit is to ask it mid investigation to justify a specific number it has reported, the underlying CLI call, and what the raw output actually said, before it gets written into a report. This catches the kind of mistake that costs real investigation time: misreading which CloudWatch dimension a metric needs, or pulling a number from the wrong environment’s configuration file by mistake. A good agent, like a good engineer, will happily show its work and correct itself when asked.
11. A few closing notes
Run this on a schedule, not just once. An account’s risk profile shifts constantly: a maintenance event moves both nodes of a supposedly multi availability zone service into one AZ, an alarm quietly goes to INSUFFICIENT_DATA when a service migrates to a metric namespace it does not publish, a batch job starts running at a different time and nobody notices. A monthly or quarterly pass with these same prompts, diffed against the last run’s risk register, will surface drift long before it becomes an incident.
Keep the read only identity genuinely read only, and keep it separate from any identity you use for day to day work, so there is never a moment of hesitation about whether a command Claude Code is about to run could change anything. And if any part of this investigation touches an API token, an access key, or any other credential along the way, whether it belongs to an observability tool, an internal dashboard, or anything else, treat it the same way you would after any investigation that required elevated read access: rotate it once the work is done, rather than leaving it live longer than it needed to be.
Read calls are not free either. CloudWatch’s GetMetricData, which most of these prompts lean on heavily, is billed per metric requested rather than per API call, and Logs Insights queries are billed by data scanned. None of this is expensive for a single pass over one account, but an agent working through thirteen deep dive prompts across dozens of resources will make a genuinely large number of calls, so it is worth asking Claude Code to batch metric requests where the API allows it and to keep observation windows deliberately bounded, 14 or 30 days, as the prompts above do, rather than defaulting to the maximum history available.
Finally, resist the urge to skip straight to phase 2 and 3 without phases 0 and 1. The native evidence and inventory steps feel like overhead the first time you run them, but they are what stops the deep dive prompts from wasting time on resource types that are not actually in the account or on findings AWS’s own tooling had already surfaced, and they are what gives the final synthesis something honest to check its coverage against.