G
GuideDevOps
Lesson 11 of 14

Ansible Vault

Part of the Ansible tutorial series.

The Problem with Clear-text Secrets

A core principle of modern Operations is that everything lives in Git. However, you often need sensitive data to perform automation:

  • Database administrator passwords (to create schemas)
  • API Keys (to talk to Datadog or AWS)
  • Private SSH Keys
  • SSL/TLS Certificates

If you commit these secrets to a Git repository in plain text, you create a massive security vulnerability. Anyone with read access to the repository—or anyone who compromises it in the future—can extract those production passwords.

The solution is encryption at rest: Ansible Vault.


What is Ansible Vault?

Ansible Vault allows you to securely encrypt strings, files, or entire Variable dictionaries using AES-256 encryption.

The workflow operates like this:

  1. You use the ansible-vault CLI command to encrypt a file. You provide a master password.
  2. The file becomes an unreadable block of ciphertext.
  3. You commit this ciphertext file safely to Git.
  4. When you execute an ansible-playbook command, you supply the master password.
  5. Ansible dynamically decrypts the contents in memory (never writing the unencrypted secret back to the hard drive), uses the variable to complete the task, and drops it from memory.

Using ansible-vault

1. Creating a new encrypted file

To create a new encrypted file from scratch:

ansible-vault create vars/db_secrets.yml

You will be prompted to essentially invent a new master password. Ensure you remember it (or better, store it in your team's password manager, like 1Password or BitWarden). After confirming the password, it will dump you into a vi editor.

Type your YAML secrets:

# Inside the editor
db_root_password: "SuperSecretPassword123!"
datadog_api_key: "abc123def456"

Save and exit. If you immediately run cat vars/db_secrets.yml, you will NOT see your variables. You will see:

$ANSIBLE_VAULT;1.1;AES256
38363765383566373733363339366635393539346638363462613565346332613337373733366366
...

You can now safely git commit this file!

2. Editing an encrypted file

To modify variables in the future, do not decrypt and re-encrypt the file manually. Just use the edit command:

ansible-vault edit vars/db_secrets.yml
# Prompts for Vault password, opens editor, saves ciphertext automatically.

3. Encrypting an existing plain-text file

If you already have a YAML file with secrets, encrypt it in place:

ansible-vault encrypt vars/my_existing_file.yml

Executing Playbooks with Vaults

When your playbook references an encrypted vars_files document, it has no native way to decrypt it. Trying to run the playbook normally will trigger a hard crash: “ERROR! Attempting to decrypt but no vault secrets found”

You must provide the master password at runtime.

Method A: Interactive Prompt

The easiest method for local execution is to tell Ansible to intentionally pause and ask you to type the password.

ansible-playbook site.yml --ask-vault-pass

Method B: Password File (For CI/CD)

CI/CD pipelines (like Jenkins or GitHub Actions) cannot pause to wait for a human to interactively type a password. You must configure them to read the password from a designated file on the build server.

# This file contains the master password in plain text.
# Do NOT commit the vault_pass.txt file to Git! 
echo "my_master_vault_password" > ~/.vault_pass.txt
 
# Run the playbook utilizing the password file
ansible-playbook site.yml --vault-password-file ~/.vault_pass.txt

In a professional CI/CD pipeline, the string my_master_vault_password is injected directly by the pipeline's native Secret Management tool (like GitHub Secrets).


Encrypting Individual Strings (Advanced)

Encrypting entire files causes minor annoyances: You can't see the Git diff when a peer modifies a variable, because the whole file is encrypted garbage!

Ansible Vault allows you to encrypt individual strings while leaving the rest of the YAML file in plain text. This is highly recommended for readability.

  1. Generate the encrypted string:
ansible-vault encrypt_string 'SuperSecretPassword123!' --name 'db_root_password'
  1. The output will look like a massive block of YAML. Copy the entire output.
  2. Paste it directly into your regular, unencrypted vars/main.yml file:
# vars/main.yml (Plain text file)
database_port: 5432
database_user: postgres
 
# The encrypted string variable (Safe to commit to Git)
db_root_password: !vault |
          $ANSIBLE_VAULT;1.1;AES256
          36373866383664653531393630653036666138616131343734616238646633653166663233376536
          3537386164393366363237666236303233383030306161340a646430336236616630353664323265
          64636662326162336338373738306362306263623838323638356133616630303862356534353235
          3565353564636239630a663965616663643764663734326561633532353361666338396531393036
          35366535616164316130323061386131666530666531633333333465313531383035

Now, anyone can read the file and see that database_port is 5432, but they cannot read the database password. When Ansible runs, it will attempt to decrypt only the variables tagged with !vault.