One of the sweetest side of Ansible is its ultra easy learning curve which allows IT people to grasp it almost instantly.
Ansible is known for being entirely plain text which means that also passwords are unencrypted... Or not?
This is for you, who are looking for a way to protect your private data within playbooks.

What is Ansible Vault? And what are Vault IDs?

ansible-vault is the Ansible tool for protecting secret information. It can create encrypted file and encrypt plain text ones.
Ansible-vault has the option to view and edit the encrypted file and allows for replacing the old password used to encrypt a file with a new one, and decrypts the file too!
It only has one issue: It does not support having multiple encrypted vaults in the same playbook. Starting with Ansible version 2.4, Vault IDs are the way to go when adding in your playbook multiple encrypted files.
We will go through setting up an Ansible Vault for showing how quick this process can be, with a major focus on the Vault IDs.

Create and operate your Vault

ansible-vault comes as a utility when installing ansible, which means you pretty much have it already in you system!

The vault file extension can be any, or none. Generally in Linux extensions don't matter, and for this reason ansible-vault can be used to encrypt many types of file, but for their purpose in Ansible, we will encrypt files in YAML format because their content as key: value is our secret variables and we will use them for our secret playbook!

How to use ansible-vault

You can start with a simple text file. Let's create a yaml file that stores our password

cat > mysecret.yml<<EOF
foo: bar
EOF

And encrypt it

ansible-vault encrypt mysecret.yml

You will be asked to provide a password. This password will unlock the file. As any other password, try not to forget it (:

New Vault password: 
Confirm New Vault password: 
Encryption successful

You can now use this vault file in your playbook and run it as ansible-playbook play-vault-file.yml --ask-vault-pass

- name: test password file
  hosts: localhost
  gather_facts: false
  vars_file:
    - mysecret.yml
  tasks:
    - debug:
        var: foo

You can also create a file and encrypt it already:

ansible-vault create vault.yml

And other useful commands to operate with your vault are:

  • View

    ansible-vault view vault.yml
  • Edit

    ansible-vault edit vault.yml
  • Update vault key (rekey)

    ansible-vault rekey vault.yml
  • Decrypt

    ansible-vault decrypt vault.yml
  • Encrypt string

    ansible-vault encrypt_string "hello Ansible" --name mysecretvar

The option encrypt_string doesn't directly operate on vault files but it is particularly useful for encrypting a string on-the-fly and use it in a playbook as extra vars:

Quoting from the ansible-vault doc:

Typing secret content directly at the command line (without a prompt) leaves the secret string in your shell history. Do not do this outside of testing.

So, maybe, instead of using the format above it is suggested to get the secret from --stdin:

ansible-vault encrypt_string --stdin-name 'new_user_password'
New Vault password: 
Confirm New Vault password: 
Reading plaintext input from stdin. (ctrl-d to end input, twice if your content does not already have a newline)
myNewNotSoSecurePassword!
Encryption successful
new_user_password: !vault |
          $ANSIBLE_VAULT;1.1;AES256
          39623231313438633166383138633162636638333435336331343830303763643161663831316462
          3239386139633239666333353730343663353164316163650a643663376665393063336134633561
          63616266396263353231636438613232306430646331366366653464386161333837346661643761
          3732353133653037320a363536666664663963356339663563613837326432366262613038663236
          3862

Which we can then use in a playbook and run it as ansible-playbook play-vault-var.yml --ask-vault-pass

- name: test password var
  hosts: localhost
  gather_facts: false
  vars:
    new_user_password: !vault |
              $ANSIBLE_VAULT;1.1;AES256
              39623231313438633166383138633162636638333435336331343830303763643161663831316462
              3239386139633239666333353730343663353164316163650a643663376665393063336134633561
              63616266396263353231636438613232306430646331366366653464386161333837346661643761
              3732353133653037320a363536666664663963356339663563613837326432366262613038663236
              3862
  tasks:
    - debug:
        var: new_user_password

And this, people, is what you should avoid!

Technically, this is not wrong. There are better ways to manage and call vault variables and files.
I'll see you in the next chapter, where we introduce the Vault IDs and Ansible tweaks for storing master passwords!

Meet the Ansible Vault IDs

The Ansible Vault ID is a user-defined string that helps identify a particular vault password. It's like giving a name or label to a specific vault password.
With Vault ID we can use different vault passwords for different files or environments.

How to Use Vault ID

When working with Vault ID with files, you can specify a Vault ID using the --vault-id option.

  • Create the vault.yml

This creates vault.yml using the vault password associated with the vault ID my-vault-id.

ansible-vault create --vault-id my-vault-id@prompt vault.yml
New vault password (my-vault-id): 
Confirm new vault password (my-vault-id):
  • Decrypt the vault.yml

This decrypts the vault.yml using the vault password associated with the vault ID my-vault-id.

ansible-vault decrypt --vault-id my-vault-id@prompt vault.yml
Vault password (my-vault-id): 
Decryption successful

Other operations are also allowed, same as the basic vault files.

Handling multiple Vault file

Have you noticed the @prompt?
This option allows us to type in the password of the vault file similar to --ask-vault-pass that we used earlier. The main difference is --ask-vault-pass can be called only once when launching the playbook:

ansible-playbook play-vault-var.yml --ask-vault-pass

While with @prompt we can feed multiple vault files:

ansible-playbook playbook.yml --vault-id dev-vault@prompt --vault-id prod-vault@prompt

Although this is not exactly related to @prompt rather to --vault-id, it's the best example for showing why Vault ID is the preferred choice when working with multiple vault files.

Playbook example with multiple Vault ID

Let's go for a full run of how to use multiple Vault ID in Ansible!

1) Create the vault files in plain text.

