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 deployThe 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 dashboardIn 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
| Option | Default | Values |
|---|---|---|
frameworks | Required | Friendly names or Prowler IDs — see below |
schedule | 'daily' | 'daily', 'weekly', 'none', or { cron, timezone } |
retention | '1y' | '30d', '90d', '180d', '1y', '2y', '7y' |
scanOnDeploy | false | Start 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,
ciscan 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 name | Framework |
|---|---|
soc2 | soc2_aws |
iso27001 | iso27001_2022_aws |
cis | cis_7.0_aws |
nist-800-53 | nist_800_53_revision_5_aws |
nist-800-171 | nist_800_171_revision_2_aws |
nist-csf | nist_csf_2.0_aws |
pci | pci_4.0_aws |
hipaa | hipaa_aws |
fsbp | aws_foundational_security_best_practices_aws |
gdpr | gdpr_aws |
nis2 | nis2_aws |
dora | dora_2022_2554 |
fedramp-low | fedramp_low_revision_4_aws |
fedramp-moderate | fedramp_moderate_revision_4_aws |
cmmc | cmmc_2.0 |
essential-eight | asd_essential_eight_aws |
well-architected | aws_well_architected_framework_security_pillar_aws |
c5 | c5_aws |
csa-ccm | csa_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.
| Resource | What it's for |
|---|---|
| CodeBuild project | Runs Prowler. No VPC, no source code; Prowler's official image, pinned to an exact version |
| Scan role | Read-only access for Prowler (see Security) |
| Results bucket | Stores scan results. Encrypted, versioned, private, TLS-only |
| Schedule group + invoke role | Holds each app's schedule; the role can only start scans |
| Log group | Scan 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
| Command | Does |
|---|---|
anvil compliance dashboard | Opens the local dashboard |
anvil compliance scan | Scans your app now, using your App's settings. --wait follows it and prints a summary; --frameworks, --regions and --retention override your settings |
anvil compliance status | Shows the scanner, which apps have schedules, and your app's latest scan |
anvil compliance setup | Creates or updates the shared scanner without deploying (--yes skips the prompt) |
anvil compliance teardown | Removes 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
| Item | Cost |
|---|---|
| The scanner while idle | Nothing |
| Each scan | A minute or two of CodeBuild — under a cent |
| A daily schedule | Roughly 10–30 cents a month per app |
| Storing results | About 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-ccmand 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.