A Shadowy figure in a foggy background.

Copilot Studio and Transforming Untyped Objects.


Introduction

The Copilot Studio experiences with UntypedObjects is a bit painful at the moment.

This article helps to show an approach that I took to be able to work with them in Copilot Studio when building Copilot Agents.

Firstly, let’s explain what an UntypedObject is. It is effectively a dynamic object which Copilot Studio does not know what the structure of it is. The object gains a dynamicProperty property which should allow you to read properties but within Copilot Studio currently that seems to go wrong.

The Problem

If you are working with data that Copilot Studio cannot resolve the type of, then you will be working with an UnTypedObject.

An example that I had was with Dataverse where I using a Environment variable to state which environment to connect to. This meant that the data that was being retrieved could not be resolved at design time and so it returned back a record of UntypedObject as shown in the screenshot below.

A screenshot of a computer  AI-generated content may be incorrect.

I tried all sorts of different approaches to see if I could make use of the object but ultimately it was the dynamicProperties property that was causing problems as Copilot Studio seemed to want to use that to convert the data and of course my real object did not have that as a property.

In the end, I stumbled on a solution using the Parse JSON activity.

A Solution

I am hoping that there are nicer solutions coming from the Copilot Studio team but this is how I turned the UntypedObject into a Typed Object.

I tried a few different approaches, including using PowerFX Types to see if I could manage the conversion of Type. However, these ended up not working or hitting syntax error issues.

Examples of PowerFX functions

https://learn.microsoft.com/en-us/power-platform/power-fx/untyped-object

As I said previously, I then stumbled on the ParseJson activity.

Using the ParseJson Copilot Studio activity, along with a copy of the JSON string object, helped me get over the issues.

Here is what I did. First of all I outputted the whole table to a message using a Send Message activity, as shown below.

A screenshot of a computer  AI-generated content may be incorrect.

Then, I copied an example of the Json and used that with the ParseJson activity.

A screenshot of a computer  AI-generated content may be incorrect.

Here is the schema for the Parse JSON activity.

A screenshot of a computer  AI-generated content may be incorrect.

The output of the ParseJson gave me a variable with a known shape.

Now that I have a variable with a known type, we are golden and everything is working as you would expect.

The data can be used as normal with other activities, such as Power Automate.

A screenshot of a computer  AI-generated content may be incorrect.

Problem solved!

Conclusion

As we spend more time working on Copilot Studio solutions, I am sure these experiences will improve. Anyway, I hope you stumble on this, and it helps you out.

I spent a few hours trying different things and ultimately found this solution.

Thanks for reading.

Image which shows two books and has the title Solving ProfileBadRequestException Exceptions when updating Microsoft Graph User Profile Photo

Solving ProfileBadRequestException Exceptions when updating Microsoft Graph User Profile Photo


Introduction

I had a scenario where we needed to copy user profile photos from one Microsoft Entra ID tenant to another. One of the issues that companies face is that Microsoft’s Cross Tenant Sync does not update synchronise user profile photos.

Approach

So, I built a Microsoft Azure Function that uses the Microsoft Graph SDK for .NET to connect to the source Microsoft Entra ID tenant and the target Microsoft Entra ID tenant.
The Azure Function runs through all the users in the target Entra ID and copies over the profile photo from the source Entra ID.

It seems pretty straight-forward, but of course, there was a little gotcha. It seems that my approach was failing when trying to write the photo content to the user’s profile. The exception that I was getting was this:
Microsoft.Fast.Profile.Core.Exception.ProfileBadRequestException

This exception was thrown when the following call was made:

[csharp]
await targetGraphClient.Users[user.Id].Photo.Content.PutAsync(stream);
[/csharp]

After looking around the web, I realized I was not alone. Quite a few other people were complaining about this exception and looking for solutions. Therefore, I wanted to share my approach to resolving this issue.

The first thing was to make sure we were not trying to do anything too clever and keep everything Binary.

[csharp]
System.IO.Stream sourcePhotoContent = null;
byte[] sourcePhotoBytes = null;
System.IO.MemoryStream sourcePhotoMemoryStream = new MemoryStream();

try
{
sourcePhotoContent = await sourceGraphClient.Users[sourceUser.Id].Photo.Content.GetAsync();
await sourcePhotoContent.CopyToAsync(sourcePhotoMemoryStream);
sourcePhotoMemoryStream.Position = 0;
var streamReader = new BinaryReader(sourcePhotoMemoryStream);
sourcePhotoBytes = streamReader.ReadBytes((int)sourcePhotoMemoryStream.Length);
sourcePhotoMemoryStream.Position = 0;
await targetGraphClient.Users[user.Id].Photo.Content.PutAsync(new MemoryStream(sourcePhotoBytes));
}
catch (Exception sourceImageEx)
{
}
[/csharp]

To be honest the secret was to use the Http2Stream coming from the sourcePhotoContent and copying it to the sourcePhotoMemoryStream. After that, we needed to make sure we were setting the position of the MemoryStream to 0 so that when the BinaryReader runs through the stream it reads all of the bytes and puts them into the sourcePhotoBytes byte[].

Fundamentally that is it!

Oh one last thing, permissions! Mke sure you have the right Microsoft Graph Permissions assigned to the Microsoft Entra ID application that you are using. The permissions that I used were the following:

  • User.ReadBasic.All
  • ProfilePhoto.ReadWrite.All

Conclusion

This was a short post. I hope you found it useful if you are trying to do something similar using the Microsoft Graph SDK and hitting the same “Microsoft.Fast.Profile.Core.Exception.ProfileBadRequestException” exception.

If you used this, let us know you got on.