Red Hat Certified Engineer (RHCE) #10 Ansible Vault: Managing Secrets
In #9 Tags and Conditionals we ran tasks selectively and controlled flow with conditions and loops. This time it’s about keeping the secret values a playbook handles safe. If you leave values like database passwords, API tokens, or initial user passwords in cleartext in an inventory or variable file, they get exposed as-is in your git repository or the exam grading environment. To prevent this, Ansible provides an encryption mechanism called Ansible Vault.
Vault encrypts an entire variable file, or a single variable, with AES256 and decrypts it with a password only at the moment the playbook runs. On the RHCE exam, “encrypt a secret variable with vault and use it in a playbook” and “place a password file so the playbook runs non-interactively” are perennial tasks, so let’s get the ansible-vault subcommands and runtime options into our hands.
The problem Vault solves #
A playbook’s behavior branches on its variables. Among those variables are values that must not be shown to anyone.
- Database connection passwords
- API keys and tokens for external services
- Initial passwords for user accounts
- Credential material like TLS private keys
If you write such values in cleartext in group_vars/db/vars.yml, anyone who reads the file learns the secret. Vault keeps the file encrypted on disk and decrypts it only with the vault password supplied at playbook runtime, so even if you commit it to git the contents stay hidden.
An encrypted file begins on its first line with $ANSIBLE_VAULT;1.1;AES256, followed by a blob of hexadecimal below it. When Ansible loads a variable file, it sees this header and automatically attempts decryption.
ansible-vault subcommands #
ansible-vault handles encrypted files through the following subcommands. These six are everything you use on the exam.
| Command | What it does |
|---|---|
ansible-vault create <file> | Creates a new encrypted file and opens it in the editor |
ansible-vault edit <file> | Decrypts an encrypted file and opens it in the editor, re-encrypting on save |
ansible-vault view <file> | Decrypts an encrypted file and shows its contents on screen |
ansible-vault encrypt <file> | Encrypts an existing cleartext file |
ansible-vault decrypt <file> | Reverts an encrypted file back to cleartext |
ansible-vault rekey <file> | Changes the vault password to a new one |
create: making a new encrypted file #
To build an encrypted variable file from scratch, use create. After you enter the password twice, the editor specified by $EDITOR opens.
ansible-vault create group_vars/db/secret.yml
# New Vault password:
# Confirm New Vault password:Inside the editor you write ordinary YAML.
# group_vars/db/secret.yml (the cleartext you see in the editor)
db_root_password: "S3cr3t!root"
db_app_password: "App#2026pw"Once you save and exit, it’s written to disk in encrypted form.
cat group_vars/db/secret.yml
# $ANSIBLE_VAULT;1.1;AES256
# 39613266313764336239...view and edit: viewing and modifying #
Since an encrypted file shows only hexadecimal under cat, you check its contents with view.
ansible-vault view group_vars/db/secret.yml
# Vault password:
# db_root_password: "S3cr3t!root"
# db_app_password: "App#2026pw"To change a value, use edit. It opens the decrypted temporary contents in the editor and re-encrypts on save.
ansible-vault edit group_vars/db/secret.yml
# Vault password:encrypt and decrypt: converting an existing file #
If you already have a variable file written in cleartext, encrypt it in one shot with encrypt.
ansible-vault encrypt group_vars/web/secret.yml
# Vault password:
# Encryption successfulConversely, to undo the encryption and revert to cleartext, use decrypt.
ansible-vault decrypt group_vars/web/secret.yml
# Vault password:
# Decryption successfulrekey: changing the password #
To swap the vault password, use rekey: enter the current password, then set a new one.
ansible-vault rekey group_vars/db/secret.yml
# Vault password:
# New Vault password:
# Confirm New Vault password:
# Rekey successfulEncrypting a variable file and using it #
The standard pattern in both practice and the exam is to split the group variables into two. Values that are fine to expose go in vars.yml, and secret values go in vault.yml.
group_vars/
db/
vars.yml # cleartext: db_user, db_port, etc.
vault.yml # encrypted: vault_db_password, etc.Inside the encrypted file, a common convention is to prefix variable names with vault_.
# group_vars/db/vault.yml (cleartext as seen via ansible-vault edit)
vault_db_password: "S3cr3t!root"In the cleartext file you wire that vault variable to a regular name. This way the playbook references only an ordinary variable name, while the actual secret lives only in the encrypted file.
# group_vars/db/vars.yml
db_user: appuser
db_port: 3306
db_password: "{{ vault_db_password }}"In the playbook you use it just like a cleartext variable.
# site.yml
- name: Configure database
hosts: db
tasks:
- name: Set DB root password
ansible.builtin.debug:
msg: "connecting with {{ db_user }} / {{ db_password }}"You must supply the vault password at runtime for the variable to be decrypted.
ansible-playbook site.yml --ask-vault-pass
# Vault password:Supplying the password at runtime #
A playbook that uses encrypted variables won’t run without the vault password. There are two ways to pass it.
–ask-vault-pass: interactive entry #
You enter the password directly when running. This suits a one-off run with a person watching.
ansible-playbook site.yml --ask-vault-passYou use the same option when running with ansible-navigator.
ansible-navigator run site.yml -m stdout --ask-vault-pass–vault-password-file: automating with a password file #
Write the password on a single line in a file and pass that file’s path, and decryption happens automatically with no input. This is the approach for cron, unattended runs, and the exam requirement to “make it run non-interactively.”
echo 'mypassword' > ~/.vault_pass
chmod 600 ~/.vault_pass
ansible-playbook site.yml --vault-password-file ~/.vault_passRegister it in ansible.cfg and that file is always used, even without the option.
# ansible.cfg
[defaults]
vault_password_file = ~/.vault_passThe ansible-vault subcommands take the same option too, so with a password file in place, view and edit also work non-interactively.
ansible-vault view group_vars/db/vault.yml --vault-password-file ~/.vault_passencrypt_string: inline-encrypting a single variable #
When you want to encrypt just one value rather than an entire variable file, use encrypt_string. You can paste the resulting ciphertext block directly into a cleartext YAML file.
ansible-vault encrypt_string 'S3cr3t!root' --name 'db_password'
# Vault password:
# db_password: !vault |
# $ANSIBLE_VAULT;1.1;AES256
# 39613266313764336239...Paste this output into a cleartext vars.yml and the file itself is cleartext, but that single variable alone is encrypted.
# group_vars/db/vars.yml
db_user: appuser
db_password: !vault |
$ANSIBLE_VAULT;1.1;AES256
39613266313764336239...Have it read the password from standard input and you can use it in a pipeline.
ansible-vault encrypt_string --vault-password-file ~/.vault_pass \
'App#2026pw' --name 'db_app_password'A value tagged with !vault is decrypted automatically when Ansible evaluates the variable, so in the playbook you reference it plainly as {{ db_password }}.
vault id: multiple passwords #
When you use files encrypted with different vault passwords together in one playbook, you distinguish them by attaching labels with a vault id. You pass several at once in the --vault-id label@source form.
ansible-playbook site.yml \
--vault-id dev@~/.vault_dev --vault-id prod@promptSpecify the same label when encrypting, and on decryption Ansible matches which password to use by the label.
ansible-vault encrypt --vault-id prod@~/.vault_prod group_vars/db/vault.ymlExam points #
- Password file permissions. The file used with
--vault-password-fileholds the cleartext password, so lock it down withchmod 600so only you can read it. Loose permissions leave the secret-exposure risk intact. - Non-interactive runs with –vault-password-file. When the exam says “make it run without entering a password,” create a password file and either register it in
ansible.cfg’svault_password_fileor pass it as an option.--ask-vault-passis interactive and doesn’t fit an automation requirement. - Separating what gets encrypted. The standard structure is secrets in
vault.yml, cleartext invars.yml, with the cleartext referencing thevault_variables. If a secret is left in cleartext, that’s a deduction during grading. - A single value uses encrypt_string. When the requirement is not to encrypt the whole file, or to hide just one variable, paste the
encrypt_stringoutput inline into a cleartext file. - A play with vault variables fails without the password. Check the contents first with
ansible-vault view, and verify that supplying the password isn’t missing from your run options.
Wrap-up #
What this post locked in:
- Ansible Vault encrypts a variable file or a single variable with AES256 and keeps it safely on disk.
- Subcommands.
create,edit,view,encrypt,decrypt, andrekeyhandle encrypted files. - The standard pattern. Put secrets in
group_vars/<group>/vault.ymland reference thevault_variables from a cleartextvars.yml. - Supplying the password at runtime.
--ask-vault-passis interactive,--vault-password-fileis for non-interactive automation and can also be registered inansible.cfg. - Inline-encrypt a single value with
encrypt_string, and distinguish multiple passwords by label with--vault-id.
Next — writing and using roles #
We’ve got the safe handling of secrets down. Now we move on to roles, which bundle playbooks into reusable units.
In #11 Writing and using roles, we’ll build our own: a role’s directory structure (tasks/handlers/templates/vars/defaults), how to scaffold the skeleton with ansible-galaxy init, how to call a role from a playbook, and the common exam type of “write a role to the given requirements and apply it.”