Spider Permission Script – An alternative script to determine mailbox permissions FASTER than Microsoft’s script

Scenario:   We found that using Microsoft’s spider script (the script that determines all mailbox permissions and delegates found here) was slow and would eat RAM (like Cookie Monster eats cookies) causing our Exchange servers to error out.  So we developed a newer, faster, but more raw version of the spider script.

Script: Edit the global variables and then copy and paste in normal Windows Powershell.  You want the scriptlet pretty and easy to read, copy and paste into PowerShell ISE.  This is not meant to run as a .ps1, we like to see everything….

<#Script Notes:
0. Output of the file will display FullAccess, SendAs, SendOnBehalf, and Calendar Permissions (Reviewer,Editor, ect.)
1. Open a normal Windows Powershell with an account with Exchange Permissions (Run-As)
2. Take a list of all Exchange On-Premises Mailboxes (get-mailbox -resultsize unlimited | Select Alias | export-csv C:tempAll_mbx.csv. All we need is the alias column (keep the header alias), nothing else.
3. This script requires the ActiveDirectory Module
4. In the  New-OnPremExchangeSession it will randomly select one of our Exchange servers to process the remote PowerShell Commands.  After 500 mailboxes, it will randomly select another.
5. If you have to start over, open a brand new normal Windows Powershell with an account with Exchange Permission (Run-As)

End_ScriptNotes #>

#Global Variables############################################################################################
$m = import-csv “C:tempAll_Mbx.csv”   #Import a list of all your Mailboxes.  If you have not done this yet, feel free to run the following command inside a Exchange PowerShell:  get-mailbox -resultsize unlimited | Export-csv C:tempAll_mbx.csv
$outfile = “C:tempAll_Mbx_Spider_Results.csv”  #Final Spider Results Export
$report_Final = @()   #Used to capture all spider rights within the script
$count = 0 #The Count starts the counter for each mailbox processed
$CounterSleep = 500 #This offset countersleep allows the script to sleep after 500 Mailboxes and pickup a new Exchange server to connect to to avoid hardware/resource problems
$ExchangeServers = “ExSrv1.domain.com”,”ExSrv2.domain.com”,”ExSrv3.domain.com”   #Exchange server pool that your Powershell session with connect to after the CounterSleep hits
$domainForUsername = “domain”  #Replace the domain with the domain name used for sign ins.  Example: If you login is domainusername: contososteve   make the value  “contoso”
$dom = $env:userdomain;$usr = $env:username   #Just for fun in the script
$DisplayName = ([adsi]”WinNT://$dom/$usr,user”).fullname #Just for fun in the script
#End_Global Variables############################################################################################

#This is where the MAGIC HAPPENS
#Import Module####################################################################################################
#Required for AD Permissions
Import-Module ActiveDirectory
#End_Import Module#####################################################################################################

#Functions required##################################################################################################################################
#Gets nested group members
function Get-DistributionGroupMemberRecursive ($GroupIdentity) {
$member_list = Get-DistributionGroupMember -Identity $GroupIdentity
foreach ($member in $member_list) {
if ($member.RecipientType -like ‘*Group*’) {
Get-DistributionGroupMemberRecursive -GroupIdentity $member.Identity
} else {
$member
}
}
}
#Connects to Exchange remote Powershell on servers randomly
Function New-OnPremExchangeSession(){
#close any old remote session. We don’t need it. Don’t want it.
Get-PSSession | Remove-PSSession -Confirm:$false

For ($i=60; $i -gt 1; $i–-) {
Write-Progress -Activity “$DisplayName made me fall asleep.” -SecondsRemaining $i
Start-Sleep 1
}

Do{
#get-random number for exchange server
$ex_srv = get-random $ExchangeServers
$ex_ps = “http://”+$ex_srv +”/powershell/”
#start a new remote session
“Attempting to connect to Remote Powershell on esgmtwex$ex_i”
$OnPremsession = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $ex_ps -Authentication Kerberos
$OnPrem = Import-PSSession $OnPremSession
#check for session
$opensession = Get-PSSession | Where {($_.Configurationname -eq “Microsoft.Exchange”) -and ($_.State -eq “Opened”)}
}While($opensession -eq $null)
}
#End_Functions##############################################################################################

#Connect to Exchange##############################################################################################################################
New-OnPremExchangeSession
#END_ConnecttoExchange##############################################################################################################################

#Loop Each Mailbox#################################################################################################################################################################
$m.alias | sort | %{
#Define InLoop Variables
$u = $_
#write-it so you can see it.
Write-host “$U – $count/$($m.count – 1)” -ForegroundColor green
$count++
If($count -eq $CounterSleep){
New-OnPremExchangeSession $OnPremCredentials
$CounterSleep = $CounterSleep + 500
“CounterSleep now set to $CounterSleep”
}
[int]$percentComplete = [int](($Count/$($M.count * 100)))

Write-Progress -Activity “Connected to $((Get-PSSession | Where {($_.Configurationname -eq “Microsoft.Exchange”) -and ($_.State -eq “Opened”)}).computername)” -PercentComplete “$percentComplete” -Status (“Processing Mailbox: $($U) – $($Count) of $($m.count – 1 )”)
“————————”
#Check Mbx
$error.clear()
$mbx = get-mailbox $u
If($error[0].exception -like “*The I/O operation has been aborted*”){“I/O Error Operation found, sleeping 3 minutes”;sleep 180; $mbx=get-mailbox $u}
If($mbx -ne $null){

#here we go!!!!!
#Collect SendOnBehalf permissions##############################################################################################
”     Checking SendOnBehalf for $U”
$SendOnBehalf =  $mbx.grantsendonbehalfto
If($SendOnBehalf -ne $null){
$SendOnBehalf | %{
$sob_name = $_
$sob_name = ($sob_name -split ‘/’)[-1]
$obj = new-object psObject
$obj | Add-Member -membertype noteproperty -Name Mbx -Value $u
$obj | Add-Member -membertype noteproperty -Name Delegate -Value $sob_name
$obj | Add-Member -membertype noteproperty -Name AccessRight -Value “SendOnBehalf”
$Report_final += $obj
}
}else{
$obj = new-object psObject
$obj | Add-Member -membertype noteproperty -Name Mbx -Value $u
$obj | Add-Member -membertype noteproperty -Name Delegate -Value “None”
$obj | Add-Member -membertype noteproperty -Name AccessRight -Value “SendOnBehalf”
$Report_final += $obj
}
$SendOnBehalf = $null
#End_Calendar##################################################################################################################

#Collect Calendar permissions##############################################################################################
”     Checking Calendar for $U”
$cal_var = ($mbx.alias).tostring() +”:calendar”
$Calendar_Perm = get-mailboxfolderpermission $cal_Var | Where {($_.User -notlike “*Default*”) -and ($_.user -notlike “*anonymous*”)}
If($Calendar_Perm -ne $Null){
$calendar_Perm | %{
$cal_user_name =  $_.user.adrecipient.name
If($cal_user_name -ne $null){

$cal_user_right = $_ | Select -ExpandProperty AccessRights
#”$Cal_user_name – $Cal_user_right”
$obj = new-object psObject
$obj | Add-Member -membertype noteproperty -Name Mbx -Value $u
$obj | Add-Member -membertype noteproperty -Name Delegate -Value $cal_user_name
$obj | Add-Member -membertype noteproperty -Name AccessRight -Value “Calendar:$cal_user_right”
$Report_final += $obj
}
}
}else{
$obj = new-object psObject
$obj | Add-Member -membertype noteproperty -Name Mbx -Value $u
$obj | Add-Member -membertype noteproperty -Name Delegate -Value None
$obj | Add-Member -membertype noteproperty -Name AccessRight -Value “Calendar”
$Report_final += $obj
}
#Clear FullAccess
$Cal_Var = $null
$Calendar_Perm = $null
$Cal_user_name = $null
$Cal_user_right = $null
#End_Calendar##################################################################################################################

#Collect SendAs permissions##############################################################################################
#This method does not perform a get-adpermission because its slow.
#Why does Microsoft want us to run slow commands when the offer faster commands.
”     Checking Sendas for $U”
$SendAs_Perm = (Get-acl -path “AD:$($mbx.distinguishedname)”).access  |where {($_.ActiveDirectoryRights -like “*ExtendedRight*”) -and ($_.IsInherited -like “*false*”) -and ($_.IdentityReference -like “$domainForUsername*”) -and ($_.ObjectType -eq “ab721a54-1e2f-11d0-9819-00aa0040529b”)}
#$SendAs = $mbx | Get-ADPermission | Where extendedrights -like send-as | Where User -notlike “Nt AuthoritySelf”
If($SendAs_Perm -ne $Null){
$SendAs_users = @()
$Sendas_Perm.IdentityReference.Value |%{
#Format User Name
$object = $_
$object = $object -replace “$domainForUsername\”,””

#ADCheck
$AD_Chk = Get-adobject -filter {samaccountname -eq $object}
#Write-host ”      $($AD_Chk | Select name, objectclass)” -ForegroundColor yellow
#ObjectClass Check based off ADObject $AD_CHK
If($AD_Chk.objectclass -eq “User”){$SendAs_users += $AD_CHK | Select -ExpandProperty Name}
If($AD_Chk.objectclass -eq “Group”){$SendAs_users += Get-DistributionGroupMemberRecursive $($AD_Chk | Select -ExpandProperty Name) | Select -ExpandProperty Name}
$Sendas_users | select -Unique | %{
$delegate = $_

$obj = new-object psObject
$obj | Add-Member -membertype noteproperty -Name Mbx -Value $u
$obj | Add-Member -membertype noteproperty -Name Delegate -Value $delegate
$obj | Add-Member -membertype noteproperty -Name AccessRight -Value “SendAs”
$Report_final += $obj
}
}
}else{
$obj = new-object psObject
$obj | Add-Member -membertype noteproperty -Name Mbx -Value $u
$obj | Add-Member -membertype noteproperty -Name Delegate -Value None
$obj | Add-Member -membertype noteproperty -Name AccessRight -Value “SendAs”
$Report_final += $obj
}
#Clear SendAsVariables
$SendAs_Perm = $null
$Sendas_users = $null
$object = $null
$AD_Chk = $null
$delegate = $null

#End_SendAs##################################################################################################################

#Collect MBX permissions##############################################################################################
”     Checking FullAccess for $U”
$FullAccess_Perm = $mbx | get-mailboxpermission | Where {($_.IsInherited -eq $false) -and ($_.User -notlike “NT Auth*”) -and ($_.Deny -eq $false)}
If($FullAccess_Perm -ne $Null){

$fullAccess_Users = @()
$fullAccess_Perm.user | %{
#$fullAccess_Perm.user.securityidentifier | %{
#Format User Name
$object = $_
$object = $object -replace “$domainForUsername\”,””
#ADCheck
#$AD_Chk = Get-adobject -filter {objectsid -eq $object}
$AD_Chk = Get-adobject -filter {samaccountname -eq $object}
#ObjectClass Check based off ADObject $AD_CHK
If($AD_Chk.objectclass -eq “User”){$FullAccess_users += $AD_CHK | Select -ExpandProperty Name}
If($AD_Chk.objectclass -eq “Group”){$FullAccess_users += Get-DistributionGroupMemberRecursive $($AD_Chk | Select -ExpandProperty Name) | Select -ExpandProperty Name}
$FullAccess_users | select -Unique | %{
$delegate = $_

$obj = new-object psObject
$obj | Add-Member -membertype noteproperty -Name Mbx -Value $u
$obj | Add-Member -membertype noteproperty -Name Delegate -Value $delegate
$obj | Add-Member -membertype noteproperty -Name AccessRight -Value “FullAccess”
$Report_final += $obj
}
}
}else{
$obj = new-object psObject
$obj | Add-Member -membertype noteproperty -Name Mbx -Value $u
$obj | Add-Member -membertype noteproperty -Name Delegate -Value None
$obj | Add-Member -membertype noteproperty -Name AccessRight -Value “FullAccess”
$Report_final += $obj
}

#Clear FullAccess
$FullAccess_Perm = $null
$FullAccess_users = $null
$object = $null
$AD_Chk = $null
$delegate = $null

#End_FullAccess##################################################################################################################

}else{
Write-Host “No Mbx for $u” -ForegroundColor Magenta
$obj = new-object psObject
$obj | Add-Member -membertype noteproperty -Name Mbx -Value $u
$obj | Add-Member -membertype noteproperty -Name Delegate -Value None
$obj | Add-Member -membertype noteproperty -Name AccessRight -Value “No_Mbx_Found”
$Report_final += $obj}

#Clear InLoop Variables.
#Wash, Rinse, Repeat
$u = $null
$mbx = $null
}
#END_Loop Each Mailbox#################################################################################################################################################################

#View the Report##################################################################################################################################################################
#$report_Final
#$report_Final | out-gridview
$Report_Final | export-csv $outfile
#View the Report##################################################################################################################################################################

 

EWS Script: Perform a RegEx search against all Items in a Mailbox and perform an action

Scenario:  Although RegEx searches are not supported within the Exchange toolset, here is an EWS Script that will perform two kinds of RegEx searches; Patterns AND exact matches (not case sensitive).

The script below is going to do the following:

  • If the mailbox item matches the RegEx, it will move the email item into a folder: BadFolder_Reg
  • If the mailbox item matches the Term, it will move the email item into a folder: BadFolder_Term
  • If any mailbox item has an attachment, it will download it into a directory

Scripts:

#Finding items With RegEx
#Variables
$cred = Get-credential  #credentials will fullaccess to access the mailbox
$mailboxname = “stevetest25@domain.com”  #The Mailbox you wish to perform the query and restore on
$EWS_DLL = “C:Program FilesMicrosoftExchange ServerV15BinMicrosoft.Exchange.WebServices.dll”
$EWS_URL = “https://domain/ews/exchange.asmx&#8221;

#Configure connection to EWS
Import-Module -Name $EWS_DLL
$service = new-object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.Exchangeversion]::exchange2013)
$service.Url = new-object System.Uri($EWS_URL)
$service.UseDefaultCredentials = $false
$service.Credentials = $cred.GetNetworkCredential()

 

