Encrypting passwords using Ansible's Vault

By Kirk Byers
2015-05-05

You are not happy about having your passwords sitting in the clear inside your Ansible files; you really would like these files to be encrypted. Ansible has a nice feature called Vault that allows you to accomplish this.

Take, for example, the following scenario—you have four Arista switches and a single Juniper SRX:

[local]
localhost ansible_connection=local

[arista]
pynet-sw1 eapi_port=8243
pynet-sw2 eapi_port=8343
pynet-sw3 eapi_port=8443
pynet-sw4 eapi_port=8543

[juniper]
pynet-sf-srx  

The above file is my Ansible inventory file which I have relocated to ~/ansible-hosts.


Now Ansible has a lot of ways to specify host and group variables. One of these methods utilizes a directory named 'group_vars'. By default, Ansible will look for the 'group_vars' directory in the directory containing your inventory file.

Inside of this group_vars directory you can create a subdirectory named after each of the groups:

[ ~]$ cd group_vars/
[ group_vars]$ ls -al
total 4
drwxrwxr-x.  4 kbyers kbyers     33 Mar 30 13:31 .
drwx------.   26 kbyers kbyers 4096 Mar 30 13:32 ..
drwxrwxr-x.  2 kbyers kbyers     45 Mar 30 13:33 arista
drwxrwxr-x.  2 kbyers kbyers     45 Mar 30 13:31 juniper 

Each of the files inside of these 'group' directories will be processed looking for variables relevant to that group. So inside of the 'arista' subdirectory, I can create a standard.yml file and specify the following variables:

[ group_vars]$ cd arista/
[ arista]$ cat standard.yml 
---
ansible_connection: local
eapi_hostname: 10.10.10.10
eapi_username: admin
eapi_password: arista123 

I can create a similar file inside of the ~/group_vars/juniper directory.

At this point, I can execute a test playbook. This playbook adds a VLAN onto the four Arista switches (this verifies that my Arista inventory definition including group_vars/arista/standard.yml is correct):

[ ARISTA]$ ansible-playbook create_vlans.yml 

PLAY [Arista-Ansible Exercise1] *********************************************** 

GATHERING FACTS *************************************************************** 
ok: [pynet-sw1]
ok: [pynet-sw2]
ok: [pynet-sw3]
ok: [pynet-sw4] 

TASK: [create VLANs] ********************************************************** 
changed: [pynet-sw3] => (item={'vlan_name': 'kb5', 'vlan_id': 225})
changed: [pynet-sw1] => (item={'vlan_name': 'kb5', 'vlan_id': 225})
changed: [pynet-sw2] => (item={'vlan_name': 'kb5', 'vlan_id': 225})
changed: [pynet-sw4] => (item={'vlan_name': 'kb5', 'vlan_id': 225}) 

PLAY RECAP ******************************************************************** 
pynet-sw1                  : ok=2    changed=1    unreachable=0    failed=0   
pynet-sw2                  : ok=2    changed=1    unreachable=0    failed=0   
pynet-sw3                  : ok=2    changed=1    unreachable=0    failed=0   
pynet-sw4                  : ok=2    changed=1    unreachable=0    failed=0  

Now this is all fine and good, but the password is still in the standard.yml file in the clear.

Now let's move the password out of standard.yml and put it into the ~/group_vars/arista/passwords.yml file:

[ arista]$ cat passwords.yml 
---
eapi_password: arista123 

Now I can use the 'ansible-vault' command to encrypt the passwords.yml file:

[ arista]$ ansible-vault encrypt passwords.yml 
Vault password:                          # specify a vault password to use
Confirm Vault password: 
Encryption successful

[ arista]$ cat passwords.yml 
$ANSIBLE_VAULT;1.1;AES256
37633932346233383134646133636236373430623961623530383935323730359934323966626565
3634366238323939366130086431393864663630383432380a673265393334317462663763636439
63363430373138666435373439393865333138623630343064653833323730363232663735356633
3431626133393838370a326938653039319991633866353237666333333061306535646530616539
31336562388437376536303939663862823126623063643964396232303866613038 

