none
EWS powershell script Exchange 2013 - error handling RRS feed

  • Question

  • Hi there,
     I am tasked at the moment in my workplace to report on retention tags in use in the organisation - it seems with Exchange 2013, EWS is the only way to accomplish this...  I am tasked with reporting on users use of retention tags - across onprem and o365 -. It is working at the folder level - but it may be requested I report on retention tags at the email level as you can override what is set at the folder level at the actual email level. I have a question and to illustrate I have pasted in the script I am using - how do I incorporate error handling into this - I have never really used error handling in scripts before and EWS uses .net methods more so than native cmdlets - so I cannot use the -erroraction STOP parameter. My script takes pipeline input binding on the primarysmtpaddress property name - so you can pipe get-mailbox to it and csv files - should I wrap the whole foreach($user in $primarysmtpaddres) block in a try? and then catch at the end - I can't seem to get it to work?  I could be reporting on 500 plus mailboxes so I don't want the script to terminate - just log the error in a log file and continue onto the next?  Also as I said I may be asked to report on the tags used on individual emails - so there could be 1000's buried under the folder - and many have their own custom folders as well as the wellknown folder names? Is it possible to do this?  Here is the script I created - any help much appreciated


    [CmdletBinding()]

        param(

        [Parameter(
        Position = 0,
        Mandatory=$true,
        ValueFromPipelineByPropertyName=$true
       
        )]
        [String[]]
        $PrimarySmtpAddress

       

    )

    begin{
    <#
        if (-not (Get-PSSnapin | where {$_.Name -eq "Microsoft.Exchange.Management.PowerShell.SnapIn"} )) {
            Write-Host "Adding Exchange 2013 PowerShell role"
            Add-PSSnapin Microsoft.Exchange.Management.PowerShell.SnapIn

            }

      #>   
           
            $startTime = Get-Date
            [string]$LogFile = "C:\Temp\Log.txt" 
           
            $o365credential = get-credential -credential admin@domain.onmicrosoft.com
           
            $ExchangeSession = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "https://outlook.office365.com/powershell-liveid/" -Credential $o365credential -Authentication "Basic" -AllowRedirection
            Import-PSSession $ExchangeSession -Prefix o365 -AllowClobber

            $credential = Get-Credential "admin creds"


            Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\2.0\Microsoft.Exchange.WebServices.dll"
            $exchangeService = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService

            $Global:retentioninfo = @()
            $Global:RetentionTags = @()
           
    }

    process {

      foreach($user in $PrimarySmtpAddress){
           
                 
                   function folderretention()
                   {
                            $FPageSize = 100
                            $FOffset = 0
                            $folderView = new-object Microsoft.Exchange.WebServices.Data.FolderView($FPageSize,$FOffset,[Microsoft.Exchange.WebServices.Data.OffsetBasePoint]::Beginning)
                            $folderView.Traversal = [Microsoft.Exchange.WebServices.Data.FolderTraversal]::Deep
                            $oFindFolders = $exchangeService.FindFolders([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot,$null,$folderView)
                            $oFindFolders1 = $oFindFolders | ?{(($_.DisplayName -notlike 'Calendar') -and ($_.DisplayName -notlike '*contacts') -and ($_.DisplayName -notlike '*recipient*'))}
               
                            $Global:RetentionTags = $exchangeService.GetUserRetentionPolicyTags()

                                function GetTagName($tagGUID) {
                                if (!$tagGUID) { return ($RetentionTags.RetentionPolicyTags | ? {$_.Type -eq "All"}).DisplayName }
                                foreach ($tag in $RetentionTags.RetentionPolicyTags) {
                                if ($tag.RetentionId -eq $tagGUID ) { return $tag.DisplayName }
                                }
                            }


                                function GetRetentionAction($tagGUID)  {
                                if (!$tagGUID) { return ($RetentionTags.RetentionPolicyTags | ? {$_.Type -eq "All"}).RetentionAction }
                                foreach ($tag in $RetentionTags.RetentionPolicyTags) {
                                if ($tag.RetentionId -eq $tagGUID ) { return $tag.RetentionAction }

                                }
                            }

                                 function GetRetentionPeriod($tagGUID)  {
                                if (!$tagGUID) { return ($RetentionTags.RetentionPolicyTags | ? {$_.Type -eq "All"}).RetentionPeriod }
                                foreach ($tag in $RetentionTags.RetentionPolicyTags) {
                                if ($tag.RetentionId -eq $tagGUID ) { return $tag.RetentionPeriod }

                                }
                            }

                            $oFindFolders1 | %{


                            $obj = New-Object PSObject
                            $obj | add-member noteproperty DisplayName $_.DisplayName
                            $obj | add-member noteproperty PolicyTag (GetTagName $_.PolicyTag.RetentionId)
                            $obj | add-member noteproperty ArchiveTag (GetTagName $_.ArchiveTag.RetentionId)                       
                            $obj | add-member noteproperty RetentionActionPolicyTag (GetRetentionAction $_.PolicyTag.RetentionId)
                            $obj | add-member noteproperty RetentionActionArchiveTag (GetRetentionAction $_.ArchiveTag.RetentionId)
                            $obj | add-member noteproperty RetentionPeriodPolicyTag (GetRetentionPeriod $_.PolicyTag.RetentionId)
                            $obj | add-member noteproperty RetentionPeriodArchiveTag (GetRetentionPeriod $_.ArchiveTag.RetentionId)
                            $obj | add-member noteproperty Usermailbox $user
                            $Global:retentioninfo += $obj
                        }
                    }

          
                       
                            if((get-recipient $user).recipienttype -eq 'UserMailbox')
                            {
              
                                $exchangeService.Credentials = New-Object Microsoft.Exchange.WebServices.Data.WebCredentials -ArgumentList $credential.UserName, $credential.GetNetworkCredential().password
                                $id = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId -ArgumentList "SmtpAddress",$user
                                $exchangeService.ImpersonatedUserId = $id

                                $exchangeService.AutodiscoverUrl($user)
            
                                folderretention;
           
                           
                                        
                            }
                       
                       
                       
                       
                       
                        else{

                       
                        $exchangeService.Credentials = New-Object Microsoft.Exchange.WebServices.Data.WebCredentials -ArgumentList $o365credential.UserName, $o365credential.GetNetworkCredential().password
                        $id = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId -ArgumentList "SmtpAddress",$user
                        $exchangeService.ImpersonatedUserId = $id
                        $exchangeService.Url = "https://outlook.office365.com/EWS/Exchange.asmx"

                        folderretention;

                        } 


         }  

         $Global:retentioninfo | Export-Csv report1.csv -NoTypeInformation
     }

       
          
      end{

      $endTime = Get-Date
      Write-Host "Total time = " + $($endTime -$StartTime)

      }      

    Tuesday, July 4, 2017 8:58 PM

All replies

  • For Items you will need to make use of Extended properties in EWS there is a good post that explains and details the properties to use https://blogs.msdn.microsoft.com/akashb/2011/11/30/stamping-retention-policy-tag-on-items-using-ews-managed-api-1-1-from-powershellexchange-2010/ you will need to turn that into a retrieval instead somebody has already written a good one for Office365 you should be able to make use of https://gallery.technet.microsoft.com/office/Report-Retention-3341472a

    For Error handing I would use Try Catch block eg here's an example of one I use for Import-CSV with EWS

    $MbResults = @()
    Import-Csv -Path $CSVFile | ForEach-Object {
    	$error.clear()
    	try{
    		Write-Host ("Processing Mailbox " + $_.EmailAddress) -ForegroundColor Cyan
    		if($service -eq $null)
    		{
    			$service = Connect-Exchange -MailboxName $_.EmailAddress -Credentials $Credentials 
    		}
    		$service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $_.EmailAddress) 
                    $folderid = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot,$_.EmailAddress)   
    		$RootFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid)
                    $mbRes.Result = "Success"
    		$MbResults+=$mbRes
    		Write-Output $mbRes
    	}
    	catch
    	{
    		$mbRes.Result = "Failed"
    		$mbRes.Error = $_.Exception.Message
    		$MbResults+=$mbRes
    		Write-Output $mbRes
    		
    	}
    }
    Cheers
    Glen

    Tuesday, July 4, 2017 10:07 PM
  • Thanks for that Glen, I can tell from your blog - you are a guru when it comes to EWS and powershell scripting..

    So I got the error handling to work by just wrapping this line of code:

                                                                                                                                                                                                                                                                                                                          

                        try{ 
                                $CurrentUser = get-recipient $user -ErrorAction STOP



                                if(($CurrentUser).recipienttype -eq 'UserMailbox')
                                {

                                    $exchangeService.Credentials = New-Object Microsoft.Exchange.WebServices.Data.WebCredentials -ArgumentList $credential.UserName, $credential.GetNetworkCredential().password
                                    $id = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId -ArgumentList "SmtpAddress",$user
                                    $exchangeService.ImpersonatedUserId = $id

                                    $exchangeService.AutodiscoverUrl($user)

                                    folderretention;                        

                                }





                                else{


                                $exchangeService.Credentials = New-Object Microsoft.Exchange.WebServices.Data.WebCredentials -ArgumentList $o365credential.UserName, $o365credential.GetNetworkCredential().password
                                $id = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId -ArgumentList "SmtpAddress",$user
                                $exchangeService.ImpersonatedUserId = $id
                                $exchangeService.Url = "https://outlook.office365.com/EWS/Exchange.asmx"

                                folderretention;

                                }  

                        }catch{

                            $errcond = $_.Exception.Message
                            $timestamp = (get-date).DateTime
                            "Time of exception:  $timestamp" | Out-File $LogFile -Append
                            "User: $user" | out-file $LogFile -Append
                            $errcond | out-file -FilePath $LogFile -append
                        }

    rather than setting $erroractionpreference to STOP at the top of the script in the Begin section - this seemed to have no effect on any errors generated by the Get-Recipient cmdlet in my testing - feeding it bogus or non existant users - the catch block would never kick in and I would get the red error text on the screen - its possibly something to do with scope but im not sure to be honest.  Also it seems that any error generated in a try{} block - is treated as terminating not just the cmdlet that -erroraction STOP is applied to - and the catch always kicks in.... it is possible there could be further errors up in the functions but I am ignoring that for now - rather than wrap the whole foreach block in a try{} again i am unsure whether the try should be outside the foreach or inside the loop - but no matter.... I understand the basics of EWS like connecting, impersonation sending mail messages etc. but when it comes to extendedproperties my eyes begin to glaze over.... like what is the difference between the baseproperty set and extended properties - are they like 'hidden' properties or something - if someone could explain them in layman or plain english terms I would be grateful  Regards nicholas

    Wednesday, July 5, 2017 1:36 PM
  • For errors that are generated by EMS cmdlets these are a little different I would suggest reading https://blogs.technet.microsoft.com/bshukla/2011/07/13/why-does-catch-not-catch/ which has a good explanation of what you need.

    With extended properties there is no real easy explanation https://msdn.microsoft.com/en-us/library/office/dn467898(v=exchg.150).aspx is one of the better pieces of seen written and explains the propertyset and extended property question. It depends how far you want to go down the rabbit hole (there are multiple type of Extended properties and even sub categories within these) ? if you going to be doing a lot of EWS coding then getting hold of Inside Exchange Web Services https://www.amazon.com/Inside-Microsoft-Exchange-Server-Services/dp/0735623929 even though its old it has some of the best technical explanations of things.

    >> are they like 'hidden' properties or something

    No there are no hidden properties (but there are hidden items and folders) but EWS only returns the properties you request (or define) if you want to know all the properties that are available on a item then using a Mapi editor like OutlookSpy or MFCMapi is a must for any EWS coding.

    Cheers
    Glen

    • Marked as answer by nickkinn Thursday, July 6, 2017 12:47 PM
    • Unmarked as answer by nickkinn Thursday, July 13, 2017 10:26 PM
    Wednesday, July 5, 2017 10:10 PM