#Find all Mailbox folders
$MailboxRootid= new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot,$MailboxName)
$MailboxRoot=[Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$MailboxRootid)
$FolderList = new-object Microsoft.Exchange.WebServices.Data.FolderView(1000)
$FolderList.Traversal = [Microsoft.Exchange.WebServices.Data.FolderTraversal]::Deep
$findFolderResults = $MailboxRoot.FindFolders($FolderList)

#Bind to BadFolders for RegEx and Terms
$BADfolder_Reg = $findFolderResults | Where displayname -like “BadFolder_Reg”
$BADfolder_Term = $findFolderResults | Where displayname -like “BadFolder_Term”

If($BadFolder_Reg -eq $null){
#Create a Folder called BAD on the Root
“Bad folder doesnt exist, creating it now”
$folderid = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot,$mailboxname)
$Folder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid)
$NewFolder = new-object Microsoft.Exchange.WebServices.Data.Folder($service)
$NewFolder.DisplayName = “BadFolder_reg”
$NewFolder.Save($Folder.id)

$MailboxRootid= new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot,$MailboxName)
$MailboxRoot=[Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$MailboxRootid)
#Find all folders in the mailbox
$FolderList = new-object Microsoft.Exchange.WebServices.Data.FolderView(1000)
$FolderList.Traversal = [Microsoft.Exchange.WebServices.Data.FolderTraversal]::Deep
$findFolderResults = $MailboxRoot.FindFolders($FolderList)
$BADfolder_Reg = $findFolderResults | Where displayname -like “BadFolder_Reg”
}

