# export-ExchangeUserPhoto.ps1 # # Export Exchange Photo using EWS into files # # Sample to process many numbers # get-mailbox -ResultSize 3 | %{$_.primarysmtpaddress} | .\export-exchangeUserphoto.ps1 # # 20160505 FC Initial Version # 20170312 FC More debugging and Output, Process Indicator # 20170911 FC Added Pipeline support, Statistics # param( [Parameter(Mandatory=$True, ValueFromPipeline=$True)][string]$SmtpAddress , [string]$photodirectory = ((Convert-Path .) + "\Photos\") , [string]$impersonateUser = "", [string]$impersonatepassword = "" , [string]$EWSManagedApiPath = "C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll" ) # # Init global Modules and variables # begin { set-psdebug -strict write-host "Start Export-ExchangeUserPhoto" write-host " Parameter Photodirectory: $photodirectory" write-verbose "Loadig EWS-DLL from $EWSManagedApiPath" if (!(test-path $photodirectory)) { try { New-Item -path $photodirectory -Type directory | out-null } catch { throw "Unable to use photodirectory $photodirectory" } } try { #Get-Item -Path $EWSManagedApiPath | out-null # check if file is there Add-Type -Path $EWSManagedApiPath $Service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2013SP1) } catch { throw "Unable to find EWS Managed API at $($EWSManagedApiPath)." } if ($impersonateUser -ne "") { write-verbose "Using Impersonatino User $impersonateUser" $service.UseDefaultCredentials = $false $service.Credentials = New-Object System.Net.NetworkCredential($impersonateUser, $impersonatepassword) } # Create the search filter to find the UserPhoto item $SearchFilter = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.ItemSchema]::ItemClass,"IPM.UserPhoto") # Define the EWS search view $ItemView = New-Object Microsoft.Exchange.WebServices.Data.ItemView(1000) $ItemView.PropertySet = New-Object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties) # Define a property set containing the extended properties where the photos are stored - each different resolution is in a unique extended property write-verbose "Initialize Peroperty Set" $UserPhotoPropset = New-Object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties) $UserPhotoPropset.Add((New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition([Microsoft.Exchange.WebServices.Data.DefaultExtendedPropertySet]::Common,"UserPhotoHR48x48",[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary))) $UserPhotoPropset.Add((New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition([Microsoft.Exchange.WebServices.Data.DefaultExtendedPropertySet]::Common,"UserPhotoHR64x64",[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary))) $UserPhotoPropset.Add((New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition([Microsoft.Exchange.WebServices.Data.DefaultExtendedPropertySet]::Common,"UserPhotoHR96x96",[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary))) $UserPhotoPropset.Add((New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition([Microsoft.Exchange.WebServices.Data.DefaultExtendedPropertySet]::Common,"UserPhotoHR120x120",[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary))) $UserPhotoPropset.Add((New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition([Microsoft.Exchange.WebServices.Data.DefaultExtendedPropertySet]::Common,"UserPhotoHR240x240",[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary))) $UserPhotoPropset.Add((New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition([Microsoft.Exchange.WebServices.Data.DefaultExtendedPropertySet]::Common,"UserPhotoHR360x360",[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary))) $UserPhotoPropset.Add((New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition([Microsoft.Exchange.WebServices.Data.DefaultExtendedPropertySet]::Common,"UserPhotoHR432x432",[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary))) $UserPhotoPropset.Add((New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition([Microsoft.Exchange.WebServices.Data.DefaultExtendedPropertySet]::Common,"UserPhotoHR504x504",[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary))) $UserPhotoPropset.Add((New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition([Microsoft.Exchange.WebServices.Data.DefaultExtendedPropertySet]::Common,"UserPhotoHR648x648",[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary))) [long]$totalcount=0 [long]$totalexportcount=0 [long]$totalfailedcount=0 } process { $totalcount++ write-host " ($totalcount/$totalexportcount/$totalfailedcount) Processing Mailaddress $($SmtpAddress)" [bool]$continue=$true if ($SmtpAddress.contains("@")) { try { $Service.AutodiscoverUrl($SmtpAddress) write-verbose " Found AutodiscoverURL" } catch{ $continue=$false write-Warning "SKIP: Unable to find Autodiscover for User $smtpaddress" } if ($continue) { Write-Verbose "URL: $($Service.Url.AbsoluteUri)" $Service.ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $SmtpAddress) $FolderId = [Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Root try { write-verbose "Try to bind Root Folder" $Rootfolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($Service,$FolderId) } catch { write-warning "SKIP: Unable to bind to mailbox root folder, maybe no ews impersonation right" $totalfailedcount++ $continue = $false } if ($continue) { write-verbose " Found Inbox. Continue" If ($Results = $Service.FindItems( $FolderId, $SearchFilter, $ItemView )) { Write-Verbose "IPM.UserPhoto Item Found" $result = "" | select PrimarySmtpAddress,Uploadtimestamp $result.PrimarySmtpAddress = $SmtpAddress $result.Uploadtimestamp = $Results.Items[0].DateTimeCreated try { [Void]$service.LoadPropertiesForItems($Results,$UserPhotoPropset) } catch { write-warning "SKIP:Unable to load Photoinformation" $continue = $false } If ($continue -and $Results.Items[0].ExtendedProperties) { # Loop through each extended property ForEach ($UserPhoto in $Results.Items[0].ExtendedProperties) { Write-Verbose "Prop: $($UserPhoto.PropertyDefinition.Name)" $result | Add-Member -type NoteProperty -name $UserPhoto.PropertyDefinition.Name -value $UserPhoto.Value $Filename = $photodirectory + $smtpaddress + "_" + $UserPhoto.PropertyDefinition.Name + ".jpg" Write-verbose $Filename [IO.File]::WriteAllBytes("$Filename",$UserPhoto.Value) $result | Add-Member -type NoteProperty -name "$($UserPhoto.PropertyDefinition.Name)File" -value $Filename } $totalexportcount++ } else { $totalfailedcount++ } Return $result } else { $totalfailedcount++ } } } else { $totalfailedcount++ } } else { write-warning "SMTP Address does not contain @-Character" $totalfailedcount++ } } end { write-host "Summary" write-host "Total Count: $totalcount" write-host "Total Export: $totalexportcount" write-host "Total Fail: $totalfailedcount" write-host "End Export-ExchangeUserPhoto" }