Hi ,

I am trying to show members of groups in Active Directory and then enter them into XML format but am having problems getting it to work.
I am new to all this stuff but have to get this to work fairly quick.

Can someone advise please.

""" Script to enumerate the members of groups in the domain"""
import sys
from win32com.client import *


# Write a file
filename = "C:\Documents and Settings\user\Desktop\AD Groups.txt"

out_file = open(filename, "w")

## The values should be the groups you wish to examine, use the group's sAMAccountName
groups = [".30_Write" , ".31_Write" , ".32_Write" , ".33_Write" , ".34_Write" , ".35_Write" , ".36_Write" ,
	".37_Write" , ".38_Write" , ".39_Write" , ".3A_Write" , ".3B_Write" , ".3C_Write" ,".3D_Write" ,
	".3E_Write" , ".3F_Write" , ".3G_Write" , ".3H_Write" , ".3I_Write" , ".3J_Write" ,".3K_Write" ,
	".3L_Write" , ".3M_Write" , ".3N_Write" , ".3O_Write" , ".3P_Write" , ".3Q_Write" , ".3R_Write" ,
	".3S_Write" , ".3T_Write" , ".3U_Write" , ".3V_Write" , ".3W_Write" , ".3X_Write" ,".3Y_Write" ,
	".3Z_Write" ,"Account Operators" ,"Administrators" ,
        "Backup Operators" , "Distributed COM Users" ,  "Domain Admins" , 
	"Enterprise Admins" ,"Guests" , "Group Policy Creator Owners" ,
        "Incoming Forest Trust Builders" , "Network Configuration Operators" ,
        "Performance Log Users" ,"Performance Monitor Users" ,
        "Pre-Windows 2000 Compatible Access" ,"Print Operators" ,"Remote Desktop Users" ,
        "Replicator" ,"Server Operators" ,"Terminal Server License Servers" ,
        "Users" ,"Windows Authorization Access Group" ,
	"...QDI_3_File_Adminis" ,"...QDI_4_File_Adminis" ,
        "...QDI_5_File_Adminis" ,"...Security_Password" ,
	"...Security_Helpdesk" ,"...Security_Desktops" ,"...Security_Audit_Read_Only" ,
        "...Security_HomeAndProfiles" ,"...Security_Desktops"]

# Select the AD attributes you wish the query to return
attribs = "name,member,objectClass,adspath,primaryGroupToken,primaryGroupID"
objConnection = Dispatch("ADODB.Connection")
objConnection.Open("Provider=ADsDSOObject")

def getDefaultDN(server=None):
    """ Get the base LDAP Naming Context, used specified server if available."""

    if server is None:
        ldap_root = GetObject('LDAP://rootDSE')
    else:
        ldap_root = GetObject('LDAP://%s/rootDSE' % server)

    ldap_loc = ldap_root.Get('defaultNamingContext')

    return ldap_loc

def getGrpInfo(name, searchRoot, category="Group"):
    """ Find the group in AD and set up a dictionary for its attributes.
    
    searchRoot is the part of the LDAP tree that you want to start searching from.
    category filters what objedts to search on.  So you could also search for a User.
    name is the account's sAMAccountName which is a unique identifier in AD.
    attribs is the list of attributes to return from the query.
    """
    strSearch = \
        "<LDAP://%s>;(&(objectCategory=%s)(sAMAccountName=%s));%s;subtree" % \
        (searchRoot, category, name, attribs)
    objRecordSet = objConnection.Execute(strSearch)[0]
    objRecord = dict()

    # Normally, we would only expect one object to be retrieved.
    if objRecordSet.RecordCount == 1:
        # Set up a dictionary with attribute/value pairs and return the dictionary.
        for f in objRecordSet.Fields:
            objRecord[f.Name] = f.Value

        #print objRecord.items()
        return objRecord
    else:
        # Group not found
        return None

