Microsoft Intune device configuration policy duplicator

I work a lot with organizations who have fleets of devices for their frontline workforce. Deployment of frontline workforce devices range from 100s to 1000s deployed across multiple locations. In some cases each location may have a separate device configuration, such as WiFi config, and so on. If an organization has many locations, some in the 100s, creating a special device configuration profile for each location is extremely time consuming, thus there is a desire to duplicate configurations to make the process easier. With Intune this can be accomplished programmatically utilizing the Intune Graph, however modeling after one of my previous posts on device group management, I opted to create a Power App to duplicate Intune device configuration policies.

In this months post I walk through how to create a device configuration policy duplicator for Intune utilizing Power Apps and Power Automate. With the app we can duplicate one or more device configuration policies and either utilize the existing policy name or prefix the policy name with some characters. For example, if the policy name is “Android device config” we can add a prefix such as “Store A -” so the duplicated profile will be created with the name of “Store A – Android device config”. Using the app the process the process of duplicating policies is fast and I was able to create multiple sets of policies with different prefixes in under a minute.

Let’s get started!

Requirements

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


Power Automate

To accomplish everything we need to do we’ll need to create three different Power Automate Flows:

  • Get device configuration profiles – pulls all of the device configuration profiles from Intune.
  • Duplicate device configuration profiles – duplicates one or more device configuration profiles.
  • Delete device configuration profiles – deletes one of more device configuration profiles.


Get device configuration profiles


Simple Flow here, at the top we have a PowerApps trigger, followed by HTTP action to pull all the device configuration policies from Intune using Graph. We then parse the JSON from the HTTP action, and finally add a Response action that Power Apps will create a collection from.

Graph query for Get device configurations HTTP action:

https://graph.microsoft.com/beta/deviceManagement/deviceConfigurations?$select=displayName,id,createdDateTime,lastModifiedDateTime

Parse JSON action:

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

For the Response action, add the “value” from Parse JSON action from the dynamic content.

Duplicate device configuration profiles


Again we start the Flow with a PowerApps trigger then initialize two variables which will be utilized to store prefix text and the configuration policy ID.

Initialize variable varPrefix: Select “Ask in PowerApps” from dynamic content.

Initialize variable varConfigPolicy: Select “Ask in PowerApps” from dynamic content.

Parse JSON: we need to take the selected policy IDs and convert to JSON as they’ll come in as an array from Power Apps. The content will be varConfigPolicy and the schema is shown below:

{
    "type": "array",
    "items": {
        "type": "object",
        "properties": {
            "id": {
                "type": "string"
            }
        },
        "required": [
            "id"
        ]
    }
}

Apply to each – in for each action, we need to do several things, first for each selected policy in Power Apps (e.g. policy ID), query Graph to pull the policies. Next we need to Parse the JSON returned from the queries because we must remove several JSON properties to write back as a new policy. I’ll step through each action below:

Get selected config profile (e.g. HTTP action) we’ll utilize the HTTP action to query each policy selected from Power Apps.

Parse JSON for get selected config profile – here we utilize the JSON from a Graph Explorer query to return all policies. I have a lot of policies so I pretty much have everything covered from a policy schema standpoint, however if you don’t you may need to update the schema from time to time. This should be fairly simple as you’ll run the same query in Graph Explorer and copy the JSON output of all the policies, delete the existing schema, and under Generate from sample, paste the JSON output from Graph Explorer. The schema is quite large so I opted not to paste it here, sorry.

Also, because Parse JSON actions don’t like null values, we need to edit the JSON schema and add in the capability to handles null values. Do this by finding and replacing in bulk the following using your favorite text editor. This should only be a one time process, unless the schema changes:

Replace of of the these types:

“type”: “string”

“type”: “boolean”

“type”: “integer”

With the following:

“type”: [“string”,”null”]

“type”: [“boolean”,”null”]

“type”: [“integer”,”null”]


Add 10 Compose actions – we’ll utilize the compose actions to manipulate the JSON for each policy returned.

Let’s split the compose action into groups so it’s easier to understand:

Convert odata.context to a new value of odatacontext

Because I need to use the “removeProperty” function to remove properties from the JSON output I need to convert from JSON to a string. The removeProperty function does not like “.” so we have to transform it to a string by removing the “.”.

Compose JSON to String:

string(body('Parse_JSON_for_get_selected_config_profile'))

Compose replace odatacontext:

replace(outputs('Compose_JSON_to_String'),'@odata.context','odatacontext')

Compose convert to JSON, all we’re doing here is converting the policy string back to JSON:

json(replace(outputs('Compose_replace_odatacontext'),'@odata.context','odatacontext'))


Remove JSON properties

To duplicate the policies we need to remove six properties from the JSON (odataconext, id, lastModifiedDateTime, supportsScopeTags, createdDateTime, and version):

Compose remove odatacontext:

removeProperty(outputs('Compose_convert_to_JSON'),'odatacontext')

 Compose remove id:

removeProperty(outputs('Compose_remove_odatacontext'),'id')

Compose remove lastModifiedDateTime:

removeProperty(outputs('Compose_remove_id'),'lastModifiedDateTime')

Compose remove supportsScopeTags:

removeProperty(outputs('Compose_remove_lastModifiedDateTime'),'supportsScopeTags')

Compose remove createdDateTime:

removeProperty(outputs('Compose_remove_supportsScopeTags'),'createdDateTime')


Compose remove version:

removeProperty(outputs('Compose_remove_createdDateTime'),'version')


Combine prefix with display name and create duplicate policies

Compose combine prefix and displayName. Here we need to combine the prefix (if one exists) with the existing display name of the policy. If there is no prefix provided, we’ll utilize the existing display name, however this can get confusing so I recommend adding a prefix:

setProperty(outputs('Compose_remove_version'),'displayName',concat(variables('varPrefix'),' ',body('Parse_JSON_for_get_selected_config_profile')?['displayName']))

Create copy of profile (HTTP action):

https://graph.microsoft.com/beta/deviceManagement/deviceConfigurations/

For the body, select the “Outputs” from “Compose combine prefix and displayName” action.


Delete device configuration profiles


As usual, at the top we have a PowerApps trigger, however we need to create a variable to store the policy IDs selected from Power Apps. We then add a Parse JSON action to create an array of policy IDs after which we’ll utilize an HTTP action to in a apply to each loop (this will be auto generated once ID is selected in the HTTP action), to delete all the selected profiles.

Add initialize variable action and give it a name. Then select “Ask in PowerApps” from dynamic content. This will store selected device configuration policy IDs to be utilized in the delete Graph query.

Add a Parse JSON action referencing the variable created above and add the following schema:

{
    "type": "array",
    "items": {
        "type": "object",
        "properties": {
            "id": {
                "type": "string"
            }
        },
        "required": [
            "id"
        ]
    }
}

Add a HTTP action (Delete device configurations HTTP action) and at the end of the URI, choose “id” from dynamic content:

https://graph.microsoft.com/beta/deviceManagement/deviceConfigurations/@{items('Apply_to_each')['id']}

Power Apps

Create a new Power App canvas app and let’s step through each field in the form:

App screen: under ACTION > OnVisible add the code below. What’s it’s doing is calling the Get device configuration profile Power Automate Flow adding it to a collection, then it’s clearing any existing selections. The UpdateContext values are both populated under the Checkbox DATA fields which is explained below.

ClearCollect(deviceConfigCollection,Getdeviceconfigurations.Run());Set(JSONVariable,JSON(deviceConfigCollection,JSONFormat.IncludeBinaryData));Clear(selectedconfigcollection);UpdateContext({ClearCheckbox:true});UpdateContext({ClearCheckbox:false});UpdateContext({CheckCheckbox:true});UpdateContext({CheckCheckbox:false})

Search text box: Nothing special here, just add a textbox and rename it if you’d like. We’ll reference it within the Gallery.

Prefix value text box: Nothing special here, just add a textbox and rename it if you’d like. If populated, we’ll return the value along with the selected device payload from the Gallery.

Gallery: add the following under DATA > Items, this where the view changes when text is entered in the search text box and the sort icons are selected:

SortByColumns(Search(deviceConfigCollection,Searchbox.Text,"displayName","id","createdDateTime","lastModifiedDateTime"),SortColumn, If(SortDecending,Ascending,Descending))

Gallery labels: because we have a blank Gallery, select within the gallery and start adding labels. You’ll see all the proper label headers added from the collection referenced (e.g. deviceConfigCollection).

Checkbox: We want to create a new collection to collect selected policies. Select the Gallery and add a checkbox. This will automatically add a checkbox for each item returned to the collection:

Populate OnCheck with:

Collect(selectedconfigcollection,ThisItem)

Populate OnUncheck with:

Remove(selectedconfigcollection,ThisItem)

Under DATA > Default add:

CheckCheckbox

Under DATA > Reset add:

ClearCheckbox

Sort icons (these live outside of the Gallery as do the label headers for each column) add a sort icon for each column:

Under ACTION > OnSelect add:

UpdateContext({SortColumn: "displayName",SortDecending: !SortDecending})

Refresh list button:

Under ACTION > OnSelect add the following which is identical to app screen OnVisible:

ClearCollect(deviceConfigCollection,Getdeviceconfigurations.Run());Set(JSONVariable,JSON(deviceConfigCollection,JSONFormat.IncludeBinaryData));Clear(selectedconfigcollection);UpdateContext({ClearCheckbox:true});UpdateContext({ClearCheckbox:false})

Delete config button:

References the delete configuration policy Power Automate Flow. Select the button and under Action select Power Automate, then fill out the remainder of the statement under ACTION > OnSelect:

Deleteconfigurations.Run(JSON(selectedconfigcollection.id,JSONFormat.IndentFour))

Duplicate button:

Reference the duplicate device configuration policy Power Automate Flow. Select the button and under Action select Power Automate, then fill out the remainder of the statement under ACTION > OnSelect:

Copyconfigurationprofile.Run(JSON(selectedconfigcollection.id,JSONFormat.IndentFour),'TextInput prefix'.Text)

Note: Do not select any OEMConfig profiles, technically those are app config profiles so duplication in Power Automate will fail. There may be other policies that cannot be duplicated programmatically as well, for example I noticed the Windows 10 “Edition upgrade and mode switch” policy could not be duplicated, probably wouldn’t need to do this anyway as it’s only two settings. Most policies across multiple platforms I tested can be duplicated programmatically though, just test things out.

Conclusion
That’s it! We created a Power Automate Flow to pull and duplicate Intune device configuration profiles with an optional prefix, delete profiles, as well as utilized Power Apps to create a UI to accomplish the task. This will no doubt make duplicating device configuration policies a simple process. Use what you’ve learned here and with my previous posts to create something unique and tweet out to @mscloudinfra.