Thursday, September 21, 2017

Dynamics 365 Admin API quirks

Today I finally solved an issue I've been struggling with for 51 days now.
On August 10th Microsoft launched a new API for Microsoft Dynamics 365 Customer Engagement which allows you to perform administrative tasks (finally) for your online environment.
So like to edge kind of guy I am I set forth and tested it out which led to my blog series on the topic (part1, part2, and part3).
The most exciting feature for me was the ability to back up instances to azure storage. Unfortunately, all my attempts to use this function failed with an error message of 500: Internal server error.
I opened up a ticket with Microsoft, and they confirmed my findings and proceeded to escalate it internally.

Now I'm not someone to let something like this just lie, so I've spent quite some time digging into what's wrong with the API. That's when I came across a blog post by Rami Mounla which said that he was using it without issues.
I found that strange, because I was using the example code provided by Microsoft (with some tweaks), so I decided to test it out with the example code straight out of the box

No dice, as you can see I receive a big, fat error 500 in return.
So I tried the next logical step, I excluded some of the values too see what happened if I didn't include the required fields.

Now I got an error 400, stating that I was missing the InstanceId.
Great stuff, now we just need to go by way of deduction. So I started from scratch, including only the required fields. At least I would be able to do a normal MSDYN365 backup then?
No, even with only the required fields it still gave me an error 500.
So I updated my ticket with Microsoft, went on a social media rampage and managed to escalate it to the product group. The product group confirmed the error, but I think they might have other priorities than just my ticket. Luckily, the most awesome Swedish person in the world, Jonas Rapp, used his wide range of connections to get me in touch with the not-less amazing George Doubinsky.
It seems George is getting it to work as well, and he agreed to help me with some debugging.

After hours of debugging and trying all sorts of stuff (remove all headers, try xml, try building the code from scratch, try creating a new Azure AD App, the list goes on), we finally tried the following.
I created a new trial environment, and we both used our own code.
Worked for George, not for me. Obviously I'm doing something wrong?
So George packed together a postman file for me with the authorization header included. Surely that should work on my computer?
No, even with the exact same request I got an error 500, and George got an OK 202.

That was really, really strange to me, so I tried to figure out what was wrong with my machine. I did IE cleanup, reboot, flushed all kerberos tickets. Still nothing. Tried to replay the request in fiddler: still no luck.
Finally, I created a new user on my computer, and installed postman for chrome instead of the desktop client.
BOOM! OK 202. Checked my azure subscription, and right enough there was my new, shining backup.

So I took a screenshot of the entire request and response, and brought it back to my other machine.
Turns out, the Postman extension for Chrome automatically adds the Accept-Language header to the request, while the desktop client does not.
That means that the example provided by Microsoft is the one to blame (thankfully, because I've spent A LOT of time on this.
To make it work, simply add this one little beautiful line into the OAuthMessageHandler class in the authenticationhelper:
request.Headers.AcceptLanguage.Add(new StringWithQualityHeaderValue("en-US"));

So there you have it. For some reason the impementation requirements are not the same for all of the methods in the admin API. On to the next issue!