Powershell Code to Import Staff Photos to Exchange Hybrid Mode Environment

Office 365 and Exchange Online in hybrid mode makes importing staff photos difficult.  There is 1 scenario to be considered and 3 different save locations.  The first scenario is whether a user’s mailbox has been migrated to Exchange Online. This will depend on the user and will determine where to upload a photo.  The three different save locations are Exchange Online, local Exchange server, and Active Directory.  Exchange Online or a local Exchange server can store photos of 648 x 648.  Active Directory’s recommended size is 96 x 96 with max size of 10kb.  Local exchange will save a smaller photo into Active Directory that will need to be overwritten with the 96 x 96 photo.

I created a powershell script to generate the powershell code needed to import photos into Exchange Online, a local Exchange server, and Active Directory.  It will determine if photo exists for the user found and whether the user has been migrated to Exchange Online.  It will then generate the powershell code needed to import the photos found for each user found in AD for the specified filter.  The AD username has to match the picture file name of “username.jpg” in both directories.  Irfanview or Photoshop can be used to resize photos in batches.  Advanced Renamer or powershell can be used to get the photos renamed correctly.

#Generate Powershell Script to Import Staff Photos into Hybrid Mode Exchange Environment and Active Directory
Import-Module ActiveDirectory


#Edit Variables As Needed
$localexchangeserver = 'localExchangeServer.contoso.com';
#Limited to 1000 records filter as best as possible for this limitation to do in batches
#<-Looks for all active users with an email address
$ADFilter = "(&(&(&(&(objectCategory=person)(objectClass=user))(mail=*)(!userAccountControl:1.2.840.113556.1.4.803:=2)))(&(!(samAccountName=9*))(!(samAccountName=8*))))";

#Change these paths as desired
#648 x 648 size photo path with username.jpg files for each user
#Path to 648 X 648 pictures  (All pictures must be 648 X 648 max)
#Exchange will create thumbnails of different sizes from this 648x648 photo as the source
$picmainpath = 'C:\Photos\Photos648\'
#96 x 96 size photo path with username.jpg files
#Path to 96 X 96 pictures   (All pictures must by 96 X 96 and less than 10KB)
$picmainpath2 = 'C:\Photos\Photos96\'

#Powershell Script file to generate to (This will generate the file needed for review)
$exportfile = 'c:\Photos\ImportPhotos.ps1'
$missingpics = 'c:\Photos\MissingPics.txt';


#End Editable Variables


if (Test-Path ($exportfile))
{
    Remove-Item $exportfile -Confirm:$false
}

if (Test-Path ($missingpics))
{
    Remove-Item $missingpics -Confirm:$false
}

$streamExportFile = [System.IO.StreamWriter] $exportfile
$streamMissingPics = [System.IO.StreamWriter] $missingpics

