# Ansible Vault: Securing Your Automation Secrets


In the world of infrastructure automation, security remains a paramount concern.
As organizations increasingly adopt infrastructure as code practices, sensitive
information like API keys, passwords, and certificates must be handled
carefully.

Ansible Vault provides a robust solution to this challenge, allowing you to
encrypt sensitive data while keeping it seamlessly integrated with your
automation workflows.

[ad-logs]

## Understanding Ansible Vault

At its core, Ansible Vault is a feature built into Ansible that provides
encryption and decryption services for sensitive data. It uses AES-256
encryption to protect files, ensuring that unauthorized users cannot access your
secrets.

Ansible Vault works by encrypting files or individual variables with a password.
During playbook execution, Ansible automatically decrypts this information when
needed, making the encryption process transparent to your automation workflows.

What makes Ansible Vault particularly powerful is its deep integration with the
broader Ansible ecosystem. You can use encrypted content anywhere you would use
unencrypted content in your playbooks, roles, or variable files. This seamless
integration ensures that adding security doesn't increase complexity or reduce
functionality.

Key benefits of Ansible Vault include:

1. **Simple implementation**: Requires minimal additional setup as it's built
   into Ansible.
2. **Flexible encryption**: Can encrypt entire files or individual variables.
3. **Secure storage**: Allows sensitive data to be safely committed to version
   control.
4. **Transparent usage**: Encrypted data works seamlessly with existing Ansible
   functionality.

## Getting started with Ansible Vault

Ansible Vault comes built into Ansible, so if you have Ansible installed, you
already have access to Vault. To confirm your Ansible installation includes
Vault, you can check your Ansible version:

```command
ansible --version
```

```text
[output]
ansible [core 2.16.14]
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/home/ayo/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3.13/site-packages/ansible
  ansible collection location = /home/ayo/.ansible/collections:/usr/share/ansible/collections
  executable location = /usr/bin/ansible
  python version = 3.13.2 (main, Feb  4 2025, 00:00:00) [GCC 14.2.1 20250110 (Red Hat 14.2.1-7)] (/usr/bin/python3)
  jinja version = 3.1.6
  libyaml = True
```

Ansible Vault commands follow a consistent structure:

```command
ansible-vault [action] [options] file_name
```

Common actions include:

- `create`: Create a new encrypted file.
- `encrypt`: Encrypt an existing file.
- `decrypt`: Decrypt an encrypted file.
- `edit`: Edit an encrypted file.
- `view`: View the contents of an encrypted file.
- `rekey`: Change the encryption password.

Let's create a new encrypted file to store database credentials:

```command
ansible-vault create secret_vars.yml
```

![Ansible vault](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/2183292f-a533-4fa4-2523-88b02725ec00/orig =2010x638)

When you run this command, Ansible will prompt you to create and confirm a
password. After entering a password, your default editor will open, allowing you
to add content to the file.

```yaml
[label secret_vars.yml]
db_user: admin
db_password: supersecretpassword123
db_host: db.example.com
db_port: 5432
```

After saving and closing the editor, your file will be encrypted automatically.
If you try to view the file using a regular text editor or command like `cat`,
you'll see only encrypted content:

```command
cat secret_vars.yml
```

```text
[output]
$ANSIBLE_VAULT;1.1;AES256
32613736303961633861643936643839396438396438653932323939666630373962373765626362
3935633831643635633434323937333538346165663337610a373530626436343764646562636461
30663835356565383735653761386536663730306262386132613836363465636665323131353865
6365663932623835610a653565353362333435623135343965623836653634643935636363333734
30646337623166303739306436383861396661373439356635393131323331323839623366333065
36363162383235303637333166653534333764653965376562323532323433343832343766643834
37383666363663346531343530366266616638346337303033363065383337646236636563633265
34313733656566653032
```

