Nie wiem jak Wam, ale mi się taki klimat podoba. Niestandardowe tło z logiem firmy, zdjęcie pracownika na ekranie logowania przy koncie użytkownika…to sprawia, że jak ktoś patrzy na takie środowisko z boku to myśli, że wdrażał to jakiś profesjonalista, który dba o wszystkie szczegóły i w sumie w mojej pracy to jest to, do czego dążę, czyli być najlepszym w swoim rodzaju. Okej, ale dosyć lizania własnych jajek, przejdźmy do konkretów, czyli do tego jak to wdrożyć – zdjęcia użytkowników w AD i w Exchange 2019.
Exchange to taki dodatek do zdjęć użytkowników – tutaj ponownie poczta wygląda profesjonalnie w organizacji, gdy jest wszystko tak poukładane. Efekt w OWA (Outlook Web Access) wygląda tak:
Do tego będą nam potrzebne zdjęcia naszych użytkowników i te zdjęcia muszą być kwadratowe, czyli np. być rozmiaru 500x500px. Nie ma znaczenia czy te zdjęcie jest duże czy małe (byle nie za małe, bo wtedy będzie pikseloza, a nie twarze użytkowników) dlatego, bo i tak te zdjęcia zostaną w pewnym stopniu skompresowane. Zdjęcia, które ja wykorzystałem są z Unsplash i przedstawiają modelów w moim testowym środowisku. Do wycięcia zdjęć można użyć program wg własnego uznania, np. IrfanView czy Adobe Photoshop. Następnie takie pliki użytkowników należy jakoś nazwać. Najlepszym rozwiązaniem jest nazwanie plików imieniem i nazwiskiem, ponieważ wtedy możemy je wykorzystać lepiej w programie, którym będziemy przypisywać zdjęcia do użytkowników w AD, przykład: Radosław.Serba.jpg
. Ja skorzystałem z nazwy sAMAccountName, czyli w moim przypadku rserba.jpg
(niestety ta opcja nie pozwala na automatyczne dopasowanie obrazków do kont użytkowników w CodeTwo Active Directory Photos.
Zdjęcia w AD
Ogólnie zdjęcie użytkownika jest przechowywane w atrybucie użytkownika thumbnailPhoto
i w plaintext można zobaczyć tylko w krótkim fragmencie:
Get-ADUser -Identity Administrator -Properties thumbnailPhoto DistinguishedName : CN=Administrator,CN=Users,DC=serba,DC=local Enabled : True GivenName : Name : Administrator ObjectClass : user ObjectGUID : 9dfaf3bb-fcc9-4f09-be3f-b35b95f7f278 SamAccountName : Administrator SID : S-1-5-21-723521058-2419329218-2805022534-500 Surname : thumbnailPhoto : {137, 80, 78, 71...} UserPrincipalName : Administrator@serba.local
W takim razie jak ustawić taki atrybut? Są dwa rozwiązania: przez PowerShell lub za pomocą fajnej aplikacji zrobionej przez CodeTwo.
PowerShell:
Set-ADUser rserba -Replace @{thumbnailPhoto=([byte[]](Get-Content "C:\zdjecia\rserba.jpg" -Encoding byte))}
CodeTwo Active Directory Photos:
Sytuacja jest prosta: instalujemy program z linka, odpalamy, wybieramy OU, w którym chcemy ustawić zdjęcia i zaznaczamy konta, w których chcemy ustawić avatarki, a następnie klikamy Import.
Tutaj wszystko zależy od tego jaki mamy schemat nazw plików obrazków. Jeśli trzymaliśmy się schematu, który proponowałem na początku to wystarczy, że pomiędzy {First name}
i {Last name}
wstawimy kropkę, więc pole będzie wypełnione tak:{First name}.{Last name}
Po tym możemy kliknąć przycisk Automatch >.
Co prawda, ja tu już zdjęcia mam, ale to, co mamy tutaj na ekranie to scenariusz, w którym zdjęcia mogły nie być dopasowane i musimy je dopasować ręcznie, więc wystarczy przeciągnąć obrazki z prawej strony do odpowiedniego użytkownika, a następnie kliknąć Next >. Po tym wystarczy sprawdzić, czy zmiany, które chcemy zastosować nam pasują i zaznaczyć checkbox Apply the above settings to all selected images. Wartość jest ustawiona na 100 KB nieprzypadkowo, ponieważ to jest limit wielkości obrazka przechowywany w atrybcie thumbnailPhoto
użytkownika.
![](https://it.supra.tf/wp-content/uploads/2020/09/image-14.png)
Gdy to zatwierdzimy to zdjęcia się zaimportują i jedną część mamy gotową. Drugą częścią jest podpięcie gotowego skryptu, który będzie pobierał zdjęcia użytkowników przy wylogowywaniu się. Bez niego obrazki będą się importowały nam na maszynach tylko i wyłącznie, gdy posiadamy na nich lokalne uprawnienia administracyjne, czyli w sumie będzie to działać tylko dla adminów. Mowa tutaj o skrypcie, który także jest na stronie CodeTwo:
[CmdletBinding(SupportsShouldProcess = $true)]Param() function Test-Null($InputObject) { return !([bool]$InputObject) } Function ResizeImage() { param([String]$ImagePath, [Int]$Quality = 90, [Int]$targetSize, [String]$OutputLocation) Add-Type -AssemblyName "System.Drawing" $img = [System.Drawing.Image]::FromFile($ImagePath) $CanvasWidth = $targetSize $CanvasHeight = $targetSize #Encoder parameter for image quality $ImageEncoder = [System.Drawing.Imaging.Encoder]::Quality $encoderParams = New-Object System.Drawing.Imaging.EncoderParameters(1) $encoderParams.Param[0] = New-Object System.Drawing.Imaging.EncoderParameter($ImageEncoder, $Quality) # get codec $Codec = [System.Drawing.Imaging.ImageCodecInfo]::GetImageEncoders() | Where { $_.MimeType -eq 'image/jpeg' } #compute the final ratio to use $ratioX = $CanvasWidth / $img.Width; $ratioY = $CanvasHeight / $img.Height; $ratio = $ratioY if ($ratioX -le $ratioY) { $ratio = $ratioX } $newWidth = [int] ($img.Width * $ratio) $newHeight = [int] ($img.Height * $ratio) $bmpResized = New-Object System.Drawing.Bitmap($newWidth, $newHeight) $graph = [System.Drawing.Graphics]::FromImage($bmpResized) $graph.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic $graph.Clear([System.Drawing.Color]::White) $graph.DrawImage($img, 0, 0, $newWidth, $newHeight) #save to file $bmpResized.Save($OutputLocation, $Codec, $($encoderParams)) $bmpResized.Dispose() $img.Dispose() } #get sid and photo for current user $user = ([ADSISearcher]"(&(objectCategory=User)(SAMAccountName=$env:username))").FindOne().Properties $user_photo = $user.thumbnailphoto $user_sid = [System.Security.Principal.WindowsIdentity]::GetCurrent().User.Value Write-Verbose "Updating account picture for $($user.displayname)..." #continue if an image was returned If ((Test-Null $user_photo) -eq $false) { Write-Verbose "Success. Photo exists in Active Directory." #set up image sizes and base path $image_sizes = @(32, 40, 48, 96, 192, 200, 240, 448) $image_mask = "Image{0}.jpg" $image_base = "C:\ProgramData\AccountPictures" #set up registry $reg_base = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\AccountPicture\Users\{0}" $reg_key = [string]::format($reg_base, $user_sid) $reg_value_mask = "Image{0}" If ((Test-Path -Path $reg_key) -eq $false) { New-Item -Path $reg_key } #save images, set reg keys ForEach ($size in $image_sizes) { #create hidden directory, if it doesn't exist $dir = $image_base + "\" + $user_sid If ((Test-Path -Path $dir) -eq $false) { $(mkdir $dir).Attributes = "Hidden" } #save photo to disk, overwrite existing files $file_name = ([string]::format($image_mask, $size)) $pathtmp = $dir + "\_" + $file_name $path = $dir + "\" + $file_name Write-Verbose " saving: $file_name" $user_photo | Set-Content -Path $pathtmp -Encoding Byte -Force ResizeImage $pathtmp $size $size $path Remove-Item $pathtmp #save the path in registry, overwrite existing entries $name = [string]::format($reg_value_mask, $size) $value = New-ItemProperty -Path $reg_key -Name $name -Value $path -Force } Write-Verbose "Done." } else { Write-Error "No photo found in Active Directory for $env:username" }
Taki skrypt zapisujemy w pliku, ja nazwałem go UserProfilePicture.ps1
i umieściłem go w folderze NETLOGON
. Ten folder jest synchronizowany pomiędzy wszystkimi kontrolerami domeny. Następnie należy stworzyć politykę GPO, w której definiujemy ten skrypt jako skrypt uruchamiany przy wylogowaniu użytkownika, więc należy przejść do Konfiguracja użytkownika > Ustawienia systemu Windows > Skrypty (logowanie/wylogowywanie) > Wylogowywanie.
Następnie, w zakładce Skrypty PowerShell należy wskazać ścieżkę do pliku. Jeśli plik jest umieszczony bezpośrednio w NETLOGON, ścieżka powinna być //<nazwa-domeny>/NETLOGON/<nazwa-skryptu>
i tak też jest w moim przypadku.
Po dodaniu można zapisać i zamknąć wszystkie okna, a następnie poczekać, aż polityki się zaaktualizują na komputerach. Po tym kwestia zdjęć na komputerach jest załatwiona.
Zdjęcia w Exchange 2019
Obstawiam, że sposób działania zdjęć jest mniej więcej taki sam w starszych wersjach Exchange 2019 (mam na myśli Exchange 2016 i 2013, bo w 2010 zmiany są ponoć spore). Do wrzucenia zdjęć możemy wykorzystać poniższy skrypt, lecz będziemy w nim musieli zmienić główną gałąź naszego OU, w którym przechowujemy użytkowników (linia 3) oraz ścieżkę do folderu, w którym przechowujemy pliki (linia 7). Poniżej opcja skryptu dla wykorzystywania nazw sAMAccountName:
Import-Module activedirectory $users = Get-ADUser -Filter "ObjectClass -eq 'user'" -SearchBase 'OU=it.supra.tf,DC=serba,DC=local' foreach($user in $users) { $samaccountname = $user.SamAccountName Set-UserPhoto -Identity $user.SamAccountName -PictureData ([System.IO.File]::ReadAllBytes("C:\Users\rserba.SERBA\Desktop\it.supra.tf\$samaccountname.jpg")) -Confirm:$false }
Ponadto druga opcja z imionami i nazwiskami oddzielonymi kropką:
Import-Module activedirectory $users = Get-ADUser -Filter "ObjectClass -eq 'user'" -SearchBase 'OU=it.supra.tf,DC=serba,DC=local' foreach($user in $users) { $firstname = $user.firstname $lastname = $user.lastname Set-UserPhoto -Identity $user.SamAccountName -PictureData ([System.IO.File]::ReadAllBytes("C:\Users\rserba.SERBA\Desktop\it.supra.tf\$firstname.$lastname.jpg")) -Confirm:$false }
Taki skrypt odpalamy w Exchange Management Shell. Jeśli popełniliśmy błędy w nazwie plików lub one nie istnieją, zostaniemy o tym poinformowani przez skrypt:
[PS] C:\Users\rserba.SERBA\desktop>.\UpdateExchangePictureProfile.ps1 Exception calling "ReadAllBytes" with "1" argument(s): "Nie można odnaleźć pliku 'C:\Users\rserba.SERBA\Desktop\it.supra.tf\ferexio.jpg'." At C:\Users\rserba.SERBA\desktop\UpdateExchangePictureProfile.ps1:7 char:5 + Set-UserPhoto -Identity $user.SamAccountName -PictureData ([Syste ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [], MethodInvocationException + FullyQualifiedErrorId : FileNotFoundException Exception calling "ReadAllBytes" with "1" argument(s): "Nie można odnaleźć pliku 'C:\Users\rserba.SERBA\Desktop\it.supra.tf\kszymocha.jpg'." At C:\Users\rserba.SERBA\desktop\UpdateExchangePictureProfile.ps1:7 char:5 + Set-UserPhoto -Identity $user.SamAccountName -PictureData ([Syste ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [], MethodInvocationException + FullyQualifiedErrorId : FileNotFoundException
Efekty widać na początku posta 😊