If($BadFolder_Term -eq $null){
#Create a Folder called BAD on the Root
“Bad folder doesnt exist, creating it now”
$folderid = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot,$mailboxname)
$Folder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid)
$NewFolder = new-object Microsoft.Exchange.WebServices.Data.Folder($service)
$NewFolder.DisplayName = “BadFolder_Term”
$NewFolder.Save($Folder.id)

$MailboxRootid= new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot,$MailboxName)
$MailboxRoot=[Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$MailboxRootid)
#Find all folders in the mailbox
$FolderList = new-object Microsoft.Exchange.WebServices.Data.FolderView(1000)
$FolderList.Traversal = [Microsoft.Exchange.WebServices.Data.FolderTraversal]::Deep
$findFolderResults = $MailboxRoot.FindFolders($FolderList)
$BADfolder_Term = $findFolderResults | Where displayname -like “BadFolder_Term”
}

#Create Variables for Search

$ItemPropset = new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)
$i = 0
$i_end = $findfolderResults.item.count
$reg = @()

$reg +=  “[1-9][0-9]{2}-[0-9]{2}-[0-9]{4}^d”

$reg += “(^|D)(d{3}-d{2}-d{4})(D|$)”

$DownloadDirectory = “\servershare$attachments”
$term = ” test123 “,” steve “,” batman “,” superman ”

