suggestions on cleaning up script

by rgiles5122 at 2013-05-01 08:11:51

i have been playing with a script to help look at the inactive DLs in our exchange environment and then email the managers of those DL to see if they can be deleted. For the ones not managed my thought was to list the members of the group and email the first 5, but i havent gotten quite that far yet. The reason why is that the script is pegging the cpu at 100% as it is currently written. Any suggestions on how i could clean it up and make it run a little (lot) smoother?

#looking for DLs that are older than 90 days
$today = get-dateÂ
$date = (get-date) - (new-timespan -days 90)

#Variables needed to email
$FromAddress = ""
$ToAddress = ""
$DistributionList = ""
$MessageSubject = "Audit of Distribution List"
$SendingServer = ""

Clear-Variable ToAddress
Clear-Variable DistributionList

write-host "getting all DLs"
#Get all the DLs in AD
$allDLs = Get-DistributionGroup -resultsize unlimited | select displayname | sort displayname

#Finds all the active DLs by searching each of the Hub server’s message tracking logs.
#All of the entries are grouped by email address and the last use of the DL is time stamped.
#The results are then appended to the file, which will allow a history to be kept.
#During the append older dauplicate data is automatically over-written,
#so that there is only one entry for each DL and last recorded time it was used.

write-host "finding active DLs"
$activeDLs = get-transportserver |
Get-MessageTrackingLog -EventId Expand -ResultSize Unlimited|
Sort-Object relatedrecipientaddress |
#Group-Object relatedrecipientaddress |
#foreach-object {$.group| Sort-Object timestamp| select -last 1} |
Select-Object relatedrecipientaddress,timestamp

#import history file
$history = c:\temp\dl-active.csv

Write-host "merging active DLs with history file"

#when grouping objects and selecting last -1 it removes any single entiers, so i am doubling up the variable until i can find another solution
$activeDLs = $activeDLs + $activeDLs
$history = $history + $history

#merge history variable with activeDLs variable to get a complete list of all the DLs used
$activeDLs = $activeDLs + $history

#sort file and removed extra entries
$activemerged = $activeDLs | Sort-Object relatedrecipientaddress,timestamp |
Group-Object relatedrecipientaddress |
foreach-object {$
.group| select -last 1} |
Select-Object relatedrecipientaddress,timestamp

