Skip to main content

AWSherlock

AWSherlock

Release v0.3.0 PyPI Python 3.11+ 40 security checks 10 supported AWS services License MIT

AWSherlock is a command-line scanner for AWS security configuration. It has 40 registered checks across ten supported services: 29 default configuration checks across seven services, six opt-in IAM identity governance checks, three opt-in RDS checks, one opt-in GuardDuty check and one opt-in DynamoDB check. Reports appear in your terminal, as JSON, or as an HTML file you can open in a browser. Optional identity evidence covers external agents, IAM users, role chains, OIDC workloads and native Bedrock/AgentCore role bindings.

# Scan with an existing AWS profile
awsherlock scan --profile production

Installation · First scan · All CLI options · Reports · Checks and permissions · Troubleshooting


Installation

Install Python 3.11 or newer. AWSherlock installs into an isolated environment so it can run from any directory without activating a virtual environment. Git is needed only for repository installation or the built-in GitHub updater.

PyPI installation

AWSherlock is available on PyPI. With pipx installed, run:

pipx install awsherlock
awsherlock --version
awsherlock --help

Alternatively, install with pip in an activated virtual environment:

python -m pip install awsherlock

Git is not required for an index installation. Upgrade an index-based pipx installation with pipx upgrade awsherlock; use python -m pip install --upgrade awsherlock in a virtual environment. The built-in awsherlock --update installs the GitHub main source and can include changes beyond the latest PyPI release.

Maintainers: see the publishing guide for package validation and GitHub Release steps. Contributors: see Contributing and Writing a security check.

Optional Kiro contributor aids

Kiro users can load .kiro/steering/check-authoring.md when changing checks, copy the new-check specification template, and select the read-only awsherlock-security-reviewer agent for a security review. The IDE hook runs focused local tests after a rule or collector file save. In Kiro CLI, run python .kiro/scripts/check_rule_tests.py --path src/awsherlock/rules/rds.py explicitly for a changed file. When the local ignored test module is absent, the script reports that only the public synthetic scanner smoke ran; this is not validation of the changed rule. It does not contact or modify AWS. The steering complements the public contributor guides; maintainer-only planning files are not part of the published repository.

Kiro's PostFileSave event is currently IDE-only; CLI users run the focused script manually. For a larger new check, use Kiro's requirements/design/tasks Spec flow as a local planning aid, then follow the approved project scope. See Kiro's specs, steering, hooks and custom agents documentation.

Scanner CI

Pull requests and pushes to main run the separate scanner CI workflow. It builds a wheel/source archive, installs the wheel in a fresh Python environment on Linux, Windows and macOS, then runs synthetic EC2 secure/insecure/denied checks and the installed offline CLI/JSON/HTML smoke. The checks use no AWS credentials or real AWS calls. The larger local test suite remains local; this CI gate is a focused public smoke, not a replacement for full local regression. Tag-triggered package publishing remains a separate workflow.

Windows

Open PowerShell:

# Clone the repository
git clone https://github.com/0gulcandogann/awsherlock.git
cd awsherlock

# Install the command in an isolated environment
py -m pip install --user pipx
py -m pipx ensurepath
py -m pipx install .

Close and reopen your terminal, then check the installed command:

awsherlock --version
awsherlock --help

Installation is per user and does not need administrator access. Running awsherlock without arguments also displays help and does not contact AWS.

Linux and macOS

# Clone and install without sudo
git clone https://github.com/0gulcandogann/awsherlock.git
cd awsherlock
./install.sh
awsherlock --version

The installer creates a Python environment in ~/.local/share/awsherlock/venv and links the command into ~/.local/bin. It does not need sudo or change your system Python.

If the command is not found, add the bin directory to PATH:

export PATH="$HOME/.local/bin:$PATH"

Add that line to your shell configuration, such as ~/.bashrc or ~/.zshrc, to keep it for future terminals. The installer prints the PATH command for your chosen location. Set PYTHON to select a Python executable, or AWSHERLOCK_INSTALL_ROOT and AWSHERLOCK_BIN_DIR to choose other user-owned directories.

On systems that package Python's virtual-environment support separately, install it before running the script. For example, Debian and Ubuntu provide python3-venv.

First scan

AWSherlock uses the standard AWS SDK credential chain. Existing AWS CLI profiles, environment credentials, instance or container roles, and authenticated IAM Identity Center sessions can supply credentials. The AWS CLI is useful for setting up profiles and SSO; it is not required for every scan.

If you use IAM Identity Center, sign in with your configured profile first:

aws sso login --profile production
awsherlock scan --profile production

To use the default credential chain:

awsherlock scan

Set the region in your profile or through AWS_DEFAULT_REGION, override it with --region, or select explicit regions with --regions. IAM is global, and S3 buckets are inspected in their own regions. Regions are not automatically discovered.

Your identity needs permission to read the configuration being inspected. See checks and required permissions before assigning access to an audit role. AWSherlock does not create roles or attach policies for you.

Preview a scan

For a terminal-guided live single-account plan, run awsherlock guide. It lists locally configured profiles, lets you choose a profile, a region override and supported services, then prints the validated preview and an equivalent awsherlock scan command to run. The guide does not authenticate, scan, contact AWS, create files or change your AWS configuration. It requires an interactive terminal; scripts should use scan --preview with explicit options. Profile names and configured regions are local metadata, not verified credentials. The printed command uses PowerShell quoting on Windows and POSIX shell quoting elsewhere.

awsherlock guide
awsherlock scan --preview --profile production --services iam,s3
awsherlock scan organization --preview --accounts 123456789012
awsherlock scan facts.json --preview
awsherlock scan --preview --preview-format json --services iam,s3

The preview validates local options and shows targets, services, evaluation-only check/resource selectors, and intended report/snapshot destinations. Live identity, credentials and organization membership remain unverified; no AWS calls, resource collection, rule evaluation or output files occur. Offline preview reads snapshot metadata and checks requested services/checks locally. Preview prints a text plan even when --output json or html is selected; it never writes those reports.

For a script that checks scan scope before execution, add --preview-format json. It writes one version-1 JSON object to stdout and no output file. This plan is separate from findings JSON: --output still names the intended report format. The top-level fields are schema_version (integer 1), kind (scan-preview), mode (single-account, organization, or offline), and these objects:

Object Fields and meaning
verification identity is unverified or snapshot_metadata; organization_membership is not_discovered for organization plans, otherwise null.
target profile, source_role, expected_account, snapshot_path, snapshot_account, snapshot_region, regions, region_source, organization_role_name, accounts, ous. Snapshot account/region come from saved metadata, not live verification. region_source is explicit, sdk_default or snapshot_metadata.
collection services is the selected list, or the snapshot's saved services offline.
evaluation checks and resources are selected lists or null for all; selectors_affect_collection is always false; fail_on appears as high or critical only when requested.
destinations report_format, intended report_file and snapshot paths. Paths are not created.
options Booleans external_id_supplied, identity_governance_requested and request_timeouts_configured. External ID and inventory contents are never printed.

null means no explicit value or not applicable; it does not confirm an AWS default. An invalid option exits 2, unreadable/invalid offline snapshot exits 1, and a valid local plan exits 0. --preview-format requires --preview; text is the default. JSON output stays uncolored even with --color always.

Choose services

A plain scan selects the seven default services. To limit the scan or opt in to RDS, pass a comma-separated list:

awsherlock scan --profile production --services iam,s3
awsherlock scan --profile production --services ec2,lambda,kms
awsherlock scan --profile production --services rds --region eu-west-1

Accepted names are iam, s3, ec2, lambda, secretsmanager, cloudtrail, and kms.

Control the startup display

Interactive scans show the AWSherlock banner and a percentage bar. The percentage counts finished scan steps; it is not an estimate of remaining time.

awsherlock scan --no-banner
awsherlock scan --no-progress

--no-banner hides the logo while keeping the bar. --no-progress hides both.

awsherlock scan --verbose
awsherlock scan --summary-only
awsherlock scan --region eu-west-1

--verbose writes scan stages and completed work units to stderr, including with --no-progress or redirected output. It does not enable SDK debug logs or print API payloads. JSON stdout remains unchanged. --summary-only keeps console counts, scan coverage and collection issues while hiding individual finding cards. Incomplete scans still exit with code 1. It requires console output and cannot be combined with --output json or html.

--region overrides the SDK region for live regional services, including member accounts in scan organization. Without it, SDK configuration remains in effect. IAM remains global; S3 still discovers bucket locations rather than filtering buckets to the selected region. Other regions are outside the regional scan scope. The option validates syntax, not region availability or account opt-in status; AWS failures retain incomplete coverage. Offline snapshots cannot use --region. Session region selection uses the standard Boto3 session API.