Now, when I try to execute my Ansible playbook, I receive the following:

[ ARISTA]$ ansible-playbook create_vlans.yml 
ERROR: A vault password must be specified to decrypt /home/kbyers/group_vars/arista/passwords.yml 

I then pass in the '--ask-vault-pass' option:

[ ARISTA]$ ansible-playbook create_vlans.yml  --ask-vault-pass
Vault password: 

PLAY [Arista-Ansible Exercise1] *********************************************** 

GATHERING FACTS *************************************************************** 
ok: [pynet-sw1]
ok: [pynet-sw2]
ok: [pynet-sw3]
ok: [pynet-sw4]

TASK: [create VLANs] ********************************************************** 
ok: [pynet-sw3] => (item={'vlan_name': 'kb5', 'vlan_id': 225})
ok: [pynet-sw2] => (item={'vlan_name': 'kb5', 'vlan_id': 225})
ok: [pynet-sw1] => (item={'vlan_name': 'kb5', 'vlan_id': 225})
ok: [pynet-sw4] => (item={'vlan_name': 'kb5', 'vlan_id': 225}) 

PLAY RECAP ******************************************************************** 
pynet-sw1                  : ok=2    changed=0    unreachable=0    failed=0   
pynet-sw2                  : ok=2    changed=0    unreachable=0    failed=0   
pynet-sw3                  : ok=2    changed=0    unreachable=0    failed=0   
pynet-sw4                  : ok=2    changed=0    unreachable=0    failed=0   

Note, at this point, the VLAN already exists so nothing actually changes when I execute the playbook. The playbook is connecting to the switch correctly, however.

For more information see: Ansible Vault


If you want to learn more about network automation, Python, and Ansible, join my email-list. I also periodically run a free Python for Network Engineers email course which you can sign-up for here.

Kirk Byers
CCIE #6243 emeritus
Twitter: @kirkbyers

Encrypting passwords using Ansible's Vault

By Kirk Byers
2015-05-05

You are not happy about having your passwords sitting in the clear inside your Ansible files; you really would like these files to be encrypted. Ansible has a nice feature called Vault that allows you to accomplish this.

Take, for example, the following scenario—you have four Arista switches and a single Juniper SRX:

[local]
localhost ansible_connection=local

[arista]
pynet-sw1 eapi_port=8243
pynet-sw2 eapi_port=8343
pynet-sw3 eapi_port=8443
pynet-sw4 eapi_port=8543

[juniper]
pynet-sf-srx  

The above file is my Ansible inventory file which I have relocated to ~/ansible-hosts.


Now Ansible has a lot of ways to specify host and group variables. One of these methods utilizes a directory named 'group_vars'. By default, Ansible will look for the 'group_vars' directory in the directory containing your inventory file.

Inside of this group_vars directory you can create a subdirectory named after each of the groups:

[ ~]$ cd group_vars/
[ group_vars]$ ls -al
total 4
drwxrwxr-x.  4 kbyers kbyers     33 Mar 30 13:31 .
drwx------.   26 kbyers kbyers 4096 Mar 30 13:32 ..
drwxrwxr-x.  2 kbyers kbyers     45 Mar 30 13:33 arista
drwxrwxr-x.  2 kbyers kbyers     45 Mar 30 13:31 juniper 

Each of the files inside of these 'group' directories will be processed looking for variables relevant to that group. So inside of the 'arista' subdirectory, I can create a standard.yml file and specify the following variables:

[ group_vars]$ cd arista/
[ arista]$ cat standard.yml 
---
ansible_connection: local
eapi_hostname: 10.10.10.10
eapi_username: admin
eapi_password: arista123 

I can create a similar file inside of the ~/group_vars/juniper directory.

At this point, I can execute a test playbook. This playbook adds a VLAN onto the four Arista switches (this verifies that my Arista inventory definition including group_vars/arista/standard.yml is correct):