This encrypted format ensures that your sensitive information remains protected
while at rest. The encrypted file contains the AES256 cipher, a salt, and the
actual encrypted data.

![Ansible secrets](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/020eb713-1fc9-4510-a7e4-642756363600/md2x =2034x1050)

## Core operations

Let's explore the essential operations you can perform with Ansible Vault. If
you already have configuration files with sensitive data, you can encrypt them
using the `encrypt` command:

```command
ansible-vault encrypt existing_config.yml
```

This command will prompt you for a password and then encrypt the file in place,
replacing the original unencrypted file with the encrypted version. For example,
let's say you have an existing file with API credentials:

```yaml
api_key: a8d7f9e1c5b2e4d0
api_secret: 93hdj47dh3kdi7hd3jd93
api_endpoint: https://api.example.com/v1
```

After running the encrypt command, the file will be transformed into an
encrypted version that's safe to store in version control.

You can encrypt multiple files simultaneously by providing multiple file paths:

```command
ansible-vault encrypt file1.yml file2.yml file3.yml
```

This is particularly useful when setting up a new project with multiple
configuration files containing sensitive information.

To convert an encrypted file back to plaintext, use the `decrypt` command:

```command
ansible-vault decrypt secret_vars.yml
```

You'll be prompted for the vault password, and upon successful authentication,
the file will be decrypted in place. Be cautious with this command, as it
removes the encryption protection from your sensitive data.

Decryption is typically used when you need to make significant changes to a file
or when you're transitioning to a different security approach.

### Editing encrypted files

![Editing encrypted files](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/325a4f30-92fa-4ad6-32ad-c45c9a820e00/orig =2034x1050)

Instead of decrypting a file to edit it and then re-encrypting it afterward
(which creates a window of vulnerability), Ansible Vault provides a direct
editing capability:

```command
ansible-vault edit secret_vars.yml
```

This command will:

1. Prompt you for the vault password.
2. Decrypt the file in memory (not on disk).
3. Open the decrypted content in your default editor.
4. Re-encrypt the file automatically when you save and close the editor.

This approach ensures your sensitive data is never written to disk in an
unencrypted form during the editing process.

### Viewing encrypted content

When you need to check the contents of an encrypted file without editing it, the
`view` command is ideal:

```command
ansible-vault view secret_vars.yml
```

![Viewing encrypted content](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/c60c7704-4ceb-4f3c-50b9-fd6474100f00/lg2x =2034x1050)

Similar to the edit command, this will decrypt the file in memory and display
its contents without writing the decrypted version to disk.

### Changing the encryption password

Security best practices often recommend periodic password rotation. To change
the password used to encrypt a vault file:

```command
ansible-vault rekey secret_vars.yml
```

You'll be prompted to enter the current password and then to create and confirm
a new password. The file will be re-encrypted with the new password while the
content remains unchanged.

## Working with vault passwords

Managing vault passwords effectively is critical to maintaining both security
and operational efficiency.

### Single password management

The simplest approach is to use a single password for all your vault-encrypted
files. While this is convenient, it provides limited security isolation. If you
use this approach, you can avoid repeatedly typing the password by storing it in
a file:

```command
echo "your_secure_password" > ~/.vault_pass
```

```command
chmod 600 ~/.vault_pass
```

Ensure that you set restrictive permissions on this file to prevent unauthorized
access. Now, you can reference this password file in your commands:

```command
ansible-vault create --vault-password-file=~/.vault_pass new_secret.yml
```

You can also configure Ansible to use this password file automatically by adding
the following to your `ansible.cfg`:

```text
[label ansible.cfg]
[defaults]
vault_password_file = ~/.vault_pass
```

With this configuration, you won't need to specify the password file in each
command.

### Password files vs. password scripts

Instead of storing the vault password in a plaintext file (which has obvious
security implications), you can use a script that retrieves the password
dynamically. For example, you might create a script that fetches the password
from a secure password manager:

```python
[label vault-pass.py]
#!/usr/bin/env python3
import subprocess

def get_password_from_manager():
   # This is just an example - implement your own secure method
   result = subprocess.run(['pass', 'show', 'ansible/vault'],
                          capture_output=True, text=True)
   return result.stdout.strip()

if __name__ == "__main__":
   print(get_password_from_manager())
```

Make the script executable:

```command
chmod +x vault-pass.py
```

Then use it with vault commands:

```command
ansible-vault edit --vault-password-file=./vault-pass.py secret_vars.yml
```

This approach adds an extra layer of security by avoiding storage of the vault
password in plaintext.

### Multiple vault password strategies

As your infrastructure grows, you might need different levels of security for
different environments or types of secrets. Ansible Vault supports multiple
password strategies through Vault IDs.

First, create separate password files for each environment:

```command
echo "dev_environment_password" > ~/.vault_pass.dev
```

```command
echo "prod_environment_password" > ~/.vault_pass.prod
```

```command
chmod 600 ~/.vault_pass.*
```

Now you can create or encrypt files with specific Vault IDs:

```command
ansible-vault create --vault-id dev@~/.vault_pass.dev dev_secrets.yml
```

```command
ansible-vault create --vault-id prod@~/.vault_pass.prod prod_secrets.yml
```

The `dev@` and `prod@` prefixes are Vault IDs that identify which password
should be used for which file.

### Vault ID implementation

You can configure multiple Vault IDs in your `ansible.cfg` file:

```text
[label ansible.cfg]
[defaults]
vault_identity_list = dev@~/.vault_pass.dev, prod@~/.vault_pass.prod
```

With this configuration, Ansible will try each password in order when decrypting
files. During encryption, you'll need to specify which ID to use:

```command
ansible-vault encrypt --vault-id dev@~/.vault_pass.dev config_file.yml
```

This approach allows for more sophisticated secret management strategies while
maintaining the simplicity of the Ansible Vault interface.

Here's how to use multiple vault passwords in a playbook:

```yaml
- name: Deploy application with environment-specific secrets
  hosts: all
  vars_files:
   - common_vars.yml
   - "{{ env }}_secrets.yml"  # Will be either dev_secrets.yml or prod_secrets.yml

  tasks:
   - name: Configure application
     template:
       src: app_config.j2
       dest: /etc/app/config.yml
     vars:
       config_env: "{{ env }}"
```

When running this playbook, you would specify which environment to use:

```command
ansible-playbook deploy.yml -e "env=dev" --vault-id dev@~/.vault_pass.dev
```

This command tells Ansible to use the `dev_secrets.yml` file and decrypt it
using the password stored in `~/.vault_pass.dev`.

## Using encrypted content in playbooks

Now that we understand how to manage encrypted files, let's explore how to use
them effectively in Ansible playbooks.

### Including encrypted files in playbooks

The simplest way to use encrypted content is to include encrypted files in your
playbooks using `vars_files`:

```yaml
- name: Deploy web application
  hosts: webservers
  vars_files:
   - common_vars.yml
   - secret_vars.yml  # This is an encrypted file

  tasks:
   - name: Configure database connection
     template:
       src: db_config.j2
       dest: /var/www/app/config/database.php
       owner: www-data
       group: www-data
       mode: '0640'
```

When executing this playbook with the correct vault password provided, Ansible
will automatically decrypt the `secret_vars.yml` file and make its variables
available to the playbook.

### Running playbooks with vault-encrypted files

When running a playbook that uses encrypted files, you need to provide the vault
password:

```command
ansible-playbook deploy_webapp.yml --ask-vault-pass
```

This command will prompt you for the vault password before executing the
playbook. Alternatively, you can use a password file:

```command
ansible-playbook deploy_webapp.yml --vault-password-file=~/.vault_pass
```

If you've configured a `vault_password_file` in your `ansible.cfg`, you don't
need to specify it in the command.

