Amazon Inspector is AWS's managed vulnerability scanner. It finds EC2 instances, container images in Amazon ECR and Lambda functions in an account, works out what software each one contains, and compares that inventory against vulnerability advisories. Each match becomes a finding with a severity, an environment-adjusted score and, when one exists, the version that fixes it. It also reports EC2 network exposure. It does not watch for intrusions, which is GuardDuty's job, and it does not patch anything.
The hard part is not turning it on. It is getting full coverage, then turning thousands of findings into a short list that someone will fix. This article explains how each scan type collects inventory, what a finding contains, how to enable Inspector across an organization, how to route and suppress findings, how to scan images in CI before they reach a registry, a worked triage example, failure modes and a checklist. Details were checked against the Amazon Inspector user guide and CLI reference on 2026-10-04.
How every scan works
Every scan type follows the same three steps. Inspector collects a software inventory for a resource: packages and versions, from operating system package databases and language manifests. It matches that inventory against its vulnerability data and produces package-vulnerability findings. For EC2 it separately analyses network paths to report reachability findings. Scanning is continuous, not scheduled. Inspector rescans when a resource changes, for example a new instance, a new package or a new image push, and when a newly published CVE matches inventory it already holds.
That last trigger is the important one. A container image that was clean on Monday can have a critical finding on Thursday without anyone touching it, because the advisory arrived on Thursday. Findings close automatically when Inspector sees the fix, such as the package upgraded or the image no longer in scope. Your process therefore has to cope with findings that open and close on their own.
EC2: agent-based, agentless and hybrid
EC2 has two ways to collect inventory. Agent-based scanning uses AWS Systems Manager. The instance must be an SSM managed instance in the same account, which means the SSM Agent is running and has an instance profile with permissions such as the AmazonSSMManagedInstanceCore policy. Inspector creates SSM associations in your account with names ending -do-not-delete, such as InspectorInventoryCollection-do-not-delete. If they are deleted, it recreates them at the next interval. Agent-based scans rescan on launch, on software install and on new CVEs. On Linux they add deep inspection of language packages in default and configured paths. AWS now also offers an Enhanced EC2 Scanning upgrade based on a VM Scanner, and recommends it for more consistent results across operating systems.
Agentless scanning creates EBS snapshots of an instance's volumes, tags them InspectorScan, reads them through EBS direct APIs and deletes them afterwards. It covers instances that are not SSM managed, or whose inventory is stale, provided they are EBS-backed with ext3, ext4 or xfs file systems, have fewer than 8 attached volumes, and total 1,200 GB or less. Agentless scans run every 24 hours, so a CVE published at 09:00 may not appear for an unmanaged instance until the next daily pass. Agentless scans look at all paths for language packages, agent-based scans only at configured ones. The same instance can therefore show different findings depending on which method scanned it.
Hybrid mode uses agent-based scanning where SSM is available and agentless scanning elsewhere. The user guide is inconsistent about which mode a newly activated account starts in, so read the EC2 scanning settings page rather than assuming. Separately, network reachability analysis runs every 12 hours. To exclude an instance, tag it with the InspectorEc2Exclusion key. The key is case-insensitive and excluded instances are not charged.
Container images in ECR
Activating ECR scanning switches the private registry from ECR's basic scanning to enhanced scanning, which Inspector performs and bills. Enhanced scanning covers operating system and language packages. Each repository is set to scan on push only or to scan continuously. With on-push, an image is scanned once and never re-evaluated against new CVEs. With continuous, it is rescanned when a matching CVE is published. Inspector scans only images whose ECR status is ACTIVE. When an image is archived its findings are closed, then deleted three days later.
Continuous monitoring is bounded in time. For accounts created on or after May 16, 2025, the documented default keeps monitoring an image while it was pushed within 14 days, last used within 14 days, or is inside the configured re-scan duration. Older accounts defaulted to 90 days since push or pull. An image deployed six months ago that still runs in production can drop out of monitoring unless the last-in-use signal keeps it in. Inspector maps images to running ECS tasks and EKS pods, including on Fargate, and exposes ecrImageInUseCount and ecrImageLastInUseAt as filters. That mapping is the most useful prioritisation signal for containers: a critical CVE in an image nobody runs can wait. One gap to know: the Docker manifest-list media type is not supported, so multi-architecture images need checking.
Lambda functions and code
Lambda standard scanning scans functions and layers for vulnerable dependencies when they are deployed or updated, and rescans when new CVEs arrive. Lambda code scanning is an optional extra layer that analyses the function code itself and produces code-vulnerability findings. Enabling it also enables standard scanning. A separate Code Security feature scans first-party code, third-party dependencies and infrastructure as code in connected source repositories, using the Amazon Q Developer scanning engine. Treat that as a source-repository tool rather than a runtime one.
Reading a finding
Findings arrive in three types: PACKAGE_VULNERABILITY, NETWORK_REACHABILITY and CODE_VULNERABILITY. Each has a status of ACTIVE, SUPPRESSED or CLOSED. The fields that drive triage are visible in the EventBridge event Inspector emits, abridged here from the documented example:
{
"source": "aws.inspector2",
"detail-type": "Inspector2 Finding",
"resources": ["i-0c2a343f1948d5205"],
"detail": {
"type": "PACKAGE_VULNERABILITY",
"severity": "MEDIUM",
"status": "ACTIVE",
"exploitAvailable": "YES",
"fixAvailable": "YES",
"findingArn": "arn:aws:inspector2:us-east-1:111122223333:finding/FINDING_ID",
"packageVulnerabilityDetails": {
"vulnerabilityId": "CVE-2022-3303",
"cvss": [{"baseScore": 4.7, "source": "NVD", "version": "3.1"}],
"vulnerablePackages": [{
"name": "linux-image-aws",
"version": "5.15.0.1026.30~20.04.16",
"fixedInVersion": "0:5.15.0.1027.31~20.04.16",
"remediation": "apt update && apt install --only-upgrade linux-image-aws"
}]
}
}
}The Inspector score starts from the NVD CVSS base score and adjusts it for your environment. The documentation's example lowers the score of a network-exploitable CVE on an instance that has no open network path to the internet. Use that score, together with exploitAvailable and fixAvailable, rather than raw CVSS. A finding that has a public exploit, an available fix and a reachable resource is a ticket today. A finding with no fix is a watch item.
Enabling it across an organization
Inspector is regional and per account. In an AWS Organization, designate a delegated administrator account, normally your security tooling account. It can activate scanning for members, auto-enable new accounts, view aggregated findings and set the EC2 scan mode for everyone. Do this in every Region you use, because a Region where Inspector is off is a Region with no findings, which looks identical to a clean one.
# From the delegated administrator account, per Region:
aws inspector2 enable \
--account-ids 111122223333 444455556666 \
--resource-types EC2 ECR LAMBDA LAMBDA_CODE
# Check coverage: what is scanned, and why the rest is not
aws inspector2 list-coverage --region eu-west-1The valid --resource-types values are EC2, ECR, LAMBDA, LAMBDA_CODE and CODE_REPOSITORY. Coverage, not findings count, is the first metric to watch. Instances that show as unmanaged or with stale inventory are the ones silently falling back to daily agentless scans, or not being scanned at all.
Routing and suppressing findings
Inspector publishes every new finding and every state change to EventBridge on the default bus, per Region and on a best-effort basis. A delegated administrator receives member accounts' events too, with awsAccountId identifying the source. Match all statuses so closures reach your handler too:
{
"source": ["aws.inspector2"],
"detail-type": ["Inspector2 Finding"],
"detail": {
"severity": ["HIGH", "CRITICAL"],
"status": ["ACTIVE", "SUPPRESSED", "CLOSED"]
}
}Point it at a Lambda function that keys tickets on findingArn, because every state change of a finding arrives as a new event. Create tickets only for active findings with a fix, and let later events update or close them:
def handler(event, context):
d = event["detail"]
arn = d["findingArn"]
ticket = tickets.find_by_external_id(arn) # your tracker's API
if d["status"] == "CLOSED":
if ticket: tickets.resolve(ticket, "Inspector observed the fix")
return
if d["status"] == "SUPPRESSED":
if ticket: tickets.resolve(ticket, "Risk accepted by suppression rule")
return
summary = f'{d["title"]} on {event["resources"][0]} (account {d["awsAccountId"]})'
if ticket:
tickets.update(ticket, summary=summary, severity=d["severity"])
elif d.get("fixAvailable") == "YES":
tickets.create(external_id=arn, summary=summary, severity=d["severity"],
owner=owner_from_tags(d["resources"][0]))Suppress what you have decided to accept with a suppression rule: a filter with action SUPPRESS. Suppressed findings stay queryable, but leave the default view and stop counting. Scope rules narrowly and record the reason, or they quietly become a way to hide work:
aws inspector2 create-filter \
--name "accept-CVE-2023-12345-batch-images" \
--action SUPPRESS \
--reason "No network listener in batch images; revisit 2027-01-31" \
--filter-criteria '{
"vulnerabilityId": [{"comparison": "EQUALS", "value": "CVE-2023-12345"}],
"ecrImageRepositoryName": [{"comparison": "EQUALS", "value": "batch-jobs"}]
}'
Scanning before the registry
Registry scanning tells you about a vulnerable image after it has been pushed. To fail a build earlier, use the Amazon Inspector SBOM Generator (inspector-sbomgen) in the pipeline. It builds a CycloneDX SBOM for an image, directory, archive or Go and Rust binary, and with --scan-sbom sends it to the Inspector Scan API for vulnerability results. Plugins exist for Jenkins, TeamCity and GitHub Actions. The Scan API rejects SBOMs with more than 5,000 packages with an HTTP 400.
./inspector-sbomgen container --image "$IMAGE:$GIT_SHA" \
--scan-sbom --scan-sbom-output-format inspector \
--outfile /tmp/inspector_scan.json
# then fail the job if the report contains CRITICAL findings with a fix availableFor an inventory of what is already deployed, aws inspector2 create-sbom-export writes SBOMs for scanned resources to S3 in CYCLONEDX_1_4 or SPDX_2_3 format, encrypted with a KMS key you supply. That is the answer when someone asks which services ship a given library.
Worked example: from 9,000 findings to a work list
Take an illustrative organization: 400 EC2 instances, of which 310 are SSM managed, 70 are unmanaged but EBS-backed and eligible, and 20 are ineligible. It also has 1,800 ECR images and 150 Lambda functions. In the first week Inspector reports about 9,000 active findings. The numbers are made up but the shape is typical, and the triage is the same at any size:
- Coverage first. The 20 ineligible instances, perhaps with volumes over 1,200 GB, have no findings at all. Bring them under SSM management.
- Drop what is not running. Filter container findings to
ecrImageInUseCountgreater than zero. Old images with no running tasks often account for most container findings. - Fix available and exploit available. Filter the rest to HIGH or CRITICAL with
fixAvailable = YESandexploitAvailable = YES. That leaves a short list measured in dozens. - Group by remediation, not by CVE. One base image rebuild or one AMI refresh closes hundreds of findings at once. Assign work per owner, using resource tags.
- Measure closure. Track time from first observed to closed per severity. Because Inspector closes findings automatically when it sees the fix, the metric needs no manual updates.
Failure modes
- Silent coverage gaps. Instances without SSM and ineligible for agentless scanning, Regions where Inspector is off, and images outside the monitoring window all produce zero findings. Watch coverage, not the findings count.
- On-push only. Repositories set to scan on push are never re-evaluated against new CVEs. Use continuous scanning for anything that runs.
- Deleted SSM associations. Someone cleans up resources ending
-do-not-delete. Inspector recreates them, but agent-based scans pause until it does. - Duplicate tickets. Every state change is a new event. Key on
findingArn. - Suppression creep. Broad rules with no reason or review date hide real risk. Require a reason and expire them.
- Different results from different methods. Agentless and agent-based scans see different language-package paths, so a finding can appear when an instance switches method.
Trade-offs
| Choice | Gains | Costs |
|---|---|---|
| Agent-based EC2 | Rescans on change, deep inspection, network-independent | Needs SSM managed instances and permissions |
| Agentless EC2 | No agent; covers unmanaged instances | Daily cadence; volume and file-system limits |
| Continuous ECR | New CVEs reach old images | Billed by Inspector; bounded monitoring window |
| CI scan with sbomgen | Blocks bad images before push | Only sees the build, not runtime drift |
Inspector findings flow into AWS Security Hub, alongside threat detections from GuardDuty. Routing is covered in EventBridge, organization-wide guardrails in Organizations and SCPs, and SBOM formats in SBOMs and supply chain security.
What to do next
- Pick a delegated administrator account and enable Inspector for every member account in every Region you use, with auto-enable for new accounts.
- Open the EC2 scanning settings and confirm the scan mode. Prefer hybrid, and plan the Enhanced EC2 Scanning upgrade.
- Run
list-coverageand fix every instance that is unmanaged, has stale inventory or is ineligible. Put SSM on by default with Default Host Management Configuration or instance profiles. - Set running repositories to continuous scanning and check the re-scan duration against how long your images actually stay deployed.
- Create one EventBridge rule for HIGH and CRITICAL findings in every status, feeding a handler that keys on
findingArn, opens tickets for fixable active findings and resolves them on close. - Write a suppression policy: narrow criteria, a recorded reason and a review date.
- Add
inspector-sbomgenwith--scan-sbomto the image build and fail on CRITICAL findings that have a fix. - Report time-to-close per severity monthly, and treat coverage as a metric alongside it.