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»Windows Autopilot lifecycle automation with Logic Apps
    Automation

    Windows Autopilot lifecycle automation with Logic Apps

    Peter KlapwijkBy Peter KlapwijkMarch 8, 2021Updated:February 14, 2025413 Mins Read

    Now that we`re working more and more with Windows Autopilot, the question is how to handle lifecycle management around Autopilot registered devices. This might involve (re-)assigning different Autopilot deployment profiles, but also the retirement of old devices.
    Multiple solutions are available to get the job done, like creating some automation with PowerShell or delegating the rights to the Endpoint Manager admin center to perform the job manually.
    I had at least the requirements to build some automation, and also to delegate the job to different departments (IT staff). Besides that, a solution that runs fully in the cloud would also be nice. I decided to use Logic Apps to build the back-end of our lifecycle management solution. The basic solution for my Logic App I share in this blog post.

    The solution in short

    This lifecycle automation process consists of a number of components. I created a SharePoint List to which IT staff, who are responsible to get the job done, can add the serial number of a device for which the deployment profile needs to be changed or the device needs to be retired. The Logic App flow checks the SharePoint List every couple of minutes for new items.

    If a new item is available, the Logic Apps flow starts. The flow first checks, using Graph, if the serial number is found in the Autopilot service. If the serial is not found, the List is updated and an email is sent to the IT employee who added the serial to the list.

    If the serial is found the next check is done, based on the Request type. This Request type is Change Profile or Retired. This value is set in the new item on the SharePoint List.
    The Change Profile job uses Graph to set a new Group TAG, based on which the device is added to a dynamic group. To this group, an Autopilot deployment profile is assigned.

    If the Request type is Retired, the flow checks if the Autopilot object has an enrollmentState of enrolled or notContacted. If the state is notContacted, the serial is removed from the Autopilot service, the List is updated and an email is sent.
    If the enrollmentState is enrolled, this means there is also a managed device (Intune) object which we need to remove. The rest of the flow is equal to the notContacted flow.

    In an overview this all looks like this, pretty simple isn’t it 🙂

    Requirements

    For this Logic Apps to work we have some requirements.

    First of all, we need a SharePoint List. IT staff needs access to the List to add new items which are handled by Logic Apps. I created the below columns, I renamed the default Title column to Serialnr. I added new columns RequestType and NewProfile (of type choice) which are required in my solution. The others are optional (IsProcessed, StatusCode and StatusMessage), based on which information you want to write back after a flow run and what information you’d like to see (Created and Created By).

    Via HTTP actions, I`m able to use Graph to change the Group TAG and remove the objects from the Autopilot service. These HTTP actions use an Azure App Registration, which is the second requirement. The App Registration is used for authentication in the HTTP actions and also for granting the required permissions.
    The minimum required permissions for this Logic App are shown below:
    DeviceManagementManagedDevices.ReadWrite.All
    DeviceManagementServiceConfig.ReadWrite.All

    Instead of using HTTP actions, you could set up a custom connector to work with Graph in Logic Apps. This could replace all the HTTP actions, or you can use them together in the same flow.

    To secure the HTTP actions I use Azure Key Vault, which is described in this previous post.

    In my case, I used a service account to authenticate from the Logic App to SharePoint, Azure Key Vault and I also use this account to send emails from a shared mailbox. So I have the requirement to set up an account with a mailbox and grant that account rights to the Sharepoint List and the shared mailbox.

    Setup the first part of the Logic App

    Let`s see how the first part of the Logic App is built from the SharePoint trigger to the first switch.
    I suggest renaming the actions to something which describes the action. We might have multiple actions of the same type, which can make it difficult when determining the correct output in later actions.

    Sign-in to the Azure portal and open the Logic Apps service. Here create a new, blank Logic App.

    In the search box for connections and triggers search for SharePoint. Select SharePoint, scroll down (or search again) and select When an item is created.

    Click Sign in and authentication with your (service) account which has access to the list.

    Select the Site address from the drop-down of your List. If the address is not shown, click Enter custom value and enter the address.
    Pick the List Name from the drop-down list.
    Make your choice of how often the Logic App needs to check the List for new items.

    I first add the Azure Key Vault action to my flow, so I can get my secret and use it in all the upcoming HTTP actions.
    Click the Add step button, search for Azure Key Vault and select Get secret.
    Enter the Vault name and click Sign in.

    After authentication is done, enter the Name of the secret.

    The next action we add is an HTTP action. With this type of action, we can perform the same queries as, for example, we use Graph Explorer against Graph.
    Choose GET as Method.
    As URI enter:

    https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeviceIdentities?$filter=((contains(serialnumber,'[TITLE]')))

    We need to replace [TITLE] with dynamic content by searching in the Dynamic content popup which is shown on the right. Search for Title as this is the serial number retrieved from the SharePoint List by the trigger (recognized by the SharePoint icon).
    Choose Add new parameter and check Authentication.

    Enter the Tenant ID and Client ID as found on your App Registration.
    Enter https://graph.microsoft.com as Audience.
    As secret, we add the Dynamic content Value (Value of the secret).

    This is how our first HTTP action looks like.

    To use the information we received (by executing the previous HTTP action) in the upcoming actions, we need to parse this information with a Parse JSON action.
    Add the Parse JSON action to the flow.
    We’re going to parse the response body of the previous HTTP action. In the Content box, we add Body, from the previous action, which is found again via the Dynamic content popup.

    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 performing the same GET query (as in the previous HTTP action), but this time using Graph Explorer. Copy the response in Graph Explorer like below. Click Use sample payload in the Parse JSON action, and past the information in the new pop-up.

    This generates the required Schema:

    {
        "type": "object",
        "properties": {
            "@@odata.context": {
                "type": "string"
            },
            "@@odata.count": {
                "type": "integer"
            },
            "value": {
                "type": "array",
                "items": {
                    "type": "object",
                    "properties": {
                        "id": {
                            "type": "string"
                        },
                        "deploymentProfileAssignmentStatus": {
                            "type": "string"
                        },
                        "deploymentProfileAssignmentDetailedStatus": {
                            "type": "string"
                        },
                        "deploymentProfileAssignedDateTime": {
                            "type": "string"
                        },
                        "groupTag": {
                            "type": "string"
                        },
                        "purchaseOrderIdentifier": {
                            "type": "string"
                        },
                        "serialNumber": {
                            "type": "string"
                        },
                        "productKey": {
                            "type": "string"
                        },
                        "manufacturer": {
                            "type": "string"
                        },
                        "model": {
                            "type": "string"
                        },
                        "enrollmentState": {
                            "type": "string"
                        },
                        "lastContactedDateTime": {
                            "type": "string"
                        },
                        "addressableUserName": {
                            "type": "string"
                        },
                        "userPrincipalName": {
                            "type": "string"
                        },
                        "resourceName": {
                            "type": "string"
                        },
                        "skuNumber": {
                            "type": "string"
                        },
                        "systemFamily": {
                            "type": "string"
                        },
                        "azureActiveDirectoryDeviceId": {
                            "type": "string"
                        },
                        "azureAdDeviceId": {
                            "type": "string"
                        },
                        "managedDeviceId": {
                            "type": "string"
                        },
                        "displayName": {
                            "type": "string"
                        }
                    },
                    "required": [
                        "id",
                        "deploymentProfileAssignmentStatus",
                        "deploymentProfileAssignmentDetailedStatus",
                        "deploymentProfileAssignedDateTime",
                        "groupTag",
                        "purchaseOrderIdentifier",
                        "serialNumber",
                        "productKey",
                        "manufacturer",
                        "model",
                        "enrollmentState",
                        "lastContactedDateTime",
                        "addressableUserName",
                        "userPrincipalName",
                        "resourceName",
                        "skuNumber",
                        "systemFamily",
                        "azureActiveDirectoryDeviceId",
                        "azureAdDeviceId",
                        "managedDeviceId",
                        "displayName"
                    ]
                }
            }
        }
    }

    The next step in our flow is to add a Switch action. With a Switch, we can split our flow into multiple flows (cases), based on values we get from one of the previous actions.
    In this case, we use the value from @odata.count to make the switch. If the value is 0 like below, this means the serial number isn’t found in the Autopilot service. If the value is 1, the serial number is found.

    Add a Switch action (found under Control).
    Search for @odata.count in the Dynamic content and at it in the On box from the switch.

    This creates one Case. Click the + sign on the right to add a second case.
    Put a 0 in the Equals box of one Case (Object not found) and put 1 in the other box (Object is found). Based on these values the flow makes the first switch.

    Case – Serial number not found

    Let’s first build our case when the serial number is not found in the Windows Autopilot service. When the serial number isn’t found the item on the Sharepoint List is updated and an email is to the initiator from a shared mailbox.

    Under the case where we filled in a 0 (the serial isn’t found), click the Add an action button.
    We use a SharePoint – Update item action to update the item on SharePoint.
    Enter the Site Address and List Name.
    In the ID box, we need to fill in the ID, which is found as Dynamic content. And the Title box needs to be filled in with the Title as also found as Dynamix content.
    Optional is to fill in the Status Code from the previous HTTP GET action.

    I enter a text message in the StatusMessage box, which is also updated on the list item. And I set the IsProcessed box to Yes.

    The last action in this flow case is an Office 365 Outlook action to send an email to the initiator.
    Select the Send an email from a shared mailbox action.
    Fill in the email address from the shared mailbox as Original Mailbox Address.
    Search for Created By Email as Dynamix content to add into the To box. This retrieves the email address from the creator of the List item.

    You can create your own text. I use several items from the SharePoint trigger to personalize the email, show the RequestType and serial number.



    Let`s move on to the cases where the serial number is found.

    Case – Change Autopilot Deployment profile

    The case to change the Autopilot Deployment profile is actually to change the Group TAG of the object. Based on the Group TAG a dynamic group is filled to which the profile is assigned.

    Under the case where we filled in a 1 (Object is found), we create an additional switch. With the switch, we determine the RequestType. In the On box enter RequestType, which is found via the Dynamic content popup. In the new Case Equals box enter ChangeProfile (which should correspond with the value we receive from our SharePoint List item when we want to change the AP profile). Add a second Case and enter Retired.

    When we run the GET query using the serial number, we received back a response that contains an ID, like in below example from Grap Explorer. We’re going to use that ID in our next action to set the new Group TAG.

    Add a new HTTP action.
    As Method select POST.
    As URI enter:

    https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeviceIdentities/[id]/UpdateDeviceProperties

    Replace [id] with the ID you can find via the Dynamic content. You should be able to see this ID is used from the Parse JSON action (purple icon), which we defined at the beginning of our flow.

    In the Body field enter:

    {
    "groupTag": "[NewProfile Value]"
    }

    Replace [NewProfile Value] with NewProfile Value found in the Dynamic content. This retreives the value from the SharePoint item.
    As in the previous HTTP action, select the Authentication type, enter the Tenant, Audience and Client ID. And make sure to select the Key Vault secret from the Key Vault action.

    Add a SharePoint – Update item action.
    The information we need to fill in for this action equals the information from the previous Update item action. This time I enter Success in the StatusMessage field.

    As last step in this case I add a Send an email from a shared mailbox action.

    This is how the case Change Autopilot Deployment profile looks like.

    Case – Retired object

    In the case of retired devices, I use an additional switch. Based on the enrollmentState value received in the first GET query I make a switch on notContacted and Enrolled.
    If the enrollmentState is notContacted, this means there is no associated Intune object found for this Autopilot object. We only need to delete the object from the Autopilot service.
    If the enrollmentState is enrolled, there is an associated Intune object found. In this case, we first need to remove the object from Intune and after that, we’re able to delete the object from Autopilot.

    In the On box from this Box, we enter enrollmentState.
    Create a second case. In one case enter notContacted in the equals box and in the other enter enrolled.

    Under the Retired notcontacted case, add a new HTTP action.
    Add a new HTTP action.
    As Method select DELETE.
    As URI enter:

    https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeviceIdentities/[id]

    Replace [id] with the ID you can find via the Dynamic content.

    Select the Authentication type, enter the Tenant, Audience and Client ID. And make sure to select the Key Vault secret from the Key Vault action.

    Again, add an Update item action like we have done a couple of times before.

    And add the last step for this case, Send an email from a shared mailbox.

    This is our case for retired devices, which doesn’t have an associated Intune device.

    Swith to the case for retired devices, which do have an associated Intune device (enrollmentState = enrolled). This case has an extra step, to remove the Intune (managed device) object.

    Add an HTTP action.
    As Method select DELETE.
    As URI enter:

    https://graph.microsoft.com/beta/deviceManagement/managedDevices/[managedDeviceId]

    Replace [managedDeviceId] with the managedDeviceId you can find via the Dynamic content.

    Select the Authentication type, enter the Tenant, Audience and Client ID. And make sure to select the Key Vault secret from the Key Vault action.

    Add a second HTTP action to this case, which is exactly the same as for the case where there is no associated Intune device.
    The URI is again:

    https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeviceIdentities/[id]

    Finish the case by adding a (SharePoint) Update item action and a Send an email from a shared mailbox action.

    The case looks like below.

    The Logic App is now complete.

    Initiate the Autopilot lifecycle automation flow

    To start this flow add a new item to the SharePoint List.
    Enter a serial number.
    Make your choice for the Request Type.
    If the request type is Change Profile, make a choice for the New Profile.
    Click Save.

    Depending on the trigger time, in a couple of minutes, the item is picked up by the Logic Apps flow.
    When finished, the item is updated on the SharePoint List.

    e

    An email is send to the initiator.

    And, in this example, the Group TAG is changed and the correct Autopilot profile assigned.

    That`s it for this post. This is just an example of how lifecycle management can be automated. The flow can be further expanded, for example by also removing the device object in Azure AD as I describe in part 2.
    You could also create a separate Logic App to automatically clean-up the SharePoint List and/ or archive the items to a separate List.

    And an related post is Windows Autopilot automation – assigning Group TAGs, which might also be interesting for you.

    Thanks for reading and if you have any question, drop me a comment below.

    Autopilot Flow Graph Graph API Intune Logic Apps MEM Microsoft Endpoint Manager Power Automate PowerApps Windows Autopilot
    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

    Create an application-based Azure AD group with Logic Apps

    May 9, 2022

    Export Endpoint Analytics Proactive remediation data with Logic Apps

    April 19, 2021

    MEM Monitoring: Get your Windows Autopilot deployment events in a Teams channel with Logic Apps – Part 1

    April 16, 2021
    View 4 Comments

    4 Comments

    1. Saranraj on November 10, 2021 10:43

      Hi Peter,

      Thank you for the post.

      When you remove the serial number from Autopilot, it doesnt remove the device name from Azure AD. In the recent changes of Microsoft, everytime when you want to retry the autopilot whiteglove, you will need to cleanup the entry of the device from Intune, serial number and Azure AD device name. Would you add this flow to this process?

      Reply
      • Peter Klapwijk on May 18, 2022 15:58

        Part 2 describes that process, but a requirement is to change the authentication to a Managed Identity. https://inthecloud247.com/windows-autopilot-lifecycle-automation-with-logic-apps-part2/

        Reply
    2. Nil on November 1, 2023 15:06

      Hi Peter,
      Thank you so much for the post. I am new to Azure and Intune, working for a public school system and managing about 40k devices. I have been trying to find a way to remove the computers automatically that will be disposed of at the end of every school year. I found your post and have been working on it to understand how I can apply it to our system. We don’t have the Key Vault service and cannot buy it because of the budget. Is there any alternative way to do this without using Key Vault?

      Reply
      • Peter Klapwijk on November 2, 2023 15:38

        Hi Nil,

        I would not use an App registration and key vault anymore. A Managed Identity is more save and easier to maintain. I hope that’s an option for you https://inthecloud247.com/configure-a-user-assigned-managed-identity-the-basics/

        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}