Slow User's Office 365 Photo Exportation

Hi everyone,

I’m trying to export the office365 photos of all users at my organization, but the whole process is during about 3 hours. We’ve about 500 users.

Have something that I can improve at the code, that can turn the process faster?

$users = Get-ADGroupMember "All Employees" | Select-Object samAccountName

$365Cred = Get-Credential
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.outlook.com/powershell/ -Credential $365Cred -Authentication Basic -AllowRedirection
Import-PSSession $Session

foreach ($user in $users){
    $photo = Get-UserPhoto -Identity $($user.SamAccountName) -ErrorAction silentlycontinue
    Set-Content -Value $photo.PictureData "C:\Photos\$($user.samAccountName).jpg" -Encoding byte
     }
}

Remove-PSSession $Session

You could attempt to do it as a Workflow so that you can run them them in parallel.

https://docs.microsoft.com/en-us/powershell/module/psworkflow/about/about_foreach-parallel?view=powershell-5.1

Since you are in a session, you may need to get all of the photos and then attempt to create the images in parallel. Not sure how much time it would save because you aren’t indicating what portion is taking the time.

Try logic like this:

$users = Get-ADGroupMember "All Employees" | Select-Object samAccountName

$365Cred = Get-Credential
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.outlook.com/powershell/ -Credential $365Cred -Authentication Basic -AllowRedirection
Import-PSSession $Session

$photos = foreach ($user in $users){
    Get-UserPhoto -Identity $($user.SamAccountName) -ErrorAction silentlycontinue
}

Remove-PSSession $Session
...

foreach -parallel ($photo in $photos) {
    Set-Content -Value $photo.PictureData "C:\Photos\$($user.samAccountName).jpg" -Encoding byte

}

Sorry for the lack of this information, the Get-UserPhoto consumes about 90% of the time. I did not know this statement, so I was reading about but some doubts have arisen. Should the code loke like this?

Workflow Get-HBUserPhotos
{
    param([string[]]$Users)

    $photos = foreach ($user in $users){
    Get-UserPhoto -Identity $($user.SamAccountName) -ErrorAction silentlycontinue
    }
    
    foreach -parallel ($photo in $photos) {
        Set-Content -Value $photo.PictureData "C:\Photos\$($user.samAccountName).jpg" -Encoding byte
    }
}

I appreciate your help!

If Get-UserPhoto is where the bottleneck is, I’m not sure the workflow is going to get you any performance benefit because you have to share that remote session for each parallel process. See this:

https://stackoverflow.com/questions/17178404/powershell-workflow-exchange-remoting

From my own experience with uploading photos to O365, it is a slow painful process. The joys of dealing with the cloud :wink:

After several researchs I discover a better way to handle with the slow exports:

param
    (
        [Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)]
        [System.Array]
        $users,
 
        [Parameter(Mandatory=$False)]
        [ValidateSet("96x96","240x240","648x648")]
        $Size="240x240",
 
        [Parameter(Mandatory)]
        $Path,
 
        [Parameter(Mandatory)]
        [pscredential]
        [System.Management.Automation.CredentialAttribute()]
        $Credential
    )
 
process
    {
    foreach($user in $users){
        try
        {
            $Uri = [String]::Concat("https://outlook.office365.com/ews/Exchange.asmx/s/GetUserPhoto?email=",$user.UserPrincipalName,"&size=HR",$Size)
            if(Test-Path -Path $Path)
            {
                $File = [string]::Concat($Path,"\",$user.sAMAccountName,".jpg")
                Invoke-WebRequest -Uri $Uri -Credential $Credential -OutFile $File
            }
            else
            {
                Write-Warning "Please Check the Path"
            }
        }
        catch
        {
            $_.Exception.Message 
        }
    }
}

$users is an array of a Get-ADUSer invocation and $path is the export location.

With this code I could decrement the execution time from 3 hours to 8 minutes.

Reference: http://chen.about-powershell.com/2016/08/import-user-profile-picture-from-exchange-online-using-ews-and-powershell/

Thank you all for the help and ideas!