# Prowler Cheat Sheet

> **Tool:** Prowler
> **Category:** Security & Compliance
> **Verified against:** Official docs — docs.prowler.com/getting-started/installation/prowler-cli,
> the project README at github.com/prowler-cloud/prowler, docs.prowler.com's reporting guide, and
> the current `aws_mutelist.yaml` schema in the project's GitHub repo — 2026-09-05 (not installable in
> this sandboxed environment; verified against current official docs/source rather than a local `--help` run)
> **Official docs:** https://docs.prowler.com/

## 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](/tutorials/devsecops/06-compliance-and-cheatsheet): 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

```mermaid
flowchart TD
    Scan["prowler aws"] --> Checks["Hundreds of read-only<br/>API checks against the<br/>REAL account"]
    Checks --> Findings["Per-check PASS/FAIL,<br/>with the exact resource<br/>and region"]
    Findings --> Native["Native report:<br/>every check, by AWS service"]
    Findings --> Compliance["--compliance flag:<br/>the SAME findings,<br/>re-grouped by framework"]
    Compliance --> CIS["CIS AWS Foundations"]
    Compliance --> SOC2["SOC 2"]
    Compliance --> PCI["PCI-DSS"]

    classDef info fill:#e5f0fa,stroke:#1d6fb8,color:#10161c
    classDef accent fill:#f0e9fb,stroke:#6d43c0,color:#10161c
    class Native info
    class CIS,SOC2,PCI accent
```

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

```bash
# 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 -v
```

> [!IMPORTANT]
> **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

```bash
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 against
```

Running `--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

```bash
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 only
```

## Compliance-framework scans

```bash
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 run
```

## Output formats and filtering results

```bash
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-reports
```

Sample `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:

```csv
# 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_days
```

```bash
prowler aws --compliance internal-baseline_aws --custom-compliance-frameworks ./custom_framework_aws.csv
```

## Muting 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):

```yaml
# mutelist.yaml
Mutelist:
  Accounts:
    "123456789012":
      Checks:
        s3_bucket_public_access:
          Regions:
            - "us-east-1"
          Resources:
            - "static-assets-cdn-origin"
```

```bash
prowler aws -w mutelist.yaml                              # local file
prowler aws -w s3://compliance-configs/mutelist.yaml       # or a shared, centrally-managed S3 path
```

A 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:

```bash
prowler aws --compliance pci_4.0_aws --region us-east-1 \
  --output-formats html json --output-directory ./pci-evidence-q3
```

The 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 json` to 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

```bash
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 practice
```

## Common 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:

```bash
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 checks
```

A 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

```yaml
# .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: ./reports
```

Retaining 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.
