Initial commit

This commit is contained in:
yo
2020-08-06 23:18:48 +02:00
commit 630827e058
4 changed files with 617 additions and 0 deletions

304
modules/putinvault.py Normal file
View 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()