!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:
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.
Backend selected by a YAML function
store: and sops: can be a YAML function instead of a literal name, for example to read the backend
name from a CloudFormation stack's Outputs:
Selectors are resolved lazily, one declaration at a time, only when a command uses that declaration.
!secret API_KEY and atmos secret get API_KEY resolve the selector for API_KEY and nothing else, so
a selector that cannot be resolved (for example, its producer component is not deployed yet) fails only
that declaration. The error names the secret, component, stack and selector, and suggests deploying the
producer. Declarations with a literal backend, such as DB_PASSWORD above, keep working, including
atmos secret delete.
Three things differ from literal backends:
atmos secret listis credential-free and never evaluates selectors. It shows the declaration with statusunresolvedand a reason until you pass--verify.atmos secret validateresolvessops:selectors to check for SOPS file collisions across the stack. If asops:selector cannot be resolved, validation fails instead of skipping it, because the file it would use is unknown.atmos describe affectedmarks components affected when a SOPS file they use changes, and it normally runs without credentials, so it does not evaluate selectors. Asops:selector is never skipped: the component is treated as affected by any change under the directories its SOPS providers can use (spec.path, defaultsecrets, or the static directory prefix ofspec.file), and a warning names the component and the declaration. Use a literalsops:name when you want the exact file to decide.
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.