Synchronize your user profile pictures from AzureAD to CRM Online in powershell

Hello everyone,

Few days ago, I navigate through my CRM and I was like :
Why don't we have "standard" picture profile synchronisation ?!

It's indeed not available with the out of the box synchronisation between the Azure Active

If you manage to push your users to update their profile pictures in Office 365, you did a nice work. That would be too bad to not take advantage of this and push this picture directly to the user record inside the CRM.
The added value here is not that much, but let's say it's cool to find the same profile picture everywhere you go between your online Apps !

What do we want to achieve in this scenario? :

  1. Connect to CRM and AAD in powershell
  2. Retrieve my active CRM users to check the picture
  3. For each user, retrieve its profile picture from his Azure Active Directory profile
  4. Update the picture if necessary
  5. What can we do next ?

Here is a sum up of the actions done by the script :

Pre-requisites :

Disclaimer : This is a sample/draft script.

Let's do it now !

1. Connect to AAD & CRM

#Add-Crm-Sdk;
Import-Module -Name ".\Microsoft.Xrm.Data.Powershell"

# Preparing the needed informations to connect to AAD & CRM
$aadUser = "yourname@domain.com"
$aadPassword = ConvertTo-SecureString -String "YourStrongestPassword" -AsPlainText -Force
$crmUser = "yourname@domain.com"
$crmPassword = ConvertTo-SecureString -String "YourStrongestPassword" -AsPlainText -Force
$crmUrl = "https://tenant.crm4.dynamics.com"

# creating a connection to your AAD
$CredentialAAD = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $aadUser, $aadPassword

try {  
    $connAAD = Connect-AzureAD -Credential $CredentialAAD
}
catch {  
    write-host "Caught an exception during connection to AAD:" -ForegroundColor Red
    write-host "Exception Message: $($_.Exception.Message)" -ForegroundColor Red
    exit 1
}
# creating a connection your your CRM instance
$CredentialCRM = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList crmUser, $crmPassword
$conn = Connect-CrmOnline -Credential $CredentialCRM -ServerUrl $crmUrl

# we make sure that the CRM connection was performed properly
if($conn.IsReady -ne $true){  
    write-host "Caught an issue during connection to CRM instnace:" -ForegroundColor Red
    Write-Host $conn.LastCrmError -ForegroundColor Red
    exit 1
}

A bit of explanations :

  • We import the Microsoft.Xrm.Data.Powershell module as said in prerequisites, this will allow us to query and perform actions over the CRM.
  • We define the user accounts which will be used to create a connection to AAD & CRM (it's the same account in my case for example, but based on the permissions control in your organization, it can be different)

2. Retrieve Active CRM Users

Now we want to get our users from the CRM, active ones, since we can't update disabled one.
So we will do that using our previous downloaded module "Microsoft.Xrm.Data.Powershell"
As said, we want to focus on the users who are currently Licensed (via the office365 portal) and are not disabled.

#Getting all users and who are not disabled
$CrmUsers = (Get-CrmRecords -conn $conn -EntityLogicalName systemuser -FilterAttribute isdisabled -FilterOperator eq -FieldValue false -Fields domainname,entityimage,islicensed).CrmRecords
# Now from the result, we keep only the licensed users (to avoid getting the crmoln@microsoft.com users for example)
$CrmUsers = $CrmUsers | ? { $_.islicensed -eq "Yes" }

Write-Host $CrmUsers.Count" users retrieved."  

3. Retrieve Picture profile of each user

In order to get access to the profile picture, we need to perform 2 actions :

  • Retrieve the user from the Azure AD
  • If the user is found, then retrieve the picture
# retrieving the user from the AAD
$userAAD = Get-AzureADUser -ObjectId $CrmUser.domainname
Write-Host "   -> AAD profile retrieved."

#Making sure the user was found
if($userAAD -ne $null)  
{
    #getting the picture and downloading it locally (by default it will be the GUID.jpg filename)
    Get-AzureADUserThumbnailPhoto -ObjectId $userAAD.ObjectId -FilePath (Get-Location).Path
    # we get the exact filename in case the file doesn't have a jpg extension
    $picName = (Get-ChildItem -Path (Get-Location).Path | ? { $_.Name -like ''+$userAAD.ObjectId+'*' }).Name # to handle any kind of extension
    # storing the full path to picture
    $picPath = (Get-Location).Path+"\"+$picName
    Write-Host "   -> AAD picture profile retrieved. (path : " $picPath")"

    # step 4 of the process will goes here
}

In this part of our script, as you can see in the comments, we perform :

  • Retrieve the user from the Azure Active Directory using the DomainName
  • If we have a result from that user, we try getting the picture
  • and then keeping the picture path in a variable to use it later

4. Update the picture in CRM

if((Test-Path $picPath))  
{
    # getting the picture content in Bytes
    $pictureInBytes = [System.IO.File]::ReadAllBytes($picPath)

    # setting the image in bytes to systemuser record 
    Set-CrmRecord -conn $conn -EntityLogicalName "systemuser" -Id $CrmUser.systemuserid -Fields @{"entityimage"=$pictureInBytes}

    Write-Host "   -> profile picture updated."

    # cleaning the user picture from your drive
    Remove-Item -Path $picPath
}

Now that we have all necessary data to actually perform the picture update on the CRM side, we will do it !
The systemuser entity has a field entityimage (field name is similar to other entities which support picture too), this is a field which accept bytes data.

5. What can we do next ?

When you are done "configuring the accounts in the script, there are a lot of possibilities to use it, few quick ideas here :

  • Set it up as a schedule task (every day for example) so you make sure that the user's picture are always up to date.
  • Improve the way to retrieve the picture : using GraphApi and BearerToken as example
  • Improve the error handling : in this version if a user is not found in the AAD, an exception is thrown which is not that "end user" friendly.
  • Integrate it in some automation execution in TFS/VSTS

Bonus : démo !

On left side, this is the CRM
On right side, it's my office365 profile.

Limitations

The script works fine but as mentioned above, it needs an account which have enough permissions to connect to the AAD and retrieve profile pictures of other users as well as an account which need enough permissions (read/write all users on the CRM side) to perform the update action.

The error handling is not that perfect so far, as if you enter the wrong user/password for the connection to the AAD, it will throw an exception even if you encapsulate the connection in a try/catch.

Conclusion

As we've seen, this script is pretty simple in term of actions and added value to the CRM.
But i thought, it was really nice regarding the look and feel of the CRM instance having your picture in the top right corner.
Besides that, it reminds us that we can do a lot with Powershell and CRM to add some extra automation / stuff inside your instance.

Hope this can be useful to some of you !
Happy CRM'in !

PS : here is the full script

Clement

Dynamics 365 CRM & Power Platform addict.