Skip to main content

standstill

CI / Release Python License: MPL 2.0 Coverage

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

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

Mozilla Public License 2.0

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)

Source distribution for standstill 0.2.0
File Size Uploaded
standstill-0.2.0.tar.gz 190.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for standstill 0.2.0
File Interpreter ABI Platform
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 log

Release 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

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page