It’s obviously a good idea to monitor certain Active Directory groups for membership changes. An example of a group that should be monitored is the Domain Admins group. I was looking for an easy way to monitor groups for changes and to send an email alert when something has changed. I’ve written a Python script to do this. I’ve opted to hash the group members and to store this value along with the group name in a SQLite database. I chose to compare hash values to speed up the operation and to not have to worry about comparing individual group members each time. I won’t go into the details on how to set up or update SQLite databases as there are many good resources available that describe this process.
The script I wrote uses pyad to connect to Active Directory, you can click here to read up on the pyad libraby. I’m also using a template file to generate the body of the email message, I’ll post a screenshot of the simple template after the code.
For this script to be effective at alerting for changes you should set up a scheduled task to run the script frequently.
from pyad import adgroup import hashlib import sqlite3 import smtplib from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from string import Template #get current hash values of monitored groups def getHash(adg): mlist =  for x,y in adg: group = adgroup.ADGroup.from_dn("CN="+x+y+"DC=testdom,DC=com") gg = group.get_members() fhash = hashlib.sha256(repr(gg).encode('utf-8')).hexdigest() mlist.append((x,fhash)) return mlist #get stored hash values and compare to newly retrieved values def compVal(newVal): msqll =  conn = sqlite3.connect('ad.sqlite') conn.row_factory = sqlite3.Row cur = conn.execute('SELECT Item, HashVal FROM adhash') for row in cur: msqll.append((row['Item'],row['HashVal'])) conn.close() mydiff = set(newVal) - set(msqll) return mydiff #read email message template def readTemp(filename): with open(filename, 'r', encoding='utf-8') as temp: tempContent = temp.read() return Template(tempContent) #send an email alert with a list of the groups that have been changed def sendAlert(myChanges): msgTemp = readTemp('Temp.txt') s = smtplib.SMTP(host='172.30.16.15', port=25) msg = MIMEMultipart() message = msgTemp.substitute(ChGroups=myChanges) msg['From'] = 'email@example.com' msg['To'] = 'firstname.lastname@example.org' msg['Subject'] = 'Active Directory Change Alert' msg.attach(MIMEText(message, 'plain')) s.send_message(msg) s.quit() groupslist = [("TestSec",",CN=Users,"),("Domain Admins",",CN=Users,"),("MonMe",",OU=SecGroup,")] #include name of the group and all associated OUs newq = getHash(groupslist) statq = compVal(newq) #If changes are detected an email will be sent with the list of groups that were changed if statq: mlist= for x,y in statq: mlist.append(x) adchgstr = ', '.join(mlist) sendAlert(adchgstr)
Here is a screenshot of the simple email template text file:
Here is a screenshot of the generated email: