Skip to main content

Exchange 2007 Mailbox Size Powershell Form Script

This script has been superseded by Version 5 this is a much better script that doesn't require EWS and also allows you see quota usage click here to goto version 5.

With the demise of the Exchange_Mailbox WMI class in Exchange 2007 the new method of getting Mailbox sizes on Exchange 2007 has moved into the Exchange Management Shell via the new get-mailboxstatistics cmdlet. Mailbox sizes in themselves while useful can’t tell you where the space in a particular mailbox is being used. This is where you need to be able to drill down into the mailbox folders themselves and see where a user in particular is using their space eg(inbox, sent items etc). So I’ve put together a powershell script that can do just that it creates a winform that allows you to select what server you want to report on. Then it will run the get-mailboxstatistics cmdlet to retrieve all mailbox sizes for all users on this server you select and populate a ListView with these values. An event is wired to the ListView rows so when you select a row in this ListView (eg a mailbox you want to know where the space is being use). Another function runs that then performs a FindFolder operation via Exchange Web Services on the selected user which will retrieve a list of every folder in the mailbox and the size of that folder. This data is used to populate a datatable which is then used to populate another list view object that will display the names and sizes of those folders in the mailbox. To keep with the hierarchal nature of a mailbox the folder size listview first shows all the root folders and the sizes displayed are the sums of the size of the root folders plus the size of any child folders within those root folder. To see what the child folder sizes are if you click the row in the second listview for the corresponding folder another function will fire that will clear the listview and then redisplay the data for the child folder with summed totals for all these folders. To go back up one folder a back button will appear at the top of the second ListView that allows back navigation. This second operation doesn’t cause the Server to be re-queried it just uses the same data from the datatable and presents it in a different format.

To access the folders within a mailbox the EWS code relies on Impersonation to be configured the Exchange SDK has details on how to give an account Impersonation rights it involves granting two rights the first is on the Server which allows a user to submit impersonation calls and the second is either on the user account you wish to access itself or the Mailbox database if you wanted to access all mailboxes within a particular mail store for this script you want to grant the latter permission on all stores that you want to be able to get the mailbox folder sizes from. For details of how to do this see. Once you have setup impersonation you then need to configure the code with the details of the user you have setup to allow impersonation. In line 3 there are the following 4 lines that affect this.

$unUserName = "username"
$psPassword = "password$"
$dnDomainName = "domain"
$cdUsrCredentials = new-object System.Net.NetworkCredential($unUserName , $psPassword , $dnDomainName)

If you instead want to use NTLM authentication from the user that is running the script so you don’t have hard code the username and password if you comment (or delete) the lines above in the script and change the following line (124) from

$WDRequest.Credentials = $cdUsrCredentials

To

$WDRequest.UseDefaultCredentials = $True

This will mean the code will run the EWS Findfolder operation under the security context of the user executing the script. The error’s you receive if you get access denied aren’t very descriptive you usually just get a 501 error so be wary or this.

With the EWS code because it runs across SSL you need to make sure that browser trusts the certificate that’s being used (eg the code cant deal with SSL certificate errors) this is usually just a matter of importing the certificate from OWA so its trusted if you not using a registered certificate. (You’ll know what I mean if you get an error about SSL when you try to run the EWS code).

The script actually makes use of 2 Exchange cmdlet’s the first is get-mailboxstatitics and then get-user is used to get the SID of the user in question. The SID is used as part of the impersonation header within the EWS code. Because of this reliance on the Exchange cmdlet’s the script needs to be run from within the Exchange Management Shell.

I’ve put a downloadable copy of the script here the code itself looks like

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

$unUserName = "username"
$psPassword = "password$"
$dnDomainName = "domain"
$cdUsrCredentials = new-object System.Net.NetworkCredential($unUserName , $psPassword
, $dnDomainName)

function getMailboxSizes(){

$lbListView.clear()
$lbListView.Columns.Add("UserName",150)
$lbListView.Columns.Add("# Items",70)
$lbListView.Columns.Add("MB Size(MB)",80)
$lbListView.Columns.Add("DelItems (KB)",90)

get-mailboxstatistics -Server $snServerNameDrop.SelectedItem.ToString()
ForEach-Object{
$item1 = new-object System.Windows.Forms.ListViewItem($_.DisplayName)
$item1.SubItems.Add($_.ItemCount)
$item1.SubItems.Add($_.TotalItemSize.Value.ToMB() )
$item1.SubItems.Add($_.TotalDeletedItemSize.Value.ToKB())

$lbListView.items.add($item1)
}

$form.Controls.Add($lbListView)
}