#Loop each mail folder and perform the search
Do{
If(($findFolderResults.Folders[$i]).DisplayName -notlike “Badfolder*”){

“Checking Folder: $(($findFolderResults.Folders[$i]).DisplayName)”
$ItemView = New-Object Microsoft.Exchange.WebServices.Data.ItemView(1000)

Do{
$AqsString = “System.Message.DateReceived:01/01/2000..12/31/2099”
$fiItems = $findFolderResults.Folders[$i].FindItems($AqsString,$ItemView)
$ItemView.offset += $fiItems.Items.Count
[Void]$service.LoadPropertiesForItems($fiItems , $ItemPropset)

foreach($Item in $fiItems.Items){
“Checking on $($Item.Subject)”
#Check for attachments#########################
If($item.Hasattachments -eq $true){
“Attachment Detected on $($Item.Subject)”
foreach($attach in $Item.Attachments){
$attach.Load()
$fiFile = new-object System.IO.FileStream(($downloadDirectory + “” + $attach.Name.ToString()), [System.IO.FileMode]::Create)
$fiFile.Write($attach.Content, 0, $attach.Content.Length)
$fiFile.Close()
write-host “Downloaded Attachment : ” + (($downloadDirectory + “” + $attach.Name.ToString()))
}
}
#^Check for attachments#########################

#Check for Reg#########################
$reg_result = $false
$b_temp = $Item.body.text
#Loop regex
$reg | %{
$r = $b_temp -match “$_”
“Result: $r”
if($r -eq $true){“Setting $reg_result to $r”;$reg_result = $true}
}

#display
“$Reg_result – The MSG with Subject: $($Item.subject) ”

If($reg_result -eq $true){
“Moving $($Item.Subject) to BadFolder_reg”
[VOID]$Item.Move($BadFolder_reg.Id)}
#^Check for Reg##################################################

#Check for Terms#########################
$Term_result = $false
$b_temp = $Item.body.text
#Loop term
$term | %{

$rr = $b_temp -match $_
“Result: $rr”
if($rr -eq $true){“Setting $term_result to $rr”;$term_result = $true}
$term_temp = $null
}

#display
“$term_result – The MSG with Subject: $($Item.subject) ”

If($term_result -eq $true){
“Moving $($Item.Subject) to BadFolder_term”
[VOID]$Item.Move($BadFolder_term.Id)}
#^Check for Terms##################################################

#clean var
$r = $null
$b_temp = $null

}
}While($fiItems.moreavailable -eq $true)
}

$i++
“FolderID Counter $i”

}While ($i -le $i_end)

 

 

EWS Script: Record Mailbox Size per Year and Export into Multiple PSTs.

Scenario:  You want to use a EWS Script to perform two functions:

-Calculate the total size of the mailbox per year. This includes both the Mailbox and the Mailbox Dumpster.

-Export a PST for each year.  If the size of the yearly emails exceeds 10GB, adjust the dates within that year so each export for that year is under 10GB.

Script:

#Edit Variables############################
$mailboxname = “steve@domain.com”
$Export_Log = “C:tempMbx_Size_Years.csv”
$Export_PST = “\ExServerPSTFiles”
$PSTFilePath = $Export_PST
$Batchname = “Steve”
$Size_for_PST = [int]10240
$PST_Final = @()
$EWSServer = “https://mail.domain.com/EWS/Exchange.asmx&#8221;
##########################################

#Paste the Script#########################
Write-host “Starting $Mailboxname” -ForegroundColor Cyan
Import-Module -Name “C:Program FilesMicrosoftExchange ServerV15BinMicrosoft.Exchange.WebServices.dll”
$service = new-object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.Exchangeversion]::exchange2013)
$service.Url = new-object System.Uri($EwsServer)

