Close Menu
Peter Klapwijk – In The Cloud 24-7Peter Klapwijk – In The Cloud 24-7
    Facebook X (Twitter) Instagram
    Peter Klapwijk – In The Cloud 24-7Peter Klapwijk – In The Cloud 24-7
    • Home
    • Intune
    • Windows
      • Modern Workplace
    • macOS
    • Android
    • iOS
    • Automation
      • Logic Apps
      • Intune Monitoring
      • GitHub
    • Security
      • Passwordless
      • Security
    • Speaking
    • About me
    Peter Klapwijk – In The Cloud 24-7Peter Klapwijk – In The Cloud 24-7
    Home»Automation»Create an application-based Azure AD group with Logic Apps
    Automation

    Create an application-based Azure AD group with Logic Apps

    Peter KlapwijkBy Peter KlapwijkMay 9, 2022Updated:February 14, 2025410 Mins Read

    Yes, again a blog post related to a Logic Apps flow!

    That’s because I needed to have an Azure AD group filled with Windows devices, based on if a certain application is installed on the device. I have the information on which device, which applications are installed available in an Azure Log Analytics workspace, as I have the Enhance Intune Inventory data with Proactive Remediations and Log Analytics solution running from the MsEndpointMgr team. Although that script runs in my environment once a day, group membership isn’t changed instantly, but that isn’t an issue for me. It is fine to change group membership just once a day.

    With this information available in Log Analytics (LA), it should be possible to query this information with a Log Analytics flow and via Microsoft Graph to add or remove devices to an Azure AD group.

    So, maybe for somebody else, this is helpful. So let’s start again with an automation-related blog post.

    The solution

    The flow consists of two parallel branches. In the first one, we query the LA workspace via the API with an HTTP action. When a device is retrieved on which the application is installed, we query Azure AD to get the Object ID of that device.
    With the Object ID, we are able to determine if the device is already a member of the Azure AD group, or not. When the device is not a member of the group, we add the device to the group and this part of the flow is finished.

    In the second parallel, we query the Azure AD group first via Microsoft Graph. For every group member, we query the LA workspace, to determine if the application is still installed on the device. If the application wasn’t determined anymore on the device, based on the latest inventory, the device is removed from the Azure AD group.

    This will be our complete flow, with the two parallel branches.

    The flow will be added later to my GitHub repository.

    Requirements

    The flow mostly runs HTTP actions, so besides an Azure AD group, we only need to have a Managed Identity in place with the needed permissions.

    To query the Log Analytics workspace, we assign an Azure role to the Managed Identity. It is enough to assign the Log Analytics Reader role to the identity.

    And these permissions are needed to query Azure AD for the device details and add/ remove devices from/ to the AAD group:
    Device.Read.All
    GroupMember.ReadWrite.All

    Setup the Logic Apps flow – part 1

    Let’s configure the Logic Apps flow. In the first part we set up the flow itself and configure the part of the flow, which queries Log analytics if the application is installed or not. When the application is detected on the device, the device is added to an AAD group.

    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.

    I first initialize two variables, which is optional. As we need the Log Analytics Workspace ID and AAD Group ID in multiple upcoming actions, I find it handy to initialize these at the beginning of the flow. When I need one of them, I now simply pick the one I need from the dynamic content list, instead of entering the ID every time I need it.

    Add an Initialize variable action, twice.

    In the first action, enter WorkspaceID as Name, select String as type, and enter the Log Analytics Workspace ID as value.

    In the second action, enter GroupObjectID as Name, select String as type, and add the Object ID of the Azure AD group (to which the devices are added).

    next, add an HTTP action to the flow to query Log Analytics.
    Choose GET as Method.
    As URI enter:

    https://api.loganalytics.io/v1/workspaces/[WorkspaceID]/query

    Replace [WorkspaceID] with the WorkspaceID from the Dynamic content list.

    Choose Add new parameter and check Authentication. Select Managed Identity as authentication type. Choose the previously added Managed Identity and enter https://api.loganalytics.io as Audience.

    At the Queries row, enter this KQL query in the left textbox and enter your query in the right text box.
    This is my example query for Chrome:

    AppInventory_CL
    | where AppName_s contains "Chrome"
    | where TimeGenerated > ago(1d)
    | project TimeGenerated,ComputerName_s,AppName_s,ManagedDeviceID_g
    | summarize arg_max(TimeGenerated, *) by ComputerName_s

    This query is the same one, which you could use directly in the Log Analytics workspace.

    This is what the HTTP action looks like.

    Usually, we can simply use a Parse JSON action, to process the output of the HTTP action, so we can use the variables from the output later in the flow. But the output of this HTTP action is in rows. That is where I got stuck, as I wasn’t able to grab the device name from the output. I was able to grab the array with the information, but I only needed the device name. But luckily Luise is awesome with everything Power platform related and solved my issue (thanks again!).

    We again add two Initialize variable actions to the flow. One to grab the Object (Table) from the output of the HTTP action, and the second one to grab the Array (Row) from that Object.

    This is the raw output from an HTTP action, showing the table and a row.

    Add an Initialize variable action and add AppResults as Name. Select Object as Type and select Body from the Dynamic content list as Value.

    Add another Initialize variable action and add AppresultsHelper as Name. Select Array as Type and as value we add an Expression:

    variables('AppResults')?['tables'][0]?['rows']

    This is the Initialize variable action.

    Now that we have the arrays, we add a For each action, to loop through each array (Row). We use the output from the latest Initialize variables action.

    Next, add a Compose action, which is a Data operations action. With this action we loop through the computers and next from the output of this action, we grab the device name (in another compose action).
    As Input add Current item from the Dynamic content list.

    When we run this part of the flow, it provides us with all the rows separate as you can see in this example.

    Add a second Compose action. Enter below Expression as Input, which returns the first object from the Array, which is the device name.

    outputs('Compose_loop_over_all_computers')[0]

    We are now going to use Microsoft Graph to query Azure AD for some device details by using the device name, as we need the Object ID later in the flow. The Object ID is returned by this query as ID.

    Add an HTTP Action.
    Choose GET as Method.
    As URI enter:

    https://graph.microsoft.com/v1.0/devices?$filter=(displayName eq '[OUTPUTS]')&$select=id,deviceId,displayName

    Replace [OUTPUTS] with the Outputs from the last compose action, which can be selected from the Dynamic contents list.

    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.

    Add a Parse JSON action.
    As content select Body from the latest HTTP action.
    As Schema you can enter:

    {
        "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"
    }

    Or run the flow, copy the body from the HTTP action and enter that as example payload.

    With the next HTTP action, we check if the device is already a member of the AAD group. If we would skip this step, just try to add the device to the group, but if the device is already member of the group, this would end in an error and a flow that fails.

    Choose GET as Method.
    As URI enter:

    https://graph.microsoft.com/v1.0/devices/[ID]/memberOf/[GroupObjectID]

    Replace [ID] with the ID from the HTTP output and replace [GroupObjectID] with the Group Object ID variable, both from the Dynamic content list.

    The HTTP action will be placed in a For each.
    Don’t forget to enter the Authentication.

    Add a Condition action, which is a Control action.

    As value in the left box, we add StatusCode from the last HTTP action.
    Choose is equal to and enter 404 in the right box.

    Image title

    As the HTTP action results in an error when the device is not a member of the group, we need to change the Run after setting of the Condition otherwise the flow will fail.
    For this, click on the three dots to open the settings of the condition, choose Configure run after. Select has failed and click Done.

    Under True we add the HTTP action to add the device as member of the AAD group.
    Choose POST as Method.
    As URI enter:

    https://graph.microsoft.com/v1.0/groups/[GroupObjectID]/members/$ref

    Replace [GroupObjectID] with the Group Object ID variable.

    As Body enter:

    {
      "@@odata.id": "https://graph.microsoft.com/v1.0/devices/[ID]"
    }

    Replace [ID] with the ID from the Parse JSON action.

    Finish the HTTP action by entering the authentication settings.

    This part of the flow is ready!

    Setup the Logic Apps flow – part 2

    In this part of the flow, we query Azure AD for the AAD group members. And next, we check if the group members still have the application installed, by querying Log analytics.

    Click the Plus sign between the second Initialize variable and first HTTP action and select Add a parallel branch.

    Add these two Initialize variable actions, like in the other parallel branch, but this time leave the values empty.

    First, we query Azure AD for the group members of the AAD group with an HTTP Action.

    Choose GET as Method.
    As URI enter:

    https://graph.microsoft.com/v1.0/groups/[GroupObjectID]/members?&$select=displayName,id

    Replace [GroupObjectID] with the Group Object ID variable.

    Add a Parse JSON action with this schema:

    {
        "properties": {
            "@@odata.context": {
                "type": "string"
            },
            "value": {
                "items": {
                    "properties": {
                        "@@odata.type": {
                            "type": "string"
                        },
                        "displayName": {
                            "type": "string"
                        },
                        "id": {
                            "type": "string"
                        }
                    },
                    "required": [
                        "@@odata.type",
                        "displayName",
                        "id"
                    ],
                    "type": "object"
                },
                "type": "array"
            }
        },
        "type": "object"
    }

    With the next HTTP Action, we query Log analytics with the device name of all group members we just retrieved. The HTTP action will be (automatically) placed in a For each action.

    Choose GET as Method.
    As URI enter:

    https://api.loganalytics.io/v1/workspaces/[WorkspaceID]/query

    Replace [WorkspaceID] with your Workspace ID.

    At the Queries row, enter query in the left textbox and enter your query in the right text box.
    This is my example query for Chrome:

    AppInventory_CL
    | where AppName_s contains "Chrome" and ComputerName_s contains "[displayName]"
    | project TimeGenerated,ComputerName_s,AppName_s,ManagedDeviceID_g
    | summarize arg_max(TimeGenerated, *) by ComputerName_s

    Replace [displayName] with the displayName found in the Dynamic content list.

    We use the variables again to grab the Object and Array.

    Add an Initialize variable action and select DeviceAppResults. As value enter the Body from the last HTTP action.

    Add a second Initialize variable action and select DeviceAppResultsHelper.
    Add this expression as value:

    variables('DeviceAppResults')?['tables'][0]?['rows']

    We don’t use the Compose actions here. As the application isn’t detected anymore, the array will be empty for that particular device. We use a Condition to filter on the empty array.

    Add this expression to the left text box of the Condition action:

    length(body('Set_variable_DeviceAppResultsHelper')?['value'])

    Select is equal to and add 0 to the right box.

    Under True, we add the HTTP action to remove the device from the AAD group.

    As URI enter:

    https://graph.microsoft.com/v1.0/groups/[GroupObjectID]/members/[ID]/$ref

    Replace [GroupObjectID] with the Group Object ID variable and replace [ID] with the ID found in the Dynamic content list.

    And this is the last action of our Logic Apps flow.

    Thanks for reading this post. I hope it is helpful for somebody 🙂

    Intune Logic Apps MEM Microsoft Endpoint Manager Modern Workplace Power Automate
    Share. Facebook Twitter LinkedIn Email WhatsApp
    Peter Klapwijk
    • Website
    • X (Twitter)
    • LinkedIn

    Peter is a Security (Intune) MVP since 2020 and is working as Modern Workplace Engineer at Wortell in The Netherlands. He has more than 15 years of experience in IT, with a strong focus on Microsoft technologies like Microsoft Intune, Windows, and (low-code) automation.

    Related Posts

    Assign Deny Local Log On user right to an (Azure) AD group by using Microsoft Intune

    June 1, 2022

    Monitor your Microsoft 365 licenses with Logic Apps

    March 5, 2022

    Export Endpoint Analytics Proactive remediation data with Logic Apps

    April 19, 2021
    View 4 Comments

    4 Comments

    1. Andrew H on January 4, 2024 12:49

      Im struggling to find where to obtain the Log Analytics Workspace ID from ? Are we supposed to create a Log analytics workspace?

      Reply
      • Peter Klapwijk on January 4, 2024 13:04

        Yes, you should.
        This flow pulls data from the workspace. That data is stored in the workspace because we implemented this solution first https://msendpointmgr.com/2021/04/12/enhance-intune-inventory-data-with-proactive-remediations-and-log-analytics/

        Reply
    2. Andy W on June 20, 2024 17:28

      I’m trying to build this but am getting invalid JSON on the initial query. Trying to get results for Zoom but not being overly familiar with JSON it’s just not working for me. Any pointers would be massively appreciated

      Reply
      • Peter Klapwijk on June 20, 2024 18:53

        Hi Andy,

        Do you mean on the first HTTP action? If that’s the case, do you have the Log Analytics Workspace created and filled with some data by setting up the solution of msendpointmgr?
        Then you could first try to execute the (KQL) query directly in the Logs section of the Log Analytics Workspace. That should provide you some info if it fails.

        Reply
    Leave A Reply Cancel Reply

    Peter Klapwijk

    Hi! Welcome to my blog post.
    I hope you enjoy reading my articles.

    Hit the About Me button to get in contact with me or leave a comment.

    Awards
    Sponsor
    Latest Posts

    Hide the “Turn on an ad privacy feature” pop-up in Chrome with Microsoft Intune

    April 19, 2025

    How to set Google as default search provider with Microsoft Intune

    April 18, 2025

    Using Windows Autopilot device preparation with Windows 365 Frontline shared cloud PCs

    April 13, 2025

    Using Visual Studio with Microsoft Endpoint Privilege Management, some notes

    April 8, 2025
    follow me
    • Twitter 4.8K
    • LinkedIn 6.1K
    • YouTube
    Tags
    Administrative Templates Android Automation Autopilot Azure Azure AD Browser Conditional Access Edge EMS Exchange Online Feitian FIDO2 Flow Google Chrome Graph Graph API Identity Management Intune Intune Monitoring iOS KIOSK Logic Apps macOS MEM MEMMonitoring Microsoft 365 Microsoft Edge Microsoft Endpoint Manager Modern Workplace Office 365 OneDrive for Business Outlook Passwordless PowerApps Power Automate Security SharePoint Online Teams Windows Windows 10 Windows10 Windows 11 Windows Autopilot Windows Update
    Copy right

    This information is provided “AS IS” with no warranties, confers no rights and is not supported by the authors, or In The Cloud 24-7.

     

    Copyright © 2025 by In The Cloud 24-7/ Peter Klapwijk. All rights reserved, No part of the information on this web site may be reproduced or posted in any form or by any means without the prior written permission of the publisher.

    Shorthand; Don’t pass off my work as yours, it’s not nice.

    Recent Comments
    • Peter Klapwijk on Using Windows Autopilot device preparation with Windows 365 Frontline shared cloud PCs
    • John M on Using Windows Autopilot device preparation with Windows 365 Frontline shared cloud PCs
    • Christoffer Jakobsen on Connect to Azure file shares with Microsoft Entra Private Access
    • Ludo on How to block Bluetooth file transfer with Microsoft Intune
    • RCharles on Automatically configure the time zone (during Autopilot enrollment)
    most popular

    Application installation issues; Download pending

    October 1, 2024

    Restrict which users can logon into a Windows 10 device with Microsoft Intune

    April 11, 2020

    How to change the Windows 11 language with Intune

    November 11, 2022

    Update Microsoft Edge during Windows Autopilot enrollments

    July 9, 2024
    Peter Klapwijk – In The Cloud 24-7
    X (Twitter) LinkedIn YouTube RSS
    © 2025 ThemeSphere. Designed by ThemeSphere.

    Type above and press Enter to search. Press Esc to cancel.

    Manage Cookie Consent
    To provide the best experiences, we use technologies like cookies to store and/or access device information. Consenting to these technologies will allow us to process data such as browsing behavior or unique IDs on this site. Not consenting or withdrawing consent, may adversely affect certain features and functions.
    Functional Always active
    The technical storage or access is strictly necessary for the legitimate purpose of enabling the use of a specific service explicitly requested by the subscriber or user, or for the sole purpose of carrying out the transmission of a communication over an electronic communications network.
    Preferences
    The technical storage or access is necessary for the legitimate purpose of storing preferences that are not requested by the subscriber or user.
    Statistics
    The technical storage or access that is used exclusively for statistical purposes. The technical storage or access that is used exclusively for anonymous statistical purposes. Without a subpoena, voluntary compliance on the part of your Internet Service Provider, or additional records from a third party, information stored or retrieved for this purpose alone cannot usually be used to identify you.
    Marketing
    The technical storage or access is required to create user profiles to send advertising, or to track the user on a website or across several websites for similar marketing purposes.
    Manage options Manage services Manage {vendor_count} vendors Read more about these purposes
    View preferences
    {title} {title} {title}