Security

Ansible Vault: Secrets Management Best Practices

Teach me Ansible | 2025-02-10 | 24 min read

Learn how to secure sensitive data in your Ansible automation with Ansible Vault. This comprehensive guide covers encryption strategies, best practices, and real-world patterns for protecting passwords, API keys, and certificates.

What is Ansible Vault?

Ansible Vault is a built-in feature that encrypts sensitive data at rest. Instead of storing passwords and API keys in plain text, Vault lets you encrypt entire files or individual variables, keeping your secrets secure even when stored in version control.

Why Use Ansible Vault?

  • Security - Protect secrets from unauthorized access
  • Version Control - Safely commit encrypted files to Git
  • Compliance - Meet security and regulatory requirements
  • No Additional Tools - Built into Ansible core
  • Easy Integration - Works seamlessly with playbooks

Getting Started with Vault

Creating an Encrypted File

# Create and encrypt a new file
ansible-vault create secrets.yml

# You'll be prompted for a password
New Vault password:
Confirm New Vault password: 

Add your secrets:

---
db_password: SuperSecret123!
api_key: sk-1234567890abcdef
aws_access_key: AKIAIOSFODNN7EXAMPLE
aws_secret_key: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
ssl_private_key: |
  -----BEGIN PRIVATE KEY-----
  MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC...
  -----END PRIVATE KEY-----

Encrypting Existing Files

# Encrypt an existing file
ansible-vault encrypt group_vars/production/vault.yml

