Skip to main content

!secret

The !secret YAML function resolves a declared secret from its configured backend (a secret: true store such as AWS SSM, AWS Secrets Manager, HashiCorp Vault, Azure Key Vault, or GCP Secret Manager, or a SOPS-encrypted file) and substitutes its value into an Atmos stack manifest. Resolved values are automatically registered with the I/O masker, so they are redacted in all output.

Usage​

!secret <NAME> [| path <yq-expression> | raw] [| default <default-value>]

The secret <NAME> must be declared under the component's secrets.vars before it can be referenced — this keeps the registry of secrets GitOps-friendly and explicit.

Usage examples:

# Resolve a declared secret
vars:
api_key: !secret DATADOG_API_KEY

# Provide a default when the secret is missing
vars:
api_key: !secret DATADOG_API_KEY | default "dev-key"

# Extract a nested value from a structured (JSON) secret
vars:
db_host: !secret DATABASE_CONFIG | path ".host"
db_password: !secret DATABASE_CONFIG | path ".credentials.password"

# Preserve the exact backend payload when valid JSON must remain an opaque string
vars:
service_credentials: !secret SERVICE_CREDENTIALS | raw

# Combine path and default
vars:
db_host: !secret DATABASE_CONFIG | path ".host" | default "localhost"

Bare !secret preserves the store's existing value contract, including structured maps and lists. Use | raw when the original textual payload is required byte-for-byte. The raw and path modifiers are mutually exclusive.

Some store backends detect structured values by attempting to decode the payload as JSON. Use | raw whenever a secret that must remain a string might be valid JSON—including objects, arrays, bare numbers, booleans, null, and JSON-quoted strings. This prevents a future secret rotation from silently changing the resolved value's type.

atmos secret set writes string values verbatim, so new values round-trip through | raw. Secrets written by older Atmos releases may contain JSON quote characters in the stored payload; set them again with the current release before switching those references to | raw.

Declaration​

A secret must be declared before use. Declarations live in stack/component config and reference a backend by name:

stacks/prod/api.yaml
components:
terraform:
api:
secrets:
vars:
DATADOG_API_KEY:
description: "Datadog API key for monitoring"
store: app-secrets # a `secret: true` store (track 1)
required: true
GITHUB_APP_KEY:
sops: dev-sops # a SOPS provider (track 2)
vars:
datadog_api_key: !secret DATADOG_API_KEY

The backend (store: or sops:) carries the provider, region, prefix, and optional auth identity, so the declaration stays terse. See Secrets configuration for backend setup.

Masking and Inspection​

!secret is automatically masked everywhere its value appears. In inspection commands — atmos describe and the atmos list family — !secret resolves to <MASKED> without contacting the backend when masking is enabled (the default). This means you can inspect a stack's shape with no cloud credentials:

# No credentials needed — the secret renders as <MASKED>
atmos describe component api --stack=prod

# Reveal the real value (requires access to the secret backend)
atmos describe component api --stack=prod --mask=false

In value-producing commands (terraform plan/apply, atmos secret get), the value is always retrieved because it is needed to function; --mask only controls whether it is redacted in display output.

Access Rule​

!secret is the only accessor for a secret: true store. Using !store against a secret: true store is an error — this makes declarations mandatory-by-construction for secrets and removes any ambiguity about whether a value is sensitive.

See Also​