[ ARISTA]$ ansible-playbook create_vlans.yml 

PLAY [Arista-Ansible Exercise1] *********************************************** 

GATHERING FACTS *************************************************************** 
ok: [pynet-sw1]
ok: [pynet-sw2]
ok: [pynet-sw3]
ok: [pynet-sw4] 

TASK: [create VLANs] ********************************************************** 
changed: [pynet-sw3] => (item={'vlan_name': 'kb5', 'vlan_id': 225})
changed: [pynet-sw1] => (item={'vlan_name': 'kb5', 'vlan_id': 225})
changed: [pynet-sw2] => (item={'vlan_name': 'kb5', 'vlan_id': 225})
changed: [pynet-sw4] => (item={'vlan_name': 'kb5', 'vlan_id': 225}) 

PLAY RECAP ******************************************************************** 
pynet-sw1                  : ok=2    changed=1    unreachable=0    failed=0   
pynet-sw2                  : ok=2    changed=1    unreachable=0    failed=0   
pynet-sw3                  : ok=2    changed=1    unreachable=0    failed=0   
pynet-sw4                  : ok=2    changed=1    unreachable=0    failed=0  

Now this is all fine and good, but the password is still in the standard.yml file in the clear.

Now let's move the password out of standard.yml and put it into the ~/group_vars/arista/passwords.yml file:

[ arista]$ cat passwords.yml 
---
eapi_password: arista123 

Now I can use the 'ansible-vault' command to encrypt the passwords.yml file:

[ arista]$ ansible-vault encrypt passwords.yml 
Vault password:                          # specify a vault password to use
Confirm Vault password: 
Encryption successful

[ arista]$ cat passwords.yml 
$ANSIBLE_VAULT;1.1;AES256
37633932346233383134646133636236373430623961623530383935323730359934323966626565
3634366238323939366130086431393864663630383432380a673265393334317462663763636439
63363430373138666435373439393865333138623630343064653833323730363232663735356633
3431626133393838370a326938653039319991633866353237666333333061306535646530616539
31336562388437376536303939663862823126623063643964396232303866613038 

Now, when I try to execute my Ansible playbook, I receive the following:

[ ARISTA]$ ansible-playbook create_vlans.yml 
ERROR: A vault password must be specified to decrypt /home/kbyers/group_vars/arista/passwords.yml 

I then pass in the '--ask-vault-pass' option:

[ ARISTA]$ ansible-playbook create_vlans.yml  --ask-vault-pass
Vault password: 

PLAY [Arista-Ansible Exercise1] *********************************************** 

GATHERING FACTS *************************************************************** 
ok: [pynet-sw1]
ok: [pynet-sw2]
ok: [pynet-sw3]
ok: [pynet-sw4]

TASK: [create VLANs] ********************************************************** 
ok: [pynet-sw3] => (item={'vlan_name': 'kb5', 'vlan_id': 225})
ok: [pynet-sw2] => (item={'vlan_name': 'kb5', 'vlan_id': 225})
ok: [pynet-sw1] => (item={'vlan_name': 'kb5', 'vlan_id': 225})
ok: [pynet-sw4] => (item={'vlan_name': 'kb5', 'vlan_id': 225}) 

PLAY RECAP ******************************************************************** 
pynet-sw1                  : ok=2    changed=0    unreachable=0    failed=0   
pynet-sw2                  : ok=2    changed=0    unreachable=0    failed=0   
pynet-sw3                  : ok=2    changed=0    unreachable=0    failed=0   
pynet-sw4                  : ok=2    changed=0    unreachable=0    failed=0   

Note, at this point, the VLAN already exists so nothing actually changes when I execute the playbook. The playbook is connecting to the switch correctly, however.

For more information see: Ansible Vault


If you want to learn more about network automation, Python, and Ansible, join my email-list. I also periodically run a free Python for Network Engineers email course which you can sign-up for here.

Kirk Byers
CCIE #6243 emeritus
Twitter: @kirkbyers