4 March 2015

Загрузка фото пользователя в Active Directory с помощью PowerShell

PowerShell
Sandbox
Если вы используете Active Directory у вас используется Exchange, OwnCloud, SharePoint или другая система с возможностью отображения аватара или фото, то после прочтения данный статьи у вас появится возможность загрузить фото пользователя в AD для отображения в Outlook, Lync, на порталах SharePoint и других системах.

Нашел похожую статью («Добавление фотографий в Active Directory»), но прошло много времени, решил оживить тему.

Требования:
  • Фото сотрудников желательно в формате JPG; название файлов, желательно, стандартизировать;
  • PowerShell и модуль Active Directory for PowerShell на компьютере;
  • Схема Active Directory должна быть Win 2008 или новее (это не означает наличие контроллеров под Window 2008, достаточно запустит adprep с диска Windows 2008 для расширения схемы);
  • Пользователь должен иметь права на изменение атрибутов thumbnailphoto, jpegPhoto в Active Directory (по умолчанию, пользователь может менять свою фотографию, но права можно делегировать).


Минусы:
  • Дополнительная нагрузка на поддержку с просьбами заменить фото;
  • Рост базы Active Directory NTDS.DIT, что может привести к проблемам репликации.


Для справки:
Ограничение Active Directory на размер атрибута thumbnailPhoto и jpegPhoto 100 кб. Фото пользователя в Outlook 2010 будут отображаться даже если не установлен Exchange, достаточно иметь схему Active Directory Win 2008 или новее (Это не означает наличие контроллеров под Window 2008, достаточно запустит adprep с диска Windows 2008 для расширения схемы). Для отображения фото пользователя, в разных системах, используются разные атрибуты в Active Directory. Например для отображения в Outlook thumbnailPhoto, а для отображения в SharePoint jpegPhoto.


Автор не несет ответственности за любой возможный вред, причиненный материалами данной статьи.

В статье не приведен скрипт целиком. На вкус и цвет фломастеры разные.
В действительности, моим готовым скриптом, при необдуманном использовании, проще сломать Active Directory.

Нежелающие изучать PowerShell могут воспользоваться программами:


Или допилить оснастку Active Directory User & computers


Существует несколько вариантов загрузки фото в AD с помощью PowerShell:

Используя модуль Microsoft PowerShell for Active Directory:

Import-Module ActiveDirectory
$photo = [byte[]](Get-Content C:\Photo\MyPhoto.jpg -Encoding byte)
Set-ADUser <sAMAaccountName> -Replace @{thumbnailPhoto=$photo} 
Set-ADUser <sAMAaccountName> -Replace @{jpegPhoto;=$photo} 

Используя оснастку Quest PowerShell for Active Directory:

Add-PSSnapin Quest.ActiveRoles.ADManagement
$photo = [byte[]](Get-Content C:\Photo\MyPhoto.jpg -Encoding byte)
Set-QADUser <sAMAaccountName> -ObjectAttributes @{thumbnailPhoto=$photo}
Set-QADUser <sAMAaccountName> -ObjectAttributes @{jpegPhoto=$photo}

Используя оснастку PowerShell for Exchange:

Add-PSSnapin Microsoft.Exchange.Management.Powershell.E2010
Import-RecipientDataProperty -Identity <sAMAaccountName> -Picture -FileData ([Byte[]]$(Get-Content -Path "C:\Photo\MyPhoto.jpg" -Encoding Byte -ReadCount 0))

Ограничение оснастки на размер файла 10 кб. Заменяет только thumbnailphoto.

Используя оснастку PowerShell for Exchange 2013:

Add-PSSnapin Microsoft.Exchange.Management.PowerShell.SnapIn
$photo = ([Byte[]] $(Get-Content -Path "C:\Photo\MyPhoto.jpg" -Encoding Byte -ReadCount 0))
Set-UserPhoto -Identity <sAMAaccountName> -PictureData $photo -Confirm:$False
Set-UserPhoto -Identity <sAMAaccountName> -Save -Confirm:$False

Проверить фото через браузер (если у Вас Exchange 2013)
https://mail.domain.local/ews/Exchange.asmx/s/GetUserPhoto?email=user@domain.com&size=HR648x648


Используя PowerShell и ADSI:

[byte[]]$jpg = Get-Content "C:\Photo\MyPhoto.jpg" -encoding byte
$user = [adsi]"LDAP://cn=user1,cn=users,dc=domain,dc=loc"
$user.Properties["jpegPhoto"].Clear()
$null = $user.Properties["jpegPhoto"].Add($jpg)
$user.Properties["thumbnailPhoto"].Clear()
$null = $user.Properties["thumbnailPhoto"].Add($jpg)
$user.CommitChanges()