# Encrypt multiple files
ansible-vault encrypt secrets/*.yml

Viewing Encrypted Files

# View encrypted file content
ansible-vault view secrets.yml

# Edit encrypted file
ansible-vault edit secrets.yml

# Decrypt to stdout (dangerous!)
ansible-vault decrypt secrets.yml --output=-

Decrypting Files

# Decrypt in place (not recommended for version control)
ansible-vault decrypt secrets.yml

# Decrypt to different file
ansible-vault decrypt secrets.yml --output=secrets-plain.yml

Managing Vault Passwords

Using Password Files

# Create password file
echo "MyVaultPassword123" > .vault_pass

# Secure the file
chmod 600 .vault_pass

# Add to .gitignore
echo ".vault_pass" >> .gitignore

# Use with ansible-vault
ansible-vault create secrets.yml --vault-password-file .vault_pass

# Use with playbooks
ansible-playbook site.yml --vault-password-file .vault_pass

Configure in ansible.cfg

[defaults]
vault_password_file = ./.vault_pass

Using Environment Variables

# Set password in environment
export ANSIBLE_VAULT_PASSWORD_FILE=~/.vault_pass

# Or use password directly (less secure)
export ANSIBLE_VAULT_PASSWORD="MyPassword123"

# Run playbook (password file is used automatically)
ansible-playbook site.yml

Using Password Scripts

Create a script that retrieves passwords (useful for secret managers):

#!/bin/bash
# vault-pass.sh
# Retrieve password from HashiCorp Vault, AWS Secrets Manager, etc.

# Example: From AWS Secrets Manager
aws secretsmanager get-secret-value \
    --secret-id ansible-vault-password \
    --query SecretString \
    --output text

# Example: From environment
echo "${ANSIBLE_VAULT_PASSWORD}"

# Example: From macOS Keychain
security find-generic-password -w -s ansible-vault
# Make executable
chmod +x vault-pass.sh

# Use script
ansible-playbook site.yml --vault-password-file ./vault-pass.sh

Using Vault in Playbooks

Method 1: Encrypted Variable Files

Directory structure:

group_vars/
├── production/
│   ├── vars.yml        # Plain variables
│   └── vault.yml       # Encrypted secrets

group_vars/production/vars.yml (unencrypted):

---
db_host: db.production.example.com
db_port: 5432
db_name: myapp
db_user: "{{ vault_db_user }}"
db_password: "{{ vault_db_password }}"

group_vars/production/vault.yml (encrypted):

---
vault_db_user: admin
vault_db_password: SuperSecret123!
vault_api_key: sk-1234567890

Encrypt the vault file:

ansible-vault encrypt group_vars/production/vault.yml

Method 2: Inline Encrypted Variables

# Encrypt a string value
ansible-vault encrypt_string 'SuperSecret123!' --name 'db_password'

Output to include in your vars file:

db_password: !vault |
          $ANSIBLE_VAULT;1.1;AES256
          66386439653231336662626134653634376532633233306437343336303436653433643066663533
          6362313362623330316264343232346461366365666631380a313435636139393330376632383164
          38663733343138653764613564366236346137346437373534373765636136376338623531383163
          3165333162613330650a316263323837383736643939343662633461623062643365363336383635
          6339

Running Playbooks with Vault

# Prompt for password
ansible-playbook site.yml --ask-vault-pass

# Use password file
ansible-playbook site.yml --vault-password-file .vault_pass

# Use multiple vault passwords (Ansible 2.4+)
ansible-playbook site.yml \
  --vault-id prod@.vault_pass_prod \
  --vault-id dev@.vault_pass_dev

Multi-Vault IDs (Advanced)

Use different passwords for different environments:

Create Multiple Vault Files

# Create production secrets
ansible-vault create group_vars/production/vault.yml \
  --vault-id prod@prompt

# Create development secrets
ansible-vault create group_vars/development/vault.yml \
  --vault-id dev@prompt

Label Encrypted Strings

# Encrypt with vault ID
ansible-vault encrypt_string \
  --vault-id prod@.vault_pass_prod \
  'ProductionSecret' \
  --name 'api_key'

Run with Multiple IDs

ansible-playbook site.yml \
  --vault-id dev@.vault_pass_dev \
  --vault-id prod@.vault_pass_prod

Best Practices

1. Never Commit Unencrypted Secrets

# Add to .gitignore
cat >> .gitignore << EOF
.vault_pass
*_vault_pass
vault-pass.sh
secrets-plain.yml
EOF

2. Use Vault Variable Naming Convention

# Prefix vault variables with "vault_"
# vars.yml (unencrypted)
database_password: "{{ vault_database_password }}"
api_token: "{{ vault_api_token }}"

# vault.yml (encrypted)
vault_database_password: "secret123"
vault_api_token: "abc-xyz-789"

3. Separate Encrypted and Unencrypted Variables

group_vars/
└── production/
    ├── vars.yml      # Non-sensitive, unencrypted
    └── vault.yml     # Sensitive, encrypted

4. Rotate Vault Passwords Regularly

# Rekey vault with new password
ansible-vault rekey secrets.yml

# Rekey with password files
ansible-vault rekey secrets.yml \
  --vault-password-file .old_vault_pass \
  --new-vault-password-file .new_vault_pass

5. Use ansible-vault in CI/CD

GitHub Actions:

name: Deploy
on: [push]
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.x'

      - name: Install Ansible
        run: pip install ansible

      - name: Run playbook
        env:
          ANSIBLE_VAULT_PASSWORD: ${{ secrets.VAULT_PASSWORD }}
        run: |
          echo "$ANSIBLE_VAULT_PASSWORD" > .vault_pass
          ansible-playbook deploy.yml --vault-password-file .vault_pass
          rm .vault_pass

GitLab CI:

deploy:
  stage: deploy
  script:
    - pip install ansible
    - echo "$VAULT_PASSWORD" > .vault_pass
    - ansible-playbook deploy.yml --vault-password-file .vault_pass
    - shred -u .vault_pass  # Secure deletion
  variables:
    VAULT_PASSWORD: $VAULT_PASSWORD  # Set in GitLab CI/CD variables

Integration with Secret Managers

HashiCorp Vault Integration

#!/bin/bash
# vault-pass-hashicorp.sh
export VAULT_ADDR="https://vault.example.com"
export VAULT_TOKEN="s.abcdef1234567890"

vault kv get -field=password secret/ansible/vault

AWS Secrets Manager

#!/bin/bash
# vault-pass-aws.sh
aws secretsmanager get-secret-value \
    --secret-id ansible-vault-password \
    --query SecretString \
    --output text

Azure Key Vault

#!/bin/bash
# vault-pass-azure.sh
az keyvault secret show \
    --name ansible-vault-password \
    --vault-name my-key-vault \
    --query value \
    --output tsv

Real-World Examples

Example 1: Secure Database Deployment

group_vars/production/vars.yml:

---
postgresql_version: "14"
postgresql_host: "db.prod.example.com"
postgresql_port: 5432
postgresql_database: "myapp"

# Reference encrypted variables
postgresql_admin_user: "{{ vault_postgresql_admin_user }}"
postgresql_admin_password: "{{ vault_postgresql_admin_password }}"
postgresql_app_user: "{{ vault_postgresql_app_user }}"
postgresql_app_password: "{{ vault_postgresql_app_password }}"

group_vars/production/vault.yml (encrypted):

---
vault_postgresql_admin_user: postgres
vault_postgresql_admin_password: "P@ssw0rd123!Admin"
vault_postgresql_app_user: myapp
vault_postgresql_app_password: "MyApp$ecret456"

Playbook usage:

---
- name: Setup PostgreSQL
  hosts: databases
  become: yes
  tasks:
    - name: Create database
      postgresql_db:
        name: "{{ postgresql_database }}"
        login_host: "{{ postgresql_host }}"
        login_user: "{{ postgresql_admin_user }}"
        login_password: "{{ postgresql_admin_password }}"

    - name: Create application user
      postgresql_user:
        name: "{{ postgresql_app_user }}"
        password: "{{ postgresql_app_password }}"
        db: "{{ postgresql_database }}"
        priv: "ALL"
        login_host: "{{ postgresql_host }}"
        login_user: "{{ postgresql_admin_user }}"
        login_password: "{{ postgresql_admin_password }}"

Example 2: Cloud API Credentials

vars/cloud-secrets.yml (encrypted):

---
# AWS Credentials
aws_access_key_id: AKIAIOSFODNN7EXAMPLE
aws_secret_access_key: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
aws_region: us-east-1

# Azure Credentials
azure_subscription_id: 12345678-1234-1234-1234-123456789012
azure_tenant_id: 87654321-4321-4321-4321-210987654321
azure_client_id: abcdef12-3456-7890-abcd-ef1234567890
azure_client_secret: "Secret~Key.123"

# GCP Credentials
gcp_project_id: my-project-123456
gcp_service_account_email: ansible@my-project.iam.gserviceaccount.com
gcp_auth_kind: serviceaccount
gcp_service_account_file: /path/to/service-account.json

Example 3: SSL Certificates

---
# Encrypt certificate content
ssl_certificate: !vault |
          $ANSIBLE_VAULT;1.1;AES256
          -----BEGIN CERTIFICATE-----
          MIIDXTCCAkWgAwIBAgIJAKJ...
          -----END CERTIFICATE-----

ssl_private_key: !vault |
          $ANSIBLE_VAULT;1.1;AES256
          -----BEGIN PRIVATE KEY-----
          MIIEvgIBADANBgkqhki...
          -----END PRIVATE KEY-----

# Use in playbook
- name: Deploy SSL certificate
  copy:
    content: "{{ ssl_certificate }}"
    dest: /etc/nginx/ssl/cert.pem
    mode: '0644'

- name: Deploy SSL private key
  copy:
    content: "{{ ssl_private_key }}"
    dest: /etc/nginx/ssl/key.pem
    mode: '0600'

Troubleshooting

ERROR! Attempting to decrypt but no vault secrets found

# Verify file is encrypted
file group_vars/production/vault.yml

# Check ansible.cfg for vault_password_file
ansible-config dump | grep VAULT

# Explicitly provide password
ansible-playbook site.yml --ask-vault-pass

Decryption Failed

# Wrong password
ERROR! Decryption failed

# Solution: Verify password file content
cat .vault_pass

# Or rekey with new password
ansible-vault rekey vault.yml

Mixed Encrypted/Unencrypted Variables

# This works - mix encrypted and plain
database_host: db.example.com  # Plain
database_user: !vault |        # Encrypted
          $ANSIBLE_VAULT;1.1;AES256
          ...
database_password: !vault |    # Encrypted
          $ANSIBLE_VAULT;1.1;AES256
          ...

Security Considerations

What Vault DOES Protect

  • ✅ Data at rest (files in version control)
  • ✅ Accidental credential exposure in code reviews
  • ✅ Secrets from unauthorized team members

What Vault DOESN'T Protect

  • ❌ Data in transit (use SSH/SSL)
  • ❌ Secrets in memory during playbook execution
  • ❌ Compromised target systems
  • ❌ Weak vault passwords

Additional Security Layers

  1. Strong Passwords - Use 20+ character passwords
  2. Password Rotation - Change vault passwords quarterly
  3. Access Control - Limit who has vault passwords
  4. Audit Logs - Track vault file access
  5. Secret Scanning - Use tools like git-secrets or trufflehog

Conclusion

Ansible Vault is essential for securing your automation infrastructure. By following the practices in this guide, you'll:

  • Keep secrets out of version control
  • Meet compliance requirements
  • Safely collaborate with team members
  • Integrate with enterprise secret management systems

Remember: Security is a journey, not a destination. Regularly review and update your secret management practices as your infrastructure evolves.

Important Reminder

If you've ever committed unencrypted secrets to Git, encrypting them now is NOT enough. The secrets still exist in Git history. You must rotate those credentials and use tools like BFG Repo-Cleaner or git-filter-repo to purge them from history.