Backup and Import Intune Device Configuration Profiles
Why backup Device Configuration Profiles?
Why would you need to back up something that runs in stored and hosted on Azure? Well there are numerous answers to this question really.
- If you make a change and you break something you can look back and analyse what it was
- You can make copies of the policy easily rather than having two windows side by side
- In case one is deleted... (Let's hope this is never the case)
- To review the maturity of your policies (Lets say you started from ground zero and now have over 100 policy settings... Might be nice to review what you've done)
Well when using Intune there was no way to export or backup your profiles or policies from the console… I have seen people taking screenshots of the pages as a backup of the policies which is far from an ideal scenario.
What if I told you there is a way you can back-up your configuration policies using the Microsoft Graph API?? Well it’s possible and it’s easier than you think.
This is the first of a series of guides on how to backup and import different types of policies and profiles using the API. This one will be focusing on Device Configuration Profiles.
The Script
You will notice that most of this (the authentication part and most of the param block at least) are the same as my other script… But if its not broke why fix it? (Those famous last words!!!). Although this script does have an alternative run method, if you run it directly without the ClientID, ClientSecret and TenantID parameters it will install the Azure AD Powershell module and use a custom Function Connect-AzAD_Token to enable users to interact with a login Window if they do not wish to use Azure AD App Registrations with client secrets.
This script can be run from anywhere, as a user, as an Admin or even as System. You could put this into an Automation Engine to do backups on a schedule if that is your desire.
param(
[Parameter(DontShow = $true)]
[string]
$MsGraphVersion = "beta",
[Parameter(DontShow = $true)]
[string]
$MsGraphHost = "graph.microsoft.com",
#The AzureAD ClientID (Application ID) of your registered AzureAD App
[string]
$ClientID,
#The Client Secret for your AzureAD App
[string]
$ClientSecret,
#Your Azure Tenent ID
[string]
$TenantId,
[Parameter()]
[string]
$OutputFolder = ".\ConfigurationProfileBackup",
[switch]
$Import,
[string]
$importJSON
)
FUNCTION Connect-AzAD_Token {
Write-Host -ForegroundColor Cyan "Checking for AzureAD module..."
$AADMod = Get-Module -Name "AzureAD" -ListAvailable
if (!($AADMod)) {
Write-Host -ForegroundColor Yellow "AzureAD PowerShell module not found, looking for AzureADPreview"
$AADModPrev = Get-Module -Name "AzureADPreview" -ListAvailable
#Check to see if the AzureAD Preview Module is insalled, If so se that as the AAD Module Else Insall the AzureAD Module
IF ($AADModPrev) {
$AADMod = Get-Module -Name "AzureADPreview" -ListAvailable
} else {
try {
Write-Host -ForegroundColor Yello "AzureAD Preview is not installed..."
Write-Host -ForegroundColor Cyan "Attempting to Install the AzureAD Powershell module..."
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -ErrorAction Stop | Out-Null
Install-Module AzureAD -Force -ErrorAction Stop
}
catch {
Write-Host -ForegroundColor Red "Failed to install the AzureAD PowerShell Module `n $($Error[0])"
break
}
}
} else {
Write-Host -ForegroundColor Green "AzureAD Powershell Module Found"
}
$AADMod = ($AADMod | Select-Object -Unique | Sort-Object)[-1]
$ADAL = Join-Path $AADMod.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.dll"
$ADALForms = Join-Path $AADMod.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll"
[System.Reflection.Assembly]::LoadFrom($ADAL) | Out-Null
[System.Reflection.Assembly]::LoadFrom($ADALForms) | Out-Null
$UserInfo = Connect-AzureAD
# Microsoft Intune PowerShell Enterprise Application ID
$MIPEAClientID = "d1ddf0e4-d672-4dae-b554-9d5bdfd93547"
# The redirectURI
$RedirectURI = "urn:ietf:wg:oauth:2.0:oob"
#The Authority to connect with (YOur Tenant)
Write-Host -Foregroundcolor Cyan "Connected to Tenant: $($UserInfo.TenantID)"
$Auth = "https://login.microsoftonline.com/$($UserInfo.TenantID)"
try {
$AuthContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList $Auth
# https://msdn.microsoft.com/en-us/library/azure/microsoft.identitymodel.clients.activedirectory.promptbehavior.aspx
# Change the prompt behaviour to force credentials each time: Auto, Always, Never, RefreshSession
$platformParameters = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.PlatformParameters" -ArgumentList "Auto"
$userId = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.UserIdentifier" -ArgumentList ($UserInfo.Account, "OptionalDisplayableId")
$authResult = $AuthContext.AcquireTokenAsync(("https://" + $MSGraphHost),$MIPEAClientID,$RedirectURI,$platformParameters,$userId).Result
# If the accesstoken is valid then create the authentication header
if($authResult.AccessToken){
# Creating header for Authorization token
$AADAccessToken = $authResult.AccessToken
return $AADAccessToken
} else {
Write-Host -ForegroundColor Red "Authorization Access Token is null, please re-run authentication..."
break
}
}
catch {
Write-Host -ForegroundColor Red $_.Exception.Message
Write-Host -ForegroundColor Red $_.Exception.ItemName
break
}
}
# Web page used to help with getting the access token
#https://morgantechspace.com/2019/08/get-graph-api-access-token-using-client-id-and-client-secret.html
if (($ClientID) -and ($ClientSecret) -and ($TenantId) ) {
#Create the body of the Authentication of the request for the OAuth Token
$Body = @{client_id=$ClientID;client_secret=$ClientSecret;grant_type="client_credentials";scope="https://$MSGraphHost/.default";}
#Get the OAuth Token
$OAuthReq = Invoke-RestMethod -Method Post -Uri "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token" -Body $Body
#Set your access token as a variable
$global:AccessToken = $OAuthReq.access_token
} else {
$global:AccessToken = Connect-AzAD_Token
}
if ($Import)
{
IF ($ImportJSON){
#$JSON = GET-Content $ImportJSON
$JSON = Get-Content $ImportJSON | ConvertFrom-Json | Select-Object -Property * -ExcludeProperty Version,LastModifiedTime,CreatedDateTime,id,supportsScopeTags | ConvertTo-Json
Invoke-RestMethod -Method POST -Uri "https://$MSGraphHost/$MsGraphVersion/deviceManagement/deviceConfigurations" -Headers @{Authorization = "Bearer $AccessToken"} -Body $JSON -ContentType "application/json"
} else {
Write-Host -ForegroundColor RED "You must specify an a JSON file using the -ImportJSON parameter"
}
} else {
$FormattedOutputFolder = "$OutputFolder\$(Get-Date -Format yyyyMMdd_HH-mm-ss)"
IF (!(Test-Path $FormattedOutputFolder)){
try {
mkdir $FormattedOutputFolder -ErrorAction Stop | Out-Null
}
catch {
Write-Host -ForegroundColor Red "Failed to create $FormattedOutputFolder"
$Error[0]
break
}
}
Invoke-RestMethod -Method GET -Uri "https://$MSGraphHost/$MsGraphVersion/deviceManagement/deviceConfigurations" -Headers @{Authorization = "Bearer $AccessToken"} | Select-Object -ExpandProperty "Value" | %{
$_ | ConvertTo-Json | Out-File "$FormattedOutputFolder\$($_.displayname).json"
}
}
Pre-Reqs
Azure AD App Registration
To make the script work without any interaction you will need an Azure App Registration with the following permissions for the Microsoft Graph API.
Backing Up Device Configuration Profiles
- DeviceManagementConfiguration.Read.All (Application Permission)
Importing Device Configuration Profiles
- DeviceManagementConfiguration.ReadWrite.All (Application Permission)
If you are not executing the script directly, you will also need the Tenant ID and the account that the script will be running as will need permission to the Output folder for backups.
If your not sure how to create an Azure AD App Registration head over to one of my other posts by clicking HERE, Don’t forget to store your Client ID and Secret securely and also have it to hand for the rest of the post :D.
Executing the Script
Using Azure AD App Registrations
You can run this script directly from a PowerShell console, using Task Scheduler or using a 3rd party automation product that supports PowerShell.
The main thing we will go through here is just the parameters and then putting them all together from the command line, it’s really that simple.
For Backup Only
- Client ID: This is the Client ID for your Azure AD App
- ClientSecret: The Client Secret for the Azure AD App
- TenantID: Your Azure Tenant ID
- OutputFolder: Your desired Output folder
For Importing Policies
- Client ID: This is the Client ID for your Azure AD App
- ClientSecret: The Client Secret for the Azure AD App
- TenantID: Your Azure Tenant ID
- Import: This is a switch parameter which states if your intention is to import or not
- ImportJSON: the path to your JSON file.
./Backup_DeviceConfigurationPolicies.ps1 -ClientID “” -ClientSecret “” -TenantID “” -Import -ImportJSON “./YourServerBackups/ConfigurationPolicies/ImportMe.JSON”
Direct Execution
If you launch the script without the Client ID, Secret and Tenant ID you will be prompted with a Microsoft Logon Window similar to the below.![](/images/post/backupconfigprofile/MSGraphWindow_hu3473e43f0886b317d217ed86cd989e3b_12794_565x0_resize_q100_h2_box_3.webp)
Once you login the script will continue to run and then output the configuration files in the same way it would using the App Registration. You will need an account with permissions to be able to read (for backups only) or Read and Write the Device Configuration Profiles. However the likelihood is that if you are looking at this guide you are probably an Intune Service Administrator or Global Administrator on your Tenant.
When you run it directly without any switches the script will prompt you to log in and it would only perform a backup of your profiles and output the configurations to the the folder you are executing it from.
If you add the -OutputFolder parameter you can change the destination of the base output folder. However if you are wishing to use the script to Import policies you can add the -Import and -ImportJSON parameters, If you specify the -Import parameter you must also specify the -ImportJSON parameter with a path to the JSON file (e.g. C:/ImportMe.json) otherwise the script will display a message that you did not specify the -ImportJSON Parameter.
You will notice that when you run the script, if the folder does not exist it will create it. It also put its into a dated folder in the yyyyMMdd_HH-mm-ss format leaving you with something like 20200901_16_05_36
Summary
This can also be useful if you are wanting to make a copy of your policies to assign to a test machine. All you will need to do is backup your current policies and amend the JSON file, If you find the displayName field in the JSON file and amend it and save the file you will be able to re-import this the same settings. All you then need to do is assign it.
I have tested this myself at the time of writing the post but if you come across any information you think may be wrong then please leave a comment or e-mail me on [email protected].