Compliance scanning

Scheduled compliance scans of your deployed app, powered by Prowler.

Anvil's components are built to line up with frameworks like SOC 2 and ISO 27001. Compliance scanning checks that against what's actually running: add compliance to your App, and Anvil scans your app's deployed resources on a schedule using Prowler, the open-source cloud security scanner. Results show up in a local dashboard, grouped by the component that created each resource.

Prowler isn't installed on your machine. It runs in AWS CodeBuild inside your own account, and the scanner can only read configuration — never your data.

Turn it on

Add compliance to your App:

import { App } from '@anvil-cloud/sdk';

export default new App({
  providers: {
    aws: { region: 'ap-southeast-2' },
  },
  compliance: {
    frameworks: ['soc2', 'iso27001', 'cis'], // required
    schedule: 'daily',                       // default
    retention: '1y',                         // default
    scanOnDeploy: true,                      // default false
  },
  run(ctx) {
    // ...your components
  },
});
import anvil_cloud as anvil

def infra(ctx: anvil.Context):
    ...  # your components

anvil.App(
    run=infra,
    aws_providers={"aws": anvil.AwsProviderConfig(region="ap-southeast-2")},
    compliance=anvil.ComplianceConfig(
        frameworks=["soc2", "iso27001", "cis"],  # required
        schedule="daily",                        # default
        retention="1y",                          # default
        scan_on_deploy=True,                     # default False
    ),
)
anvil.Run(anvil.AppConfig{
    AwsProviders: map[string]*anvil.AwsProviderConfig{
        "aws": {Region: "ap-southeast-2"},
    },
    Compliance: &anvil.ComplianceConfig{
        Frameworks:   []string{"soc2", "iso27001", "cis"}, // required
        Schedule:     "daily",                             // default
        Retention:    "1y",                                // default
        ScanOnDeploy: true,                                // default false
    },
    Run: func(ctx *anvil.Context) error {
        // ...your components
        return nil
    },
})

Then deploy:

anvil deploy

The first time any app in an AWS account and region turns on compliance, Anvil asks before creating the scanner those apps will share:

  No compliance scanner exists yet in account 123456789012 (ap-southeast-2).
  Anvil will create shared resources, used by every Anvil app in this account and region:

    • CodeBuild project + read-only scan role   anvil-compliance-123456789012-ap-southeast-2
    • Results bucket (encrypted, versioned)     anvil-compliance-123456789012-ap-southeast-2
    • Scheduler group + invoke role             anvil-compliance
    • Build log group (30-day retention)        /aws/codebuild/anvil-compliance-...

? Create them [y/N]

After that, your app is scanned on its schedule — and after every deploy if scanOnDeploy is on. To see the results:

anvil compliance dashboard

In CI, pass --yes to anvil deploy to create the shared scanner without a prompt. Without a terminal and without --yes, the deploy stops instead of creating account-wide resources silently.

Options

OptionDefaultValues
frameworksRequiredFriendly names or Prowler IDs — see below
schedule'daily''daily', 'weekly', 'none', or { cron, timezone }
retention'1y''30d', '90d', '180d', '1y', '2y', '7y'
scanOnDeployfalseStart a scan after each successful deploy

Frameworks

There's no default — you choose what to scan against. frameworks takes two forms, and you can mix them:

  • Friendly names are short and always point at the newest version Anvil supports. When Anvil upgrades, cis can move from CIS 7.0 to a later CIS automatically.
  • Prowler IDs are any AWS compliance framework Prowler supports. They never change, so use them to stay on an older version (cis_6.0_aws) or for frameworks without a friendly name (ens_rd2022_aws, ffiec_aws, mitre_attack_aws, …).
compliance: { frameworks: ['soc2', 'cis', 'ens_rd2022_aws', 'cis_6.0_aws'] }
compliance=anvil.ComplianceConfig(frameworks=["soc2", "cis", "ens_rd2022_aws", "cis_6.0_aws"])
Compliance: &anvil.ComplianceConfig{Frameworks: []string{"soc2", "cis", "ens_rd2022_aws", "cis_6.0_aws"}}

A typo fails the deploy straight away with a suggestion (unknown compliance framework "soc2_aw" — did you mean "soc2_aws"?), rather than failing later inside a scan.

Friendly nameFramework
soc2soc2_aws
iso27001iso27001_2022_aws
ciscis_7.0_aws
nist-800-53nist_800_53_revision_5_aws
nist-800-171nist_800_171_revision_2_aws
nist-csfnist_csf_2.0_aws
pcipci_4.0_aws
hipaahipaa_aws
fsbpaws_foundational_security_best_practices_aws
gdprgdpr_aws
nis2nis2_aws
doradora_2022_2554
fedramp-lowfedramp_low_revision_4_aws
fedramp-moderatefedramp_moderate_revision_4_aws
cmmccmmc_2.0
essential-eightasd_essential_eight_aws
well-architectedaws_well_architected_framework_security_pillar_aws
c5c5_aws
csa-ccmcsa_ccm_4.0

