Distribution Groups – Cleanup with PowerShell

I recently had a client who wanted me to find an automated method for finding and hiding/disabling distribution groups that are essentially defunct. The project was part of a larger goal to cleanup AD and Exchange and keep it as automated as possible.

Criteria for the goal:

  • Any group that had not received email in 6 months is hidden.
  • Any group that had not received email in 12 months is disabled.
  • When a group is hidden or disabled, an email goes to the manger.
  • IT is notified when the group id disabled to determine if its OK to delete.
  • Run the script monthly


As with any script of this type, I wrote it in a test lab that had multiple Exchange 2010 Transport servers and over a thousand groups (due to the 1000 item limit in some scripts. In order to test this script without waiting for 12 months, I had to generate some messaging logs. See my previous article on how to do this. Once these logs were in place I could perform my testing. In order to track activity or inactivity for a group, I am using the attribute ‘CustomAttribute10’ to keep track of

Here is the script, that has been test and provided to you as-is.

The below section of code is simply an explanation of what the script is about as well as some variable definition for later use.

<#
.SYNOPSIS  
	Distribution List Cleanup Script
 
.DESCRIPTION  
	This script will cleanup all scripts that have not been used within a certain timeframe.
 
.NOTES  
    Version						: 1.6
    Date Created				: 11/12/2013
    Change Log					: 1.6 - Fixed email notifications
								: 1.5 - Added support for multiple transport servers
								: 1.4 - Finalized single transport server solution
								: 1.3 - Converting from CSV to array variables and AD attribute
								: 1.2 - Email notifications tested
								: 1.1 - Store all data in CSV files
								: 1.0 - Script first set up
    Wish list                   : HTML Report to IT?
    Rights Required             : Local admin on server
    Sched Task Req'd            : No
    Exchange Version            : 2010
    Author                      : Damian Scoles
    Dedicated Blog				: https://justaucguy.wordpress.com/
    Disclaimer					: You are on your own.  This was not written by, support by, or endorsed by Microsoft.
    Code stolen from:			: Multiple sources to be listed later  

.EXAMPLE
                .\DistributionListCleanup.ps1
		To be run once per month as a recurring task
 
.INPUTS
                None. You cannot pipe objects to this script.
#>

# Global variable section

# Testing Dates
# $onemonth = ((get-date).addmonths(-13))
# $current = ((get-date).addmonths(-12))

# Production Dates
$current = get-date
$onemonth = ((get-date).addmonths(-1))

# Arrays
$activegroups2 = @()
$activegroups = @()
$inactivegroups = @()
$allgroups = @()
$smtp = @()

# Load AD Module for PowerShell
import-module activedirectory

Let’s take a look at the rest of the script, section by section.

# Get a list of the active groups
$servers = get-transportserver
foreach ($name in $servers) {
     $activegroups2 += (Get-MessageTrackingLog -Server $name.name -EventId Expand -ResultSize Unlimited -start $onemonth -end $current | Sort-Object RelatedRecipientAddress | Group-Object RelatedRecipientAddress | Sort-Object Name | select-object name)
}
$activegroups2 = $activegroups2 | sort-object name | group-object name
foreach ($line in $activegroups2) {
	$activegroups += $line.name
}

This part of the script stores information about groups that have had email sent to them in the past month. Information from this part of the script is stored in an array variable called $activegroups. We’ll store this information to be used later in the script.

# Get a list of all groups
$allgroups2 = get-distributiongroup -resultsize unlimited | Select-Object -Property @{Label="Name";Expression={$_.PrimarySmtpAddress}}
foreach ($line in $allgroups2) {
	$allgroups += $line.name
}

The $AllGroups variable gets a list of the distribution groups in Active Directory

# Find inactive groups by comparing active groups to all groups
$InactiveGroups2 = Compare-Object $activegroups $allgroups
foreach ($line in $inactivegroups2) {
	$smtp2=$line.inputobject
	$address=$smtp2.local+"@"+$smtp2.domain
	$inactivegroups += $address

}

This section compares the two sets of data that was gathered – all distribution groups ($Allgroups) with the active distribution groups ($ActiveGroups). The differences are stored in a variable called $Inactivegroups which holds information about groups that were not active this past month.

# Set custom attribute 10 for active groups to 0
foreach ($line in $ActiveGroups){
 	set-distributiongroup -identity $line -CustomAttribute10 0 -warningaction silentlycontinue
}

This section resets the CustomAttribute10 value to 0 (the one we are using to track how many months a group has been inactive for. So 0 is active and 1-whatever means inactive.

# Set custom attribute 10 for inactive groups - increase by 1
# Hide or disable group
foreach ($line in $InactiveGroups){
		[string]$email = $line
		[int]$number = (get-distributiongroup -identity $email).CustomAttribute10
		$number += 1
		set-distributiongroup -identity $email -CustomAttribute10 $number 
		if ($number -eq 6) {
			$notes = "$current - Hidden from address list due to inactive use."
			Set-Group -identity $email -notes $notes
			Set-DistributionGroup -identity $email -HiddenFromAddressListsEnabled $true
			# Email manager - group hidden
			mail-managerhidden $email
		}
		if ($number -eq 12) {
			# Email manager and IT of group removal
			mail-managerdisabled $email
			emailIT-Groupremoval $email
			# Disable the group
			$notes = "$current - No longer Mail Enabled due to inactive use."
			Set-Group -identity $email -notes $notes
			Disable-DistributionGroup -identity $email -Confirm:$false
		}
}

The above section is a bit complex, but what it essentially does i go through each group deemed inactive and see if it has been inactive for 6 or 12 months. If it has been inactive for 6 months (customerattribute10 = 6), then the group is hidden and an email is sent to the manager of the group through a function called ‘mail-managerhidden’. If the group has been inactive 12 months (customerattribute10 = 12), then the group is disabled and an email is sent to the IT group through a function called ’emailIT-Groupremoval’. The functions are listed below and are custom coded for this script.

function mail-managerhidden ($groupsmtpaddress) {
	$manager = ((get-distributiongroup $groupsmtpaddress).managedby)
	$manager | foreach {
		$mgr = $_.name
		$smtp4 = (get-mailbox $mgr).emailaddresses
		$smtp4 | foreach {
			$smtp3 = $_.smtpaddress
			$smtp3
		}
		$smtp += @($smtp3)
	}
	$DLName =  (get-distributiongroup $groupsmtpaddress).displayname
	[string] $body = "<strong>NOTIFICATION</strong><BR><BR>As a part of regular maintanence, IT has decided to monitor the usage of Distribution # Lists.<BR><BR>This email is a notification that an Email Distribution List that you manage has been inactive for 6 months. Because of this level of inactivity, the group has been hidden from the Global Address List. Please check this list to see if it is still valid or not.<BR><BR>Please send an email to dscoles@testing.local if the list is no longer needed.   Thanks for you assistance with this matter."
	foreach ($line in $smtp) {
   	     $messageParameters = @{
		Subject = "Distribution List Manager Alert - Inactive Distribution List - $DLName"
		Body = $body
		From = "tuser01@dsl4.local"
		To = $line
		SmtpServer = "172.31.122.159"
             }
                Send-MailMessage @messageParameters –BodyAsHtml
	}
}

In the above code section, the first this we need to do is get the manager list for each distribution group. Because of the way the managers are stored we need to parse each one to get the correct email address for the manager. After we get the manager, we format the message body, subject, who it goes to and who it is from. Then we send the message.

function mail-managerdisabled ($groupsmtpaddress) {
	$manager = ((get-distributiongroup $groupsmtpaddress).managedby)
	$manager | foreach {
		$mgr = $_.name
		$smtp2 = (get-mailbox $mgr).emailaddresses
		$smtp2 | foreach {
			$smtp3 = $_.smtpaddress
		}
		$smtp += @($smtp3)
	}
	$DLName =  (get-distributiongroup $groupsmtpaddress).displayname
	[string] $body = "<strong>NOTIFICATION</strong><BR><BR>As a part of regular maintanence, IT has decided to monitor the usage of Distribution # Lists.<BR><BR>This email is a notification that an Email Distribution List that you manage has been inactive for over 12 months. This Distribiution group has been deleted.<BR><BR>Please send an email to dscoles@dsl4.local if you have any questions.   Thanks for you assistance with this matter."
	foreach ($line in $smtp) {
 	     $messageParameters = @{
  		Subject = "Distribution List Manager Alert - Removed Distribution List - $DLName"
		Body = $body
		From = "tuser01@dsl4.local"
		To = $line
		SmtpServer = "172.31.122.159"
                }
                Send-MailMessage @messageParameters –BodyAsHtml
	     }
}

This code is almost identical to the first function, with the only difference being the content of the subject and body.

function emailIT-Groupremoval ($groupsmtpaddress) {
	$DLName =  (get-distributiongroup $groupsmtpaddress).displayname
	[string] $body = "<strong>NOTIFICATION</strong><BR><BR>As a part of regular maintanence this group was disabled.  The group has been inactive for 12 months per the DL Cleanup Script. Confirm that this list can be deleted and remove it from AD."
	$messageParameters = @{
		Subject = "Distribution List - IT Alert - Removed Distribution List - $DLName"
		Body = $body
		From = "tuser01@dsl4.local"
		to = "dscoles@dsl4.local"
		SmtpServer = "172.31.122.158"
                }
	Send-MailMessage @messageParameters –BodyAsHtml
}

This last function sends an email to the It Group about the group being disabled. No parsing is needed so that section was not included.

I’ve provided the entire script here for your testing as the script has only been used in a test lab with Exchange 2010 SP2.

Download the script below:
The script is in the TechNet Gallery.

Notes
Some information needs to be changed to match your environment – SmtpServer, From, To, as well as what you want the Subject and Body of the message to contain. Also, the script uses ‘CustomAttribute10’ for tracking the activity. Feel free to change it to the attribute necessary for your environment.

Advertisements

11 thoughts on “Distribution Groups – Cleanup with PowerShell

  1. Tried this, I get several errors and the emails that get sent do not have the DL names in them.

    The operation couldn’t be performed because object ‘@’ couldn’t be found on ‘DC-01’.
    + CategoryInfo : NotSpecified: (:) [Get-DistributionGroup], ManagementObjectNotFoundException
    + FullyQualifiedErrorId : 1E4CCB2D,Microsoft.Exchange.Management.RecipientTasks.GetDistributionGroup

    The operation couldn’t be performed because object ‘@’ couldn’t be found on ‘DC-01’.
    + CategoryInfo : NotSpecified: (0:Int32) [Set-DistributionGroup], ManagementObjectNotFoundException
    + FullyQualifiedErrorId : E62ED3F7,Microsoft.Exchange.Management.RecipientTasks.SetDistributionGroup

    These errors go on several times so I’m thinking one of the foreach loops is broken but I can’t seem to find it.

    Thanks for any help….

    • Well, let’s start with some basics. What version of Exchange, what OS is Exchange on and what level is your Domain/Forest?

      ** This script ran perfectly in an Exchange 2013 (on Windows 2008 R2) and DCs are 2008 R2. Then I checked another test environment with Windows 2012 DCs and Exchange 2013 servers (on Windows 2012) and it also ran with out issue.

  2. Get-MessageTrackingLog doens’t appear to be an option with Exchange Online (O365). Are there other was to determine is a distList has been used?

    • You might be able to use Get-MessageTrace to do something similar. The script you are referencing is written for an on-premises deployment and not Office 365. If I can find the time I will see if I can modify this to something similar in Office 365.

    • I should have something by the end of the weekend. Just needed to tweak the way messages are found for the PowerShell commands are different in O365 versus on premises for message tracking.

    • Jonathan Turwy, I am finalizing my testing now … The script will look at logs in Office 365 as well as your on premises Exchange Server. Any group that receives an email will be registered. Look for this in the next day or so as I want to put it through its paces.

  3. Pingback: Cleaning Up Those Pesky Distribution Lists | Just A UC Guy

Leave a Reply

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

WordPress.com Logo

You are commenting using your WordPress.com 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 )

Google+ photo

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

Connecting to %s