def getGrpMembers(strLdap, header=""):
    """ Recursively look up a group's members.
    
    strLdap is the groups adspath attribute.
    header is used for indenting to show sub groups.
    """
    strSearch = "<%s>;;%s" % (strLdap, attribs)
    objRecordSet = objConnection.Execute(strSearch)[0]
    objRecord = dict()
    memberList = []

    # Normally, we would only expect one object to be retrieved.
    if objRecordSet.RecordCount == 1:
        for f in objRecordSet.Fields:
            objRecord[f.Name] = f.Value

        # Check to see if the group has any members
        if objRecord['member'] is not None:
            # Look up each member and get their LDAP object
            for mbr in objRecord['member']:
                objRS = objConnection.Execute("<LDAP://%s>;;name,objectClass,adspath" % mbr)[0]

                # Check to see if the member is a group.
                # If so, look up its members.
                # The Field index number corresponds to the order that you list the
                # attributes in on the LDAP query string.
                if 'group' in objRS.Fields[1].Value:
                    memberList.append("%sGroup - %s, members:" % (header, objRS.Fields[0].Value))
                    memberList.extend(getGrpMembers(objRS.Fields[2].Value, header+"   "))
                else:
                    memberList.append("%s%s" % (header, objRS.Fields[0].Value))
        
    # Return the list of results
    return sorted(memberList)

def getPrimaryGroup(searchRoot, token, header="   "):
    """ Used to look up Users whose Primary Group is set to one of the groups we're
    looking up.  This is necessary as AD uses that attribute to calculate a group's
    membership.  These type of users do not show up if you query the group's member field
    directly.
    
    searchRoot is the part of the LDAP tree that you want to start searching from.
    token is the groups primaryGroupToken.
    """
    strSearch = \
        "<LDAP://%s>;(primaryGroupID=%d);name;subtree" % \
        (searchRoot, token)
    objRecordSet = objConnection.Execute(strSearch)[0]
    memberList = []

    # Process if accounts are found.
    if objRecordSet.RecordCount > 0:
        memberList.append("Primary Group calculated:")
        objRecordSet.MoveFirst()

        while not objRecordSet.EOF:
            memberList.append("%s%s" % (header, objRecordSet.Fields[0].Value))
            objRecordSet.MoveNext()
            
    # Return the list of results
    return memberList

def main(server=None):
    """ Main program logic. """
    dn = getDefaultDN(server)
    message = []

    for grp in groups:
        objGrp = getGrpInfo(grp, dn)
        # If the group is found, process the group's membership.
        if objGrp is not None:
            message.append("\nMembers of %s:" % objGrp['name'])
            message.extend(getGrpMembers(objGrp['adspath']))
            message.extend(getPrimaryGroup(dn, objGrp['primaryGroupToken']))

            data = "\n".join(message)

            tag_list = ['group', 'home', 'hobby', 'gender']

            xml_data_list = ['<?xml version="1.0" encoding="ISO-8859-1"?>','<people>']

            for i, item in enumerate(data.split()):
                xml_data_list.append("<%s>%s</%s>" % (tag_list[i], item, tag_list[i]))

                xml_data_list.append('</AD_Groups>')

                print '\n'.join(xml_data_list)
        
           out_file.write("\n".join(message))
           out_file.close()


if __name__ == '__main__':
    if len(sys.argv) == 1:
        main()
    else:
        # If a server is given on the command line, run the script against it.
        main(sys.argv[1])

   
# Read a file and encode it into base64 format
fo = open(filename, "rb")
filecontent = fo.read()
encodedcontent = base64.b64encode(filecontent)  # base64

sender = 'email address'
reciever = 'email address'

marker = "Domain"

body ="""AD High Level Group Access."""

# Define the main headers.
part1 = """From: From Person <'email address'>
To: To Person <'email address'>
Subject: AD Group List
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary=%s
--%s
""" % (marker, marker)

