none
Error trapping to avoid strange behavior? RRS feed

  • Question

  • I have a folder of employee photos taken in conjunction with ID badges that we would like to use to update the user picture in Active Directory.  I found the following script on the coffeefueled blog and adapted it for our environment.  It cycles through a folder of image files and parses the JPEG file names and searches AD to find a match with its Surname and GivenName fields.  Occasionally, there will be an error because the aka name used by the JPEG doesn't match the legal name in AD.  It is supposed to display those names in red and the successes in green, but instead, it displays all in green with the standard PowerShell error message "You cannot call a method on a null-valued expression." inter-spaced between the green success lines. 

    The failures wouldn't be a huge problem to subsequently fix manually, but the problem is that nothing is attached to the user who is expected to fail, and the failed JPEG gets attached to the next user creating two errors.  As I test the script on a dozen accounts, it is one thing, but when I am asked to do this company-wide, I'm going to have trouble tracking down the errors.

    Here are two versions of the code I am using.  Any suggestions?

    # Set-ThumbnailPhotoBatch.ps1
    # Sets the thumbnail photo in Active Directory.  The photo will display in Skype, SharePoint, and modern versions of Outlook.
    # The photos to be updated are based on folder location.
    
    #Cycle through the designated folder of JPEGs
    foreach ($photo in (Get-ChildItem -Path '\\hpfs\path-to\Emp\Deployment'))
    {
      #The filenames are in the format "lastname_firstname".  I used the free app, Batch Renamer to replace the comma and space.
      #You can see what is going on with the 0 position and the 1 position  
        $surname = $photo.BaseName.Split('_')[0] 
        $firstname = $photo.BaseName.Split('_')[1]  
     
        [Byte[]]$image = Get-Content $photo.FullName -Encoding byte #Important - must be cast as a Byte array
        
        #The following 5 lines set up the search for a user matching the GivenName/Surname fields 
        $root = New-Object DirectoryServices.DirectoryEntry 'LDAP://ADROOT'
        $searcher = New-Object DirectoryServices.DirectorySearcher
        $searcher.SearchRoot = $root
        $searcher.Filter = "(&&(objectCategory=person)(objectClass=user)(sn=$surname)(givenName=$firstname))"
        $user = $searcher.FindOne().GetDirectoryEntry()
         
      #The following lines set/replace the user photo with the matching JPEG  
        if ($user -ne $null)    
        {
            $user.Properties['jpegPhoto'].Clear()
            $user.Properties['thumbnailPhoto'].Clear()
            $user.Properties['thumbnailPhoto'].Add($image)
            $user.CommitChanges()
            
            #The following lines are designed to provide feedback, but currently aren't working.
            #For now, even error accounts are returned as green successes, but with red error text following.
            #The designed red error when a $user isn't found, isn't showing up.
            #This is likely because failing users are not really $null
            Write-Host ("$firstname $surname has been updated") -ForegroundColor Green
        }
        else
        {
            Write-Host ("$firstname $surname not found") -ForegroundColor Red
        }
    }

    Version two:

    # Set-ThumbnailPhotoBatch.ps1
    # Sets the thumbnail photo in Active Directory.  The photo will display in Skype, SharePoint, and modern versions of Outlook.
    # The photos to be updated are based on folder location.
    # Accounts with pre-existing photos are skipped and not modified.
    
    #Cycle through the designated folder of JPEGs
    foreach ($photo in (Get-ChildItem -Path '\\hpfs\path-to\Emp\Deployment'))
    {
        $surname = $photo.BaseName.Split('_')[0] #The filenames are in the format "surname_firstname"
        $firstname = $photo.BaseName.Split('_')[1]  
     
        [Byte[]]$image = Get-Content $photo.FullName -Encoding byte #Important - must be cast as a Byte array
         
        $root = New-Object DirectoryServices.DirectoryEntry 'LDAP://ADRoot'
        $searcher = New-Object DirectoryServices.DirectorySearcher
        $searcher.SearchRoot = $root
        $searcher.Filter = "(&(objectCategory=person)(objectClass=user)(sn=$surname)(givenName=$firstname))"
        $user = $searcher.FindOne().GetDirectoryEntry() 
         
        #My attempt at error trapping returns the same problem   
        if ($Error.Contains('You cannot call a method on a null valued expression'))
         {
            Write-Host ("$firstname $surname not found") -ForegroundColor Red
        }
         else   
        {
            $user.Properties['jpegPhoto'].Clear()
            $user.Properties['thumbnailPhoto'].Clear()
            $user.Properties['thumbnailPhoto'].Add($image)
            $user.CommitChanges()
            Write-Host ("$firstname $surname has been updated") -ForegroundColor Green
        }
    
       
    }

    Wednesday, November 30, 2016 4:12 PM

