Monday, September 09, 2013

Exchange Attachment statistics reporting with EWS and Powershell

The following script is a combination of a couple of other scripts I've posted in the past, what it does is compiles statistics of the messages with and without attachments in a Mailbox and then produces a report. To do this in EWS you need to look at every Item in the mailbox,  if the item has attachments it determines how large these attachments are and keeps a running tab of the largest attachment in the mailbox. In Exchange there are a number of different attachment types but EWS divides them into two distinct types, File Attachments and ItemAttachment which are basically attached Exchange Items. This script compile statistics around these two distinct types but doesn't process down to the embeeded attachment level. The results of all the requests are added together to produce a final report that reflects the attachment statistics for the entire mailbox. A picture is worth a thousand words so it produces a report like


 To get the information about the Items in the Mailbox the FindItem Operation is used, however as FindItem doesn't return detailed information about attachments the LoadPropertiesForItems method is used which does a batch GetItem request on a collection of results. One thing to note is the AttachmentSize information isn't returned by EWS in Exchange 2007 so this script will only work for Exchange 2010 up.

This script will prompt for what security credentials you want to use and these creds need full access to the Mailboxes your scanning (an alternative would be to use Impersonation but that would require the script to be changed).

You need to feed the script with a CSV file like

smtpaddress
user1@domain.com
user2@domain.com