# Define the message action
part2 = """Content-Type: text/plain
Content-Transfer-Encoding:8bit

%s
--%s
""" % (body,marker)

# Define the attachment section
part3 = """Content-Type: multipart/mixed; name=\"%s\"
Content-Transfer-Encoding:base64
Content-Disposition: attachment; filename=%s

%s
--%s--
""" %(filename, filename, encodedcontent, marker)
message = part1 + part2 + part3

try:
   smtpObj = smtplib.SMTP('domain')
   smtpObj.sendmail(sender, reciever, message)
   print "Successfully sent email"
except Exception:
   print "Error: unable to send email"

Edited 6 Years Ago by jonb7: incorrect post template

Code snippets are for working code only!
Place questions in the regular Python forum!

It's taken care of already, thanks!

Edited 6 Years Ago by vegaseat: n/a

This is a shortened version, i can get it to work somewhat but it fails to split each group seperatly.

# Create File
filename = "file.xml"
out_file = open(filename, "w")

## The values should be the groups you wish to examine, use the group's sAMAccountName
groups = [".32_Write" , ".34_Write"]


# Select the AD attributes you wish the query to return
attribs = "name,member,objectClass,adspath,primaryGroupToken,primaryGroupID"
objConnection = Dispatch("ADODB.Connection")
objConnection.Open("Provider=ADsDSOObject")

def getDefaultDN(server=None):
    """ Get the base LDAP Naming Context, used specified server if available."""

    if server is None:
        ldap_root = GetObject('LDAP://rootDSE')
    else:
        ldap_root = GetObject('LDAP://%s/rootDSE' % server)

    ldap_loc = ldap_root.Get('defaultNamingContext')

    return ldap_loc

def getGrpInfo(name, searchRoot, category="Group"):
    """ Find the group in AD and set up a dictionary for its attributes.
    
    searchRoot is the part of the LDAP tree that you want to start searching from.
    category filters what objedts to search on.  So you could also search for a User.
    name is the account's sAMAccountName which is a unique identifier in AD.
    attribs is the list of attributes to return from the query.
    """
    strSearch = \
        "<LDAP://%s>;(&(objectCategory=%s)(sAMAccountName=%s));%s;subtree" % \
        (searchRoot, category, name, attribs)
    objRecordSet = objConnection.Execute(strSearch)[0]
    objRecord = dict()

    # Normally, we would only expect one object to be retrieved.
    if objRecordSet.RecordCount == 1:
        # Set up a dictionary with attribute/value pairs and return the dictionary.
        for f in objRecordSet.Fields:
            objRecord[f.Name] = f.Value

        #print objRecord.items()
        return objRecord
    else:
        # Group not found
        return None

def getGrpMembers(strLdap, header=""):
    """ Recursively look up a group's members.
    
    strLdap is the groups adspath attribute.
    header is used for indenting to show sub groups.
    """
    strSearch = "<%s>;;%s" % (strLdap, attribs)
    objRecordSet = objConnection.Execute(strSearch)[0]
    objRecord = dict()
    memberList = []

    # Normally, we would only expect one object to be retrieved.
    if objRecordSet.RecordCount == 1:
        for f in objRecordSet.Fields:
            objRecord[f.Name] = f.Value

        # Check to see if the group has any members
        if objRecord['member'] is not None:
            # Look up each member and get their LDAP object
            for mbr in objRecord['member']:
                objRS = objConnection.Execute("<LDAP://%s>;;name,objectClass,adspath" % mbr)[0]

                # Check to see if the member is a group.
                # If so, look up its members.
                # The Field index number corresponds to the order that you list the
                # attributes in on the LDAP query string.
                if 'group' in objRS.Fields[1].Value:
                    memberList.append("%sGroup - %s, members:" % (header, objRS.Fields[0].Value))
                    memberList.extend(getGrpMembers(objRS.Fields[2].Value, header+"   "))
                else:
                    memberList.append("%s%s" % (header, objRS.Fields[0].Value))
        
    # Return the list of results
    return sorted(memberList)