$Provider=New-Object Microsoft.CSharp.CSharpCodeProvider
$Compiler=$Provider.CreateCompiler()
$Params=New-Object System.CodeDom.Compiler.CompilerParameters
$Params.GenerateExecutable=$False
$Params.GenerateInMemory=$True
$Params.IncludeDebugInformation=$False
$Params.ReferencedAssemblies.Add(“System.DLL”) | Out-Null

$TASource=@’
namespace Local.ToolkitExtensions.Net.CertificatePolicy{
public class TrustAll : System.Net.ICertificatePolicy {
public TrustAll() {
}
public bool CheckValidationResult(System.Net.ServicePoint sp,
System.Security.Cryptography.X509Certificates.X509Certificate cert,
System.Net.WebRequest req, int problem) {
return true;
}
}
}
‘@
$TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource)
$TAAssembly=$TAResults.CompiledAssembly

## We now create an instance of the TrustAll and attach it to the ServicePointManager
$TrustAll=$TAAssembly.CreateInstance(“Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll”)
[System.Net.ServicePointManager]::CertificatePolicy=$TrustAll

#CAS URL Option 1 Autodiscover
$service.AutodiscoverUrl($MailboxName,{$true})
“Using CAS Server : ” + $Service.url
# Bind to the Inbox Folder
$folderid= new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox,$MailboxName)
$Inbox = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid)

#Define ItemView to retrive just 1000 Items
$ivItemView =  New-Object Microsoft.Exchange.WebServices.Data.ItemView(1000)
$psPropset= new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::IdOnly)
$psPropset.Add([Microsoft.Exchange.WebServices.Data.ItemSchema]::Size)
$psPropset.Add([Microsoft.Exchange.WebServices.Data.ItemSchema]::DateTimeReceived)
$psPropset.Add([Microsoft.Exchange.WebServices.Data.ItemSchema]::DateTimeCreated)
$ivItemView.PropertySet = $psPropset
$TotalSize = 0
$TotalItemCount = 0

#Define Function to convert String to FolderPath
function ConvertToString($ipInputString){
$Val1Text = “”
for ($clInt=0;$clInt -lt $ipInputString.length;$clInt++){
$Val1Text = $Val1Text + [Convert]::ToString([Convert]::ToChar([Convert]::ToInt32($ipInputString.Substring($clInt,2),16)))
$clInt++
}
return $Val1Text
}

#Define Extended properties
$PR_FOLDER_TYPE = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(13825,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer);
$folderidcnt = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot,$MailboxName)
$folderidcnt_r = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::RecoverableItemsRoot,$MailboxName)

#Define the FolderView used for Export should not be any larger then 1000 folders due to throttling
$fvFolderView =  New-Object Microsoft.Exchange.WebServices.Data.FolderView(1000)

#Deep Transval will ensure all folders in the search path are returned
$fvFolderView.Traversal = [Microsoft.Exchange.WebServices.Data.FolderTraversal]::Deep;
$psPropertySet = new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)
$PR_Folder_Path = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(26293, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String);

#Add Properties to the  Property Set
$psPropertySet.Add($PR_Folder_Path);
$fvFolderView.PropertySet = $psPropertySet;

#The Search filter will exclude any Search Folders
$sfSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo($PR_FOLDER_TYPE,”1″)
$fiResult = $null
$rptHash = @{}
$minYear = (Get-Date).Year
$maxYear = (Get-Date).Year

#The Do loop will handle any paging that is required if there are more the 1000 folders in a mailbox
do {
$fiResult = $Service.FindFolders($folderidcnt,$sfSearchFilter,$fvFolderView)
$fiResult2 = $Service.FindFolders($folderidcnt_r,$sfSearchFilter,$fvFolderView)

$collection_folders = @()
$Collection_Folders = $firesult.folders
$Collection_Folders += $firesult2.folders

$firesult3 = @()
$firesult3 = $fiResult
$firesult3 += $fiResult2

foreach($ffFolder in $collection_Folders){
#foreach($ffFolder in $fiResult.Folders){
$foldpathval = $null
#Try to get the FolderPath Value and then covert it to a usable String
if ($ffFolder.TryGetProperty($PR_Folder_Path,[ref] $foldpathval))
{
$binarry = [Text.Encoding]::UTF8.GetBytes($foldpathval)
$hexArr = $binarry | ForEach-Object { $_.ToString(“X2″) }
$hexString = $hexArr -join ”
$hexString = $hexString.Replace(“FEFF”, “5C00”)
$fpath = ConvertToString($hexString)
}
“FolderPath : ” + $fpath

#Define ItemView to retrive just 1000 Items
$ivItemView =  New-Object Microsoft.Exchange.WebServices.Data.ItemView(1000)
$psPropset= new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::IdOnly)
$psPropset.Add([Microsoft.Exchange.WebServices.Data.ItemSchema]::Size)
$psPropset.Add([Microsoft.Exchange.WebServices.Data.ItemSchema]::DateTimeReceived)
$psPropset.Add([Microsoft.Exchange.WebServices.Data.ItemSchema]::DateTimeCreated)
$ivItemView.PropertySet = $psPropset

$fiItems = $null
do{
$fiItems = $service.FindItems($ffFolder.Id,$ivItemView)
#[Void]$service.LoadPropertiesForItems($fiItems,$psPropset)
foreach($Item in $fiItems.Items){
$dateVal = $null
if($Item.TryGetProperty([Microsoft.Exchange.WebServices.Data.ItemSchema]::DateTimeReceived,[ref]$dateVal )-eq $false){
$dateVal = $Item.DateTimeCreated
}
if($rptHash.ContainsKey($dateVal.Year)){
$rptHash[$dateVal.Year].TotalNumber += 1
$rptHash[$dateVal.Year].TotalSize += [Int64]$Item.Size
}
else{
$rptObj = “” | Select TotalNumber,TotalSize
$rptObj.TotalNumber = 1
$rptObj.TotalSize = [Int64]$Item.Size
$rptHash.add($dateVal.Year,$rptObj)
if($dateVal.Year -lt $minYear){$minYear = $dateVal.Year}
}
}
$ivItemView.Offset += $fiItems.Items.Count
}while($fiItems.MoreAvailable -eq $true)
}
$fvFolderView.Offset += $collection_folders.Count
}while($fiResult3.MoreAvailable -eq $true)

