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»Get notified on expiring Intune LOB apps
    Automation

    Get notified on expiring Intune LOB apps

    Peter KlapwijkBy Peter KlapwijkMarch 14, 2023Updated:February 14, 20258 Mins Read

    It’s been a while since the last post in the MEM Monitoring series, but today we have a new blog post. This time I describe how we can set up a Logic Apps flow to get notified when an iOS LOB app is about to expire. An expired iOS LOB app can’t be deployed to our users anymore, so we need to make sure the apps are renewed before they expire. But who keeps an eye on the expiration date? Nobody, so receiving a notification before the app expires will warn us on time.

    This is a pretty simple flow compared with the flows I shared in the past. It’s not much more than an HTTP action, Condition, and the action to send out the notification.

    This blog post is part of the MEM (Intune) Monitoring series. An article with a short explanation of every MEM Monitoring flow I shared and links to the related articles can be found here.

    The solution in short

    As written this is a short flow to get the job done. We query Microsoft Graph for all the iOS LOB apps. We do this by using an HTTP action in an Azure Logic Apps flow. When we have an overview of the apps pulled from Graph, we use a Condition action to filter on the expiration date. In this example, I send a notification when the expiration is in less than 30 days. For these expiring apps, I send out an email and a message to a Teams channel, to show both possibilities.

    For easy deployment, you can also find the flow on my GitHub.

    Requirements

    For this solution, we have some requirements. To query Microsoft Graph we need to have permission to perform this job. There are different options to authenticate to MS Graph and assign permissions, but I prefer an Azure Managed Identity.
    The required Graph (Application) permission we need is:
    DeviceManagementApps.Read.All

    So the first requirement is a Managed Identity with the above permission.

    The only other requirement depends on what kind of notification you want to send. If this is per email, you need a (shared) mailbox. If you want to send out a message to a Teams channel, you need to create an incoming webhook.

    Setup the first part of the Logic App flow

    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, and choose Recurrence.

    Change the interval settings to your needs.

    Click New step to add our first action to the flow. Search for HTTP and add an HTTP action to the flow.

    As Method select GET.
    As URI enter:

    https://graph.microsoft.com/beta/deviceAppManagement/mobileApps?$filter=(isof(%27microsoft.graph.iosLobApp%27)%20or%20isof(%27microsoft.graph.managedIOSLobApp%27))%20and%20(microsoft.graph.managedApp/appAvailability%20eq%20null%20or%20microsoft.graph.managedApp/appAvailability%20eq%20%27lineOfBusiness%27%20or%20isAssigned%20eq%20true)&$orderby=displayName&

    You can find such a URI yourself by opening the developer tools in Edge when you’re browsing through the Intune portal. The network tab shows which Graph API calls are made to pull the data when browsing through the portal.

    Choose Add Parameter and select Authentication.
    As Authentication type select Managed identity.
    Select your Managed identity from the list.
    And add https://graph.microsoft.com as Audience.

    Next, we need to add a Parse JSON action. We parse the output of the HTTP action, to be able to use the values later on in the flow.
    As Content, we select Body from the Dynamic content list that is from our HTTP action.

    To get the schema, we can use an example payload to generate our schema. We can get such an example payload by running the flow (without the Parse JSON action), to grab the output from the HTTP action. Or we use Graph Explorer, run the URI from the HTTP action and use that output as an example.

    Click Use sample payload option to create the schema. Paste the information from Graph Explorer in the sample payload field to create the schema.

    And this is our Parse JSON action

    Sometimes when we run the flow, the Parse JSON action throws an error like “Invalid type. Expected string but got Null” .

    The value is null, but a string was expected. We can simply solve this issue by replacing:

    "type": "string"
    

    For this in the schema:

    "type": ["string", "null"]

    This is the schema I use, which you can use in your flow.

    {
        "properties": {
            "@@odata.context": {
                "type": "string"
            },
            "@@odata.count": {
                "type": "integer"
            },
            "value": {
                "items": {
                    "properties": {
                        "@@odata.type": {
                            "type": "string"
                        },
                        "appAvailability": {
                            "type": "string"
                        },
                        "applicableDeviceType": {
                            "properties": {
                                "iPad": {
                                    "type": "boolean"
                                },
                                "iPhoneAndIPod": {
                                    "type": "boolean"
                                }
                            },
                            "type": "object"
                        },
                        "buildNumber": {
                            "type": "string"
                        },
                        "bundleId": {
                            "type": "string"
                        },
                        "committedContentVersion": {},
                        "createdDateTime": {
                            "type": "string"
                        },
                        "dependentAppCount": {
                            "type": "integer"
                        },
                        "description": {
                            "type": "string"
                        },
                        "developer": {
                            "type": "string"
                        },
                        "displayName": {
                            "type": "string"
                        },
                        "expirationDateTime": {
                            "type": "string"
                        },
                        "fileName": {
                            "type": "string"
                        },
                        "id": {
                            "type": "string"
                        },
                        "identityVersion": {
                            "type": "string"
                        },
                        "informationUrl": {},
                        "isAssigned": {
                            "type": "boolean"
                        },
                        "isFeatured": {
                            "type": "boolean"
                        },
                        "largeIcon": {},
                        "lastModifiedDateTime": {
                            "type": "string"
                        },
                        "minimumSupportedOperatingSystem": {
                            "properties": {
                                "v10_0": {
                                    "type": "boolean"
                                },
                                "v11_0": {
                                    "type": "boolean"
                                },
                                "v12_0": {
                                    "type": "boolean"
                                },
                                "v13_0": {
                                    "type": "boolean"
                                },
                                "v14_0": {
                                    "type": "boolean"
                                },
                                "v15_0": {
                                    "type": "boolean"
                                },
                                "v16_0": {
                                    "type": "boolean"
                                },
                                "v8_0": {
                                    "type": "boolean"
                                },
                                "v9_0": {
                                    "type": "boolean"
                                }
                            },
                            "type": "object"
                        },
                        "notes": {
                            "type": "string"
                        },
                        "owner": {
                            "type": "string"
                        },
                        "privacyInformationUrl": {
                            "type": [
                                "string",
                                "null"
                            ]
                        },
                        "publisher": {
                            "type": "string"
                        },
                        "publishingState": {
                            "type": "string"
                        },
                        "roleScopeTagIds": {
                            "type": "array"
                        },
                        "size": {
                            "type": "integer"
                        },
                        "supersededAppCount": {
                            "type": "integer"
                        },
                        "supersedingAppCount": {
                            "type": "integer"
                        },
                        "uploadState": {
                            "type": "integer"
                        },
                        "version": {},
                        "versionNumber": {
                            "type": "string"
                        }
                    },
                    "required": [
                        "@@odata.type",
                        "id",
                        "displayName",
                        "description",
                        "publisher",
                        "largeIcon",
                        "createdDateTime",
                        "lastModifiedDateTime",
                        "isFeatured",
                        "privacyInformationUrl",
                        "informationUrl",
                        "owner",
                        "developer",
                        "notes",
                        "uploadState",
                        "publishingState",
                        "isAssigned",
                        "roleScopeTagIds",
                        "dependentAppCount",
                        "supersedingAppCount",
                        "supersededAppCount",
                        "appAvailability",
                        "version",
                        "committedContentVersion",
                        "fileName",
                        "size",
                        "bundleId",
                        "expirationDateTime",
                        "versionNumber",
                        "buildNumber",
                        "identityVersion",
                        "applicableDeviceType",
                        "minimumSupportedOperatingSystem"
                    ],
                    "type": "object"
                },
                "type": "array"
            }
        },
        "type": "object"
    }

    Next, we add a Condition action, which is a Control action.
    With this action, we will filter out all applications with an expiration date of 30 days. If the expiration date is in the next 30 days, the result of the condition is true, thus under true we will add the next part of the flow.

    In the left box add expirationDateTime, which you can find as Dynamic content. This will add the condition in a For each action, which will loop through every found application.
    Choose is less than from the drop-down list.

    In the right box, we will add an expression. This will add the current date/ time plus 30 days.
    Expression:

    addToTime(utcNow(),30,'day')

    And this is our condition.

    Under True, we add our action to send out a notification. In this case, I add an action to send out an email using a shared mailbox. You need to authenticate to the shared mailbox, which I don’t show in the screen shot.

    As you can see you can just add your own text to the Subject and Body. But we can also use dynamic content items to clarify which LOB app is about to expire and when the expiration date is.

    And another option to send out a notification, us send out a message to Teams.

    Add an HTTP action.
    As Method select POST.
    As URI enter the webhook URL.
    As Headers enter Content-Type – application/json

    In the body, we add the text and title in JSON format. Below is the JSON format we need to use for this, without our variables:

    {
     "text": "WRITE YOUR MESSAGE TEXT HERE",
      "title": "WRITE YOUR TITLE HERE"
    }

    We can use dynamic content and enter our own text.
    Use \n\n in the text to create new lines in the text message, otherwise, the message consists of one long line of text.
    And I used ** to get some text in bold.

    This is the JSON I used in my example:

    {
      "text": "**Application name:** @{items('For_each_iOS_LOB_Apps')?['displayName']}\n\n **Expiration date:** @{items('For_each_iOS_LOB_Apps')?['expirationDateTime']}\n\n Please contact the application owner/ developer of the LOB app to supply an updated version of the app.",
      "title": "iOS LOB App @{items('For_each_iOS_LOB_Apps')?['displayName']} is about to expire!"
    }

    And this is our complete flow.

    The end result

    The end result is a Teams message in one of your Teams channels.

    Or an email message in your mailbox!

    That’s it for today. Thanks for reading!

    Intune Intune Monitoring Logic Apps MEM MEMMonitoring 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

    MEM Monitoring: Assignment monitoring to keep your tenant cleaned up

    August 24, 2022

    MEM Monitoring: Monitor Security baselines in Endpoint Security

    August 16, 2022

    Create an application-based Azure AD group with Logic Apps

    May 9, 2022
    Add A Comment
    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}