def getPrimaryGroup(searchRoot, token, header="   "):
    """ Used to look up Users whose Primary Group is set to one of the groups we're
    looking up.  This is necessary as AD uses that attribute to calculate a group's
    membership.  These type of users do not show up if you query the group's member field
    directly.
    
    searchRoot is the part of the LDAP tree that you want to start searching from.
    token is the groups primaryGroupToken.
    """
    strSearch = \
        "<LDAP://%s>;(primaryGroupID=%d);name;subtree" % \
        (searchRoot, token)
    objRecordSet = objConnection.Execute(strSearch)[0]
    memberList = []

    # Process if accounts are found.
    if objRecordSet.RecordCount > 0:
        memberList.append("Primary Group calculated:")
        objRecordSet.MoveFirst()

        while not objRecordSet.EOF:
            memberList.append("%s%s" % (header, objRecordSet.Fields[0].Value))
            objRecordSet.MoveNext()
            
    # Return the list of results
    return memberList

def main(server=None):
    """ Main program logic. """
    dn = getDefaultDN(server)
    message = []

    for grp in groups:
        objGrp = getGrpInfo(grp, dn)
        # If the group is found, process the group's membership.
        if objGrp is not None:
            message.append("\nMembers of %s:" % objGrp['name'])
            message.extend(getGrpMembers(objGrp['adspath']))
            message.extend(getPrimaryGroup(dn, objGrp['primaryGroupToken']))
                
            data = '\n'.join(message)

            tag_list1 = ['group']
            tag_list2 = ['member']

            xml_data_list = ['<?xml version="1.0" encoding="ISO-8859-1"?>']

            xml_data_list.append('<AD_Groups>')


            for i, item in enumerate(data.split('nMembers of %s:''\n')):
                xml_data_list.append("<%s>%s</%s>" % (tag_list1[i], item, tag_list1[i]))
                xml_data_list.append("<%s>%s</%s>" % (tag_list2[i], item, tag_list2[i]))

                xml_data_list.append('</AD_Groups>')
                    
            print '\n'.join(xml_data_list)
            out_file.write("\n".join(xml_data_list))


if __name__ == '__main__':
    if len(sys.argv) == 1:
        main()
    else:
        # If a server is given on the command line, run the script against it.
        main(sys.argv[1])


out_file.close()

If you could narrow down still more, include real error message, put as attachment any needed external files, could be possible to give ideas. As is only the top gurus can read this code without live run debugging.

Could you put some prints just before error point or give super simplified input to program (for example one line file with only one expected hit, which do not come as result).

i fixed the xml piece now, below is the code
replace the section "def main(server=None):"

def main(server=None):
    """ Main program logic. """
    dn = getDefaultDN(server)

    # Data structure for the groups and their members
    groupData={}
	
    for grp in groups:
        objGrp = getGrpInfo(grp, dn)
        # If the group is found, process the group's membership.
        if objGrp is not None:
            groupName=objGrp['name']
            groupMembers=getGrpMembers(objGrp['adspath'])+getPrimaryGroup(dn, objGrp['primaryGroupToken'])
            groupData[groupName]=groupMembers
            
    # Write of the XML file		
    textOutput=[]

    # Build the xml string
    textOutput.append("<ad_xml>")
    for grp in groupData:
        textOutput.append(' <group name="%s">' % grp)
        for user in groupData[grp]:
            textOutput.append('    <user>\n       <name>%s</name>\n    </user>' % user)
        textOutput.append(' </group>')
    textOutput.append("</ad_xml>")
    xmlText="\n".join(textOutput)

    #print the xml string
    print xmlText
    
    #save the xml string to a file
    out_file = open(filename, "w")
    out_file.write(xmlText)
    out_file.close()

