Monday, July 28, 2008

Show Exchange Whitespace / Retained Items and Deleted Mailbox Space in Powershell with flashy visuals

Each night most Exchange servers will run a scheduled maintenance operation which among other things does an online de-fragmentation of the Exchange Store, and removes deleted items and mailboxes that are past the stores configured retention periods. The result of these operations are logged to the event log with some interesting information about how much whitespace the online defrag operation recovered, the size of the retained items and retained deleted mailboxes remaining in the store. I've used these before in VBS here to do this in powershell is pretty easy. Lets jump into some code samples this is how you can get the whitepace in a Exchange store by querying the event logs via WMI for 1221 Event Log ids and then parsing the Size out of the event log message.

$WmidtQueryDT = [System.Management.ManagementDateTimeConverter]::ToDmtfDateTime([DateTime]::UtcNow.AddDays(-3))
Get-WmiObject -computer servername -query ("Select * from Win32_NTLogEvent Where Logfile='Application' and Eventcode = '1221' and TimeWritten >='" + $WmidtQueryDT + "'") |
foreach-object{
$mbnamearray = $_.message.split("`"")
$esEndString = $mbnamearray[2].indexof("megabytes ")-6
[System.Management.ManagementDateTimeConverter]::ToDateTime($_.TimeGenerated).ToString() + " " + $mbnamearray[1] + " " + $mbnamearray[2].Substring(5,$esEndString) + " MB"

}

For the Retained items you can do the following

$WmidtQueryDT = [System.Management.ManagementDateTimeConverter]::ToDmtfDateTime([DateTime]::UtcNow.AddDays(-3))
Get-WmiObject -computer servername -query ("Select * from Win32_NTLogEvent Where Logfile='Application' and Eventcode = '1207' and TimeWritten >='" + $WmidtQueryDT + "'") |
foreach-object{
$mbnamearray = $_.message.split("`"")
$enditem = $mbnamearray[2].Substring($mbnamearray[2].indexof("End:"))
$esize = $enditem.SubString($enditem.indexof("items")+7,$enditem.indexof(" Kbytes")-($enditem.indexof("items")+7))
[System.Management.ManagementDateTimeConverter]::ToDateTime($_.TimeGenerated).ToString() + " " + $mbnamearray[1] + " " + [math]::round(($esize/1024),2) + " MB"

}

For deleted Mailboxes you can do the following

$WmidtQueryDT = [System.Management.ManagementDateTimeConverter]::ToDmtfDateTime([DateTime]::UtcNow.AddDays(-3))
Get-WmiObject -computer servername -query ("Select * from Win32_NTLogEvent Where Logfile='Application' and Eventcode = '9535' and TimeWritten >='" + $WmidtQueryDT + "'") |
foreach-object{
$mbnamearray = $_.message.split("`"")
$retMbs = $mbnamearray[2].Substring($mbnamearray[2].indexof("have been removed."))
$retMbsSize = $retMbs.Substring(($retMbs.indexof("(")+1),$retMbs.indexof(")")-($retMbs.indexof("(")+1)).Replace(" KB","")
[System.Management.ManagementDateTimeConverter]::ToDateTime($_.TimeGenerated).ToString() + " " + $mbnamearray[1] + " " + [math]::round(($retMbsSize/1024),2) + " MB"

}

Okay so to put these little snippiets into something a whole lot more usefull by producing a Report that will first go out and grab the mailbox/public folder store sizes then grab the Whitespace, RetainedItems sizes and retained deleted Mailbox sizes and put them into a Datagrid and display them in a Winform. Also following on from last week we can then graph this data the first graph is a pie graph that show the percentage of space used across all stores on the server. Then the second graph is a stacked bar chart that compares all the parameters that where measured across all stores on a server. The below Pic shows what I mean


The graphs are generated using Google Charts and the picture Control Image location trick i talked about last week. This script uses straight ADSI and WMI so will work on either Exchange 2003 and 2007. I've put a download of this script here the code itself looks like

[System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
[System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms")

$snServerName = "servername"
$StoreFileSizes = @{ }
$StoreWhitespace = @{ }
$StoreRetainItemSize = @{ }
$StoreDeletedMailboxSize = @{ }
$hcHourCount = @{ }
$root = [ADSI]'LDAP://RootDSE'
$cfConfigRootpath = "LDAP://" + $root.ConfigurationNamingContext.tostring()
$configRoot = [ADSI]$cfConfigRootpath
$searcher = new-object System.DirectoryServices.DirectorySearcher($configRoot)
$searcher.Filter = '(&(objectCategory=msExchExchangeServer)(cn=' + $snServerName + '))'
$searchres = $searcher.FindOne()
$snServerEntry = New-Object System.DirectoryServices.directoryentry
$snServerEntry = $searchres.GetDirectoryEntry()




$adsiServer = [ADSI]('LDAP://' + $snServerEntry.DistinguishedName)
$dfsearcher = new-object System.DirectoryServices.DirectorySearcher($adsiServer)
$dfsearcher.Filter = "(objectCategory=msExchPrivateMDB)"
$srSearchResults = $dfsearcher.FindAll()
foreach ($srSearchResult in $srSearchResults){
$msMailStore = $srSearchResult.GetDirectoryEntry()
$sgStorageGroup = $msMailStore.psbase.Parent
if ($sgStorageGroup.msExchESEParamBaseName -ne "R00"){
$edbfile = [WMI]("\\" + $snServerName + "\root\cimv2:CIM_DataFile.Name='" + $msMailStore.msExchEDBFile + "'")
$StoreFileSize = [math]::round($edbfile.filesize/1048576,0)
$exStoreName = ($sgStorageGroup.Name.ToString() + "\" + $msMailStore.Name.ToString())
$StoreFileSizes.Add($exStoreName,$StoreFileSize)
}
}
"Finshed Mailstores"
$dfsearcher.Filter = "(objectCategory=msExchPublicMDB)"
$srSearchResults = $dfsearcher.FindAll()
foreach ($srSearchResult in $srSearchResults){
$msMailStore = $srSearchResult.GetDirectoryEntry()
$sgStorageGroup = $msMailStore.psbase.Parent
if ($sgStorageGroup.msExchESEParamBaseName -ne "R00"){
$edbfile = [WMI]("\\" + $snServerName + "\root\cimv2:CIM_DataFile.Name='" + $msMailStore.msExchEDBFile + "'")
$StoreFileSize = [math]::round($edbfile.filesize/1048576,0)
$exStoreName = ($sgStorageGroup.Name.ToString() + "\" + $msMailStore.Name.ToString())
$StoreFileSizes.Add($exStoreName,$StoreFileSize)
}
}
"Finshed Public Folder Stores"
$WmidtQueryDT = [System.Management.ManagementDateTimeConverter]::ToDmtfDateTime([DateTime]::UtcNow.AddDays(-3))
Get-WmiObject -computer $snServerName -query ("Select * from Win32_NTLogEvent Where Logfile='Application' and Eventcode = '1221' and TimeWritten >='" + $WmidtQueryDT + "'") |
foreach-object{
$mbnamearray = $_.message.split("`"")
$esEndString = $mbnamearray[2].indexof("megabytes ")-6
if ($StoreWhitespace.Containskey($mbnamearray[1]) -eq $false){
$StoreWhitespace.Add($mbnamearray[1],$mbnamearray[2].Substring(5,$esEndString))
}
}
"Finished WhiteSpace"
$WmidtQueryDT = [System.Management.ManagementDateTimeConverter]::ToDmtfDateTime([DateTime]::UtcNow.AddDays(-3))
Get-WmiObject -computer $snServerName -query ("Select * from Win32_NTLogEvent Where Logfile='Application' and Eventcode = '1207' and TimeWritten >='" + $WmidtQueryDT + "'") |
foreach-object{
$mbnamearray = $_.message.split("`"")
$enditem = $mbnamearray[2].Substring($mbnamearray[2].indexof("End:"))
$esize = $enditem.SubString($enditem.indexof("items")+7,$enditem.indexof(" Kbytes")-($enditem.indexof("items")+7))
if ($StoreRetainItemSize.Containskey($mbnamearray[1]) -eq $false){
$StoreRetainItemSize.Add($mbnamearray[1],[math]::round(($esize/1024),0))
}

}
"Finshed Retained Items"
$WmidtQueryDT = [System.Management.ManagementDateTimeConverter]::ToDmtfDateTime([DateTime]::UtcNow.AddDays(-3))
Get-WmiObject -computer $snServerName -query ("Select * from Win32_NTLogEvent Where Logfile='Application' and Eventcode = '9535' and TimeWritten >='" + $WmidtQueryDT + "'") |
foreach-object{
$mbnamearray = $_.message.split("`"")
$retMbs = $mbnamearray[2].Substring($mbnamearray[2].indexof("have been removed."))
$retMbsSize = $retMbs.Substring(($retMbs.indexof("(")+1),$retMbs.indexof(")")-($retMbs.indexof("(")+1)).Replace(" KB","")
if ($StoreDeletedMailboxSize.Containskey($mbnamearray[1]) -eq $false){
$StoreDeletedMailboxSize.Add($mbnamearray[1],[math]::round(($retMbsSize/1024),0))
}

}
"Finished Deleted Items"
$Dataset = New-Object System.Data.DataSet
$ssTable = New-Object System.Data.DataTable
$ssTable.TableName = "TrackingLogs"
$ssTable.Columns.Add("Storage Group")
$ssTable.Columns.Add("Store Name")
$ssTable.Columns.Add("EDB (MB)",[int])
$ssTable.Columns.Add("WhiteSpace (MB)",[int])
$ssTable.Columns.Add("Retained (MB)",[int])
$ssTable.Columns.Add("Deleted (MB)",[int])
$Dataset.tables.add($ssTable)

$i = 0
$TitleBlock = "Mailboxes" + "|" + "Public Folders" + "|" + "WhiteSpace" + "|" + "Retained Items" + "|" + "Deleted Mailboxes"
$lval = 0
$mbsize = 0
$pfsize = 0
$wsSize = 0
$risize = 0
$dmsize = 0
$lval = 0
$valueBlockbc = ""
$valueBlockbc1 = ""
$valueBlockbc2 = ""
$valueBlockbc3 = ""
$valueBlockbc4 = ""
$TitleBlockbc1 = ""
$dscale = ""
$StoreFileSizes.GetEnumerator() | sort name -descending | foreach-object {
$snames = $_.key.split("\")
if ($StoreDeletedMailboxSize.containskey($_.key)){
$cmdsize = ([INT]$StoreWhitespace[$_.key] + [INT]$StoreRetainItemSize[$_.key] + [INT]$StoreDeletedMailboxSize[$_.key])
$mbsize = + ($_.Value - $cmdsize)
$dmsize = $dmsize + $StoreDeletedMailboxSize[$_.key]
$ssTable.Rows.Add($snames[0],$snames[1],[math]::round(($_.Value),2),$StoreWhitespace[$_.key],$StoreRetainItemSize[$_.key],$StoreDeletedMailboxSize[$_.key])
if ($valueBlockbc -eq ""){$valueBlockbc = $mbsize.ToString()
$valueBlockbc1 = $StoreWhitespace[$_.key].ToString()
$valueBlockbc2 = $StoreRetainItemSize[$_.key].ToString()
$valueBlockbc3 = $StoreDeletedMailboxSize[$_.key].ToString() }
else {$valueBlockbc = $valueBlockbc + "," + $mbsize.ToString()
$valueBlockbc1 = $valueBlockbc1 + "," + $StoreWhitespace[$_.key].ToString()
$valueBlockbc2 = $valueBlockbc2 + "," + $StoreRetainItemSize[$_.key].ToString()
$valueBlockbc3 = $valueBlockbc3 + "," + $StoreDeletedMailboxSize[$_.key].ToString()
}
}
else{
$pfsize = $pfsize + ($_.Value - ([INT]$StoreWhitespace[$_.key] + [INT]$StoreRetainItemSize[$_.key]))
$ssTable.Rows.Add($snames[0],$snames[1],[math]::round(($_.Value),2),$StoreWhitespace[$_.key],$StoreRetainItemSize[$_.key],0)
if ($valueBlockbc -eq ""){$valueBlockbc = $pfsize.ToString()
$valueBlockbc1 = $StoreWhitespace[$_.key].ToString()
$valueBlockbc2 = $StoreRetainItemSize[$_.key].ToString()
$valueBlockbc3 = "0" }
else {$valueBlockbc = $valueBlockbc + "," + $pfsize.ToString()
$valueBlockbc1 = $valueBlockbc1 + "," + $StoreWhitespace[$_.key].ToString()
$valueBlockbc2 = $valueBlockbc2 + "," + $StoreRetainItemSize[$_.key].ToString()
$valueBlockbc3 = $valueBlockbc3 + ",0"}
}
if ($TitleBlockbc1 -eq ""){$TitleBlockbc1 = $snames[1]}
else {$TitleBlockbc1 = $snames[1] + "|" + $TitleBlockbc1}
$wsSize = $wsSize + $StoreWhitespace[$_.key]
$risize = $risize + $StoreRetainItemSize[$_.key]
if ($dscale -eq "") {$dscale = "0," + $_.Value}
else {$dscale = $dscale + "|0," + $_.Value}
if ($lval -lt $_.Value) {$lval = $_.Value}



}
$valueBlock = $mbsize.ToString() + "," + $pfsize.ToString() + "," + $wssize.ToString() + "," + $risize.ToString() + "," + $dmsize.ToString()
$csString = "http://chart.apis.google.com/chart?cht=p3&chs=370x120&chds=0," + $lval + "&chd=t:" + $valueBlock + "&chl=" + $TitleBlock + "&chco=0000ff,00ff00,ff0000,FFFFFF,000000"
$csString
$csString1 = "http://chart.apis.google.com/chart?cht=bhs&chs=850x350&chd=t:" + $valueBlockbc + "|" + $valueBlockbc1 + "|" + $valueBlockbc2 + "|" + $valueBlockbc3 +"&chds=0," + $lval + "&chxt=x,y&chxr=" + "&chxr=0,0," + ($lval+20) + "&chxl=1:|" + $TitleBlockbc1 + "&chdl=MailStorage|WhiteSpace|RetainedItems|DeletedMailboxes&chco=4d89f9,ff0000,c6d9fd,000000"
$csString1
$form = new-object System.Windows.Forms.form
$form.Text = "Server Store Size Report"

#add Picture box

$pbox = new-object System.Windows.Forms.PictureBox
$pbox.Location = new-object System.Drawing.Size(615,10)
$pbox.Size = new-object System.Drawing.Size(380,150)
$pbox.ImageLocation = $csString
$form.Controls.Add($pbox)

$pbox1 = new-object System.Windows.Forms.PictureBox
$pbox1.Location = new-object System.Drawing.Size(10,320)
$pbox1.Size = new-object System.Drawing.Size(1000,350)
$pbox1.ImageLocation = $csString1
$form.Controls.Add($pbox1)

# Add DataGrid View

$dgDataGrid = new-object System.windows.forms.DataGridView
$dgDataGrid.Location = new-object System.Drawing.Size(10,10)
$dgDataGrid.size = new-object System.Drawing.Size(600,300)
$dgDataGrid.ColumnHeadersHeightSizeMode = [System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode]::AutoSize
$dgDataGrid.AutoSizeColumnsMode = [System.Windows.Forms.DataGridViewAutoSizeColumnsMode]::Fill
$form.Controls.Add($dgDataGrid)
$dgDataGrid.DataSource = $ssTable

$form.Size = new-object System.Drawing.Size(1000,600)

$form.topmost = $true
$form.Add_Shown({$form.Activate()})
$form.ShowDialog()

52 comments:

Sean McGilvray said...

I get the following error when running this script.

You cannot call a method on a null-valued expression.
At C:\Scripts\showstores.ps1:17 char:46
+ $snServerEntry = $searchres.GetDirectoryEntry( <<<< )

Any suggesstions

Glen said...

Did you set the
$snServerName = "servername"

varible to the name of the server you wanted to report on. Make sure you use the Netbios name of the server and not the FQDN.

Cheers
Glen

Anonymous said...

Hi Glen,
This is not only a great script in providing exactly the information i just needed but a pretty good example for what is possible with powershell (which is totally new for me).

Thank you for this !

ps:
pbox1 appeared "to be empty" unless i resize the form or modify the $csString1 to read "chs=850x250" instead of "chs=850x350".
Of course this may depend on resolution and font sizes, i know...

Eltate Friky News said...

Hi Glen,

Is it possible to get the output of the script in html format?

Great script and very good job.

Thanks.

Glen said...

All the display elements in the in the winform could be converted to html eg convert the datagrid to Table and the google charts can be easly used as img tag inside a div. However its a time consuming process to build a document that displays the Divs in the right location etc not hard just time consuming.

Its a good idea you could build the output as HTML and then email to team of people on a daily/weekly basis using this as the body of the message.

Cheers
Glen

Eltate Friky News said...

Hi again Glen!

I have found how to export the report to html, but I need to close the winform to do that.

How can I close the winform automaticaly, when it finish?

Regards,

Glen said...

Form.Close()

should work

Cheers
Glen

Eltate Friky News said...

I can close the form with Form.Close()

but i need that the script finish.

I have to put a delay or wait until the script finish, and after I execute Form.Close()

How can I put the delay or wait until the script have finished?

Regards

Glen said...

You can use Start-Sleep http://www.microsoft.com/technet/scriptcenter/topics/msh/cmdlets/start-sleep.mspx to put a pause in a script


Cheers
Glen

T said...

Well done sir! I love finding things like this that give me more than I expected. There were three gotchas that I had with it - It didn't like that we had a store dismounted (mounted it to resolve) It didn't like us having retained e-mail set to zero (we are short on disk space - changed it to 3 days) and we are temporarily running an archive process so the logs had rolled out the expected 1221 entries. All fixed in short order but worth mentioning if anyone comes up against it. Keep up the good work! -T

Anonymous said...

This is a great script. Is there a way to add the STM file into the mix as the total store size includes it?

Glen said...

The STM doesn't exist on 2007 but on 2003 you could do this by duplicating the EDB file section but instead use the msExchSLVFile property to get the file path to the STM file and then use WMI to get the file size

Cheers
Glen

Anonymous said...

I hate to say it, but I have no coding experience. Could you possibly post the code I would need to add the 2 together for $StoreFileSize

Glen said...

Okay have a look at http://msgdev.mvps.org/exdevblog/showstoresstm.zip

Cheers
Glen

Anonymous said...

awsome, Thanks

Anonymous said...

That is a great script. I had also not seen the google chart APIs in action before. Awesome!!

I have been playing with adding a server prompt box since we have a bunch ... but I suck. If you ever feel like makeing a 1.1 version... please add that.

You are the bomb!

Anonymous said...

I have set the $snServerEntry to my server name and I still receive " Cannot call a method on a null-valued"

Any suggestions

Glen said...

You should not be changing the $snServerEntry varible this isn't where the server name should go it needs to go in

$snServerName = "servername"

make sure you use the Netbios name of the server

$snServerEntry represent the Directory object of the Exchange Server.

Cheers
Glen

Anonymous said...

Great Stuff!!! How hard would it be to make it an array of servers with multiple forms? Also, for large servers, the google components do not scale up, they just chop off data. Any way to scale, or make the boxes bigger?

Glen said...

It starts to become a little unwielding if you have multiple servers maybe the best you could do is have a drop down list of servers and then you choose the server you want to report on. You can set the size of the charts to a point who many Stores are you trying to report on and after how many does it wig out.

I started writing a script a little Script/web Application that would cater for trend tracking and allow things like if you see you have 2 GB in Deleted Mailbox you could click a hyperlink which would then display what these mailboxes are and when each if going to be deleted etc. This would also allow for on the fly graphing per server

Cheers
Glen

superdave said...

Awesome script! I use it now to send me morning "stats", so to speak.

I've recently added a fifth database, but it screws up the script somehow. My graphs are off. Is this intended for only four databases?

Thanks!
Dave

Glen said...

You can modify the chs= value which will change the size of the chart

Cheers
Glen

Bruce said...

I am getting this error when running against a CCR 2007 setup:

You cannot call a method on a null-valued expression.
At C:\Scripts\showstores.ps1:37 char:46
+ $snServerEntry = $searchres.GetDirectoryEntry( <<<< )

I have set the $snServerName = "servername" to both the virtual name and the active node name. I get the same error from both. (and I did use the netbios name)

Glen said...

This means the AD query if failing if you have a look at configuration partition with ADSI edit have a look at what the cn value of the server object is for your CCR cluster. This is the value you should set that varible to for the script to work.

Cheers
Glen

Bruce said...

thanks - I had the wrong virtual name. All is working now, just need to format it to see all the SGs.
Excellent work and thanks for the support.

Anthony B said...

is there a way to output to Excel insted of Google Charts. Looking more for the grid?

Still learnig Powershell and love the scripts

Glen said...

I added that feature after i posted this to the download you should see a export grid button on the form which you can click which will export the grid to a CSV file which you can open in Excel.

Cheers
Glen

ChristianWickham said...

I made a small change - I changed the snServerName parameter to accept the imput from the command line

$snServerName = $args[0]

So, run the script as ./showstores.ps1 yourMBXservername

But, I can't get the graphs to work - just a red X...

Glen said...

What can cause the Graph to fail is that if you use a proxy to access the Internet. This script wont try to connect to google charts via the proxy it will try to go straight out which will work fine for NAT network no so good for proxyied

Cheers
Glen

Anonymous said...

Great script

Would it be possible to add an additonal column that contains the mailbox count per database

I like your report presentation, and would like to export to csv and send email to admins

I came across this script from powershell community forum.

------------------------------------

$date = ( get-date ).ToString('yyyyMMdd')
$exchangeservers = "your server name"
$AllServers = @()
foreach ($server in $exchangeservers)
{
$db = Get-MailboxDatabase -server $server
foreach ($objItem in $db)
{
$edbfilepath = $objItem.edbfilepath
$path = "`\`\" + $server + "`\" + $objItem.EdbFilePath.DriveName.Remove(1).ToString() + "$"+ $objItem.EdbFilePath.PathName.Remove(0,2)
$dbsize = Get-ChildItem $path
$dbpath = $EdbFilePath.PathName.Remove(0,2).remove($EdbFilePath.PathName.length-6)
$mailboxcount = Get-MailboxStatistics -database $objItem |measure-object
$ReturnedObj = New-Object PSObject
$ReturnedObj | Add-Member NoteProperty -Name "Server\StorageGroup\Database" -Value $objItem.Identity
$ReturnedObj | Add-Member NoteProperty -Name "Size (MB)" -Value ("{0:n2}" -f ($dbsize.Length/1024KB))
$ReturnedObj | Add-Member NoteProperty -Name "Mailbox Count" -Value $mailboxcount.count
$AllServers += $ReturnedObj
}
}

$AllServers |export-csv C:\scripts\$date-DatabaseSize.csv -notype -force

#Send mailbox statistics script

#First, the administrator must change the mail message values in this section
$FromAddress = "ExchangeReports@domain.com"
$ToAddress = "ExchangeAdmins@domain.com"
$MessageSubject = "Exchange 2007 Database Size Report"
$MessageBody = "Report of Exchange 2007 Database Sizes and Mailbox Counts"
$SendingServer = "your mail server"


#Create the mail message and add the statistics text file as an attachment
$SMTPMessage = New-Object System.Net.Mail.MailMessage $FromAddress, $ToAddress,
$MessageSubject, $MessageBody
$Attachment = New-Object Net.Mail.Attachment("C:\scripts\$date-DatabaseSize.csv")
$SMTPMessage.Attachments.Add($Attachment)

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

-----------------------------------

so looking to add mailbox count, export to csv options to your already great script

Awaiting your reply

Anonymous said...

Hi, Not sure if your post will be picked up. I have tested your script and think it's great. Motivating me to start using the powershell.

Would you still be able to help with a couple of issues I have with it? part of the script is looking for a 'check_SPN.txt' while running.

best regards,

Glen said...

See http://gsexdev.blogspot.com/2009/05/mailbox-size-summary-reporting-gui-for.html

Cheers
Glen

Glen said...

'check_SPN.txt' is not part of any of my scripts must be something your using for somewhere else.

Cheers
Glen

Methos said...

Hello,
i love all your script but i have a problem with this one.

According my investigation, it's because of localization (French there).
A message from Logfile looks like that (First Whitespace, Second Retained) :

La base de données « Second Storage Group\Public Folder Database » contient 41 méga-octets d'espace disponible suite à la défragmentation en ligne.

Le nettoyage des éléments périmés pour la récupération d'éléments est terminé pour la base de données « Groupe Stockage Siege 01\Siege ».
Début : 9125 éléments ; 1030453 Ko
Fin : 7321 éléments ; 895754 Ko


As you can see, of course key words are different, because in french, but double quotes are differents too. At the beginning and ending.

Any idea to modify the script for it to work in any language ?

To make it work in french, my real problem is «, », and maybe the words order.
Could you show me english sentences on which you based your script ?

Thanks.

Methos said...

That's OK.
I've made some changes in sentences analysis and it works now with French system.

:) 50% of my databases are whitespace.

If someone is interested in, tell me.

John Brines said...

Hi Glen,

Brilliant script, is it possible to automatically export to a csv file rather than having to press the button?

John.

Anonymous said...

Glen, I love this script and works great on one of ou Exhange servers but the other one I get this error. You cannot call a method on a null-valued expression.
At C:\Users\franchej\Desktop\lexchstorewspace\showstores\showstores.ps1:160 char:83
My exchange environment is a 2 cluster active/passive nodes and the virtual exhcange server pointing to the active node. I've looked at adsi edit to see what the cn name was for the virutual node and it's the same as in the script.
Any ideas?

Glen said...

A quick fix maybe just to be use a silient continue on the error to see if the script finishs eg add the line at the top of the script

$erroractionpreference = "SilentlyContinue"

The issue is probably because you may have used / in your storename you could try modifying the script to use another seperator character instead of slash eg change the line

$exStoreName = ($sgStorageGroup.Name.ToString() + "\" + $msMailStore.Name.ToString()) to something like

$exStoreName = ($sgStorageGroup.Name.ToString() + "|" + $msMailStore.Name.ToString())

you also need to change then

$snames = $_.key.split("\")

to

$snames = $_.key.split("|")

Cheers
Glen

sal said...

hi glen,
great script; i've used it successfully on one of my exchange clusters. however on my other cluster i get:

You cannot call a method on a null-valued expression.
At :line:164 char:82
+ $valueBlockbc1 = $valueBlockbc1 + "," + $StoreWhitespace[$_.key].ToString <<<< ()

You cannot call a method on a null-valued expression.
At :line:165 char:86
+ $valueBlockbc2 = $valueBlockbc2 + "," + $StoreRetainItemSize[$_.key].ToString <<<< ()

it looks like it is doing this for every store.

any thoughts? could it be that i do not have any 1221 events in my logs?

thanks,

sal

Glen said...

Yes the event would only get logged to the active node so if you run it against the passive node you would get an error. You could through an error continue if your woried about the script failing.

Cheers
Glen

Anonymous said...

Awesome Script

Anonymous said...

Hey, am getting this error

You cannot call a method on a null-valued expression.
At :line:165 char:86
+ $valueBlockbc2 = $valueBlockbc2 + "," + $StoreRetainItemSize[$_.key].ToString <<<< ()

on Active Node Itself, Any suggesstions ?

Neo said...

Excellent Script to get such useful information!!

I am very new to PowerShell stuff and was wondering if you could help me with few more thing in this script, i.e.:

- Options to run this for multiple servers or it can read server names from a different file
- Although its go to see graphical output as well, to avoid Google charts as well
- and HTML tabled output over the email.

thanks
Neo

Wirrel said...

We are seeing a case where for some reason the whitespace variable when it is contructing the table is null.

I verified that the events are on the server.

Any idea what could be causing it to not be able to get the event and parse it?

Voogyman said...

Hello Glen,

What a great script this is.

We onle have 2 errors:

Finished WhiteSpace
You cannot call a method on a null-valued expression.
At C:\Program Files\Microsoft\Exchange Server\V14\Scripts\showstores.ps1:84 char:62
+ $enditem = $mbnamearray[2].Substring($mbnamearray[2].indexof <<<< ("End:"))
+ CategoryInfo : InvalidOperation: (indexof:String) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull

Finshed Retained Items
You cannot call a method on a null-valued expression.
At C:\Program Files\Microsoft\Exchange Server\V14\Scripts\showstores.ps1:96 char:61
+ $retMbs = $mbnamearray[2].Substring($mbnamearray[2].indexof <<<< ("have been removed."))
+ CategoryInfo : InvalidOperation: (indexof:String) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull

You cannot call a method on a null-valued expression.
At C:\Program Files\Microsoft\Exchange Server\V14\Scripts\showstores.ps1:97 char:50
+ $retMbsSize = $retMbs.Substring(($retMbs.indexof <<<< ("(")+1),$retMbs.indexof(")")-($retMbs.indexof("(")+1)).Rep
lace(" KB","")
+ CategoryInfo : InvalidOperation: (indexof:String) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull

9centwhore said...

The pie graph is incorrect.
It only shows data from the last storage group queried plus the public folder info.
...well for me anyway.

Anonymous said...

This is amazing!

Thank you so much for sharing.

Anonymous said...

thanks for sharing this script. It's so cool and smart but with red x is getting me a hard time. If putting the http lines into a browser the pics showing well but not from the script. Is there a hint getting this through a proxied connection?
Thanks anyway for this great script!
Lex

BearsFan said...

I am receiving the following when running this script. Any ideas?

You cannot call a method on a null-valued expression.
At C:\showstores.ps1:160 char:83
+ $valueBlockbc1 = $valueBlockbc1 + "," + $StoreWhitespace[$_.key].ToString( <<<< )
You cannot call a method on a null-valued expression.
At C:\showstores.ps1:161 char:87
+ $valueBlockbc2 = $valueBlockbc2 + "," + $StoreRetainItemSize[$_.key].ToString( <<<< )

Shashi said...

Please share the complete script with the report in HTML and mail. It is really nice script and very helpful for admin purpose.

suman said...

Hi,
I am receiving below errors.

[PS] C:\temp>C:\temp\showstores.ps1

GAC Version Location
--- ------- --------
True v2.0.50727 C:\WINDOWS\assembly\GAC_MSIL\System.Drawing\2.0.0.0__b...
True v2.0.50727 C:\WINDOWS\assembly\GAC_MSIL\System.Windows.Forms\2.0....
You cannot call a method on a null-valued expression.
At C:\temp\showstores.ps1:37 char:46
+ $snServerEntry = $searchres.GetDirectoryEntry( <<<< )
Finshed Mailstores
Finshed Public Folder Stores
Get-WmiObject : The RPC server is unavailable. (Exception from HRESULT: 0x80070
6BA)
At C:\temp\showstores.ps1:71 char:14
+ Get-WmiObject <<<< -computer $snServerName -query ("Select * from Win32_NTLo
gEvent Where Logfile='Application' and Eventcode = '1221' and TimeWritten >='"
+ $WmidtQueryDT + "'") | sort $_.TimeWritten -Descending |
Finished WhiteSpace
Get-WmiObject : The RPC server is unavailable. (Exception from HRESULT: 0x80070
6BA)
At C:\temp\showstores.ps1:81 char:14
+ Get-WmiObject <<<< -computer $snServerName -query ("Select * from Win32_NTLo
gEvent Where Logfile='Application' and Eventcode = '1207' and TimeWritten >='"
+ $WmidtQueryDT + "'") | sort $_.TimeWritten -Descending |
Finshed Retained Items
Get-WmiObject : The RPC server is unavailable. (Exception from HRESULT: 0x80070
6BA)
At C:\temp\showstores.ps1:93 char:14
+ Get-WmiObject <<<< -computer $snServerName -query ("Select * from Win32_NTLo
gEvent Where Logfile='Application' and Eventcode = '9535' and TimeWritten >='"
+ $WmidtQueryDT + "'") | sort $_.TimeWritten -Descending |
Finished Deleted Items

Anonymous said...

Can this work with Exchange 2010 ?