function enumFolderSizes($fsFolderIDtoSearch){
$private:enfsFilterString = "ParentID = '" + $fsFolderIDtoSearch + "'"
$private:enSubFolders = $fsTable.select($enfsFilterString)
for($fcount1 = 0;$fcount1 -le $enSubFolders.GetUpperBound(0); $fcount1++){
$global:fldSize = $global:fldSize + $enSubFolders[$fcount1][3]
$global:itemCount = $global:itemCount + $enSubFolders[$fcount1][5]
if ($enSubFolders[$fcount1][4] -ne 0){
enumFolderSizes($enSubFolders[$fcount1][1])
}

}
}

function BackFolder(){
$private:bfFilterString = "FolderID = '" + $global:LastFolder + "'"
$private:bfFolder = $fsTable.select($bfFilterString)
GetSubFolderSizes($bfFolder[0][2])

}

function GetSubFolderSizes($fiFIDToSearch){
$global:LastFolder = $fiFIDToSearch
$upButton.visible = $true
$lbFldListView.clear()
$lbFldListView.Columns.Add("Folder Name",150)
$lbFldListView.Columns.Add("# Items",80)
$lbFldListView.Columns.Add("Size(MB)",80)
$lbFldListView.Columns.Add("Has Sub",80)
$lbFldListView.Columns.Add("FID",0)
$subfsFilterString = "ParentID = '" + $fiFIDToSearch + "'"
$subFolders = $fstable.select($subfsFilterString)
for($fcount2 = 0;$fcount2 -le $subFolders.GetUpperBound(0); $fcount2++){
$global:fldSize = $subFolders[$fcount2][3]
$global:itemCount = $subFolders[$fcount2][5]
if ($subFolders[$fcount2][4] -ne 0){
enumFolderSizes($subFolders[$fcount2][1])
}
$item1 = new-object System.Windows.Forms.ListViewItem($subFolders[$fcount2][0])
$item1.SubItems.Add($global:itemCount)
$item1.SubItems.Add([math]::round(($fldsize/1mb),2))
if ($subFolders[$fcount2][4] -ne 0){
$item1.SubItems.Add("Yes")
}
else {
$item1.SubItems.Add("No")
}
$item1.SubItems.Add($subFolders[$fcount2][1])
$lbFldListView.items.add($item1)
}


}