For anyone whom may be interested, here is the near final version of AD to XML file and creating a comparison.
I am still working on a better comparison section to detail the results better but this may help some people start with what they need.
If i finalise the whole script i will re-post.

Thank you to anyone whom has helped so far.

""" Script to enumerate the members of groups in the domain"""
import sys
from win32com.client import *
import smtplib
import base64
import time
from datetime import date, timedelta
import difflib
import filecmp


# Create todays date string 
today = time.localtime(time.time())
theDate = time.strftime("%d.%m.%y", today)


# Create File
filename = "AD_%s.xml" % theDate


# Yesterday's file to allow comparison
yesterday = date.today() - timedelta(1)
previousfile = yesterday.strftime('%d.%m.%y')
oldfile = "AD_%s.xml" % previousfile


## The values should be the groups you wish to examine, use the group's sAMAccountName
groups = ["group1" , "group2" , "group3" , "group4" , "group5"]


# Select the AD attributes you wish the query to return
attribs = "name,member,objectClass,adspath,primaryGroupToken,primaryGroupID"
objConnection = Dispatch("ADODB.Connection")
objConnection.Open("Provider=ADsDSOObject")


def getDefaultDN(server=None):
    """ Get the base LDAP Naming Context, used specified server if available."""

    if server is None:
        ldap_root = GetObject('LDAP://rootDSE')
    else:
        ldap_root = GetObject('LDAP://%s/rootDSE' % server)

    ldap_loc = ldap_root.Get('defaultNamingContext')

    return ldap_loc


def getGrpInfo(name, searchRoot, category="Group"):
    """ Find the group in AD and set up a dictionary for its attributes.
    
    searchRoot is the part of the LDAP tree that you want to start searching from.
    category filters what objedts to search on.  So you could also search for a User.
    name is the account's sAMAccountName which is a unique identifier in AD.
    attribs is the list of attributes to return from the query.
    """
    strSearch = \
        "<LDAP://%s>;(&(objectCategory=%s)(sAMAccountName=%s));%s;subtree" % \
        (searchRoot, category, name, attribs)
    objRecordSet = objConnection.Execute(strSearch)[0]
    objRecord = dict()

    # Normally, we would only expect one object to be retrieved.
    if objRecordSet.RecordCount == 1:
        # Set up a dictionary with attribute/value pairs and return the dictionary.
        for f in objRecordSet.Fields:
            objRecord[f.Name] = f.Value

        #print objRecord.items()
        return objRecord
    else:
        # Group not found
        return None


def getGrpMembers(strLdap, header=""):
    """ Recursively look up a group's members.
    
    strLdap is the groups adspath attribute.
    header is used for indenting to show sub groups.
    """
    strSearch = "<%s>;;%s" % (strLdap, attribs)
    objRecordSet = objConnection.Execute(strSearch)[0]
    objRecord = dict()
    memberList = []

    # Normally, we would only expect one object to be retrieved.
    if objRecordSet.RecordCount == 1:
        for f in objRecordSet.Fields:
            objRecord[f.Name] = f.Value

        # Check to see if the group has any members
        if objRecord['member'] is not None:
            # Look up each member and get their LDAP object
            for mbr in objRecord['member']:
                objRS = objConnection.Execute("<LDAP://%s>;;name,objectClass,adspath" % mbr)[0]

                # Check to see if the member is a group.
                # If so, look up its members.
                # The Field index number corresponds to the order that you list the
                # attributes in on the LDAP query string.
                if 'group' in objRS.Fields[1].Value:
                    memberList.append("%sGroup - %s, members:" % (header, objRS.Fields[0].Value))
                    memberList.extend(getGrpMembers(objRS.Fields[2].Value, header+"   "))
                else:
                    memberList.append("%s%s" % (header, objRS.Fields[0].Value))
        
    # Return the list of results
    return sorted(memberList)