awsherlock scan --expect-account 123456789012 --save-snapshot facts.json
awsherlock scan --regions eu-central-1,eu-west-1 --save-snapshot scan-facts --stats
awsherlock scan organization --regions eu-central-1,eu-west-1 --save-snapshot org-facts
awsherlock scan --connect-timeout 5 --read-timeout 30
awsherlock scan --timeout 30 --color never
awsherlock --color always --list-checks

--expect-account verifies the authenticated target account before collecting resources. In organization mode it checks the discovery/source account; member accounts come from discovery. With --role it checks the target role account. Offline scans check snapshot metadata before evaluating. A mismatch exits with code 1; malformed IDs fail as usage errors before AWS work.

--regions scans explicit, comma-separated regions sequentially using the shared authenticated session. Repeated regions are removed in order. It cannot be combined with --region or an offline snapshot. IAM and S3 run once per account; regional coverage is labeled in console, JSON, HTML and SARIF, including denied/skipped accounts. Identical repeated findings (such as CloudTrail shadow trails) are shown once; different evidence is retained. Resource/check counts represent collection and evaluation observations across the selected scopes. Other regions remain outside the selected scope, and failures retain incomplete coverage and exit code 1.

--save-snapshot writes normalized facts, including collection issues, without another collection pass. A single live scan writes a new JSON file. Organization or --regions scans create a new directory of ACCOUNT-SCOPE.json files; replay an individual file with awsherlock scan scan-facts/123456789012-eu-west-1.json. Destinations must not exist; existing artifacts are never overwritten. Failed account authentication cannot produce a snapshot. A failed scan can leave the successfully saved subset in the new directory; review report coverage for omissions.

--connect-timeout and --read-timeout accept finite positive seconds up to 3600 for socket connection/read requests, including STS, organization discovery and collector auxiliary calls. Unspecified settings keep SDK defaults. --timeout sets both and cannot be combined with either separate option. Retries can make total elapsed time longer; these flags are not an overall scan deadline. The snapshot command also supports these timeouts, --region, --expect-account and --color.

--stats writes measured elapsed time and resource/check/finding totals to stderr; it does not claim to count AWS API calls or alter JSON data. For live scans, --measurements-file measurements.json writes a separate, new JSON file with SDK invocation counts and collection timing samples by account, opaque identity scope, region and service. The schema starts at version 1. An invocation is one SDK operation call, including denied calls, not an HTTP attempt or retry. Authentication calls are excluded. The file contains no request/response payloads, credentials or caller ARNs. It is written after collection even when coverage is incomplete; offline replay and --preview reject the flag. Reports and snapshots keep their existing schemas.

--color auto|always|never is available at the top level and on scan/snapshot. Place it before --help or --version to style eager output. auto detects the terminal; always explicitly allows ANSI colors even when redirected; never disables ANSI styling. NO_COLOR disables colors in every mode. Forcing colors does not enable the progress display on a redirected stream. JSON remains unstyled. These switches also work with organization and offline snapshot scans. They do not hide findings, permission failures or incomplete coverage, or change exit codes.

Inspect the active identity and local profiles

awsherlock profiles list
awsherlock whoami --profile production
awsherlock whoami --profile production --expect-account 123456789012

whoami verifies the effective account and principal through the same AWS SDK session layer used by scanning, without collecting resources. It contacts STS; credential resolution may also use SSO, credential_process or metadata providers. Its output shows account ID, principal type, caller ARN, profile and region. The profile is configuration metadata, not a separately verified source account. An assumed-role caller ARN is displayed as returned by STS, without inventing an IAM role path. Identity verification does not prove scanner permission coverage.

profiles list shows a table of locally configured profile names and configured regions (or unknown), including profiles defined only in the shared credentials file. It uses SDK config parsing without resolving credentials, executing a credential process, checking SSO sessions or contacting AWS. It does not display credential fields; a listed profile is not proof of a usable login or access to an account. No profiles or credentials are changed. The table uses cyan profile names, green known regions and yellow unknown or unverified state, with a profile count and an explicit Not verified identity column. Region color indicates configured metadata, not authentication success. Use --color never or NO_COLOR=1 for an uncolored table; redirected output is uncolored by default.

Live scans now show verified identity and selected scope on stderr before collection. Organization scans distinguish discovery identity from each verified target identity. Offline replay labels the account as snapshot metadata and does not verify a live identity. These summaries remain visible with --no-progress and keep JSON stdout unchanged. Recognized SSO-session failures suggest an external login command; AWSherlock does not log in automatically.

Inspect the installation and available checks

awsherlock --doctor
awsherlock --describe-check AWSH-CT-001
awsherlock --list-checks
awsherlock --list-services

--describe-check ID shows a supported check's title, service, required normalized fact, remediation and scope limitations. IDs are case-insensitive; unknown IDs fail with a usage error. This describes configuration indicators without evaluating resources. Use --list-checks to find IDs.

Terminal output shares one palette: orange headings, cyan information, green completion/remediation, yellow cautions, red errors and purple borders/badges. Finding severity remains explicit in text: CRITICAL red, HIGH orange, MEDIUM yellow, LOW cyan and INFO green. Colors adapt to terminal support; redirected output is plain by default. Set NO_COLOR=1 to disable colors. JSON, HTML and snapshot contents are unaffected by the terminal palette.

These commands do not contact AWS or resolve credentials. --doctor shows the running Python, package location, PATH launcher, dependency versions and terminal information. Use it when the command appears to run an older installation. --list-checks lists the registered check IDs and titles; --list-services shows each supported service and its check count. Use one action at a time, without a subcommand.

Command reference

Every supported option is listed below, including --help. Options belong to the command shown in each table; for example, use awsherlock --doctor and awsherlock scan --stats. --color is also available on whoami, profiles list and guide.

awsherlock --help
awsherlock whoami --help
awsherlock profiles list --help
awsherlock guide --help
awsherlock identities --help
awsherlock diff --help
awsherlock history --help
awsherlock leads --help
awsherlock scan --help
awsherlock snapshot --help

Top-level options: awsherlock [OPTIONS]

Option Default Usage
--help Off Show command help and exit without AWS calls.
--version Off Print the installed version and exit without AWS calls.
--update Off Update the installation from GitHub main; requires network access. See updates.
--doctor Off Show allowlisted local installation, dependency and terminal diagnostics; no AWS calls.
--list-checks Off List all 40 registered check IDs and titles; no AWS calls. Six identity governance checks, three RDS checks, one GuardDuty check and one DynamoDB check are opt-in.
--list-services Off List supported services and their check counts; no AWS calls.
--describe-check ID Not selected Explain a supported check, required fact, remediation and scope; IDs are case-insensitive. Example: AWSH-CT-001. No AWS calls.
--color MODE auto auto, always or never; honors NO_COLOR. Place before eager --help/--version to style them.

Choose one of --update, --doctor, --list-checks, --list-services or --describe-check; these actions cannot be combined with each other or with a subcommand. --version exits eagerly rather than running other actions.

Identity options: awsherlock whoami [OPTIONS]

Option Default Usage
--profile NAME SDK credential chain Select an AWS profile.
--role ARN Not selected Assume this IAM role before STS identity verification.
--role-session-name NAME AWSherlock when assuming a role Requires --role.
--external-id ID Not selected Trust-policy external ID; requires --role and is not displayed.
--region REGION SDK configuration Select the SDK region.
--expect-account ID Not selected Require this 12-digit verified account; mismatch exits 1 without identity output.
--timeout SECONDS SDK defaults Set both socket timeouts, greater than 0 and at most 3600; not an overall deadline.
--color MODE auto auto, always or never.
--help Off Show help without authentication.

Successful verification exits 0; session failures exit 1; invalid options exit 2. This command produces human-readable diagnostics, not a findings report.

Profile options: awsherlock profiles list [OPTIONS]

Option Default Usage
--color MODE auto auto, always or never.
--help Off Show local profile command help.

Missing config files or an empty profile list are normal (exit 0); unreadable or malformed configuration exits 1 with a sanitized error. SDK configuration paths, including AWS_CONFIG_FILE and AWS_SHARED_CREDENTIALS_FILE, are respected.

Guided setup options: awsherlock guide [OPTIONS]

Option Default Usage
--color MODE auto auto, always or never.
--help Off Show guide help without reading profiles.

The guide requires an interactive terminal. It prints a local scan preview and an equivalent command, then exits without scanning.

Compare saved snapshots: awsherlock diff BEFORE AFTER [OPTIONS]

Compare two normalized snapshot JSON files locally:

