Skip to main content
RunBook Academy

AnsibleXXI · Secrets ManagementSecrets management

What Vault does not protect

Advanced⏱ ~22 minansible-core

What you'll learn

  • State precisely which hop of a secret journey Vault covers and which it does not
  • Predict what a vault-encrypted variable does once it is decrypted into a run
  • Identify what variable-level encryption does not cover that file-level does
  • Choose the control that matches the hop you are protecting, rather than reaching for Vault again

Prerequisites

Verified against ansible-core 2.21.x · ansible (community package) 14.x · Python (controller) 3.12+ · ansible-lint 26.x · Molecule 26.x · Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL / Rocky / AlmaLinux 9.x · 2026-08-11

Not yet marked complete on this device.

Ansible Vault does one thing, and it does it well:

It encrypts data at rest in your repository.

That is the whole scope. Everything people believe it also does, it does not do, and the belief is what turns a good tool into a false sense of completion.

This lesson is the boundary, stated plainly and measured where it can be measured.

A vault-encrypted variable is an ordinary string

The clearest demonstration is the simplest one. The vars file below has db_password as an inline !vault value — encrypted at rest, unreadable in a pull request. Here is what happens once the play runs:

Read-only / Safeencrypted in the file; plaintext in the run
$ ansible-playbook -i localhost, vaultleak.yml --vault-id prod@.prod-pass
TASK [The value is vault-encrypted in the repository] *************
ok: [localhost] => {
  "db_password": "REPLACE_ME"
}

TASK [And it templates into anything] *****************************
ok: [localhost] => {
  "msg": "postgresql://billing_ro:REPLACE_ME@db1.example.com/app"
}

The encryption did not follow the value into the run, and it was never supposed to. Once decrypted, the variable is a string like any other. It prints, it templates, it concatenates, it goes into a file, it goes over the wire, it appears in a return value.

That is not a flaw. A secret you cannot use is not a secret you have. The entire point of decrypting it is to use it. But it means every control that applies to strings — no_log, output discipline, log permissions, who can read a ticket — is a separate control that Vault does not provide.

The journey, annotated

Take the diagram from the first lesson of this part and mark it honestly:

HopProtected by Vault?What protects it instead
At rest in the repositoryyesthis is the whole job
In a Git object from an earlier plaintext commitnorotation; history rewriting
Decrypted in controller memorynocontroller access control
In a debug or a registered resultnono_log, and not printing results whole
In --diff outputnono_log on the task, or not diffing that task
In log_path or a callback lognofile permissions, directory permissions, umask
As a module argument on the managed nodenopipelining removes the file; nothing removes the argument
In a temporary file on the targetnopipelining
In the target process table while the task runsnousing a module rather than shell
In the config file the task wrotenofile mode, ownership, the target’s own controls
In the target service’s own logsnothat service’s configuration
In a backup: true copy on the targetnonot using backup for credential files
In CI job output or artifactsnoCI configuration and retention policy
Against someone who has the vault passwordnovault ID scoping; where the password lives

One row is yes. That is the accurate picture, and it is worth putting in front of anyone who says “it is fine, it is in Vault”.

What variable-level encryption specifically does not cover

encrypt_string encrypts values. Not keys, not structure, not tasks, not anything else in the file.

Read-only / Safewhat a reader of this file learns without the password
# Everything here except the value is public information.
db_host: db1.example.com          # your database hostname
db_user: billing_ro               # the account name
db_port: 5432                     # the port
api_endpoint: https://api-internal.example.com/v2
db_password: !vault |
        $ANSIBLE_VAULT;1.2;AES256;prod
        33656432323139626338306532383365366262333432313935643735336539393735303161336162

A reader without the password learns your internal hostnames, your account names, your ports, your API endpoints, the fact that there is a prod vault label, and that a password exists for that account. That is reconnaissance, delivered neatly.

Whether that matters depends on where the repository is. For an internal repository it is usually fine and the reviewability is worth more. For anything public, or anything a wide contractor population can read, the structure itself is worth protecting and file-level encryption is the better choice.

The related limits, which follow from the same fact:

rekey does not touch inline values. It operates on a vault file payload. A file of encrypt_string values is ordinary YAML with encrypted scalars in it, so a password rotation must re-encrypt each value individually — covered with its consequences in the encrypt_string lesson.

Tasks are not encryptable. You can encrypt a variable a task uses. You cannot encrypt the task, the play, or the fact that this playbook configures a payments database. If the existence of something is sensitive, Vault is the wrong layer entirely.

Knowledge check

Knowledge check · 4 questions

  1. Q1. A variable is stored as an inline !vault value. A task does debug with that variable. What is printed?

  2. Q2. Which statement about Vault and the person holding the vault password is accurate?

  3. Q3. Which are true of variable-level encryption specifically, as opposed to file-level? Select all that apply.

  4. Q4. Because Ansible Vault uses strong symmetric encryption, adding a contributor who may write secrets but must not read existing ones is a matter of configuring their vault ID correctly.

Passing score: 75%. Answers are checked in this browser.