Ansible Vault ID — Multi-Password Vault Management

Introduction

Ansible Vault encrypts sensitive data — passwords, keys, tokens. But real projects need multiple vault passwords: one for dev, one for staging, one for production. Vault IDs solve this by labeling encrypted content with an identifier, so Ansible knows which password to use for each secret.

Basic Vault ID Usage

# Encrypt with a vault ID label
ansible-vault encrypt --vault-id dev@prompt vars/dev-secrets.yml
ansible-vault encrypt --vault-id prod@prompt vars/prod-secrets.yml

# Decrypt/edit with vault ID
ansible-vault edit --vault-id dev@prompt vars/dev-secrets.yml

# Run playbook with multiple vault IDs
ansible-playbook site.yml \
  --vault-id dev@prompt \
  --vault-id prod@prompt

Password Sources

Prompt (Interactive)

ansible-vault encrypt --vault-id myapp@prompt secrets.yml
# Prompts: "Vault password (myapp):"

Password File

# Create password files
echo "dev-password-here" > ~/.vault/dev.pass
echo "prod-password-here" > ~/.vault/prod.pass
chmod 600 ~/.vault/*.pass

# Use password files
ansible-vault encrypt --vault-id dev@~/.vault/dev.pass vars/dev.yml
ansible-vault encrypt --vault-id prod@~/.vault/prod.pass vars/prod.yml

# Run with password files
ansible-playbook site.yml \
  --vault-id dev@~/.vault/dev.pass \
  --vault-id prod@~/.vault/prod.pass

Client Script

#!/bin/bash
# ~/.vault/vault-client.sh — retrieves password from external source
# Receives vault-id label as argument

case "$1" in
  dev)
    # From environment variable
    echo "${VAULT_DEV_PASSWORD}"
    ;;
  prod)
    # From secrets manager
    aws secretsmanager get-secret-value \
      --secret-id ansible/prod-vault \
      --query SecretString --output text
    ;;
  *)
    echo "Unknown vault-id: $1" >&2
    exit 1
    ;;
esac
chmod +x ~/.vault/vault-client.sh

# Use the client script
ansible-playbook site.yml \
  --vault-id dev@~/.vault/vault-client.sh \
  --vault-id prod@~/.vault/vault-client.sh

ansible.cfg Configuration

# ansible.cfg
[defaults]
# Default vault identity list (replaces --vault-id on command line)
vault_identity_list = dev@~/.vault/dev.pass, prod@~/.vault/prod.pass

# Or use a client script
# vault_identity_list = dev@~/.vault/vault-client.sh, prod@~/.vault/vault-client.sh

Per-Environment Secrets Structure

project/
├── ansible.cfg
├── playbooks/
│   └── deploy.yml
├── group_vars/
│   ├── all/
│   │   └── vars.yml          # Unencrypted shared vars
│   ├── dev/
│   │   ├── vars.yml          # Unencrypted dev vars
│   │   └── vault.yml         # Encrypted with vault-id "dev"
│   ├── staging/
│   │   ├── vars.yml
│   │   └── vault.yml         # Encrypted with vault-id "staging"
│   └── production/
│       ├── vars.yml
│       └── vault.yml         # Encrypted with vault-id "prod"
└── inventories/
    ├── dev.yml
    ├── staging.yml
    └── production.yml
# Encrypt each environment's secrets with its own vault ID
ansible-vault encrypt --vault-id dev@~/.vault/dev.pass group_vars/dev/vault.yml
ansible-vault encrypt --vault-id staging@~/.vault/staging.pass group_vars/staging/vault.yml
ansible-vault encrypt --vault-id prod@~/.vault/prod.pass group_vars/production/vault.yml

Inline Encrypted Variables

# Encrypt a single string with a vault ID
ansible-vault encrypt_string --vault-id prod@~/.vault/prod.pass \
  'super-secret-password' --name 'db_password'

Output (paste into vars file):

db_password: !vault |
  $ANSIBLE_VAULT;1.2;AES256;prod
  61626364656667...

The prod label after AES256; tells Ansible which vault ID decrypts it.

Mixing Encrypted and Unencrypted

# group_vars/production/vault.yml (encrypted)
vault_db_password: "encrypted-value"
vault_api_key: "encrypted-value"
vault_ssl_key: "encrypted-value"

# group_vars/production/vars.yml (plain text, references vault vars)
db_host: db.example.com
db_port: 5432
db_password: "{{ vault_db_password }}"
api_key: "{{ vault_api_key }}"

Rekeying

# Change the password for a vault ID
ansible-vault rekey --vault-id prod@~/.vault/prod.pass \
  --new-vault-id prod@~/.vault/prod-new.pass \
  group_vars/production/vault.yml

# Rekey multiple files
ansible-vault rekey --vault-id prod@~/.vault/prod.pass \
  --new-vault-id prod@prompt \
  group_vars/production/*.yml

CI/CD Integration

# .github/workflows/deploy.yml
name: Deploy
on:
  push:
    branches: [main]
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Create vault password file
        run: echo "${{ secrets.VAULT_PROD_PASSWORD }}" > .vault-pass
        
      - name: Run playbook
        run: |
          ansible-playbook deploy.yml \
            --vault-id prod@.vault-pass \
            -i inventories/production.yml
        
      - name: Clean up
        if: always()
        run: rm -f .vault-pass

Troubleshooting

IssueSolution
"Decryption failed"Wrong vault ID or password for the file
"No vault secrets found"Missing --vault-id or vault_identity_list
Can't decrypt inline varVault ID label in $ANSIBLE_VAULT header must match
Password file not foundCheck path; use absolute path in ansible.cfg
Multiple prompts annoyingUse password files or vault client script

Best Practices

  1. One vault ID per environment — dev, staging, prod with different passwords
  2. Never commit password files — add *.pass to .gitignore
  3. Use vault_ prefix — name encrypted vars vault_db_password, reference as db_password
  4. Use client scripts — pull passwords from AWS Secrets Manager, HashiCorp Vault, etc.
  5. Rekey regularly — change vault passwords when team members leave
  6. Limit prod access — only CI/CD and ops team have prod vault password

Conclusion

Vault IDs bring proper secret management to Ansible. Instead of one password for everything, each environment gets its own encryption key. Combined with client scripts that pull passwords from external secret managers, you get enterprise-grade secret management without leaving the Ansible ecosystem.