Resizing a photo to a format compatible with Active Directory
I have a function to resize an image that I am receiving from an external system to then enter into Active Directory to meet the size restrictions, however it doesn't seem to work. When I resize the image using this code, it doesn't seem to be compatible and attempting to set the image generates errors.
If I open this image, it looks fine. If i then then save it with an image editor, I can then import it into AD, so it's definitely something about this function that isn't quite right, I think it's something encoding related, but I don't know quite enough to fix it.
I can load the photo (the original one that's too big for AD) using the following code:
$original = [system.io.file]::ReadAllBytes('C:original.jpg')
I then resize it and attempt to add it to AD, which fails.
$scaled = ResizeImage -ImageData $original -Percentage 80
Set-ADUser user -Replace @{ thumbnailPhoto=$scaled }
This gives me errors from
Set-ADUser : Multiple values were specified for an attribute that can have only one value
to
Set-ADUser : Unable to contact the server. This may be because this server does not exist, it is currently down, or it does not have the Active Directory Web Services running.
depending on how and where it's run. I've confirmed it's not permissions or connectivity related, as I can write other data and images not scaled through this process.
If i write the the $Scaled image to disk, i can then re-save it with an image editor which makes this work. Function is below, any help would be massively appreciated!
function ResizeImage {
[CmdletBinding()]
Param (
[Parameter(Mandatory)]
[byte]
$ImageData,
[Parameter(Mandatory)]
[double]
$Percentage,
[Parameter()]
[ValidateSet('HighQuality', 'HighSpeed', 'Antialias', 'Default', 'None')]
[string]
$SmoothingMode = "Default",
[Parameter()]
[ValidateSet('Bicubic', 'Bilinear', 'HighQualityBicubic', 'HighQualityBilinear', 'Default', 'High', 'Low', 'NearestNeighbor')]
[string]
$InterpolationMode = "Default",
[Parameter()]
[ValidateSet('HighQuality', 'HighSpeed', 'Half', 'Default', 'None')]
[string]
$PixelOffsetMode = "Default"
)
Begin {
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
}
Process {
$ImageBase64 = [System.Convert]::ToBase64String($ImageData)
$ImageMemory = [System.IO.MemoryStream][System.Convert]::FromBase64String($ImageBase64)
$Bitmap = [System.Drawing.Bitmap][System.Drawing.Image]::FromStream($ImageMemory)
$Product = $Percentage / 100
[int]$NewHeight = $Bitmap.Height * $Product
[int]$NewWidth = $Bitmap.Width * $Product
$NewMemory = [System.IO.MemoryStream]::New()
$NewBitmap = [System.Drawing.Bitmap]::New($NewWidth, $NewHeight)
$NewImage = [System.Drawing.Graphics]::FromImage($NewBitmap)
$NewImage.SmoothingMode = $SmoothingMode
$NewImage.InterpolationMode = $InterpolationMode
$NewImage.PixelOffsetMode = $PixelOffsetMode
$NewImage.DrawImage($Bitmap, $(New-Object -TypeName System.Drawing.Rectangle -ArgumentList 0, 0, $NewWidth, $NewHeight))
$NewBitmap.Save($NewMemory, [System.Drawing.Imaging.ImageFormat]::Jpeg)
[byte]$NewMemory.ToArray()
}
}
.net powershell active-directory
add a comment |
I have a function to resize an image that I am receiving from an external system to then enter into Active Directory to meet the size restrictions, however it doesn't seem to work. When I resize the image using this code, it doesn't seem to be compatible and attempting to set the image generates errors.
If I open this image, it looks fine. If i then then save it with an image editor, I can then import it into AD, so it's definitely something about this function that isn't quite right, I think it's something encoding related, but I don't know quite enough to fix it.
I can load the photo (the original one that's too big for AD) using the following code:
$original = [system.io.file]::ReadAllBytes('C:original.jpg')
I then resize it and attempt to add it to AD, which fails.
$scaled = ResizeImage -ImageData $original -Percentage 80
Set-ADUser user -Replace @{ thumbnailPhoto=$scaled }
This gives me errors from
Set-ADUser : Multiple values were specified for an attribute that can have only one value
to
Set-ADUser : Unable to contact the server. This may be because this server does not exist, it is currently down, or it does not have the Active Directory Web Services running.
depending on how and where it's run. I've confirmed it's not permissions or connectivity related, as I can write other data and images not scaled through this process.
If i write the the $Scaled image to disk, i can then re-save it with an image editor which makes this work. Function is below, any help would be massively appreciated!
function ResizeImage {
[CmdletBinding()]
Param (
[Parameter(Mandatory)]
[byte]
$ImageData,
[Parameter(Mandatory)]
[double]
$Percentage,
[Parameter()]
[ValidateSet('HighQuality', 'HighSpeed', 'Antialias', 'Default', 'None')]
[string]
$SmoothingMode = "Default",
[Parameter()]
[ValidateSet('Bicubic', 'Bilinear', 'HighQualityBicubic', 'HighQualityBilinear', 'Default', 'High', 'Low', 'NearestNeighbor')]
[string]
$InterpolationMode = "Default",
[Parameter()]
[ValidateSet('HighQuality', 'HighSpeed', 'Half', 'Default', 'None')]
[string]
$PixelOffsetMode = "Default"
)
Begin {
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
}
Process {
$ImageBase64 = [System.Convert]::ToBase64String($ImageData)
$ImageMemory = [System.IO.MemoryStream][System.Convert]::FromBase64String($ImageBase64)
$Bitmap = [System.Drawing.Bitmap][System.Drawing.Image]::FromStream($ImageMemory)
$Product = $Percentage / 100
[int]$NewHeight = $Bitmap.Height * $Product
[int]$NewWidth = $Bitmap.Width * $Product
$NewMemory = [System.IO.MemoryStream]::New()
$NewBitmap = [System.Drawing.Bitmap]::New($NewWidth, $NewHeight)
$NewImage = [System.Drawing.Graphics]::FromImage($NewBitmap)
$NewImage.SmoothingMode = $SmoothingMode
$NewImage.InterpolationMode = $InterpolationMode
$NewImage.PixelOffsetMode = $PixelOffsetMode
$NewImage.DrawImage($Bitmap, $(New-Object -TypeName System.Drawing.Rectangle -ArgumentList 0, 0, $NewWidth, $NewHeight))
$NewBitmap.Save($NewMemory, [System.Drawing.Imaging.ImageFormat]::Jpeg)
[byte]$NewMemory.ToArray()
}
}
.net powershell active-directory
Try changing theSet-ADUser
argument:@{ thumbnailPhoto=$scaled }
->@{ thumbnailPhoto=,$scaled }
– Mathias R. Jessen
Nov 12 '18 at 16:41
This gives me anSet-ADUser : Invalid type 'System.Object'.
error. I know the setting of the attribute is OK, since if I import an image without scaling it that part works, it's just the run through of the function that seems to change the encoding or the properties of the image in such a way that it's incompatible with AD (or the Set-Aduser function).
– Dave Green
Nov 12 '18 at 17:12
Then it's most likely just because of a type check, explicitly cast to[byte]
and you should be good:@{ thumbnailPhoto = [byte]$scaled }
– Mathias R. Jessen
Nov 12 '18 at 17:30
Thats crazy! it works in my test, i'll try it in my full test tomorrow and let you know. Do you know why that's needed to work even though i'm using [byte]$NewMemory.ToArray() to emit the object from the function? I can't see any difference in the object when piping both $scaled and [byte]$scaled to get-member.
– Dave Green
Nov 12 '18 at 19:05
This works over a wide range of images, thank you very much!
– Dave Green
Nov 13 '18 at 7:55
add a comment |
I have a function to resize an image that I am receiving from an external system to then enter into Active Directory to meet the size restrictions, however it doesn't seem to work. When I resize the image using this code, it doesn't seem to be compatible and attempting to set the image generates errors.
If I open this image, it looks fine. If i then then save it with an image editor, I can then import it into AD, so it's definitely something about this function that isn't quite right, I think it's something encoding related, but I don't know quite enough to fix it.
I can load the photo (the original one that's too big for AD) using the following code:
$original = [system.io.file]::ReadAllBytes('C:original.jpg')
I then resize it and attempt to add it to AD, which fails.
$scaled = ResizeImage -ImageData $original -Percentage 80
Set-ADUser user -Replace @{ thumbnailPhoto=$scaled }
This gives me errors from
Set-ADUser : Multiple values were specified for an attribute that can have only one value
to
Set-ADUser : Unable to contact the server. This may be because this server does not exist, it is currently down, or it does not have the Active Directory Web Services running.
depending on how and where it's run. I've confirmed it's not permissions or connectivity related, as I can write other data and images not scaled through this process.
If i write the the $Scaled image to disk, i can then re-save it with an image editor which makes this work. Function is below, any help would be massively appreciated!
function ResizeImage {
[CmdletBinding()]
Param (
[Parameter(Mandatory)]
[byte]
$ImageData,
[Parameter(Mandatory)]
[double]
$Percentage,
[Parameter()]
[ValidateSet('HighQuality', 'HighSpeed', 'Antialias', 'Default', 'None')]
[string]
$SmoothingMode = "Default",
[Parameter()]
[ValidateSet('Bicubic', 'Bilinear', 'HighQualityBicubic', 'HighQualityBilinear', 'Default', 'High', 'Low', 'NearestNeighbor')]
[string]
$InterpolationMode = "Default",
[Parameter()]
[ValidateSet('HighQuality', 'HighSpeed', 'Half', 'Default', 'None')]
[string]
$PixelOffsetMode = "Default"
)
Begin {
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
}
Process {
$ImageBase64 = [System.Convert]::ToBase64String($ImageData)
$ImageMemory = [System.IO.MemoryStream][System.Convert]::FromBase64String($ImageBase64)
$Bitmap = [System.Drawing.Bitmap][System.Drawing.Image]::FromStream($ImageMemory)
$Product = $Percentage / 100
[int]$NewHeight = $Bitmap.Height * $Product
[int]$NewWidth = $Bitmap.Width * $Product
$NewMemory = [System.IO.MemoryStream]::New()
$NewBitmap = [System.Drawing.Bitmap]::New($NewWidth, $NewHeight)
$NewImage = [System.Drawing.Graphics]::FromImage($NewBitmap)
$NewImage.SmoothingMode = $SmoothingMode
$NewImage.InterpolationMode = $InterpolationMode
$NewImage.PixelOffsetMode = $PixelOffsetMode
$NewImage.DrawImage($Bitmap, $(New-Object -TypeName System.Drawing.Rectangle -ArgumentList 0, 0, $NewWidth, $NewHeight))
$NewBitmap.Save($NewMemory, [System.Drawing.Imaging.ImageFormat]::Jpeg)
[byte]$NewMemory.ToArray()
}
}
.net powershell active-directory
I have a function to resize an image that I am receiving from an external system to then enter into Active Directory to meet the size restrictions, however it doesn't seem to work. When I resize the image using this code, it doesn't seem to be compatible and attempting to set the image generates errors.
If I open this image, it looks fine. If i then then save it with an image editor, I can then import it into AD, so it's definitely something about this function that isn't quite right, I think it's something encoding related, but I don't know quite enough to fix it.
I can load the photo (the original one that's too big for AD) using the following code:
$original = [system.io.file]::ReadAllBytes('C:original.jpg')
I then resize it and attempt to add it to AD, which fails.
$scaled = ResizeImage -ImageData $original -Percentage 80
Set-ADUser user -Replace @{ thumbnailPhoto=$scaled }
This gives me errors from
Set-ADUser : Multiple values were specified for an attribute that can have only one value
to
Set-ADUser : Unable to contact the server. This may be because this server does not exist, it is currently down, or it does not have the Active Directory Web Services running.
depending on how and where it's run. I've confirmed it's not permissions or connectivity related, as I can write other data and images not scaled through this process.
If i write the the $Scaled image to disk, i can then re-save it with an image editor which makes this work. Function is below, any help would be massively appreciated!
function ResizeImage {
[CmdletBinding()]
Param (
[Parameter(Mandatory)]
[byte]
$ImageData,
[Parameter(Mandatory)]
[double]
$Percentage,
[Parameter()]
[ValidateSet('HighQuality', 'HighSpeed', 'Antialias', 'Default', 'None')]
[string]
$SmoothingMode = "Default",
[Parameter()]
[ValidateSet('Bicubic', 'Bilinear', 'HighQualityBicubic', 'HighQualityBilinear', 'Default', 'High', 'Low', 'NearestNeighbor')]
[string]
$InterpolationMode = "Default",
[Parameter()]
[ValidateSet('HighQuality', 'HighSpeed', 'Half', 'Default', 'None')]
[string]
$PixelOffsetMode = "Default"
)
Begin {
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
}
Process {
$ImageBase64 = [System.Convert]::ToBase64String($ImageData)
$ImageMemory = [System.IO.MemoryStream][System.Convert]::FromBase64String($ImageBase64)
$Bitmap = [System.Drawing.Bitmap][System.Drawing.Image]::FromStream($ImageMemory)
$Product = $Percentage / 100
[int]$NewHeight = $Bitmap.Height * $Product
[int]$NewWidth = $Bitmap.Width * $Product
$NewMemory = [System.IO.MemoryStream]::New()
$NewBitmap = [System.Drawing.Bitmap]::New($NewWidth, $NewHeight)
$NewImage = [System.Drawing.Graphics]::FromImage($NewBitmap)
$NewImage.SmoothingMode = $SmoothingMode
$NewImage.InterpolationMode = $InterpolationMode
$NewImage.PixelOffsetMode = $PixelOffsetMode
$NewImage.DrawImage($Bitmap, $(New-Object -TypeName System.Drawing.Rectangle -ArgumentList 0, 0, $NewWidth, $NewHeight))
$NewBitmap.Save($NewMemory, [System.Drawing.Imaging.ImageFormat]::Jpeg)
[byte]$NewMemory.ToArray()
}
}
.net powershell active-directory
.net powershell active-directory
asked Nov 12 '18 at 16:34
Dave GreenDave Green
1
1
Try changing theSet-ADUser
argument:@{ thumbnailPhoto=$scaled }
->@{ thumbnailPhoto=,$scaled }
– Mathias R. Jessen
Nov 12 '18 at 16:41
This gives me anSet-ADUser : Invalid type 'System.Object'.
error. I know the setting of the attribute is OK, since if I import an image without scaling it that part works, it's just the run through of the function that seems to change the encoding or the properties of the image in such a way that it's incompatible with AD (or the Set-Aduser function).
– Dave Green
Nov 12 '18 at 17:12
Then it's most likely just because of a type check, explicitly cast to[byte]
and you should be good:@{ thumbnailPhoto = [byte]$scaled }
– Mathias R. Jessen
Nov 12 '18 at 17:30
Thats crazy! it works in my test, i'll try it in my full test tomorrow and let you know. Do you know why that's needed to work even though i'm using [byte]$NewMemory.ToArray() to emit the object from the function? I can't see any difference in the object when piping both $scaled and [byte]$scaled to get-member.
– Dave Green
Nov 12 '18 at 19:05
This works over a wide range of images, thank you very much!
– Dave Green
Nov 13 '18 at 7:55
add a comment |
Try changing theSet-ADUser
argument:@{ thumbnailPhoto=$scaled }
->@{ thumbnailPhoto=,$scaled }
– Mathias R. Jessen
Nov 12 '18 at 16:41
This gives me anSet-ADUser : Invalid type 'System.Object'.
error. I know the setting of the attribute is OK, since if I import an image without scaling it that part works, it's just the run through of the function that seems to change the encoding or the properties of the image in such a way that it's incompatible with AD (or the Set-Aduser function).
– Dave Green
Nov 12 '18 at 17:12
Then it's most likely just because of a type check, explicitly cast to[byte]
and you should be good:@{ thumbnailPhoto = [byte]$scaled }
– Mathias R. Jessen
Nov 12 '18 at 17:30
Thats crazy! it works in my test, i'll try it in my full test tomorrow and let you know. Do you know why that's needed to work even though i'm using [byte]$NewMemory.ToArray() to emit the object from the function? I can't see any difference in the object when piping both $scaled and [byte]$scaled to get-member.
– Dave Green
Nov 12 '18 at 19:05
This works over a wide range of images, thank you very much!
– Dave Green
Nov 13 '18 at 7:55
Try changing the
Set-ADUser
argument: @{ thumbnailPhoto=$scaled }
-> @{ thumbnailPhoto=,$scaled }
– Mathias R. Jessen
Nov 12 '18 at 16:41
Try changing the
Set-ADUser
argument: @{ thumbnailPhoto=$scaled }
-> @{ thumbnailPhoto=,$scaled }
– Mathias R. Jessen
Nov 12 '18 at 16:41
This gives me an
Set-ADUser : Invalid type 'System.Object'.
error. I know the setting of the attribute is OK, since if I import an image without scaling it that part works, it's just the run through of the function that seems to change the encoding or the properties of the image in such a way that it's incompatible with AD (or the Set-Aduser function).– Dave Green
Nov 12 '18 at 17:12
This gives me an
Set-ADUser : Invalid type 'System.Object'.
error. I know the setting of the attribute is OK, since if I import an image without scaling it that part works, it's just the run through of the function that seems to change the encoding or the properties of the image in such a way that it's incompatible with AD (or the Set-Aduser function).– Dave Green
Nov 12 '18 at 17:12
Then it's most likely just because of a type check, explicitly cast to
[byte]
and you should be good: @{ thumbnailPhoto = [byte]$scaled }
– Mathias R. Jessen
Nov 12 '18 at 17:30
Then it's most likely just because of a type check, explicitly cast to
[byte]
and you should be good: @{ thumbnailPhoto = [byte]$scaled }
– Mathias R. Jessen
Nov 12 '18 at 17:30
Thats crazy! it works in my test, i'll try it in my full test tomorrow and let you know. Do you know why that's needed to work even though i'm using [byte]$NewMemory.ToArray() to emit the object from the function? I can't see any difference in the object when piping both $scaled and [byte]$scaled to get-member.
– Dave Green
Nov 12 '18 at 19:05
Thats crazy! it works in my test, i'll try it in my full test tomorrow and let you know. Do you know why that's needed to work even though i'm using [byte]$NewMemory.ToArray() to emit the object from the function? I can't see any difference in the object when piping both $scaled and [byte]$scaled to get-member.
– Dave Green
Nov 12 '18 at 19:05
This works over a wide range of images, thank you very much!
– Dave Green
Nov 13 '18 at 7:55
This works over a wide range of images, thank you very much!
– Dave Green
Nov 13 '18 at 7:55
add a comment |
1 Answer
1
active
oldest
votes
For an AD thumbnailPhoto
the maximum size is 96x96 pixels, so I usually use functions like this to make the original image fit that little square.
Instead of using a function that requires you to give a percentage, it is easier to simply use these max. dimensions.
This is the helper function that resizes the origimal image:
function Shrink-Image {
# helper function to resize an image so it fits inside the given "TargetSize"
# It returns the path and filename of the resized image or the path to the original image if
# that happened to fit inside the "TargetSize" square.
[CmdletBinding()]
param (
[Parameter(Mandatory=$true, Position=0)]
[Alias("FileName")]
[ValidateScript({Test-Path $_ -PathType Leaf})]
[string]$ImagePath,
[Parameter(Mandatory=$true, Position=1)]
[int]$TargetSize,
[int]$Quality = 90
)
Add-Type -AssemblyName "System.Drawing"
$img = [System.Drawing.Image]::FromFile($ImagePath)
# if the original image fits inside the target size, we're done
if ($img.Width -le $TargetSize -and $img.Height -le $TargetSize) {
$img.Dispose()
return $ImagePath
}
# set the encoder 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)
# set the output codec to jpg
$Codec = [System.Drawing.Imaging.ImageCodecInfo]::GetImageEncoders() | Where-Object {$_.MimeType -eq 'image/jpeg'}
# calculate the image ratio
$ratioX = [double]($TargetSize / $img.Width)
$ratioY = [double]($TargetSize / $img.Height)
if ($ratioX -le $ratioY) { $ratio = $ratioX } else { $ratio = $ratioY }
$newWidth = [int]($img.Width * $ratio)
$newHeight = [int]($img.Height * $ratio)
$newImage = New-Object System.Drawing.Bitmap($newWidth, $newHeight)
$graph = [System.Drawing.Graphics]::FromImage($newImage)
$graph.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic
$graph.Clear([System.Drawing.Color]::White)
$graph.DrawImage($img, 0, 0, $newWidth, $newHeight)
# save the new image as temp file
$OutputPath = [System.IO.Path]::GetTempFileName()
# For safety: a [System.Drawing.Bitmap] does not have a "overwrite if exists" option for the Save() method
if (Test-Path $OutputPath) { Remove-Item $OutputPath -Force }
$newImage.Save($OutputPath, $Codec, $($encoderParams))
$graph.Dispose()
$newImage.Dispose()
$img.Dispose()
return $OutputPath
}
It resizes if needed to the given $TargetSize
and returns the full path and filename for this resized image.
(Note: the returned path CAN also be the same as the original image if that happens to fit inside the $TargetSize
square)
Next the function to actually set the picture in the AD thumbnailPhoto
property of a user:
function Set-ADUserPicture {
[CmdletBinding(DefaultParameterSetName = 'ByName')]
param (
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'ByName', Position = 0)]
[String]$Identity, # This can be: Distinguished Name, objectGUID, objectSid, sAMAccountName
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'ByObject', Position = 0)]
[Object]$User, # This is the user object from Get-ADUser
[Parameter(Mandatory = $true, Position = 1)]
[String]$ImageFile
)
if ($PSCmdlet.ParameterSetName -eq 'ByName') {
$User = Get-ADUser -Identity $Identity
}
if ($User) {
Write-Verbose ("Inserting userpicture in Active Directory for user {0}" -f $User.Name)
try {
# create a thumbnail using the original image, and size it down to 96x96 pixels
$pictureFile = Shrink-Image -ImagePath $ImageFile -TargetSize 96
[byte]$pictureData = [System.IO.File]::ReadAllBytes($pictureFile)
$User | Set-ADUser -Replace @{thumbnailPhoto = $pictureData } -ErrorAction Stop
}
catch {
Write-Error "Set-ADUserPicture: $($_.Exception.Message)"
}
finally {
if ($pictureFile -ne $ImageFile) {
# the original image was larger than 96x96 pixels, so we created a temp file. Remove that.
Remove-Item $pictureFile -Force -ErrorAction SilentlyContinue
}
}
}
}
You call it using an Identity like Set-ADUserPicture -Identity $SamAccountName -ImageFile $UserImagePath
or by using the user object you have obtained earlier using the Get-ADUser
cmdlet like $userObject | Set-ADUserPicture -ImageFile $UserImagePath
If you want to do something similar for the Office365 profile picture, then the max. dimensions are 648x648 pixels. For that you need to log in first of course using something like this:
$Cred = Get-Credential -UserName "admin@yourdomain.com" -Message "Please enter admin credentials for Office365"
$Url = "https://outlook.office365.com/powershell-liveid/?proxyMethod=RPS"
$Session = New-PSSession -ConfigurationName "Microsoft.Exchange" -ConnectionUri $Url -Credential $Cred -Authentication Basic -AllowRedirection
Import-PSSession $Session -DisableNameChecking -AllowClobber -WarningAction SilentlyContinue -ErrorAction SilentlyContinue | Out-Null
After that, the next function will set that image similar to the function for the AD thumbnailPhoto
:
function Set-O365UserPicture {
[CmdletBinding(DefaultParameterSetName = 'ByName')]
param (
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'ByName', Position = 0)]
[String]$Identity, # This can be: Distinguished Name, objectGUID, objectSid, sAMAccountName
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'ByObject', Position = 0)]
[Object]$User, # This is the user object from Get-ADUser
[Parameter(Mandatory = $true, Position = 1)]
[String]$ImageFile
)
if ($PSCmdlet.ParameterSetName -eq 'ByName') {
$User = Get-ADUser -Identity $Identity
}
if ($User) {
Write-Verbose ("Inserting userpicture in Office365 for user {0}" -f $User.Name)
try {
# shrink the original image, and size it down to 648x648 pixels
$pictureFile = Shrink-Image -ImagePath $ImageFile -TargetSize 648
[byte]$pictureData = [System.IO.File]::ReadAllBytes($pictureFile)
$User | Set-UserPhoto -PictureData $pictureData -Confirm:$false -ErrorAction Stop
}
catch {
Write-Error "Set-O365UserPicture: $($_.Exception.Message)"
}
finally {
if ($pictureFile -ne $ImageFile) {
# the original image was larger than 648x648, so we created a temp file. Remove that.
Remove-Item $pictureFile -Force -ErrorAction SilentlyContinue
}
}
}
}
You call it using an Identity like Set-O365UserPicture -Identity $SamAccountName -ImageFile $UserImagePath
or by using the user object you have obtained earlier using the Get-ADUser
cmdlet like $userObject | Set-O365UserPicture -ImageFile $UserImagePath
For both functions Set-ADUserPicture
and Set-O365UserPicture
it is advisable to also add the -Verbose
switch.
I know for definite that the maximum size for the ADthumbnailphoto
is not resolution based, it's size only with a maximum of 100kB. It might be scaled down by client programs, but in my experience Outlook, Skype4Business and every other program supports arbitrary image data. The same actually goes for Office 365, just the limit there is something like 4MB instead.
– Dave Green
Nov 14 '18 at 17:36
In my situation, I'm actually working solely with bytestream and base64 strings, passing data from one system to another with no disk writes in between for sync. I know they are trivially alterable, but this doesn't really fit as a solution to my question.
– Dave Green
Nov 14 '18 at 17:39
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53266417%2fresizing-a-photo-to-a-format-compatible-with-active-directory%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
For an AD thumbnailPhoto
the maximum size is 96x96 pixels, so I usually use functions like this to make the original image fit that little square.
Instead of using a function that requires you to give a percentage, it is easier to simply use these max. dimensions.
This is the helper function that resizes the origimal image:
function Shrink-Image {
# helper function to resize an image so it fits inside the given "TargetSize"
# It returns the path and filename of the resized image or the path to the original image if
# that happened to fit inside the "TargetSize" square.
[CmdletBinding()]
param (
[Parameter(Mandatory=$true, Position=0)]
[Alias("FileName")]
[ValidateScript({Test-Path $_ -PathType Leaf})]
[string]$ImagePath,
[Parameter(Mandatory=$true, Position=1)]
[int]$TargetSize,
[int]$Quality = 90
)
Add-Type -AssemblyName "System.Drawing"
$img = [System.Drawing.Image]::FromFile($ImagePath)
# if the original image fits inside the target size, we're done
if ($img.Width -le $TargetSize -and $img.Height -le $TargetSize) {
$img.Dispose()
return $ImagePath
}
# set the encoder 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)
# set the output codec to jpg
$Codec = [System.Drawing.Imaging.ImageCodecInfo]::GetImageEncoders() | Where-Object {$_.MimeType -eq 'image/jpeg'}
# calculate the image ratio
$ratioX = [double]($TargetSize / $img.Width)
$ratioY = [double]($TargetSize / $img.Height)
if ($ratioX -le $ratioY) { $ratio = $ratioX } else { $ratio = $ratioY }
$newWidth = [int]($img.Width * $ratio)
$newHeight = [int]($img.Height * $ratio)
$newImage = New-Object System.Drawing.Bitmap($newWidth, $newHeight)
$graph = [System.Drawing.Graphics]::FromImage($newImage)
$graph.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic
$graph.Clear([System.Drawing.Color]::White)
$graph.DrawImage($img, 0, 0, $newWidth, $newHeight)
# save the new image as temp file
$OutputPath = [System.IO.Path]::GetTempFileName()
# For safety: a [System.Drawing.Bitmap] does not have a "overwrite if exists" option for the Save() method
if (Test-Path $OutputPath) { Remove-Item $OutputPath -Force }
$newImage.Save($OutputPath, $Codec, $($encoderParams))
$graph.Dispose()
$newImage.Dispose()
$img.Dispose()
return $OutputPath
}
It resizes if needed to the given $TargetSize
and returns the full path and filename for this resized image.
(Note: the returned path CAN also be the same as the original image if that happens to fit inside the $TargetSize
square)
Next the function to actually set the picture in the AD thumbnailPhoto
property of a user:
function Set-ADUserPicture {
[CmdletBinding(DefaultParameterSetName = 'ByName')]
param (
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'ByName', Position = 0)]
[String]$Identity, # This can be: Distinguished Name, objectGUID, objectSid, sAMAccountName
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'ByObject', Position = 0)]
[Object]$User, # This is the user object from Get-ADUser
[Parameter(Mandatory = $true, Position = 1)]
[String]$ImageFile
)
if ($PSCmdlet.ParameterSetName -eq 'ByName') {
$User = Get-ADUser -Identity $Identity
}
if ($User) {
Write-Verbose ("Inserting userpicture in Active Directory for user {0}" -f $User.Name)
try {
# create a thumbnail using the original image, and size it down to 96x96 pixels
$pictureFile = Shrink-Image -ImagePath $ImageFile -TargetSize 96
[byte]$pictureData = [System.IO.File]::ReadAllBytes($pictureFile)
$User | Set-ADUser -Replace @{thumbnailPhoto = $pictureData } -ErrorAction Stop
}
catch {
Write-Error "Set-ADUserPicture: $($_.Exception.Message)"
}
finally {
if ($pictureFile -ne $ImageFile) {
# the original image was larger than 96x96 pixels, so we created a temp file. Remove that.
Remove-Item $pictureFile -Force -ErrorAction SilentlyContinue
}
}
}
}
You call it using an Identity like Set-ADUserPicture -Identity $SamAccountName -ImageFile $UserImagePath
or by using the user object you have obtained earlier using the Get-ADUser
cmdlet like $userObject | Set-ADUserPicture -ImageFile $UserImagePath
If you want to do something similar for the Office365 profile picture, then the max. dimensions are 648x648 pixels. For that you need to log in first of course using something like this:
$Cred = Get-Credential -UserName "admin@yourdomain.com" -Message "Please enter admin credentials for Office365"
$Url = "https://outlook.office365.com/powershell-liveid/?proxyMethod=RPS"
$Session = New-PSSession -ConfigurationName "Microsoft.Exchange" -ConnectionUri $Url -Credential $Cred -Authentication Basic -AllowRedirection
Import-PSSession $Session -DisableNameChecking -AllowClobber -WarningAction SilentlyContinue -ErrorAction SilentlyContinue | Out-Null
After that, the next function will set that image similar to the function for the AD thumbnailPhoto
:
function Set-O365UserPicture {
[CmdletBinding(DefaultParameterSetName = 'ByName')]
param (
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'ByName', Position = 0)]
[String]$Identity, # This can be: Distinguished Name, objectGUID, objectSid, sAMAccountName
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'ByObject', Position = 0)]
[Object]$User, # This is the user object from Get-ADUser
[Parameter(Mandatory = $true, Position = 1)]
[String]$ImageFile
)
if ($PSCmdlet.ParameterSetName -eq 'ByName') {
$User = Get-ADUser -Identity $Identity
}
if ($User) {
Write-Verbose ("Inserting userpicture in Office365 for user {0}" -f $User.Name)
try {
# shrink the original image, and size it down to 648x648 pixels
$pictureFile = Shrink-Image -ImagePath $ImageFile -TargetSize 648
[byte]$pictureData = [System.IO.File]::ReadAllBytes($pictureFile)
$User | Set-UserPhoto -PictureData $pictureData -Confirm:$false -ErrorAction Stop
}
catch {
Write-Error "Set-O365UserPicture: $($_.Exception.Message)"
}
finally {
if ($pictureFile -ne $ImageFile) {
# the original image was larger than 648x648, so we created a temp file. Remove that.
Remove-Item $pictureFile -Force -ErrorAction SilentlyContinue
}
}
}
}
You call it using an Identity like Set-O365UserPicture -Identity $SamAccountName -ImageFile $UserImagePath
or by using the user object you have obtained earlier using the Get-ADUser
cmdlet like $userObject | Set-O365UserPicture -ImageFile $UserImagePath
For both functions Set-ADUserPicture
and Set-O365UserPicture
it is advisable to also add the -Verbose
switch.
I know for definite that the maximum size for the ADthumbnailphoto
is not resolution based, it's size only with a maximum of 100kB. It might be scaled down by client programs, but in my experience Outlook, Skype4Business and every other program supports arbitrary image data. The same actually goes for Office 365, just the limit there is something like 4MB instead.
– Dave Green
Nov 14 '18 at 17:36
In my situation, I'm actually working solely with bytestream and base64 strings, passing data from one system to another with no disk writes in between for sync. I know they are trivially alterable, but this doesn't really fit as a solution to my question.
– Dave Green
Nov 14 '18 at 17:39
add a comment |
For an AD thumbnailPhoto
the maximum size is 96x96 pixels, so I usually use functions like this to make the original image fit that little square.
Instead of using a function that requires you to give a percentage, it is easier to simply use these max. dimensions.
This is the helper function that resizes the origimal image:
function Shrink-Image {
# helper function to resize an image so it fits inside the given "TargetSize"
# It returns the path and filename of the resized image or the path to the original image if
# that happened to fit inside the "TargetSize" square.
[CmdletBinding()]
param (
[Parameter(Mandatory=$true, Position=0)]
[Alias("FileName")]
[ValidateScript({Test-Path $_ -PathType Leaf})]
[string]$ImagePath,
[Parameter(Mandatory=$true, Position=1)]
[int]$TargetSize,
[int]$Quality = 90
)
Add-Type -AssemblyName "System.Drawing"
$img = [System.Drawing.Image]::FromFile($ImagePath)
# if the original image fits inside the target size, we're done
if ($img.Width -le $TargetSize -and $img.Height -le $TargetSize) {
$img.Dispose()
return $ImagePath
}
# set the encoder 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)
# set the output codec to jpg
$Codec = [System.Drawing.Imaging.ImageCodecInfo]::GetImageEncoders() | Where-Object {$_.MimeType -eq 'image/jpeg'}
# calculate the image ratio
$ratioX = [double]($TargetSize / $img.Width)
$ratioY = [double]($TargetSize / $img.Height)
if ($ratioX -le $ratioY) { $ratio = $ratioX } else { $ratio = $ratioY }
$newWidth = [int]($img.Width * $ratio)
$newHeight = [int]($img.Height * $ratio)
$newImage = New-Object System.Drawing.Bitmap($newWidth, $newHeight)
$graph = [System.Drawing.Graphics]::FromImage($newImage)
$graph.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic
$graph.Clear([System.Drawing.Color]::White)
$graph.DrawImage($img, 0, 0, $newWidth, $newHeight)
# save the new image as temp file
$OutputPath = [System.IO.Path]::GetTempFileName()
# For safety: a [System.Drawing.Bitmap] does not have a "overwrite if exists" option for the Save() method
if (Test-Path $OutputPath) { Remove-Item $OutputPath -Force }
$newImage.Save($OutputPath, $Codec, $($encoderParams))
$graph.Dispose()
$newImage.Dispose()
$img.Dispose()
return $OutputPath
}
It resizes if needed to the given $TargetSize
and returns the full path and filename for this resized image.
(Note: the returned path CAN also be the same as the original image if that happens to fit inside the $TargetSize
square)
Next the function to actually set the picture in the AD thumbnailPhoto
property of a user:
function Set-ADUserPicture {
[CmdletBinding(DefaultParameterSetName = 'ByName')]
param (
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'ByName', Position = 0)]
[String]$Identity, # This can be: Distinguished Name, objectGUID, objectSid, sAMAccountName
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'ByObject', Position = 0)]
[Object]$User, # This is the user object from Get-ADUser
[Parameter(Mandatory = $true, Position = 1)]
[String]$ImageFile
)
if ($PSCmdlet.ParameterSetName -eq 'ByName') {
$User = Get-ADUser -Identity $Identity
}
if ($User) {
Write-Verbose ("Inserting userpicture in Active Directory for user {0}" -f $User.Name)
try {
# create a thumbnail using the original image, and size it down to 96x96 pixels
$pictureFile = Shrink-Image -ImagePath $ImageFile -TargetSize 96
[byte]$pictureData = [System.IO.File]::ReadAllBytes($pictureFile)
$User | Set-ADUser -Replace @{thumbnailPhoto = $pictureData } -ErrorAction Stop
}
catch {
Write-Error "Set-ADUserPicture: $($_.Exception.Message)"
}
finally {
if ($pictureFile -ne $ImageFile) {
# the original image was larger than 96x96 pixels, so we created a temp file. Remove that.
Remove-Item $pictureFile -Force -ErrorAction SilentlyContinue
}
}
}
}
You call it using an Identity like Set-ADUserPicture -Identity $SamAccountName -ImageFile $UserImagePath
or by using the user object you have obtained earlier using the Get-ADUser
cmdlet like $userObject | Set-ADUserPicture -ImageFile $UserImagePath
If you want to do something similar for the Office365 profile picture, then the max. dimensions are 648x648 pixels. For that you need to log in first of course using something like this:
$Cred = Get-Credential -UserName "admin@yourdomain.com" -Message "Please enter admin credentials for Office365"
$Url = "https://outlook.office365.com/powershell-liveid/?proxyMethod=RPS"
$Session = New-PSSession -ConfigurationName "Microsoft.Exchange" -ConnectionUri $Url -Credential $Cred -Authentication Basic -AllowRedirection
Import-PSSession $Session -DisableNameChecking -AllowClobber -WarningAction SilentlyContinue -ErrorAction SilentlyContinue | Out-Null
After that, the next function will set that image similar to the function for the AD thumbnailPhoto
:
function Set-O365UserPicture {
[CmdletBinding(DefaultParameterSetName = 'ByName')]
param (
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'ByName', Position = 0)]
[String]$Identity, # This can be: Distinguished Name, objectGUID, objectSid, sAMAccountName
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'ByObject', Position = 0)]
[Object]$User, # This is the user object from Get-ADUser
[Parameter(Mandatory = $true, Position = 1)]
[String]$ImageFile
)
if ($PSCmdlet.ParameterSetName -eq 'ByName') {
$User = Get-ADUser -Identity $Identity
}
if ($User) {
Write-Verbose ("Inserting userpicture in Office365 for user {0}" -f $User.Name)
try {
# shrink the original image, and size it down to 648x648 pixels
$pictureFile = Shrink-Image -ImagePath $ImageFile -TargetSize 648
[byte]$pictureData = [System.IO.File]::ReadAllBytes($pictureFile)
$User | Set-UserPhoto -PictureData $pictureData -Confirm:$false -ErrorAction Stop
}
catch {
Write-Error "Set-O365UserPicture: $($_.Exception.Message)"
}
finally {
if ($pictureFile -ne $ImageFile) {
# the original image was larger than 648x648, so we created a temp file. Remove that.
Remove-Item $pictureFile -Force -ErrorAction SilentlyContinue
}
}
}
}
You call it using an Identity like Set-O365UserPicture -Identity $SamAccountName -ImageFile $UserImagePath
or by using the user object you have obtained earlier using the Get-ADUser
cmdlet like $userObject | Set-O365UserPicture -ImageFile $UserImagePath
For both functions Set-ADUserPicture
and Set-O365UserPicture
it is advisable to also add the -Verbose
switch.
I know for definite that the maximum size for the ADthumbnailphoto
is not resolution based, it's size only with a maximum of 100kB. It might be scaled down by client programs, but in my experience Outlook, Skype4Business and every other program supports arbitrary image data. The same actually goes for Office 365, just the limit there is something like 4MB instead.
– Dave Green
Nov 14 '18 at 17:36
In my situation, I'm actually working solely with bytestream and base64 strings, passing data from one system to another with no disk writes in between for sync. I know they are trivially alterable, but this doesn't really fit as a solution to my question.
– Dave Green
Nov 14 '18 at 17:39
add a comment |
For an AD thumbnailPhoto
the maximum size is 96x96 pixels, so I usually use functions like this to make the original image fit that little square.
Instead of using a function that requires you to give a percentage, it is easier to simply use these max. dimensions.
This is the helper function that resizes the origimal image:
function Shrink-Image {
# helper function to resize an image so it fits inside the given "TargetSize"
# It returns the path and filename of the resized image or the path to the original image if
# that happened to fit inside the "TargetSize" square.
[CmdletBinding()]
param (
[Parameter(Mandatory=$true, Position=0)]
[Alias("FileName")]
[ValidateScript({Test-Path $_ -PathType Leaf})]
[string]$ImagePath,
[Parameter(Mandatory=$true, Position=1)]
[int]$TargetSize,
[int]$Quality = 90
)
Add-Type -AssemblyName "System.Drawing"
$img = [System.Drawing.Image]::FromFile($ImagePath)
# if the original image fits inside the target size, we're done
if ($img.Width -le $TargetSize -and $img.Height -le $TargetSize) {
$img.Dispose()
return $ImagePath
}
# set the encoder 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)
# set the output codec to jpg
$Codec = [System.Drawing.Imaging.ImageCodecInfo]::GetImageEncoders() | Where-Object {$_.MimeType -eq 'image/jpeg'}
# calculate the image ratio
$ratioX = [double]($TargetSize / $img.Width)
$ratioY = [double]($TargetSize / $img.Height)
if ($ratioX -le $ratioY) { $ratio = $ratioX } else { $ratio = $ratioY }
$newWidth = [int]($img.Width * $ratio)
$newHeight = [int]($img.Height * $ratio)
$newImage = New-Object System.Drawing.Bitmap($newWidth, $newHeight)
$graph = [System.Drawing.Graphics]::FromImage($newImage)
$graph.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic
$graph.Clear([System.Drawing.Color]::White)
$graph.DrawImage($img, 0, 0, $newWidth, $newHeight)
# save the new image as temp file
$OutputPath = [System.IO.Path]::GetTempFileName()
# For safety: a [System.Drawing.Bitmap] does not have a "overwrite if exists" option for the Save() method
if (Test-Path $OutputPath) { Remove-Item $OutputPath -Force }
$newImage.Save($OutputPath, $Codec, $($encoderParams))
$graph.Dispose()
$newImage.Dispose()
$img.Dispose()
return $OutputPath
}
It resizes if needed to the given $TargetSize
and returns the full path and filename for this resized image.
(Note: the returned path CAN also be the same as the original image if that happens to fit inside the $TargetSize
square)
Next the function to actually set the picture in the AD thumbnailPhoto
property of a user:
function Set-ADUserPicture {
[CmdletBinding(DefaultParameterSetName = 'ByName')]
param (
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'ByName', Position = 0)]
[String]$Identity, # This can be: Distinguished Name, objectGUID, objectSid, sAMAccountName
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'ByObject', Position = 0)]
[Object]$User, # This is the user object from Get-ADUser
[Parameter(Mandatory = $true, Position = 1)]
[String]$ImageFile
)
if ($PSCmdlet.ParameterSetName -eq 'ByName') {
$User = Get-ADUser -Identity $Identity
}
if ($User) {
Write-Verbose ("Inserting userpicture in Active Directory for user {0}" -f $User.Name)
try {
# create a thumbnail using the original image, and size it down to 96x96 pixels
$pictureFile = Shrink-Image -ImagePath $ImageFile -TargetSize 96
[byte]$pictureData = [System.IO.File]::ReadAllBytes($pictureFile)
$User | Set-ADUser -Replace @{thumbnailPhoto = $pictureData } -ErrorAction Stop
}
catch {
Write-Error "Set-ADUserPicture: $($_.Exception.Message)"
}
finally {
if ($pictureFile -ne $ImageFile) {
# the original image was larger than 96x96 pixels, so we created a temp file. Remove that.
Remove-Item $pictureFile -Force -ErrorAction SilentlyContinue
}
}
}
}
You call it using an Identity like Set-ADUserPicture -Identity $SamAccountName -ImageFile $UserImagePath
or by using the user object you have obtained earlier using the Get-ADUser
cmdlet like $userObject | Set-ADUserPicture -ImageFile $UserImagePath
If you want to do something similar for the Office365 profile picture, then the max. dimensions are 648x648 pixels. For that you need to log in first of course using something like this:
$Cred = Get-Credential -UserName "admin@yourdomain.com" -Message "Please enter admin credentials for Office365"
$Url = "https://outlook.office365.com/powershell-liveid/?proxyMethod=RPS"
$Session = New-PSSession -ConfigurationName "Microsoft.Exchange" -ConnectionUri $Url -Credential $Cred -Authentication Basic -AllowRedirection
Import-PSSession $Session -DisableNameChecking -AllowClobber -WarningAction SilentlyContinue -ErrorAction SilentlyContinue | Out-Null
After that, the next function will set that image similar to the function for the AD thumbnailPhoto
:
function Set-O365UserPicture {
[CmdletBinding(DefaultParameterSetName = 'ByName')]
param (
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'ByName', Position = 0)]
[String]$Identity, # This can be: Distinguished Name, objectGUID, objectSid, sAMAccountName
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'ByObject', Position = 0)]
[Object]$User, # This is the user object from Get-ADUser
[Parameter(Mandatory = $true, Position = 1)]
[String]$ImageFile
)
if ($PSCmdlet.ParameterSetName -eq 'ByName') {
$User = Get-ADUser -Identity $Identity
}
if ($User) {
Write-Verbose ("Inserting userpicture in Office365 for user {0}" -f $User.Name)
try {
# shrink the original image, and size it down to 648x648 pixels
$pictureFile = Shrink-Image -ImagePath $ImageFile -TargetSize 648
[byte]$pictureData = [System.IO.File]::ReadAllBytes($pictureFile)
$User | Set-UserPhoto -PictureData $pictureData -Confirm:$false -ErrorAction Stop
}
catch {
Write-Error "Set-O365UserPicture: $($_.Exception.Message)"
}
finally {
if ($pictureFile -ne $ImageFile) {
# the original image was larger than 648x648, so we created a temp file. Remove that.
Remove-Item $pictureFile -Force -ErrorAction SilentlyContinue
}
}
}
}
You call it using an Identity like Set-O365UserPicture -Identity $SamAccountName -ImageFile $UserImagePath
or by using the user object you have obtained earlier using the Get-ADUser
cmdlet like $userObject | Set-O365UserPicture -ImageFile $UserImagePath
For both functions Set-ADUserPicture
and Set-O365UserPicture
it is advisable to also add the -Verbose
switch.
For an AD thumbnailPhoto
the maximum size is 96x96 pixels, so I usually use functions like this to make the original image fit that little square.
Instead of using a function that requires you to give a percentage, it is easier to simply use these max. dimensions.
This is the helper function that resizes the origimal image:
function Shrink-Image {
# helper function to resize an image so it fits inside the given "TargetSize"
# It returns the path and filename of the resized image or the path to the original image if
# that happened to fit inside the "TargetSize" square.
[CmdletBinding()]
param (
[Parameter(Mandatory=$true, Position=0)]
[Alias("FileName")]
[ValidateScript({Test-Path $_ -PathType Leaf})]
[string]$ImagePath,
[Parameter(Mandatory=$true, Position=1)]
[int]$TargetSize,
[int]$Quality = 90
)
Add-Type -AssemblyName "System.Drawing"
$img = [System.Drawing.Image]::FromFile($ImagePath)
# if the original image fits inside the target size, we're done
if ($img.Width -le $TargetSize -and $img.Height -le $TargetSize) {
$img.Dispose()
return $ImagePath
}
# set the encoder 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)
# set the output codec to jpg
$Codec = [System.Drawing.Imaging.ImageCodecInfo]::GetImageEncoders() | Where-Object {$_.MimeType -eq 'image/jpeg'}
# calculate the image ratio
$ratioX = [double]($TargetSize / $img.Width)
$ratioY = [double]($TargetSize / $img.Height)
if ($ratioX -le $ratioY) { $ratio = $ratioX } else { $ratio = $ratioY }
$newWidth = [int]($img.Width * $ratio)
$newHeight = [int]($img.Height * $ratio)
$newImage = New-Object System.Drawing.Bitmap($newWidth, $newHeight)
$graph = [System.Drawing.Graphics]::FromImage($newImage)
$graph.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic
$graph.Clear([System.Drawing.Color]::White)
$graph.DrawImage($img, 0, 0, $newWidth, $newHeight)
# save the new image as temp file
$OutputPath = [System.IO.Path]::GetTempFileName()
# For safety: a [System.Drawing.Bitmap] does not have a "overwrite if exists" option for the Save() method
if (Test-Path $OutputPath) { Remove-Item $OutputPath -Force }
$newImage.Save($OutputPath, $Codec, $($encoderParams))
$graph.Dispose()
$newImage.Dispose()
$img.Dispose()
return $OutputPath
}
It resizes if needed to the given $TargetSize
and returns the full path and filename for this resized image.
(Note: the returned path CAN also be the same as the original image if that happens to fit inside the $TargetSize
square)
Next the function to actually set the picture in the AD thumbnailPhoto
property of a user:
function Set-ADUserPicture {
[CmdletBinding(DefaultParameterSetName = 'ByName')]
param (
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'ByName', Position = 0)]
[String]$Identity, # This can be: Distinguished Name, objectGUID, objectSid, sAMAccountName
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'ByObject', Position = 0)]
[Object]$User, # This is the user object from Get-ADUser
[Parameter(Mandatory = $true, Position = 1)]
[String]$ImageFile
)
if ($PSCmdlet.ParameterSetName -eq 'ByName') {
$User = Get-ADUser -Identity $Identity
}
if ($User) {
Write-Verbose ("Inserting userpicture in Active Directory for user {0}" -f $User.Name)
try {
# create a thumbnail using the original image, and size it down to 96x96 pixels
$pictureFile = Shrink-Image -ImagePath $ImageFile -TargetSize 96
[byte]$pictureData = [System.IO.File]::ReadAllBytes($pictureFile)
$User | Set-ADUser -Replace @{thumbnailPhoto = $pictureData } -ErrorAction Stop
}
catch {
Write-Error "Set-ADUserPicture: $($_.Exception.Message)"
}
finally {
if ($pictureFile -ne $ImageFile) {
# the original image was larger than 96x96 pixels, so we created a temp file. Remove that.
Remove-Item $pictureFile -Force -ErrorAction SilentlyContinue
}
}
}
}
You call it using an Identity like Set-ADUserPicture -Identity $SamAccountName -ImageFile $UserImagePath
or by using the user object you have obtained earlier using the Get-ADUser
cmdlet like $userObject | Set-ADUserPicture -ImageFile $UserImagePath
If you want to do something similar for the Office365 profile picture, then the max. dimensions are 648x648 pixels. For that you need to log in first of course using something like this:
$Cred = Get-Credential -UserName "admin@yourdomain.com" -Message "Please enter admin credentials for Office365"
$Url = "https://outlook.office365.com/powershell-liveid/?proxyMethod=RPS"
$Session = New-PSSession -ConfigurationName "Microsoft.Exchange" -ConnectionUri $Url -Credential $Cred -Authentication Basic -AllowRedirection
Import-PSSession $Session -DisableNameChecking -AllowClobber -WarningAction SilentlyContinue -ErrorAction SilentlyContinue | Out-Null
After that, the next function will set that image similar to the function for the AD thumbnailPhoto
:
function Set-O365UserPicture {
[CmdletBinding(DefaultParameterSetName = 'ByName')]
param (
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'ByName', Position = 0)]
[String]$Identity, # This can be: Distinguished Name, objectGUID, objectSid, sAMAccountName
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'ByObject', Position = 0)]
[Object]$User, # This is the user object from Get-ADUser
[Parameter(Mandatory = $true, Position = 1)]
[String]$ImageFile
)
if ($PSCmdlet.ParameterSetName -eq 'ByName') {
$User = Get-ADUser -Identity $Identity
}
if ($User) {
Write-Verbose ("Inserting userpicture in Office365 for user {0}" -f $User.Name)
try {
# shrink the original image, and size it down to 648x648 pixels
$pictureFile = Shrink-Image -ImagePath $ImageFile -TargetSize 648
[byte]$pictureData = [System.IO.File]::ReadAllBytes($pictureFile)
$User | Set-UserPhoto -PictureData $pictureData -Confirm:$false -ErrorAction Stop
}
catch {
Write-Error "Set-O365UserPicture: $($_.Exception.Message)"
}
finally {
if ($pictureFile -ne $ImageFile) {
# the original image was larger than 648x648, so we created a temp file. Remove that.
Remove-Item $pictureFile -Force -ErrorAction SilentlyContinue
}
}
}
}
You call it using an Identity like Set-O365UserPicture -Identity $SamAccountName -ImageFile $UserImagePath
or by using the user object you have obtained earlier using the Get-ADUser
cmdlet like $userObject | Set-O365UserPicture -ImageFile $UserImagePath
For both functions Set-ADUserPicture
and Set-O365UserPicture
it is advisable to also add the -Verbose
switch.
answered Nov 13 '18 at 16:38
TheoTheo
4,1662520
4,1662520
I know for definite that the maximum size for the ADthumbnailphoto
is not resolution based, it's size only with a maximum of 100kB. It might be scaled down by client programs, but in my experience Outlook, Skype4Business and every other program supports arbitrary image data. The same actually goes for Office 365, just the limit there is something like 4MB instead.
– Dave Green
Nov 14 '18 at 17:36
In my situation, I'm actually working solely with bytestream and base64 strings, passing data from one system to another with no disk writes in between for sync. I know they are trivially alterable, but this doesn't really fit as a solution to my question.
– Dave Green
Nov 14 '18 at 17:39
add a comment |
I know for definite that the maximum size for the ADthumbnailphoto
is not resolution based, it's size only with a maximum of 100kB. It might be scaled down by client programs, but in my experience Outlook, Skype4Business and every other program supports arbitrary image data. The same actually goes for Office 365, just the limit there is something like 4MB instead.
– Dave Green
Nov 14 '18 at 17:36
In my situation, I'm actually working solely with bytestream and base64 strings, passing data from one system to another with no disk writes in between for sync. I know they are trivially alterable, but this doesn't really fit as a solution to my question.
– Dave Green
Nov 14 '18 at 17:39
I know for definite that the maximum size for the AD
thumbnailphoto
is not resolution based, it's size only with a maximum of 100kB. It might be scaled down by client programs, but in my experience Outlook, Skype4Business and every other program supports arbitrary image data. The same actually goes for Office 365, just the limit there is something like 4MB instead.– Dave Green
Nov 14 '18 at 17:36
I know for definite that the maximum size for the AD
thumbnailphoto
is not resolution based, it's size only with a maximum of 100kB. It might be scaled down by client programs, but in my experience Outlook, Skype4Business and every other program supports arbitrary image data. The same actually goes for Office 365, just the limit there is something like 4MB instead.– Dave Green
Nov 14 '18 at 17:36
In my situation, I'm actually working solely with bytestream and base64 strings, passing data from one system to another with no disk writes in between for sync. I know they are trivially alterable, but this doesn't really fit as a solution to my question.
– Dave Green
Nov 14 '18 at 17:39
In my situation, I'm actually working solely with bytestream and base64 strings, passing data from one system to another with no disk writes in between for sync. I know they are trivially alterable, but this doesn't really fit as a solution to my question.
– Dave Green
Nov 14 '18 at 17:39
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53266417%2fresizing-a-photo-to-a-format-compatible-with-active-directory%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Try changing the
Set-ADUser
argument:@{ thumbnailPhoto=$scaled }
->@{ thumbnailPhoto=,$scaled }
– Mathias R. Jessen
Nov 12 '18 at 16:41
This gives me an
Set-ADUser : Invalid type 'System.Object'.
error. I know the setting of the attribute is OK, since if I import an image without scaling it that part works, it's just the run through of the function that seems to change the encoding or the properties of the image in such a way that it's incompatible with AD (or the Set-Aduser function).– Dave Green
Nov 12 '18 at 17:12
Then it's most likely just because of a type check, explicitly cast to
[byte]
and you should be good:@{ thumbnailPhoto = [byte]$scaled }
– Mathias R. Jessen
Nov 12 '18 at 17:30
Thats crazy! it works in my test, i'll try it in my full test tomorrow and let you know. Do you know why that's needed to work even though i'm using [byte]$NewMemory.ToArray() to emit the object from the function? I can't see any difference in the object when piping both $scaled and [byte]$scaled to get-member.
– Dave Green
Nov 12 '18 at 19:05
This works over a wide range of images, thank you very much!
– Dave Green
Nov 13 '18 at 7:55