A lot of companies have business in different countries, with a local IT department that needs to manage the Intune managed devices of their specific country. In Intune, it is possible to scope permissions to a certain group of devices using scope tags. These scope tags can be assigned to devices, based on membership of an Azure AD (AAD) group. For Windows devices, for which a naming convention per country can be used via Windows Autopilot or Group TAGs can be assigned per country, membership can be easily done with a dynamic AAD group. But for Android and iOS devices this is more complicated.
In this blog post, I show you how we can create country bases devices groups, by using a Logic Apps flow.
The solution
For this solution, we need to have AAD user groups per country. With Graph API we retrieve all the managed Android and iOS devices. In the flow, we determine who the primary user of a device is. We take that primary user and determine which AAD user group the primary user is a member of. With this information, we are able to add the device to an AAD Device group. I used two device groups per country, one for Android and one for iOS, you might create just one device group per country.
To make the flow not too complicated and make it easily possible to expand the countries with their AAD groups, I use a SharePoint List where I store the object IDs from the AAD Device groups.
This is what the complete flow looks like.
The flow is available on my GitHub repository as an ARM template. The Bicep deployment files will follow soon.
The requirements
To get this Logic Apps flow up and running we have a few requirements.
I previously used an Azure App Registration to authenticate to Graph and set the permissions. But I recently learned it is easier and safer to use an Azure Managed Identity for this. So the first requirement is an Azure Managed Identity.
These permissions need to be assigned to the Managed Identity:
Device.Read.All
DeviceManagementManagedDevices.Read.All
GroupMember.ReadWrite.All
User.Read.All
We need an AAD User group per country (or business unit) and an AAD Device group per country (or like I use, one group per country for Android and one per country for iOS).
And we need to create a SharePoint List, to which a (service) account has read permission to grab the information we need from the list.
I created the List like the below example. The Name columns are just there for easy reference to which ObjectID belongs to which AAD group. The names of the columns which hold a Group ObjectId, need to match the query in the SharePoint action (which we later in the post add in the Logic Apps flow). Otherwise, the flow won’t add devices to the device groups.
Setup the Logic Apps flow
Let’s configure the Logic Apps flow! I start with the part of the flow which retrieves all the Android devices and processes these.
Sign in to the Azure portal and open the Logic App service. I created a blank Logic App of type Consumption.
When the flow is created, click on the name of the flow at the top of the screen, open the Identity section, and on the tab User assigned add your Managed Identity.
Open the Overview tab, which shows a few templates, choose Recurrence.
The Logic App designer is opened and the recurrence trigger is added. Choose the recurrence settings of your choice; once a day, once an hour.
First, we are going to add an Initialize variable action to the flow, which is found as (built-in) variables action. In this initialize variable, we store the object IDs of our AAD user groups.
Enter a Name and choose Object as type.
Enter the object IDs in this format (replace these with your own object IDs):
{
"groupIds": [
"000000-000e-0ac1-a0ec-0ad80f0f0d0",
"111111-1a11-1fcd-b11d-11d11bfdf111"
]
}
The next step we’re going to add is an HTTP Action, to query Intune via Graph for all managed Android devices.
Choose GET as Method.
As URI enter:
https://graph.microsoft.com/beta/deviceManagement/managedDevices?$filter=((((deviceType%20eq%20'android')%20or%20(deviceType%20eq%20'androidForWork')%20or%20(deviceType%20eq%20'androidnGMS'))%20or%20((deviceType%20eq%20'androidEnterprise')%20and%20((deviceEnrollmentType%20eq%20'androidEnterpriseDedicatedDevice')%20or%20(deviceEnrollmentType%20eq%20'androidEnterpriseFullyManaged')%20or%20(deviceEnrollmentType%20eq%20'androidEnterpriseCorporateWorkProfile')))))
Choose Add new parameter and check Authentication. Select Managed Identity as authentication type. Choose the previously added Managed Identity and enter https://graph.microsoft.com as Audience.
To use the information we received (by executing this HTTP action) in the upcoming actions, we need to parse the received information with a Parse JSON action.
As Content, we add the Body from the HTTP action, which is found as Dynamic content.
To fill the schema with the correct information, we can add an example payload. The information to use as an example payload can be ‘generated’ by running the flow. Save the flow and hit Run trigger to start the flow.
Open the Runs history from the flow and open the HTTP action. Copy the body.
Another approach is to run the query via Graph Explorer and copy the information from the response.
Back in de designer, click Use sample payload in the Parse JSON action, past the information which we copied from the runs history (or Graph explorer) in the new pop-up, and click Done.
We’re adding a second HTTP action. In the previous HTTP action we retrieved the Azure AD Device ID from every device, but to add the device to a group, we need to have the object ID of the device, which we retrieve with this action.
Choose GET as Method.
As URI enter:
https://graph.microsoft.com/v1.0/devices?$filter=(deviceId eq '[azureADDeviceId]')&$select=id,deviceId,displayName
Replace [azureADDeviceId] with the azureADDeviceId Dynamic content from the Parse JSON action.
The HTTP Action is automatically added to a For each action.
Further, select the authentication method and audience like in the previous HTTP action.
Again we add a Parse JSON action to our flow.
This is the schema when you used the same select values as I did:
{
"properties": {
"@@odata.context": {
"type": "string"
},
"value": {
"items": {
"properties": {
"deviceId": {
"type": "string"
},
"displayName": {
"type": "string"
},
"id": {
"type": "string"
}
},
"required": [
"id",
"deviceId",
"displayName"
],
"type": "object"
},
"type": "array"
}
},
"type": "object"
}
The next step is to add a Condition to the flow, which is a Control action.
With this action, we filter out devices that don’t have a primary user set and thus for which we are not able to determine from which country these are.
Search for userPrincipalName as Dynamic content, which is a value from the first Parse JSON Action, and add it in the left text box.
Select is not equal to, from the drop-down list and leave the right text box empty.
This is our Condition action.
False we leave empty as we don’t have a UPN, we can’t move on with the flow.
Under True, we add an HTTP Action.
Choose POST as Method.
As URI enter:
https://graph.microsoft.com/v1.0/users/[UPN]/checkMemberGroups
Replace [UPN] with userPrincipalName as Dynamic content, which is a value from the first Parse JSON Action.
In the Body field, we add the UserGroupIDs (from the initialize variable action).
Further, select the authentication method and audience like in the previous HTTP action.
Add a Parse JSON action.
This is the schema you can use.
{
"properties": {
"@@odata.context": {
"type": "string"
},
"value": {
"items": {
"type": "string"
},
"type": "array"
}
},
"type": "object"
}
We are going to add a SharePoint action, which is the Get Items action. With this action, we retrieve the information we stored in the SharePoint List, by using the group we retrieved in the previous action.
Click Sign in to sign in with the (service) account.
Choose the Site Address and List name from the drop-down list (or enter it as a custom value).
Click Add a new parameter and choose Filter query.
As query add:
UserGroupObjectID eq '[item]'
Replace [item] with the Item, which can be found as Dynamic content from the last Parse JSON action. This adds the SharePoint action in a For each.
Next, we’re adding an HTTP action. With this action, we check if the device is already a member of the device group. If we don’t first check the membership, but just try to add the device to the group and it is already a member, our flow will end with a failure.
Choose GET as Method.
As URI enter:
https://graph.microsoft.com/v1.0/devices/[id]/memberOf/[AndroidDeviceGroupObjectID]
Replaced [id] with the Id of the Parse JSON Get AADDevice Android action.
And replace [AndroidDeviceGroupObjectID] with the AndroidDeviceGroupObjectID of the SharePoint Get items action.
These steps will automatically add the HTTP action in two For each actions.
Further, select the authentication method and audience like in the previous HTTP action.
When the device is not yet a member of the group, the previous HTTP action will fail with a status code 404. We use that status code in a Condition action, to end our flow when the device is already a member of the group, or to move on with the flow and add the device to a group.
Add a Condition action.
In the left text box add Status code from the previous HTTP action.
Choose is equal to from the drop-down list. Enter 404 in the right text box.
We need to make sure this condition action runs even when the HTTP action fails. For this, click on the three dots to open the settings of the condition, choose Configure run after. Select has failed and click Done.
For this part of the flow, add the last HTTP action under True.
Choose POST as Method.
As URI enter:
https://graph.microsoft.com/v1.0/groups/[AndroidDeviceGroupObjectID]/members/$ref
Replace [AndroidDeviceGroupObjectID] with the AndroidDeviceGroupObjectID of the SharePoint Get items action.
In the Body field enter:
{
"@@odata.id": "https://graph.microsoft.com/v1.0/devices/[id]"
}
Replace [id] with the id of the Parse JSON Get AADDevice Android action.
Further, select the authentication method and audience like in the previous HTTP action.
This is what our current flow looks like.
Depending on your needs, you can create a second flow with the same steps to retrieve all iOS devices. Or we can create a parallel branch in the same flow to retrieve and process the iOS devices.
If the choice is a parallel branch, click the plus button between the initialize variable and first HTTP action.
And add an HTTP Action in the new branch.
The URI to retrieve all iOS devices for the new action is:
https://graph.microsoft.com/beta/deviceManagement/managedDevices?$filter=(((deviceType%20eq%20'iPad')%20or%20(deviceType%20eq%20'iPhone')%20or%20(deviceType%20eq%20'iPod')))
Add all the steps which we added for Android, also for the iOS devices. If you finished creating the whole flow, for Android and iOS, it looks like this one.
That’s it. We now have our device groups filled with devices. Based on these groups we can assign scope tags in Intune and provide local IT with the required permissions for their countries devices.
Thanks for reading!
3 Comments
Hey Peter,
Thanks so much for this guide! It’s helped me solve another problem we were having around device grouping based on user dept. I’ve got it working now, the only remaining struggle I have is the logic app status reports as failed. It will show failed if the device is already in the group or if the primary user no longer exists(this is a process thing for my team).
I was looking at adding a condition to filter out the failed tasks so it would show success if devices were already a member of the group but if I put a condition after checkmembergroups http call it will fail.
If you have any insight into where I could look or what I might be able to implement to help mitigate these false positives that would be amazing!
Thanks again and great post!!
Hi Peter,
It was a very useful guide. Thank you so much for dedicating time for doing this.
I wanted to add an additional step that is required just before making the first call to the “HTTP Get Android Devices” and is that if your tenant has more than a 1000 devices the first Get will only get the first 1000.
To solve this I have made a Do-While, declaring a variable before with URI + filters for the first 1000 devices, and then in the get URI putting the variable on it. The same query replies with an attribute called “@odata.nextLink” with the new URI for the next 1000 devices.
Hi Peter,
I had one question regarding this flow. Why is the SharePoint List access not at the beginning? With that change you can optimize and only change the groups on the Sharepoint List without accessing the flow to change the declared variable.
Thank you