$rptCollection = @()
$fnFinalreporttlt = “” | Select Name,TotalNumber,TotalSize, @{Name=”Mbx”;Expression={“$mailboxname”}}
$fnFinalreporttlt.Name = $MailboxName
$rptCollection += $fnFinalreporttlt
$fnFinalreport = “” | Select Name,TotalNumber,TotalSize,@{Name=”Mbx”;Expression={“$mailboxname”}}
$rptCollection += $fnFinalreport
$rptHash.GetEnumerator() | Sort-Object Name | ForEach-Object {
$fnFinalreport = “” | Select Name,TotalNumber,TotalSize,@{Name=”Mbx”;Expression={“$mailboxname”}}
$fnFinalreport.Name = $_.key
$fnFinalreport.TotalNumber = $rptHash[$_.key].TotalNumber
$fnFinalreport.TotalSize = [Math]::Round($rptHash[$_.key].TotalSize/1MB,2)
$fnFinalreporttlt.TotalNumber += $rptHash[$_.key].TotalNumber
$fnFinalreporttlt.TotalSize += [Math]::Round($rptHash[$_.key].TotalSize/1MB,2)
$rptCollection += $fnFinalreport
}

$rptCollection | Select Mbx,Name,TotalNumber,TotalSize

$rptCollection | Export-csv $export_log

############################

$tableStyle = @”
<style>
BODY{background-color:white;}
TABLE{border-width: 1px;
border-style: solid;
border-color: black;
border-collapse: collapse;
}
TH{border-width: 1px;
padding: 10px;
border-style: solid;
border-color: black;
background-color:#66CCCC
}
TD{border-width: 1px;
padding: 2px;
border-style: solid;
border-color: black;
background-color:white
}
</style>
“@

$body = @”
<p style=”font-size:25px;family:calibri;color:#ff9100″>
$TableHeader
</p>
“@

############################################

$PST_Size = $null

#Loop
$rptcollection | Where {($_.Name -ne “”) -and ($_.name -notlike “*@*”)} |  %{
#Declare Variables

$Size = [int]$_.TotalSize
$Name = $_.Mbx
$year = $_.Name
$Start = “1/1/$year”
$End = $Start;$End = [datetime]$End;$End = $end.AddYears(1);$End = $End.ToString(“M/d/yyyy”)

“`n`rStarting Loop for:  $Name – $Size – $Start <–> $End”

If((Test-Path $PSTFilePath$NAME) -eq $False){MD “$PSTFilePath$NAME”}
If($Size -lt $Size_For_PST){
$PSTName = $Name+”_”+$Start+”_”+$End
$PSTName = $PSTName -replace (“/”,”-“)
$F = “$PSTFilePath$Name$PstName.pst”
write-host “…..Size is less than $Size_for_PST” -foregroundcolor cyan
New-MailboxExportRequest -mailbox $Name -contentFilter {(Received -ge $start) -and (Received -le $end)} -name $PSTName -filepath $f -baditemlimit 1000000 -acceptlargedataloss -batchname $Batchname
}

If($Size -gt $Size_For_PST){
$R = [math]::Round($Size/$Size_For_PST)
If(($R -eq 0) -or ($R -eq 1)){$R = 2}
$Months = 12/$r
$count = 0
$LoopEnd = [datetime]$End
“Startin Loop  r = $R, months = $Months”

Do{
$Count++
write-host “Size is bigger than $Size_for_PST” -foregroundcolor green
$start = [datetime]$Start
If($start -ge (Get-date)){Break}
$end = $start.AddMonths($Months)
$start = $start.tostring(“M/d/yyyy”)
$end = $end.tostring(“M/d/yyyy”)
$PSTName = $Name+”_”+$Start+”_”+$End
$PSTName = $PSTName -replace (“/”,”-“)
$F = “$PSTFilePath$Name$PstName.pst”
“…..New Mailbox Export Request for $name for  $start –> $end”
$start = $End
$Loopstart = [datetime]$start
New-MailboxExportRequest -mailbox $Name -contentFilter {(Received -ge $start) -and (Received -le $end)} -name $PSTName -filepath $f -baditemlimit 1000000 -acceptlargedataloss -batchname $Batchname
}While($loopstart -ne $Loopend)
“End Loop”
}
}

 

Exchange PowerShell: Export a mailbox into multiple psts by size using native Exchange commands