Answers

  • There are a number of weird mistakes in your code so I cannot see how it is working.  Using $ErrorActionPreference is likely the cause.  Don't use that until you understand what it is for and how it works.

    This is how we would do this in PowerShell.  Same code without the mistakes and unnecessary lines.

    # Set-ThumbnailPhotoBatch.ps1
    <#
    	Sets the thumbnail photo in Active Directory.  The photo will display in Skype, SharePoint, and modern versions of Outlook.
    	The photos to be updated are based on folder location.
    	The filenames are in the format "lastname_firstname".  
    	I used the free app, Batch Renamer to replace the comma and space.
    #>
    $imagepath = '\\hpfs\SharedSecure$\Operations\IS\SecurityandPrivacy\Security\DEEP\Emp\Deployment'
    $searcher = [adsisearcher]([adsi]'LDAP://HEALTHPARTNERS')
    
    Get-ChildItem $imagepath |
    	ForEach-Object{
    		$surname,$firstname  = $_.BaseName -split '_'
    		[byte[]]$image = Get-Content $_.FullName -Encoding byte
    		$searcher.Filter = "(&(objectCategory=person)(objectClass=user)(sn=$surname)(givenName=$firstname))"
    		if($result = $searcher.FindOne()){
    $user = $result.GetDirectoryEntry() $user.Properties['jpegPhoto'].Clear() $user.Properties['thumbnailPhoto'].Clear() [void]$user.Properties['thumbnailPhoto'].Add($image) $user.CommitChanges() Write-Host ("$firstname $surname has been updated") -ForegroundColor Green }else{ Write-Host ("$firstname $surname not found") -ForegroundColor Red } }

    Note that no variables need to be cleared when the code is structured correctly.

    The great weakness here is if two people have the same name.  Using SamAccountName for image file names would be simpler and safer.


    \_(ツ)_/




    • Edited by jrv Wednesday, November 30, 2016 10:18 PM
    • Marked as answer by art Alexion Thursday, December 1, 2016 3:43 PM
    Wednesday, November 30, 2016 10:10 PM
  • I fixed this as far as I know.  One of the problems is that with all of the tests, assigned variables persisted in the environment.  One particular person was always the last processed good account, so when a search was supposed to come up $null, it still had his account and that’s why he always got the picture of whoever’s JPEG filename didn’t match their name in AD.  I handled that by clearing the variables each time the script looped.  I suppressed default error messages and replaced them with more meaningful ones that will allow fixing the filenames and rerunning. 

     

    As a test, I renamed Anthony Cedrone’s image file Cendrone_Tony.jpg so that it would fail.  Put a few well named files in the folder as well.  Ran the script and got the intended output.

    0

    Person One has been updated

    0

    Person Two has been updated

    0

    Person Three has been updated

    Person Four not found

     

    Below is the final script.  I commented as much as I could so that anyone running it can see what is going on.

     

    # Set-ThumbnailPhotoBatch.ps1
    
    # Sets the thumbnail photo in Active Directory.  The photo will display in Skype, SharePoint, and modern versions of Outlook.
    
    # The photos to be updated are based on folder location.
    
     
    
    #Cycle through the designated folder of JPEGs
    
    foreach ($photo in (Get-ChildItem -Path '\\hpfs\SharedSecure$\Operations\IS\SecurityandPrivacy\Security\DEEP\Emp\Deployment'))
    
    {
    
     
    
      #But first lets clear the variables set in the last iteration of this loop.
    
      
    
      Clear-Variable -Name surname
    
      Clear-Variable -Name firstname
    
      Clear-Variable -Name searcher
    
      Clear-Variable -Name user
    
     
    
      #supress error messages
    
      $ErrorActionPreference = "SilentlyContinue"
    
     
    
      #The filenames are in the format "lastname_firstname".  I used the free app, Batch Renamer to replace the comma and space.
    
      #You can see what is going on with the 0 position and the 1 position 
    
        $surname = $photo.BaseName.Split('_')[0]
    
        $firstname = $photo.BaseName.Split('_')[1] 
    
     
    
        [Byte[]]$image = Get-Content $photo.FullName -Encoding byte #Important - must be cast as a Byte array
    
       
    
        #The following 5 lines set up the search for a user matching the GivenName/Surname fields
    
        $root = New-Object DirectoryServices.DirectoryEntry 'LDAP://HEALTHPARTNERS'
    
        $searcher = New-Object DirectoryServices.DirectorySearcher
    
        $searcher.SearchRoot = $root
    
        $searcher.Filter = "(&(objectCategory=person)(objectClass=user)(sn=$surname)(givenName=$firstname))"
    
        $user = $searcher.FindOne().GetDirectoryEntry()
    
        
    
      #The following lines set/replace the user photo with the matching JPEG 
    
        if ($user -ne $null)   
    
        {
    
            $user.Properties['jpegPhoto'].Clear()
    
            $user.Properties['thumbnailPhoto'].Clear()
    
            $user.Properties['thumbnailPhoto'].Add($image)
    
            $user.CommitChanges()
    
           
    
            #The following lines are designed to provide feedback, but currently aren't working.
    
            #For now, even error accounts are returned as green successes, but with red error text following.
    
            #The designed red error when a $user isn't found, isn't showing up.
    
            #This is likely because failing users are not really $null
    
            Write-Host ("$firstname $surname has been updated") -ForegroundColor Green
    
        }
    
        else
    
        {
    
            Write-Host ("$firstname $surname not found") -ForegroundColor Red
    
        }
    
    }
    

    • Marked as answer by art Alexion Wednesday, November 30, 2016 9:18 PM
    Wednesday, November 30, 2016 9:17 PM