awsherlock diff before.json after.json
awsherlock diff before.json after.json --output json

NEW means a finding appears in the later snapshot and the earlier service coverage was complete. RESOLVED means a finding is absent later and that service's later coverage was complete. A finding seen in both is UNCHANGED. Missing/denied service coverage or changed account/region scope makes an unmatched finding UNKNOWN, never RESOLVED. These statuses describe observed scanner findings, not verified remediation or effective AWS access. The comparison is intentionally conservative: a partial service can make an unrelated resource change unknown. No AWS calls or output files occur.

--output json emits a version-1 snapshot-diff document with snapshot IDs, scope, service coverage, status counts and changes. It omits raw finding evidence and credentials. Exit 0 means both files were compared, including comparisons with unknown coverage; invalid options exit 2 and unreadable or invalid snapshots exit 1. This command does not apply suppression or CI thresholds.

Use diff BEFORE AFTER --fail-on new for automation: confidently new findings exit 3. Changed scope, missing service or incomplete coverage exits 1 first; no new findings with complete coverage exits 0. The option does not alter JSON or console results.

Review observed finding history

awsherlock history first.json later.json [more.json...] reads two or more saved normalized snapshots in increasing collection-time order. All inputs must describe the same account and region scope and have distinct scan IDs. Use --output json for a version-1 finding-history document. Console and JSON show per-snapshot service coverage plus the first and last timestamps where each stable check/resource finding was observed. No AWS calls or output files occur. Raw evidence is omitted.

The timestamps are bounded by the supplied snapshots. A finding absent from a later input is not reported as remediated or continuously present; missing permissions and skipped services remain visible in coverage. Valid history exits 0 even with incomplete coverage; invalid options exit 2 and invalid or unreadable snapshots exit 1.

Review correlated Lambda findings

awsherlock leads facts.json reads one normalized snapshot offline and links two existing findings on the same Lambda function: a Function URL without IAM authentication (AWSH-LAMBDA-001) and a broad AWS-managed execution-role policy (AWSH-LAMBDA-002). Use --output json for a version-1 investigation-leads document. The output retains scan coverage; incomplete coverage exits 1. No match is not proof of safety when facts are missing.

Each lead is a review hint. It does not prove anonymous reachability, effective permissions, exploitability or an attack path. It creates no new security check, graph or AWS call, and omits raw finding evidence. Invalid options exit 2; invalid or unreadable snapshots exit 1.

Record expiring suppressions

Pass --suppressions-file exceptions.json to a live or offline scan when a finding has an approved temporary exception. The file is local version-1 JSON:

{
  "schema_version": 1,
  "suppressions": [
    {
      "check_id": "AWSH-EC2-006",
      "account_id": "123456789012",
      "region": "eu-west-1",
      "resource_id": "vol-123",
      "owner": "platform",
      "reason": "Migration tracked in change ticket",
      "expires_on": "2026-12-31"
    }
  ]
}

resource_id matches a finding's exact resource ID or ARN. The account, region (use null for a global finding), and check ID must also match. Expiry is a UTC calendar date, inclusive. Findings remain in every report; matching active entries annotate them, and console/JSON/HTML show applied, unmatched and expired entries plus counts. Suppression never changes incomplete coverage or the scan exit status. Do not put credentials or secrets in owner or reason. SARIF rejects this option because its current export has no suppression audit. This file does not create or update a baseline automatically.

Offline identity view: awsherlock identities SNAPSHOT [OPTIONS]

Read IAM role/user summaries from an existing normalized snapshot without AWS calls or file writes. Example: awsherlock identities identity-facts.json. Console output includes identities and collection coverage; an empty IAM inventory is stated explicitly. --output json prints a separate version-1 identity-view document with schema_version, kind, account_id, coverage and identities. It is not the scan report schema. --color accepts auto, always or never. The command exits 0 for complete coverage and 1 for incomplete coverage or input errors. The snapshot must contain IAM; old snapshots lacking governance facts remain incomplete, not empty proof of safety.

--view ai|unowned|stale|shared filters displayed identities while keeping full IAM coverage. ai matches explicit AI declarations or native Bedrock/AgentCore bindings, never identity names or User-Agent guesses; unowned and stale match existing IAM-007 and IAM-009 review findings; shared matches an explicit shared declaration. For example, awsherlock identities identity-facts.json --view stale --output json emits a version-2 identity-view document with view, total_identities and matched_identities in addition to the version-1 fields. The default all view keeps version 1. No match is not proof that no such identities exist: missing facts, limited usage windows and incomplete coverage still matter.

Scan options: awsherlock scan [OPTIONS] [snapshot_path]

Omit snapshot_path for a live account scan, use organization for discovered member accounts, or pass a normalized JSON file for offline evaluation. awsherlock scan --help groups options by target, selection, identity evidence, reports and execution. It works locally without contacting AWS. On narrow terminals, Typer may shorten long labels; the table below lists every option name in full.

Option Default Usage
--help Off Show scan help without contacting AWS.
--profile NAME SDK credential chain Use a named AWS profile for live authentication.
--role ARN No assumed role Assume an IAM role for a single-account scan, or for organization discovery/source authentication.
--role-session-name NAME AWSherlock Name the assumed-role session; requires --role in a single-account scan. In organization mode, applies to member-account roles.
--external-id ID Not supplied External ID for the assumed role; requires --role in a single-account scan. In organization mode, applies to member-account roles.
--services LIST Seven default services Comma-separated supported service names; RDS, GuardDuty and DynamoDB are opt-in. Offline selection must exist in the snapshot.
--checks LIST All checks Evaluate comma-separated registered IDs, such as AWSH-S3-001,AWSH-S3-003. Collection is unchanged; IDs must belong to the selected services/snapshot. Excluded checks remain visible in coverage.
--accounts LIST All discovered accounts Organization-only: scan comma-separated 12-digit IDs. Discovery still lists all accounts; excluded and undiscovered requested accounts are NOT_SCANNED.
--ous LIST No OU restriction Organization-only: comma-separated OU IDs and all descendants; intersects --accounts. Requires paginated organizations:ListChildren. Failed membership discovery prevents role assumption for uncertain accounts.
--resources LIST All discovered resources Evaluate exact comma-separated resource IDs or ARNs, case-sensitive; collection/saved snapshots remain unchanged. No wildcards or tags. Exclusions and unmatched selectors remain visible; works offline.
--preview Off Validate and print a local scan plan without AWS calls, rule evaluation or output files. Offline mode reads snapshot metadata. Identity and organization membership remain unverified.
--preview-format FORMAT text with --preview text or version-1 json; requires --preview. Independent of --output, which still describes the intended findings report.
--identity-governance Off Collect IAM role/user ownership, purpose, trust, usage and joined policy evidence, plus regional Lambda/EC2 role bindings. Live scans require IAM in --services; offline scans expose missing facts. Saved governance evidence is evaluated automatically.
--identity-inventory FILE Not supplied Version-1 exact ARN declarations/approvals; requires --identity-governance. Works live/offline. Missing, partial or out-of-scope approval evidence remains unknown.
--identity-events Off Live opt-in regional CloudTrail management-event attribution, including role chains. Requires --identity-governance. No raw events or credential identifiers are exported.
--identity-ai-services Off Live Bedrock/AgentCore execution-role metadata through the shared session; requires --identity-governance. No agent invocation or prompt/model-log reads.
--identity-analyzers Off Read active findings from existing regional Access Analyzer instances. Requires live --identity-governance; never creates analyzers or query jobs.
--identity-days N 30 CloudTrail lookback, 1–90 days; a non-default value requires --identity-events.
--identity-max-pages N 20 Evidence page/read budget, 1–1000, shared across regions within each supplemental collector. Requires live --identity-governance.
--identity-max-seconds N 60 Evidence time budget between requests, 1–3600 seconds, within each supplemental collector. In-flight SDK timeouts/retries may exceed it. Requires live --identity-governance.
--output FORMAT console console, json, html or sarif. JSON/SARIF goes to stdout unless --report-file is supplied. HTML defaults to a new awsherlock-report.html.
--report-file PATH Not supplied Write a new JSON/HTML/SARIF report; requires --output json, --output html or --output sarif. Existing files are not overwritten.
--suppressions-file PATH Off Read an exact, expiring local suppression JSON file for console/JSON/HTML. Findings and coverage remain visible; unavailable with SARIF.
--fail-on LEVEL Off high or critical: exit 3 if any unsuppressed finding meets the level. Incomplete coverage still exits 1 first.
--role-name NAME AWSherlockAuditRole Member-account role name/path, such as audit/Reader; only valid with scan organization.
--no-progress Off Hide the banner and progress bar; explicit --verbose messages still appear.
--no-banner Off Hide the banner while retaining interactive progress.
--verbose Off Write sanitized scan stages and work units to stderr; does not enable raw SDK debug logging.
--summary-only Off Console totals, coverage and issues without individual finding cards; requires console output.
--region REGION SDK-configured region Select one live region for regional services. Mutually exclusive with --regions.
--regions LIST Not selected Explicit comma-separated live regions, deduplicated in order; IAM/S3 run once per account. Mutually exclusive with --region.
--expect-account ID Not supplied Require the resolved 12-digit target account ID. Organization mode checks the discovery/source account; offline mode checks snapshot metadata.
--save-snapshot PATH Off Live scans only. Write a new file for a single-account scan, or a new directory for organization/--regions scans. Destination must differ from the report path.
--connect-timeout SECONDS SDK default Finite positive socket connection timeout, at most 3600 seconds.
--read-timeout SECONDS SDK default Finite positive socket read timeout, at most 3600 seconds.
--timeout SECONDS SDK defaults Set both request timeouts; cannot be combined with either separate timeout flag. Not an overall scan deadline.
--color MODE auto / inherited top-level preference Override terminal colors with auto, always or never. JSON payloads remain unstyled.
--stats Off Write measured elapsed time and resource/check/finding counts to stderr; no API-call count claims.
--measurements-file PATH Off Live scans only: write version-1 SDK invocation counts and collection timing samples to a new JSON file, separate from reports and snapshots. Authentication calls and HTTP retries are excluded.

