After a long and lazy summer, it’s time to catch up on some of the updates Microsoft released over the last few weeks. One such update is the new photoUpdateSettings resource in the Graph API, which as the name suggests allows you to control some settings related to profile photos. Let’s dig right in.
If you ever had to deal with setting profile photos across the various Microsoft 365 workloads, you’re probably aware of how disjointed the experience was and the many moving parts involved. Microsoft finally decided to do something about this last year, by launching a Graph-centric (or Entra ID centric) approach to storing and managing profile photos, and soon after, by deprecating the existing Exchange Online cmdlets related to managing photos. Clients and admin tools should now all use the Graph API methods.
One piece that was missing was the “policy” part of it, i.e. being able to configure some controls as to who and how can make updates to photos. In the past, we were able to exert control via OWA mailbox policies, but the Exchange Online dependency did pose some challenges and support across all Microsoft 365 workloads never materialized. Now, with the unified Graph-based approach things should hopefully work better. Or do they?
To start with, lets examine what the available controls within the photoUpdateSettings resource are. As detailed in the official documentation, we have two properties we can work with. The source property defines whether photos can be updated via onPremises synchronization or directly in the cloud. The allowedRoles control defines which Entra ID admin roles are able to perform photo updates. This is an optional property, and by default has a null (empty string) value.
By default, an “empty” photoUpdateSettings configuration exists in all tenants, as illustrated on the screenshot below. To query the current configuration, one can use a GET request against the /beta/admin/people/photoupdatesettings endpoint, the needed permissions being PeopleSettings.Read.All. Contrary to what the documentation tells you, there is no id property in the output.
Thus, in the default configuration, updates to profile photos are allowed and can be performed by both end users and admins alike. Here is probably a good place to add few words on how updates can actually be made. Across the Microsoft 365 suite, we are left with only a handful of places where photos can be changed. End users can only do this via their Delve profile page and the Teams clients. All other clients redirect to those locations, or in the case of classic Outlook, take you to OWA, where the corresponding bits were removed.
Admins on the other hand can update photos via variety of methods ranging from the admin consoles, to PowerShell cmdlets and Graph API methods. Of course, as most other admin tasks, the relevant permissions must be granted to the admin user in order for them to be able to make changes on behalf of others. In particular, the microsoft.directory/users/photo/update permission must be granted. According to the official documentation, only 5 admin roles feature said permission:
- Directory writers
- Intune administrator
- Partner Tier1 Support
- Partner Tier2 Support
- User administrator
And of course we also have the all-encompassing microsoft.directory/users/allProperties/allTasks permission, included in the Global administrator role. While the documentation does not explicitly list which (of the above) roles can be configured as part of the allowedRoles control, some testing reveals that only two are actually supported: User administrator (with id of fe930be7-5e62-47db-91af-98c3a49a38b1) and Global administrator (with id of 62e90394-69f5-4237-9190-012177145e10).
With that information at hand, let’s examine how we can update the photoUpdateSettings control. To do so, one must issue a PATCH request against the /beta/admin/people/photoupdatesettings endpoint. The JSON payload should contain the source property, and optionally the list of allowedRoles. If you omit the latter, every user within the organization will be able to change their photo, once the changes have been replicated (which can take up to 24 hours). The permissions required for making changes to the photoUpdateSettings resource are PeopleSettings.ReadWrite.All. Here is a sample query:
PATCH https://graph.microsoft.com/beta/admin/people/photoupdatesettings { "source": "cloud", "allowedRoles": [] }
The above configuration defines that photo updates are allowed for cloud-only scenarios, and imposes no restrictions as to which users can make changes. If you want to exert some control in the process, configure the allowedRoles property to list the set of admin roles you’d want to be able to change photos. As mentioned above, out of the five built-in roles that have the necessary permission for updating photos, only the User administrator one is supported. In addition, you can also use Global administrator role.
Do note that there is no notion of “seniority” here – even though the Global administrator role is much more powerful than the User administrator one, if you do not specify the former in the set of allowedRoles, even users holding the Global admin role will NOT be able to make changes! Also, the allowedRoles property only plays a role when you configure cloud settings, if you are configuring the hybrid scenario (i.e. source=onPremises), you should pass an empty list value for allowedRoles.
PATCH https://graph.microsoft.com/beta/admin/people/photoupdatesettings { "source": "cloud", "allowedRoles": [ "fe930be7-5e62-47db-91af-98c3a49a38b1" ] }
The example above sets the User administrator role as the only one allowed to make changes. Again, doing so will block even your Global administrators from making changes to profile photos, which is something you should be aware of. Not that you should be leveraging the most powerful accounts for such operations to begin with.
Another thing to note here is that the default “user” role (with id of b79fbf4d-3ef9-4689-8143-76b194e85509) is not allowed. If you want to permit changes from end users, you should leave the allowedRoles property empty. In addition, AU-scoped roles do not seem to be supported currently, and neither are any custom roles (which is expected, given we cannot currently create custom roles with the microsoft.directory/users/photo/update permission).
The other side of the coin is how controls are enforced on the client side. As mentioned above, for end users the only clients that can be used to change the profile photo are Delve and Teams. Both will happily allow you to change the photo if no allowedRoles are defined, as is the default configuration. Once you restrict the list of roles that can make changes, clients will throw an error upon any attempt to update the photo (see screenshot below). Similar behavior can be observed in the admin consoles as well.
Unlike the good old OWA Mailbox policy based controls however (which are still supported by both Delve and Teams), you will only get an error after pressing the corresponding button to apply the changes. This experience is a bit rough, but will be addressed as the various teams within Microsoft adopt the new controls. In contrast, restrictions configured via the OWA Mailbox policy disable the relevant buttons to begin with, so the experience is much better.
Both Delve and Teams “proxy” the photo update request through their internal “wrappers”, thus if you want to take a look at the actual error response from the backend, expect to see something like MsgraphSetProfilePictureCallFailed or External endpoint returned ‘Forbidden’. To get the actual Graph API response, you can use Graph SDK for PowerShell or make direct request, in which case you will receive something resembling the following:
Status Code: Forbidden Body: { "error": { "code": "ForbiddenByAdmindEditPolicy", "message": "Exception of type 'Microsoft.Fast.Profile.Core.Exception.ImagePolicyViolationException' was thrown.", "innerError": { "date": "2024-09-05T11:17:08", "request-id": "794be237-6e67-4b7a-a12f-e23882db913b", "client-request-id": "0f78e0f9-98be-44bd-b84c-5da616aa1f99" } } }
Do note that end users can also make changes to their profile photos via PowerShell direct Graph requests, provided they are allowed access to such tools, and you are not blocking consent requests for the User.ReadWrite permission. Similarly, if you are using an admin account to make changes to user’s photo outside of the admin consoles, you will need an application with the ProfilePhoto.ReadWrite.All or User.ReadWrite.All permission, regardless of what you have configured for the photoUpdateSettings control.
In summary, we discussed the newly introduced photoUpdateSettings resource, which allows you to restrict profile photo updates in your tenant. Configuring photoUpdateSettings is fairly straightforward and allows you to exert control over who can update profile photos in the organization. Unfortunately though, the resource is another example of a Graph API functionality that does NOT support application permissions, which poses some challenges for automation scenarios.
In addition, better granularity support will likely be appreciated. With the previous OWA mailbox policy based solution, we were able to control things on a per-user level, thus enabling a subset of the users to change their own photos, but preventing the rest from doing so. Similarly, on the admin side we could granularly delegate permissions to only users within a scope. In contrast, the photoUpdateSettings controls are “all or nothing” type of solution, with no support for granular delegation or scoped RBAC roles.
Do keep in mind that configuring these controls has zero effect on any existing profile photos. This behavior is no different from what we already had, so there should be no surprises here, and if needed you can use the available methods to remove any existing photos. Lastly, keep in mind that changes to this control might take up to 24h to take effect, so some patience is advised. Oh, and remember that the OWA Mailbox policy still has effect on clients and might potentially cause conflict with the photoUpdateSettings controls.
P.S. I know someone will ask this, so here is an example on how to update your profile photo as an end user by leveraging the Graph SDK for PowerShell. Note that while the User.ReadWrite permission (needed for the photo update operation) does not require admin consent, your organization might be blocking end user consent requests, thus preventing you from doing this. Anyway, here’s the corresponding cmdlet:
#Connect to the Graph Connect-MgGraph -Scopes User.ReadWrite #Update your own profile photo (make sure to provide *your* UPN) Set-MgUserPhotoContent -UserId user@domain.com -InFile "C:\Photos\headshot.jpg"