From e8f9f48bfd1973c955979b5a8a521b91c09c7305 Mon Sep 17 00:00:00 2001 From: "Dustin C. Hatch" Date: Tue, 24 Jul 2018 21:01:44 -0500 Subject: [PATCH] roles/winbind: Actually perform domain join --- roles/winbind/defaults/main.yml | 3 + roles/winbind/library/ads_member | 104 +++++++++++++++++++++++++++++++ roles/winbind/tasks/main.yml | 6 ++ 3 files changed, 113 insertions(+) create mode 100644 roles/winbind/library/ads_member diff --git a/roles/winbind/defaults/main.yml b/roles/winbind/defaults/main.yml index 9b6b93e..92985c2 100644 --- a/roles/winbind/defaults/main.yml +++ b/roles/winbind/defaults/main.yml @@ -4,3 +4,6 @@ winbind_use_default_domain: true winbind_offline_login: true winbind_kerberos_method: secrets and keytab winbind_refresh_tickets: false + +winbind_join_username: '' +winbind_join_password: '' diff --git a/roles/winbind/library/ads_member b/roles/winbind/library/ads_member new file mode 100644 index 0000000..e312b2a --- /dev/null +++ b/roles/winbind/library/ads_member @@ -0,0 +1,104 @@ +#!/usr/bin/python +import os +import subprocess + + +class JoinFailed(Exception): + pass + + +def _make_env(): + env = os.environ.copy() + for k in list(env.keys()): + if k == 'LANG' or k.startswith('LC_'): + del env[k] + env['LANG'] = 'en_US.UTF-8' + return env + + +def is_domain_member(): + cmd = ['net', 'ads', 'status', '-P'] + with open(os.devnull, 'w+') as null: + p = subprocess.Popen(cmd, stdin=null, stdout=null, stderr=null) + return p.wait() == 0 + + +def join_domain(username, password): + cmd = ['net', 'ads', 'join', '-U', username] + p = subprocess.Popen( + cmd, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + env=_make_env(), + ) + output = p.communicate(password.encode('utf-8')) + if p.wait() != 0: + raise JoinFailed(output.decode('utf-8')) + + +def leave_domain(username, password): + cmd = ['net', 'ads', 'leave', '-U', username] + p = subprocess.Popen( + cmd, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + env=_make_env(), + ) + output = p.communicate(password.encode('utf-8')) + if p.wait() != 0: + raise JoinFailed(output.decode('utf-8')) + + +def main(): + module = AnsibleModule( + argument_spec=dict( + username=dict( + required=True, + ), + password=dict( + required=True, + no_log=True, + ), + state=dict( + choices=[ + 'joined', + 'unjoined', + ], + default='joined', + ) + ), + supports_check_mode=True, + ) + + username = module.params['username'] + password = module.params['password'] + state = module.params['state'] + + changed = False + if is_domain_member(): + if state == 'unjoined': + changed = True + if not module.check_mode: + if not password: + module.fail_json(msg='Need password to leave domain') + try: + leave_domain(username, password) + except JoinFailed as e: + module.fail_json(message=e.args[0]) + elif state == 'joined': + changed = True + if not module.check_mode: + if not password: + module.fail_json(msg='Need password to join domain') + try: + join_domain(username, password) + except JoinFailed as e: + module.fail_json(message=e.args[0]) + + module.exit_json(changed=changed) + + +from ansible.module_utils.basic import * +main() diff --git a/roles/winbind/tasks/main.yml b/roles/winbind/tasks/main.yml index 1f4f9ea..a103807 100644 --- a/roles/winbind/tasks/main.yml +++ b/roles/winbind/tasks/main.yml @@ -36,3 +36,9 @@ template: src=default-realm.krb5.conf.j2 dest=/etc/krb5.conf.d/default-realm.conf + +- name: ensure machine is a member of the domain + ads_member: + username: '{{ winbind_join_username }}' + password: '{{ winbind_join_password }}' + state: joined