Offline scans reject authentication options (--profile, --role, --role-session-name, --external-id), regional selection, request timeouts and --save-snapshot and --measurements-file. They support service selection, account verification, report formats and display/statistics options. --role-name is organization-only.

# Live: verify account, select regions, retain facts and write an HTML report
awsherlock scan --profile production --expect-account 123456789012 --regions eu-central-1,eu-west-1 --save-snapshot scan-facts --output html --report-file audit.html --timeout 30 --stats

# Organization: assume a configurable member-account role
awsherlock scan organization --profile audit --role-name audit/Reader --role-session-name audit-session --external-id configured-trust-id --regions eu-central-1,eu-west-1

# Offline: show only a compact console summary from one saved scope
awsherlock scan scan-facts/123456789012-eu-west-1.json --summary-only --no-progress --color never

Replace the account ID, profile and role trust values with your actual configuration. Explicit exclusions make coverage incomplete and retain exit code 1. These selectors do not suppress findings after scanning. Exact resource ID/ARN and descendant OU selection are implemented; tag selection remains deferred.

awsherlock scan facts.json --checks AWSH-S3-001,AWSH-S3-003 --output json
awsherlock scan organization --accounts 123456789012,999999999999 --services iam,s3
awsherlock scan organization --ous ou-abcd-12345678 --services iam,s3
awsherlock scan facts.json --resources arn:aws:s3:::example-bucket --output json

Saved directories contain individually replayable files; pass a file, not the directory, to offline scan. Coverage and error exit codes remain visible in all modes.

Snapshot options: awsherlock snapshot [OPTIONS]

This command collects one live account's normalized facts without evaluating security rules. --output is required and always names a JSON snapshot file; it is different from scan's --output FORMAT.

Option Default Usage
--help Off Show snapshot help without contacting AWS.
--output PATH Required Write a new normalized JSON snapshot file; existing files are not overwritten.
--services LIST Seven default services Select supported services using a comma-separated list; RDS is opt-in.
--profile NAME SDK credential chain Authenticate with a named AWS profile.
--role ARN No assumed role Assume this IAM role before collecting facts.
--role-session-name NAME AWSherlock Set the role session name; requires --role.
--external-id ID Not supplied External ID required by the role trust policy; requires --role.
--region REGION SDK-configured region Override the region for regional services. IAM remains global; S3 uses bucket locations.
--expect-account ID Not supplied Stop before collection if the resolved account differs from this 12-digit ID.
--connect-timeout SECONDS SDK default Finite positive socket connection timeout, at most 3600 seconds.
--read-timeout SECONDS SDK default Finite positive socket read timeout, at most 3600 seconds.
--timeout SECONDS SDK defaults Set both request timeouts; cannot be combined with either separate timeout flag.
--color MODE auto / inherited top-level preference auto, always or never; honors NO_COLOR.
awsherlock snapshot --output facts.json --profile production --services iam,s3,ec2 --region eu-central-1 --expect-account 123456789012 --timeout 30
awsherlock scan facts.json --output html --report-file offline-audit.html

Snapshot does not accept scan-only --regions, --save-snapshot, --role-name, --stats, --verbose, --summary-only, --no-banner, --no-progress or --report-file. For organization or multi-region fact capture, use awsherlock scan ... --save-snapshot PATH instead.

Reading the results

Terminal output starts with finding totals, severity counts, and a coverage table. Findings follow in order of severity. Each card identifies the resource and shows its exact normalized evidence, risk, remediation and configuration-only verification limit. Scan issues, including collection errors, appear in a separate section.

Severity describes the reported configuration risk. Coverage tells you whether the scanner had enough information to evaluate the selected checks. Read both before drawing a conclusion from the results.

Coverage Meaning
COMPLETE Applicable checks on known resources were evaluated. This is not a statement that the account is secure.
PARTIAL Some checks ran, but other checks lacked facts or permissions.
ACCESS_DENIED Permission denial prevented evaluation for this entry.
ERROR Collection or evaluation failed.
NOT_SCANNED Required facts were unavailable, or an organization account was not active.

A failed resource listing can hide resources the scanner never learned about. The count of checks not scanned covers known resources only.

Exit code Meaning
0 Evaluation completed. Findings may still exist.
1 Coverage was incomplete, or an operational error occurred.
2 The command or its arguments were invalid.
3 A requested --fail-on threshold was met with complete coverage.

Reports

Terminal HTML JSON SARIF
Review findings while scanning Explore findings in your browser Process structured scan data Exchange findings with SARIF readers
Severity-ordered cards and coverage Search, filters, evidence and remediation Metadata, findings and collection issues Logical AWS resource locations and full coverage properties
Default output --output html --output json --output sarif

HTML

# Generate a standalone browser report
awsherlock scan --profile production --output html

Open awsherlock-report.html in your browser. To choose a file name:

awsherlock scan --profile production --output html --report-file production.html

The report uses a light theme with bordered cards. It includes scan metadata, severity counts, findings, evidence, remediation, and coverage details. Use the search field and severity, service, and account filters to narrow the findings. Sorting is available by severity, service, resource, or check ID.

JSON

Write structured JSON to stdout:

awsherlock scan --profile production --output json

Or save it directly:

awsherlock scan --profile production --output json --report-file findings.json

JSON includes metadata, summary counts, normalized findings, coverage, and collection issues. --report-file is supported for JSON, HTML and SARIF output. Existing report files are never overwritten; choose a new name or move the earlier report.

For current scan/diff exit statuses and machine-readable artifact versions, see Output contracts.

SARIF

Export existing findings as SARIF 2.1.0:

awsherlock scan facts.json --output sarif --report-file findings.sarif

Each finding becomes a result with its check ID and a logical AWS resource location. AWS resources are not source files, so no physical file/line location is fabricated. The run's properties.coverage and properties.incomplete retain all coverage states, even when results are empty. This is a report conversion, not GitHub code-scanning integration, and it omits raw finding evidence. The existing incomplete-scan exit code remains 1; complete scans exit 0 even when findings exist, unless --fail-on high|critical is requested and met.

Scan another account

Use a source profile to assume an audit role in the target account:

awsherlock scan --profile production --role arn:aws:iam::123456789012:role/AWSherlockAuditRole

Replace the account ID and role name with your target role. The source identity needs sts:AssumeRole, the target role must trust that source, and the role needs the service read permissions listed in checks and permissions.

If the trust policy requires an external ID, or you want a specific session name:

awsherlock scan --profile production --role arn:aws:iam::123456789012:role/AWSherlockAuditRole --external-id audit-external-id --role-session-name security-review

Temporary credentials stay in memory. Explicit assumed-role credentials do not automatically refresh during a scan. Interactive MFA arguments are not supported; use an already authenticated source session.

Scan an organization

awsherlock scan organization --profile management --output html --report-file organization.html

The source account needs access to organizations:ListAccounts and permission to assume the audit roles in member accounts. With --ous, it also needs organizations:ListChildren to discover descendant OUs/accounts. OU and account selectors intersect; excluded accounts remain visible. If any OU branch fails, OU membership is treated as unverified and no selected member roles are assumed. AWSherlock discovers accounts and scans active accounts sequentially using AWSherlockAuditRole by default.

