#!/usr/bin/python3 import collections import os import re import ldap3 {% set cluster_password = lookup('passwordstore', "cluster/"+cluster.name, returnall=true) | from_yaml %} realm = '{{ hostvars[inventory_hostname]["sync-ldap"] }}' ldap_host = '{{ domain }}' ldap_user = '{{ cluster_password.ldap_user }}' ldap_pass = '{{ cluster_password.ldap_pass }}' ldap_base = '{{ domain | split(".") | map("regex_replace", "^", "dc=") | join(",") }}' # build LDAP query for users filters = [ '(objectClass=user)', # only users '(objectCategory=person)', # that are people '(schacHomeOrganization=*)', # presumably '(!(userAccountControl:1.2.840.113556.1.4.803:=2))' # with enabled accounts ] # run query server = ldap3.Server(ldap_host, use_ssl=True) ldap = ldap3.Connection(server, ldap_user, ldap_pass, auto_bind=True) ldap.search(ldap_base, f'(&{"".join(filters)})', # conjuction (&(…)(…)(…)) of queries attributes=['userPrincipalName', 'givenName', 'sn', 'mail', 'memberOf']) # build user and group dicts all_users = {} all_groups = collections.defaultdict(set) for e in ldap.entries: user = f'{e.userPrincipalName.value}@{realm}' all_users[user] = { k: e[k].value for k in e.entry_attributes } for group in e.memberOf: if m := re.match(r'^CN=([^,]*)', group.replace('\\,', '-')): group = re.sub(r'[^A-Za-z0-9_.-]', '-', m[1]) all_groups[group].add(user) with open('/etc/pve/user.cfg.new', 'w') as f: # user:{username}@{realm}:1:0:{name}:{surname}:{mail}:AD sync:: for user, info in sorted(all_users.items()): print(f'user:{user}:1:0:{info["givenName"]}:{info["sn"]}:{info["mail"]}:AD sync::', file=f) # group:{name}:{users}:AD sync: print(f'group:ALL:{",".join(sorted(all_users))}:AD sync:', file=f) for group, users in all_groups.items(): print(f'group:{group}:{",".join(sorted(users))}:AD sync:', file=f) # keep everything not added by us for line in open('/etc/pve/user.cfg'): if not re.match('^(user|group):.*:AD sync:', line): print(line, end='', file=f) os.rename('/etc/pve/user.cfg.new', '/etc/pve/user.cfg')