function GetFolderSizes($siSIDToSearch){
$fsTable.clear()
$lbFldListView.clear()
$lbFldListView.Columns.Add("Folder Name",150)
$lbFldListView.Columns.Add("# Items",80)
$lbFldListView.Columns.Add("Size(MB)",80)
$lbFldListView.Columns.Add("Has Sub",80)
$lbFldListView.Columns.Add("FID",0)
$snServername = $snServerNameDrop.SelectedItem.ToString()
$siSIDToSearch = get-user $siSIDToSearch

$smSoapMessage = "<?xml version='1.0' encoding='utf-8'?>" `
+ "<soap:Envelope xmlns:soap=`"http://schemas.xmlsoap.org/soap/envelope/`" " `
+ " xmlns:xsi=`"http://www.w3.org/2001/XMLSchema-instance`"
xmlns:xsd=`"http://www.w3.org/2001/XMLSchema`"" `
+ " xmlns:t=`"http://schemas.microsoft.com/exchange/services/2006/types`" >" `
+ "<soap:Header>" `
+ "<t:ExchangeImpersonation>" `
+ "<t:ConnectingSID>" `
+ "<t:SID>" + $siSIDToSearch.SID + "</t:SID>" `
+ "</t:ConnectingSID>" `
+ "</t:ExchangeImpersonation>" `
+ "</soap:Header>" `
+ "<soap:Body>" `
+ "<FindFolder
xmlns=`"http://schemas.microsoft.com/exchange/services/2006/messages`" " `
+ "xmlns:t=`"http://schemas.microsoft.com/exchange/services/2006/types`"
Traversal=`"Deep`"> " `
+ "<FolderShape>" `
+ "<t:BaseShape>AllProperties</t:BaseShape>" `
+ "<AdditionalProperties
xmlns=""http://schemas.microsoft.com/exchange/services/2006/types"">" `
+ "<ExtendedFieldURI PropertyTag=""0x0e08"" PropertyType=""Integer"" />" `
+ "</AdditionalProperties>" `
+ "</FolderShape>" `
+ "<ParentFolderIds>" `
+ "<t:DistinguishedFolderId Id=`"root`"/>" `
+ "</ParentFolderIds>" `
+ "</FindFolder>" `
+ "</soap:Body></soap:Envelope>"

$strRootURI = "https://" + $snServername + "/ews/Exchange.asmx"
$WDRequest = [System.Net.WebRequest]::Create($strRootURI)
$WDRequest.ContentType = "text/xml"
$WDRequest.Headers.Add("Translate", "F")
$WDRequest.Method = "Post"
$WDRequest.Credentials = $cdUsrCredentials
$bytes = [System.Text.Encoding]::UTF8.GetBytes($smSoapMessage)
$WDRequest.ContentLength = $bytes.Length
$RequestStream = $WDRequest.GetRequestStream()
$RequestStream.Write($bytes, 0, $bytes.Length)
$RequestStream.Close()
$WDResponse = $WDRequest.GetResponse()
$ResponseStream = $WDResponse.GetResponseStream()
$ResponseXmlDoc = new-object System.Xml.XmlDocument
$ResponseXmlDoc.Load($ResponseStream)
$DisplayNameNodes = @($ResponseXmlDoc.getElementsByTagName("t:DisplayName"))
$ExtenedPropertyField = @($ResponseXmlDoc.getElementsByTagName("t:Value"))
$FolderIdNodes = @($ResponseXmlDoc.getElementsByTagName("t:FolderId"))
$ParentFolderIdNodes =
@($ResponseXmlDoc.getElementsByTagName("t:ParentFolderId"))
$ChildFolderCountNodes =
@($ResponseXmlDoc.getElementsByTagName("t:ChildFolderCount"))
$TotalItemCountNodes = @($ResponseXmlDoc.getElementsByTagName("t:TotalCount"))
for($i=0;$i -lt $DisplayNameNodes.Count;$i++){
if ($DisplayNameNodes[$i].'#text' -eq "Top of Information Store"){$rootFolderID
= $FolderIdNodes[$i].GetAttributeNode("Id").'#text'}
$fiFolderID = $FolderIdNodes[$i].GetAttributeNode("Id")
$pfParentFolderID = $ParentFolderIdNodes[$i].GetAttributeNode("Id")
$fsTable.Rows.Add($DisplayNameNodes[$i].'#text',$fiFolderID.'#text',$pfParentFolderID.'#text',$ExtenedPropertyField[$i].'#text',$ChildFolderCountNodes[$i].'#text',$TotalItemCountNodes[$i].'#text')
}
$fsFilterString = "ParentID = '" + $rootFolderID + "'"
$rrRootFolders = $fstable.select($fsFilterString)
for($fcount = 0;$fcount -le $rrRootFolders.GetUpperBound(0); $fcount++){
if ($rrRootFolders[$fcount][0] -ne "Top of Information Store"){
$global:fldSize = $rrRootFolders[$fcount][3]
$global:itemCount = $rrRootFolders[$fcount][5]
if ($rrRootFolders[$fcount][4] -ne 0){
enumFolderSizes($rrRootFolders[$fcount][1])
}
$item1 = new-object
System.Windows.Forms.ListViewItem($rrRootFolders[$fcount][0])
$item1.SubItems.Add($global:itemCount)
$item1.SubItems.Add([math]::round(($fldsize/1mb),2))
if ($rrRootFolders[$fcount][4] -ne 0){
$item1.SubItems.Add("Yes")
}
else {
$item1.SubItems.Add("No")
}
$item1.SubItems.Add($rrRootFolders[$fcount][1])
$lbFldListView.items.add($item1)
}
}

$form.Controls.Add($lbFldListView)
}
$form = new-object System.Windows.Forms.form
$global:LastFolder = ""
# Add DataTable

$Dataset = New-Object System.Data.DataSet
$fsTable = New-Object System.Data.DataTable
$fsTable.TableName = "Folder Sizes"
$fsTable.Columns.Add("DisplayName")
$fsTable.Columns.Add("FolderID")
$fsTable.Columns.Add("ParentID")
$fsTable.Columns.Add("Size)",[int])
$fsTable.Columns.Add("ChildFolderCount",[int])
$fsTable.Columns.Add("TotalCount",[int])
$Dataset.tables.add($fsTable)

# Add Server DropLable
$snServerNamelableBox = new-object System.Windows.Forms.Label
$snServerNamelableBox.Location = new-object System.Drawing.Size(10,20)
$snServerNamelableBox.size = new-object System.Drawing.Size(100,20)
$snServerNamelableBox.Text = "ServerName"
$form.Controls.Add($snServerNamelableBox)

# Add Server Drop Down
$snServerNameDrop = new-object System.Windows.Forms.ComboBox
$snServerNameDrop.Location = new-object System.Drawing.Size(130,20)
$snServerNameDrop.Size = new-object System.Drawing.Size(130,30)
get-mailboxserver ForEach-Object{$snServerNameDrop.Items.Add($_.Name)}
$snServerNameDrop.Add_SelectedValueChanged({getMailboxSizes})
$form.Controls.Add($snServerNameDrop)

# Add List Box to DisplayMailboxs