write-host "finding display name for active DLs"
#Converting the the relatedrecipientaddress to display for comparison
$smtptoname = ForEach ($address in $activemerged.relatedrecipientaddress){
Get-DistributionGroup -id $address |
select DisplayName,Managedby,WhenChanged,PrimarySMTPAddress |
Sort-Object DisplayName
$smtptoname | export-csv c:\temp\activedisplayname.csv -NoTypeInformation

#exporting some variables to output files for history purposes
$file = "c:\temp\activeDLs"+$today.month+$$today.year+".csv"
$activemerged | export-csv $file -NoTypeInformation
$file = "c:\temp\allDLs"+$today.month+$$today.year+".csv"
$allDLs | export-csv $file -NoTypeInformation

Write-host "Compaing all DLs to active DLs"
#Comparing all DLs list to the Active DLs list. Output should be inactive DLs
$temp = Compare-Object $smtptoname $allDLs -Property DisplayName -SyncWindow 5000 |
Sort-Object DisplayName

Write-host "Clearing memory"

#cleaning up memory space by clearing variables no longer needed
clear-variable allDLs
clear-variable activeDLs
clear-variable history
clear-variable activemerged

Write-host "Cleaning up list of inactive DLs"
#cleaning up variable. Looking for items listed in $allDLs and not listed in $smtptoname
foreach ($line in $temp){
If ($temp.SideIndicator -like "=>"){
$clean = $clean + $temp.displayname

Write-host "Retrieving Properties of inactive DLs"
#gathering all the properties of the Managed inactive DLs
$InactiveDLs = ForEach ($Name in $clean){
Get-qadgroup -id $Name -service wstdcprd01:3268 |
where {($.creationdate -lt $date)} |
select displayname,managedby,primarysmtpaddress,creationdate,members,dn

Write-Progress $Name -Status "Please wait"

#exporting variables to output file for history purposes
$file = "c:\temp\IncativeDLs"+$today.month+$$today.year+".csv"
$InactiveDLs | export-csv $file

$managedinactivedls = $inactivedls | where {($
.managedby -gt "")} | sort managedby
$unmanagedinactivedls = $inactivedls | where {($_.managedby -eq "")}

#Setting variable for previous manager
$previousMGR = $managedinactivedls.managedby[0]

Write-host "Emailing managers of inactive DLs"
ForEach ($group in $managedinactivedls){
if ($group.parentcontainerDN -contains "OU=OracleSOA")
#do nothing. Skip group.
Elseif ($group.managedby -eq $previousMGR)
#add a new line and additional groups
$DistributionList = $DistributionList + "rn "+$group.Name
$user = Get-QADUser $previousMGR
$ToAddress = $user.primarysmtpaddress
$MessageBody = "The following DL(s) have not been used in over 4 months and you are listed as the owner:"+"rn "+$DistributionList+"rnrn"+"Please respond within two weeks of this notice if any of your DL(s) will need to remain active. Otherwise after two weeks your DL’s will be hidden from the Global Address Book and scheduled for deletion."

###Create the mail message
$SMTPMessage = New-Object System.Net.Mail.MailMessage $FromAddress, $ToAddress,
$MessageSubject, $MessageBody

###Send the message
$SMTPClient = New-Object System.Net.Mail.SMTPClient $SendingServer

$previousMGR = $group.ManagedBy
$DistributionList = $group.Name


by MasterOfTheHat at 2013-05-01 09:09:00
First, use the powershell tags to enclose your code! Makes it much more readable on the forum. :slight_smile:

Second, a few questions. Do you have any idea where it is in the script when it pegs the CPU? Does it ever finish or does it just run endlessly?
by rgiles5122 at 2013-05-01 09:32:02
It does take forever to run, but i am dealing 5000+ DLs, so that is to be expected. I have added a few write-host and write-progress lines to track the progress, but to answer your question has it ever finished? Yes, if i used with a limited data set (supplying my own input files).

The cpu is pegged during the "compare all DLs to active DLs" phase and then the "cleaning up list of inactive DLs’, the rest of the time it bounces between 50-100%
by MasterOfTheHat at 2013-05-01 11:24:36
Ok. Good info…

I’m not really well versed with Compare-Object, but I think your "Comparing all DLs to active DLs" is basically doing this for each line in the $smtptoname array:
[list=1][]Look at the value at the same index in $allDLs[/][]If that doesn’t match, look at each of the 5000 lines in $smtptoname prior to that index until you find a match[/][]If no match, look at each of the 5000 lines after that index[/][/list]
(I’m not sure how it chooses which direction, etc) If that is the case, it is doing 10,001 compares for each value in $smtptoname that doesn’t exist in $allDLs. That’s going to take some time and processing power! You should be able to cut resource usage and time here by using hash tables and the .ContainsKey method. If $smtptoname were a hash table keyed off of the DisplayName property, then you could do something like, (totally untested so just use for logic):
$allDLs | ForEach-Object {
if(-not ($smtptoname.ContainsKey($PSItem)))
$unusedDLs += $PSItem

You may also want to look at running the "Comparing all DLs…" section in batches so that you don’t have such a large data structure stuck in memory. i.e. Instead of processing the entire $allDLs array at once, dump that array to a CSV file and then read in 500 lines at a time and see if each line exists in the $smtptoname hash table.

As for the "Cleaning up list of inactive DLs" section, that code is pretty trivial, but I’ll be that the $temp array is HUGE at this point. See if you can dump the size of that array before you go into the foreach and see what you get.

Interesting script, btw. I like the idea, and I see how that can be extremely useful!
by rgiles5122 at 2013-05-01 13:20:12
Thank you very much for the input. I will try it out and let you know.