Microsoft Graph Delta Query from Microsoft Flow: Part 2
Since you made it to this page I assume that you read and followed the first article. If you didn’t, stop and go do that. Even if you aren’t using the SharePoint parts, you will need to configure your App in Azure to access the Microsoft Graph. Once you do that, come back here.
Next, this is a long, detailed post and there is even a Part 3 that makes it all work. If you get lost in the details, don’t despair and don’t panic! I captured the entire Flow in a single graphic at the end of this page so you can use it as a map if you get lost.
Part 1: The App, Consent, and the Plan
deltaLink
Part 3: Delta Query and Deleted Objectsaccess-token
.
In several places in this exercise, you are going to use a Parse JSON action on the response body of the HTTP action. The Parse JSON action needs to know the schema of the response to perform it’s work. Here is how to save yourself a ton of time.
{
"token_type": "Bearer",
"expires_in": 3600,
"ext_expires_in": 3600,
"access_token": "eyJ0eXAiOiJKV1QiLCJub25jZSI6IkFRQUJBQUFBQUFDRWZleFh4amFtUWIzT2VHUTRHdWd2YlRYZDhBQVlEWHExQjJwbEpTbWpiZVBkYldkTkRHLUVoaEYtNlZ3bFM5MXE5Q3o3Uzg1VmFqNkRmZUJJOG5BdVY3R0I0ZGkIjoiOTY4ZDAwZjctM2FmOS00NzQxLTk5ZDUtZTU1Y2JiODg5MjhlIiwicm9sZXMiOlsiR3JvdXAuUmVhZC5BbGwiLCJVc2VyLlJlYWQuQWxsIl0sInN1YiI6Ijk2OGQwMGY3LTNhZjktQYXH6AvQQlDNyMv15xkV6She9Fwbx00kCgQhDg0Vl-g"
}
Now that we are authenticated, we are going to use the access token
for every subsequent request. When we get a response from the groups request it is going to return Users
and either a nextLink
or a deltaLink
. So we need variables for these three items.
https://graph.microsoft.com/beta/groups/delta/?$filter=id%20eq%20'd3dd0158-a239-4417-bd75-150a16bfdc99'&$select=displayName,description&$expand=members
[space]
between the word “Bearer” and the access token. Failing to include it caused me many wasted hours!)
{
"@odata.context": "https://graph.microsoft.com/beta/$metadata#groups(displayName,description,members)",
"@odata.nextLink": "https://graph.microsoft.com/beta/groups/delta/?$skiptoken=rUj7-LdP8JZhP1eR4xwz8RFLB10gzqT4VuUmz2aba8sDbpfE38oeFi6if63K9w1nK9VN1n4yiakdtAI8XJ_8vQUL4rbSs8El740j...",
"value": [
{
"displayName": "Spanning Protected Users",
"description": "Users protected by Spanning Backup for Office 365",
"id": "d3dd0158-a239-4417-bd75-150a16bfdc99",
"members@delta": [
{
"@odata.type": "#microsoft.graph.user",
"id": "13af31a2-18e9-432e-bb21-c6989c85af18"
},
{
"@odata.type": "#microsoft.graph.user",
"id": "e2e6d7ad-eb03-4466-a05c-74dee6b60b10",
"@removed": {
"reason": "deleted"
}
},
{
"@odata.type": "#microsoft.graph.user",
"id": "d04f8905-55e3-4be9-af52-5c4abe131989"
}
]
}
]
}
Pretty cool isn’t it! All we have to do now is process the JSON like we did before and decide what to do with the users. Notice that there are two kinds of users; two are current members of the group and one was removed by deletion. Copy the body of the response and do yourself a favor - reorder the member data so that the deleted user is first. Like this:
{
"@odata.context": "https://graph.microsoft.com/beta/$metadata#groups(displayName,description,members)",
"@odata.nextLink": "https://graph.microsoft.com/beta/groups/delta/?$skiptoken=rUj7-LdP8JZhP1eR4xwz8RFLB10gzqT4VuUmz2aba8sDbpfE38oeFi6if63K9w1nK9VN1n4yiakdtAI8XJ_8vQUL4rbSs8El740j...",
"value": [
{
"displayName": "Spanning Protected Users",
"description": "Users protected by Spanning Backup for Office 365",
"id": "d3dd0158-a239-4417-bd75-150a16bfdc99",
"members@delta": [
{
"@odata.type": "#microsoft.graph.user",
"id": "e2e6d7ad-eb03-4466-a05c-74dee6b60b10",
"@removed": {
"reason": "deleted"
}
},
{
"@odata.type": "#microsoft.graph.user",
"id": "13af31a2-18e9-432e-bb21-c6989c85af18"
},
{
"@odata.type": "#microsoft.graph.user",
"id": "d04f8905-55e3-4be9-af52-5c4abe131989"
}
]
}
]
}
In the next section we’re going to add a Parse JSON action and use this result to prep the schema. The schema tools is pretty lazy, it only looks at the first object and we want to be sure that @removed
is part of the schema. Otherwise you are at the mercy of the Flow expression editor; personally I’d prefer a slow walk across hot coals.
Since this is just a demo, and I don’t know what you want to do with your users, I am just going to write them to a SharePoint list. First we have to process the JSON. We’ll use the same technique as before.
@@removed
object.
Now that we have the user info, it is up to you to decide what you want to do with each user. In our case we’ll just write them to a SharePoint list. The main thing is that you need to submit another request using the nextLink
that was returned in the response until you get a deltaLink
.
Get
into our variables. Add three Set variable actions and name them:
First(body('Parse_JSON_-_Group_Members')?['value'])['members@delta']
. This expression will grab the array of Users and load them into the variable so we can loop over them.OK, this is where teaching and documenting Flow gets a bit tricky. We need to loop through the Users and then request more until we don’t get a nextLink
but get a deltaLink
instead. In Flow that means Do-Until
we have no users, Apply to Each
user, If
the user is not deleted, and then Do Something
with the user - in our case we’ll record the user in SharePoint. (If you want to see all this visually, take a look at the Visio diagram in the first post.) Here we go:
@empty(variables('Users'))
in Advanced mode.@equals(items('Apply_to_each_-_User_in_Array')?['@removed']?['reason'], null)
.Whew! Almost there! One last condition, if we don’t have a nextLink
then we have a deltaLink
. So after the first batch of users has been processed, check the nextLink
and then go get more users or exit the loop.
@empty(variables('NextLink'))
[]
. This will empty the Users arrray variable.Once the Flow runs, check your work. You should see that the Apply to each - User in Array ran for every user, even the deleted ones. What we need right now is the result of the HTTP - Get Next Data. Open the inspector and copy the Body to a text editor.
Notice that the format is the same as our original response, it is just missing the nextLink
. That’s because there is no more data, so we need to save the deltaLink
for the next time we make a request.
{
"@odata.context": "https://graph.microsoft.com/beta/$metadata#groups",
"@odata.deltaLink": "https://graph.microsoft.com/beta/groups/delta/?$deltatoken=rUj7-LdP8JZhP1eR4xwz8RFLB10gzqT4VuUmz2aba8tcB5NxzFGmUQgI3AlFDJJc4d9ZzgtsbBAsOodPpYW4d-mq1jGwJJxgeIx7w06Sfj1lZ3DjfrZ5LK84qT-c_5ghdwmt96DHVOrgfscSeG84E0nQQ-C6jv92NVyGxc_Cs5CDcl1dp8zs5om9S4YBSCvsVmkJMSafNeKhm9lm46cYlKiOiQV64SGxX83z.TOZqP3XGc2gay36Ha9uDL84JwCVk_IbWFL5A6J0n-Ns",
"value": []
}
Notice that there is no nextLink
. Remember that we need to evaluate if there is a nextLink
or a deltaLink
. Also, remember that the JSON syntax parser is lazy. So let’s do a little work and help that parser. Copy the deltaLink
from up above and paste it right under the @odata.context
bit in the previous response. It should look like this. (Heck, you could just copy mine, it doesn’t matter.)
{
"@odata.context": "https://graph.microsoft.com/beta/$metadata#groups(displayName,description,members)",
"@odata.deltaLink": "https://graph.microsoft.com/beta/groups/delta/?$deltatoken=rUj7-LdP8JZhP1eR4xwz8RFLB10gzqT4VuUmz2aba8tcB5NxzFGmUQgI3AlFDJJc4d9ZzgtsbBAsOodPpYW4d-mq1jGwJJxgeIx7w06Sfj1lZ3DjfrZ5LK84qT-c_5ghdwmt96DHVOrgfscS",
"@odata.nextLink": "https://graph.microsoft.com/beta/groups/delta/?$skiptoken=rUj7-LdP8JZhP1eR4xwz8RFLB10gzqT4VuUmz2aba8sDbpfE38oeFi6if63K9w1nK9VN1n4yiakdtAI8XJ_8vQUL4rbSs8El740j...",
"value": [
{
"displayName": "Spanning Protected Users",
"description": "Users protected by Spanning Backup for Office 365",
"id": "d3dd0158-a239-4417-bd75-150a16bfdc99",
"members@delta": [
{
"@odata.type": "#microsoft.graph.user",
"id": "e2e6d7ad-eb03-4466-a05c-74dee6b60b10",
"@removed": {
"reason": "deleted"
}
},
{
"@odata.type": "#microsoft.graph.user",
"id": "13af31a2-18e9-432e-bb21-c6989c85af18"
},
{
"@odata.type": "#microsoft.graph.user",
"id": "d04f8905-55e3-4be9-af52-5c4abe131989"
}
]
}
]
}
Now copy all of that and go back and Edit your Flow.
deltaLink
and the nextLink
.First(body('Parse_JSON_-_Next_Data')?['value'])?['members@delta']
Whew! What now? Well, now that we’ve managed to get through one full cycle, we have to store the deltaLink
. Let’s just throw it into our FlowGroupConfiguration list on your SharePoint site.
Wow, ok, I admit, that was a lot of work and all we have to show for it is one pass through the Flow. If you got lost along the way don’t panic! I captured the entire flow in a single graphic so you can use it as a map if you get lost.
In the next post in the series we are going to augment our Flow so that it check for the Delta Link and if it doesn’t find it, it’ll run the initializtion. If it does find the Delta Link, it’ll run the Delta Query and only get the items that have changed.
Hop over to part three: Part 3: Delta Query and Deleted Objects
Ready to start your next project with us? That’s great! Give us a call or send us an email and we will get back to you as soon as possible!
+1.512.539.0322