standstill
standstill is a CLI designed to manage AWS security at scale.
What problem it solves
AWS provides the building blocks for a strong organizational security posture: over 1,200 Control Tower controls spanning preventive, detective, and proactive behaviors, plus GuardDuty, Security Hub, Macie, Inspector, and Access Analyzer for threat detection and compliance posture. The challenge is not knowing what exists — it is deploying and managing all of it systematically across dozens or hundreds of accounts without a dedicated team.
The AWS console does not scale. Raw SDK scripts do not compose. Terraform modules exist but are not aware of the Control Tower operational model, its async behavior, or the relationship between OU baselines, control enrollment, and the pending operations journal.
standstill is built around that operational model. It treats the desired security state as something that can be declared, planned, diffed, and applied — the same way infrastructure engineers think about Terraform — but specifically for the security controls and services that AWS organizations are built on.
Why standstill instead of the Control Tower console?
The console is fine for a handful of clicks. It does not scale to enrolling hundreds of controls across dozens of OUs, or configuring five security services org-wide across every account and region. standstill is a power-user operations layer on top of Control Tower — it does not replace the console, it makes the repetitive, at-scale work sane and repeatable (the same way Terraform relates to the AWS console).
| Task | standstill CLI | Control Tower console | Winner |
|---|---|---|---|
| Enroll controls across many OUs / behaviors | apply --enable-detective --ou … --dry-run |
Click each control, each OU, one at a time | CLI, decisively |
| Plan / diff before changing | --dry-run shows the plan |
No preview at all | CLI |
| Declarative, version-controlled, reviewable state | YAML in git, apply --file |
Clicks, no artifact | CLI |
| Configure 5 security services org-wide | one security apply (two-phase, cross-account) |
Bounce between GuardDuty / Security Hub / Macie / Inspector / Access Analyzer consoles, per region | CLI |
| Config recorders across N accounts | recorder setup --all fan-out |
Per-account, per-region clicks | CLI |
| Auditing / scripting / CI integration | --output json → pipe |
Not possible | CLI |
| First-time setup / guided landing zone | Manual flags | Guided wizard, validation, visual | Console |
| Provisioning new accounts | Account Factory automation | Account Factory works | Console |
| Visual security posture, findings drill-down, compliance scores | Static tables | Rich dashboards, resource-level links | Console |
| Discoverability for a new user | Must know IDs / ARNs | Point-and-click | Console |
The rule of thumb: for bulk, repeatable, auditable control and security-service management at org scale, the CLI wins. For guided first-run setup and visual posture and findings investigation, stay in the console.
Account maturity model
Mature AWS organizations separate account infrastructure into layers with distinct ownership boundaries. standstill is designed around this model: each layer has a dedicated tool and a dedicated team, and lower layers are treated as immutable by the layers above them.
| Layer | Name | What it contains | Managed by | Owned by |
|---|---|---|---|---|
| 0 | Organization & security controls | CT controls (SCPs, detective, proactive), GuardDuty, Security Hub, Macie, Inspector, Access Analyzer, Config recorders | standstill | Security / platform team |
| 1 | Account foundation | VPC, subnets, route tables, Transit Gateway attachment, DNS resolver rules, default security groups, break-glass IAM roles | standstill blueprints | DevOps / platform team |
| 2 | Application infrastructure | Compute (ECS/EKS/Lambda), databases, storage, application-specific resources | Terraform / CloudFormation / CDK | Dev / DevOps teams |
Layer 0 and Layer 1 are set once per account and treated as immutable by Layer 2. Application
teams reference foundation resources via data sources or SSM Parameter Store — they never own or
modify them. A terraform plan will see the VPC already exists and has no opinion about it.
CloudFormation is used for Layer 1 rather than Terraform precisely because it creates a hard
governance boundary: foundation resources do not exist in any application state file, cannot be
drifted by a terraform apply, and can be protected with stack termination protection and a
deny-delete SCP. GuardDuty, CloudTrail, and Config belong to Layer 0 — they are org-wide services
managed centrally by standstill, not per-account Terraform resources. Application infrastructure
has to live with both layers, which is the intended design.
Layer 1 is applied via blueprints — YAML files that describe one or more CloudFormation stacks to deploy into a new account at creation time:
standstill blueprint apply --file blueprints/networking.yaml --account 123456789012
standstill accounts create --name "ClientA" --email a@client.com --ou ou-xxx --blueprint blueprints/networking.yaml
The security layers standstill manages
Preventive controls — Service Control Policies
SCPs attached to OUs that block non-compliant API calls before they happen. No IAM policy in a member account can override an SCP. Once in place, the control is ambient: it requires no agent, no scheduled evaluation, and no alerting pipeline. It simply denies the action.
Detective controls — AWS Config rules
Managed Config rules deployed across the organization that continuously evaluate resource configuration. When a resource drifts out of compliance — a security group opens an unrestricted port, an S3 bucket loses its block-public-access setting, a root access key gets created — the rule flags it. Detective controls are the primary mechanism for catching configuration drift that happened before preventive controls were enrolled or that slipped through other gaps.
Proactive controls — CloudFormation hooks
Hooks that intercept CloudFormation stack deployments before non-compliant resources are created. They operate at the infrastructure-as-code layer, blocking stacks that would provision resources violating the defined security policies before anything is provisioned in AWS.
Security services — threat detection and posture
- GuardDuty — Analyzes CloudTrail, VPC Flow Logs, DNS logs, and runtime environments for active threats: unauthorized access, crypto mining, credential exfiltration, lateral movement. Deploys org-wide via a delegated administrator account with configurable protection plans (S3, RDS, EKS, ECS, EC2 malware scanning, Lambda network logs).
- Security Hub — Aggregates findings from GuardDuty, Config, Macie, and Inspector into a unified compliance posture. Standards include AWS Foundational Security Best Practices (FSBP), CIS Benchmarks (v1.4 and v3.0), PCI-DSS, and NIST 800-53. Supports cross-region aggregation.
- Macie — Discovers and classifies sensitive data in S3: PII, credentials, financial records. Includes automated discovery with configurable sampling depth and managed identifier sets.
- Inspector — Continuous vulnerability scanning for EC2 instances, container images in ECR, and Lambda functions. Surfaces CVEs, network reachability issues, and software package vulnerabilities.
- Access Analyzer — Identifies IAM roles, S3 buckets, KMS keys, SQS queues, and other resources with resource-based policies that grant access to external principals. Supports organization-level analyzers and unused access analysis.
Core capabilities
Declarative control management
Controls are declared in a YAML file that maps OUs to the list of controls that should be active on them. standstill diffs the desired state against what is currently enrolled in Control Tower, skips already-enabled controls, validates that target OUs have an active CT baseline, and applies only the delta.
targets:
- ou_id: ou-ab12-34cd5678
controls:
- arn:aws:controltower:us-east-1::control/AWS-GR_RESTRICT_ROOT_USER
- arn:aws:controltower:us-east-1::control/AWS-GR_ENCRYPTED_VOLUMES
- arn:aws:controltower:us-east-1::control/AWS-GR_CLOUDTRAIL_ENABLED
- ou_id: ou-cd34-56ef7890
controls:
- arn:aws:controltower:us-east-1::control/AWS-GR_ENCRYPTED_VOLUMES
- arn:aws:controltower:us-east-1::control/AWS-GR_S3_BUCKET_PUBLIC_READ_PROHIBITED
standstill apply --file controls.yaml --dry-run # plan
standstill apply --file controls.yaml # apply
The same file format works for disabling controls:
standstill disable --file controls.yaml --dry-run
standstill disable --file controls.yaml
Bulk enablement and disable
For bootstrapping a new OU or landing zone, entire control tiers can be enrolled in a single command:
standstill apply --enable-detective --ou ou-ab12-34cd5678
standstill apply --enable-preventive --ou ou-ab12-34cd5678
standstill apply --enable-proactive --ou ou-ab12-34cd5678
standstill apply --enable-all --ou ou-ab12-34cd5678
Disabling by tier works symmetrically:
standstill disable --disable-detective --ou ou-ab12-34cd5678
standstill disable --disable-preventive --ou ou-ab12-34cd5678
standstill disable --disable-all --ou ou-ab12-34cd5678
Interactive control selection
When you want to enable or disable a subset of controls without writing a YAML file, --category launches an interactive picker. It prompts for a primary filter dimension (behavior, AWS service, or common control) and then an optional severity filter:
standstill apply --category --ou ou-ab12-34cd5678
standstill disable --category --ou ou-ab12-34cd5678
The picker only shows behaviors and severities that exist in the loaded catalog, so every selection produces at least one control.
Parallel apply
Control Tower operations are asynchronous. standstill submits all enable operations concurrently in a configurable thread pool, then polls all of them simultaneously. The total wall-clock time is bounded by the slowest single operation rather than the sum — which makes the difference between hours and minutes when enrolling the full catalog.
standstill apply --enable-detective --ou ou-ab12-34cd5678 --concurrency 20
Pending operations journal
If AWS credentials expire during a long-running apply, standstill catches the expiry, writes all in-flight operations to a local journal (~/.standstill/pending_operations.yaml), and exits cleanly. The journal can be checked in a subsequent session:
standstill operations list
standstill operations check # poll status; does not modify the journal
standstill operations check --clear # poll status and remove completed entries
standstill operations clear # remove all entries immediately
Security services configuration
An interactive wizard generates a YAML configuration file covering all five security services with cost annotations at each step. The generated file can be version-controlled and applied idempotently:
standstill security init # interactive wizard → generates YAML
standstill security apply --file security_services.yaml # deploy org-wide
standstill security status # current state
standstill security assess # member account health
If you have security services already deployed and want to bring them under standstill management, security pull snapshots the live configuration into a local YAML file:
standstill security pull --account 123456789012
standstill security apply --file security_services.yaml --dry-run
Regions. GuardDuty, Security Hub, Macie, and Inspector are regional services — delegation and configuration apply only to the region they run in. security apply targets the current region (--region/AWS_DEFAULT_REGION) by default; pass --regions to configure several in one run. Access Analyzer's organization analyzer and delegated-admin registration are region-scoped too, so run every region you operate in:
standstill security apply -f security_services.yaml --regions us-east-1,eu-west-1,ap-southeast-2 -y
security status and security assess report a single region at a time; re-run them with --region to inspect each.
Config recorder management
Detective controls depend on AWS Config recorders being active in every account. AWS Config is also the most common source of unexpected cost in a Control Tower deployment — the default allSupported mode records every resource type AWS supports, including high-volume types like CloudFormation stacks, ENIs, and SSM compliance items, that generate millions of configuration items per month without adding meaningful security coverage.
standstill sidesteps this by configuring recorders in INCLUSION_BY_RESOURCE_TYPES mode, recording only the specific types that Security Hub standards and the enrolled detective controls actually evaluate. The bundled list is tuned to exclude high-churn types. It can be inspected and customized before any recorder is touched:
standstill recorder types list # show the active inclusion list
standstill recorder types add TYPE # add a resource type
standstill recorder types remove TYPE # remove a resource type
standstill recorder types reset # revert to bundled Security Hub defaults
Once the inclusion list reflects what you need, auditing and configuring recorders across the organization is a two-step operation:
standstill recorder status --all
standstill recorder setup --all
Organization visibility
standstill view ous # OU hierarchy as a tree
standstill view accounts # all accounts with OU and status
standstill view controls # enabled controls per OU with status breakdown
standstill accounts check-roles # verify CT execution role reachability across all accounts
Audit log
Every invocation is appended as one JSON line to ~/.standstill/audit.log (override with STANDSTILL_AUDIT_LOG), recording the timestamp, the command and its arguments, the active profile and region, and the exit code. The log is written centrally at the CLI entry point, so it captures every mutation without per-command wiring — and writing is best-effort, so an audit failure never breaks a command.
{"ts": "2026-08-31T18:22:04.512Z", "args": ["scp", "detach", "-n", "DenyDeleteLogging", "-t", "ou-ab12-34cd5678"], "exit_code": 0, "profile": "org-management", "region": "us-east-1", "pid": 40122}
Prerequisites
- An AWS organization with a Control Tower landing zone already deployed
- Credentials for the management account (or a role that can assume into it)
- The caller needs at least the following IAM permissions:
controltower:ListEnabledControls
controltower:EnableControl
controltower:DisableControl
controltower:GetControlOperation
controltower:ListControlOperations
organizations:ListRoots
organizations:ListOrganizationalUnitsForParent
organizations:ListAccountsForParent
organizations:ListAccounts
organizations:DescribeOrganization
sts:GetCallerIdentity
Additional permissions are required for security services commands (guardduty:*, securityhub:*, macie2:*, inspector2:*, accessanalyzer:*) scoped to the delegated admin account.
Run standstill check after installation to verify connectivity and permissions before doing anything else.
Quick start
# 1. Install
git clone https://github.com/dbnz-io/standstill
cd standstill && pip install -e .
# 2. Configure your management account profile (stored in ~/.standstill/config.yaml)
standstill config set-profile my-mgmt-profile
standstill config set-delegated-admin 123456789012 # your security tooling account
# 3. Verify connectivity
standstill check
# 4. Explore your org
standstill view ous
standstill view accounts
# 5. Verify execution roles are reachable in all accounts
standstill accounts check-roles
# 6. Ensure Config recorders are running everywhere (required for detective controls)
standstill recorder status --all
standstill recorder setup --all
# 7. Dry-run before applying anything
standstill apply --file examples/controls.yaml --dry-run
# 8. Apply
standstill apply --file examples/controls.yaml
Installation
git clone https://github.com/dbnz-io/standstill
cd standstill
pip install -e .
Requires Python 3.11+.
Command reference
standstill [--profile PROFILE] [--region REGION] [--output table|json|csv] COMMAND
check Verify AWS connectivity and CT permissions
view ous Render the OU hierarchy as a tree
view accounts List all accounts with OU membership and status
view controls [--ou OU] Show enabled controls per OU
apply --file FILE Apply controls declared in a YAML file
apply --enable-all --ou Enable every control in the catalog
apply --enable-preventive --ou Enable all Preventive controls
apply --enable-detective --ou Enable all Detective controls
apply --enable-proactive --ou Enable all Proactive controls
apply --category --ou Interactively select controls to enable
--dry-run Preview changes without applying
--yes / -y Skip confirmation prompt
--concurrency N Parallel submissions (default: 10)
--no-wait Submit and return immediately
disable --file FILE Disable controls declared in a YAML file
disable --disable-all --ou Disable every enabled control on the OU
disable --disable-preventive --ou Disable all enabled Preventive controls
disable --disable-detective --ou Disable all enabled Detective controls
disable --disable-proactive --ou Disable all enabled Proactive controls
disable --category --ou Interactively select controls to disable
--dry-run Preview changes without applying
--yes / -y Skip confirmation prompt
--concurrency N Parallel submissions (default: 10)
--no-wait Submit and return immediately
catalog info Show catalog metadata
catalog build Refresh catalog from the live CT API
operations list Show pending CT operations
operations check [--clear] Poll live status; --clear removes completed entries
operations clear Remove all entries from the journal
security init [--output FILE] Interactive config wizard
security pull [--account ID] Snapshot live config to a YAML file
security apply --file FILE [--dry-run] Deploy security services org-wide
--yes / -y Skip confirmation prompt
security status [--account ID | --file F] Current state of all security services
security assess [--account ID | --file F] Member account health across all services
recorder status --all | --account ID Show recorder state
recorder setup --all | --account ID Configure and start recorders
recorder types list List recorded resource types
recorder types add TYPE Add a resource type
recorder types remove TYPE Remove a resource type
recorder types reset Revert to bundled Security Hub defaults
accounts check-roles [--role-name NAME] Verify CT execution role in every account
accounts list List all accounts with OU membership and status
accounts describe --account ID Show account details and parent OU
accounts create --name N --email E --ou OU Create a new account via CT Account Factory
--blueprint FILE Apply a blueprint after the account is ready
--no-wait Submit and return immediately (skips blueprint)
accounts enroll --account ID --ou OU Enroll an existing account into Control Tower
--blueprint FILE Apply a blueprint after enrollment completes
--no-wait Submit and return immediately (skips blueprint)
accounts deregister --account ID Deregister an account from Control Tower
accounts move --account ID --ou OU Move an account to a different OU
ou create --parent ID --name NAME Create a new OU
ou delete --ou OU Delete an empty OU
ou rename --ou OU --name NAME Rename an OU
ou describe --ou OU Show OU details, child OUs, and accounts
blueprint list List blueprints in ~/.standstill/blueprints/
blueprint validate --file FILE Validate a blueprint YAML without deploying
blueprint apply --file FILE Apply a blueprint to accounts
--account ACCOUNT_ID Target a single account
--ou OU_ID Target all active accounts in an OU
--dry-run Preview stacks without deploying
--param KEY=VALUE Override a parameter (repeatable)
--role-name NAME IAM role to assume (default: AWSControlTowerExecution)
--yes / -y Skip confirmation prompt
lz status Show landing zone status, version, and drift state
lz reset Remediate landing zone drift
lz update Upgrade the landing zone to the latest version
lz settings Show landing zone service settings
lz settings-set Update landing zone service settings
config set-profile PROFILE Set the default AWS profile
config unset-profile Remove the default AWS profile
config set-delegated-admin ACCOUNT_ID Set the default delegated security admin account
config unset-delegated-admin Remove the default delegated security admin account
config show Show current CLI configuration
Recommended hardening sequence
standstill check
standstill view ous
standstill accounts check-roles
standstill recorder status --all && standstill recorder setup --all
standstill apply --file examples/preventive_controls.yaml --dry-run
standstill apply --file examples/preventive_controls.yaml
standstill security init && standstill security apply --file security_services.yaml
standstill view controls && standstill security status
Docker
Building locally
docker build -t standstill .
Running with AWS credentials
Pass credentials via environment variables or mount your ~/.aws directory:
# Environment variables
docker run --rm \
-e AWS_ACCESS_KEY_ID \
-e AWS_SECRET_ACCESS_KEY \
-e AWS_SESSION_TOKEN \
-e AWS_DEFAULT_REGION=us-east-1 \
standstill check
# Mounted credentials file
docker run --rm \
-v "$HOME/.aws:/root/.aws:ro" \
-e AWS_PROFILE=my-mgmt-profile \
-e AWS_DEFAULT_REGION=us-east-1 \
standstill check
Development
pip install -e ".[dev]"
pytest
pytest --cov=standstill --cov-report=term-missing --cov-fail-under=80
ruff check .
Tests mock all AWS calls — no real AWS account required. See CONTRIBUTING.md for contribution guidelines.
License
Release files for standstill 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| standstill-0.2.0.tar.gz | 190.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| standstill-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 341.7 kB
Release files / standstill-0.2.0.tar.gz
| Download URL | standstill-0.2.0.tar.gz |
|---|---|
| Size | 190.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
82928f1537410c0ce5f45681d1c368fd8857b28a291c5908e31a7131f7e3e00e
|
|
BLAKE2b-256 checksum How to use checksums |
b30c9205369d00de32340f7b567bb1a8a6e9288bfc4bd25eb6e9def163c7d807
|
| 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 26, 2026.
Transparency logRelease files / standstill-0.2.0-py3-none-any.whl
| Download URL | standstill-0.2.0-py3-none-any.whl |
|---|---|
| Size | 150.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e5f04dac2facf39bc05219be11823bcd90eef9d104d947bb71b93000096f94ba
|
|
BLAKE2b-256 checksum How to use checksums |
e9f2ca1bc4e568fff852786b19f1a5e28864dc2fb97327fc0e0886fcd67bcad6
|
| 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 26, 2026.
Transparency log