$lbListView = new-object System.Windows.Forms.ListView
$lbListView.Location = new-object System.Drawing.Size(10,50)
$lbListView.size = new-object System.Drawing.Size(400,500)
$lbListView.LabelEdit = $True
$lbListView.AllowColumnReorder = $True
$lbListView.CheckBoxes = $False
$lbListView.FullRowSelect = $True
$lbListView.GridLines = $True
$lbListView.View = "Details"
$lbListView.Sorting = "Ascending"
$lbListView.add_click({GetFolderSizes($this.SelectedItems.item(0).text)});


# Add List Box to Display FolderSizes


$lbFldListView = new-object System.Windows.Forms.ListView
$lbFldListView.Location = new-object System.Drawing.Size(500,50)
$lbFldListView.size = new-object System.Drawing.Size(400,500)
$lbFldListView.LabelEdit = $True
$lbFldListView.AllowColumnReorder = $True
$lbFldListView.FullRowSelect = $True
$lbFldListView.GridLines = $True
$lbFldListView.View = "Details"
$lbFldListView.Sorting = "Ascending"
$lbFldListView.add_click({GetSubFolderSizes($this.SelectedItems.item(0).subitems[4].text)});


# UP folder Button

$upButton = new-object System.Windows.Forms.Button
$upButton.Location = new-object System.Drawing.Size(500,19)
$upButton.Size = new-object System.Drawing.Size(120,23)
$upButton.Text = "Back Folder level"
$upButton.visible = $false
$upButton.Add_Click({BackFolder})
$form.Controls.Add($upButton)

$form.Text = "Exchange 2007 Mailbox Size Form"
$form.size = new-object System.Drawing.Size(1000,600)
$form.autoscroll = $true
$form.topmost = $true
$form.Add_Shown({$form.Activate()})
$form.ShowDialog()

Popular posts from this blog

Downloading a shared file from Onedrive for business using Powershell

I thought I'd quickly share this script I came up with to download a file that was shared using One Drive for Business (which is SharePoint under the covers) with Powershell. The following script takes a OneDrive for business URL which would look like https://mydom-my.sharepoint.com/personal/gscales_domain_com/Documents/Email%20attachments/filename.txt This script is pretty simple it uses the SharePoint CSOM (Client side object Model) which it loads in the first line. It uses the URI object to separate the host and relative URL which the CSOM requires and also the SharePointOnlineCredentials object to handle the Office365 SharePoint online authentication. The following script is a function that take the OneDrive URL, Credentials for Office365 and path you want to download the file to and downloads the file. eg to run the script you would use something like ./spdownload.ps1 ' https://mydom-my.sharepoint.com/personal/gscales_domain_com/Documents/Email%20attachments/filena

Export calendar Items to a CSV file using EWS and Powershell

Somebody asked about this last week and while I have a lot of EWS scripts that do access the Calendar I didn't have a simple example that just exported a list of the Calendar events with relevant information to a CSV file so here it is. I've talked on this one before in this howto  but when you query the calendar folder using EWS you need to use a CalendarView which will expand any recurring appointments in a calendar. There are some limits when you use a calendarview in that you can only return a maximum of 2 years of appointments at a time and paging will limit the max number of items to 1000 per call. So if you have a calendar with a very large number of appointments you need to break your query into small date time blocks. In this example script I'm just grabbing the next 7 days of appointments if you want to query a longer period you need to adjust the following lines (keeping in mind what I just mentioned) #Define Date to Query $StartDate = (Get-Date) $EndDate

How to test SMTP using Opportunistic TLS with Powershell and grab the public certificate a SMTP server is using

Most email services these day employ Opportunistic TLS when trying to send Messages which means that wherever possible the Messages will be encrypted rather then the plain text legacy of SMTP.  This method was defined in RFC 3207 "SMTP Service Extension for Secure SMTP over Transport Layer Security" and  there's a quite a good explanation of Opportunistic TLS on Wikipedia  https://en.wikipedia.org/wiki/Opportunistic_TLS .  This is used for both Server to Server (eg MTA to MTA) and Client to server (Eg a Message client like Outlook which acts as a MSA) the later being generally Authenticated. Basically it allows you to have a normal plain text SMTP conversation that is then upgraded to TLS using the STARTTLS verb. Not all servers will support this verb so if its not supported then a message is just sent as Plain text. TLS relies on PKI certificates and the administrative issue s that come around certificate management like expired certificates which is why I wrote th
All sample scripts and source code is provided by for illustrative purposes only. All examples are untested in different environments and therefore, I cannot guarantee or imply reliability, serviceability, or function of these programs.

All code contained herein is provided to you "AS IS" without any warranties of any kind. The implied warranties of non-infringement, merchantability and fitness for a particular purpose are expressly disclaimed.