Ansible Vault: Secrets Management Best Practices
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
- Strong Passwords - Use 20+ character passwords
- Password Rotation - Change vault passwords quarterly
- Access Control - Limit who has vault passwords
- Audit Logs - Track vault file access
- 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.