Все эти примеры, подгружают фото пользователя, не изменяя размер и качество картинки.

Для себя я остановился на использовании с модулем Microsoft PowerShell for Active Directory. Но при первой же попытке загрузить фото получил ошибку о невозможности подгрузить фото из файла размером 5 мегабайт. Первая идея была сконвертировать фото, сжав их до приемлемого размера. Но желание подучить PowerShell победило.

Так что усложним задачу загрузки фото. Добавим функцию изменения разрешения фото.

Функция принимает на входе полный путь к файлу, максимальное разрешение, качество сжатия.

Почти готовая функция была найдена на просторах интернета и допилена под конкретные задачи.
Function resizephoto(){
Param ( [Parameter(Mandatory=$True)] [ValidateNotNull()] $imageSource,
[Parameter(Mandatory=$true)][ValidateNotNull()] $canvasSize,
[Parameter(Mandatory=$true)][ValidateNotNull()] $quality )

# функция берет файлик и ужимет его

# проверки
if (!(Test-Path $imageSource)){throw( "Файл не найден")}
if ($canvasSize -lt 10 -or $canvasSize -gt 1000){throw( " Параметр размер должен быть от 10 до 1000")}
if ($quality -lt 0 -or $quality -gt 100){throw( " Параметр качества должен быть от 0 до 100")}

[void][System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")

$imageBytes = [byte[]](Get-Content $imageSource -Encoding byte)
$ms = New-Object IO.MemoryStream($imageBytes, 0, $imageBytes.Length)
$ms.Write($imageBytes, 0, $imageBytes.Length);

$bmp = [System.Drawing.Image]::FromStream($ms, $true)

# разрешение картинки после конвертации
$canvasWidth = $canvasSize
$canvasHeight = $canvasSize

# Задание качества картинки
$myEncoder = [System.Drawing.Imaging.Encoder]::Quality
$encoderParams = New-Object System.Drawing.Imaging.EncoderParameters(1)
$encoderParams.Param[0] = New-Object System.Drawing.Imaging.EncoderParameter($myEncoder, $quality)
#Получаем тип картинки
$myImageCodecInfo = [System.Drawing.Imaging.ImageCodecInfo]::GetImageEncoders()|where {$_.MimeType -eq 'image/jpeg'}

# Высчитывание кратности
$ratioX = $canvasWidth / $bmp.Width;
$ratioY = $canvasHeight / $bmp.Height;
$ratio = $ratioY
if($ratioX -le $ratioY){
    $ratio = $ratioX
}

# Создание пустой картинки
$newWidth = [int] ($bmp.Width*$ratio)
$newHeight = [int] ($bmp.Height*$ratio)
$bmpResized = New-Object System.Drawing.Bitmap($newWidth, $newHeight)
$graph = [System.Drawing.Graphics]::FromImage($bmpResized)

$graph.Clear([System.Drawing.Color]::White)
$graph.DrawImage($bmp,0,0 , $newWidth, $newHeight)

# Создание пустого потока
$ms = New-Object IO.MemoryStream
$bmpResized.Save($ms,$myImageCodecInfo, $($encoderParams))

# уборка
$bmpResized.Dispose()
$bmp.Dispose()

return $ms.ToArray()
}


Вставим данную функцию в скрипт.

Отрывок основной часть скрипта, прописать путь к фото, логин, табельный номер, ФИО
  $PhotoPath = '\\server\FOTO\' 
  #создаем PSDrive чтобы избежать проблем с обращениям к сетевым дискам при подключённой оснастке PSSQL 
  New-PSDrive -Name Photo -PSProvider FileSystem -Root $PhotoPath

    $UserLogin = 'login'
    $EmployeeID = '503'
    $FullName = 'Full User Name'
    
    # закомментировать для уменьшения вывода
    write-host "Обрабатываем: `n Логин: " $UserLogin "`n номер:"   $EmployeeID "`n ФИО: "  $FullName 
    # проверяем  соответствие логина в AD и внешней системе   
    $aduser = get-aduser $UserLogin -ErrorAction SilentlyContinue 
    if ($aduser.name -ne $FullName) {
            # если не совпадает выводим на экран и ничего не делаем 
            write-host "in Office " $FullName "`n in ad  " $aduser.name  "`nLogin " $UserLogin " `n`n" -ForegroundColor Red
    } else {
        # присваиваем EmployeeID в AD из внешней системы
        Set-ADUser $UserLogin -EmployeeID $EmployeeID
        
        $PhotoFile = 'Photo:\'+$EmployeeID+'.jpg'
        # проверяем что фото есть
        If (Test-Path $PhotoFile )  {
            # Если есть фото сотрудника

            # указываем путь к фото и качество
            $thumbnailPhoto = [byte[]]( $(resizephoto $PhotoFile 64 80))
            $jpegPhoto = [byte[]]( $(resizephoto $PhotoFile 648 80))

            # добавляем фото в thumbnailPhoto
            Set-ADUser $UserLogin -Replace @{thumbnailPhoto=$thumbnailPhoto} -ErrorVariable ErrorthumbnailPhoto  #-WhatIf
            if ($ErrorthumbnailPhoto -ne $null) {
                # в случае ошибки
                write-host "Ошибка добавления thumbnailPhoto на логине "$UserLogin  " ID " $_.autokey
                exit
			} 
            # добавляем фото в jpegPhoto
            Set-ADUser $UserLogin -Replace @{jpegPhoto=($jpegPhoto)} -ErrorVariable ErrorjpegPhoto #-WhatIf
            if ($ErrorjpegPhoto -ne $null) {
                # в случае ошибки
                write-host "Ошибка добавления jpegPhoto на логине "$UserLogin  " ID " $_.autokey
                exit
			} 
            if (!$ErrorthumbnailPhoto -and !$ErrorjpegPhoto) {
                # Если без ошибок
                # закомментировать для уменьшения вывода
                write-host 'Обработали...' -ForegroundColor Green
            }

        } else {
            # Если нет фото
            Write-Host " Фото " $PhotoFile " для " $UserLogin " не найдено"  -foregroundcolor red
        }

    }


Путь к фото задан через PSDrive, так как после подключения модуля PowerShell PSSQL для работы с MS SQL текущий путь меняется на PS SQLSERVER:\> и обращение к сетевым ресурсам, без смены папки, становится невозможным. Фото хранится на сетевом ресурсе, где имя файл табельный номер. В примере убрано логирование и обработка ошибок.


Выгрузка фото из Active Directory:
Несколько примеров для проверки корректности загрузки фото в Active Directory.

Используя модуль Microsoft PowerShell for Active Directory:

Import-Module ActiveDirectory
$user = Get-ADUser <sAMAaccountName> -Properties thumbnailphoto , jpegPhoto
$user.thumbnailphoto | Set-Content $env:temp\thumbnailphoto.jpg -Encoding byte 
$user.jpegPhoto | Set-Content $env:temp\jpegPhoto.jpg -Encoding byte 

Используя PowerShell и ADSI:

$username=$env:username
$domain=$env:userdomain
$temp=$env:temp
$thumbnailphoto = ([ADSISEARCHER]"samaccountname=$($username)").findone().properties.thumbnailphoto
if(!($thumbnailphoto -eq $null)) {$thumbnailphoto | set-content $temp\$domain+$username.thumbnailphoto.jpg -Encoding byte}
$jpegphoto = ([ADSISEARCHER]"samaccountname=$($username)").findone().Properties.jpegphoto
if(!($jpegphoto -eq $null)) {$jpegphoto  | set-content $temp\$domain+$username.jpegPhoto.jpg -Encoding byte}

Поиск пользователей с/без фото:

Import-Module ActiveDirectory
Get-ADUser -Filter * -properties thumbnailPhoto | ? {$_.thumbnailPhoto} | select Name
Get-ADUser -Filter * -properties thumbnailPhoto | ? {(-not($_.thumbnailPhoto))} | select Name
Get-ADUser -Filter * -properties jpegPhoto | ? {$_.jpegPhoto} | select Name
Get-ADUser -Filter * -properties jpegPhoto | ? {(-not($_.jpegPhoto))} | select Name

Что можно ещё делать с фото загруженными в Active Directory?

Использование фото из AD в меню Windows
«Use AD Photos as Windows 7 User Tiles»;
«Set Windows 7 User Tile to AD Thumbnail pic».

Или написать свой телефонный справочник как titulusdesiderio" «Telephone Directory» с блэкджеком и фото.
Tags:powershellactive directoryexchangeoutlooklyncsharepointscripting
Hubs: PowerShell
+18
66.6k 171
Comments 5
Top of the last 24 hours