Ress
Ress

Reputation: 77

Updating users' Microsoft profile pictures with PowerShell

Our company is migrating user info to our Microsoft tenant. Part of this info includes the profile picture.
Most of the information I am able to update using Set-MSolUser, but for the profile picture I've been trying to use Microsoft Graph, with no success.
I am a tenant admin and yet I've had no success in updating users' profile pictures. Here's my code:

$token = [my-token]

$Headers = @{
    "Authorization" = "Bearer $token"
    "Content-Type" = "image/jpeg"
}

Invoke-RestMethod -Uri 'https://graph.microsoft.com/v1.0/users/{user-id}/photo/$value' -Method Put -Headers $Headers

This has just been for test purposes so I'm trying with a single user id and no picture. This has been the output:

  "error": {
    "code": "ErrorAccessDenied",
    "message": "AccessDeniedException",

And the same thing happens when querying directly through the graph website:

error-message-ms-graph

Per the documentation, certain permissions are necessary (contact, group or user read.write) which I have ticked all on the Graph website, but still nothing.

Any ideas would be greatly appreciated.

Upvotes: 1

Views: 1999

Answers (2)

filimonic
filimonic

Reputation: 4634

Under user permissions you able to edit only your own picture. You have to create an Azure AD application with User.ReadWrite.*All* permission to edit others' pictures.

Go to: portal.azure.com -> Active Directory -> App Registration -> New Registration. Then under API permissions you grant User.ReadWrite.All and click grant admin consent. Then under Certificates and Secrets you create an app secret.

$AzAppSecret = 'abcDEFghiJKLmnoPQRstuVWXyz01234567'   # from AzureAD -> App Registrations -> YourApp -> Certificates & secrets
$AzAppId     = 'AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA' # from AzureAD -> App Registrations -> YourApp -> Overview -> Application (client) ID
$AzTenantId  = 'BBBBBBBB-BBBB-BBBB-BBBB-BBBBBBBBBBBB' # from AzureAD -> App Registrations -> YourApp -> Overview -> Directory (tenant) ID
$AzUserUPN   = '[email protected]'                 # from AzureAD -> Users -> YourUser -> User Principal Name
$AzUserImage = 'S:\samplepic.jpg'                     # Jpeg file


# Request token
$tokenRequestBody = @{
        Grant_Type    = "client_credentials"
        Scope         = "https://graph.microsoft.com/.default"
        Client_Id     = $AzAppID
        Client_Secret = $AzAppSecret
    }
$tokenRequestUri = [String]::Format('https://login.microsoftonline.com/{0}/oauth2/v2.0/token', $AzTenantId)
$tokenResponse = Invoke-RestMethod -Uri $tokenRequestUri -Method 'POST' -Body $tokenRequestBody -ErrorAction Stop
$accessToken = $tokenResponse.access_token

$uri = [String]::Format('https://graph.microsoft.com/v1.0/users/{0}/photo/$value', $AzUserUPN)
$Headers = @{
    'Authorization' = [String]::Format('Bearer {0}', $accessToken)
    'Content-Type'  = 'image/jpeg'
}

Invoke-RestMethod -Method Put -Uri $uri -InFile $AzUserImage -Headers $Headers 

Alternative way:
# Load Assembly System.Net.Http
# In PS7 built-in, on PS5 - Download NuGet package from https://www.nuget.org/packages/System.Net.Http/ and unzip using 7zip
[void][System.Reflection.Assembly]::LoadFile('P:\path-to\system.net.http.4.3.4\runtimes\win\lib\netstandard1.3\System.Net.Http.dll')

# Prepare httpClient and URI
$httpClient = [System.Net.Http.HttpClient]::new()
$httpClient.DefaultRequestHeaders.Authorization = [String]::Format('Bearer {0}', $accessToken)

# Prepare Content
$content = [System.Net.Http.ByteArrayContent]::new([System.IO.File]::ReadAllBytes($AzUserImage));
$content.Headers.ContentType = "image/jpeg";

# Run
$task = $httpClient.PutAsync($uri,$content)
$task.Wait()
$task.Result.IsSuccessStatusCode

Remember to keep Application secret private

Upvotes: 2

Vadim Gremyachev
Vadim Gremyachev

Reputation: 59318

This is expected error in case if

  • for app using application permissions, User.ReadWrite.All permission is missing when calling this Microsoft Graph endpoint
  • token is acquired with delegated permissions

Documentation says the following in this regard:

update the photo of any user in the organization, your app must have the User.ReadWrite.All application permission and call this API under its own identity, not on behalf of a user

And last but not least, profile image is expected to be passed via request body, in case of PowerShell the request could be constructed like this:

$Headers = @{
    "Authorization" = "Bearer $access_token"
    "Content-Type" = "image/jpeg"
}

$url = "https://graph.microsoft.com/v1.0/users/$($userId)/photo/$value"
$profilePath = "--path to profile file--" 

Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/users/$($userId)/photo/$value" -Method Put -InFile $profilePath -Headers $Headers

Upvotes: 1

Related Questions