Adding frameworks costs very little. Prowler runs checks, and each framework maps its requirements onto those checks — so several frameworks share most of their checks, and a scan runs each check once.

Schedules

  • 'daily' and 'weekly' run at a fixed time picked from your project and stage names. Apps in the same account don't all scan at midnight, and your app's time stays the same across deploys.
  • 'none' means scans only run after deploys (scanOnDeploy) or when you ask.
  • A custom schedule takes six AWS cron fields (minutes, hours, day of month, month, day of week, year) and an optional time zone, which defaults to UTC. It can run at most once an hour.
compliance: {
  frameworks: ['soc2'],
  schedule: { cron: '0 3 * * ? *', timezone: 'Australia/Sydney' }, // 3am Sydney, daily
},
compliance=anvil.ComplianceConfig(
    frameworks=["soc2"],
    schedule=anvil.ComplianceCron(cron="0 3 * * ? *", timezone="Australia/Sydney"),
),
Compliance: &anvil.ComplianceConfig{
    Frameworks: []string{"soc2"},
    Cron:       "0 3 * * ? *",
    Timezone:   "Australia/Sydney",
},

What gets scanned

Exactly the resources your app created — everything tagged with Anvil's ManagedBy, project and stage tags, which every Anvil component sets for you. That includes your stage's state bucket from anvil bootstrap. Scans cover your app's regions plus us-east-1, where CloudFront, CloudFront WAFs and Lambda@Edge live.

  • Several apps and stages in one account are scanned separately and never see each other's resources.
  • Resources you create directly with Pulumi (not through an Anvil component) aren't scanned.
  • Account-wide checks — like whether CloudTrail or GuardDuty are on — aren't part of an app's results, because they aren't about any one app.

What Anvil creates in your account

One shared scanner per AWS account and region, used by every Anvil app there. It's created the first time an app turns compliance on, and updated automatically when you upgrade Anvil.

ResourceWhat it's for
CodeBuild projectRuns Prowler. No VPC, no source code; Prowler's official image, pinned to an exact version
Scan roleRead-only access for Prowler (see Security)
Results bucketStores scan results. Encrypted, versioned, private, TLS-only
Schedule group + invoke roleHolds each app's schedule; the role can only start scans
Log groupScan logs, kept 30 days

Each app adds one thing to its own stack: an EventBridge schedule that starts its scans. It's created, changed and deleted with your app like any other resource.

Results and retention

Each scan's results are kept for your retention period and then deleted automatically. Changing retention only affects new scans — shortening it never deletes results you already have, so past audit evidence isn't lost by accident.

If you need results for audits, '1y' (the default) covers a typical SOC 2 Type II review period.

Security

The scanner is built so it can't change anything or read your data:

  • Read-only. The scan role can read resource configuration and write its own results — nothing else.
  • No data access. The role is explicitly denied reading S3 objects, logs, Lambda code, container images, secrets, parameters, database items and queue messages. An explicit deny always wins, so this holds even if AWS widens its managed policies.
  • Locked-down roles. The scan role can only be used by your account's scanner project, and the schedule's role can only start scans.
  • A pinned image. Prowler runs from its official image, pinned to an exact version that only changes when you upgrade Anvil.

Because the scanner can't read data, Prowler's "secrets in code, logs or images" checks don't run. None of them are part of SOC 2, ISO 27001 or CIS.

Commands

CommandDoes
anvil compliance dashboardOpens the local dashboard
anvil compliance scanScans your app now, using your App's settings. --wait follows it and prints a summary; --frameworks, --regions and --retention override your settings
anvil compliance statusShows the scanner, which apps have schedules, and your app's latest scan
anvil compliance setupCreates or updates the shared scanner without deploying (--yes skips the prompt)
anvil compliance teardownRemoves the shared scanner. Refuses while any app still has compliance on; keeps results unless you pass --delete-results

All commands take --stage.

Turning it off

Remove compliance from your App and deploy — the app's schedule is deleted, and past results stay until their retention ends. Once no app in the account and region uses it, anvil compliance teardown removes the shared scanner.

Costs

ItemCost
The scanner while idleNothing
Each scanA minute or two of CodeBuild — under a cent
A daily scheduleRoughly 10–30 cents a month per app
Storing resultsAbout 100 KB per scan — cents per year

Good to know

  • A passing score isn't certification. Prowler checks what it can automate against your resources; many requirements need evidence from you as well.
  • dora, cmmc, csa-ccm and the FedRAMP 20x frameworks run their checks, but the dashboard can't score them per requirement yet.
  • Brand-new resources can take a few minutes to become visible to the scanner, so a scan straight after your very first deploy might miss some.
  • If your account also uses CodeBuild for CI, scans share the account's CodeBuild concurrency. New accounts can have a low limit; scans then run one at a time.