Knowledge Base

Preserving for the future: Shell scripts, AoC, and more

Upload user photo to AD

I wrote a series of Powershell functions and scripts to perform a set of tasks. The new hires get their photo taken, and those photos need to be applied to the various systems that store user photos. More will be added over time, but now, I have functions that upload to Active Directory, Azure AD, Exchange, and a SQL Server database. As you might be aware, most of those systems only accept images up to a certain size. So I ripped off a function that resizes an image. I wrote a wrapper to shrink an image a little at a time until its size is smaller than the indicated value. Check out the whole repository at https://gitlab.com/bgstack15/former- gists/tree/master/userphotos. The basic task is to upload an image to Active Directory. A workflow in a Sharepoint instance handles the image uploads from users and the approval by a security officer, but then a script downloads all the photos Apply-Photos-From- SharePoint.ps1. The images are named according to the email address of the user already, so they're just downloaded wholesale. Once the files are on the local filesystem, that script loops through them and uploads them.

ForEach ($file in Get-ChildItem $Outdir | Where-Object { -Not ( $_.Name -Match ".*(_[0-9]{2}|[0-9]{6})\.[^.]{1,6}" ) } )
{
    $null = Process-User-Photo -File $file
}

In the library, the function Process-User-Photo extracts the user in question based on the name and calls the wrapper function for uploading to all services.

        $Result = Set-Photo-All -User $samaccountname -Filename $file.Fullname
        if ($Result -eq 0)
        {
            Log "Success: ${file}"
            Move-With-Rename -Path $file.Fullname -Destination "$MoveToOutdir\"
        }

And Set-Photo-All is pretty simple. It will return an integer which represents the number of failures of the individual set-photo functions.

# Set-Photo-All -User $User -Filename $Filename
Function Set-Photo-All {
    Param(
        [Parameter(Mandatory=$true)] $User,
        [Parameter(Mandatory=$true)] $Filename
    )
    $Result = 0
    $Result -= Set-Photo-AD -User $User -Filename $Filename
    # AzureAD synchronizes from on-prem AD approximately every 15 minutes.
    #$Result -= Set-Photo-AzureAD -User $User -Filename $Filename
    $Result -= Set-Photo-Outlook -User $User -Filename $Filename
    $Result -= Set-Photo-SQLServer -User $User -Filename $Filename
    return $Result
}

Uploading to AD is actually really easy.

# Set-Photo-AD -User $User -Filename $Filename
Function Set-Photo-AD {
    Param(
        [Parameter(Mandatory=$true)] $User,
        [Parameter(Mandatory=$true)] $Filename
    )
    $Result = -1
    $newfile = Get-Photo-Below-Size -Filename $filename -MaxSize 100000
    $photo = [byte[]](Get-Content $newfile -Encoding byte)
    Try
    {
        Get-ADUser $User | Set-ADUser -Replace @{thumbnailPhoto=$photo}
        If ($newfile -ne $Filename) { Remove-Item -Path $newfile }
        Log "Set AD photo for ${User} to ${newfile}"
        $Result = 0
    }
    Catch
    {
        Write-Error $_
    }
    Remove-Variable newfile
    return $Result
}

Of course there's a getter function as well, for each service.

# Get-Photo-AD -User $user -Filename $filename
Function Get-Photo-AD {
    Param (
        [Parameter(Mandatory=$True)] $user,
        [Parameter(Mandatory=$True)] $filename
    )
    # save photo down from AD to inspect it
    # https://devblogs.microsoft.com/scripting/weekend-scripter-exporting-and-importing-photos-in-active-directory/
    $user = get-aduser $user -properties thumbnailphoto
    [System.Io.File]::WriteAllBytes($filename,$user.thumbnailphoto)
}

So the whole library is modular. You can dot-source the whole thing and run just individual statements ad-hoc, or use the library for a wholistic solution which is why I needed it.

Comments