All replies

  • The problem here is that you're essentially asking: "I found this script on the Internet, but it doesn't work for me. Can someone fix this for me?"

    See the first post at the top of this forum:

    This forum is for scripting questions rather than script requests

    In general, what you need to do is write a very short script that contains only the minimum amount of code needed to reproduce the error. This is particularly important when using code found on the interwebs - you always need to thoroughly understand what the code is doing before using in any kind of production environment.


    -- Bill Stewart [Bill_Stewart]

    Wednesday, November 30, 2016 4:36 PM
    Moderator
  • I fixed this as far as I know.  One of the problems is that with all of the tests, assigned variables persisted in the environment.  One particular person was always the last processed good account, so when a search was supposed to come up $null, it still had his account and that’s why he always got the picture of whoever’s JPEG filename didn’t match their name in AD.  I handled that by clearing the variables each time the script looped.  I suppressed default error messages and replaced them with more meaningful ones that will allow fixing the filenames and rerunning. 

     

    As a test, I renamed Anthony Cedrone’s image file Cendrone_Tony.jpg so that it would fail.  Put a few well named files in the folder as well.  Ran the script and got the intended output.

    0

    Person One has been updated

    0

    Person Two has been updated

    0

    Person Three has been updated

    Person Four not found

     

    Below is the final script.  I commented as much as I could so that anyone running it can see what is going on.

     

    # Set-ThumbnailPhotoBatch.ps1
    
    # Sets the thumbnail photo in Active Directory.  The photo will display in Skype, SharePoint, and modern versions of Outlook.
    
    # The photos to be updated are based on folder location.
    
     
    
    #Cycle through the designated folder of JPEGs
    
    foreach ($photo in (Get-ChildItem -Path '\\hpfs\SharedSecure$\Operations\IS\SecurityandPrivacy\Security\DEEP\Emp\Deployment'))
    
    {
    
     
    
      #But first lets clear the variables set in the last iteration of this loop.
    
      
    
      Clear-Variable -Name surname
    
      Clear-Variable -Name firstname
    
      Clear-Variable -Name searcher
    
      Clear-Variable -Name user
    
     
    
      #supress error messages
    
      $ErrorActionPreference = "SilentlyContinue"
    
     
    
      #The filenames are in the format "lastname_firstname".  I used the free app, Batch Renamer to replace the comma and space.
    
      #You can see what is going on with the 0 position and the 1 position 
    
        $surname = $photo.BaseName.Split('_')[0]
    
        $firstname = $photo.BaseName.Split('_')[1] 
    
     
    
        [Byte[]]$image = Get-Content $photo.FullName -Encoding byte #Important - must be cast as a Byte array
    
       
    
        #The following 5 lines set up the search for a user matching the GivenName/Surname fields
    
        $root = New-Object DirectoryServices.DirectoryEntry 'LDAP://HEALTHPARTNERS'
    
        $searcher = New-Object DirectoryServices.DirectorySearcher
    
        $searcher.SearchRoot = $root
    
        $searcher.Filter = "(&(objectCategory=person)(objectClass=user)(sn=$surname)(givenName=$firstname))"
    
        $user = $searcher.FindOne().GetDirectoryEntry()
    
        
    
      #The following lines set/replace the user photo with the matching JPEG 
    
        if ($user -ne $null)   
    
        {
    
            $user.Properties['jpegPhoto'].Clear()
    
            $user.Properties['thumbnailPhoto'].Clear()
    
            $user.Properties['thumbnailPhoto'].Add($image)
    
            $user.CommitChanges()
    
           
    
            #The following lines are designed to provide feedback, but currently aren't working.
    
            #For now, even error accounts are returned as green successes, but with red error text following.
    
            #The designed red error when a $user isn't found, isn't showing up.
    
            #This is likely because failing users are not really $null
    
            Write-Host ("$firstname $surname has been updated") -ForegroundColor Green
    
        }
    
        else
    
        {
    
            Write-Host ("$firstname $surname not found") -ForegroundColor Red
    
        }
    
    }
    

    • Marked as answer by art Alexion Wednesday, November 30, 2016 9:18 PM
    Wednesday, November 30, 2016 9:17 PM
  • Thanks, Bill.

    New to this.  I figured out the answer and posted it.

    Wednesday, November 30, 2016 9:19 PM
  • There are a number of weird mistakes in your code so I cannot see how it is working.  Using $ErrorActionPreference is likely the cause.  Don't use that until you understand what it is for and how it works.

    This is how we would do this in PowerShell.  Same code without the mistakes and unnecessary lines.

    # Set-ThumbnailPhotoBatch.ps1
    <#
    	Sets the thumbnail photo in Active Directory.  The photo will display in Skype, SharePoint, and modern versions of Outlook.
    	The photos to be updated are based on folder location.
    	The filenames are in the format "lastname_firstname".  
    	I used the free app, Batch Renamer to replace the comma and space.
    #>
    $imagepath = '\\hpfs\SharedSecure$\Operations\IS\SecurityandPrivacy\Security\DEEP\Emp\Deployment'
    $searcher = [adsisearcher]([adsi]'LDAP://HEALTHPARTNERS')
    
    Get-ChildItem $imagepath |
    	ForEach-Object{
    		$surname,$firstname  = $_.BaseName -split '_'
    		[byte[]]$image = Get-Content $_.FullName -Encoding byte
    		$searcher.Filter = "(&(objectCategory=person)(objectClass=user)(sn=$surname)(givenName=$firstname))"
    		if($result = $searcher.FindOne()){
    $user = $result.GetDirectoryEntry() $user.Properties['jpegPhoto'].Clear() $user.Properties['thumbnailPhoto'].Clear() [void]$user.Properties['thumbnailPhoto'].Add($image) $user.CommitChanges() Write-Host ("$firstname $surname has been updated") -ForegroundColor Green }else{ Write-Host ("$firstname $surname not found") -ForegroundColor Red } }

    Note that no variables need to be cleared when the code is structured correctly.

    The great weakness here is if two people have the same name.  Using SamAccountName for image file names would be simpler and safer.


    \_(ツ)_/




    • Edited by jrv Wednesday, November 30, 2016 10:18 PM
    • Marked as answer by art Alexion Thursday, December 1, 2016 3:43 PM
    Wednesday, November 30, 2016 10:10 PM
  • Thanks jrv! 

    I am impressed with the brevity!  I also notice the 0 on the output line has disappeared, which is very good.  One possible issue is the name matching.  In AD, my GivenName is Arthur.  When I rename my JPEG from Alexion_Arthur.jpg to Alexion_Art.jpg, your version succeeds.  If I misspell my surname in the JPEG, or add a letter to the beginning, like Alexion_Bart.jpg, it fails as expected.  I assume that it is doing a begins-with type match?

    Last, I agree that SAM, UPN, or EmpID would be better AD fields to match, but the photos are generated using an HR system instead of AD.  Eventually, I will work with the upstream process to correct this.

    Thanks again.
    Thursday, December 1, 2016 3:42 PM