Choose another role name or path, and limit services if needed:

awsherlock scan organization --profile management --role-name audit/Reader --services iam,s3 --output html --report-file organization-s3-iam.html

Each member role must trust the source identity and allow the required reads. Accounts that cannot be accessed are recorded in the report while scanning continues for the remaining accounts. Non-active accounts are marked NOT_SCANNED.

For organization scans, --external-id and --role-session-name apply to the member-account roles. Use --role to assume a source discovery role first. The HTML account filter lets you review findings from one account at a time.

Capture a snapshot for offline analysis

Collect configuration facts without evaluating security rules:

awsherlock snapshot --profile production --services iam,s3 --output snapshot.json

Evaluate that file later, or generate a report without contacting AWS:

awsherlock scan snapshot.json
awsherlock scan snapshot.json --output html --report-file offline.html

Offline scans use the same rules as live scans. Authentication arguments such as --profile and --role cannot be used with a snapshot. --services can select services already present in the file. Existing snapshot files are never overwritten.

Snapshots reflect the time of collection. They contain account IDs, resource names, policies, and security metadata. Treat snapshots and reports as internal audit data and sanitize them before sharing. Organization scans can save per-account/scope snapshots with scan organization --save-snapshot; replay individual files, not the directory.

Checks

Service Count Configuration reviewed
IAM 12 Six default policy/credential checks; six opt-in ownership, purpose, stale role, trust and approval checks
S3 5 Public access safeguards, default encryption, versioning, access logging, public policy status
EC2 6 Internet-wide SSH, RDP and database ingress; IMDSv1; public addresses; EBS encryption
Lambda 3 Public function URLs, broad managed execution-role policies, deprecated runtimes
Secrets Manager 3 Rotation, broad resource-policy principals, custom encryption key state
CloudTrail 4 Usable trail, multi-region/global logging, log validation and management-event read/write or source gaps
KMS 2 Eligible key rotation, broad key-policy principals
RDS (opt-in) 3 Public manual snapshot restore, non-cluster DB instance storage encryption and public-access setting
GuardDuty (opt-in) 1 Regional detector enabled status
DynamoDB (opt-in) 1 Table point-in-time recovery status

Checks and permissions lists the check IDs, finding triggers, required AWS actions, and service-specific limits.

AWSherlock evaluates configuration indicators. It does not prove effective access or simulate policy conditions, explicit denies, permissions boundaries, or SCPs. Unknown Lambda runtimes and container-image runtimes leave runtime coverage incomplete. OU analysis, automatic remediation, and compliance certification are not included.

The scanner does not retrieve secret values, Lambda code or environment values, EC2 user data, or KMS key material. Validation includes mocked AWS APIs, local browser checks, and LocalStack integration scenarios; live AWS-account validation is not claimed.

Checks and permissions

Use these permissions to review the access needed by your audit identity. They describe reads used by the scanner, rather than a ready-to-attach policy. Source identities and target roles also need the STS or Organizations permissions described above.

S3 scanner

awsherlock scan --services s3
awsherlock scan --profile production --services s3
awsherlock scan --role arn:aws:iam::123456789012:role/AWSherlockAuditRole --services s3

Service names can be combined, for example --services iam,s3. The S3 collector lists general-purpose buckets with pagination, resolves each region, and reads five configurations through the shared session. Each setting is read once per bucket; regional clients are reused. Required read permissions:

  • s3:ListAllMyBuckets
  • s3:GetBucketLocation
  • s3:GetBucketPublicAccessBlock
  • s3:GetAccountPublicAccessBlock (account context through S3 Control)
  • s3:GetEncryptionConfiguration
  • s3:GetBucketVersioning
  • s3:GetBucketLogging
  • s3:GetBucketPolicyStatus
Check Finding trigger Severity
AWSH-S3-001 Bucket Block Public Access absent or not fully enabled MEDIUM
AWSH-S3-002 Default encryption absent or algorithm unrecognized LOW
AWSH-S3-003 Versioning absent or suspended MEDIUM
AWSH-S3-004 Server access logging destination absent LOW
AWSH-S3-005 S3 classifies the bucket policy as public (IsPublic=true) HIGH

Encryption accepts SSE-S3 (AES256), SSE-KMS, and DSSE-KMS, including the AWS-managed KMS default without an explicit key ID. A missing configuration finding does not mean objects are unencrypted: S3 encrypts new uploads automatically. Existing object encryption and KMS key usability are not inspected.

The policy check uses S3 GetBucketPolicyStatus instead of downloading or interpreting policy documents. It detects S3's public policy classification, not every unsafe policy or effective access path. No policy is a valid absence, distinct from an unreadable policy. Logging checks configuration, not delivery success or alternative CloudTrail coverage.

AWSH-S3-001 produces a MEDIUM finding when the bucket configuration is absent or any of its four safeguards is disabled. This identifies potential exposure; it does not prove anonymous access. Account Block Public Access is read once per collection and attached as normalized context. Findings show bucket, account and combined flags (logical OR per safeguard); the bucket finding remains even if account safeguards compensate. Missing account permission or old snapshots without account facts remain partial. Policy documents, ACLs, access points, and object access are not evaluated. AWS applies the most restrictive applicable Block Public Access settings.

Missing configuration is distinct from AccessDenied. Failed or malformed reads are shown as errors; other checks on the same bucket and remaining buckets continue. If a bucket fact is unavailable, its coverage issue names the affected S3 check and fact. It points to a matching bucket read failure when one is recorded; otherwise the fact is absent from the snapshot. Confirmed absent configurations remain evaluable. Account Block Public Access context has its own separate issue. Only checks with successfully collected facts run. Coverage is COMPLETE, PARTIAL, ACCESS_DENIED, ERROR or NOT_SCANNED; the evaluated check count is displayed. Errors exit with code 1; completed evaluations exit with code 0 even when findings exist. Directory buckets are not supported. No settings are changed and no objects are downloaded.

IAM scanner

awsherlock scan --services iam runs six IAM configuration checks: directly attached AWS AdministratorAccess, wildcard/complement Allow actions, wildcard/ complement Allow resources, console users without MFA, active keys older than 90 days, and active keys unused for more than 90 days (including never-used keys). Key IDs are used transiently for API requests and excluded from collected data.

Managed policies use the default version; inline policies are included. IAM is global and scanned once. Group attachments are reported on the group. Policy checks are risk indicators, not effective-permission simulation: conditions, explicit denies, boundaries, and SCPs may restrict access. Wildcard resources are necessary for some actions. Stale passwords/root credentials are not checked.

Required reads: iam:GetAccountAuthorizationDetails, iam:GetLoginProfile, iam:ListMFADevices, iam:ListAccessKeys, iam:GetAccessKeyLastUsed. For the six core checks, a missing normalized fact names its check ID in coverage. A matching per-resource policy, console-MFA or key-last-used failure is reported separately; otherwise the fact is absent from the snapshot. Inactive keys are excluded from the stale-key check. Key IDs and raw policy documents are not included in these explanations.

Identity governance and AI attribution

Start with IAM governance, then opt into additional evidence:

awsherlock scan --services iam --region eu-central-1 --identity-governance
awsherlock scan --services iam --regions eu-central-1,eu-west-1 --identity-governance --identity-inventory identities.json --identity-events --identity-ai-services --identity-analyzers --save-snapshot identity-facts
awsherlock scan identity-facts/123456789012-global-bucket.json --identity-governance --identity-inventory identities.json --output html --report-file identities.html

The JSON report contains an identities inventory; console and standalone HTML show classification, approval and observed connection mechanisms. HTML includes the normalized trust, policy source, usage, workload and event evidence. Excluded identities remain visible with NOT_SCANNED evaluation scope. Saved evidence is replayed offline without AWS calls; requesting governance on old snapshots exposes missing facts. Governance capture uses scan --save-snapshot. Use awsherlock identities SNAPSHOT for the same normalized identity inventory as a dedicated offline console or JSON view. It does not resolve credentials. Missing owner/profile, role usage/trust or approval facts name the affected governance check in coverage. If a managed-policy or group join is incomplete, IAM-002/003 remain incomplete even when no broad grant was found in known statements. Confirmed broad grants are still reported; incomplete joins never establish that no other broad grants exist.

Check Finding trigger Severity
AWSH-IAM-007 Owner absent from Owner tag and supplied declaration MEDIUM
AWSH-IAM-008 Purpose absent from Purpose tag and supplied declaration MEDIUM
AWSH-IAM-009 Role last used over 90 days ago, or over-90-day-old role with no recorded use in the tracking window MEDIUM
AWSH-IAM-010 Supported trust is broad, including OIDC without exact provider audience/subject restrictions HIGH
AWSH-IAM-011 Identity absent from a complete approval inventory for its account MEDIUM
AWSH-IAM-012 Supported role trust or observed caller differs from approvals, or declared required trust controls are missing HIGH