Scenario:  Using native Exchange PowerShell commands, export a single mailbox into multiple 10GB PSTs (or another size). This is to mimic the eDiscovery tool in which it makes multiple PST’s that stay around 10GB each.

The script/commands below will perform a search mailbox until it hits the 10000 item limit.  When it hits the 10,000 item limit, it finds the previous  date where the search results is under the 10,000 item limit and records it.  Then the script will take all the dates it records and add the size until it hits around 10GB.  It records those dates in a array and then exports to a PST.

Script:

#Initialize Variables###########################################
$mbx = “Steve”
$stat = Get-mailboxstatistics $mbx | Select DisplayName,TotalDeletedItemSize, TotalItemSize,ItemCount
$TotalSize = [int]$stat.TotalItemSize.Value.ToMB() + [int]$stat.TotalDeletedItemSize.Value.ToMB()
$SizeforPST = [int]10240 ; “$MBX : Total Size $totalSize MB”
$CutThePST = “Yes”
$PSTFilePath =”\ExSvr1PSTFILES”
###############################################################

#If MBX totalsize is greater than $SizeforPST ###############################################################
If($TotalSize -gt $SizeforPST){

“Entering If Statement:  TotalSize ($TotalSize) is greater than $SizeForPST”

#Find Oldest Item in Mailbox to know when to stop searching
$OldestItem = Get-MailboxFolderStatistics $mbx -IncludeOldestAndNewestItems | Where OldestItemReceivedDate -ne $Null | Sort OldestItemReceivedDate | Select -ExpandProperty OldestItemReceivedDate -first 1
$oldestItem = $oldestItem.AddMonths(-1)  #Subtract a month to ensure that search goes before oldest item.
$OldestItem = $OldestItem.ToSTring((“M/d/yyyy”))
#Set Start and End Month 1 Month Apart
$end = get-date -Format “M/d/yyyy”
$start = [datetime]$end
$start= $start.AddMonths(-1)
$start = $start.ToString((“M/d/yyyy”))

#Initialize $results Variable and Final variable
$Results = Search-Mailbox $mbx -SearchDumpster -SearchQuery “Received:$start..$End” –estimateresultonly | Select ResultItemsCount,@{n=’Size’;e={([Microsoft.Exchange.Data.ByteQuantifiedSize]$_.ResultItemsSize).ToMb()}}
$final = @()

#Loop It –
Do{
If($results.ResultItemsCount -ge 10000){
Do{
$start= [datetime]$start
$start= $start.AddDays(5)
$start = $start.ToString((“M/d/yyyy”))
$Results = Search-Mailbox $mbx -SearchDumpster -SearchQuery “Received:$start..$End” –estimateresultonly | Select ResultItemsCount,@{n=’Size’;e={([Microsoft.Exchange.Data.ByteQuantifiedSize]$_.ResultItemsSize).ToMb()}}
“ResultCount: $results.ResultItemsCount between $start – $end”
}While($Results.ResultItemsCount -ge 10000)
}

If($Results.ResultItemsCount -lt 10000){
Do{
If([datetime]$Start -lt [datetime]$oldestitem){Break}
$start= [datetime]$start
$start= $start.AddMonths(-1)
$start = $start.ToString((“M/d/yyyy”))
$Results = Search-Mailbox $mbx -SearchDumpster -SearchQuery “Received:$start..$End” –estimateresultonly | Select ResultItemsCount,@{n=’Size’;e={([Microsoft.Exchange.Data.ByteQuantifiedSize]$_.ResultItemsSize).ToMb()}}
“ResultCount: $results.ResultItemsCount between $start – $end”
}While($Results.ResultItemsCount -lt 10000)

#Since ResultItemCount just became greater than or equal too 10000, subtract add a month to start.
$start= [datetime]$start
$start= $start.AddMonths(1)
$start = $start.ToString((“M/d/yyyy”))
$Results = Search-Mailbox $mbx -SearchDumpster -SearchQuery “Received:$start..$End” –estimateresultonly | Select ResultItemsCount,@{n=’Size’;e={([Microsoft.Exchange.Data.ByteQuantifiedSize]$_.ResultItemsSize).ToMb()}}

}

“Result Count: $($results.ResultItemsCount) Size $($results.size)”

#Build the Array
$ServerObj = New-Object PSObject
$ServerObj | Add-Member NoteProperty -Name “Alias” -Value $mbx
$ServerObj | Add-Member NoteProperty -Name “Start” -Value $start
$ServerObj | Add-Member NoteProperty -Name “End” -Value $end
$ServerObj | Add-Member NoteProperty -Name “ItemCount” -Value $results.ResultItemsCount
$ServerObj | Add-Member NoteProperty -Name “Size” -Value $results.Size
$Final += $ServerObj

#Recalibrate Variables
$end = $start
$start = [datetime]$end
$start= $start.AddMonths(-1)
$start = $start.ToString((“M/d/yyyy”))
$Results = Search-Mailbox $mbx -SearchDumpster -SearchQuery “Received:$start..$End” –estimateresultonly | Select ResultItemsCount,@{n=’Size’;e={([Microsoft.Exchange.Data.ByteQuantifiedSize]$_.ResultItemsSize).ToMb()}}

“While Start: $start > OldestItem: $oldestitem”
}While([datetime]$start -ge [datetime]$OldestItem)

$ServerObj = New-Object PSObject
$ServerObj | Add-Member NoteProperty -Name “Alias” -Value $mbx
$ServerObj | Add-Member NoteProperty -Name “Start” -Value $start
$ServerObj | Add-Member NoteProperty -Name “End” -Value $end
$ServerObj | Add-Member NoteProperty -Name “ItemCount” -Value $results.ResultItemsCount
$ServerObj | Add-Member NoteProperty -Name “Size” -Value $results.Size
$Final += $ServerObj
###########################################################################################################

#Create a PST Hash Table

$PST_Final = @()
$PSTSize = 999999

$Final | %{
“____________________________________”
“Starting Loop”

if($PSTSize -eq 999999){

$PSTSize = $_.Size
$PST_end = $_.End
$PST_Start = $_.Start
“1st Loop: $PST_Start — $PST_end — $PSTSize”

}else{

“Entering Else Loop”
$PST_Start_temp = $_.start
$PST_End_temp = $_.end

#When on second pass, add the two sizes together
$PSTSize_temp = $PSTSize + $_.Size
Write-host “PST Size = $PSTSize_Temp” -ForegroundColor Cyan

If($PSTSize_temp -lt $SizeForPST){
“..Less than $SizeForPST  ($PSTSize_Temp)”
$PSTSize = $PSTSize_Temp
$PST_Start = $PST_Start_Temp

“….New DateRange:  $PST_Start — $PST_END”

}

If($PSTSize_Temp -gt $SizeForPST){
“..Greater Than $SizeForPST ($PSTSize_temp)”
$ServerObj = New-Object PSObject
$ServerObj | Add-Member NoteProperty -Name “Alias” -Value $mbx
$ServerObj | Add-Member NoteProperty -Name “Start” -Value $PST_Start
$ServerObj | Add-Member NoteProperty -Name “End” -Value $PST_End
$ServerObj | Add-Member NoteProperty -Name “Size” -Value $PSTSize
$PST_Final += $ServerObj

$PST_END = $PST_Start
$PSTSize = [int]0

}

}

}

$ServerObj = New-Object PSObject
$ServerObj | Add-Member NoteProperty -Name “Alias” -Value $mbx
#$ServerObj | Add-Member NoteProperty -Name “Start” -Value $PST_Start
$ServerObj | Add-Member NoteProperty -Name “Start” -Value “1/1/1900”
$ServerObj | Add-Member NoteProperty -Name “End” -Value $PST_End
$ServerObj | Add-Member NoteProperty -Name “Size” -Value $PSTSize
$PST_Final += $ServerObj

$PST_FINAL

}

