Search code examples
powershelloffice365exchange-server

Exchange Online - Get-UserPhoto - how to catch non terminating error?


I'm trying to export a list of all users with no photo from our Exchange Online account using powershell. I cannot get it to work and have tried various methods.

Get-UserPhoto returns this exception when there is no profile present.

Microsoft.Exchange.Data.Storage.UserPhotoNotFoundException: There is no photo stored here.

First of all I tried use Errorvariable against the command but received:

A variable that cannot be referenced in restricted language mode or a Data section is being referenced. Variables that can be referenced include the following: $PSCulture, $PSUICulture, $true, $false, and  $null.
+ CategoryInfo          : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : VariableReferenceNotSupportedInDataSection
+ PSComputerName        : outlook.office365.com

Next I tried try, catch but the non-terminating error never calls the catch despite various methods followed online about setting $ErrorActionPreference first of all.

Any ideas ? Here is the script:

 $UserCredential = Get-Credential
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $UserCredential -Authentication Basic -AllowRedirection
Import-PSSession $Session 

$timestamp = $timestamp = get-date -Format "dd/M/yy-hh-mm"
$outfile = "c:\temp\" + $timestamp + "UserswithoutPhotos.txt"




$resultslist=@()
$userlist = get-user -ResultSize unlimited -RecipientTypeDetails usermailbox | where {$_.accountdisabled -ne $True}


Foreach($user in $userlist)
{
    try
    {

        $user | get-userphoto -erroraction stop 
    }
    catch
    {
        Write-Host "ERROR: $_"
        $email= $user.userprincipalname
        $name = $user.DisplayName
        $office = $user.office
        write-host "User photo not found...adding to list : $name , $email, $office" 
        $resultslist += $user

    }
}
$resultslist | add-content $outfile 
$resultslist

Solution

  • PowerShell's error handling is notoriously tricky, especially so with cmdlets that use implicit remoting via a locally generated proxy script module.

    The following idiom provides a workaround based on temporarily setting $ErrorActionPreference to Stop globally (of course, you may move the code for restoring the previous value outside the foreach loop), which ensures that even functions from different modules see it:

    try {
    
      # Temporarily set $ErrorActionPreference to 'Stop' *globally*
      $prevErrorActionPreference = $global:ErrorActionPreference
      $global:ErrorActionPreference = 'Stop'
    
      $user | get-userphoto
    
    } catch {
    
        Write-Host "ERROR: $_"
        $email= $user.userprincipalname
        $name = $user.DisplayName
        $office = $user.office
        write-host "User photo not found...adding to list : $name, $email, $office" 
        $resultslist += $user
    
    } finally {  
    
      # Restore the previous global $ErrorActionPreference value
      $global:ErrorActionPreference = $prevErrorActionPreference
    
    }
    

    As for why this is necessary:

    • Functions defined in modules do not see the caller's preference variables - unlike cmdlets, which do.

    • The only outside scope that functions in modules see is the global scope.

    For more information on this fundamental problem, see GitHub issue #4568.