Deploy Microsoft Endpoint Manager configuration policies on a schedule with Power Apps and Power Automate

Organizations I work with have all sorts of unique scenarios to align to business objectives and processes to managing endpoints. Recently I learned about a scenario where organizations would like to schedule the deployment of a device configuration policy. I thought this was an interesting topic as it doesn’t come up very often, however with the power of the Microsoft Power Platform we can create a solution for this scenario. This type of scheduler can be applied to apps as well or other actions such as cleaning out devices, wiping devices, etc., so feel free to change the Graph targets in Power Automate and update Power Apps with the data needed to accomplish your task.

For this post I’m focusing on device configuration policies as it’s the most common request I’ve heard about so far.

Even though scheduling a policy may seem straight forward, there is some complexity around all this, however I’ve worked through all that complexity and will review in detail throughout this post.

Let’s get started!

Requirements

  • Microsoft Endpoint Manager
  • Power Apps
  • Power Automate
  • Microsoft Graph

VIDEO: https://www.screencast.com/t/fVBSW23qU

Power Automate

For the Power App we need three separate Power Automate flows, one to query all the configuration policies, one to query AAD groups, and one to set the policy schedule. Two of these I cover in this post, and the AAD group flow is covered in an earlier post, simply copy and paste. However for the main scheduling flow at first glance it looks daunting, and believe me I think I have a few new grey hairs creating it, however it’s all there and I articulate each step.


Query MEM configuration policies

As usual test manually by using a manual trigger, then change over to Power Apps trigger. My flows are complete and is why my triggers start with PowerApps.

We need to pull in all the Intune policies into a data table in Power Apps, we do this by creating a flow and adding the following:

HTTP action – Method = GET, URI = https://graph.microsoft.com/beta/deviceManagement/deviceConfigurations?$select=id,displayName,createdDateTime&$sortby=displayName

Parse JSON action – if you run the HTTP get before adding the parse JSON action, simply copy the output from the HTTP action and paste in the Generate from sample dialog.

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

Add a Response action and use the value from the Parse JSON action to send the data back to Power Apps in the form of a collection.

Schedule MEM policy

From Power Automate, create a new flow, again I start with a manual trigger so I can test the whole process end-to-end before connecting to Power Apps, I strongly suggest anyone to do this as it save a lot of frustration.

Add three variables (i.e. initialize variable) and name them accordingly. These will be utilized when this flow is added to Power Apps to send data over.

Next add a Convert time zone action. We need to convert the time (not really the time zone, however it’s useful regardless) because what is sent over isn’t in the proper format and I’d like the time zone to align with a particular time zone. For the base time, utilize the variable varSelectedDateTime which will come from the selected date & time in the Power App. In the format string add the following expression:

formatDateTime(variables('varSelectedDateTime'),'yyyy-MM-ddThh:mm:ssZ')

The expression above formats the date and time in the format the Delay action requires.

Add a delay action and reference the output (i.e. converted time) from the convert time zone action.

Okay, here’s where the complexity begins so read carefully please:

Add three new variables (i.e. initialize variable) and name them accordingly.


varGroupIDs (i.e. GroupIDs), this will be utilized to store the existing group IDs assigned to a policy, we have to do this because currently Intune does not support adding a group to an existing set of groups assigned to a policy. If we attempt to do this through graph, all groups are cleared out and only the new addition will be added, not good when we’re just trying to add a group to the rest. If there are no existing groups, not a big deal. So this is where things start to get complex because I needed to reference existing policy assigned groups AND add the new scheduled group addition by constructing the JSON body on the fly to send back through a POST Graph call.

varBegin (i.e. Begin), as stated above, we need to construct the JSON body on the fly, this was tough, so I needed to get very creative in my process. We’ll use varBegin to essentially add a static “header” to the JSON. BTW a JSON body is required when adding groups to policies, more details here: Create deviceConfigurationAssignment – Microsoft Graph v1.0 | Microsoft Docs

varMiddle (i.e. varMiddle), this is where the “meat” of JSON group assignments live. Think of this as a hamburger, we have a top bun, some middle fixings, and a bottom bun. Top and Botton buns are static, where as the middle is not and needs to built on the fly. This was very challenging to work though and is why this flow is so lengthy.

Add an HTTP action with the following (as with my past posts, I reference a registered app in Azure AD with the proper API permissions to call the Intune Graph.):

Method = GET, URI = https://graph.microsoft.com/beta/deviceManagement/deviceConfigurations/@{variables('varSelectedPolicyID')}/assignments

Next add a Parse JSON action, if you run the GET query in Microsoft Graph Explorer, copy the output and use to “Generate from sample” as it will generate the JSON schema for you:

