AWS Inspector v2: Continuous Vulnerability Scanning for EC2, ECR, and Lambda
Inspector Classic (v1) required you to schedule scans, install an agent manually, and remember to run assessments after deployments. Inspector v2, launched in November 2021, works differently. Enable it once and it continuously monitors your EC2 instances, ECR container images, and Lambda function packages for known CVEs — scanning whenever you push a new image, deploy a new function, or when a new vulnerability is published for software you’re already running. No schedules, no manual triggers.
This guide covers what Inspector v2 actually scans, how the risk scoring works, how to deploy it organization-wide, and how to integrate findings into your CI/CD pipeline to block vulnerable images from reaching production.
What Gets Scanned
Inspector v2 covers three resource types, each with different scanning mechanisms:
EC2 instances — Inspector uses the SSM Agent (no separate Inspector agent) to scan OS packages and network reachability. It checks installed packages against the NVD and vendor advisories, and it evaluates whether open ports and network paths make vulnerabilities exploitable from the internet. An instance with a critical CVE on a package only reachable from localhost scores differently than the same CVE on a port-443 service facing the internet.
ECR container images — Inspector scans images on push to ECR and re-scans them automatically when new CVEs are published for packages the image contains. You don’t need to push a new image to get updated vulnerability data — if a new critical CVE is disclosed against OpenSSL, all your images containing that OpenSSL version get re-scanned and findings appear within hours.
Lambda functions — Inspector scans the function package (dependencies in node_modules, Python site-packages, or Java JAR files) for known CVE-affected versions. It doesn’t scan the function code itself for logic bugs — only the package dependencies.
Check what Inspector can actually see in your account:
# Enable Inspector v2
aws inspector2 enable \
--resource-types EC2 ECR LAMBDA
# Check coverage — which resources are being scanned vs not
aws inspector2 list-coverage \
--filter-criteria '{
"resourceType": [{"comparison":"EQUALS","value":"AWS_EC2_INSTANCE"}]
}' \
--query 'coveredResources[].{
Resource:resourceId,
Status:scanStatus.statusCode,
Reason:scanStatus.reason
}' \
--output table
The scanStatus.reason field tells you exactly why an instance isn’t being scanned. Common reasons:
NO_INVENTORY— SSM Agent not running or not reporting inventoryUNMANAGED_EC2_INSTANCE— instance not registered with SSMRESOURCE_TERMINATED— instance was terminatedEXCLUDED_BY_TAG— resource excluded via a suppression tag
EC2 coverage gaps are almost always SSM Agent issues. The instance needs the AmazonSSMManagedInstanceCore IAM policy and the SSM Agent must be running and reporting to Systems Manager. The SSM Session Manager guide covers the agent setup in detail.
Risk Score Calculation
Inspector calculates a risk score (0.0–10.0) for each finding. The score isn’t just the CVSS base score — it’s adjusted based on your specific environment:
Inspector Risk Score = CVSS Base Score
+ Network Reachability Adjustment
+ Exploit Intelligence Adjustment
Network reachability adds up to 1.5 points if the vulnerable resource is reachable from the internet (open port, public IP, no WAF or security group blocking the path). Exploit intelligence adds up to 1.0 points if the CVE has a known exploit available in the wild — Inspector monitors CISA’s Known Exploited Vulnerabilities catalog and adjusts scores within hours of a CVE being added.
A CVE with CVSS 7.5 becomes Inspector score 10.0 if the affected port is internet-facing and an exploit is publicly available. That same CVE on an internal-only instance with no exploit scores 7.5. This context-awareness is what makes v2 meaningfully different from a plain CVE scanner.
Viewing and Filtering Findings
# List critical findings with available fixes
aws inspector2 list-findings \
--filter-criteria '{
"severity": [{"comparison":"EQUALS","value":"CRITICAL"}],
"fixAvailable": [{"comparison":"EQUALS","value":"YES"}]
}' \
--sort-criteria '{"field":"INSPECTOR_SCORE","sortOrder":"DESC"}' \
--query 'findings[].{
Title:title,
Score:inspectorScore,
Resource:resources[0].id,
FixedIn:packageVulnerabilityDetails.vulnerablePackages[0].fixedInVersion
}' \
--output table
# Get a summary of findings by severity
aws inspector2 get-findings-statistics \
--finding-status-filters '{"status":"ACTIVE"}'
The most actionable filter is fixAvailable: YES — findings where a patched version of the package exists. Sort by Inspector score descending and work through the list. Findings without a fix available need a different response: accept the risk with a suppression note, apply a compensating control, or remove the software entirely.
ECR Integration: Block on Push
The most valuable use of Inspector in a CI/CD context is blocking vulnerable images from being pushed or deployed. Inspector integrates with ECR’s enhanced scanning to run automatically on push. Enable it at the registry level:
# Enable enhanced scanning on ECR (uses Inspector v2)
aws ecr put-registry-scanning-configuration \
--scan-type ENHANCED \
--rules '[{
"repositoryFilters": [{"filter":"*","filterType":"WILDCARD"}],
"scanFrequency": "CONTINUOUS_SCAN"
}]'
With enhanced scanning enabled, every image pushed to ECR gets scanned by Inspector v2. The scan results appear in the ECR image details within minutes. To block deployment of images with critical findings, add a check to your CI/CD pipeline:
#!/bin/bash
# Wait for Inspector scan to complete and check results
IMAGE_DIGEST=$1
REPO_NAME=$2
MAX_WAIT=300 # 5 minutes
ELAPSED=0
while [ $ELAPSED -lt $MAX_WAIT ]; do
STATUS=$(aws ecr describe-image-scan-findings \
--repository-name $REPO_NAME \
--image-id imageDigest=$IMAGE_DIGEST \
--query 'imageScanStatus.status' \
--output text 2>/dev/null)
if [ "$STATUS" = "COMPLETE" ]; then
break
fi
sleep 10
ELAPSED=$((ELAPSED + 10))
done
# Check Inspector v2 findings via Inspector API
CRITICAL_COUNT=$(aws inspector2 list-findings \
--filter-criteria "{
\"ecrImageHash\": [{\"comparison\":\"EQUALS\",\"value\":\"$IMAGE_DIGEST\"}],
\"severity\": [{\"comparison\":\"EQUALS\",\"value\":\"CRITICAL\"}],
\"fixAvailable\": [{\"comparison\":\"EQUALS\",\"value\":\"YES\"}]
}" \
--query 'length(findings)' \
--output text)
if [ "$CRITICAL_COUNT" -gt "0" ]; then
echo "BLOCKED: Image has $CRITICAL_COUNT critical vulnerabilities with fixes available"
exit 1
fi
echo "PASSED: No blocking vulnerabilities found"
exit 0
Run this script after pushing to ECR and before deploying to ECS or EKS. A critical finding with a fix available means there’s no excuse not to update the package — it’s a blocking condition. Critical findings without a fix available are worth alerting on but shouldn’t necessarily block deployment if there’s no action to take.
Lambda Scanning
Lambda scanning works with packaged dependencies, not Lambda layers or the runtime itself. Inspector scans the deployment package ZIP or container image:
# Check Lambda coverage
aws inspector2 list-coverage \
--filter-criteria '{
"resourceType": [{"comparison":"EQUALS","value":"AWS_LAMBDA_FUNCTION"}]
}' \
--query 'coveredResources[].{Function:resourceId,Status:scanStatus.statusCode}'
# List Lambda-specific findings
aws inspector2 list-findings \
--filter-criteria '{
"resourceType": [{"comparison":"EQUALS","value":"AWS_LAMBDA_FUNCTION"}],
"severity": [{"comparison":"EQUALS","value":"HIGH"}]
}' \
--query 'findings[].{
Function:resources[0].id,
Package:packageVulnerabilityDetails.vulnerablePackages[0].name,
Version:packageVulnerabilityDetails.vulnerablePackages[0].version,
FixedIn:packageVulnerabilityDetails.vulnerablePackages[0].fixedInVersion
}'
Lambda findings often surface requests, urllib3, boto3 dependency chains in Python functions and axios, lodash, or transitive npm dependencies in Node.js. The fix is almost always pip install --upgrade package or npm update, followed by redeployment.
Suppression Rules
Not every finding needs remediation. Some CVEs affect code paths your application never exercises. Suppression rules archive specific findings based on criteria:
# Suppress a specific CVE across all resources (after risk assessment)
aws inspector2 create-filter \
--name "AcceptedRisk-CVE-2023-XXXXX" \
--action SUPPRESS \
--filter-criteria '{
"vulnerabilityId": [{"comparison":"EQUALS","value":"CVE-2023-XXXXX"}]
}' \
--description "CVE-2023-XXXXX affects only Windows targets; all our EC2 is Linux"
Always document suppression decisions. The description field is the audit trail — “affects only Windows targets” is a valid reason. “We’ll fix it later” is not.
Organization-Wide Deployment
# Designate delegated admin (run from management account)
aws inspector2 enable-delegated-admin-account \
--delegated-admin-account-id 999999999999
# From security account: auto-enable for all org members
aws inspector2 update-organization-configuration \
--auto-enable '{
"ec2": true,
"ecr": true,
"lambda": true,
"lambdaCode": false
}'
lambdaCode: true enables code scanning (in addition to package scanning) — Inspector analyzes function code for secrets and code quality issues. It’s in preview and generates more noise than package scanning. Leave it off initially.
Pricing
Inspector v2 pricing per resource per month: EC2 instances $0.11, ECR images $0.09 for the initial push scan and $0.01 for continuous re-scans as new CVEs emerge, Lambda functions $0.09. A typical environment with 50 EC2 instances, 200 ECR image scans, and 30 Lambda functions runs roughly $5.50 (EC2) + $18 (ECR) + $2.70 (Lambda) = about $26/month.
The 30-day free trial covers all three resource types with no usage limits. Run it, look at the finding volume and what it catches, then decide on suppression rules before you start paying. Most teams find the ECR scanning alone worth the subscription — catching a vulnerable log4j version before it reaches production ECS is worth significantly more than the $18/month.
Inspector works best alongside GuardDuty for runtime threat detection and Security Hub for unified finding management. Inspector finds what’s vulnerable; GuardDuty finds when something is being actively exploited. You need both.
Comments