I've put a download of this script here  the script itself looks like.

  1. $Script:rptCollection = @()    
  2. ## Load Managed API dll    
  3. Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\2.0\Microsoft.Exchange.WebServices.dll"    
  4.     
  5. ## Set Exchange Version    
  6. $ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP2    
  7.     
  8. ## Create Exchange Service Object    
  9. $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)    
  10.     
  11. ## Set Credentials to use two options are availible Option1 to use explict credentials or Option 2 use the Default (logged On) credentials    
  12.     
  13. #Credentials Option 1 using UPN for the windows Account    
  14. $psCred = Get-Credential    
  15. $creds = New-Object System.Net.NetworkCredential($psCred.UserName.ToString(),$psCred.GetNetworkCredential().password.ToString())    
  16. $service.Credentials = $creds        
  17.     
  18. #Credentials Option 2    
  19. #service.UseDefaultCredentials = $true    
  20.     
  21. ## Choose to ignore any SSL Warning issues caused by Self Signed Certificates    
  22.     
  23. ## Code From http://poshcode.org/624  
  24. ## Create a compilation environment  
  25. $Provider=New-Object Microsoft.CSharp.CSharpCodeProvider  
  26. $Compiler=$Provider.CreateCompiler()  
  27. $Params=New-Object System.CodeDom.Compiler.CompilerParameters  
  28. $Params.GenerateExecutable=$False  
  29. $Params.GenerateInMemory=$True  
  30. $Params.IncludeDebugInformation=$False  
  31. $Params.ReferencedAssemblies.Add("System.DLL") | Out-Null  
  32.   
  33. $TASource=@' 
  34.   namespace Local.ToolkitExtensions.Net.CertificatePolicy{ 
  35.     public class TrustAll : System.Net.ICertificatePolicy { 
  36.       public TrustAll() {  
  37.       } 
  38.       public bool CheckValidationResult(System.Net.ServicePoint sp, 
  39.         System.Security.Cryptography.X509Certificates.X509Certificate cert,  
  40.         System.Net.WebRequest req, int problem) { 
  41.         return true; 
  42.       } 
  43.     } 
  44.   } 
  45. '@   
  46. $TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource)  
  47. $TAAssembly=$TAResults.CompiledAssembly  
  48.   
  49. ## We now create an instance of the TrustAll and attach it to the ServicePointManager  
  50. $TrustAll=$TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll")  
  51. [System.Net.ServicePointManager]::CertificatePolicy=$TrustAll  
  52.   
  53. ## end code from http://poshcode.org/624  
  54.     
  55. ## Set the URL of the CAS (Client Access Server) to use two options are availbe to use Autodiscover to find the CAS URL or Hardcode the CAS to use    
  56.     
  57. #CAS URL Option 1 Autodiscover    
  58. #$service.AutodiscoverUrl($MailboxName,{$true})    
  59. #"Using CAS Server : " + $Service.url     
  60.      
  61. #CAS URL Option 2 Hardcoded    
  62.     
  63. #$uri=[system.URI] "https://casservername/ews/exchange.asmx"    
  64. #$service.Url = $uri      
  65.     
  66. ## Optional section for Exchange Impersonation    
  67.     
  68. #$service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $MailboxName)   
  69.   
  70.   
  71. function Process-Mailbox{  
  72.     param (  
  73.             $SmtpAddress = "$( throw 'SMTPAddress is a mandatory Parameter' )"  
  74.           )  
  75.     process{  
  76.     $rptObj = "" | select MailboxName,TotalItem,TotalItemSize,TotalItemsNoAttach,TotalItemsNoAttachSize,TotalItemsAttach,TotalItemsAttachSize,TotalFileAttachments,TotalFileAttachmentsSize,TotalItemAttachments,TotalItemAttachmentsSize,LargestAttachmentSize,LargestAttachmentName  
  77.     $rptObj.MailboxName = $SmtpAddress  
  78.     $rptObj.TotalItem = 0  
  79.     $rptObj.TotalItemSize = [Int64]0  
  80.     $rptObj.TotalItemsNoAttach = 0  
  81.     $rptObj.TotalItemsNoAttachSize = [Int64]0  
  82.     $rptObj.TotalItemsAttach = 0  
  83.     $rptObj.TotalItemsAttachSize = [Int64]0  
  84.     $rptObj.TotalFileAttachments = 0  
  85.     $rptObj.TotalFileAttachmentsSize  = [Int64]0  
  86.     $rptObj.TotalItemAttachments = 0  
  87.     $rptObj.TotalItemAttachmentsSize  = [Int64]0  
  88.     $rptObj.LargestAttachmentSize = [Int64]0  
  89.     $rptObj.LargestAttachmentName = ""  
  90.     "Processing Mailbox : " + $SmtpAddress  
  91.       
  92.     #check Anchor header for Exchange 2013/Office365  
  93.     if($service.HttpHeaders.ContainsKey("X-AnchorMailbox")){  
  94.         $service.HttpHeaders["X-AnchorMailbox"] = $SmtpAddress  
  95.     }else{  
  96.         $service.HttpHeaders.Add("X-AnchorMailbox"$SmtpAddress);  
  97.     }  
  98.     "AnchorMailbox : " + $service.HttpHeaders["X-AnchorMailbox"]  
  99.     #Define ItemView to retrive just 1000 Items      
  100.     $ivItemView =  New-Object Microsoft.Exchange.WebServices.Data.ItemView(1000)    
  101.     $psPropset= new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::IdOnly)    
  102.     $psPropset.Add([Microsoft.Exchange.WebServices.Data.ItemSchema]::Size)  
  103.     $psPropset.Add([Microsoft.Exchange.WebServices.Data.ItemSchema]::DateTimeReceived)  
  104.     $psPropset.Add([Microsoft.Exchange.WebServices.Data.ItemSchema]::DateTimeCreated)  
  105.     $ivItemView.PropertySet = $psPropset  
  106.     $TotalSize = 0  
  107.     $TotalItemCount = 0  
  108.   
  109.   
  110.     #Define Function to convert String to FolderPath    
  111.     function ConvertToString($ipInputString){    
  112.         $Val1Text = ""    
  113.         for ($clInt=0;$clInt -lt $ipInputString.length;$clInt++){    
  114.                 $Val1Text = $Val1Text + [Convert]::ToString([Convert]::ToChar([Convert]::ToInt32($ipInputString.Substring($clInt,2),16)))    
  115.                 $clInt++    
  116.         }    
  117.         return $Val1Text    
  118.     }   
  119.   
  120.     #Define Extended properties    
  121.     $PR_FOLDER_TYPE = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(13825,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer);    
  122.     $folderidcnt = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot,$SmtpAddress)    
  123.     #Define the FolderView used for Export should not be any larger then 1000 folders due to throttling    
  124.     $fvFolderView =  New-Object Microsoft.Exchange.WebServices.Data.FolderView(1000)    
  125.     #Deep Transval will ensure all folders in the search path are returned    
  126.     $fvFolderView.Traversal = [Microsoft.Exchange.WebServices.Data.FolderTraversal]::Deep;    
  127.     $psPropertySet = new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)    
  128.     $PR_Folder_Path = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(26293, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String);    
  129.     #Add Properties to the  Property Set    
  130.     $psPropertySet.Add($PR_Folder_Path);    
  131.     $fvFolderView.PropertySet = $psPropertySet;    
  132.     #The Search filter will exclude any Search Folders    
  133.     $sfSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo($PR_FOLDER_TYPE,"1")    
  134.     $fiResult = $null    
  135.     #The Do loop will handle any paging that is required if there are more the 1000 folders in a mailbox    
  136.     do {    
  137.         $fiResult = $Service.FindFolders($folderidcnt,$sfSearchFilter,$fvFolderView)    
  138.         foreach($ffFolder in $fiResult.Folders){    
  139.             $foldpathval = $null    
  140.             #Try to get the FolderPath Value and then covert it to a usable String     
  141.             if ($ffFolder.TryGetProperty($PR_Folder_Path,[ref] $foldpathval))    
  142.             {    
  143.                 $binarry = [Text.Encoding]::UTF8.GetBytes($foldpathval)    
  144.                 $hexArr = $binarry | ForEach-Object { $_.ToString("X2") }    
  145.                 $hexString = $hexArr -join ''    
  146.                 $hexString = $hexString.Replace("FEFF""5C00")    
  147.                 $fpath = ConvertToString($hexString)    
  148.             }    
  149.               
  150.             $totalItemCnt = 1  
  151.             if($ffFolder.TotalCount -ne $null){  
  152.                 $totalItemCnt = $ffFolder.TotalCount  
  153.                 "Processing FolderPath : " + $fpath  + " Item Count " + $totalItemCnt  
  154.             }  
  155.             else{  
  156.                 "Processing FolderPath : " + $fpath  
  157.             }  
  158.             if($totalItemCnt -gt 0){  
  159.                 #Define ItemView to retrive just 1000 Items      
  160.                 $ivItemView =  New-Object Microsoft.Exchange.WebServices.Data.ItemView(1000)    
  161.                 $psPropset= new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::IdOnly)    
  162.                 $psPropset.Add([Microsoft.Exchange.WebServices.Data.ItemSchema]::Size)  
  163.                 $psPropset.Add([Microsoft.Exchange.WebServices.Data.ItemSchema]::DateTimeReceived)  
  164.                 $psPropset.Add([Microsoft.Exchange.WebServices.Data.ItemSchema]::DateTimeCreated)  
  165.                 $psPropset.Add([Microsoft.Exchange.WebServices.Data.ItemSchema]::Attachments)  
  166.                 $psPropset.Add([Microsoft.Exchange.WebServices.Data.ItemSchema]::HasAttachments)  
  167.                 $fipsPropset= new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::IdOnly)    
  168.                 $ivItemView.PropertySet = $fipsPropset        
  169.                 $fiItems = $null      
  170.                 do{      
  171.                     $fiItems = $service.FindItems($ffFolder.Id,$ivItemView)   
  172.                     if($fiItems.Items.Count -gt 0){  
  173.                         [Void]$service.LoadPropertiesForItems($fiItems,$psPropset)   
  174.                         "processing : " + $fiItems.Items.Count + " Items"  
  175.                         foreach($Item in $fiItems.Items){  
  176.                             $rptObj.TotalItem +=1  
  177.                             $rptObj.TotalItemSize += [Int64]$Item.Size  
  178.                             if($Item.Attachments.Count -gt 0){  
  179.                                 $rptObj.TotalItemsAttach +=1  
  180.                                 $rptObj.TotalItemsAttachSize += [Int64]$Item.Size  
  181.                                 foreach($Attachment in $Item.Attachments){                            
  182.                                     if($Attachment -is [Microsoft.Exchange.WebServices.Data.FileAttachment]){  
  183.                                         $rptObj.TotalFileAttachments +=1  
  184.                                         $rptObj.TotalFileAttachmentsSize += $Attachment.Size  
  185.                                         $attachSize = [Math]::Round($Attachment.Size/1MB,2)  
  186.                                         if($attachSize -gt $rptobj.LargestAttachmentSize){  
  187.                                             $rptobj.LargestAttachmentSize = $attachSize  
  188.                                             $rptobj.LargestAttachmentName = $Attachment.Name  
  189.                                         }  
  190.                                     }  
  191.                                     else{  
  192.                                         $rptObj.TotalItemAttachments +=1  
  193.                                         $rptObj.TotalItemAttachmentsSize += $Attachment.Size  
  194.                                     }  
  195.                                 }  
  196.                             }  
  197.                             else{  
  198.                                 $rptObj.TotalItemsNoAttach +=1  
  199.                                 $rptObj.TotalItemsNoAttachSize += [Int64]$Item.Size  
  200.                             }  
  201.                         }  
  202.                     }      
  203.                     $ivItemView.Offset += $fiItems.Items.Count      
  204.                 }while($fiItems.MoreAvailable -eq $true)  
  205.             }  
  206.         }   
  207.         $fvFolderView.Offset += $fiResult.Folders.Count  
  208.     }while($fiResult.MoreAvailable -eq $true)  
  209.     #convert Sizes to MB  
  210.   
  211.     if($rptObj.TotalItemSize -ne 0){  
  212.         $rptObj.TotalItemSize = [Math]::Round($rptObj.TotalItemSize/1MB)  
  213.     }  
  214.     if($rptObj.TotalItemsNoAttachSize -ne 0){  
  215.         $rptObj.TotalItemsNoAttachSize = [Math]::Round($rptObj.TotalItemsNoAttachSize/1MB)  
  216.     }  
  217.     if($rptObj.TotalItemsAttachSize -ne 0){  
  218.         $rptObj.TotalItemsAttachSize = [Math]::Round($rptObj.TotalItemsAttachSize/1MB)  
  219.     }  
  220.     if($rptObj.TotalFileAttachmentsSize -ne 0){  
  221.         $rptObj.TotalFileAttachmentsSize = [Math]::Round($rptObj.TotalFileAttachmentsSize/1MB)  
  222.     }  
  223.     if($rptObj.TotalItemAttachmentsSize -ne 0){  
  224.         $rptObj.TotalItemAttachmentsSize = [Math]::Round($rptObj.TotalItemAttachmentsSize/1MB)  
  225.     }  
  226.     $Script:rptCollection += $rptObj  
  227.     }  
  228. }  
  229.   
  230. Import-Csv -Path $args[0] | ForEach-Object{  
  231.     if($service.url -eq $null){  
  232.         $service.AutodiscoverUrl($_.SmtpAddress,{$true})   
  233.         "Using CAS Server : " + $Service.url   
  234.     }  
  235.     Try{  
  236.         Process-Mailbox -SmtpAddress $_.SmtpAddress  
  237.     }  
  238.     catch{  
  239.         LogWrite("Error processing Mailbox : " + $_.SmtpAddress + $_.Exception.Message.ToString())  
  240.     }  
  241. }  
  242. $Script:rptCollection | Export-Csv -NoTypeInformation -Path c:\temp\mbAttachReport.csv  




