What strata Does — A Practical Overview
Audience: DevOps engineer / sysadmin who lives in the terminal. You know Terraform, Ansible, and Helm. You’ve managed the copy-paste
.tfvarssprawl. You want to know what strata actually does before deciding if it’s worth your time.
strata is a CLI tool that sits in front of your IaC toolchain. It generates Terraform, Helm, and Ansible inputs from layered YAML configuration, resolves secrets from external stores, and orchestrates multi-stage deployments. The output is plain Terraform/Helm/Ansible — nothing proprietary, no lock-in.
Configuration as YAML layers, not copy-paste
Instead of duplicating .tfvars per environment, you write it once and override per environment:
workspaces/platform.yaml ← what to build (providers, topology, modules)
environments/env-prd.yaml ← production overrides (image versions, secrets, region)
environments/env-stg.yaml ← staging overrides
deployments/deploy-prd.yaml ← binds workspace + env, declares stages
deployments/deploy-stg.yaml
strata merges the layers at build time and generates the final Terraform/Helm inputs. No manual diffing. No “which tfvars is the real one?”
Cross-repo references
IaC can live in a separate repository. Reference it with @repo_name/path:
provisioners:
- name: platform_iac
provisioner: terraform
source:
repository: xyz-infrastructure # registered with `strata repo add`
source_path: terraform/platform
strata resolves the path to the local clone at build time. Single-repo setups skip the repository
field and use a relative path directly.
Secret resolution — never in git
Declare where a secret lives. strata fetches it at runtime and injects it into the build output.
Supported secret backends:
Store |
Value field contains |
|---|---|
|
Environment variable name on the runner |
|
GitHub Actions secret name (runner-injected) |
|
Secret name in Azure Key Vault |
|
Bitwarden item ID |
|
HashiCorp Vault path |
|
Infisical secret path |
secrets:
- key: DB_PASSWORD
store: azure-keyvault
value: prod-db-password # secret name in Key Vault
- key: API_TOKEN
store: github
value: MY_API_TOKEN # GitHub Actions secret name
Secrets are resolved into memory, written to build output, and never logged. For Ansible SSH keys:
the PEM is written to a chmod 600 temp file for the subprocess window, then deleted — even if the
playbook fails.
Auto-generated secrets
For secrets that don’t yet exist, declare a generator spec. strata creates the secret in the backing store on first run:
secrets:
- key: SESSION_KEY
store: azure-keyvault
value: app-session-key
generate:
type: urlsafe
length: 64
Supported generators: urlsafe, hex, password, alphanumeric, numeric, base64, uuid4,
uuid7. Minimum length: 8. Maximum: 1024.
Variable stores for non-sensitive config
Variables (non-sensitive) support the same pluggable backend pattern:
Store |
Resolves from |
|---|---|
|
Literal value in the YAML file |
|
Environment variable on the runner |
|
Azure App Configuration key |
|
HashiCorp Consul key |
|
HashiCorp Vault path |
|
Infisical secret path |
|
etcd key |
Feature flags use constant, environment, azure-appconfig, or flagsmith.
Build — generate your Terraform/Helm inputs
strata build run --file deploy/deploy-prd.yaml
Generates .strata/build/deploy-prd/<stage>/ containing:
main.tf,variables.tf,terraform.tfvars.json(Terraform stages)values.yaml(Helm stages)Populated
backend.tfpointing to your existing state backend
Your state backend, your Terraform modules, your provider plugins — untouched. The output is plain files that work without strata installed.
Preview changes before you apply
strata build plan --file deploy/deploy-prd.yaml
Runs terraform plan in the generated output directory. Review the diff before committing to
apply. Useful as a PR gate — fail the pipeline on unexpected changes.
# Clean generated artifacts and start fresh
strata build clean --file deploy/deploy-prd.yaml
Deploy — run the provisioner pipeline
strata deploy run --file deploy/deploy-prd.yaml
strata deploy run --file deploy/deploy-prd.yaml --stage infrastructure # single stage
strata deploy run --file deploy/deploy-prd.yaml --force # skip prompts
Orchestrates stages in dependency order. Each stage calls the provisioner via subprocess —
terraform apply, ansible-playbook, helm upgrade, docker stack deploy, or a script.
Supported provisioners
Provisioner |
What it calls |
Typical stage type |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Shell / PowerShell / Python scripts |
any |
|
Renders Jinja2 template → commits to git → ArgoCD reconciles |
|
|
Renders Jinja2 template → commits to git → Flux reconciles |
|
ArgoCD and Flux are GitOps provisioners — strata build run renders a Jinja2 template using
the full deployment context (versions, variables, namespace, tenant) and writes the output to a
git remote. The controller picks up the committed file and reconciles the cluster.
strata deploy health queries the controller API for reconciliation status alongside infrastructure
health checks — no extra flags needed.
Other deploy subcommands
strata deploy show --file deploy/deploy-prd.yaml # inspect resolved configuration
strata env output --file deploy/deploy-prd.yaml # live infrastructure outputs
strata deploy history --file deploy/deploy-prd.yaml # deployment history
strata deploy health --file deploy/deploy-prd.yaml # run health checks
strata deploy output --file deploy/deploy-prd.yaml # print infrastructure outputs (cached)
strata deploy destroy --file deploy/deploy-prd.yaml # tear down (with confirmation)
strata deploy lock --file deploy/deploy-prd.yaml # acquire deployment lock
Version management — pin and lock chart/image versions
strata versions init --file deploy/deploy-prd.yaml # scaffold a starter version manifest
strata versions add --file versions/v1.yaml # create a new snapshot
strata versions refresh --file versions/v1.yaml # sync pins against versionable targets
strata versions apply --file versions/v1.yaml # convert manifest → version-lock file
strata versions lock --file versions/v1.yaml # compute + store a SHA-256 hash of pins
strata versions export --file versions/v1.yaml # print resolved flat pin state
Version manifests (kind: version) declare pinned chart/image versions per ring. Pins flow into
the platform artifact as chart_versions and image_versions convenience fields — templates
and provisioners read them directly, no lookups required.
Promotions — advance versions across rings
# Promote a version manifest to a ring (writes a pointer lock)
strata promote --ring prd --file versions/v2.yaml --promotion canary
# Check promotion status and history
strata promote status --ring prd
strata promote history --ring prd
# Roll back to the previous ring state
strata promote rollback --ring prd
# View the version matrix across all rings
strata promote matrix
Each ring (dev, staging, prod) holds a pointer lock that references the active version manifest.
Promoting advances the pointer — the next strata build run picks up the new versions automatically.
Rollback restores the previous pointer; no manual file edits needed.
Deployment locking
Before applying, strata can acquire a lease on your state backend to prevent concurrent deploys.
Set locking: true in your deployment YAML or pass --lock on the CLI. Backed by the same
mechanism as your Terraform backend (Azure Blob lease, S3 DynamoDB lock, etc.).
strata deploy lock --file deploy/deploy-prd.yaml # acquire lock
strata deploy run --file deploy/deploy-prd.yaml # fails if another lock is held
Lifecycle hooks — run scripts at any phase
Attach shell, PowerShell, or Python scripts to any phase of the pipeline:
# workspace.yaml
lifecycle:
deploy_apply_before:
scripts:
- scripts/backup-state.ps1 # take a snapshot before applying
deploy_run_before:
scripts:
- scripts/notify-slack.sh # post to Slack when provisioning starts
deploy_stage_after:
scripts:
- scripts/export-ips.ps1 # write IPs to a config file after each stage
Scripts receive context via environment variables (STRATA_PHASE, STRATA_BUILD_PATH,
STRATA_WORKSPACE_PATH, etc.). Works cross-platform — strata tries .sh on Linux, .ps1 on
Windows, with fallbacks.
Stage-level hooks (deploy_stage_before, deploy_apply_before, deploy_apply_after,
deploy_stage_after) execute hierarchically: scripts defined in the configuration, then the
workspace, then each namespace, provider, resource, and module — in that order. Put a hook
directly in a namespace or resource YAML and it fires automatically when that level is reached,
with STRATA_NAMESPACE / STRATA_RESOURCE / etc. already set. No top-level orchestration
scripts needed.
Policy guardrails
Declare compliance rules in a configuration.yaml. strata evaluates them at the appropriate
phase — no wrappers, no external policy server.
spec:
policies:
- name: enforce_regions
type: tenant_zone
phase: plan
enforcement: deny # deny | warn | audit
- name: require_tags
type: required_tags
phase: build
enforcement: warn
- name: naming_convention
type: naming_pattern
phase: validate
enforcement: deny
- name: checkov_scan
type: script
phase: plan
enforcement: deny
configuration:
command: checkov --directory .strata/build/deploy-prd/infra
Built-in policy types:
Type |
Checks |
|---|---|
|
Terraform resource regions vs. allowed zones |
|
Required tags present on all resources |
|
|
|
Delegates to any external command (OPA, Checkov, custom) |
|
Container images use pinned (non-floating) tags |
|
Images come only from approved registries |
|
No dependency matches a purl/name blocklist |
|
Total SBOM component count stays within a budget |
|
Dependency licenses match an allow/deny list |
Schema validation on any YAML file
strata validate --file workspaces/platform.yaml
Returns exit code 3 on schema violations, with structured error output:
{
"success": false,
"errors": [
{ "code": "VALIDATION_ERROR", "message": "Field 'spec.provider' is required", "line": 12 }
]
}
Use as a pre-commit hook or PR gate. Fast — no external tools required.
Inspect resolved values before you deploy
Check exactly what strata will inject into the build — variables, secrets, feature flags — without running the full build:
strata values list --file deploy/deploy-prd.yaml
strata values get --file deploy/deploy-prd.yaml --key DB_PASSWORD
Secrets are masked in output unless you pass --reveal. Useful for debugging “why is the wrong
value being used?”
Tool health check
strata tools status # show all external tools and their versions
strata tools status --missing # show only tools not on PATH
strata tools check # exit non-zero if any required tool is absent
strata tools install # attempt to install missing tools
Covers: Terraform, OpenTofu, Ansible, Docker, Helm, Azure CLI, kubectl, Bitwarden CLI, Vault CLI, and more.
Secret utilities
# Generate a cryptographically secure secret value (without storing it anywhere)
strata secret generate --type urlsafe --length 64
strata secret generate --type uuid4
strata secret generate --type password --length 32
# Mask a secret value in a string (replaces it with ***)
strata secret mask "my text containing secretvalue123"
Useful for seeding Key Vault or Bitwarden from a setup script.
CI-friendly by design
JSON output everywhere:
strata build plan --file deploy/deploy-prd.yaml --output json
{ "success": true, "data": { "changes": 3, "additions": 1, "deletions": 2 }, "errors": [] }
Environment variable config:
export STRATA_FILE=deploy/deploy-prd.yaml
export STRATA_OUTPUT=json
export STRATA_WORK_PATH=/workspace
strata build plan # no flags needed
strata deploy run
Exit codes:
Code |
Meaning |
|---|---|
|
Success |
|
Execution failure / crash |
|
Usage error (bad flags) |
|
Validation failure (schema-invalid) |
Shell completions:
strata completion bash >> ~/.bashrc
strata completion zsh >> ~/.zshrc
strata completion fish >> ~/.config/fish/completions/strata.fish
strata completion powershell >> $PROFILE
Dev container out of the box
strata sln init creates a .devcontainer/ in your workspace with Python 3.13, Terraform, Azure
CLI, kubectl, and Helm pre-installed. Open in VS Code → Reopen in Container, or push to GitHub
and open as a Codespace — no local tool setup required.
Workspace templates
Seed a new workspace with a standard structure in one command:
strata sln init --name my-platform --template .strata/templates/three-repo-standard.yaml
Templates declare which repos to clone, which profiles to create, and which files to link. Skip the manual Phase 1–4 setup for standard layouts.
Create config files from templates
strata new workspace
strata new environment
strata new deployment
strata new provider
strata new resource
Scaffolds a valid YAML skeleton for the chosen kind. Faster than looking up the schema.
Inspect JSON schemas
strata schema list # list all supported YAML kinds
strata schema get workspace # print the full JSON schema for kind: workspace
Feed the schema into your editor’s YAML plugin for inline validation and autocompletion.
Persistent CLI defaults
strata config set output json # always use JSON output
strata config set verbose true # always show verbose logs
strata config set file deploy/prd.yaml # default deployment file
strata config list # verify what's set
strata config unset verbose # remove a default
Stored in .strata/cli.yaml — per-workspace, committed to git so the team shares the same
defaults.
Extend strata without touching core code
Drop Python files into your workspace’s .strata/ directory. strata auto-discovers and loads them
at startup — no version upgrade, no pull request to the core repo required.
Custom integrations
Add support for a new secret store, variable backend, or external tool by dropping a .py file
into .strata/integrations/:
.strata/
integrations/
my_vault.py ← loaded automatically at startup
Each file’s register() function inserts a type → class mapping into IntegrationFactory.
Any YAML entry whose type: matches the registered string gets instantiated through the factory
from that point on:
# In your workspace or configuration YAML
integrations:
- type: my_vault
spec:
endpoint: https://vault.example.com
Declare capability protocols to signal what your integration can do — the same interfaces the built-in integrations use:
Protocol |
What it enables |
|---|---|
|
Read secrets by key (plugs into secret resolution) |
|
Read key/value config (plugs into variable resolution) |
|
Read feature flags |
|
Clone / fetch / push (git-style) |
|
Plan / apply (Terraform-style) |
|
Build / run (Docker-style) |
A starter template is scaffolded at .strata/integrations/my_integration.py by strata sln init.
Custom policies
Enforce team-specific compliance rules by dropping a .py file into .strata/policies/:
.strata/
policies/
require_cost_tags.py ← loaded automatically at startup
The file’s register() call adds the type to the policy engine registry. Reference it in any
configuration.yaml the same way as a built-in type:
spec:
policies:
- name: require_cost_tags
type: require_cost_tags # matches your registered TYPE_NAME
phase: plan
enforcement: deny
configuration:
required_tags: [CostCenter, Owner, Environment]
evaluate() receives a PolicyContext containing the current phase, workspace path, resolved
artifact, and (for the plan phase) the raw terraform show -json output. Return a PolicyResult
with pass/fail and structured violations.
A starter template is scaffolded at .strata/policies/my_policy.py by strata sln init.
The escape hatch
If strata stops fitting your workflow:
Run
strata build runone last time.Copy
.strata/build/<deployment>/<stage>/to wherever you keep Terraform code.terraform init && terraform plan— works without strata, no proprietary files.
Your state backend is unchanged. Your modules are unchanged. Your infrastructure is unchanged.
strata never touches .tfstate.