Service-linked roles are excluded from ownership, purpose and stale-role checks. The tag convention is Owner, Purpose, and optional IdentityType with values ai, workload, human or unknown. Only tag presence/kind is retained; arbitrary tag values are not exported. Governance joins managed policy default versions and user group grants to their identities; unresolved associations remain partial. Broad grants are review indicators. Boundaries, denies and organization controls can restrict effective permissions; IAM role age/usage does not prove an identity can be safely retired. RoleLastUsed covers at most the trailing 400 days and may cover less in some regions.

An identities.json declaration file uses this exact version-1 schema:

{
  "schema_version": 1,
  "accounts": ["123456789012"],
  "complete": false,
  "identities": [
    {
      "arn": "arn:aws:iam::123456789012:role/invoice-agent",
      "kind": "ai",
      "owner": "finance-platform",
      "purpose": "invoice processing",
      "allowed_principals": ["arn:aws:iam::999999999999:role/integration"],
      "shared": false,
      "external_id_required": true,
      "source_identity_required": false
    }
  ]
}

external_id_required and source_identity_required are optional booleans, defaulting to false. Required controls must cover every supported role-assumption Allow branch. ExternalId is integration-specific and is not an AI identifier; its value is never exported. Approval principal values are exact AWS accounts, IAM principal/provider ARNs or AWS service principals; no wildcard declarations. complete: true asserts that the supplied list contains every approved identity in the listed accounts. Absence from a partial/out-of-scope list stays unknown. An unregistered identity is a governance mismatch, not proof of compromise. Missing inventory leaves approval checks NOT_SCANNED and coverage incomplete. The file stores governance declarations, never AWS credentials.

Connection evidence covers five branches:

  • Same/cross-account role callers and observed role chains; missing source events retain issuer-only, account-only or incomplete-chain attribution.
  • IAM users, long-lived-key metadata and temporary federated-user issuers.
  • OIDC/federation: supported provider-specific exact audience/subject constraints; unsupported condition shapes remain unknown. CI workloads are not automatically AI.
  • Lambda/EC2 role bindings and optional Bedrock/AgentCore native AI bindings.
  • Declared shared human/agent identities and intermediary applications: the AWS signer may be known while the individual application actor remains unresolved.

declared_ai comes from the supplied inventory or IdentityType tag; verified_ai_binding comes from a native agent resource's execution-role metadata. Neither identifies every session of a reused role as AI execution. Ordinary compute bindings prove workload association, not AI purpose. Names, IP changes, SDK user agents, sourceIdentity presence and permitted Bedrock actions alone do not prove AI use. Unknown classification is explicit.

Event evidence uses CloudTrail LookupEvents, with regional management history limited to 90 days and logical requests spaced at least 0.55 seconds apart per region. It does not read data-event logs, so model invocations and object accesses may be outside this evidence. Page/time truncation, unsupported/unmapped actors and denied reads remain visible. Empty history does not prove non-use. Request budgets are checked between calls; configure socket timeouts for in-flight requests. Credential/session identifiers are correlated only in memory; raw events, ExternalId/sourceIdentity values and key IDs are not exported. Native detail SDK responses are reduced immediately to role bindings; instructions, environment values, code and model content are not retained/reported.

Additional read permissions, according to enabled evidence:

Evidence IAM actions
IAM profiles/policy joins iam:ListRoleTags, iam:ListUserTags, iam:GetPolicy, iam:GetPolicyVersion, in addition to the IAM reads above
Workload bindings lambda:ListFunctions, ec2:DescribeInstances, iam:GetInstanceProfile
Audit history cloudtrail:LookupEvents
Native AI bindings bedrock:ListAgents, bedrock:GetAgent, bedrock-agentcore:ListAgentRuntimes, bedrock-agentcore:GetAgentRuntime
Existing analyzer evidence access-analyzer:ListAnalyzers, access-analyzer:ListFindings, access-analyzer:GetFinding (the latter two authorize V2 reads too)

Analyzer imports retain active finding type/status, update time, tracking window, unused services/actions and supported external principals. Absence, errors and inactive analyzers remain visible; AWSherlock never creates one or changes access. Existing unused-access analyzers have AWS charges. Findings remain configuration/usage indicators rather than automatic deletion or effective-permission decisions. No live agent is executed during scanning.

EC2 scanner

awsherlock scan --services ec2 scans the SDK-configured region (configure AWS_DEFAULT_REGION or your profile region). It checks internet-wide SSH, RDP, and common database ports; IMDSv1; public IPv4/global IPv6 addresses; and EBS encryption. IPv4/IPv6 CIDRs, port ranges, and protocol -1 are supported. SSH uses TCP; RDP/database checks include TCP/UDP. Database ports: 1433, 1521, 3306, 5432, 6379, 9042, 9200, 27017. Routing, NACLs, and application exposure are not assessed. Required reads: ec2:DescribeSecurityGroups, ec2:DescribeInstances, ec2:DescribeVolumes. Missing region or permissions are visible errors. If a discovered resource has invalid ingress, metadata, address or encryption data, its valid facts remain evaluable while the affected AWSH-EC2 checks are marked incomplete with a RequiredFact coverage explanation. An instance's metadata and public-address facts are handled independently. Discovery failures before a resource ID is known remain collection issues, not passing checks.

Lambda and Secrets Manager

Use --services lambda,secretsmanager in the configured region. Lambda checks unauthenticated function URLs (including aliases), directly attached AWS AdministratorAccess/PowerUserAccess execution policies, and deprecated managed runtimes. Custom/inline role policies require IAM review; URL resource policies and effective access are not evaluated. Container images and unknown runtimes produce incomplete coverage. The runtime catalogue is dated 2026-09-15 and must be maintained against AWS runtime dates. Missing URL, attached-role-policy or managed-runtime facts name the affected AWSH-LAMBDA check in coverage. A matching per-function collection issue is reported separately; otherwise the fact is absent from the snapshot. Missing facts, including unsupported image runtimes, are never treated as passing checks.

Secrets Manager checks rotation, broad Allow principals (including conditional statements, requiring review), and non-enabled custom KMS keys. Default aws/secretsmanager encryption is accepted. Conditions and denies are not simulated. No secret values, function code, or environment variable values enter scan data. If rotation, policy or encryption metadata is missing, coverage names the affected AWSH-SECRET check and required fact. A matching per-secret collection failure is reported separately; otherwise the fact is absent from the snapshot. Missing facts are never treated as passing checks.

Required reads: lambda:ListFunctions, lambda:ListFunctionUrlConfigs, iam:ListAttachedRolePolicies, secretsmanager:ListSecrets, secretsmanager:GetResourcePolicy, and kms:DescribeKey for custom secret keys.

CloudTrail and KMS

Use --services cloudtrail,kms. CloudTrail includes organization and shadow trails, reads status in the home region, and checks for a usable trail visible in the configured region, multi-region/global-event settings, log file validation and management-event selectors (AWSH-CT-004). Usability requires applicable regional coverage and management events in addition to logging and no reported delivery error. Basic ReadWriteType and advanced eventCategory/readOnly selectors are supported, along with documented trail eventSource NotEquals exclusions for kms.amazonaws.com and rdsdata.amazonaws.com. Unsupported source/name/resource filters remain unknown/partial. Facts preserve whether read and write management events are selected by any supported selector, plus source exclusions common to every management-enabled selector. AWSH-CT-004 reports a missing read/write class or known source exclusion. Per-source read/write combinations are not simulated; a positive indicator does not prove all API events are logged. Old snapshots remain readable; absent selector facts count as NOT_SCANNED. Snapshots with a positive management indicator but no source or read/write context cannot complete AWSH-CT-004; coverage remains partial. For selected CloudTrail checks, coverage issues name the check ID and missing fact. If a related collection issue exists, inspect that operation for denial or failure; otherwise the fact is absent from the snapshot. These explanations do not add findings or turn unknown facts into a PASS. CloudTrail Lake, log contents, actual delivery and every region are not inspected. Requires cloudtrail:DescribeTrails, cloudtrail:GetTrailStatus and cloudtrail:GetEventSelectors. Without the new read permission, coverage remains partial.

Within one multi-region scan, successful status/selector reads for the same trail are reused at its home region. Failures are not cached, and later scans read again.