{
    "type": "object",
    "properties": {
        "@@odata.context": {
            "type": "string"
        },
        "value": {
            "type": "array",
            "items": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "string"
                    },
                    "source": {
                        "type": "string"
                    },
                    "sourceId": {
                        "type": "string"
                    },
                    "target": {
                        "type": "object",
                        "properties": {
                            "@@odata.type": {
                                "type": "string"
                            },
                            "deviceAndAppManagementAssignmentFilterId": {},
                            "deviceAndAppManagementAssignmentFilterType": {
                                "type": "string"
                            },
                            "groupId": {
                                "type": "string"
                            }
                        }
                    }
                },
                "required": [
                    "id",
                    "source",
                    "sourceId",
                    "target"
                ]
            }
        }
    }
}

Add a Compose action and reference the “groupId” in the statement below from the Parse JSON action, this will automatically add it to an Apply to each action which we want. This is where we’re storing all those existing groups if any exist.

{
  "target": {
    "@odata.type": "#microsoft.graph.groupAssignmentTarget",
    "groupId": "@{items('Apply_to_each')?['target']?['groupId']}"
  }
}

Under Compose add a Append array to variable action. We need to build out an array with existing groups, so reference the output from the compose action above.

Add another Append array to variable action outside of the Apply to each action. This is where we’ll add the new group selected in Power Apps to the existing groups via JSON.

So at this point we have an interesting looking JSON array with lots of “/” and “/n” etc. basically a string. We need to clean up the string and by adding a Compose action and adding the following expression:

join(variables('varMiddle'),',')

It’s time build a JSON hamburger!

Add five Compose actions…

TopBun – reference the “Begin” variable – this is the beginning static statement of the JSON.

Middle – reference the “ComposeJoinArray” output from above. This was build on the fly in the above steps.

BottomBun – add “] }” (no quotes) as static data to close out the JSON statement.

Hamburger – this completes the JSON hamburger by combining all three above outputs from the compose actions. YUM!

ConvertHamburgerToJSON – because the hamburger output is in the form of a string I needed to convert it back to JSON. Do this by adding the following to an expression:

json(outputs('Hamburger'))

Finally we need to make a POST Graph call using the output from ConvertHamburgerToJSON in the body of the HTTP action and using the following URI:

https://graph.microsoft.com/beta/deviceManagement/deviceConfigurations/@{variables('varSelectedPolicyID')}/assign

Full details of the flow are in the image below:

Power Apps

Create a new Power Apps canvas app and begin adding the items called out in the image below. The items not called out are just labels and icons.

AAD Group selector combo box and refresh AAD groups button: I pulled these from a previous post so I won’t go into detail as it’s identical as is the Power Automate flow behind it.

For the time picker, currently Power Automate provides a date picker object, however not a time selector so we need to create one from scratch. Rather than building one on my own I borrowed from the following post and it worked out really well: Make A Time Picker In Power Apps – Matthew Devaney This is the most complex item in the app, however Matthew’s blog does a great job of covering the details.

Add a label under the date and time boxes and under ACTION > Text add the following to combine the data & time:

DateSelector.SelectedDate & " " & Text('Time selector_cmp'.Value, ShortTime)

Add a button to set the time, I need to add this because setting the variable in the schedule button wasn’t providing me with the results I needed, so I opted to create a separate button instead. Not ideal, however it works well enough, we just have to remember to select it each time or the policy will apply immediately. Under ACTION > OnSelect add the following to set the variable which essentially this references the label below where we set the date and time.:

Set(varScheduleDateTime,'Selected DT value'.Text)

Add a textbox as well as a search icon. For the search icon change the DisplayMode property to “DisplayMode.View”, this essentially disables the hover over feature for the icon.

Add a data table, I utilized a data table vs a gallery because I like control over which columns and data shown. Add a data table and add the following to enable search from the search box and sorting from the sort icons:

ITEMS:

SortByColumns(Search(MEMPolices,Searchbox.Text,"displayName"),SortColumn, If(SortDecending,Ascending,Descending))

We need to tie the data table to a collection named “MEMPolicies” which is populated when the app is first launched via Power Automate, refer to the last step in this list.

Remember those three variables added to the flow created earlier? This where we’ll reference them. For the “Schedule” button add the following to call the flow to schedule the policy:

Schedulepolicyassignment.Run(varSelectedAADGroup,varScheduleDateTime,varSelectedPolicy)

For the app under ACTION > OnVisible add the following to call the flows to populate collections (note, your flows may have different names):

ClearCollect(MEMPolices,QueryMEMpolicies.Run());ClearCollect(AADGroupCollection,{displayName:""});Collect(AADGroupCollection, 'PowerApp->HTTP'.Run());Set(JSONVariable, JSON(AADGroupCollection, JSONFormat.IncludeBinaryData))


Conclusion

That’s it! Wow we just walked through creating a MEM Intune policy scheduling app utilizing Power Apps, Power Automate, and Graph.

One item to note, even though OEMConfig shows up under device config, it needs to be targeted as an app policy vs device config policy.