prod.yml

---
prod: This is Prod

dev.yml

---
dev: This is Dev

2) Encrypt the vault files. Choose a different password for each vault.

ansible-vault encrypt --vault-id prod@prompt prod.yml
ansible-vault encrypt --vault-id dev@prompt dev.yml

3) Create the playbook play-vault.yml.

- name: Test multiple vaults
  hosts: localhost
  vars_files:
    - dev.yml
    - prod.yml
  gather_facts: false
  tasks:
  - debug:
      msg: "DEV: {{ dev }}"

  - debug:
      msg: "PROD: {{ prod }}"

4) Run the playbook.

ansible-playbook play-vault.yml --vault-id dev@prompt --vault-id prod@prompt

5) Look at the output.

ansible-playbook play-vault.yml --vault-id dev@prompt --vault-id prod@prompt
Vault password (dev): 
Vault password (prod): 
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

PLAY [test password] ************************************************************************************************************************************************************************

TASK [debug] ********************************************************************************************************************************************************************************
ok: [localhost] => {
    "msg": "DEV: this is Dev"
}

TASK [debug] ********************************************************************************************************************************************************************************
ok: [localhost] => {
    "msg": "PROD: this is Prod"
}

PLAY RECAP **********************************************************************************************************************************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Do not ask for passwords!

Whether we're using the simplest form of Vaults or the Vault ID, we can set them up so when we run the playbook it doesn't ask us for the password. And not only for playbooks. This option has effect anytime we work with vaults. ansible-vault view, create, encrypt and so on.

In the ansible.cfg we can find the option named vault_password_file. We can uncomment it and update its value to point to a file that contains the password for unlocking the vault.

...
vault_password_file=/home/user/mysecret.txt

And the password for the vault inside mysecret.txt (no, the .txt extension is not required, but it helps to recognize the kind of file instantly):

mypassword

While this approach works great with simple Ansible vaults because the option vault_password_file accepts strings only, it is not ideal for Vault ID.

With Vault ID we can replace @prompt with the path to the secret file.

ansible-playbook play-vault.yml --vault-id dev@dev-vault.txt --vault-id prod@prod-vault.txt

Conclusions

In a nutshell, Ansible Vault is like the superhero of secret-keeping for your playbooks. It effortlessly locks away your sensitive info, ensuring passwords and secrets stay as classified as a top-secret spy mission.
With a user-friendly vibe, it turns the daunting task of encryption into a walk in the digital park. Plus, the magic touch of using different vault passwords? It's like having personalized security guards for your 'Dev' and 'Prod' environments.
So, in the world of Ansible, consider Ansible Vault your trusty sidekick, ensuring your playbook secrets are safe and sound.