Wednesday, May 03, 2006

Finding and Removing Empty Distribution Lists/Groups via a script

Somebody asked about this one last week while there are some valid reasons for having empty distribution lists usually they are things that can stick around because no-one's quite sure if they should be deleted. Writing a script to find and delete empty distribution lists is not too hard although I have put a bunch of safeguards in this script to stop you deleting any groups that you may not want to. Note this script doesn’t differentiate between a distribution group and a security group it just looks for any groups that are mail enabled that have no members.

The script first does a search of AD for all mail enabled groups in active directory. It then connects to each group and starts going though its members. If there are more the 10 users in the group it stops counting. This wasn’t really a script to document the members in groups as this can get quite complicated when you consider nesting groups. If you’re after such a script Richard Muller has already written a great script to do this see http://www.rlmueller.net/. After a group is identified as empty the script will then ask if you wish to delete this group. This delete is irreversible so you need to make sure you don’t want to use this group and you have no security associations with it.

The script has two modes it can run in if you just run it with no command line parameters it will just display the mail enabled groups in Active Directory with the member counts (up to 10) for each group. Running the script with the remove switch eg cscript emptdist.vbs remove will run the script in remove mode which means it will prompt you to delete any groups that are found to have no members.

I’ve put a downloadable copy of this script here the script itself look like.


if wscript.arguments.length = 0 then
wscript.echo "Display Mode"
else
if lcase(wscript.arguments(0)) = "remove" then
mode = "remove"
wscript.echo "Remove Mode"
else
wscript.echo "Display Mode"
end if
end if
set conn = createobject("ADODB.Connection")
set com = createobject("ADODB.Command")
Set iAdRootDSE = GetObject("LDAP://RootDSE")
strDefaultNamingContext = iAdRootDSE.Get("defaultNamingContext")
Conn.Provider = "ADsDSOObject"
Conn.Open "ADs Provider"
GALQueryFilter = "(&(mailnickname=*)(|(objectCategory=group)))"
strQuery = "<LDAP://" & strDefaultNamingContext & ">;" & GALQueryFilter & ";distinguishedName,displayname,legacyExchangeDN,homemdb;subtree"
Com.ActiveConnection = Conn
Com.CommandText = strQuery
Set Rs = Com.Execute
wscript.echo "# Memebers GroupName"
wscript.echo
while not rs.eof
set objgroup = getobject("LDAP://" & replace(rs.fields("distinguishedName"),"/","\/"))

numcheck = 0
for each member in objgroup.members
numcheck = numcheck + 1
if numcheck = 10 then exit for
next
select case numcheck
case 0 wscript.echo "Empty" & " " & rs.fields("displayname")
if mode = "remove" then
contname = replace(objgroup.distinguishedName,"CN=" & objgroup.cn & ",","")
wscript.echo
wscript.echo "The Group " & rs.fields("displayname") & " in the " & contname & "
container is Empty"
WScript.StdOut.WriteLine "Do you wish to delete this List Press Y to Delete
(This is a irreversible operation)"
ans = WScript.StdIn.ReadLine
if lcase(ans) = "y" then
set objcont = getobject("LDAP://" & replace(contname,"/","\/"))
objcont.delete "group", "CN=" & replace(objgroup.cn,"/","\/")
objcont.setinfo
Wscript.echo "Deleted Group " & objgroup.displayname
else
wscript.echo "Skipping"
end if
wscript.echo
end if
case 10 wscript.echo "10+" & " " & rs.fields("displayname")
case else wscript.echo numcheck & " " & rs.fields("displayname")
end select
rs.movenext
wend

6 comments:

Briam said...

This is perfect, it will go a long way in helping us clean up years of DL neglect.

Thanks!!

Tillman Smoot said...

This is great. However, can it be modified to show the smtp address and distinguished name to a text file while it runs?

Glen said...

Sure the recordset already contains the DN you just need to change the write line statement to include the DN eg rs.fields("distinguishedName") . To include the mail address you need to modify the query so the mail address in included in the recordset eg just add the mail property to the following line

GALQueryFilter = "(&(mailnickname=*)(|(objectCategory=group)))"
strQuery = ">LDAP://" & strDefaultNamingContext & "<" & GALQueryFilter & ";distinguishedName,mail,displayname,legacyExchangeDN,homemdb;subtree"

Then simularly wirte the rs.fields("mail") property out.

Wayne said...

To return over 1000 groups edit the script like so:

Com.ActiveConnection = Conn
Com.CommandText = strQuery
Com.Properties("SearchScope") = 2 ' we want to search everything
Com.Properties("Page Size") = 500 ' and we want our records in lots of 500 (must be < query limit)
Set Rs = Com.Execute

Glen said...

Thanks Wayne I've updated the download great work.

Cheers
Glen

Anonymous said...

Hi,

I'm new to scripting, so any help would be greatly appreciated.
Is there anyway we alter the script to generate an excel doc with all the distro lists and their # of users attached?
I tried to change the script myself, but i'm not experienced enough to do so.

Thank you!