def getPrimaryGroup(searchRoot, token, header="   "):
    """ Used to look up Users whose Primary Group is set to one of the groups we're
    looking up.  This is necessary as AD uses that attribute to calculate a group's
    membership.  These type of users do not show up if you query the group's member field
    directly.
    
    searchRoot is the part of the LDAP tree that you want to start searching from.
    token is the groups primaryGroupToken.
    """
    strSearch = \
        "<LDAP://%s>;(primaryGroupID=%d);name;subtree" % \
        (searchRoot, token)
    objRecordSet = objConnection.Execute(strSearch)[0]
    memberList = []

    # Process if accounts are found.
    if objRecordSet.RecordCount > 0:
        memberList.append("Primary Group calculated:")
        objRecordSet.MoveFirst()

        while not objRecordSet.EOF:
            memberList.append("%s%s" % (header, objRecordSet.Fields[0].Value))
            objRecordSet.MoveNext()
            
    # Return the list of results
    return memberList

	
def main(server=None):
    """ Main program logic. """
    dn = getDefaultDN(server)

    # Data structure for the groups and their members
    groupData={}
	
    for grp in groups:
        objGrp = getGrpInfo(grp, dn)
        # If the group is found, process the group's membership.
        if objGrp is not None:
            groupName=objGrp['name']
            groupMembers=getGrpMembers(objGrp['adspath'])+getPrimaryGroup(dn, objGrp['primaryGroupToken'])
            groupData[groupName]=groupMembers

            
    # Write of the XML file		
    textOutput=[]

    # Build the xml string
    textOutput.append("<ad_xml>")
    for grp in groupData:
        textOutput.append(' <group name="%s">' % grp)
        for user in groupData[grp]:
            textOutput.append('    <user>\n       <name>%s</name>\n    </user>' % user)
        textOutput.append(' </group>')
    textOutput.append("</ad_xml>")
    xmlText="\n".join(textOutput)

    # Print the xml string
    print xmlText

    
    #save the xml string to a file
    out_file = open(filename, "w")
    out_file.write(xmlText)
    out_file.close()


    # read files and remove whitelines
    f1 = [line.strip() for line in open(oldfile).readlines()]
    f2 = [line.strip() for line in open(filename).readlines()]
    
    for x in f2:
        if x in f1:
            pass
        else:
            print "New user: %s" % x

    for x in f1:
        if x in f2:
            pass
        else:
            print "User removed: %s" % x


if __name__ == '__main__':
    if len(sys.argv) == 1:
        main()
    else:
        # If a server is given on the command line, run the script against it.
        main(sys.argv[1])


# pick two files you want to compare ...
file1 = (oldfile)
file2 = (filename)

# checks file comparison
if filecmp.cmp(file1, file2):
    print "Files %s and %s are identical" % (file1, file2)
else:
    print "Files %s and %s differ" % (file1, file2)


# Read a file and encode it into base64 format
fo = open(filename, "rb")
filecontent = fo.read()
encodedcontent = base64.b64encode(filecontent)  # base64

sender = 'user1@example.com'
reciever = 'user2@example.com'

marker = "somedomain"

body ="""AD High Level Group Access."""

# Define the main headers.
part1 = """From: From Person <user1@example.com>
To: To Person <user2@example.com>
Subject: AD Group List
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary=%s
--%s
""" % (marker, marker)

# Define the message action
part2 = """Content-Type: text/plain
Content-Transfer-Encoding:8bit

%s
--%s
""" % (body,marker)

# Define the attachment section
part3 = """Content-Type: multipart/mixed; name=\"%s\"
Content-Transfer-Encoding:base64
Content-Disposition: attachment; filename=%s

%s
--%s--
""" %(filename, filename, encodedcontent, marker)
message = part1 + part2 + part3

try:
   smtpObj = smtplib.SMTP('somedomain')
   smtpObj.sendmail(sender, reciever, message)
   print "Successfully sent email"
except Exception:
   print "Error: unable to send email"
This question has already been answered. Start a new discussion instead.