29 comments:

Eric Ruel said...

Hi,

Do you know why LogWrite is not reconized? And, can you give me an expmple of the syntax of .csv input?

Thanks

Glen Scales said...

I'd use Write-host instead (cut and paste error)

the CSV only need one column

smtpaddress
user1@domain.com
user2@domain.com

the use something like ./script.ps1 c:\temp\csvfilename.csv

Cheers
Glen

Eric Ruel said...

Hi,

Thanks for your fast answer. I got no error but mbAttachReport.csv is empty. Do you know how i can debug this?

Thanks

Glen Scales said...

Do you see it processing any Mailboxes it should echo this back to screen ?

If it blank it sounds like you csv isn't format correctly or your just not running it correctly you can run the script from within the ISE.

Eric Ruel said...

In my csv i write:

mail.domain.com
user@domain.com

Is ok?

Glen Scales said...

You need a header row like i posted eg

smtpaddress
user1@domain.com
user2@domain.com

Cheers
Glen

Anonymous said...

I'm seeing this error message when running the .\mbstatsAttach.ps1

The term 'LogWrite' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the sp
elling of the name, or if a path was included, verify that the path is correct and try again.
At D:\Exchange Server\bin\mbstatsAttach.ps1:239 char:11
+ LogWrite <<<< ("Error processing Mailbox : " + $_.SmtpAddress + $_.Exception.Message.ToString())
+ CategoryInfo : ObjectNotFound: (LogWrite:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException

Glen Scales said...

Replace LogWrite with
write-host

That should then echo the error to the console instead

Glen

Mike Bradley said...

This is what I get when running, always complains about SmtpAddress even though the address shows up. cmdlet Get-Credential at command pipeline position 1
Supply values for the following parameters:
Credential
The variable '$MailboxName' cannot be retrieved because it has not been set.
At C:\scripts\mbstatsAttach-1.ps1:58 char:38
+ $service.AutodiscoverUrl($MailboxName <<<< ,{$true})
+ CategoryInfo : InvalidOperation: (MailboxName:Token) [], RuntimeException
+ FullyQualifiedErrorId : VariableIsUndefined

Using CAS Server :
Using CAS Server : https://myserver.mydomain.com/EWS/Exchange.asmx
Processing Mailbox : User1@mydomain.com
AnchorMailbox : User1@mydomain.com
Property 'SmtpAddress' cannot be found on this object. Make sure that it exists.
At C:\scripts\mbstatsAttach-1.ps1:240 char:49
+ Write-Host("Error processing Mailbox : " + $_. <<<< SmtpAddress + $_.Exception.Message.ToString())
+ CategoryInfo : InvalidOperation: (.:OperatorToken) [], RuntimeException
+ FullyQualifiedErrorId : PropertyNotFoundStrict

Processing Mailbox : User1@mydomain.com
AnchorMailbox : User1@mydomain.com
Property 'SmtpAddress' cannot be found on this object. Make sure that it exists.
At C:\scripts\mbstatsAttach-1.ps1:240 char:49
+ Write-Host("Error processing Mailbox : " + $_. <<<< SmtpAddress + $_.Exception.Message.ToString())
+ CategoryInfo : InvalidOperation: (.:OperatorToken) [], RuntimeException
+ FullyQualifiedErrorId : PropertyNotFoundStrict
Any ideas what I need to do to get script to run? Exchange 2010 SP2

Anonymous said...

Another gem, Glen! Thank you. MS needs to "absorb" you into Exchange :)

