Set up Ansible Vault to encrypt sensitive data like passwords, API keys, and certificates in your playbooks. Learn to create encrypted variables, manage vault passwords, and integrate secure secret handling into automated deployments.
Prerequisites
- Linux server with sudo access
- Basic Ansible knowledge
- SSH key access to target hosts
What this solves
Ansible Vault encrypts sensitive data in your playbooks and variable files, preventing passwords, API keys, and certificates from being stored in plain text. This tutorial shows you how to create encrypted variables, manage vault passwords securely, and integrate vault-protected secrets into your automation workflows.
Step-by-step configuration
Install Ansible and required tools
Start by installing Ansible and the tools needed for vault operations.
sudo apt update
sudo apt install -y ansible python3-pip
pip3 install ansible-vaultCreate a vault password file
Create a secure password file that Ansible will use to encrypt and decrypt vault data. Store this outside your project directory.
mkdir -p ~/.ansible/vault
echo 'your-strong-vault-password-here' > ~/.ansible/vault/password
chmod 600 ~/.ansible/vault/passwordConfigure Ansible to use the vault password file
Set up Ansible configuration to automatically use your vault password file for encryption operations.
[defaults]
vault_password_file = ~/.ansible/vault/password
host_key_checking = False
inventory = inventory.ini
[ssh_connection]
ssh_args = -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=noCreate your first encrypted variables file
Create a variables file with encrypted sensitive data like database passwords and API keys.
ansible-vault create group_vars/all/vault.ymlThis opens your default editor. Add your encrypted variables:
---
vault_mysql_root_password: "MySecureP@ssw0rd123"
vault_api_key: "sk-1234567890abcdef"
vault_ssl_private_key: |
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC7
-----END PRIVATE KEY-----
vault_database_url: "postgresql://user:password@localhost:5432/mydb"Create a public variables file for non-sensitive data
Create a companion file for non-encrypted variables that reference the vault variables.
---
mysql_root_password: "{{ vault_mysql_root_password }}"
api_key: "{{ vault_api_key }}"
ssl_private_key: "{{ vault_ssl_private_key }}"
database_url: "{{ vault_database_url }}"
Non-sensitive configuration
mysql_port: 3306
api_endpoint: "https://api.example.com"
ssl_cert_path: "/etc/ssl/certs/example.com.crt"
ssl_key_path: "/etc/ssl/private/example.com.key"Create an inventory file
Set up your inventory with target hosts for the playbook.
[webservers]
web1 ansible_host=203.0.113.10 ansible_user=ubuntu
web2 ansible_host=203.0.113.11 ansible_user=ubuntu
[databases]
db1 ansible_host=203.0.113.20 ansible_user=ubuntu
[all:vars]
ansible_ssh_private_key_file=~/.ssh/id_rsaCreate a playbook that uses encrypted variables
Build a playbook that demonstrates how to use vault-encrypted secrets in real automation tasks.
---
- name: Deploy application with encrypted secrets
hosts: webservers
become: yes
vars_files:
- group_vars/all/vars.yml
- group_vars/all/vault.yml
tasks:
- name: Install required packages
package:
name:
- nginx
- mysql-client
- python3-mysqldb
state: present
- name: Create application user
user:
name: appuser
shell: /bin/bash
home: /opt/myapp
system: yes
- name: Create SSL certificate directory
file:
path: /etc/ssl/private
state: directory
mode: '0700'
owner: root
group: root
- name: Deploy SSL private key from vault
copy:
content: "{{ ssl_private_key }}"
dest: "{{ ssl_key_path }}"
mode: '0600'
owner: root
group: root
notify: restart nginx
- name: Create application configuration
template:
src: app.conf.j2
dest: /opt/myapp/config.yml
mode: '0640'
owner: appuser
group: appuser
notify: restart application
- name: Test database connection with encrypted credentials
mysql_db:
name: testdb
login_host: "{{ database_url.split('@')[1].split(':')[0] }}"
login_user: "{{ database_url.split('//')[1].split(':')[0] }}"
login_password: "{{ database_url.split(':')[2].split('@')[0] }}"
state: present
delegate_to: "{{ groups['databases'][0] }}"
run_once: true
handlers:
- name: restart nginx
systemd:
name: nginx
state: restarted
enabled: yes
- name: restart application
systemd:
name: myapp
state: restarted
enabled: yesCreate the application configuration template
Create a Jinja2 template that uses encrypted variables securely.
mkdir -p templates---
api:
endpoint: "{{ api_endpoint }}"
key: "{{ api_key }}"
timeout: 30
database:
url: "{{ database_url }}"
pool_size: 10
timeout: 5000
ssl:
certificate: "{{ ssl_cert_path }}"
private_key: "{{ ssl_key_path }}"
protocols: ["TLSv1.2", "TLSv1.3"]
logging:
level: INFO
file: /var/log/myapp.logSet up environment-specific vault files
Create separate vault files for different environments to manage secrets per stage.
mkdir -p group_vars/production group_vars/staging
ansible-vault create group_vars/production/vault.yml
ansible-vault create group_vars/staging/vault.ymlAdd environment-specific secrets to each file:
---
vault_mysql_root_password: "Pr0d-MySecureP@ssw0rd123"
vault_api_key: "sk-prod-1234567890abcdef"
vault_database_url: "postgresql://produser:prodpass@prod-db:5432/proddb"Create a vault management script
Build a helper script to manage vault operations across your infrastructure.
#!/bin/bash
VAULT_PASSWORD_FILE="~/.ansible/vault/password"
case "$1" in
"encrypt")
if [ -z "$2" ]; then
echo "Usage: $0 encrypt "
exit 1
fi
ansible-vault encrypt "$2" --vault-password-file="$VAULT_PASSWORD_FILE"
;;
"decrypt")
if [ -z "$2" ]; then
echo "Usage: $0 decrypt "
exit 1
fi
ansible-vault decrypt "$2" --vault-password-file="$VAULT_PASSWORD_FILE"
;;
"edit")
if [ -z "$2" ]; then
echo "Usage: $0 edit "
exit 1
fi
ansible-vault edit "$2" --vault-password-file="$VAULT_PASSWORD_FILE"
;;
"view")
if [ -z "$2" ]; then
echo "Usage: $0 view "
exit 1
fi
ansible-vault view "$2" --vault-password-file="$VAULT_PASSWORD_FILE"
;;
"rekey")
if [ -z "$2" ]; then
echo "Usage: $0 rekey "
exit 1
fi
ansible-vault rekey "$2" --vault-password-file="$VAULT_PASSWORD_FILE"
;;
"create")
if [ -z "$2" ]; then
echo "Usage: $0 create "
exit 1
fi
ansible-vault create "$2" --vault-password-file="$VAULT_PASSWORD_FILE"
;;
*)
echo "Usage: $0 {encrypt|decrypt|edit|view|rekey|create} "
echo "Examples:"
echo " $0 create group_vars/all/vault.yml"
echo " $0 edit group_vars/all/vault.yml"
echo " $0 view group_vars/all/vault.yml"
echo " $0 encrypt secrets.yml"
exit 1
;;
esac chmod +x vault-manager.shRun the playbook with encrypted variables
Execute your playbook and verify that encrypted secrets are properly decrypted and used.
ansible-playbook deploy-secure-app.yml --limit webservers --checkRun the actual deployment:
ansible-playbook deploy-secure-app.yml --limit webserversConfigure multiple vault password files
Set up different vault passwords for different environments or teams.
mkdir -p ~/.ansible/vault/environments
echo 'production-vault-password-123' > ~/.ansible/vault/environments/production
echo 'staging-vault-password-456' > ~/.ansible/vault/environments/staging
chmod 600 ~/.ansible/vault/environments/*Use environment-specific passwords:
ansible-playbook deploy-secure-app.yml --vault-password-file ~/.ansible/vault/environments/production --limit production
ansible-playbook deploy-secure-app.yml --vault-password-file ~/.ansible/vault/environments/staging --limit stagingAdvanced vault security practices
Set up vault ID labels for multiple encryption keys
Use vault IDs to manage multiple encryption keys within the same playbook infrastructure.
ansible-vault create --vault-id production@~/.ansible/vault/environments/production group_vars/production/vault.yml
ansible-vault create --vault-id staging@~/.ansible/vault/environments/staging group_vars/staging/vault.ymlRun playbooks with specific vault IDs:
ansible-playbook deploy-secure-app.yml --vault-id production@~/.ansible/vault/environments/production --limit productionCreate encrypted strings for inline use
Encrypt individual strings for use directly in playbooks without separate files.
ansible-vault encrypt_string 'MySecretPassword123' --name 'admin_password'This outputs encrypted data you can paste directly into playbooks:
---
- name: Deploy with inline encrypted variables
hosts: all
vars:
admin_password: !vault |
$ANSIBLE_VAULT;1.1;AES256
66633939373737386264323233343734346138663639373566353930643238336
3666373865336435353561323637363138656533373263650a373834646466316
63303138653738636666333034356462363661336565353235626562313633626
6662623865653635353864653237343739333363386536310a626436313461363
tasks:
- name: Debug encrypted variable
debug:
msg: "Admin password is {{ admin_password }}"Set up automated vault password rotation
Create a script to rotate vault passwords and re-encrypt files with new keys.
#!/bin/bash
OLD_PASSWORD_FILE="~/.ansible/vault/password.old"
NEW_PASSWORD_FILE="~/.ansible/vault/password.new"
CURRENT_PASSWORD_FILE="~/.ansible/vault/password"
Generate new password
echo "$(openssl rand -base64 32)" > "$NEW_PASSWORD_FILE"
chmod 600 "$NEW_PASSWORD_FILE"
Backup current password
cp "$CURRENT_PASSWORD_FILE" "$OLD_PASSWORD_FILE"
Find all vault files and rekey them
find . -name "*.yml" -exec grep -l "\$ANSIBLE_VAULT" {} \; | while read vault_file; do
echo "Rekeying $vault_file..."
ansible-vault rekey "$vault_file" \
--vault-password-file="$CURRENT_PASSWORD_FILE" \
--new-vault-password-file="$NEW_PASSWORD_FILE"
done
Update current password file
cp "$NEW_PASSWORD_FILE" "$CURRENT_PASSWORD_FILE"
echo "Vault password rotation completed"
echo "Old password backed up to $OLD_PASSWORD_FILE"
echo "Remember to update CI/CD systems with new password"chmod +x rotate-vault-password.shVerify your setup
# Test vault file creation and encryption
ansible-vault view group_vars/all/vault.yml
Verify playbook syntax with vault variables
ansible-playbook deploy-secure-app.yml --syntax-check
Test variable resolution without running tasks
ansible-playbook deploy-secure-app.yml --list-tasks
Check that encrypted files are properly encrypted
cat group_vars/all/vault.yml
Verify vault operations work
./vault-manager.sh view group_vars/all/vault.ymlYou should see encrypted content starting with $ANSIBLE_VAULT;1.1;AES256 when viewing raw files, and decrypted content when using vault commands.
Integration with CI/CD pipelines
For automated deployments, set up vault password management in your CI/CD system. Store the vault password as an encrypted environment variable and write it to a temporary file during pipeline execution.
deploy:
stage: deploy
script:
- echo "$ANSIBLE_VAULT_PASSWORD" > /tmp/vault_password
- chmod 600 /tmp/vault_password
- ansible-playbook deploy-secure-app.yml --vault-password-file /tmp/vault_password
- rm -f /tmp/vault_password
variables:
ANSIBLE_HOST_KEY_CHECKING: "false"
only:
- mainSimilar patterns work for GitHub Actions, Jenkins, and other CI/CD platforms. Always clean up temporary password files and use encrypted secrets storage provided by your platform.
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| ERROR! Decryption failed | Wrong vault password | Check password file path and content with ansible-vault view |
| Vault password file not found | Incorrect path in ansible.cfg | Update vault_password_file path or use --vault-password-file |
| Variables not resolving | Vault file not included in vars_files | Add vault.yml to vars_files section in playbook |
| Permission denied on password file | Wrong file permissions | Set correct permissions: chmod 600 ~/.ansible/vault/password |
| Vault ID not found | Missing vault-id parameter | Use --vault-id label@password_file when multiple IDs exist |
| Cannot edit vault file | No EDITOR environment variable | Set EDITOR: export EDITOR=nano or export EDITOR=vim |
Next steps
- Configure Kubernetes secrets management with Sealed Secrets for secure Helm values
- Integrate HashiCorp Vault with Kubernetes secrets management for secure container orchestration
- Configure Ansible dynamic inventory for AWS, Azure, and GCP with automated discovery
- Set up Ansible Molecule testing framework for infrastructure automation validation
- Configure Ansible Tower LDAP authentication for enterprise user management
Running this in production?
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Global variables
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
VAULT_PASSWORD=""
PROJECT_DIR=""
# Usage function
usage() {
echo "Usage: $0 [OPTIONS]"
echo "Configure Ansible Vault for secret management and encryption"
echo ""
echo "Options:"
echo " -p, --password PASSWORD Set vault password (will prompt if not provided)"
echo " -d, --directory DIR Set project directory (default: current directory)"
echo " -h, --help Show this help message"
echo ""
echo "Example: $0 -p 'MyStrongVaultPassword123!' -d /opt/ansible-project"
}
# Error handling and cleanup
cleanup() {
if [[ $? -ne 0 ]]; then
echo -e "${RED}[ERROR] Installation failed. Cleaning up...${NC}"
# Remove potentially corrupted files
[[ -f ~/.ansible/vault/password ]] && rm -f ~/.ansible/vault/password
[[ -f ansible.cfg ]] && rm -f ansible.cfg
fi
}
trap cleanup ERR
# Logging functions
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Check if running with appropriate privileges
check_privileges() {
if [[ $EUID -eq 0 ]]; then
log_warning "Running as root. This script should typically be run as a regular user."
read -p "Continue anyway? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 1
fi
fi
}
# Auto-detect distribution
detect_distro() {
log_info "Detecting Linux distribution..."
if [[ ! -f /etc/os-release ]]; then
log_error "Cannot detect distribution. /etc/os-release not found."
exit 1
fi
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_UPDATE="apt update"
PKG_INSTALL="apt install -y"
PYTHON_PKG="python3-pip"
;;
almalinux|rocky|centos|rhel|ol)
PKG_MGR="dnf"
PKG_UPDATE="dnf update -y"
PKG_INSTALL="dnf install -y"
PYTHON_PKG="python3-pip"
;;
fedora)
PKG_MGR="dnf"
PKG_UPDATE="dnf update -y"
PKG_INSTALL="dnf install -y"
PYTHON_PKG="python3-pip"
;;
amzn)
PKG_MGR="yum"
PKG_UPDATE="yum update -y"
PKG_INSTALL="yum install -y"
PYTHON_PKG="python3-pip"
;;
*)
log_error "Unsupported distribution: $ID"
exit 1
;;
esac
log_success "Detected: $PRETTY_NAME (Package manager: $PKG_MGR)"
}
# Parse command line arguments
parse_args() {
while [[ $# -gt 0 ]]; do
case $1 in
-p|--password)
VAULT_PASSWORD="$2"
shift 2
;;
-d|--directory)
PROJECT_DIR="$2"
shift 2
;;
-h|--help)
usage
exit 0
;;
*)
log_error "Unknown option: $1"
usage
exit 1
;;
esac
done
# Set default project directory if not provided
if [[ -z "$PROJECT_DIR" ]]; then
PROJECT_DIR="$(pwd)"
fi
# Validate project directory
if [[ ! -d "$PROJECT_DIR" ]]; then
log_error "Project directory does not exist: $PROJECT_DIR"
exit 1
fi
# Prompt for password if not provided
if [[ -z "$VAULT_PASSWORD" ]]; then
echo -n "Enter vault password (minimum 20 characters): "
read -s VAULT_PASSWORD
echo
if [[ ${#VAULT_PASSWORD} -lt 20 ]]; then
log_error "Password must be at least 20 characters long"
exit 1
fi
fi
}
# Install Ansible and required tools
install_ansible() {
echo -e "${BLUE}[1/7]${NC} Installing Ansible and required tools..."
# Update package repositories
sudo $PKG_UPDATE
# Install Ansible and Python pip
sudo $PKG_INSTALL ansible $PYTHON_PKG
# Install ansible-vault via pip
pip3 install --user ansible-vault
# Verify installation
if ! command -v ansible &> /dev/null; then
log_error "Ansible installation failed"
exit 1
fi
log_success "Ansible $(ansible --version | head -n1 | cut -d' ' -f2) installed successfully"
}
# Create vault password file
create_vault_password_file() {
echo -e "${BLUE}[2/7]${NC} Creating vault password file..."
# Create .ansible/vault directory
mkdir -p ~/.ansible/vault
# Create password file with restricted permissions
echo "$VAULT_PASSWORD" > ~/.ansible/vault/password
chmod 600 ~/.ansible/vault/password
# Verify file permissions
local perms=$(stat -c "%a" ~/.ansible/vault/password 2>/dev/null || stat -f "%A" ~/.ansible/vault/password)
if [[ "$perms" != "600" ]]; then
log_error "Failed to set correct permissions on vault password file"
exit 1
fi
log_success "Vault password file created at ~/.ansible/vault/password"
}
# Configure Ansible
configure_ansible() {
echo -e "${BLUE}[3/7]${NC} Configuring Ansible..."
cd "$PROJECT_DIR"
# Create ansible.cfg
cat > ansible.cfg << 'EOF'
[defaults]
vault_password_file = ~/.ansible/vault/password
host_key_checking = False
inventory = inventory.ini
timeout = 30
gathering = smart
fact_caching = memory
[ssh_connection]
ssh_args = -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o ControlMaster=auto -o ControlPersist=30m
pipelining = True
EOF
chmod 644 ansible.cfg
log_success "Ansible configuration created"
}
# Create directory structure
create_directory_structure() {
echo -e "${BLUE}[4/7]${NC} Creating project directory structure..."
cd "$PROJECT_DIR"
# Create necessary directories
mkdir -p group_vars/all
mkdir -p host_vars
mkdir -p roles
mkdir -p templates
mkdir -p files
# Set proper permissions
find . -type d -exec chmod 755 {} \;
log_success "Directory structure created"
}
# Create encrypted variables file
create_encrypted_variables() {
echo -e "${BLUE}[5/7]${NC} Creating encrypted variables file..."
cd "$PROJECT_DIR"
# Create vault.yml with sample encrypted variables
cat > /tmp/vault_content.yml << 'EOF'
---
# Encrypted sensitive variables
vault_mysql_root_password: "MySecureP@ssw0rd123"
vault_api_key: "sk-1234567890abcdef"
vault_database_url: "postgresql://user:password@localhost:5432/mydb"
vault_ssl_private_key: |
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC7
-----END PRIVATE KEY-----
EOF
# Encrypt the file
ansible-vault encrypt /tmp/vault_content.yml --output=group_vars/all/vault.yml
rm /tmp/vault_content.yml
chmod 644 group_vars/all/vault.yml
log_success "Encrypted variables file created at group_vars/all/vault.yml"
}
# Create public variables file
create_public_variables() {
echo -e "${BLUE}[6/7]${NC} Creating public variables file..."
cd "$PROJECT_DIR"
cat > group_vars/all/vars.yml << 'EOF'
---
# Public variables that reference vault variables
mysql_root_password: "{{ vault_mysql_root_password }}"
api_key: "{{ vault_api_key }}"
database_url: "{{ vault_database_url }}"
ssl_private_key: "{{ vault_ssl_private_key }}"
# Non-sensitive configuration
mysql_port: 3306
api_endpoint: "https://api.example.com"
ssl_cert_path: "/etc/ssl/certs/example.com.crt"
ssl_key_path: "/etc/ssl/private/example.com.key"
app_user: "appuser"
app_home: "/opt/myapp"
EOF
chmod 644 group_vars/all/vars.yml
log_success "Public variables file created at group_vars/all/vars.yml"
}
# Create sample inventory and playbook
create_sample_files() {
echo -e "${BLUE}[7/7]${NC} Creating sample inventory and playbook..."
cd "$PROJECT_DIR"
# Create inventory file
cat > inventory.ini << 'EOF'
[webservers]
web1 ansible_host=203.0.113.10 ansible_user=ubuntu
web2 ansible_host=203.0.113.11 ansible_user=ubuntu
[databases]
db1 ansible_host=203.0.113.20 ansible_user=ubuntu
[all:vars]
ansible_ssh_private_key_file=~/.ssh/id_rsa
EOF
# Create sample playbook
cat > site.yml << 'EOF'
---
- name: Deploy application with encrypted secrets
hosts: webservers
become: yes
vars_files:
- group_vars/all/vars.yml
- group_vars/all/vault.yml
tasks:
- name: Install required packages
package:
name:
- nginx
- python3
state: present
- name: Create application user
user:
name: "{{ app_user }}"
shell: /bin/bash
home: "{{ app_home }}"
system: yes
- name: Create SSL certificate directory
file:
path: /etc/ssl/private
state: directory
mode: '0700'
owner: root
group: root
- name: Display encrypted variable (for testing)
debug:
msg: "MySQL password is configured (length: {{ mysql_root_password | length }})"
EOF
chmod 644 inventory.ini site.yml
log_success "Sample inventory and playbook created"
}
# Verify installation
verify_installation() {
echo -e "${BLUE}Verifying Ansible Vault installation...${NC}"
cd "$PROJECT_DIR"
# Test vault password file
if [[ ! -f ~/.ansible/vault/password ]]; then
log_error "Vault password file not found"
return 1
fi
# Test vault file decryption
if ! ansible-vault view group_vars/all/vault.yml > /dev/null 2>&1; then
log_error "Cannot decrypt vault file"
return 1
fi
# Test playbook syntax
if ! ansible-playbook --syntax-check site.yml > /dev/null 2>&1; then
log_error "Playbook syntax check failed"
return 1
fi
log_success "All verification checks passed"
echo
echo -e "${GREEN}Ansible Vault setup completed successfully!${NC}"
echo
echo "Next steps:"
echo "1. Update inventory.ini with your actual server IPs"
echo "2. Edit group_vars/all/vault.yml to add your secrets:"
echo " ansible-vault edit group_vars/all/vault.yml"
echo "3. Test the playbook:"
echo " ansible-playbook site.yml --check"
echo
echo "Project directory: $PROJECT_DIR"
echo "Vault password file: ~/.ansible/vault/password"
}
# Main function
main() {
echo -e "${GREEN}Ansible Vault Installation Script${NC}"
echo "=================================="
check_privileges
detect_distro
parse_args "$@"
install_ansible
create_vault_password_file
configure_ansible
create_directory_structure
create_encrypted_variables
create_public_variables
create_sample_files
verify_installation
}
# Run main function with all arguments
main "$@"
Review the script before running. Execute with: bash install.sh