Initial commit
This commit is contained in:
304
modules/putinvault.py
Normal file
304
modules/putinvault.py
Normal file
@ -0,0 +1,304 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2020, Yo <yo@nosd.in>
|
||||
# Use ansible-vault python library from Tomohiro Nakamura <quickness.net@gmail.com> (Prefer v2)
|
||||
# Vastly inspired by the lineinfile module
|
||||
#
|
||||
# v.0.5 19/05/2020
|
||||
#
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: putinvault
|
||||
short_description: Manage Ansible vault file
|
||||
description:
|
||||
- Manages passwords in Ansible Vault files
|
||||
version_added: "0.1"
|
||||
options:
|
||||
path:
|
||||
description:
|
||||
- Ansible vault File path
|
||||
type: path
|
||||
required: true
|
||||
aliases: [ name, vault ]
|
||||
password:
|
||||
description:
|
||||
- The password to access the vault.
|
||||
- Required for C(state=present).
|
||||
type: str
|
||||
required: true
|
||||
aliases: [ vault_pass ]
|
||||
key:
|
||||
description:
|
||||
- The password name to insert/replace into the vault file.
|
||||
- Required for C(state=present).
|
||||
type: str
|
||||
required: true
|
||||
value:
|
||||
description:
|
||||
- The password value to insert/replace into the vault file.
|
||||
- Required for C(state=present).
|
||||
type: str
|
||||
state:
|
||||
description:
|
||||
- Whether the password should be there or not.
|
||||
type: str
|
||||
choices: [ absent, present ]
|
||||
default: present
|
||||
replace:
|
||||
description:
|
||||
- Used with C(state=present).
|
||||
- If specified and the password already exist in vault file, it will be overwritten with value.
|
||||
- By default it will fail if password exist in vault.
|
||||
type: bool
|
||||
default: no
|
||||
aliases: [ overwrite ]
|
||||
strict:
|
||||
description:
|
||||
- Used with C(state=absent).
|
||||
- If specified to no, remove password whatever value is.
|
||||
- By default it will remove password only when key and value match specified.
|
||||
type: bool
|
||||
default: yes
|
||||
create:
|
||||
description:
|
||||
- Used with C(state=present).
|
||||
- If specified, the vault file will be created if it does not already exist.
|
||||
- By default it will fail if the vault file is missing.
|
||||
type: bool
|
||||
default: no
|
||||
backup:
|
||||
description:
|
||||
- Create a backup file including the timestamp information so you can
|
||||
get the original file back if you somehow clobbered it incorrectly.
|
||||
type: bool
|
||||
default: no
|
||||
author:
|
||||
- Johan Vaucourt (johan.vaucourt@gmail.com)
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Add a new password to a vault if the file does not exist
|
||||
putinvault:
|
||||
path: /tmp/vault.yml
|
||||
key: 'vaulted_ldap_admin_password'
|
||||
value: 'admin123'
|
||||
create: yes
|
||||
|
||||
- name: Add a password to an existing vault. If the file does not exist or key already exists, it will trig an error
|
||||
putinvault:
|
||||
path: vault.yml
|
||||
password: 'vault123'
|
||||
key: 'vaulted_ldap_reader_password'
|
||||
value: 'read123'
|
||||
|
||||
- name: Overwrite LDAP admin password
|
||||
putinvault:
|
||||
path: vault.yml
|
||||
key: 'vaulted_ldap_admin_password'
|
||||
value: 'newadmin123'
|
||||
replace: yes
|
||||
'''
|
||||
|
||||
import os
|
||||
import re
|
||||
import tempfile
|
||||
from ansible_vault import Vault
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils._text import to_bytes, to_native
|
||||
|
||||
def check_file_attrs(module, changed, message, diff):
|
||||
|
||||
file_args = module.load_file_common_arguments(module.params)
|
||||
if module.set_fs_attributes_if_different(file_args, False, diff=diff):
|
||||
|
||||
if changed:
|
||||
message += " and "
|
||||
changed = True
|
||||
message += "ownership, perms or SE linux context changed"
|
||||
|
||||
return message, changed
|
||||
|
||||
|
||||
def present(module, dest, password, key, value, replace, create, backup):
|
||||
|
||||
diff = {'before': '',
|
||||
'after': '',
|
||||
'before_header': '%s (content)' % dest,
|
||||
'after_header': '%s (content)' % dest}
|
||||
|
||||
b_dest = to_bytes(dest, errors='surrogate_or_strict')
|
||||
if not os.path.exists(b_dest):
|
||||
if not create:
|
||||
module.fail_json(rc=257, msg='Destination %s does not exist !' % dest)
|
||||
b_destpath = os.path.dirname(b_dest)
|
||||
if b_destpath and not os.path.exists(b_destpath) and not module.check_mode:
|
||||
try:
|
||||
os.makedirs(b_destpath)
|
||||
except Exception as e:
|
||||
module.fail_json(msg='Error creating %s Error code: %s Error description: %s' % (b_destpath, e[0], e[1]))
|
||||
|
||||
vault = Vault(password)
|
||||
v_data = {}
|
||||
else:
|
||||
# TODO : Manage erroneous password
|
||||
vault = Vault(password)
|
||||
with open(b_dest, 'rb') as f:
|
||||
v_data = vault.load(f.read())
|
||||
|
||||
if module._diff:
|
||||
diff['before'] = to_native(v_data)
|
||||
|
||||
msg =''
|
||||
changed = False
|
||||
|
||||
if key in v_data:
|
||||
if replace == False:
|
||||
module.warn(
|
||||
"The password already exist in vault. If you want to replace it disregarding it current value, set replace to True.")
|
||||
pass
|
||||
else:
|
||||
v_data[key] = value
|
||||
msg = 'line replaced'
|
||||
changed = True
|
||||
else:
|
||||
v_data[key] = value
|
||||
msg = 'line added'
|
||||
changed = True
|
||||
|
||||
if module._diff:
|
||||
diff['after'] = to_native(v_data)
|
||||
|
||||
backupdest = ""
|
||||
if changed and not module.check_mode:
|
||||
if backup and os.path.exists(b_dest):
|
||||
backupdest = module.backup_local(dest)
|
||||
vault.dump(v_data, open(b_dest, 'w'))
|
||||
|
||||
if module.check_mode and not os.path.exists(b_dest):
|
||||
module.exit_json(changed=changed, msg=msg, backup=backupdest, diff=diff)
|
||||
|
||||
attr_diff = {}
|
||||
msg, changed = check_file_attrs(module, changed, msg, attr_diff)
|
||||
|
||||
attr_diff['before_header'] = '%s (file attributes)' % dest
|
||||
attr_diff['after_header'] = '%s (file attributes)' % dest
|
||||
|
||||
difflist = [diff, attr_diff]
|
||||
module.exit_json(changed=changed, msg=msg, backup=backupdest, diff=difflist)
|
||||
|
||||
|
||||
def absent(module, dest, password, key, value, strict, backup):
|
||||
|
||||
b_dest = to_bytes(dest, errors='surrogate_or_strict')
|
||||
if not os.path.exists(b_dest):
|
||||
module.exit_json(changed=False, msg="file not present")
|
||||
|
||||
msg = ''
|
||||
changed = False
|
||||
|
||||
diff = {'before': '',
|
||||
'after': '',
|
||||
'before_header': '%s (content)' % dest,
|
||||
'after_header': '%s (content)' % dest}
|
||||
|
||||
# TODO : Manage erroneous password
|
||||
vault = Vault(password)
|
||||
with open(b_dest, 'rb') as f:
|
||||
v_data = vault.load(f.read())
|
||||
|
||||
if module._diff:
|
||||
diff['before'] = to_native(v_data)
|
||||
|
||||
if key in v_data:
|
||||
if strict == True:
|
||||
if v_data[key] == value:
|
||||
del v_data[key]
|
||||
changed = True
|
||||
else:
|
||||
module.fail_json(msg='value should match what is in vault with strict=true')
|
||||
else:
|
||||
del v_data[key]
|
||||
changed = True
|
||||
|
||||
if module._diff:
|
||||
diff['after'] = to_native(v_data)
|
||||
|
||||
backupdest = ""
|
||||
if changed and not module.check_mode:
|
||||
if backup:
|
||||
backupdest = module.backup_local(dest)
|
||||
vault.dump(v_data, open(b_dest, 'w'))
|
||||
|
||||
if changed:
|
||||
msg = "Password removed"
|
||||
|
||||
attr_diff = {}
|
||||
msg, changed = check_file_attrs(module, changed, msg, attr_diff)
|
||||
|
||||
attr_diff['before_header'] = '%s (file attributes)' % dest
|
||||
attr_diff['after_header'] = '%s (file attributes)' % dest
|
||||
|
||||
difflist = [diff, attr_diff]
|
||||
|
||||
module.exit_json(changed=changed, msg=msg, backup=backupdest, diff=difflist)
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
path=dict(type='path', required=True, aliases=['dest', 'destfile', 'name']),
|
||||
password=dict(type='str', required=True, aliases=['vault_pass']),
|
||||
state=dict(type='str', default='present', choices=['absent', 'present']),
|
||||
key=dict(type='str', required=True),
|
||||
value=dict(type='str'),
|
||||
replace=dict(type='bool', aliases=['overwrite'], default=False),
|
||||
strict=dict(type='bool', default=True),
|
||||
create=dict(type='bool', default=False),
|
||||
backup=dict(type='bool', default=False),
|
||||
),
|
||||
add_file_common_args=True,
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
params = module.params
|
||||
path = params['path']
|
||||
password = params['password']
|
||||
state = params['state']
|
||||
key = params['key']
|
||||
value = params['value']
|
||||
replace = params['replace']
|
||||
strict = params['strict']
|
||||
create = params['create']
|
||||
backup = params['backup']
|
||||
|
||||
b_path = to_bytes(path, errors='surrogate_or_strict')
|
||||
if os.path.isdir(b_path):
|
||||
module.fail_json(rc=256, msg='Path %s is a directory !' % path)
|
||||
|
||||
if params['state'] == 'present':
|
||||
if key is None or value is None:
|
||||
module.fail_json(msg='key and value are required with state=present')
|
||||
|
||||
present(module, path, password, key, value, replace, create, backup)
|
||||
else:
|
||||
if value is None and strict is True:
|
||||
module.fail_json(msg='value is required with strict=True')
|
||||
|
||||
absent(module, path, password, key, value, strict, backup)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
Reference in New Issue
Block a user