sreekar said...

Glenn, I have come across your script for checking number of attachments for a mailbox. I am seeing the following error when i execute the powershell script. Please advise how to move further,

PS C:\Users\srikar.achanta\Desktop> .\script.ps1
Add-Type : Cannot bind parameter 'Path' to the target. Exception setting "Path": "Cannot find path 'C:\Program Files\Mi
crosoft\Exchange\Web Services\2.0\Microsoft.Exchange.WebServices.dll' because it does not exist."
At C:\Users\srikar.achanta\Desktop\script.ps1:3 char:15
+ Add-Type -Path <<<< "C:\Program Files\Microsoft\Exchange\Web Services\2.0\Microsoft.Exchange.WebServices.dll"
+ CategoryInfo : WriteError: (:) [Add-Type], ParameterBindingException
+ FullyQualifiedErrorId : ParameterBindingFailed,Microsoft.PowerShell.Commands.AddTypeCommand

Unable to find type [Microsoft.Exchange.WebServices.Data.ExchangeVersion]: make sure that the assembly containing this
type is loaded.
At C:\Users\srikar.achanta\Desktop\script.ps1:6 char:73
+ $ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion] <<<< ::Exchange2010_SP2
+ CategoryInfo : InvalidOperation: (Microsoft.Excha...ExchangeVersion:String) [], RuntimeException
+ FullyQualifiedErrorId : TypeNotFound