Try
{
    #Active Directory Searching
    $dom = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
    $root = $dom.GetDirectoryEntry()
    $search = [System.DirectoryServices.DirectorySearcher]$root
    #In Active Directory Searching the back slash has to be escaped by using "\5c"
    #Change the search filter as desired
    $search.Filter =  $ADFilter 
    $result = $search.FindAll()



    #Output the desired script
    $streamExportFile.WriteLine("#Exchange Online 648x648 size picture files");
    $streamExportFile.WriteLine("Import-Module ActiveDirectory");
    $streamExportFile.WriteLine('#Office 365 Credential');
    $streamExportFile.WriteLine('WRITE-HOST "Exchange Online Admin Credential";');
    $streamExportFile.WriteLine('$msolcred = get-credential;');
    $streamExportFile.WriteLine('$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.outlook.com/powershell/?proxymethod=rps -Credential $msolcred -Authentication Basic -AllowRedirection;');
    $streamExportFile.WriteLine('$localexchangeserver = "' + $localexchangeserver + '"');
    $streamExportFile.WriteLine('Import-PSSession $Session;');

    #Remote Mailboxes Only
    foreach ($user in $result)
    {
        #the properties have to be all lowercase
        $username1 = $($user.properties.samaccountname) 
        #$PicturePath = 'c:\photos\' + $username1 + '.jpg'
        #$ex1 = $($user.properties.extensionattribute1) 
        #export out the existing data if you want
   
        $picpath2 = $username1 + '.jpg'
        $PicturePath = $picmainpath + $picpath2
        $PicturePath2 = $picmainpath2 + $picpath2
   
    
        if (Test-Path ($PicturePath))
        {
            if ($PicturePath -ne $picmainpath)
            {
                $aduser1 = Get-ADUser -identity $username1 -Properties msExchRecipientTypeDetails,msExchRemoteRecipientType,SamAccountName;
                $msExchRecipientTypeDetails = $($aduser1.msexchrecipienttypedetails);
                $msExchRemoteRecipientType = $($aduser1.msExchRemoteRecipientType);
                #If user has been migrated to Exchange Online
                if ($msExchRecipientTypeDetails -eq '2147483648' -and $msExchRemoteRecipientType -eq '4')
                {
                    $photocmd = '$photo = ([Byte[]] $(Get-Content -Path "' + $PicturePath + '" -Encoding Byte -ReadCount 0));'
                    $streamExportFile.WriteLine($photocmd);
                    $stringout = 'Set-UserPhoto "' + $username1 + '" -PictureData $photo -Confirm:$false;'
                    $streamExportFile.WriteLine($stringout);
			    }
                #$savecmd = 'Set-UserPhoto "' + $username1 + '" -Save -Confirm:$false'
                #Write-Host $savecmd
            }
            else
            {
                $streamMissingPics.WriteLine($username1);
            }
        }
        else
        {
            $streamMissingPics.WriteLine($username1);
        }
	
	
    

    }
    

    $streamExportFile.WriteLine('#Exit Session');
    $streamExportFile.WriteLine('Remove-PSSession $Session;');
    $streamExportFile.WriteLine('');
    $streamExportFile.WriteLine('');
    $streamExportFile.WriteLine('#Local Exchange Import Pictures 648 x 648');
    $streamExportFile.WriteLine('#Local Exchange Credential');
    $streamExportFile.WriteLine('WRITE-HOST "Local Exchange Admin Credential";');
    $streamExportFile.WriteLine('$cred = get-credential;');

    $streamExportFile.WriteLine('$sessionoption = New-PSSessionOption -SkipCNCheck;');
    $streamExportFile.WriteLine('$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://$localexchangeserver/powershell/ -Credential $cred -AllowRedirection -SessionOption $sessionoption;');
    $streamExportFile.WriteLine('Import-PSSession $Session;');

    #Local Mailboxes Only
    foreach ($user in $result)
    {
        #the properties have to be all lowercase
        $username1 = $($user.properties.samaccountname) 
        #$PicturePath = 'c:\photos\' + $username1 + '.jpg'
        #$ex1 = $($user.properties.extensionattribute1) 
        #export out the existing data if you want
   
        $picpath2 = $username1 + '.jpg'
        $PicturePath = $picmainpath + $picpath2
        $PicturePath2 = $picmainpath2 + $picpath2
   
    
        if (Test-Path ($PicturePath))
        {
            if ($PicturePath -ne $picmainpath)
            {
                $aduser1 = Get-ADUser -identity $username1 -Properties msExchRecipientTypeDetails,msExchRemoteRecipientType,SamAccountName;
                $msExchRecipientTypeDetails = $($aduser1.msexchrecipienttypedetails);
                $msExchRemoteRecipientType = $($aduser1.msExchRemoteRecipientType);
                #If user has NOT been migrated to Exchange Online
                if (!($msExchRecipientTypeDetails -eq '2147483648' -and $msExchRemoteRecipientType -eq '4'))
                {
                    $photocmd = '$photo = ([Byte[]] $(Get-Content -Path "' + $PicturePath + '" -Encoding Byte -ReadCount 0));'
                    $streamExportFile.WriteLine($photocmd);
                    $stringout = 'Set-UserPhoto "' + $username1 + '" -PictureData $photo -Confirm:$false;'
                    $streamExportFile.WriteLine($stringout);
			    }
            
            
            }
        }
	
	
    

    }

    $streamExportFile.WriteLine('Remove-PSSession $Session;');

    $streamExportFile.WriteLine('');
    $streamExportFile.WriteLine('');


    $streamExportFile.WriteLine("#AD 96 byte files");
    $streamExportFile.WriteLine("#Local Exchange will write smaller files than 96 x 96 and this will overwrite or add the 96 x 96 photo into AD");
    foreach ($user in $result)
    {
        #the properties have to be all lowercase
        $username1 = $($user.properties.samaccountname) 
        #$PicturePath = 'c:\photos\' + $username1 + '.jpg'
        #$ex1 = $($user.properties.extensionattribute1) 
        #export out the existing data if you want
   
        $picpath2 = $username1 + '.jpg'
        $PicturePath = $picmainpath + $picpath2
        $PicturePath2 = $picmainpath2 + $picpath2
   
    
	
	     if (Test-Path ($PicturePath2))
        {
            if ($PicturePath2 -ne $picmainpath2)
            {
                $stringout = 'Set-ADUser ' + $username1 + ' -Replace @{thumbnailPhoto=[Byte[]]$(Get-Content -Path "' + $PicturePath2 + '" -Encoding Byte -ReadCount 0)};'
                $streamExportFile.WriteLine($stringout);
            }
        }
    

    }
}
Catch
{
    $errorMessage = $_.Exception.Message;
    $failedItem = $_.Exception.ItemName;
    WRITE-HOST "Error Item: $failedItem";
    WRITE-HOST "Error: $errorMessage";
}
Finally
{
    $streamMissingPics.Close();
    $streamExportFile.Close();
}

Leave a Reply

%d bloggers like this: