Changes to Exporting Windows 10 Custom Policies
Microsoft recently made a change to the way that Windows 10 Custom policies are stored in and queried from the Microsoft Graph. Some elements of custom policies are stored as encrypted values. You can see this clearly by using Graph Explorer [LINK] to query a single custom policy.
The encrypted values do not show the value in plain text. Instead you see a secretReferenceValueId that can be used to access the plain text value.
Why this is a problem
Many of us use the Intune PowerShell sample scripts to export and import policies in Microsoft Endpoint Manager [LINK]. These scripts have worked well for years but since the change to Microsoft Graph in August 2021, the custom policies do not export the encrypted values.
The export works perfectly but when you try and import policies then you get either get a terminating error or you get policies that are imported with plain text values.
So far, this issue only affects Windows 10 Custom Configuration profiles but I expect this issue to spread to other policies over time.
How can this be fixed
I have re-written the Export-DeviceConfiguration PowerShell script to introduce special handling for Windows 10 Custom Configuration policies. The re-written script uses the new graph function getOmaSettingPlainTextValue(secretReferenceValueId='$SecretID') to retrieve any encrypted values.
You can obtain the updated script from my GitHub repository [LINK].
How it Works
I have introduced an If statement into the export section of the main routine.
foreach($DCP in $DCPs){
write-host "Device Configuration Policy:"$DCP.displayName -f Yellow
If($DCP.'@odata.type' -eq "#microsoft.graph.windows10CustomConfiguration") {
Write-Host "Preparing Device Configuration Policy for Export"
The main routine introduces special handling for Windows 10 Custom Configuration policies. The settings that are exported from Graph are parsed and inserted into a new array.
$NewOMASettingArray = New-Object System.Collections.ArrayList
Foreach ($OMASetting in $DCP.omaSettings) {
$NewOMASetting = [pscustomobject][ordered] @{
"@odata.type" = $OMASetting.'@odata.type'
"displayName" = $OMASetting.displayName
"description" = $OMASetting.description
"omaUri" = $OMASetting.omaUri
"value" = ""
}
If($OMASetting.isEncrypted -eq $false) {
$NewOMASetting.Value = $OMASetting.value
} else {
#Get the secret value
$PlainTextValue = Get-SecretValue -Guid $DCP.ID -SecretID $OMASetting.secretReferenceValueId
$NewOMASetting.Value = $PlainTextValue
}
$NewOMASettingArray.add($NewOMASetting) | out-Null
}
If the settings are encrypted then a new function Get-SecretValue is used to retrieve the value.
Finally the device configuration policy is reconstructed in the same format using the array list of plain text values and exported.
#Reconstruct the DCP
$NewDCP = [pscustomobject][ordered] @{
"@odata.type" = $DCP.'@odata.type'
"description" = $DCP.Description
"displayName" = $DCP.DisplayName
"omaSettings" = $NewOMASettingArray
}
The Get-SecretValue function is shown below. The function uses the getOmaSettingPlainTextValue(secretReferenceValueId='$SecretID') method to retrieve the plain text values.
Function Get-SecretValue(){
<#
.SYNOPSIS
This function is used to retrieve a secret value
.DESCRIPTION
This function is used to retrieve a secret value when a device configuration setting is encrypted
.EXAMPLE
Get-SecretValue -GUID $guid -SecretID $SecretID
.NOTES
NAME: Get-SecretValue
#>
param (
[Parameter(Mandatory=$true)]$GUID,
[Parameter(Mandatory=$true)]$SecretID
)
$graphApiVersion = "Beta"
$DCP_resource = "deviceManagement/deviceConfigurations"
try {
$uri = "https://graph.microsoft.com/$graphApiVersion/$($DCP_resource)/$GUID/getOmaSettingPlainTextValue(secretReferenceValueId='$SecretID')"
(Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get).Value
} catch {
$ex = $_.Exception
$errorResponse = $ex.Response.GetResponseStream()
$reader = New-Object System.IO.StreamReader($errorResponse)
$reader.BaseStream.Position = 0
$reader.DiscardBufferedData()
$responseBody = $reader.ReadToEnd();
Write-Host "Response content:`n$responseBody" -f Red
Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
write-host
break
}
}
Conclusion
Microsoft are “encouraging” customers to switch to using a mixture of endpoint security baselines and settings catalog policies rather than using traditional MDM policies. That advice is driven by the lowest common denominator customers and does not fit enterprise usage. Enterprise customers still want to be able to version control policies, which means export and import of known settings in traditional policies.
For now, we are going to have to watch and wait to see what changes next. If other policies switch to using encrypted values then I will update the export script to include other configuration policy types.