Prowler
Verified against Official docs — docs.prowler.com/getting-started/installation/prowler-cli, · official docs
What it is and where it fits 🎯#
Prowler is an open-source cloud security posture and compliance scanning tool — point it at an AWS (or Azure/GCP/Kubernetes) account and it runs hundreds of read-only checks against your actual current configuration, then maps the results directly onto named compliance frameworks: CIS, SOC 2, PCI-DSS, NIST, HIPAA, and more. It's the concrete, hands-on tool for exactly the material this series covers in Part 6 — Compliance Frameworks: that chapter explains what SOC 2's Trust Service Criteria or PCI-DSS's twelve requirements actually demand; Prowler is how you generate real, current audit evidence for those same requirements without a human manually clicking through dozens of AWS console screens.
Every other scanning tool in this series looks at something you built (an image, a Terraform plan, source
code); Prowler instead looks at the live state of a real cloud account — every check is a read-only
Describe*/List*/Get* API call, nothing that modifies anything, but the findings are about your
actual deployed infrastructure right now, not a static artifact. This makes it the natural fit for the
kind of continuous evidence-gathering a SOC 2 Type II audit (which the tutorial notes requires proving
controls operated correctly over 6-12 months, not just at a single point in time) actually needs.
How a Prowler scan maps to a compliance report#
The underlying checks don't change based on which framework you ask for — --compliance just changes how
the same evidence is grouped and labeled, which is exactly why one scan can answer "are we CIS-compliant"
and "are we SOC 2-ready" simultaneously.
Installation#
# pipx — the officially recommended method (isolated environment, avoids dependency conflicts)
python3 -m pip install --user pipx
python3 -m pipx ensurepath
pipx install prowler
pip install prowler # plain pip, if pipx isn't available
brew install prowler # macOS/Linux Homebrew
# Docker — no local Python environment needed at all
docker run -ti --rm -v "$(pwd)/prowler-output:/home/prowler/output" toniblyx/prowler:latest aws
prowler -vImportant
Never run Prowler using your personal admin credentials. Create a dedicated, least-privilege IAM
identity for scanning first — this is the exact "principle of least privilege" material from earlier in
this series, applied to the tool doing the auditing itself. AWS publishes a managed
SecurityAudit policy that covers everything Prowler's read-only checks need without granting write
access to anything.
Core concepts#
| Concept | What it means |
|---|---|
| Check | One specific, atomic test — e.g. "is S3 bucket X publicly readable" — Prowler ships hundreds, organized by AWS service |
| Compliance framework | A named, versioned mapping of checks onto a real standard's numbered requirements — cis_2.0_aws, soc2_aws, pci_4.0_aws |
| Category | A cross-cutting grouping independent of framework (encryption, logging, internet-exposed) — useful for a targeted review outside a full compliance report |
| Severity | critical/high/medium/low/informational, assigned per check by Prowler's own risk assessment |
| Muted finding | A finding explicitly marked as an accepted/reviewed exception, so it doesn't keep surfacing as new on every re-scan |
Discovering what Prowler can check before running a full scan#
prowler aws --list-checks # every individual check available for the AWS provider
prowler aws --list-services # which AWS services have checks at all
prowler aws --list-categories # cross-cutting categories (encryption, logging, ...)
prowler aws --list-compliance # every compliance framework Prowler can report againstRunning --list-compliance before your first real scan is worth doing specifically because framework
names are versioned and easy to get slightly wrong (cis_2.0_aws vs. cis_1.5_aws) — better to confirm
the exact string than have a scan silently run against the wrong benchmark version.
Running a scan#
prowler aws # full scan, every check, native report
prowler aws --region us-east-1 us-west-2 # scope to specific regions (faster, narrower)
prowler aws --profile prod-readonly # use a specific AWS CLI profile, not the default
prowler aws --role arn:aws:iam::123456789012:role/prowler-scan # assume a dedicated cross-account scanning role
prowler aws --severity critical high # only report findings at these severities
prowler aws --service s3 iam # scope to specific AWS services onlyCompliance-framework scans#
prowler aws --compliance cis_2.0_aws # CIS AWS Foundations Benchmark v2.0
prowler aws --compliance soc2_aws # SOC 2 Trust Service Criteria mapping
prowler aws --compliance pci_4.0_aws # PCI-DSS v4.0 mapping
prowler aws --compliance cis_2.0_aws soc2_aws # multiple frameworks in one runOutput formats and filtering results#
prowler aws --output-formats csv json html # generate multiple report formats in one run
prowler aws --output-directory ./prowler-reports # where reports are written (default: current dir)
prowler aws --status FAIL # only show/report failing checks
prowler aws --compliance soc2_aws --output-formats json html csv --output-directory ./prowler-reportsSample prowler aws table output (representative — real check IDs and finding counts depend entirely on
the account scanned; never treat this as a fixed, literal capture):
AWS-Region Service Status Severity Check
us-east-1 s3 FAIL high S3 bucket "invoicing-exports" allows public read access
us-east-1 iam FAIL critical IAM user "legacy-ci-bot" has an access key older than 90 days
us-east-1 rds PASS medium RDS instance encryption at rest is enabled
Config file format — a custom compliance mapping (advanced)#
Prowler's own compliance frameworks are defined as structured CSV/JSON metadata mapping checks to framework requirements — teams with an internal, non-standard framework can define their own:
# custom_framework_aws.csv (abridged — real files have many more columns per the official schema)
Framework,Provider,Version,Requirement_Id,Requirement_Description,Check_Ids
internal-baseline,aws,1.0,IB-1,"All S3 buckets must block public access",s3_bucket_public_access
internal-baseline,aws,1.0,IB-2,"IAM access keys must rotate within 90 days",iam_rotate_access_key_90_daysprowler aws --compliance internal-baseline_aws --custom-compliance-frameworks ./custom_framework_aws.csvMuting an accepted-risk finding without losing track of it#
A real account almost always has at least a few findings that are deliberate, reviewed exceptions (a public S3 bucket that's genuinely meant to serve static assets, say) — re-triaging the same known finding on every scan wastes review time and trains people to skim past findings instead of reading them. Prowler calls this a mutelist (the "allowlist" terminology was renamed in v5):
# mutelist.yaml
Mutelist:
Accounts:
"123456789012":
Checks:
s3_bucket_public_access:
Regions:
- "us-east-1"
Resources:
- "static-assets-cdn-origin"prowler aws -w mutelist.yaml # local file
prowler aws -w s3://compliance-configs/mutelist.yaml # or a shared, centrally-managed S3 pathA muted finding still shows up in the report as MUTED rather than disappearing entirely — this matters
for an audit trail: the reviewer can see it was a deliberate, documented decision, not that Prowler simply
missed it.
One framework, many providers — where compliance material actually overlaps#
The tutorial's Part 6 compliance-mapping table shows the same underlying technical controls satisfying several frameworks at once — Prowler's own compliance mappings reflect exactly this overlap:
| Technical control Prowler checks | CIS AWS | SOC 2 | PCI-DSS |
|---|---|---|---|
| MFA enabled on the root account | Yes | "Security" criterion | Requirement 8 |
| CloudTrail logging enabled in all regions | Yes | "Security" criterion, Type II evidence | Requirement 10 |
| S3 buckets not publicly readable | Yes | "Confidentiality" criterion | Requirement 3 (protect cardholder data) |
| IAM access keys rotated within 90 days | Yes | "Security" criterion | Requirement 8 |
Running --compliance cis_2.0_aws soc2_aws pci_4.0_aws together in one scan produces three separately
labeled reports from largely the same underlying evidence — a genuinely efficient way to serve multiple
audit requirements from a single scheduled run instead of running the scan three separate times.
Real-world scenario: PCI-DSS scope evidence for an e-commerce checkout#
Directly continuing this series' Part 6 scenario — a checkout system where the architectural decision was to keep raw cardholder data entirely out of the environment via tokenization:
prowler aws --compliance pci_4.0_aws --region us-east-1 \
--output-formats html json --output-directory ./pci-evidence-q3The resulting report is exactly the kind of concrete, timestamped evidence an auditor asks for during a PCI-DSS assessment — proof that network segmentation, encryption, and access-control controls were actually configured correctly on a specific date, not just described in a policy document.
Real-world scenario: SOC 2 Type II continuous evidence#
Since SOC 2 Type II specifically requires proving controls operated correctly over 6-12 months (not a single point-in-time snapshot), a one-off scan is insufficient — Prowler needs to run on a recurring schedule with results retained:
- Create a dedicated, least-privilege scanning IAM role (never personal credentials).
- Schedule
prowler aws --compliance soc2_aws --output-formats jsonto run weekly via CI/CD (or AWS EventBridge + a Lambda/Fargate task). - Retain every run's timestamped JSON output — this retained history is the Type II evidence trail.
- Feed critical/high findings into an alerting channel so drift is caught between scheduled audits, not only discovered at the next formal review.
Warning
Never commit Prowler's raw output anywhere in a shared or public repository. It contains your real
account ID, real resource ARNs, and real configuration details — treat scan output exactly like any
other sensitive security artifact, and add its output directory to .gitignore before running anything
against a real account.
Security Hub integration#
prowler aws --security-hub # send ALL findings to AWS Security Hub, in ASFF format
prowler aws --security-hub --status FAIL # send only failing findings — the far more common choice in practiceCommon pitfalls#
- Scanning with broad admin credentials instead of a dedicated least-privilege role. See the IMPORTANT box above.
- Assuming a "PASS"-heavy report means the account is fully secure. Prowler's checks are broad but not exhaustive — a clean Prowler report is strong evidence, not proof of comprehensive security.
- Forgetting
--region, then wondering why a scan takes far longer than expected on an account with resources in only one or two actual regions — an unscoped scan checks every AWS region by default. - Committing raw scan output to a repo. See the WARNING above — this is a genuinely easy mistake for a team new to the tool, since the output "looks like" any other CI artifact at first glance.
Beyond AWS — Azure, GCP, and Kubernetes#
Prowler's provider model is the same across clouds — the subcommand changes, the flags stay largely consistent:
prowler azure --az-cli-auth --subscription-ids <sub-id> # Azure, authenticating via the Azure CLI's existing session
prowler gcp --credentials-file gcp-sa-key.json # GCP, using a service-account key
prowler kubernetes --kubeconfig-file ~/.kube/config --context prod-cluster # a Kubernetes cluster's own CIS-adjacent checksA team running a genuinely multi-cloud footprint can produce one consistent compliance report format across all three, rather than learning a separate posture-scanning tool per provider — a real practical advantage over provider-native tools like AWS Security Hub's own standards, which only ever cover AWS.
Real-world scenario: monthly scheduled scan with drift alerting#
# .github/workflows/prowler-monthly.yml
name: Prowler monthly compliance scan
on:
schedule:
- cron: "0 6 1 * *" # first of every month
jobs:
scan:
runs-on: ubuntu-latest
steps:
- name: Assume the dedicated scanning role
run: aws sts assume-role --role-arn ${{ secrets.PROWLER_ROLE_ARN }} --role-session-name prowler-ci
- name: Run Prowler
run: pipx run prowler aws --compliance soc2_aws pci_4.0_aws --output-formats json html \
--output-directory ./reports -w mutelist.yaml
- uses: actions/upload-artifact@v4
with:
name: prowler-reports-${{ github.run_id }}
path: ./reportsRetaining every monthly run as a build artifact (rather than only the latest) is what turns a single scan into the kind of longitudinal evidence trail a SOC 2 Type II auditor actually reviews — proof the controls held steady over the audit period, not just that they happened to pass on the one day someone remembered to check.
Exit codes and when to reach for something else#
Prowler exits non-zero when it encounters an execution error (bad credentials, an API throttling failure
it can't recover from); a FAIL finding on an individual check does not by itself change the process
exit code — build pass/fail pipeline gating from parsing the output's status field per finding, the same
pattern this series' other scanners require. For container image or IaC misconfiguration scanning
rather than live cloud-account posture, reach for Trivy, Checkov, or tfsec (all covered in this
series) instead — Prowler's scope is specifically the deployed cloud account's own configuration, not a
build artifact or source file.