If($PST_Final -eq $null){
$ServerObj = New-Object PSObject
$ServerObj | Add-Member NoteProperty -Name “Alias” -Value $mbx
#$ServerObj | Add-Member NoteProperty -Name “Start” -Value $PST_Start
$ServerObj | Add-Member NoteProperty -Name “Start” -Value “1/1/1900”
$ServerObj | Add-Member NoteProperty -Name “End” -Value $(get-date -Format “M/d/yyyy”)
$ServerObj | Add-Member NoteProperty -Name “Size” -Value $TotalSize
$PST_Final += $ServerObj

}

#Export the PST for this user
If($CutThePST -eq “Yes”){

$PST_Final | %{
$N = $_.Alias
$S = $_.Start
$E = $_.End
$PSTName = $N+”_”+$S+”_”+$E
$PSTName = $PSTName -replace (“/”,”-“)
$F = “$PSTFilePath$PstName.pst”
New-mailboxExportRequest -mailbox $N -contentfilter {(Received -ge $s) -and (Received -le $e)} -name $PSTName -filepath $F -baditemlimit 1000000 -acceptlargedataloss
}
}

Exchange PowerShell script that will perform a mailbox count on each database and email the results.

Below is a Exchange PowerShell script that will perform a mailbox count on each database and email the results.  The script is performed with the get-mailboxstatistics for each mailbox on that database as the results are much faster than the get-mailbox command.


# Create an empty HashTable to store database name and count.
$MailboxCount = @{}

# Collect Databases
$databases = Get-mailboxDatabase | Where Name -like “2013DB*” | Sort name

#Loop through each
ForEach ($database in $databases){
$MBs = Get-mailboxstatistics -database $database
$MailboxCount.Add($Database,$MBs.count)
}

#Format the Results
$MailboxCountOrdered = $MailboxCount.GetEnumerator() | Sort-Object Name | Out-String
$orderedMailboxCount = $MailboxCount.GetEnumerator() | Sort-object Value | Out-String

#Send an email:
$SmtpClient = new-object system.net.mail.smtpClient 
$MailMessage = New-Object system.net.mail.mailmessage 
$SmtpClient.Host = “mail.server.com” 
$mailmessage.from = (“MailboxCount@domain.com”) 
$mailmessage.To.add(“email.address”) 
$mailmessage.Subject = “Mailbox Count”
$mailmessage.Body = “Mailbox Count

The following list shows the Mailbox Count for the Databases in Ex2013. The 2 lists below are the same; one is in order of database name and the other is in order of mailbox count.
Database Order:
$MailboxCountOrdered 
Count Order:
$OrderedMailboxCount

$smtpclient.Send($mailmessage)