### Handling encrypted files in roles

When organizing your Ansible code into roles, you might need to include
encrypted files within role directories. Here's a typical structure:

```text
my_role/
├── defaults/
│   └── main.yml
├── files/
│   └── secure_config.yml.vault  # Encrypted file
├── handlers/
│   └── main.yml
├── tasks/
│   └── main.yml
├── templates/
│   └── application.conf.j2
└── vars/
   └── secrets.yml  # Encrypted variables
```

You can reference encrypted files in your tasks:

```yaml
- name: Copy secure configuration
  copy:
   src: secure_config.yml.vault
   dest: /etc/application/config.yml
   owner: root
   group: app
   mode: '0640'
```

And include encrypted variables:

```yaml
- name: Include secret variables
  include_vars:
   file: secrets.yml

- name: Configure application with sensitive data
  template:
   src: application.conf.j2
   dest: /etc/application/config.conf
   owner: root
   group: app
   mode: '0640'
```

### Variable-level encryption

Instead of encrypting entire files, Ansible Vault allows you to encrypt
individual variables within a file. This approach is useful when only a few
variables in a file contain sensitive information.

To encrypt a single variable, use the `ansible-vault encrypt_string` command:

```command
ansible-vault encrypt_string 'supersecretpassword' --name 'db_password'
```

```text
[output]
db_password: !vault |
         $ANSIBLE_VAULT;1.1;AES256
         31393861333766643966376131303735663839653432626130303766323966653866616638356233
         3033353037323730666566353564333564626437623132620a653537366336376364356239636433
         31383663373935363434366438383832303766623762656465396239636637396234613866623566
         3032376436313831640a396233366137326566373036303333613337336639636231633561633433
         3935
```

You can then include this encrypted variable in a YAML file:

```yaml
# Regular variables
db_user: admin
db_host: db.example.com
db_port: 5432

# Encrypted variable
db_password: !vault |
         $ANSIBLE_VAULT;1.1;AES256
         31393861333766643966376131303735663839653432626130303766323966653866616638356233
         3033353037323730666566353564333564626437623132620a653537366336376364356239636433
         31383663373935363434366438383832303766623762656465396239636637396234613866623566
         3032376436313831640a396233366137326566373036303333613337336639636231633561633433
         3935
```

This file can be committed to version control with only the password encrypted,
making it easier to track changes to non-sensitive variables.

### Using encrypted strings in inventory

You can also use encrypted strings in your inventory files to protect sensitive
host-specific variables:

```text
[label inventory.ini]
[webservers]
web1.example.com
web2.example.com

[database]
db1.example.com

[database:vars]
db_user=admin
db_password=!vault |
         $ANSIBLE_VAULT;1.1;AES256
         31393861333766643966376131303735663839653432626130303766323966653866616638356233
         3033353037323730666566353564333564626437623132620a653537366336376364356239636433
         31383663373935363434366438383832303766623762656465396239636637396234613866623566
         3032376436313831640a396233366137326566373036303333613337336639636231633561633433
         3935
```

This approach is particularly useful for environment-specific credentials that
need to be associated with specific inventory groups.

## Using Ansible Vault in practical scenarios

Let's explore some practical scenarios where Ansible Vault proves invaluable.

### Securing database credentials

One of the most common use cases for Ansible Vault is protecting database
credentials used in application deployments:

```yaml
mysql_root_password: SuperSecretRoot123!
app_db_name: my_application
app_db_user: app_user
app_db_password: AppUserPass987!
```

You can use these variables in a database setup playbook:

```yaml
- name: Configure MySQL database
  hosts: database_servers
  become: yes
  vars_files:
   - database_secrets.yml

  tasks:
   - name: Set MySQL root password
     mysql_user:
       name: root
       password: "{{ mysql_root_password }}"
       host_all: yes
       state: present

   - name: Create application database
     mysql_db:
       name: "{{ app_db_name }}"
       state: present
       login_user: root
       login_password: "{{ mysql_root_password }}"

   - name: Create application database user
     mysql_user:
       name: "{{ app_db_user }}"
       password: "{{ app_db_password }}"
       priv: "{{ app_db_name }}.*:ALL"
       state: present
       login_user: root
       login_password: "{{ mysql_root_password }}"
```

### Managing SSL certificates

Another common scenario is managing SSL certificates and private keys:

```text
ssl_private_key: |
 -----BEGIN PRIVATE KEY-----
 MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC7VJTUt9Us8cKj
 MzEfYyjiWA4R4/M2bS1GB4t7NXp98C3SC6dVMvDuictGeurT8jNbvJZHtCSuYEvu
 NMoSfm76oqFvAp8Gy0iz5sxjZmSnXyCdPEovGhLa0VzMaQ8s+CLOyS56YyCFGeJZ
 [... rest of the key ...]
 -----END PRIVATE KEY-----

ssl_certificate: |
 -----BEGIN CERTIFICATE-----
 MIIDnTCCAoWgAwIBAgIUJl6BIvdM+RfTfQDdBXcUxAXRnZowDQYJKoZIhvcNAQEL
 BQAwXjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRYwFAYDVQQHDA1TYW4gRnJh
 bmNpc2NvMQ8wDQYDVQQKDAZNeU9yZzExGTAXBgNVBAMMEHd3dy5leGFtcGxlLmNv
 [... rest of the certificate ...]
 -----END CERTIFICATE-----
```

These can be used in a web server configuration playbook:

```yaml
- name: Configure web server with SSL
  hosts: webservers
  become: yes
  vars_files:
   - ssl_secrets.yml

  tasks:
   - name: Create SSL directory
     file:
       path: /etc/nginx/ssl
       state: directory
       mode: '0700'

   - name: Install SSL private key
     copy:
       content: "{{ ssl_private_key }}"
       dest: /etc/nginx/ssl/private.key
       owner: root
       group: root
       mode: '0600'

   - name: Install SSL certificate
     copy:
       content: "{{ ssl_certificate }}"
       dest: /etc/nginx/ssl/certificate.crt
       owner: root
       group: root
       mode: '0644'

   - name: Configure Nginx with SSL
     template:
       src: nginx_ssl.conf.j2
       dest: /etc/nginx/sites-available/default
     notify: Restart Nginx

  handlers:
   - name: Restart Nginx
     service:
       name: nginx
       state: restarted
```

### Securing API keys

API keys and tokens used for external service integration are perfect candidates
for vault encryption:

```text
aws_access_key: AKIAIOSFODNN7EXAMPLE
aws_secret_key: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
github_api_token: ghp_aBcDeFgHiJkLmNoPqRsTuVwXyZ1234567890
slack_webhook_url: https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX
```

You can use these in a deployment playbook:

```yaml
- name: Deploy application with external integrations
  hosts: application_servers
  vars_files:
   - api_keys.yml

  tasks:
   - name: Configure AWS credentials
     template:
       src: aws_credentials.j2
       dest: /home/app/.aws/credentials
       owner: app
       group: app
       mode: '0600'

   - name: Set up GitHub access for deployments
     copy:
       content: "{{ github_api_token }}"
       dest: /home/app/.github_token
       owner: app
       group: app
       mode: '0600'

   - name: Send deployment notification to Slack
     uri:
       url: "{{ slack_webhook_url }}"
       method: POST
       body_format: json
       body:
         text: "Deployment to {{ inventory_hostname }} completed successfully!"
     delegate_to: localhost
```

## Final thoughts

Ansible Vault provides a powerful yet straightforward approach to securing
sensitive data within your automation workflows.

By encrypting either entire files or individual variables, you can protect your
secrets while still benefiting from the full capabilities of Ansible.

Thanks for reading!
