Windows Autopilot lifecycle automation with Logic Apps

WINDOWS AUTOPILOT LIFECYCLE AUTOMATION

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.

<br>

<br>

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.
You could also create a separate Logic App to automatically clean-up the SharePoint List and/ or archive the items to a separate List.

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




Be the first to comment

Leave a Reply

Your email address will not be published.


*