New-Object : Cannot find type [Microsoft.Exchange.WebServices.Data.ExchangeService]: make sure the assembly containing
this type is loaded.
At C:\Users\srikar.achanta\Desktop\script.ps1:9 char:22
+ $service = New-Object <<<< Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)
+ CategoryInfo : InvalidType: (:) [New-Object], PSArgumentException
+ FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand


cmdlet Get-Credential at command pipeline position 1
Supply values for the following parameters:
Credential
Property 'Credentials' cannot be found on this object; make sure it exists and is settable.
At C:\Users\srikar.achanta\Desktop\script.ps1:16 char:10
+ $service. <<<< Credentials = $creds
+ CategoryInfo : InvalidOperation: (Credentials:String) [], RuntimeException
+ FullyQualifiedErrorId : PropertyNotFound

Import-Csv : Cannot bind argument to parameter 'Path' because it is null.
At C:\Users\srikar.achanta\Desktop\script.ps1:230 char:17
+ Import-Csv -Path <<<< $args[0] | ForEach-Object{
+ CategoryInfo : InvalidData: (:) [Import-Csv], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.ImportCsvCo
mmand

Glen Scales said...

You need to install the EWS Managed API on the workstation your running this from http://www.microsoft.com/en-au/download/details.aspx?id=35371

Cheers
Glen

Anonymous said...

A bit late to the party on this one, but is it possible to run this against an archive?

Glen Scales said...

Yes change

$folderidcnt = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot,$SmtpAddress)

