I usually use encrypt_string
for encrypting secrets in my ansible roles but I have a new boss and he insists on encrypting the whole file but I cannot not seem to get that to work with an Ansible role.
Here is a simple example that illustrates what my problem is.
bash-3.2$ tree
.
├── playbooks
│ └── run_foo.yml
└── roles
└── foo
├── tasks
│ └── main.yml
└── vars
├── main.yml
└── vault.yml
5 directories, 4 files
Here is a playbook that uses the role.
bash-3.2$ cat playbooks/run_foo.yml
---
- name: Run Foo
hosts: all
roles:
- role: ../roles/foo
Here's the role:
bash-3.2$ cat roles/foo/tasks/main.yml
---
# tasks file for foo
- name: Show us debug clear_text
debug:
var: clear_text
- name: Show us debug password
debug:
var: password
Here is my vars/main.yml file:
bash-3.2$ cat roles/foo/vars/main.yml
---
# vars file for foo
include_vars: vault.yml
clear_text: 12345
Here is my encrypted vault.yml
file:
bash-3.2$ cat roles/foo/vars/vault.yml
$ANSIBLE_VAULT;1.1;AES256
64653334323939303461653839353634666162383130326533306234636232656162306661383761
6339376233366638643331373831316638373263663830350a333733653039353939656633376333
31323232666633373633393032396232613830393735396139376333353035633566376465636536
3930633964633861620a633735343066313938633733303538333864353665393062626338356665
63363666636638623964336461346366323565323563316434323439626239633734
This is what it looks like decrypted:
bash-3.2$ ansible-vault view roles/foo/vars/vault.yml
Vault password:
password: hovercraft
Here's what happens when I run the playbook:
bash-3.2$ ansible-playbook -i,localhost playbooks/run_foo.yml --connection=local --ask-vault-pass
Vault password:
PLAY [Run Foo] ******************************************************************************************************************************************************************************
TASK [Gathering Facts] **********************************************************************************************************************************************************************
[WARNING]: error loading fact - please check content
[WARNING]: Platform darwin on host localhost is using the discovered Python interpreter at /Library/Frameworks/Python.framework/Versions/3.6/bin/python3.6, but future installation of
another Python interpreter could change the meaning of that path. See https://docs.ansible.com/ansible/2.10/reference_appendices/interpreter_discovery.html for more information.
ok: [localhost]
TASK [../roles/foo : Show us debug clear_text] **********************************************************************************************************************************************
ok: [localhost] => {
"clear_text": 12345
}
TASK [../roles/foo : Show us debug password] ************************************************************************************************************************************************
ok: [localhost] => {
"password": "VARIABLE IS NOT DEFINED!"
}
PLAY RECAP **********************************************************************************************************************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Is it possible to use a encrypted vault.yml file like my new manager wants?
Put both files into the directory vars/main. All files in vars/main are included automatically. For example,
shell> tree .
.
├── ansible.cfg
├── hosts
├── roles
│ └── foo
│ ├── tasks
│ │ └── main.yml
│ └── vars
│ └── main
│ ├── main.yml
│ └── vault.yml
└── run_foo.yml
shell> cat roles/foo/tasks/main.yml
- debug:
var: clear_text
- debug:
var: password
shell> cat roles/foo/vars/main/main.yml
clear_text: 12345
shell> cat roles/foo/vars/main/vault.yml
password: hovercraft
shell> cat run_foo.yml
- hosts: all
roles:
- foo
gives
shell> cat hosts
localhost
shell> ansible-playbook run_foo.yml
PLAY [all] ***********************************************************************************
TASK [foo : debug] ***************************************************************************
ok: [localhost] =>
clear_text: 12345
TASK [foo : debug] ***************************************************************************
ok: [localhost] =>
password: hovercraft
PLAY RECAP ***********************************************************************************
localhost: ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
You'll get the same result with encrypted vault.yml. Encrypting the whole file is simpler and safer. Instead of the code, you update and encrypt this single file only. In addition to this, a use case is possible where each user maps its own vault.yml overlay.
Notes
Don't distribute roles with the directory vars/main or file vars/main.yml. The purpose of the role's vars is to override the role's defaults. See Understanding variable precedence. An update of a role will overwrite potential user's customization in vars if you distribute it. In development, ignore vars/main in your version control.
Put vault.yml into the directory defaults
shell> cat roles/foo/defaults/main/vault.yml
password: hovercraft
and test the password
shell> cat roles/foo/tasks/main.yml
- assert:
that: password != 'hovercraft'
fail_msg: Change and encrypt password!
when: assert_password|d(true)|bool
- debug:
var: clear_text
- debug:
var: password
By default, the user'll see
shell> ansible-playbook run_foo.yml
PLAY [all] ***********************************************************************************
TASK [foo : assert] **************************************************************************
fatal: [localhost]: FAILED! => changed=false
assertion: password != 'hovercraft'
evaluated_to: false
msg: Change and encrypt password!
Turn the assert off for testing
shell> ansible-playbook run_foo.yml -e assert_password=false
You can limit the scope of secrets to the tasks where you need them. For example, put the password to a file
shell> cat vars/password.yml
my secret password
and read it in the tasks
shell> cat roles/foo/tasks/main.yml
- assert:
that: password != 'hovercraft'
fail_msg: Change and encrypt password!
when: assert_password|d(true)|bool
vars:
password: "{{ lookup('file', password_path) }}"
- debug:
var: clear_text
- debug:
var: password
vars:
password: "{{ lookup('file', password_path) }}"
Provide the path to the file, for example, in the playbook
shell> cat run_foo.yml
- hosts: all
vars:
password_path: "{{ playbook_dir }}/vars/password.yml"
roles:
- foo
gives
shell> ansible-playbook run_foo.yml
PLAY [all] ***********************************************************************************
TASK [foo : assert] **************************************************************************
ok: [localhost] => changed=false
msg: All assertions passed
TASK [foo : debug] ***************************************************************************
ok: [localhost] =>
clear_text: 12345
TASK [foo : debug] ***************************************************************************
ok: [localhost] =>
password: my secret password
PLAY RECAP ***********************************************************************************
localhost: ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
The encryption of the files is transparent to the file lookup. This also solves your other problem to "use grep to find things in the code"
shell> grep -r password: roles/
roles/foo/defaults/main/vault.yml:password: hovercraft
roles/foo/tasks/main.yml: password: "{{ lookup('file', password_path) }}"
roles/foo/tasks/main.yml: password: "{{ lookup('file', password_path) }}"