KMS checks automatic rotation of enabled customer-managed symmetric encryption keys with AWS_KMS origin, plus broad Allow principals in customer key policies. AWS-managed, imported, asymmetric, and disabled keys are outside automatic rotation scope. Standard root delegation and Resource: "*" alone do not trigger the policy check. Conditions/denies may restrict broad statements. Requires kms:ListKeys, kms:DescribeKey, kms:GetKeyRotationStatus, kms:GetKeyPolicy. No key material or decrypted data is requested. For a discovered key, missing rotation or policy facts name the affected AWSH-KMS check in coverage. A matching per-key read failure is reported separately; otherwise the fact is absent from the snapshot. Keys that cannot be described remain collection issues, not passing checks. AWS-managed keys remain outside the key-policy check.

RDS snapshot sharing and instance posture (opt-in)

Run awsherlock scan --services rds --region eu-west-1 to check account-owned manual DB-instance and Aurora/DB-cluster snapshots. AWSH-RDS-001 is HIGH when the snapshot's restore attribute includes all. It means public restore is configured; the scan does not observe a copy, restore, or data read. Automatic and shared snapshots, DB-instance network access, and storage encryption are outside this snapshot-sharing check. RDS is not part of the seven-service default scan.

Required reads: rds:DescribeDBSnapshots, rds:DescribeDBSnapshotAttributes, rds:DescribeDBClusterSnapshots, and rds:DescribeDBClusterSnapshotAttributes. The collector requests manual snapshots with pagination. A denied or missing attribute produces incomplete coverage, never a passing check. The normalized snapshot stores only the resource identity and a restore_public boolean.

AWSH-RDS-002 is MEDIUM when a non-cluster RDS DB instance reports StorageEncrypted=false. It reads paginated rds:DescribeDBInstances using the same opt-in service. The check covers RDS Db2, MariaDB, MySQL, Oracle, PostgreSQL and SQL Server instance families, including RDS Custom. Aurora, Multi-AZ DB cluster members, Neptune and DocumentDB are excluded. It does not verify key policies, encryption in transit, backups or application access. Missing or malformed flags and denied reads leave coverage incomplete. Old RDS snapshots without a successful instance-listing marker remain readable but cannot establish complete coverage for this new check. An empty successful listing is recorded explicitly, so it is distinct from missing evidence.

AWSH-RDS-003 is MEDIUM when the same instance scope reports PubliclyAccessible=true. It uses the existing paginated rds:DescribeDBInstances read. This is a configuration indicator: the check does not establish internet reachability because it does not evaluate security groups, routes, subnet gateways, DNS or application controls. A missing or malformed flag, denied listing, or old instance snapshot without the fact is incomplete coverage, never proof of private access.

GuardDuty detector posture (opt-in)

Run awsherlock scan --services guardduty --region eu-west-1 to check one account and region. AWSH-GD-001 is MEDIUM when paginated guardduty:ListDetectors and guardduty:GetDetector show no enabled detector. An empty detector list and disabled detectors both produce a finding. Denied or malformed reads leave coverage incomplete; unknown status does not produce a false no-detector finding. An enabled detector is a regional configuration observation, not proof that every optional protection plan is enabled or other Regions are covered. No GuardDuty setting is changed.

DynamoDB point-in-time recovery (opt-in)

Run awsherlock scan --services dynamodb --region eu-west-1 to check tables in one account and region. AWSH-DDB-001 is MEDIUM when PITR is DISABLED. The scanner uses paginated dynamodb:ListTables and per-table dynamodb:DescribeContinuousBackups; it does not read table items or change backup settings. An enabled PITR status produces no finding. Missing, denied or unknown status leaves coverage incomplete. The finding does not mean that no on-demand backup exists. Enabling PITR is a separate, potentially billable owner action, so review recovery needs and pricing before changing a table.

Check IDs for the remaining services

Check Finding trigger Severity
AWSH-IAM-001 AWS AdministratorAccess directly attached HIGH
AWSH-IAM-002 Allow statement uses wildcard actions or NotAction MEDIUM
AWSH-IAM-003 Allow statement uses wildcard resources or NotResource MEDIUM
AWSH-IAM-004 Console user has no registered MFA device HIGH
AWSH-IAM-005 Active access key is older than 90 days MEDIUM
AWSH-IAM-006 Active access key unused for over 90 days, or never used and older than 90 days MEDIUM
AWSH-EC2-001 Security group permits internet-wide SSH HIGH
AWSH-EC2-002 Security group permits internet-wide RDP HIGH
AWSH-EC2-003 Security group permits internet-wide access to a listed database port HIGH
AWSH-EC2-004 Instance metadata permits IMDSv1 MEDIUM
AWSH-EC2-005 Instance has public IPv4 or global IPv6 addressing MEDIUM
AWSH-EC2-006 EBS volume is unencrypted HIGH
AWSH-LAMBDA-001 Function URL authentication is NONE HIGH
AWSH-LAMBDA-002 Execution role directly has AWS AdministratorAccess or PowerUserAccess HIGH
AWSH-LAMBDA-003 Managed runtime is deprecated in the bundled catalogue MEDIUM
AWSH-SECRET-001 Automatic rotation is disabled MEDIUM
AWSH-SECRET-002 Resource policy allows a broad principal MEDIUM
AWSH-SECRET-003 Custom encryption KMS key is not Enabled MEDIUM
AWSH-CT-001 No usable CloudTrail trail is visible in the scanned region HIGH
AWSH-CT-002 Multi-region or global service event logging is disabled MEDIUM
AWSH-CT-003 Log file validation is disabled MEDIUM
AWSH-CT-004 Management read/write events or supported KMS/RDS Data API sources are excluded MEDIUM
AWSH-KMS-001 Eligible customer key has automatic rotation disabled MEDIUM
AWSH-KMS-002 Customer key policy allows a broad principal MEDIUM
AWSH-RDS-001 Manual DB or DB-cluster snapshot permits public restore HIGH
AWSH-RDS-002 Non-cluster RDS DB instance storage encryption is disabled MEDIUM
AWSH-RDS-003 Non-cluster RDS DB instance public-access setting is enabled MEDIUM
AWSH-GD-001 No enabled GuardDuty detector in scanned region MEDIUM
AWSH-DDB-001 DynamoDB table point-in-time recovery is disabled MEDIUM

Validation and limits

The full local regression suite passed 1,100 tests (2026-09-24), primarily with mocked AWS responses. A bounded real-AWS read-only pilot in one account and eu-central-1 exercised empty RDS and DynamoDB listings and the no-detector GuardDuty case. All three service collections had complete coverage; the GuardDuty case produced AWSH-GD-001. Live JSON findings, coverage and summary matched offline replay of the same-collection snapshot. Existing-resource positive and negative cases for RDS and DynamoDB, and enabled/disabled GuardDuty detectors, remain untested in real AWS. See the 40-check validation matrix and pilot guide.

A historical LocalStack run for the earlier 28-check baseline collected all seven services without collection errors. All 25 selected secure and insecure fixture resources matched their expected finding sets. Live JSON and independently captured snapshot/offline JSON agreed on findings, coverage and summary. HTML, console output, AssumeRole and actual HTTP permission-denial scenarios were also checked. Denied reads produced incomplete coverage and exit code 1.

Positive scenarios exercised 27 of the 28 checks. IAM key-age and stale-key scenarios used the collector's controlled clock advanced by 91 days. The S3 missing-default-encryption scenario could not be reproduced because the emulator retained automatic encryption after deletion. Pagination and organization multi-account behavior were not validated in that integration run. Lambda execution, secret rotation execution and actual log delivery were not tested.

LocalStack's ready-made AWS-managed policies added findings outside the selected test resources. The 25-resource match count excludes those ambient policies; it is not an overall accuracy score. IAM wildcard findings also need context because some AWS actions require wildcard resources. Emulator validation does not establish identical behavior in a real AWS account.

Test harnesses, fixtures, screenshots, local reports and development notes are kept outside the published repository and package. The commands in this README work with your own AWS profiles or snapshots; no bundled test environment is required.

Docker

Build the image from the cloned repository:

docker build -t awsherlock:local .
docker run --rm awsherlock:local --version

For a live scan with a host AWS profile on Linux or macOS:

docker run --rm -v "$HOME/.aws:/home/scanner/.aws:ro" -e AWS_DEFAULT_REGION=eu-central-1 awsherlock:local scan --profile production --output json

On PowerShell:

docker run --rm --mount "type=bind,source=$env:USERPROFILE/.aws,target=/home/scanner/.aws,readonly" -e AWS_DEFAULT_REGION=eu-central-1 awsherlock:local scan --profile production --output json

To save an HTML report, mount an output directory at /work. On Linux or macOS:

