An issue with Google Apps Directory Sync and how to fix it
As you know, Google Apps Directory Sync is a great tool to synchronize your MS Active Directory accounts with your Google Apps (for Business or Education). It can help you map your user account structure into Google including the users, groups, OUs... You simply set up the proper search rules for users and groups of your AD architecture. The tool works great except for one thing:
When you add a new user into a group in AD, it will add that user into the counter part of that group in Google. But, when you move that user out of the group in AD, the google account will not be removed from the google groups. What google only does is stop allowing the user to post to that group.
After looking for the solution for a while, I came up with one hack that works:
1. Search for all users in AD groups which you want to sync to Google:
http://www.dangtrinh.com/2016/07/get-all-ms-active-directory-group.html
2. Search for all users in Google groups:
http://www.dangtrinh.com/2016/07/get-google-group-members-using-gam-and.html
3. Compare two lists and delete Google Group members that does not appears in ADs counter part.
Here is a working script:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/env python | |
import shlex, subprocess | |
import sys | |
# ad_utils.py: http://www.dangtrinh.com/2016/07/get-all-ms-active-directory-group.html | |
import ad_utils | |
GADMIN_ACCOUNT = '<your google apps administrator email>' | |
GAM_PATH = '/path/to/your/gam.py' | |
GOOGLE_GROUPS = { | |
'<group name>': 'group email', | |
... | |
} | |
def get_group_members(group_email, gam_path=GAM_PATH): | |
cmd = 'python %s print group-members group %s' % (gam_path, group_email) | |
if sys.platform == 'win32': | |
cmd = 'gam print group-members group %s' % (group_email) | |
args = shlex.split(cmd) | |
proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) | |
out, err = proc.communicate() | |
members = [] | |
if proc.returncode == 0: | |
if out: | |
out = out.split('\n') | |
for i in range(1, len(out)): | |
row = out[i].split(',') | |
if len(row) >= 3: | |
if row[2].lower() != GADMIN_ACCOUNT: # exclude gadmin account | |
members.append(row[2].lower()) | |
return members | |
def del_group_member(group_email, member_email, gam_path=GAM_PATH): | |
cmd = 'python %s update group %s remove user %s' % (gam_path, group_email, member_email) | |
if sys.platform == 'win32': | |
cmd = 'gam update group %s remove user %s' % (group_email, member_email) | |
args = shlex.split(cmd) | |
proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) | |
out, err = proc.communicate() | |
return proc.returncode, out, err | |
def sync_ad_gapps_group_members(group_name, group_email, ad_conn, dry_run=False): | |
ad_members = ad_utils.get_group_members(group_name, ad_conn) | |
if ad_members: | |
googlegroup_members = get_group_members(group_email) | |
for guser in googlegroup_members: | |
if guser not in ad_members: | |
print "=== %s not active in AD" % guser | |
if dry_run: | |
pass | |
else: | |
del_group_member(group_email, guser) | |
def sync_ad_gapps_groups(dry_run=False): | |
ad_conn, ad_result = ad_utils.ad_auth() | |
if ad_result: | |
for gname, gemail in GOOGLE_GROUPS.iteritems(): | |
print "\n+++ Syncing group %s" % gname | |
sync_ad_gapps_group_members(gname, gemail, ad_conn, dry_run) | |
else: | |
print "Fail!" | |
if __name__ == "__main__": | |
dry_run = False | |
if len(sys.argv) > 1: | |
dry_run = bool(sys.argv[1]) | |
if dry_run: | |
print "=== Running Google Apps Groups sync in dry-run mode" | |
print "==== Dry running: %s" % dry_run | |
sync_ad_gapps_groups(dry_run) |
Usage:
$ python google_groups_ad_sync.py [dry_run]
You can set up a scheduled task that runs this script after GADS.
alternative link download