Recursive Distribution List Script

Distribution list cleanup/management is an ongoing concern for any Exchange environment.

I am currently assisting a customer who has over 1000 distribution groups and only 400 employees. To assist them in the cleanup I focused on the groups with a low amount of members. For example I can use a simple one liner to find groups with 0 members in them:

$dls= get-distributiongroup -resultsize unlimited
$dls |? {!(get-distributiongroupmember $_)}

Going down this route I wanted a script that would take all the groups in an environment and provide a report of groups with zero members, under 5, but greater than 0 and then any group over 5? What if I needed to get all group members as well? That’s a lot for one script…. So let’s just tack on emailing these reports to someone in IT.

How can this be accomplished…. easily? Well, I have a highly modified script that I borrowed from Christopher Law over at his blog. I used his as the core for getting group members and added loops so I can use it for all groups in Exchange. Here is my code now:

       Recursive Group Membership Script
       Uses Get-DistributionGroupMember cmdlet without having to install Quest tools, only EXCH Snapin  
       MODIFIED to handle a loop as well as provide a count of group members
       Version                  : 1.3
       Change Log               : 
       Author                   : Damian Scoles
       Blog                     :
       Disclaimer               : You are on your own.  This was not written by, support by, or endorsed by Microsoft.
       Code borrowed            : Christopher Lee
       None. You cannot pipe objects to this script.

# Install Exchange Snapin
Add-PSSnapin Microsoft.Exchange.Management.PowerShell.E2010 -ErrorAction SilentlyContinue; #2010
# Create CSV file for groups with under 5 members
$row = "Distribution List," + "Users"
Add-Content  c:\script\LowMemberDistributionGroups.csv $rows
# Create CSV file for groups with over 5 members
$row = "Distribution List," + "Users"
Add-Content  c:\script\DistributionGroups.csv $rows
# Create CSV file for empty groups
$row = "Distribution List," + "User"
Add-Content  c:\script\EmptyGroups.csv $rows
# Create CSV file for all user names
$row = "Distribution List," + "User"
Add-Content c:\script\AllUsersInDls.csv $rowline
# Get a list of all distribution lists
$dl = get-distributiongroup -resultsize unlimited
foreach ($line in $dl) {
    $count = 0
 # Find empty groups first
    $empty = Get-DistributionGroupMember -identity $line.displayname
    if ($empty -eq $null) {
                $rowline = "$dl," + "$count"
                Add-Content c:\script\EmptyGroups.csv $rowline
  # Process the rest of the groups with over 1 member
  # Set Variables:
    $group = $line.displayname
    $members = New-Object System.Collections.ArrayList
  # Create the Function
    function getMembership($group) {
        $searchGroup = Get-DistributionGroupMember $group -ResultSize Unlimited -erroraction silentlycontinue
        foreach ($member in $searchGroup) {
            if ($member.RecipientTypeDetails-match "Group" -and $member.DisplayName -ne "") {
            else {
                if ($member.DisplayName -ne "") {
                    if (! $members.Contains($member.DisplayName) ) {
                        $members.Add($member.DisplayName) >$null
  # Run the function
    $list = $members.GetEnumerator() | sort-object

# add all users to spreadsheet
    foreach ($line2 in $list) {
                $name = $line2
                $dl = $line.displayname
                $rowline = "$dl," + "$name"
                Add-Content c:\script\AllUsersInDls.csv $rowline
                $count = $count + 1
    if ($count -lt 6) {
                $rowline = "$dl," + "$count"
                Add-Content c:\script\LowMemberDistributionGroups.csv $rowline
    } Else {
                $rowline = "$dl," + "$count"
                Add-Content c:\script\DistributionGroups.csv $rowline
# Email reports to IT
[string]$ToAddress = "IT Personnel Email Address"
[string]$FromAddress = "Reporting Email Address"
$Body = "This email contains 4 reports.  One for Distribution Lists with no members, one for Distribution Lists that have between 1 and 5 members, Distribution Lists with more than 5 members and membership for all groups"
$attachments = "c:\script\DistributionGroups.csv","c:\script\LowMemberDistributionGroups.csv","c:\script\AllUsersInDls.csv","c:\script\EmptyGroups.csv"
send-mailmessage -to $ToAddress -from $FromAddress -subject "Distribution Group Member Reports" -smtpserver <SMTP Relay Server> -attachments $attachments -body $body

Here is how the script is broken out:

  • Lines 25-39 get all the CSV files ready for the email to be sent out.
  • Line 42 gets all the Distribution Groups and stores then in a variable
  • Lines 47-52 checks to see if the group has no members and writes that to a file if it does
  • Lines 60-79 find the group membership recursively
  • Lines 81-97 Sort the groups by size, if 1-5 members write it to one file, if greater than 5 write it to another. Also add member list to another file.
  • Lines 99-104 Email IT all 4 reports for later analysis.

That’s it. A relatively simple script to create these useful reports. Download it here. Rename it to .PS1 to run it on your servers.


4 thoughts on “Recursive Distribution List Script

  1. The script failed due to call depth overflow. The call depth reached 1001 and the maximum is 1000.
    At line:0 char:0

    What’d I do wrong?

    • Sounds like you may have pasted the script in wrong? Usually this comes from an endless loop. I’ve run this in a couple of test environments and one production environment without seeing this issue. Try copying the script again and giving it another go. If it doesn’t work, can you post the complete error message?

  2. Error “The call depth reached 1001 and the maximum is 1000.” is thrown when DL Loop is identified. This script only works when there is no looping detected.

    To Test the script to reproduce Call depth error Assume we have 2 Groups:
    Group1 is a member of Group2. Group2 is then a member of Group1.

    You may want to use hash tables to fix the issue with Call depth.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s