mkdir -p reports
docker run --rm -v "$HOME/.aws:/home/scanner/.aws:ro" -v "$PWD/reports:/work" --user "$(id -u):$(id -g)" -e HOME=/home/scanner -e AWS_DEFAULT_REGION=eu-central-1 awsherlock:local scan --profile production --output html --report-file /work/report.html

The image normally runs as UID 10001. The output mount must be writable by the container user; the command above uses your host UID and GID. The credentials mount must also be readable. Supply authentication at runtime, never during the image build. SSO sessions must already be authenticated, and any external credential helper used by a profile must be available inside the container.

You can also mount your own snapshot read-only and run offline with --network none.

Update or remove

Run the update commands from the directory where you cloned AWSherlock.

For Linux and macOS installs made with the script:

git pull --ff-only
./install.sh

For Windows pipx installs:

git pull --ff-only
py -m pipx install --force .

Alternatively, update directly from GitHub main (internet required):

awsherlock --update
awsherlock --version

Reinstalling from the updated clone also picks up changes that keep the same version number. If awsherlock --update does not refresh a clone-based pipx install, use the platform-specific commands above. Run update as a standalone command, without scan or other top-level actions. These instructions also appear in awsherlock --help and when running awsherlock without arguments.

--update displays the installer logs directly. Ctrl+C cancels the update with exit code 130. The Quick start examples in awsherlock --help and the bare command are displayed in a bordered command/description table.

The updater forces reinstallation so GitHub main changes are installed even when the package version stays the same. Older installed updaters can report success while leaving the old code in place. For Linux installs created by install.sh, refresh that updater once:

"$HOME/.local/share/awsherlock/venv/bin/python" -m pip install --upgrade --force-reinstall 'git+https://github.com/0gulcandogann/awsherlock.git@main'

The refreshed updater takes effect on the next invocation and displays real installer logs. awsherlock --version alone cannot distinguish code changes that share a version number.

To remove a Windows pipx install:

py -m pipx uninstall awsherlock

For the default Linux/macOS script install, remove the ~/.local/bin/awsherlock symlink and the ~/.local/share/awsherlock directory. If you chose custom installation paths, remove those instead. Keep your cloned repository if you want to install again later.

Troubleshooting

awsherlock is not found. On Windows, run py -m pipx ensurepath and restart the terminal application, including the IDE if you use its terminal. On Linux/macOS, check that your selected bin directory is on PATH. Use Get-Command awsherlock in PowerShell or command -v awsherlock in a Unix shell to check which command is being found.

Credentials are missing or expired. Check your selected profile and sign in again if it uses SSO. AWSherlock does not perform an interactive login. To confirm the source account with the AWS CLI, run aws sts get-caller-identity --profile production.

The report says AccessDenied. Review the specific operation in the collection issues, then compare your role permissions with checks and permissions. Other checks can still produce findings, but denied checks have not been evaluated.

A regional service could not be scanned. Set a region through your profile, AWS_DEFAULT_REGION, --region or --regions; review per-region coverage and permissions. Only explicitly selected or configured regional scope is inspected.

A report file cannot be created. Check the output directory and choose a file name that does not already exist. AWSherlock refuses to overwrite reports and snapshots.

For the complete argument list:

awsherlock --help
awsherlock scan --help
awsherlock snapshot --help

Issues and contributions

For bugs, include the AWSherlock version, the command, the affected check ID, expected versus actual behavior, and a sanitized reproduction. A false positive or missing detection can be reported in a public issue with synthetic facts. Do not attach credentials or unsanitized snapshots or reports.

For code changes, use Python 3.11 or newer in a virtual environment:

python -m venv .venv

Activate it with source .venv/bin/activate on Linux/macOS or .\.venv\Scripts\Activate.ps1 in PowerShell, then install with python -m pip install -e ..

Keep sessions in the authentication layer, AWS reads in collectors, evaluation in rules, and rendering in reporters. Reports must remain standalone and work offline. Do not add AWS write calls, custom credential storage or secret-value retrieval. Keep pull requests focused and describe how you verified the change. Security checks need secure and insecure scenarios plus missing-permission behavior; collectors also need pagination and partial-failure checks. Use synthetic data and review the diff for credentials, local artifacts and unrelated files.

Security reporting

For a suspected vulnerability in AWSherlock, use the repository's private security reporting option when available, or contact the maintainer through their GitHub profile to arrange a private channel. Do not post credentials, private account data or exploitable sensitive details in a public issue. Include the affected version, a synthetic reproduction and expected versus actual behavior. No response-time or support-period guarantee is currently offered.

Findings are configuration indicators for review. Complete coverage applies only to supported checks and known resources. Treat all reports and snapshots as sensitive audit data.

Release notes

Version 0.3.0 adds five opt-in security checks across RDS, GuardDuty and DynamoDB: public manual RDS snapshot restore access, non-cluster RDS instance storage encryption and public-access settings, regional GuardDuty detector posture, and DynamoDB table point-in-time recovery status. The seven-service default scan remains unchanged. Missing or denied reads remain incomplete coverage. See validation and limits for the bounded live pilot and untested resource cases.

Version 0.2.0 is published on GitHub, PyPI and TestPyPI. It adds offline diff, observed history and bounded investigation leads; exact expiring suppression records; opt-in automation exit thresholds; and a separate scanner CI workflow. Existing findings can also be exported as SARIF. Identity inventory/review views, guided scan setup, JSON scan preview, CloudTrail read/write coverage and explicit missing-fact explanations improve the existing 36-check catalog. Console findings now show normalized evidence, risk and remediation. Optional Kiro contributor aids are included. Missing permission coverage remains visible; no S-03 service expansion or attack-path engine is included.

Version 0.1.15 is published on PyPI and TestPyPI. It adds scan --preview for an unverified local plan without AWS calls or output files, plus per-check CloudTrail missing-fact explanations in coverage issues. No new AWS reads, checks or report format were added in this version.

Version 0.1.10 adds opt-in NHI/AI identity governance, declarations/approvals, bounded audit attribution across five connection branches, metadata-only native AI role bindings and existing Access Analyzer evidence. The catalog has 36 checks (29 default and six opt-in governance checks). It also includes explicit check, account, OU and resource selectors, S3 account safeguard context, CloudTrail management/source exclusions, scan-local collection measurements, successful trail read reuse and safer partial collection. Measurements count SDK invocations separately from HTTP retries; --stats remains elapsed/result counts. Synthetic benchmarks do not establish live AWS performance. Collection remains read-only.

Version 0.1.5, dated 2026-09-16, adds explicit single/multiple-region selection, per-region coverage in all reports, expected-account verification, per-request timeouts and reusable snapshot saving during scans. IAM/S3 are collected once per account; identical repeated findings are deduplicated. New offline discovery commands explain checks and diagnose installation issues. Terminal output shares one palette with color controls, compact summaries, stage diagnostics and measured duration/count statistics. Existing 28 checks, SDK authentication, organization scanning, offline evaluation and report formats remain supported. Collection stays sequential; this release does not claim new security rules or measured API-call performance improvements. Version 0.1.0 remains the original release baseline.

Recent changes simplify installation from a clone, add the convenience update command, organize terminal findings into severity-ordered cards, and give HTML reports a light theme with purple borders and orange shadows. Untrusted terminal control and directional formatting characters are shown as visible escapes in terminal results and CLI file messages. JSON and HTML retain the original data. Examples and development artifacts are excluded from the public tree and source distribution.

License

MIT.

Release files for awsherlock 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for awsherlock 0.3.0
File Size Uploaded
awsherlock-0.3.0.tar.gz 142.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for awsherlock 0.3.0
File Interpreter ABI Platform
awsherlock-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 287.6 kB

Release files / awsherlock-0.3.0.tar.gz

Download URL awsherlock-0.3.0.tar.gz
Size 142.6 kB
Tags Source
SHA-256 checksum
How to use checksums
f3b7c4513106a54b3f5d77b6f2b7a00d6ea796aabc9b350f4159fb25e316a51c
BLAKE2b-256 checksum
How to use checksums
d627dcdb423b7722bc361d9d233f979b757fc9725a3c0924a88b5b0327960668
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 24, 2026.

Transparency log

Release files / awsherlock-0.3.0-py3-none-any.whl

Download URL awsherlock-0.3.0-py3-none-any.whl
Size 145.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
aa06256e660773fd1bd1cb0247f4674118a1c51d147801474afa3bbe21412f74
BLAKE2b-256 checksum
How to use checksums
bd6c451f34fb175d561cf1d912959019b570a4ee83a0edb08bc900246e69ea34
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 24, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 release files

0.2.0

2 release files

0.1.15

2 release files

0.1.10

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page