Introduction
There may come a time when the developer or admin must deal with Azure AD and all its quirks. I for instance don't get very excited at the prospect of having to go through Microsoft's documentation or interact with Azure AD questionable interface, whose user experience is less then optimal, for want of a better word.
This guide is for my future self and others too lazy or lacking the necessary inner calmness to actually use Azure AD the way it is intended.
Scope
This tutorial will guide you through extracting the necessary requests from the Azure AD UI and manipulating data so you can automate tasks. To illustrate this, we will migrate users from an OLD APP
to a NEW APP
, both of which are Enterprise Applications (although this would also work for App Registrations accessed through the Enterprise Applications tabs -- kudos for this excellent UX choice...).
Pre-Requisites
A Browser with Dev Tools (I personally use Firefox);
An Azure Active Directory subscription;
An API Client (I use Insomnia, but the same can be achieved with Postman).
Exploring the problem
Let's say you have two applications in you active directory:
![](https://paragraph.xyz/_next/image?url=https%3A%2F%2Fstorage.googleapis.com%2Fpapyrus_images%2F0f334fb34bbe009a9e641ae34130441d.png&w=640&q=75)
If you need to migrate users between them and you have an Azure AD subscription that enables groups in Enterprise Applications (which requires Active Directory Premium P1 or P2), then you may be in luck: you just have to add the necessary groups into the NEW APP
(which hopefully aren't many). If not and you have a sizeable list of users to migrate, you may find yourself having to click through a large list of users, which is both error prone and time consuming.
You could also refer to the Azure AD API documentation. But where to start? You will have to understand exactly how everything translates to the API. Are you dealing with users registered to an application? Is it a service principal? Is the user assignment managed by the application? The user itself? Something else, like a role assignment? I can't really help you here, because I gave up trying to make sense of the API documentation (hence my self denominating a Lazy Man).
It turns out we can just extract the API calls made from Azure AD by interacting with the user interface and manipulate them using a programming language of choice! This lets us delegate to the UI, which is already structured around Microsoft's domain modelling, the task of correlating the many moving parts necessary to achieve our desired user migration.
The plan, therefore, is simple:
get a list of all the users from
OLD APP
;add a single user to
NEW APP
and see which API calls Azure ID does;modify these calls using the list of users extracted from
OLD APP
.
Getting the list of users to migrate
Let's go to OLD APP and see what we can find using the dev tools.
![](https://paragraph.xyz/_next/image?url=https%3A%2F%2Fstorage.googleapis.com%2Fpapyrus_images%2Faf0b75732fd90e6e302d69342cc34a07.png&w=640&q=75)
After going to OLD APP
, open the dev tools (usually by hitting F12
) before clicking Users and groups
:
![](https://paragraph.xyz/_next/image?url=https%3A%2F%2Fstorage.googleapis.com%2Fpapyrus_images%2F033c34e96ff5b6fadd81edcb006dd006.png&w=640&q=75)
(Azure AD didn't let me name Finn the Human for some reason... )
We can see that a GET
request is made to AppRoleAssignments
. This is exactly what we need. Let's just copy the items
attribute from the response and save it to a variable in ipython
:
![](https://paragraph.xyz/_next/image?url=https%3A%2F%2Fstorage.googleapis.com%2Fpapyrus_images%2Ff2b7352548c77e7685cb92b6fed5e62b.png&w=640&q=75)
![](https://paragraph.xyz/_next/image?url=https%3A%2F%2Fstorage.googleapis.com%2Fpapyrus_images%2Fba38a136bcf37672e242f7825ea33aec.png&w=640&q=75)
Getting the API Call to add a single user
Now let's head to NEW APP
, add a single user and see what we can extract.
![](https://paragraph.xyz/_next/image?url=https%3A%2F%2Fstorage.googleapis.com%2Fpapyrus_images%2F61d85cd79bef198a7683492ff30cde37.png&w=640&q=75)
In the Users and groups
tab, click Add user/group
and add a single user you would eventually migrate to the new application:
![](https://paragraph.xyz/_next/image?url=https%3A%2F%2Fstorage.googleapis.com%2Fpapyrus_images%2Fa83198d79db90e5daaed6fc833d6304e.png&w=640&q=75)
![](https://paragraph.xyz/_next/image?url=https%3A%2F%2Fstorage.googleapis.com%2Fpapyrus_images%2F1ed95b097f901351e199ea2415248881.png&w=640&q=75)
Before actually clicking Assign
, open up the Dev Tools again:
![](https://paragraph.xyz/_next/image?url=https%3A%2F%2Fstorage.googleapis.com%2Fpapyrus_images%2F32cd3fc4a0374a9dd0c0962581d4d210.png&w=640&q=75)
We can see that a POST
request is made do appRoleAssignedTo
. Let's copy it as cURL
and transform to python code using Insomnia
:
![](https://paragraph.xyz/_next/image?url=https%3A%2F%2Fstorage.googleapis.com%2Fpapyrus_images%2Fa23ef293ea24e69e1ba442b7497b5951.png&w=640&q=75)
In Insomnia you just have to paste the cURL
string and it will automatically parse it and fill out the necessary fields:
![](https://paragraph.xyz/_next/image?url=https%3A%2F%2Fstorage.googleapis.com%2Fpapyrus_images%2Fc646c5ce221ab46b8a918bfc39476e35.png&w=640&q=75)
Now we can generate the python code:
![](https://paragraph.xyz/_next/image?url=https%3A%2F%2Fstorage.googleapis.com%2Fpapyrus_images%2Fc2abe1e050e816c918dfe114d74d402e.png&w=640&q=75)
![](https://paragraph.xyz/_next/image?url=https%3A%2F%2Fstorage.googleapis.com%2Fpapyrus_images%2F064f6603cd6e7445d48edf2019d66491.png&w=640&q=75)
Manipulating the API Calls
Now we can use the information extracted when creating the users migration list and modify the API Calls to add other users to NEW APP
.
In order to do this, we can study the POST
API Call to appRoleAssignedTo
to see that we need the principalId
, which is fortunately included when listing users from the Application UI.
All we have to do is loop through the list of users and change the principalId
from the request payload:
![](https://paragraph.xyz/_next/image?url=https%3A%2F%2Fstorage.googleapis.com%2Fpapyrus_images%2F53f69f49ad6127edf916a9e4ee3e84c2.png&w=640&q=75)
After doing it, we can execute it and see what we end up with:
![](https://paragraph.xyz/_next/image?url=https%3A%2F%2Fstorage.googleapis.com%2Fpapyrus_images%2F550331b0c2de13f7d79730f5136d98ce.png&w=640&q=75)
We can see that we fail to add a user. That is because we previously added a single user in order to extract the API Call and the user can't be added again to this application. Instead, the API returns an error code, which we can just ignore.
The other logs tell us that we successfully added the other two users to NEW APP
. Let's double check in the Azure AD portal:
![](https://paragraph.xyz/_next/image?url=https%3A%2F%2Fstorage.googleapis.com%2Fpapyrus_images%2F28de29d75ab538fbd3ce7ff516b85ffe.png&w=640&q=75)
Well that was easy! Easier then having to become a Microsoft Documentation expert.
Conclusion
Using this approach we can automate many other one-shot administrative operations without having to understand how they translate to the actual Azure AD API. For instance, this also applies, albeit with some tweaks to the API Call manipulation, to migrating users from different Azure AD groups, so even if you have a subscription that allows you to easily manage access to multiple Enterprise Application, this approach might come in handy.
Considerations to using this approach:
The Bearer token isn't valid for long. If you have to migrate an unusually large number of users, you may have to optimize a few steps, like:
parallelize the POST API Calls
implement continuation logic or ask for a new token, which can be extracted from a new API Call
Azure only displays the first 200 users, so if you have to migrate more users than that, use the same approach for the POST API, and modify the GET call to set an arbitrary number of users, using the
top
header.Azure may break the list of users in chunks. If that is the case, I couldn't force the API to retrieve the entire list using the approach described in
2.
, but clickingMore...
will make another call with the list continuation. Just do this until you end up with the complete users list and concatenate theitems
from the API Calls inipython
.