to

$folderidcnt = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::ArchiveMsgFolderRoot,$SmtpAddress)

Cheers
Glen

Rui Rodrigues said...

Hello,
I'm trying to use your script but i came across with this error.

Using CAS Server :
Processing Mailbox :
Error processing Mailbox : You cannot call a method on a null-valued expression.
You cannot call a method on a null-valued expression.
At C:\RR\mbstatsAttach-1\mbstatsAttach-1.ps1:232 char:27
+ $service.AutodiscoverUrl <<<< ($_.SmtpAddress,{$true})
+ CategoryInfo : InvalidOperation: (AutodiscoverUrl:String) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull

Can you please help me to address it ?
thank you in advance

Glen Scales said...

Your feeding it blank data check the CSV file

Rui Rodrigues said...

Thank you, only after i say that. your right, and i already correct some more settings for the script for my envirroment, but now i`m blocking on this error message.
do you have an ideia ?
Using CAS Server :
Processing Mailbox :
AnchorMailbox :
Error processing Mailbox : Exception calling "FindFolders" with "3" argument(s): "The Url property on the ExchangeService object must be set.
Exception calling "AutodiscoverUrl" with "2" argument(s): "A valid SMTP address must be specified."
At C:\RR\mbstatsAttach-1\mbstatsAttach-1.ps1:232 char:27
+ $service.AutodiscoverUrl <<<< ($_.SmtpAddress,{$true})
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException

Glen Scales said...

It still looks like your feeding it blank data the processing Mailbox an anchor mailbox line should have the values from the CSV file so you either have blank lines,noheader or no data

shreeramdk said...

Can I exclude a any specific attachment ? So this gets excluded from the query ?

Glen Scales said...

I would suggest you do that when processing the results returned

Anonymous said...

Great script, thank you. Just one question, would it be possible to include easily a condition on the receiving date of the script (to only report attachments received the day before for example) ?

Glen Scales said...

If you put a If statement in so it only processes items where the DateTimeRecieved -gt .... that should be a way to do that.

Anonymous said...

Glen, I am running this against Exchange 2013 SP1 and I notice that the Items have a size but the attachments within do not. Is this a bug in the LoadPropertiesForItems method?

Anonymous said...

Glen, could you please explain what to do with
Error processing Mailbox : Exception calling "FindFolders" with "3" argument(s): "The specified object was not found in
the store."?

Glen Scales said...

That means you don't have rights on the Mailbox you trying to access. You need to grant right via Add-MailboxPermission or consider using EWS impersonation

Anonymous said...

that user is domain administrator. Script ran from ex13 CU15 cas server from EMS. As credentials also used the same administrator`s credentials.

Glen Scales said...

Domain administrator doesn't mean anything in regards to Mailbox permission. You are using a Mailbox API so only Mailbox API rights apply and Domain administrator have no more rights then anybody else the EMS is an administrative API so RBAC delegated rights apply. Also don't use the Domain Administrator for running these type of scripts its not best practice. Create an account for the job and assigned it the necessary rights.

Anonymous said...

Glen, after giving access permissions to some user for all mailboxes, it`s started to work. Now there is other strange issue: after some time (10-15 mailboxes) it stops with the same error for exception calling "FindFolders", but it says about permissions. After log off/login it starts to work for the same mailboxes. Is there any limits for mailbox counts, or items in them?

Anonymous said...

When will people learn? Microsoft's powershell NEVER works. Since its release, I have NEVER been able to use someone else's script. NEVER!