├── CHANGELOG.md ├── IntuneBackupAndRestore ├── IntuneBackupAndRestore.psd1 ├── IntuneBackupAndRestore.psm1 ├── Private │ └── Get-MGGraphAllPages.ps1 └── Public │ ├── Compare-IntuneBackupDirectories.ps1 │ ├── Compare-IntuneBackupFile.ps1 │ ├── Invoke-IntuneBackupAppProtectionPolicy.ps1 │ ├── Invoke-IntuneBackupAppProtectionPolicyAssignment.ps1 │ ├── Invoke-IntuneBackupAutopilotDeploymentProfile.ps1 │ ├── Invoke-IntuneBackupAutopilotDeploymentProfileAssignment.ps1 │ ├── Invoke-IntuneBackupClientApp.ps1 │ ├── Invoke-IntuneBackupClientAppAssignment.ps1 │ ├── Invoke-IntuneBackupConfigurationPolicy.ps1 │ ├── Invoke-IntuneBackupConfigurationPolicyAssignment.ps1 │ ├── Invoke-IntuneBackupDeviceCompliancePolicy.ps1 │ ├── Invoke-IntuneBackupDeviceCompliancePolicyAssignment.ps1 │ ├── Invoke-IntuneBackupDeviceConfiguration.ps1 │ ├── Invoke-IntuneBackupDeviceConfigurationAssignment.ps1 │ ├── Invoke-IntuneBackupDeviceHealthScript.ps1 │ ├── Invoke-IntuneBackupDeviceHealthScriptAssignment.ps1 │ ├── Invoke-IntuneBackupDeviceManagementIntent.ps1 │ ├── Invoke-IntuneBackupDeviceManagementScript.ps1 │ ├── Invoke-IntuneBackupDeviceManagementScriptAssignment.ps1 │ ├── Invoke-IntuneBackupGroupPolicyConfiguration.ps1 │ ├── Invoke-IntuneBackupGroupPolicyConfigurationAssignment.ps1 │ ├── Invoke-IntuneRestoreAppProtectionPolicy.ps1 │ ├── Invoke-IntuneRestoreAppProtectionPolicyAssignment.ps1 │ ├── Invoke-IntuneRestoreAutopilotDeploymentProfile.ps1 │ ├── Invoke-IntuneRestoreAutopilotDeploymentProfileAssignment.ps1 │ ├── Invoke-IntuneRestoreClientAppAssignment.ps1 │ ├── Invoke-IntuneRestoreConfigurationPolicy.ps1 │ ├── Invoke-IntuneRestoreConfigurationPolicyAssignment.ps1 │ ├── Invoke-IntuneRestoreDeviceCompliancePolicy.ps1 │ ├── Invoke-IntuneRestoreDeviceCompliancePolicyAssignment.ps1 │ ├── Invoke-IntuneRestoreDeviceConfiguration.ps1 │ ├── Invoke-IntuneRestoreDeviceConfigurationAssignment.ps1 │ ├── Invoke-IntuneRestoreDeviceHealthScript.ps1 │ ├── Invoke-IntuneRestoreDeviceHealthScriptAssignment.ps1 │ ├── Invoke-IntuneRestoreDeviceManagementIntent.ps1 │ ├── Invoke-IntuneRestoreDeviceManagementScript.ps1 │ ├── Invoke-IntuneRestoreDeviceManagementScriptAssignment.ps1 │ ├── Invoke-IntuneRestoreGroupPolicyConfiguration.ps1 │ ├── Invoke-IntuneRestoreGroupPolicyConfigurationAssignment.ps1 │ ├── Start-IntuneBackup.ps1 │ ├── Start-IntuneRestoreAssignments.ps1 │ └── Start-IntuneRestoreConfig.ps1 ├── LICENSE └── README.md /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [4.0.0] - 2025-01-07 8 | - Updated to Microsoft.Graph PowerShell Module. Special thanks to @mhu4711 9 | - Added support for backing up and restoring Autopilot Deployment Profiles. 10 | - Added support for restoring Proactive Remediations. 11 | 12 | ## [3.2.0] - 2021-12-10 13 | - Added support for backing up Proactive Remediations. Special thanks to @ztrhgf and @Kosipeich 14 | - Added support for backing up Proactive Remediation Assignments. Special thanks to @ztrhgf and @Kosipeich 15 | - Added additional improvements for Comparing Files and Directories. Special thanks to @ztrhgf 16 | - Added an additional check for encrypted OMA-URI settings. 17 | 18 | ## [3.1.1] - 2021-08-19 19 | - Added check if Custom OMA-URI is encrypted before attempting to decrypt. 20 | 21 | ## [3.1.0] - 2021-08-12 22 | - Microsoft has started to encrypt Custom OMA-URI values in Device Configuration profiles. Because encrypted values are now stored in newer backups, restoration fails. This update decrypts those values prior to backing up the profile, enabling restoration again. 23 | 24 | ## [3.0.1] - 2021-06-21 25 | - Minor bug fixes. 26 | 27 | ## [3.0.0] - 2021-06-17 28 | - Added support for backing up and restoring App Protection Policy assignments. 29 | - Added support for backing up and restoring Settings Catalog profiles. 30 | - Added support for backing up and restoring Settings Catalog assignments. 31 | - Updated backup and restore output using a [PSCustomObject]. 32 | - Updated JSON depth across all functions for uniformity and to prevent (future) issues depending on the data that is being backed up/restored. 33 | - Updated Device Management Intents, such as Windows 10 Security Baselines, where backups use a shorter filename, as users could run into issues with too long filepaths. 34 | - Fixed an issue that would backup non-configured apps. (#38) 35 | 36 | 37 | ## [2.1.1] - 2021-02-20 38 | - Fixed an issue where some properties in certain configurations, such as iOS Device restrictions, would not back-up, and would result in a failure during restore. 39 | 40 | ## [2.1.0] - 2021-02-17 41 | Special thanks to @sleeuwenhoek pull #23 42 | - Added function `Invoke-IntuneBackupAppProtectionPolicy`. 43 | - Added function `Invoke-IntuneRestoreDeviceManagementIntent`. 44 | 45 | ## [2.0.0] - 2020-06-15 46 | ### BREAKING 47 | - Refactored the IntuneBackupAndRestore module to depend on Microsoft.Graph.Intune module, instead of the custom MSGraphFunctions module. 48 | - Fixed an issue where restoring assignments could result in an error. Now also supports restoring assignments for Line-of-Business Client Apps. 49 | 50 | 51 | ## [1.5.0] - 2020-04-02 52 | - Added function `Invoke-IntuneBackupDeviceManagementIntent`. 53 | - Added function `Invoke-IntuneRestoreDeviceManagementIntent`. 54 | - Embedded `Invoke-IntuneBackupDeviceManagementIntent` in the `Start-IntuneBackup` cmdlet. 55 | - Embedded `Invoke-IntuneRestoreDeviceManagementIntent` in the `Start-IntuneRestoreConfig` cmdlet. 56 | 57 | ## [1.4.3] - 2019-12-22 58 | - Added `Compare-IntuneBackupDirectories` to compare backup files from two backup sets. Co-authored-by: [Bradley Wyatt](https://github.com/bwya77) 59 | - Fixed an issue with `Compare-IntuneBackupFile`, which would ignore JSON files that had no depth. 60 | 61 | ## [1.4.2] - 2019-07-19 62 | - Update `Invoke-IntuneBackupClientApp` to include details such as detection rules, requirement rules and return codes. 63 | 64 | ## [1.4.1] - 2019-07-19 65 | - Embedded function `Invoke-IntuneBackupClientApp` in the `Start-IntuneBackup` cmdlet. 66 | 67 | ## [1.4.0] - 2019-07-19 68 | - Added function `Invoke-IntuneBackupClientApp`. Now supports backing up Intune Client App configurations. 69 | 70 | ## [1.3.2] - 2019-06-19 71 | - Fixed an issue where Invoke-IntuneREstoreGroupPolicyAssignment was not recognized as the name of a cmdlet. 72 | 73 | ## [1.3.1] - 2019-05-01 74 | - Fixed an issue introduced in v1.3.0 where Device Management Script Content would not be saved, because the file name was $null. 75 | 76 | ## [1.3.0] - 2019-04-29 77 | - Fixed issue [#5 - Compare-IntuneBackupFile does not compare sub properties](https://github.com/jseerden/IntuneBackupAndRestore/issues/5) 78 | - Fixed known issue: Intune Configurations that contain characters in their display name that are known to be invalid file name characters for Desktop Operating Systems are now backed up without error. Specifically, the invalid characters are replaced with underscores `_`, restores however use the displayName field available in the exported JSON. 79 | - Fixed issue [#4 - Policies with brackets in their name cannot be saved](https://github.com/jseerden/IntuneBackupAndRestore/issues/4) 80 | - Fixed typo: `Succesfully` is now displayed as `Successfully`. 81 | 82 | ## [1.2.1] - 2019-04-12 83 | ### Fixed 84 | - Fixed an issue where backing up Group Policy Configurations (Administrative Templates) would generate incorrect JSON output for several settings. 85 | 86 | ## [1.2.0] - 2019-04-11 87 | ### Added 88 | - Backing up Group Policy Configurations (Administrative Templates) added. 89 | - Backing up Group Policy Configuration (Administrative Template) Assignments added. 90 | - Restoring Group Policy Configurations (Administrative Templates) added. 91 | - Restoring Group Policy Configuration (Administrative Template) Assignments added. 92 | 93 | ## [1.1.0] - 2019-03-17 94 | ### Added 95 | - Added function `Compare-IntuneBackupFile` 96 | 97 | ## [1.0.1] - 2019-03-17 98 | ### Changed 99 | - Fixed exported cmdlets in Module Manifest 100 | 101 | ## [1.0.0] - 2019-03-15 102 | ### Added 103 | - PowerShell module initial release 104 | - CHANGELOG file 105 | - README file 106 | - LICENSE -------------------------------------------------------------------------------- /IntuneBackupAndRestore/IntuneBackupAndRestore.psd1: -------------------------------------------------------------------------------- 1 | # 2 | # Module manifest for module 'PSGet_IntuneBackupAndRestore' 3 | # 4 | # Generated by: John Seerden 5 | # 6 | # Generated on: 3/15/2019 7 | # 8 | 9 | @{ 10 | 11 | # Script module or binary module file associated with this manifest. 12 | RootModule = 'IntuneBackupAndRestore.psm1' 13 | 14 | # Version number of this module. 15 | ModuleVersion = '4.0.0' 16 | 17 | # Supported PSEditions 18 | # CompatiblePSEditions = @() 19 | 20 | # ID used to uniquely identify this module 21 | GUID = 'c4e21603-4445-4fe1-b202-3653b6b2e666' 22 | 23 | # Author of this module 24 | Author = 'John Seerden' 25 | 26 | # Company or vendor of this module 27 | CompanyName = 'Unknown' 28 | 29 | # Copyright statement for this module 30 | Copyright = '(c) 2025 John Seerden. All rights reserved.' 31 | 32 | # Description of the functionality provided by this module 33 | Description = 'PowerShell Module that queries Microsoft Graph, and allows for cross-tenant Backup & Restore actions of your Intune Configuration. ' 34 | 35 | # Minimum version of the Windows PowerShell engine required by this module 36 | # PowerShellVersion = '' 37 | 38 | # Name of the Windows PowerShell host required by this module 39 | # PowerShellHostName = '' 40 | 41 | # Minimum version of the Windows PowerShell host required by this module 42 | # PowerShellHostVersion = '' 43 | 44 | # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. 45 | # DotNetFrameworkVersion = '' 46 | 47 | # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. 48 | # CLRVersion = '' 49 | 50 | # Processor architecture (None, X86, Amd64) required by this module 51 | # ProcessorArchitecture = '' 52 | 53 | # Modules that must be imported into the global environment prior to importing this module 54 | # RequiredModules = @(@{ModuleName="MSGraphFunctions", ModuleVersion="2.2.0", Guid="0a3f3df4-64a0-430a-937d-a9b1901349ce"}) 55 | 56 | # Assemblies that must be loaded prior to importing this module 57 | # RequiredAssemblies = @() 58 | 59 | # Script files (.ps1) that are run in the caller's environment prior to importing this module. 60 | # ScriptsToProcess = @() 61 | 62 | # Type files (.ps1xml) to be loaded when importing this module 63 | # TypesToProcess = @() 64 | 65 | # Format files (.ps1xml) to be loaded when importing this module 66 | # FormatsToProcess = @() 67 | 68 | # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess 69 | # NestedModules = @() 70 | 71 | # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. 72 | FunctionsToExport = '*' 73 | 74 | # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. 75 | CmdletsToExport = '*' 76 | 77 | # Variables to export from this module 78 | VariablesToExport = '*' 79 | 80 | # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. 81 | AliasesToExport = @() 82 | 83 | # DSC resources to export from this module 84 | # DscResourcesToExport = @() 85 | 86 | # List of all modules packaged with this module 87 | # ModuleList = @() 88 | 89 | # List of all files packaged with this module 90 | # FileList = @() 91 | 92 | # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. 93 | PrivateData = @{ 94 | 95 | PSData = @{ 96 | 97 | # Tags applied to this module. These help with module discovery in online galleries. 98 | Tags = @("Graph", "Intune", "PowerShell", "Backup", "Restore") 99 | 100 | # A URL to the license for this module. 101 | LicenseUri = 'https://github.com/jseerden/IntuneBackupAndRestore/blob/master/LICENSE' 102 | 103 | # A URL to the main website for this project. 104 | # ProjectUri = '' 105 | 106 | # A URL to an icon representing this module. 107 | # IconUri = '' 108 | 109 | # ReleaseNotes of this module 110 | # ReleaseNotes = '' 111 | 112 | # External dependent modules of this module 113 | # ExternalModuleDependencies = '' 114 | 115 | } # End of PSData hashtable 116 | 117 | } # End of PrivateData hashtable 118 | 119 | # HelpInfo URI of this module 120 | # HelpInfoURI = '' 121 | 122 | # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. 123 | # DefaultCommandPrefix = '' 124 | 125 | } 126 | 127 | -------------------------------------------------------------------------------- /IntuneBackupAndRestore/IntuneBackupAndRestore.psm1: -------------------------------------------------------------------------------- 1 | $Public = @(Get-ChildItem -Path $PSScriptRoot\Public\*.ps1 -ErrorAction SilentlyContinue) 2 | $Private = @(Get-ChildItem -Path $PSScriptRoot\Private\*.ps1 -ErrorAction SilentlyContinue) 3 | 4 | foreach ($Import in @($Public + $Private)) { 5 | try { 6 | . $Import.Fullname -ErrorAction Stop 7 | } 8 | catch { 9 | Write-Error -Message "Failed to import function $($Import.Fullname): $_" -ErrorAction Continue 10 | } 11 | } 12 | 13 | Export-ModuleMember -Function $Public.Basename -------------------------------------------------------------------------------- /IntuneBackupAndRestore/Private/Get-MGGraphAllPages.ps1: -------------------------------------------------------------------------------- 1 | function Get-MGGraphAllPages { 2 | <# 3 | .SYNOPSIS 4 | Retrieve all pages of a Microsoft Graph Query 5 | 6 | .DESCRIPTION 7 | Retrieve all pages of a Microsoft Graph Query 8 | 9 | .PARAMETER GraphResults 10 | Microsoft Graph Query Results 11 | 12 | .EXAMPLE 13 | Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/beta/deviceManagement/deviceConfigurations" | Get-MGGraphAllPages 14 | 15 | #> 16 | 17 | [CmdletBinding()] 18 | param ( 19 | [Parameter(ValueFromPipeline)]$GraphResults 20 | ) 21 | $uri = $null 22 | $QueryResults = @() 23 | do { 24 | if($uri){$GraphResults = Invoke-MgGraphRequest -uri "$uri"} 25 | if ($GraphResults.value) { 26 | $QueryResults += $GraphResults.value 27 | } 28 | else { 29 | $QueryResults += $GraphResults 30 | } 31 | $uri = $GraphResults.'@odata.nextlink' 32 | } until (!($uri)) 33 | 34 | #Check for null Value 35 | if(($QueryResults.count -eq 2) -and ([string]::IsNullOrEmpty($QueryResults.value)) -and ($QueryResults.'@odata.context' -match "https://graph.microsoft.com/")) { 36 | $QueryResults = $null 37 | } 38 | 39 | return $QueryResults 40 | 41 | } -------------------------------------------------------------------------------- /IntuneBackupAndRestore/Public/Compare-IntuneBackupDirectories.ps1: -------------------------------------------------------------------------------- 1 | function Compare-IntuneBackupDirectories() { 2 | <# 3 | .SYNOPSIS 4 | Compare two Intune Backup Directories for changes in each of their JSON backup files. 5 | 6 | .DESCRIPTION 7 | Compare two Intune Backup Directories for changes. 8 | 9 | .PARAMETER $ReferenceDirectory 10 | Any Intune Backup Directory. 11 | 12 | .PARAMETER $DifferenceDirectory 13 | Latest Intune Backup directory 14 | 15 | .EXAMPLE 16 | - Show verbose output 17 | Compare-IntuneBackupDirectories -Verbose -ReferenceDirectory C:\Users\BradleyWyatt\AppData\Local\Temp\IntuneBackup -DifferenceDirectory C:\Users\BradleyWyatt\AppData\Local\Temp\IntuneNewBackup 18 | 19 | Compare-IntuneBackupDirectories -ReferenceDirectory C:\Users\BradleyWyatt\AppData\Local\Temp\IntuneBackup -DifferenceDirectory C:\Users\BradleyWyatt\AppData\Local\Temp\IntuneNewBackup 20 | 21 | .NOTES 22 | Requires the IntuneBackupAndRestore Module 23 | 24 | .AUTHOR 25 | Bradley Wyatt - The Lazy Administrator 26 | #> 27 | 28 | param ( 29 | [parameter(Mandatory = $true, Position = 0)] 30 | [String]$ReferenceDirectory, 31 | [parameter(Mandatory = $true, Position = 1)] 32 | [String]$DifferenceDirectory 33 | ) 34 | 35 | begin { 36 | $referenceFiles = Get-ChildItem $ReferenceDirectory -Recurse | Where-Object { $_.Name -like "*.json*" } | Select-Object -ExpandProperty VersionInfo 37 | 38 | $differenceFiles = Get-ChildItem $DifferenceDirectory -Recurse | Where-Object { $_.Name -like "*.json*" } | Select-Object @{ Label = "FileName"; Expression = { (($_.VersionInfo).FileName).split("\") | Select-Object -Last 1 } }, @{ Label = "FullPath"; Expression = { (($_.VersionInfo).FileName) } } 39 | } 40 | 41 | process { 42 | foreach ($file in $referenceFiles) { 43 | $referenceJSONFile = ($file.Filename).split("\") | Select-Object -last 1 44 | 45 | Write-Verbose "The reference file is '$referenceJSONFile'" 46 | Write-Verbose "The reference file path is $($file.FileName)" 47 | 48 | $difFileFound = $differenceFiles | Where-Object { $_.FileName -eq $referenceJSONFile } 49 | 50 | if (($difFileFound.FileName).count -gt 1) { 51 | $referenceJSONFile = (($file.Filename).split("\") | Select-Object -last 2) -join "\" 52 | $referenceJSONFileParent = ($file.FileName).split("\") | Select-Object -Last 2 53 | $referenceJSONFileParentPath = $referenceJSONFileParent -join "\" 54 | Write-Verbose "Multiple difference files found that were matching the reference file" 55 | $difFileFound = $differenceFiles | Where-Object { $_.FullPath -like "*$referenceJSONFileParentPath*" } 56 | if (($difFileFound.FileName).count -gt 1) { 57 | # path wasn't precise enough to uniquely identify the correct difference file 58 | # try to use last three parts of the file path instead of two 59 | $referenceJSONFileParent = ($file.FileName).split("\") | Select-Object -Last 3 60 | $referenceJSONFileParentPath = $referenceJSONFileParent -join "\" 61 | Write-Verbose "Multiple difference files found again that were matching the reference file" 62 | $difFileFound = $differenceFiles | Where-Object { $_.FullPath -like "*$referenceJSONFileParentPath*" } 63 | if (($difFileFound.FileName).count -gt 1) { 64 | Write-Warning "Unable to uniquely identify difference file. Skipping $referenceJSONFile" 65 | continue 66 | } 67 | } 68 | } 69 | 70 | Write-Verbose "The difference file is located at $($difFileFound.fullpath)" 71 | 72 | Write-Verbose "Checking for changes in the file '$referenceJSONFile'" 73 | 74 | $changes = Compare-IntuneBackupFile -ReferenceFilePath $file.FileName -DifferenceFilePath $difFileFound.FullPath -ErrorAction "Continue" 75 | if ($changes) { 76 | Write-Output "There was a change in the file, '$referenceJSONFile' which is located at $($difFileFound.fullpath)" 77 | $changes | Format-Table -AutoSize 78 | } 79 | } 80 | 81 | # search for new settings that doesn't exist in first backup 82 | foreach ($file in $differenceFiles) { 83 | $differenceJSONFile = $file.Filename 84 | 85 | Write-Verbose "The difference file is '$differenceJSONFile'" 86 | Write-Verbose "The difference file path is $($file.FullPath)" 87 | 88 | $difFileFound = $referenceFiles | Where-Object { (($_.FileName).split("\") | Select-Object -Last 1) -eq $differenceJSONFile } 89 | 90 | if (($difFileFound.FileName).count -gt 1) { 91 | $differenceJSONFileParent = ($file.FullPath).split("\") | Select-Object -Last 2 92 | $differenceJSONFileParentPath = $differenceJSONFileParent -join "\" 93 | Write-Verbose "Multiple reference files found that were matching the difference file" 94 | $difFileFound = $referenceFiles | Where-Object { $_.FileName -like "*$differenceJSONFileParentPath*" } 95 | if (($difFileFound.FileName).count -gt 1) { 96 | # filter wasn't precise enough to uniquely identify the correct difference file 97 | # try to use last three parts of the file path instead of two 98 | $differenceJSONFileParent = ($file.FullPath).split("\") | Select-Object -Last 3 99 | $differenceJSONFileParentPath = $differenceJSONFileParent -join "\" 100 | Write-Verbose "Multiple reference files found again that were matching the difference file" 101 | $difFileFound = $referenceFiles | Where-Object { $_.FileName -like "*$differenceJSONFileParentPath*" } 102 | if (($difFileFound.FileName).count -gt 1) { 103 | Write-Warning "Unable to uniquely identify difference file. Skipping $differenceJSONFile" 104 | continue 105 | } 106 | } 107 | } 108 | 109 | if (!$difFileFound) { 110 | Write-Output "There is a new file. '$differenceJSONFile' which is located at $($file.FullPath)" 111 | } 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /IntuneBackupAndRestore/Public/Compare-IntuneBackupFile.ps1: -------------------------------------------------------------------------------- 1 | function Compare-IntuneBackupFile() { 2 | <# 3 | .SYNOPSIS 4 | Compare two Intune Backup Files for changes. 5 | 6 | .DESCRIPTION 7 | Compare two Intune Backup Files for changes. 8 | 9 | .PARAMETER ReferenceFilePath 10 | Any Intune Backup file. 11 | 12 | .PARAMETER DifferenceFilePath 13 | Latest Intune Backup file, that matches the Intune Configuration (e.g. Device Compliance Policy, Device Configuration Profile or Device Management Script). 14 | 15 | .EXAMPLE 16 | Compare-IntuneBackupFile -ReferenceFilePath 'C:\temp\IntuneBackup\Device Configurations\Windows - Endpoint Protection.json' -DifferenceFilePath 'C:\temp\IntuneBackupLatest\Device Configurations\Windows - Endpoint Protection.json' 17 | 18 | .NOTES 19 | The DifferenceFilePath should point to the latest Intune Backup file, as it might contain new properties. 20 | #> 21 | 22 | param( 23 | [Parameter(Mandatory = $true)] 24 | [string]$ReferenceFilePath, 25 | 26 | [Parameter(Mandatory = $true)] 27 | [string]$DifferenceFilePath 28 | ) 29 | 30 | try { 31 | $backupFile = Get-Content -LiteralPath $ReferenceFilePath -ErrorAction Stop | ConvertFrom-Json 32 | } 33 | catch { 34 | Write-Error -Message "Could not retrieve ReferenceFile from the ReferenceFilePath location." -ErrorAction Stop 35 | } 36 | 37 | try { 38 | $latestBackupFile = Get-Content -LiteralPath $DifferenceFilePath -ErrorAction Stop | ConvertFrom-Json 39 | } 40 | catch { 41 | Write-Error -Message "Could not retrieve DifferenceFile from the DifferenceFilePath location." -ErrorAction Stop 42 | } 43 | 44 | function Invoke-FlattenBackupObject() { 45 | param( 46 | [Parameter (Mandatory = $true)] 47 | [PSCustomObject]$PSCustomObject, 48 | 49 | [Parameter (Mandatory = $false)] 50 | [string]$KeyName 51 | ) 52 | 53 | $flatObject = New-Object -TypeName PSObject 54 | 55 | $psCustomObject.PSObject.Properties | ForEach-Object { 56 | if ($null -eq $($_.Value)) { 57 | if ($KeyName) { 58 | $flatObject | Add-Member -NotePropertyName "$KeyName-$($_.Name)" -NotePropertyValue 'null' 59 | } 60 | else { 61 | $flatObject | Add-Member -NotePropertyName $_.Name -NotePropertyValue 'null' 62 | } 63 | } 64 | else { 65 | if (($_.Value).GetType().Name -eq 'PSCustomObject') { 66 | Invoke-FlattenBackupObject -PSCustomObject $_.Value -KeyName $_.Name 67 | } 68 | elseif (($_.Value).GetType().Name -eq 'Object[]') { 69 | Invoke-FlattenBackupObject -PSCustomObject $_.Value.GetEnumerator() -KeyName $_.Name 70 | } 71 | else { 72 | if ($KeyName) { 73 | $flatObject | Add-Member -NotePropertyName "$KeyName-$($_.Name)" -NotePropertyValue $_.Value 74 | } 75 | else { 76 | $flatObject | Add-Member -NotePropertyName $_.Name -NotePropertyValue $_.Value 77 | } 78 | } 79 | } 80 | } 81 | return $flatObject 82 | } 83 | 84 | $flattenBackupArray = Invoke-FlattenBackupObject -PSCustomObject $backupFile 85 | $flattenLatestBackupArray = Invoke-FlattenBackupObject -PSCustomObject $latestBackupFile 86 | 87 | # Check if the JSON needs flattening, else it's just an object instead of an array. 88 | if ($flattenBackupArray -is [array]) { 89 | $flattenBackupObject = New-Object -TypeName PSObject 90 | for ($i=0; $i -le $flattenBackupArray.Length; $i++) { 91 | foreach ($property in $flattenBackupArray[$i].PSObject.Properties) { 92 | $flattenBackupObject | Add-Member -NotePropertyName $property.Name -NotePropertyValue $property.Value 93 | } 94 | } 95 | } 96 | else { 97 | $flattenBackupObject = $flattenBackupArray 98 | } 99 | 100 | # Check if the JSON needs flattening, else it's just an object instead of an array. 101 | if ($flattenLatestBackupArray -is [array]) { 102 | $flattenLatestBackupObject = New-Object -TypeName PSObject 103 | for ($i=0; $i -le $flattenLatestBackupArray.Length; $i++) { 104 | foreach ($property in $flattenLatestBackupArray[$i].PSObject.Properties) { 105 | $flattenLatestBackupObject | Add-Member -NotePropertyName $property.Name -NotePropertyValue $property.Value -Force 106 | } 107 | } 108 | } 109 | else { 110 | $flattenLatestBackupObject = $flattenLatestBackupArray 111 | } 112 | 113 | $backupComparison = foreach ($latestBackupFileProperty in $flattenBackupObject.PSObject.Properties.Name) { 114 | $compareBackup = Compare-Object -ReferenceObject $flattenBackupObject -DifferenceObject $flattenLatestBackupObject -Property $latestBackupFileProperty 115 | if ($compareBackup.SideIndicator) { 116 | # If the property exists in both Intune Backup Files 117 | if ($null -ne $flattenBackupObject.$latestBackupFileProperty) { 118 | New-Object PSCustomObject -Property @{ 119 | 'Property' = $latestBackupFileProperty 120 | 'Old value' = $flattenBackupObject.$latestBackupFileProperty 121 | 'New value' = $flattenLatestBackupObject.$latestBackupFileProperty 122 | } 123 | } 124 | # If the property only exists in the latest Intune Backup File 125 | else { 126 | New-Object PSCustomObject -Property @{ 127 | 'Property' = $latestBackupFileProperty 128 | 'Old value' = $null 129 | 'New value' = $flattenLatestBackupObject.$latestBackupFileProperty 130 | } 131 | } 132 | } 133 | } 134 | 135 | return $backupComparison 136 | } 137 | -------------------------------------------------------------------------------- /IntuneBackupAndRestore/Public/Invoke-IntuneBackupAppProtectionPolicy.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-IntuneBackupAppProtectionPolicy { 2 | <# 3 | .SYNOPSIS 4 | Backup Intune App Protection Policy 5 | 6 | .DESCRIPTION 7 | Backup Intune App Protection Policies as JSON files per App Protection Policy to the specified Path. 8 | 9 | .PARAMETER Path 10 | Path to store backup files 11 | 12 | .EXAMPLE 13 | Invoke-IntuneBackupAppProtectionPolicy -Path "C:\temp" 14 | #> 15 | 16 | [CmdletBinding()] 17 | param( 18 | [Parameter(Mandatory = $true)] 19 | [string]$Path, 20 | 21 | [Parameter(Mandatory = $false)] 22 | [ValidateSet("v1.0", "Beta")] 23 | [string]$ApiVersion = "Beta" 24 | ) 25 | 26 | #Connect to MS-Graph if required 27 | if ($null -eq (Get-MgContext)) { 28 | connect-mggraph -scopes "DeviceManagementApps.ReadWrite.All, DeviceManagementConfiguration.ReadWrite.All, DeviceManagementServiceConfig.ReadWrite.All, DeviceManagementManagedDevices.ReadWrite.All" 29 | } 30 | 31 | # Get all App Protection Policies 32 | $appProtectionPolicies = Invoke-MgGraphRequest -Uri "/$ApiVersion/deviceAppManagement/managedAppPolicies" | Get-MgGraphAllPages 33 | 34 | if ($appProtectionPolicies.value -ne "") { 35 | 36 | # Create folder if not exists 37 | if (-not (Test-Path "$Path\App Protection Policies")) { 38 | $null = New-Item -Path "$Path\App Protection Policies" -ItemType Directory 39 | } 40 | 41 | foreach ($appProtectionPolicy in $appProtectionPolicies) { 42 | 43 | if (($appProtectionPolicy.AppGroupType -eq "selectedPublicApps") -and ($appProtectionPolicy.'@odata.type' -eq '#microsoft.graph.androidManagedAppProtection')) { 44 | $uri = "$ApiVersion/deviceAppManagement/androidManagedAppProtections('$($appProtectionPolicy.id)')"+'?$expand=apps' 45 | $appProtectionPolicy.apps = (Invoke-MgGraphRequest -method get -Uri $uri).apps 46 | } 47 | 48 | if (($appProtectionPolicy.AppGroupType -eq "selectedPublicApps") -and ($appProtectionPolicy.'@odata.type' -eq '#microsoft.graph.iosManagedAppProtection')) { 49 | $uri = "$ApiVersion/deviceAppManagement/iosManagedAppProtections('$($appProtectionPolicy.id)')"+'?$expand=apps' 50 | $appProtectionPolicy.add("apps",(Invoke-MgGraphRequest -method get -Uri $uri).apps) 51 | } 52 | 53 | $fileName = ($appProtectionPolicy.displayName).Split([IO.Path]::GetInvalidFileNameChars()) -join '_' 54 | $appProtectionPolicy | ConvertTo-Json -Depth 100 | Out-File -LiteralPath "$path\App Protection Policies\$fileName.json" 55 | 56 | [PSCustomObject]@{ 57 | "Action" = "Backup" 58 | "Type" = "App Protection Policy" 59 | "Name" = $appProtectionPolicy.displayName 60 | "Path" = "App Protection Policies\$fileName.json" 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /IntuneBackupAndRestore/Public/Invoke-IntuneBackupAppProtectionPolicyAssignment.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-IntuneBackupAppProtectionPolicyAssignment { 2 | <# 3 | .SYNOPSIS 4 | Backup Intune App Protection Policy Assignments 5 | 6 | .DESCRIPTION 7 | Backup Intune App Protection Policy Assignments as JSON files per App Protection Policy to the specified Path. 8 | 9 | .PARAMETER Path 10 | Path to store backup files 11 | 12 | .EXAMPLE 13 | Invoke-IntuneBackupAppProtectionPolicyAssignment -Path "C:\temp" 14 | #> 15 | 16 | [CmdletBinding()] 17 | param( 18 | [Parameter(Mandatory = $true)] 19 | [string]$Path, 20 | 21 | [Parameter(Mandatory = $false)] 22 | [ValidateSet("v1.0", "Beta")] 23 | [string]$ApiVersion = "Beta" 24 | ) 25 | 26 | #Connect to MS-Graph if required 27 | if ($null -eq (Get-MgContext)) { 28 | connect-mggraph -scopes "DeviceManagementApps.ReadWrite.All, DeviceManagementConfiguration.ReadWrite.All, DeviceManagementServiceConfig.ReadWrite.All, DeviceManagementManagedDevices.ReadWrite.All" 29 | } 30 | 31 | $appProtectionPolicies = Invoke-MgGraphRequest -Uri "/$ApiVersion/deviceAppManagement/managedAppPolicies" | Get-MgGraphAllPages 32 | 33 | if ($appProtectionPolicies.value -ne "") { 34 | 35 | # Create folder if not exists 36 | if (-not (Test-Path "$Path\App Protection Policies\Assignments")) { 37 | $null = New-Item -Path "$Path\App Protection Policies\Assignments" -ItemType Directory 38 | } 39 | 40 | foreach ($appProtectionPolicy in $appProtectionPolicies) { 41 | switch ($appProtectionPolicy.'@odata.type') { 42 | "#microsoft.graph.androidManagedAppProtection" { 43 | $dataType = "androidManagedAppProtections" 44 | break 45 | } 46 | "#microsoft.graph.iosManagedAppProtection" { 47 | $dataType = "iosManagedAppProtections" 48 | break 49 | } 50 | "#microsoft.graph.mdmWindowsInformationProtectionPolicy" { 51 | $dataType = "mdmWindowsInformationProtectionPolicies" 52 | break 53 | } 54 | "#microsoft.graph.windowsInformationProtectionPolicy" { 55 | $dataType = "windowsInformationProtectionPolicies" 56 | break 57 | } 58 | "#microsoft.graph.targetedManagedAppConfiguration" { 59 | $dataType = "targetedManagedAppConfigurations" 60 | break 61 | } 62 | Default { 63 | continue 64 | } 65 | } 66 | $assignments = Invoke-MgGraphRequest -Uri "deviceAppManagement/$dataType('$($appProtectionPolicy.id)')/assignments" 67 | 68 | $fileName = ($appProtectionPolicy.displayName).Split([IO.Path]::GetInvalidFileNameChars()) -join '_' 69 | $assignments | ConvertTo-Json -Depth 100 | Out-File -LiteralPath "$path\App Protection Policies\Assignments\$($appProtectionPolicy.id) - $fileName.json" 70 | 71 | [PSCustomObject]@{ 72 | "Action" = "Backup" 73 | "Type" = "App Protection Policy Assignments" 74 | "Name" = $appProtectionPolicy.displayName 75 | "Path" = "App Protection Policies\Assignments\$fileName.json" 76 | } 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /IntuneBackupAndRestore/Public/Invoke-IntuneBackupAutopilotDeploymentProfile.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-IntuneBackupAutopilotDeploymentProfile { 2 | <# 3 | .SYNOPSIS 4 | Backup Intune Autopilot Deployment Profiles 5 | 6 | .DESCRIPTION 7 | Backup Intune Autopilot Deployment Profiles as JSON files per deployment profile to the specified Path. 8 | 9 | .PARAMETER Path 10 | Path to store backup files 11 | 12 | .EXAMPLE 13 | Invoke-IntuneBackupAutopilotDeploymentProfile -Path "C:\temp" 14 | #> 15 | 16 | [CmdletBinding()] 17 | param( 18 | [Parameter(Mandatory = $true)] 19 | [string]$Path, 20 | 21 | [Parameter(Mandatory = $false)] 22 | [ValidateSet("v1.0", "Beta")] 23 | [string]$ApiVersion = "Beta" 24 | ) 25 | 26 | #Connect to MS-Graph if required 27 | if($null -eq (Get-MgContext)){ 28 | connect-mggraph -scopes "DeviceManagementApps.ReadWrite.All, DeviceManagementConfiguration.ReadWrite.All, DeviceManagementServiceConfig.ReadWrite.All, DeviceManagementManagedDevices.ReadWrite.All" 29 | } 30 | 31 | # Get all Autopilot Deployment Profiles 32 | $winAutopilotDeploymentProfiles = Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/$ApiVersion/deviceManagement/windowsAutopilotDeploymentProfiles" -OutputType PSObject | Select-Object -ExpandProperty Value 33 | 34 | if ($winAutopilotDeploymentProfiles.value -ne "") { 35 | 36 | # Create folder if not exists 37 | if (-not (Test-Path "$Path\Autopilot Deployment Profiles")) { 38 | $null = New-Item -Path "$Path\Autopilot Deployment Profiles" -ItemType Directory 39 | } 40 | 41 | foreach ($winAutopilotDeploymentProfile in $winAutopilotDeploymentProfiles) { 42 | $fileName = ($winAutopilotDeploymentProfile.displayName).Split([IO.Path]::GetInvalidFileNameChars()) -join '_' 43 | 44 | # Export the Deployment profile 45 | $winAutopilotDeploymentProfileObject = Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/$ApiVersion/deviceManagement/windowsAutopilotDeploymentProfiles/$($winAutopilotDeploymentProfile.id)" 46 | $winAutopilotDeploymentProfileObject | ConvertTo-Json -Depth 100 | Out-File -LiteralPath "$path\Autopilot Deployment Profiles\$fileName.json" 47 | 48 | [PSCustomObject]@{ 49 | "Action" = "Backup" 50 | "Type" = "Autopilot Deployment Profile" 51 | "Name" = $winAutopilotDeploymentProfileObject.displayName 52 | "Path" = "Autopilot Deployment Profiles\$fileName.json" 53 | } 54 | 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /IntuneBackupAndRestore/Public/Invoke-IntuneBackupAutopilotDeploymentProfileAssignment.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-IntuneBackupAutopilotDeploymentProfileAssignment { 2 | <# 3 | .SYNOPSIS 4 | Backup Intune Autopilot Deployment Profile Assignments 5 | 6 | .DESCRIPTION 7 | Backup Intune Autopilot Deployment Profile Assignments as JSON files per Deployment Profile to the specified Path. 8 | 9 | .PARAMETER Path 10 | Path to store backup files 11 | 12 | .EXAMPLE 13 | Invoke-IntuneBackupAutopilotDeploymentProfileAssignment -Path "C:\temp" 14 | #> 15 | 16 | [CmdletBinding()] 17 | param( 18 | [Parameter(Mandatory = $true)] 19 | [string]$Path, 20 | 21 | [Parameter(Mandatory = $false)] 22 | [ValidateSet("v1.0", "Beta")] 23 | [string]$ApiVersion = "Beta" 24 | ) 25 | 26 | # Get all assignments from all policies 27 | $winAutopilotDeploymentProfiles = Invoke-MgGraphRequest -Uri "deviceManagement/windowsAutopilotDeploymentProfiles" | Get-MGGraphAllPages 28 | 29 | if ($winAutopilotDeploymentProfiles.value -ne "") { 30 | 31 | # Create folder if not exists 32 | if (-not (Test-Path "$Path\Autopilot Deployment Profiles\Assignments")) { 33 | $null = New-Item -Path "$Path\Autopilot Deployment Profiles\Assignments" -ItemType Directory 34 | } 35 | 36 | foreach ($winAutopilotDeploymentProfile in $winAutopilotDeploymentProfiles) { 37 | $assignments = Invoke-MgGraphRequest -Uri "deviceManagement/windowsAutopilotDeploymentProfiles/$($winAutopilotDeploymentProfile.id)/assignments" | Get-MGGraphAllPages 38 | 39 | if ($assignments) { 40 | $fileName = ($winAutopilotDeploymentProfile.displayName).Split([IO.Path]::GetInvalidFileNameChars()) -join '_' 41 | $assignments | ConvertTo-Json | Out-File -LiteralPath "$path\Autopilot Deployment Profiles\Assignments\$fileName.json" 42 | 43 | [PSCustomObject]@{ 44 | "Action" = "Backup" 45 | "Type" = "Autopilot Deployment Profile Assignments" 46 | "Name" = $winAutopilotDeploymentProfile.displayName 47 | "Path" = "Autopilot Deployment Profiles\Assignments\$fileName.json" 48 | } 49 | } 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /IntuneBackupAndRestore/Public/Invoke-IntuneBackupClientApp.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-IntuneBackupClientApp { 2 | <# 3 | .SYNOPSIS 4 | Backup Intune Client Apps 5 | 6 | .DESCRIPTION 7 | Backup Intune Client Apps as JSON files per Device Compliance Policy to the specified Path. 8 | 9 | .PARAMETER Path 10 | Path to store backup files 11 | 12 | .EXAMPLE 13 | Invoke-IntuneBackupClientApp -Path "C:\temp" 14 | #> 15 | 16 | [CmdletBinding()] 17 | param( 18 | [Parameter(Mandatory = $true)] 19 | [string]$Path, 20 | 21 | [Parameter(Mandatory = $false)] 22 | [ValidateSet("v1.0", "Beta")] 23 | [string]$ApiVersion = "Beta" 24 | ) 25 | 26 | #Connect to MS-Graph if required 27 | if($null -eq (Get-MgContext)){ 28 | connect-mggraph -scopes "DeviceManagementApps.ReadWrite.All, DeviceManagementConfiguration.ReadWrite.All, DeviceManagementServiceConfig.ReadWrite.All, DeviceManagementManagedDevices.ReadWrite.All" 29 | } 30 | 31 | # Get all Client Apps 32 | $filter = "microsoft.graph.managedApp/appAvailability eq null or microsoft.graph.managedApp/appAvailability eq 'lineOfBusiness' or isAssigned eq true" 33 | $clientApps = Invoke-MgRestMethod -Uri "$apiversion/deviceAppManagement/mobileApps?filter=$filter" | Get-MgGraphAllPages 34 | 35 | if ($clientApps.value -ne "") { 36 | 37 | # Create folder if not exists 38 | if (-not (Test-Path "$Path\Client Apps")) { 39 | $null = New-Item -Path "$Path\Client Apps" -ItemType Directory 40 | } 41 | 42 | foreach ($clientApp in $clientApps) { 43 | $clientAppType = $clientApp.'@odata.type'.split('.')[-1] 44 | 45 | $fileName = ($clientApp.displayName).Split([IO.Path]::GetInvalidFileNameChars()) -join '_' 46 | $clientAppDetails = Invoke-MgRestMethod -Uri "$apiversion/deviceAppManagement/mobileApps/$($clientApp.id)" 47 | $clientAppDetails | ConvertTo-Json -depth 3 | Out-File -LiteralPath "$path\Client Apps\$($clientAppType)_$($fileName).json" 48 | 49 | [PSCustomObject]@{ 50 | "Action" = "Backup" 51 | "Type" = "Client App" 52 | "Name" = $clientApp.displayName 53 | "Path" = "Client Apps\$($clientAppType)_$($fileName).json" 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /IntuneBackupAndRestore/Public/Invoke-IntuneBackupClientAppAssignment.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-IntuneBackupClientAppAssignment { 2 | <# 3 | .SYNOPSIS 4 | Backup Intune Client App Assignments 5 | 6 | .DESCRIPTION 7 | Backup Intune Client App Assignments as JSON files per Client App to the specified Path. 8 | 9 | .PARAMETER Path 10 | Path to store backup files 11 | 12 | .EXAMPLE 13 | Invoke-IntuneBackupClientAppAssignment -Path "C:\temp" 14 | #> 15 | 16 | [CmdletBinding()] 17 | param( 18 | [Parameter(Mandatory = $true)] 19 | [string]$Path, 20 | 21 | [Parameter(Mandatory = $false)] 22 | [ValidateSet("v1.0", "Beta")] 23 | [string]$ApiVersion = "Beta" 24 | ) 25 | 26 | #Connect to MS-Graph if required 27 | if($null -eq (Get-MgContext)){ 28 | connect-mggraph -scopes "DeviceManagementApps.ReadWrite.All, DeviceManagementConfiguration.ReadWrite.All, DeviceManagementServiceConfig.ReadWrite.All, DeviceManagementManagedDevices.ReadWrite.All" 29 | } 30 | 31 | # Get all Client Apps 32 | $filter = "microsoft.graph.managedApp/appAvailability eq null or microsoft.graph.managedApp/appAvailability eq 'lineOfBusiness' or isAssigned eq true" 33 | $clientApps = Invoke-MgRestMethod -Uri "$apiversion/deviceAppManagement/mobileApps?filter=$filter" | Get-MgGraphAllPages 34 | 35 | if ($clientApps.value -ne "") { 36 | 37 | # Create folder if not exists 38 | if (-not (Test-Path "$Path\Client Apps\Assignments")) { 39 | $null = New-Item -Path "$Path\Client Apps\Assignments" -ItemType Directory 40 | } 41 | 42 | foreach ($clientApp in $clientApps) { 43 | $assignments = (Invoke-MgRestMethod -Uri "/$apiversion/deviceAppManagement/mobileApps/$($clientApp.id)/assignments").value 44 | if ($assignments) { 45 | $fileName = ($clientApp.displayName).Split([IO.Path]::GetInvalidFileNameChars()) -join '_' 46 | $assignments | ConvertTo-Json -Depth 100 | Out-File -LiteralPath "$path\Client Apps\Assignments\$($clientApp.id) - $fileName.json" 47 | 48 | [PSCustomObject]@{ 49 | "Action" = "Backup" 50 | "Type" = "Client App Assignments" 51 | "Name" = $clientApp.displayName 52 | "Path" = "Client Apps\Assignments\$fileName.json" 53 | } 54 | } 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /IntuneBackupAndRestore/Public/Invoke-IntuneBackupConfigurationPolicy.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-IntuneBackupConfigurationPolicy { 2 | <# 3 | .SYNOPSIS 4 | Backup Intune Settings Catalog Policies 5 | 6 | .DESCRIPTION 7 | Backup Intune Settings Catalog Policies as JSON files per Settings Catalog Policy to the specified Path. 8 | 9 | .PARAMETER Path 10 | Path to store backup files 11 | 12 | .EXAMPLE 13 | Invoke-IntuneBackupConfigurationPolicy -Path "C:\temp" 14 | #> 15 | 16 | [CmdletBinding()] 17 | param( 18 | [Parameter(Mandatory = $true)] 19 | [string]$Path, 20 | 21 | [Parameter(Mandatory = $false)] 22 | [ValidateSet("v1.0", "Beta")] 23 | [string]$ApiVersion = "Beta" 24 | ) 25 | 26 | #Connect to MS-Graph if required 27 | if($null -eq (Get-MgContext)){ 28 | connect-mggraph -scopes "DeviceManagementApps.ReadWrite.All, DeviceManagementConfiguration.ReadWrite.All, DeviceManagementServiceConfig.ReadWrite.All, DeviceManagementManagedDevices.ReadWrite.All" 29 | } 30 | 31 | # Get all Setting Catalogs Policies 32 | $configurationPolicies = Invoke-MgGraphRequest -Uri "$ApiVersion/deviceManagement/configurationPolicies" | Get-MGGraphAllPages 33 | 34 | if ($configurationPolicies.value -ne "") { 35 | 36 | # Create folder if not exists 37 | if (-not (Test-Path "$Path\Settings Catalog")) { 38 | $null = New-Item -Path "$Path\Settings Catalog" -ItemType Directory 39 | } 40 | 41 | foreach ($configurationPolicy in $configurationPolicies) { 42 | $configurationPolicy | Add-Member -MemberType NoteProperty -Name 'settings' -Value @() -Force 43 | $settings = (Invoke-MgGraphRequest -Uri "$ApiVersion/deviceManagement/configurationPolicies/$($configurationPolicy.id)/settings").value 44 | 45 | if ($settings -isnot [System.Array]) { 46 | $configurationPolicy.Settings = @($settings) 47 | } else { 48 | $configurationPolicy.Settings = $settings 49 | } 50 | 51 | $fileName = ($configurationPolicy.name).Split([IO.Path]::GetInvalidFileNameChars()) -join '_' 52 | $configurationPolicy | ConvertTo-Json -Depth 100 | Out-File -LiteralPath "$path\Settings Catalog\$fileName.json" 53 | 54 | [PSCustomObject]@{ 55 | "Action" = "Backup" 56 | "Type" = "Settings Catalog" 57 | "Name" = $configurationPolicy.name 58 | "Path" = "Settings Catalog\$fileName.json" 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /IntuneBackupAndRestore/Public/Invoke-IntuneBackupConfigurationPolicyAssignment.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-IntuneBackupConfigurationPolicyAssignment { 2 | <# 3 | .SYNOPSIS 4 | Backup Intune Settings Catalog Policy Assignments 5 | 6 | .DESCRIPTION 7 | Backup Intune Settings Catalog Policy Assignments as JSON files per Settings Catalog Policy to the specified Path. 8 | 9 | .PARAMETER Path 10 | Path to store backup files 11 | 12 | .EXAMPLE 13 | Invoke-IntuneBackupConfigurationPolicyAssignment -Path "C:\temp" 14 | #> 15 | 16 | [CmdletBinding()] 17 | param( 18 | [Parameter(Mandatory = $true)] 19 | [string]$Path, 20 | 21 | [Parameter(Mandatory = $false)] 22 | [ValidateSet("v1.0", "Beta")] 23 | [string]$ApiVersion = "Beta" 24 | ) 25 | 26 | #Connect to MS-Graph if required 27 | if($null -eq (Get-MgContext)){ 28 | connect-mggraph -scopes "DeviceManagementApps.ReadWrite.All, DeviceManagementConfiguration.ReadWrite.All, DeviceManagementServiceConfig.ReadWrite.All, DeviceManagementManagedDevices.ReadWrite.All" 29 | } 30 | 31 | # Get all assignments from all policies 32 | $configurationPolicies = (Invoke-MgGraphRequest -Uri "$ApiVersion/deviceManagement/configurationPolicies").value 33 | 34 | if ($configurationPolicies.value -ne "") { 35 | 36 | # Create folder if not exists 37 | if (-not (Test-Path "$Path\Settings Catalog\Assignments")) { 38 | $null = New-Item -Path "$Path\Settings Catalog\Assignments" -ItemType Directory 39 | } 40 | 41 | foreach ($configurationPolicy in $configurationPolicies) { 42 | $assignments = (Invoke-MgGraphRequest -Uri "$ApiVersion/deviceManagement/configurationPolicies/$($configurationPolicy.id)/assignments").value 43 | if ($assignments) { 44 | $fileName = ($configurationPolicy.name).Split([IO.Path]::GetInvalidFileNameChars()) -join '_' 45 | $assignments | ConvertTo-Json | Out-File -LiteralPath "$path\Settings Catalog\Assignments\$fileName.json" 46 | 47 | [PSCustomObject]@{ 48 | "Action" = "Backup" 49 | "Type" = "Settings Catalog Assignments" 50 | "Name" = $configurationPolicy.name 51 | "Path" = "Settings Catalog\Assignments\$fileName.json" 52 | } 53 | } 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /IntuneBackupAndRestore/Public/Invoke-IntuneBackupDeviceCompliancePolicy.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-IntuneBackupDeviceCompliancePolicy { 2 | <# 3 | .SYNOPSIS 4 | Backup Intune Device Compliance Policies 5 | 6 | .DESCRIPTION 7 | Backup Intune Device Compliance Policies as JSON files per Device Compliance Policy to the specified Path. 8 | 9 | .PARAMETER Path 10 | Path to store backup files 11 | 12 | .EXAMPLE 13 | Invoke-IntuneBackupDeviceCompliancePolicy -Path "C:\temp" 14 | #> 15 | 16 | [CmdletBinding()] 17 | param( 18 | [Parameter(Mandatory = $true)] 19 | [string]$Path, 20 | 21 | [Parameter(Mandatory = $false)] 22 | [ValidateSet("v1.0", "Beta")] 23 | [string]$ApiVersion = "Beta" 24 | ) 25 | 26 | #Connect to MS-Graph if required 27 | if($null -eq (Get-MgContext)){ 28 | connect-mggraph -scopes "DeviceManagementApps.ReadWrite.All, DeviceManagementConfiguration.ReadWrite.All, DeviceManagementServiceConfig.ReadWrite.All" 29 | } 30 | 31 | # Get all Device Compliance Policies 32 | $deviceCompliancePolicies = Invoke-MgGraphRequest -Uri "$ApiVersion/deviceManagement/deviceCompliancePolicies" | Get-MGGraphAllPages 33 | 34 | if ($deviceCompliancePolicies.value -ne "") { 35 | 36 | # Create folder if not exists 37 | if (-not (Test-Path "$Path\Device Compliance Policies")) { 38 | $null = New-Item -Path "$Path\Device Compliance Policies" -ItemType Directory 39 | } 40 | 41 | foreach ($deviceCompliancePolicy in $deviceCompliancePolicies) { 42 | $fileName = ($deviceCompliancePolicy.displayName).Split([IO.Path]::GetInvalidFileNameChars()) -join '_' 43 | $deviceCompliancePolicy | ConvertTo-Json -Depth 100 | Out-File -LiteralPath "$path\Device Compliance Policies\$fileName.json" 44 | 45 | [PSCustomObject]@{ 46 | "Action" = "Backup" 47 | "Type" = "Device Compliance Policy" 48 | "Name" = $deviceCompliancePolicy.displayName 49 | "Path" = "Device Compliance Policies\$fileName.json" 50 | } 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /IntuneBackupAndRestore/Public/Invoke-IntuneBackupDeviceCompliancePolicyAssignment.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-IntuneBackupDeviceCompliancePolicyAssignment { 2 | <# 3 | .SYNOPSIS 4 | Backup Intune Device Complaince Policy Assignments 5 | 6 | .DESCRIPTION 7 | Backup Intune Device Complaince Policy Assignments as JSON files per Device Compliance Policy to the specified Path. 8 | 9 | .PARAMETER Path 10 | Path to store backup files 11 | 12 | .EXAMPLE 13 | Invoke-IntuneBackupDeviceCompliancePolicyAssignment -Path "C:\temp" 14 | #> 15 | 16 | [CmdletBinding()] 17 | param( 18 | [Parameter(Mandatory = $true)] 19 | [string]$Path, 20 | 21 | [Parameter(Mandatory = $false)] 22 | [ValidateSet("v1.0", "Beta")] 23 | [string]$ApiVersion = "Beta" 24 | ) 25 | 26 | #Connect to MS-Graph if required 27 | if($null -eq (Get-MgContext)){ 28 | connect-mggraph -scopes "DeviceManagementApps.ReadWrite.All, DeviceManagementConfiguration.ReadWrite.All, DeviceManagementServiceConfig.ReadWrite.All" 29 | } 30 | 31 | # Get all Device Compliance Policies 32 | $deviceCompliancePolicies = Invoke-MgGraphRequest -Uri "$ApiVersion/deviceManagement/deviceCompliancePolicies" | Get-MGGraphAllPages 33 | 34 | if ($deviceCompliancePolicies.value -ne "") { 35 | 36 | # Create folder if not exists 37 | if (-not (Test-Path "$Path\Device Compliance Policies\Assignments")) { 38 | $null = New-Item -Path "$Path\Device Compliance Policies\Assignments" -ItemType Directory 39 | } 40 | 41 | foreach ($deviceCompliancePolicy in $deviceCompliancePolicies) { 42 | $assignments = Invoke-MgGraphRequest -Uri "$ApiVersion/deviceManagement/deviceCompliancePolicies/$($deviceCompliancePolicy.id)/assignments" | Get-MGGraphAllPages 43 | if ($assignments) { 44 | $fileName = ($deviceCompliancePolicy.displayName).Split([IO.Path]::GetInvalidFileNameChars()) -join '_' 45 | $assignments | ConvertTo-Json | Out-File -LiteralPath "$path\Device Compliance Policies\Assignments\$fileName.json" 46 | 47 | [PSCustomObject]@{ 48 | "Action" = "Backup" 49 | "Type" = "Device Compliance Policy Assignments" 50 | "Name" = $deviceCompliancePolicy.displayName 51 | "Path" = "Device Compliance Policies\Assignments\$fileName.json" 52 | } 53 | } 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /IntuneBackupAndRestore/Public/Invoke-IntuneBackupDeviceConfiguration.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-IntuneBackupDeviceConfiguration { 2 | <# 3 | .SYNOPSIS 4 | Backup Intune Device Configurations 5 | 6 | .DESCRIPTION 7 | Backup Intune Device Configurations as JSON files per Device Configuration Policy to the specified Path. 8 | 9 | .PARAMETER Path 10 | Path to store backup files 11 | 12 | .EXAMPLE 13 | Invoke-IntuneBackupDeviceConfiguration -Path "C:\temp" 14 | #> 15 | 16 | [CmdletBinding()] 17 | param( 18 | [Parameter(Mandatory = $true)] 19 | [string]$Path, 20 | 21 | [Parameter(Mandatory = $false)] 22 | [ValidateSet("v1.0", "Beta")] 23 | [string]$ApiVersion = "Beta" 24 | ) 25 | 26 | #Connect to MS-Graph if required 27 | if($null -eq (Get-MgContext)){ 28 | connect-mggraph -scopes "DeviceManagementApps.ReadWrite.All, DeviceManagementConfiguration.ReadWrite.All, DeviceManagementServiceConfig.ReadWrite.All, DeviceManagementManagedDevices.ReadWrite.All" 29 | } 30 | 31 | # Get all device configurations 32 | $deviceConfigurations = Invoke-MgGraphRequest -Uri "$apiVersion/deviceManagement/deviceConfigurations" | Get-MGGraphAllPages 33 | 34 | if ($deviceConfigurations.value -ne "") { 35 | 36 | # Create folder if not exists 37 | if (-not (Test-Path "$Path\Device Configurations")) { 38 | $null = New-Item -Path "$Path\Device Configurations" -ItemType Directory 39 | } 40 | 41 | foreach ($deviceConfiguration in $deviceConfigurations) { 42 | $fileName = ($deviceConfiguration.displayName).Split([IO.Path]::GetInvalidFileNameChars()) -join '_' 43 | 44 | # If it's a custom configuration, check if the device configuration contains encrypted OMA settings, then decrypt the OmaSettings to a Plain Text Value (required for import) 45 | if (($deviceConfiguration.'@odata.type' -eq '#microsoft.graph.windows10CustomConfiguration') -and ($deviceConfiguration.omaSettings | Where-Object { $_.isEncrypted -contains $true } )) { 46 | # Create an empty array for the unencrypted OMA settings. 47 | $newOmaSettings = @() 48 | foreach ($omaSetting in $deviceConfiguration.omaSettings) { 49 | # Check if this particular setting is encrypted, and get the plaintext only if necessary 50 | if ($omaSetting.isEncrypted) { 51 | $omaSettingValue = Invoke-MgGraphRequest -Uri "$apiVersion/deviceManagement/deviceConfigurations/$($deviceConfiguration.id)/getOmaSettingPlainTextValue(secretReferenceValueId='$($omaSetting.secretReferenceValueId)')" | Get-MgGraphAllPages 52 | } else { 53 | $omaSettingValue = $omaSetting.value 54 | } 55 | # Define a new 'unencrypted' OMA Setting 56 | $newOmaSetting = @{} 57 | $newOmaSetting.'@odata.type' = $omaSetting.'@odata.type' 58 | $newOmaSetting.displayName = $omaSetting.displayName 59 | $newOmaSetting.description = $omaSetting.description 60 | $newOmaSetting.omaUri = $omaSetting.omaUri 61 | $newOmaSetting.value = $omaSettingValue 62 | $newOmaSetting.isEncrypted = $false 63 | $newOmaSetting.secretReferenceValueId = $null 64 | 65 | # Add the unencrypted OMA Setting to the Array 66 | $newOmaSettings += $newOmaSetting 67 | } 68 | 69 | # Remove all encrypted OMA Settings from the Device Configuration 70 | $deviceConfiguration.omaSettings = @() 71 | 72 | # Add the unencrypted OMA Settings from the Device Configuration 73 | $deviceConfiguration.omaSettings += $newOmaSettings 74 | } 75 | 76 | # Export the Device Configuration Profile 77 | $deviceConfiguration | ConvertTo-Json -Depth 100 | Out-File -LiteralPath "$path\Device Configurations\$fileName.json" 78 | 79 | [PSCustomObject]@{ 80 | "Action" = "Backup" 81 | "Type" = "Device Configuration" 82 | "Name" = $deviceConfiguration.displayName 83 | "Path" = "Device Configurations\$fileName.json" 84 | } 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /IntuneBackupAndRestore/Public/Invoke-IntuneBackupDeviceConfigurationAssignment.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-IntuneBackupDeviceConfigurationAssignment { 2 | <# 3 | .SYNOPSIS 4 | Backup Intune Device Configuration Assignments 5 | 6 | .DESCRIPTION 7 | Backup Intune Device Configuration Assignments as JSON files per Device Configuration Policy to the specified Path. 8 | 9 | .PARAMETER Path 10 | Path to store backup files 11 | 12 | .EXAMPLE 13 | Invoke-IntuneBackupDeviceConfigurationAssignment -Path "C:\temp" 14 | #> 15 | 16 | [CmdletBinding()] 17 | param( 18 | [Parameter(Mandatory = $true)] 19 | [string]$Path, 20 | 21 | [Parameter(Mandatory = $false)] 22 | [ValidateSet("v1.0", "Beta")] 23 | [string]$ApiVersion = "Beta" 24 | ) 25 | 26 | #Connect to MS-Graph if required 27 | if($null -eq (Get-MgContext)){ 28 | connect-mggraph -scopes "DeviceManagementApps.ReadWrite.All, DeviceManagementConfiguration.ReadWrite.All, DeviceManagementServiceConfig.ReadWrite.All, DeviceManagementManagedDevices.ReadWrite.All" 29 | } 30 | 31 | # Get all assignments from all policies 32 | $deviceConfigurations = Invoke-MgGraphRequest -Uri "$apiVersion/deviceManagement/deviceConfigurations" | Get-MGGraphAllPages 33 | 34 | if ($deviceConfigurations.value -ne "") { 35 | 36 | # Create folder if not exists 37 | if (-not (Test-Path "$Path\Device Configurations\Assignments")) { 38 | $null = New-Item -Path "$Path\Device Configurations\Assignments" -ItemType Directory 39 | } 40 | 41 | foreach ($deviceConfiguration in $deviceConfigurations) { 42 | $assignments = Invoke-MgGraphRequest -Uri "$ApiVersion/deviceManagement/deviceConfigurations/$($deviceConfiguration.id)/assignments" | Get-MGGraphAllPages 43 | 44 | if ($assignments) { 45 | $fileName = ($deviceConfiguration.displayName).Split([IO.Path]::GetInvalidFileNameChars()) -join '_' 46 | $assignments | ConvertTo-Json | Out-File -LiteralPath "$path\Device Configurations\Assignments\$fileName.json" 47 | 48 | [PSCustomObject]@{ 49 | "Action" = "Backup" 50 | "Type" = "Device Configuration Assignments" 51 | "Name" = $deviceConfiguration.displayName 52 | "Path" = "Device Configurations\Assignments\$fileName.json" 53 | } 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /IntuneBackupAndRestore/Public/Invoke-IntuneBackupDeviceHealthScript.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-IntuneBackupDeviceHealthScript { 2 | <# 3 | .SYNOPSIS 4 | Backup Intune Health Scripts (Remediation scripts) 5 | 6 | .DESCRIPTION 7 | Backup Intune Health Scripts (Remediation scripts) as JSON files per Health Script to the specified Path. 8 | 9 | .PARAMETER Path 10 | Path to store backup files 11 | 12 | .EXAMPLE 13 | Invoke-IntuneBackupDeviceHealthScript -Path "C:\temp" 14 | #> 15 | [CmdletBinding()] 16 | param( 17 | [Parameter(Mandatory = $true)] 18 | [string]$Path, 19 | 20 | [Parameter(Mandatory = $false)] 21 | [ValidateSet("v1.0", "Beta")] 22 | [string]$ApiVersion = "Beta" 23 | ) 24 | 25 | #Connect to MS-Graph if required 26 | if($null -eq (Get-MgContext)){ 27 | connect-mggraph -scopes "DeviceManagementApps.ReadWrite.All, DeviceManagementConfiguration.ReadWrite.All, DeviceManagementServiceConfig.ReadWrite.All, DeviceManagementManagedDevices.ReadWrite.All" 28 | } 29 | 30 | # Get all Intune Health Scripts 31 | $healthScripts = Invoke-MgGraphRequest -Uri "$ApiVersion/deviceManagement/deviceHealthScripts" | Get-MGGraphAllPages 32 | 33 | if ($healthScripts.value -ne "") { 34 | 35 | # Create folder if not exists 36 | if (-not (Test-Path "$Path\Device Health Scripts")) { 37 | $null = New-Item -Path "$Path\Device Health Scripts" -ItemType Directory 38 | } 39 | 40 | foreach ($healthScript in $healthScripts) { 41 | $fileName = ($healthScript.displayName).Split([IO.Path]::GetInvalidFileNameChars()) -join '_' 42 | 43 | # Export the Health script profile (excluding Microsoft builtin scripts) 44 | if (-not ($healthScript.publisher -eq "Microsoft")) { 45 | $healthScriptObject = Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/$ApiVersion/deviceManagement/deviceHealthScripts/$($healthScript.id)" 46 | $healthScriptObject | ConvertTo-Json -Depth 100 | Out-File -LiteralPath "$path\Device Health Scripts\$fileName.json" 47 | 48 | # Create folder if not exists 49 | if (-not (Test-Path "$Path\Device Health Scripts\Script Content")) { 50 | $null = New-Item -Path "$Path\Device Health Scripts\Script Content" -ItemType Directory 51 | } 52 | 53 | $healthScriptObject = Invoke-MgGraphRequest -Uri "$ApiVersion/deviceManagement/deviceHealthScripts/$($healthScript.id)" 54 | $healthScriptDetectionContent = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($healthScriptObject.detectionScriptContent)) 55 | $healthScriptDetectionContent | Out-File -LiteralPath "$path\Device Health Scripts\Script Content\$fileName`_detection.ps1" 56 | $healthScriptRemediationContent = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($healthScriptObject.remediationScriptContent)) 57 | $healthScriptRemediationContent | Out-File -LiteralPath "$path\Device Health Scripts\Script Content\$fileName`_remediation.ps1" 58 | 59 | [PSCustomObject]@{ 60 | "Action" = "Backup" 61 | "Type" = "Device Health Scripts" 62 | "Name" = $healthScript.displayName 63 | "Path" = "Device Health Scripts\$fileName.json" 64 | } 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /IntuneBackupAndRestore/Public/Invoke-IntuneBackupDeviceHealthScriptAssignment.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-IntuneBackupDeviceHealthScriptAssignment { 2 | <# 3 | .SYNOPSIS 4 | Backup Intune Device Health Script Assignments 5 | 6 | .DESCRIPTION 7 | Backup Intune Device Health Script Assignments as JSON files per Device Health Script to the specified Path. 8 | 9 | .PARAMETER Path 10 | Path to store backup files 11 | 12 | .EXAMPLE 13 | Invoke-IntuneBackupDeviceHealthScriptAssignment -Path "C:\temp" 14 | #> 15 | 16 | [CmdletBinding()] 17 | param( 18 | [Parameter(Mandatory = $true)] 19 | [string]$Path, 20 | 21 | [Parameter(Mandatory = $false)] 22 | [ValidateSet("v1.0", "Beta")] 23 | [string]$ApiVersion = "Beta" 24 | ) 25 | 26 | #Connect to MS-Graph if required 27 | if($null -eq (Get-MgContext)){ 28 | connect-mggraph -scopes "DeviceManagementApps.ReadWrite.All, DeviceManagementConfiguration.ReadWrite.All, DeviceManagementServiceConfig.ReadWrite.All, DeviceManagementManagedDevices.ReadWrite.All" 29 | } 30 | 31 | # Get all assignments from all policies 32 | $healthScripts = Invoke-MgGraphRequest -Uri "$ApiVersion/deviceManagement/deviceHealthScripts" | Get-MGGraphAllPages 33 | 34 | if ($healthScripts.value -ne "") { 35 | 36 | # Create folder if not exists 37 | if (-not (Test-Path "$Path\Device Health Scripts\Assignments")) { 38 | $null = New-Item -Path "$Path\Device Health Scripts\Assignments" -ItemType Directory 39 | } 40 | 41 | foreach ($deviceHealthScript in $deviceHealthScripts) { 42 | $assignments = Invoke-MgGraphRequest -Uri "deviceManagement/deviceHealthScripts/$($deviceHealthScript.id)/assignments" | Get-MGGraphAllPages 43 | 44 | if ($assignments) { 45 | $fileName = ($deviceHealthScript.displayName).Split([IO.Path]::GetInvalidFileNameChars()) -join '_' 46 | $assignments | ConvertTo-Json -depth 100 | Out-File -LiteralPath "$path\Device Health Scripts\Assignments\$fileName.json" 47 | 48 | [PSCustomObject]@{ 49 | "Action" = "Backup" 50 | "Type" = "Device Health Scripts Assignments" 51 | "Name" = $deviceHealthScript.displayName 52 | "Path" = "Device Health Scripts\Assignments\$fileName.json" 53 | } 54 | } 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /IntuneBackupAndRestore/Public/Invoke-IntuneBackupDeviceManagementIntent.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-IntuneBackupDeviceManagementIntent { 2 | <# 3 | .SYNOPSIS 4 | Backup Intune Device Management Intents 5 | 6 | .DESCRIPTION 7 | Backup Intune Device Management Intents as JSON files per Device Management Intent to the specified Path. 8 | 9 | .PARAMETER Path 10 | Path to store backup files 11 | 12 | .EXAMPLE 13 | Invoke-IntuneBackupDeviceManagementIntent -Path "C:\temp" 14 | #> 15 | 16 | [CmdletBinding()] 17 | param( 18 | [Parameter(Mandatory = $true)] 19 | [string]$Path, 20 | 21 | [Parameter(Mandatory = $false)] 22 | [ValidateSet("v1.0", "Beta")] 23 | [string]$ApiVersion = "Beta" 24 | ) 25 | 26 | #Connect to MS-Graph if required 27 | if($null -eq (Get-MgContext)){ 28 | connect-mggraph -scopes "DeviceManagementApps.ReadWrite.All, DeviceManagementConfiguration.ReadWrite.All, DeviceManagementServiceConfig.ReadWrite.All, DeviceManagementManagedDevices.ReadWrite.All" 29 | } 30 | 31 | Write-Verbose "Requesting Intents" 32 | $intents = Get-MgBetaDeviceManagementIntent -all 33 | 34 | if ($intents.value -ne "") { 35 | 36 | # Create folder if not exists 37 | if (-not (Test-Path "$Path\Device Management Intents")) { 38 | $null = New-Item -Path "$Path\Device Management Intents" -ItemType Directory 39 | } 40 | 41 | foreach ($intent in $intents) { 42 | # Get the corresponding Device Management Template 43 | Write-Verbose "Requesting Template" 44 | $template = Get-MgBetaDeviceManagementTemplate -DeviceManagementTemplateId $($intent.templateId) 45 | $templateDisplayName = ($template.displayName).Split([IO.Path]::GetInvalidFileNameChars()) -join '_' 46 | 47 | if (-not (Test-Path "$Path\Device Management Intents\$templateDisplayName")) { 48 | $null = New-Item -Path "$Path\Device Management Intents\$templateDisplayName" -ItemType Directory 49 | } 50 | 51 | # Get all setting categories in the Device Management Template 52 | Write-Verbose "Requesting Template Categories" 53 | $templateCategories = Get-MgBetaDeviceManagementTemplateCategory -DeviceManagementTemplateId $($intent.templateId) -all 54 | 55 | $intentSettingsDelta = @() 56 | foreach ($templateCategory in $templateCategories) { 57 | # Get all configured values for the template categories 58 | Write-Verbose "Requesting Intent Setting Values" 59 | $intentSettingsDelta += Get-MgBetaDeviceManagementIntentCategorySetting -DeviceManagementIntentId: $($intent.id) -DeviceManagementIntentSettingCategoryId $($templateCategory.id) -all| ForEach-Object{ 60 | [PSCustomObject]@{ 61 | "@odata.type" = $_.AdditionalProperties."@odata.type" 62 | id = $_.id 63 | definitionId = $_.DefinitionId 64 | valueJson = $_.ValueJson 65 | value = $_.AdditionalProperties.value 66 | } 67 | } 68 | } 69 | 70 | $intentBackupValue = @{ 71 | "displayName" = $intent.displayName 72 | "description" = $intent.description 73 | "settingsDelta" = $intentSettingsDelta 74 | "roleScopeTagIds" = $intent.roleScopeTagIds 75 | } 76 | 77 | $fileName = ("$($template.id)_$($intent.displayName)").Split([IO.Path]::GetInvalidFileNameChars()) -join '_' 78 | $intentBackupValue | ConvertTo-Json -depth 10 | Out-File -LiteralPath "$path\Device Management Intents\$templateDisplayName\$fileName.json" 79 | 80 | [PSCustomObject]@{ 81 | "Action" = "Backup" 82 | "Type" = "Device Management Intent" 83 | "Name" = $intent.displayName 84 | "Path" = "Device Management Intents\$templateDisplayName\$fileName.json" 85 | } 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /IntuneBackupAndRestore/Public/Invoke-IntuneBackupDeviceManagementScript.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-IntuneBackupDeviceManagementScript { 2 | <# 3 | .SYNOPSIS 4 | Backup Intune Device Management Scripts 5 | 6 | .DESCRIPTION 7 | Backup Intune Device Management Scripts as JSON files per Device Management Script to the specified Path. 8 | 9 | .PARAMETER Path 10 | Path to store backup files 11 | 12 | .EXAMPLE 13 | Invoke-IntuneBackupDeviceManagementScript -Path "C:\temp" 14 | #> 15 | 16 | [CmdletBinding()] 17 | param( 18 | [Parameter(Mandatory = $true)] 19 | [string]$Path, 20 | 21 | [Parameter(Mandatory = $false)] 22 | [ValidateSet("v1.0", "Beta")] 23 | [string]$ApiVersion = "Beta" 24 | ) 25 | 26 | #Connect to MS-Graph if required 27 | if($null -eq (Get-MgContext)){ 28 | connect-mggraph -scopes "DeviceManagementApps.ReadWrite.All, DeviceManagementConfiguration.ReadWrite.All, DeviceManagementServiceConfig.ReadWrite.All, DeviceManagementManagedDevices.ReadWrite.All" 29 | } 30 | 31 | # Get all device management scripts 32 | $deviceManagementScripts = Invoke-MgGraphRequest -Uri "$ApiVersion/deviceManagement/deviceManagementScripts" | Get-MgGraphAllPages 33 | 34 | if ($deviceManagementScripts.value -ne "") { 35 | 36 | # Create folder if not exists 37 | if (-not (Test-Path "$Path\Device Management Scripts\Script Content")) { 38 | $null = New-Item -Path "$Path\Device Management Scripts\Script Content" -ItemType Directory 39 | } 40 | 41 | foreach ($deviceManagementScript in $deviceManagementScripts) { 42 | # ScriptContent returns null, so we have to query Microsoft Graph for each script 43 | $deviceManagementScriptObject = Invoke-MgGraphRequest -Uri "$ApiVersion/deviceManagement/deviceManagementScripts/$($deviceManagementScript.Id)" | Get-MgGraphAllPages 44 | $deviceManagementScriptFileName = ($deviceManagementScriptObject.displayName).Split([IO.Path]::GetInvalidFileNameChars()) -join '_' 45 | $deviceManagementScriptObject | ConvertTo-Json | Out-File -LiteralPath "$path\Device Management Scripts\$deviceManagementScriptFileName.json" 46 | 47 | $deviceManagementScriptContent = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($deviceManagementScriptObject.scriptContent)) 48 | $deviceManagementScriptContent | Out-File -LiteralPath "$path\Device Management Scripts\Script Content\$deviceManagementScriptFileName.ps1" 49 | 50 | [PSCustomObject]@{ 51 | "Action" = "Backup" 52 | "Type" = "Device Management Script" 53 | "Name" = $deviceManagementScript.displayName 54 | "Path" = "Device Management Scripts\$deviceManagementScriptFileName.json" 55 | } 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /IntuneBackupAndRestore/Public/Invoke-IntuneBackupDeviceManagementScriptAssignment.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-IntuneBackupDeviceManagementScriptAssignment { 2 | <# 3 | .SYNOPSIS 4 | Backup Intune Device Management Script Assignments 5 | 6 | .DESCRIPTION 7 | Backup Intune Device Management Script Assignments as JSON files per Device Management Script to the specified Path. 8 | 9 | .PARAMETER Path 10 | Path to store backup files 11 | 12 | .EXAMPLE 13 | Invoke-IntuneBackupDeviceManagementScriptAssignment -Path "C:\temp" 14 | #> 15 | 16 | [CmdletBinding()] 17 | param( 18 | [Parameter(Mandatory = $true)] 19 | [string]$Path, 20 | 21 | [Parameter(Mandatory = $false)] 22 | [ValidateSet("v1.0", "Beta")] 23 | [string]$ApiVersion = "Beta" 24 | ) 25 | 26 | #Connect to MS-Graph if required 27 | if($null -eq (Get-MgContext)){ 28 | connect-mggraph -scopes "DeviceManagementApps.ReadWrite.All, DeviceManagementConfiguration.ReadWrite.All, DeviceManagementServiceConfig.ReadWrite.All, DeviceManagementManagedDevices.ReadWrite.All" 29 | } 30 | 31 | # Get all assignments from all policies 32 | $deviceManagementScripts = Invoke-MgGraphRequest -Uri "$ApiVersion/deviceManagement/deviceManagementScripts" | Get-MgGraphAllPages 33 | 34 | if ($deviceManagementScripts.value -ne "") { 35 | 36 | # Create folder if not exists 37 | if (-not (Test-Path "$Path\Device Management Scripts\Assignments")) { 38 | $null = New-Item -Path "$Path\Device Management Scripts\Assignments" -ItemType Directory 39 | } 40 | 41 | foreach ($deviceManagementScript in $deviceManagementScripts) { 42 | $assignments = Invoke-MgGraphRequest -Uri "$ApiVersion/deviceManagement/deviceManagementScripts/$($deviceManagementScript.id)/assignments" | Get-MgGraphAllPages 43 | 44 | if ($assignments) { 45 | $fileName = ($deviceManagementScript.displayName).Split([IO.Path]::GetInvalidFileNameChars()) -join '_' 46 | $assignments | ConvertTo-Json | Out-File -LiteralPath "$path\Device Management Scripts\Assignments\$fileName.json" 47 | 48 | [PSCustomObject]@{ 49 | "Action" = "Backup" 50 | "Type" = "Device Management Script Assignments" 51 | "Name" = $deviceManagementScript.displayName 52 | "Path" = "Device Management Scripts\Assignments\$fileName.json" 53 | } 54 | } 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /IntuneBackupAndRestore/Public/Invoke-IntuneBackupGroupPolicyConfiguration.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-IntuneBackupGroupPolicyConfiguration { 2 | <# 3 | .SYNOPSIS 4 | Backup Intune Group Policy Configurations 5 | 6 | .DESCRIPTION 7 | Backup Intune Group Policy Configurations as JSON files per Group Policy Configuration Policy to the specified Path. 8 | 9 | .PARAMETER Path 10 | Path to store backup files 11 | 12 | .EXAMPLE 13 | Invoke-IntuneBackupGroupPolicyConfiguration -Path "C:\temp" 14 | #> 15 | 16 | [CmdletBinding()] 17 | param( 18 | [Parameter(Mandatory = $true)] 19 | [string]$Path, 20 | 21 | [Parameter(Mandatory = $false)] 22 | [ValidateSet("v1.0", "Beta")] 23 | [string]$ApiVersion = "Beta" 24 | ) 25 | 26 | #Connect to MS-Graph if required 27 | if ($null -eq (Get-MgContext)) { 28 | connect-mggraph -scopes "DeviceManagementApps.ReadWrite.All, DeviceManagementConfiguration.ReadWrite.All, DeviceManagementServiceConfig.ReadWrite.All, DeviceManagementManagedDevices.ReadWrite.All" 29 | } 30 | 31 | # Get all Group Policy Configurations 32 | $groupPolicyConfigurations = Invoke-MgGraphRequest -Uri "$ApiVersion/deviceManagement/groupPolicyConfigurations" | Get-MgGraphAllPages 33 | 34 | if ($groupPolicyConfigurations.value -ne "") { 35 | 36 | # Create folder if not exists 37 | if (-not (Test-Path "$Path\Administrative Templates")) { 38 | $null = New-Item -Path "$Path\Administrative Templates" -ItemType Directory 39 | } 40 | 41 | foreach ($groupPolicyConfiguration in $groupPolicyConfigurations) { 42 | $groupPolicyDefinitionValues = Invoke-MgGraphRequest -Uri "$ApiVersion/deviceManagement/groupPolicyConfigurations/$($groupPolicyConfiguration.id)/definitionValues" | Get-MgGraphAllPages 43 | $groupPolicyBackupValues = @() 44 | 45 | foreach ($groupPolicyDefinitionValue in $groupPolicyDefinitionValues) { 46 | $groupPolicyDefinition = Invoke-MgGraphRequest -Uri "$ApiVersion/deviceManagement/groupPolicyConfigurations/$($groupPolicyConfiguration.id)/definitionValues/$($groupPolicyDefinitionValue.id)/definition" 47 | $groupPolicyPresentationValues = (Invoke-MgGraphRequest -Uri "$ApiVersion/deviceManagement/groupPolicyConfigurations/$($groupPolicyConfiguration.id)/definitionValues/$($groupPolicyDefinitionValue.id)/presentationValues?`$expand=presentation" -OutputType PSObject).Value | Select-Object -Property * -ExcludeProperty lastModifiedDateTime, createdDateTime 48 | 49 | $groupPolicyBackupValue = @{ 50 | "enabled" = $groupPolicyDefinitionValue.enabled 51 | "definition@odata.bind" = "https://graph.microsoft.com/beta/deviceManagement/groupPolicyDefinitions('$($groupPolicyDefinition.id)')" 52 | } 53 | 54 | if ($groupPolicyPresentationValues.value) { 55 | $groupPolicyBackupValue."presentationValues" = @() 56 | foreach ($groupPolicyPresentationValue in $groupPolicyPresentationValues) { 57 | $groupPolicyBackupValue."presentationValues" += 58 | @{ 59 | "@odata.type" = $groupPolicyPresentationValue.'@odata.type' 60 | "value" = $groupPolicyPresentationValue.value 61 | "presentation@odata.bind" = "https://graph.microsoft.com/beta/deviceManagement/groupPolicyDefinitions('$($groupPolicyDefinition.id)')/presentations('$($groupPolicyPresentationValue.presentation.id)')" 62 | } 63 | } 64 | } 65 | elseif ($groupPolicyPresentationValues.values) { 66 | $groupPolicyBackupValue."presentationValues" = @( 67 | @{ 68 | "@odata.type" = $groupPolicyPresentationValues.'@odata.type' 69 | "values" = @( 70 | foreach ($groupPolicyPresentationValue in $groupPolicyPresentationValues.values) { 71 | @{ 72 | "name" = $groupPolicyPresentationValue.name 73 | "value" = $groupPolicyPresentationValue.value 74 | } 75 | } 76 | ) 77 | "presentation@odata.bind" = "https://graph.microsoft.com/beta/deviceManagement/groupPolicyDefinitions('$($groupPolicyDefinition.id)')/presentations('$($groupPolicyPresentationValues.presentation.id)')" 78 | } 79 | ) 80 | } 81 | 82 | $groupPolicyBackupValues += $groupPolicyBackupValue 83 | } 84 | 85 | $fileName = ($groupPolicyConfiguration.displayName).Split([IO.Path]::GetInvalidFileNameChars()) -join '_' 86 | $groupPolicyBackupValues | ConvertTo-Json -Depth 100 | Out-File -LiteralPath "$path\Administrative Templates\$fileName.json" 87 | 88 | [PSCustomObject]@{ 89 | "Action" = "Backup" 90 | "Type" = "Administrative Template" 91 | "Name" = $groupPolicyConfiguration.displayName 92 | "Path" = "Administrative Templates\$fileName.json" 93 | } 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /IntuneBackupAndRestore/Public/Invoke-IntuneBackupGroupPolicyConfigurationAssignment.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-IntuneBackupGroupPolicyConfigurationAssignment { 2 | <# 3 | .SYNOPSIS 4 | Backup Intune Group Policy Configuration Assignments 5 | 6 | .DESCRIPTION 7 | Backup Intune Group Policy Configuration Assignments as JSON files per Group Policy Configuration Policy to the specified Path. 8 | 9 | .PARAMETER Path 10 | Path to store backup files 11 | 12 | .EXAMPLE 13 | Invoke-IntuneBackupGroupPolicyConfigurationAssignment -Path "C:\temp" 14 | #> 15 | 16 | [CmdletBinding()] 17 | param( 18 | [Parameter(Mandatory = $true)] 19 | [string]$Path, 20 | 21 | [Parameter(Mandatory = $false)] 22 | [ValidateSet("v1.0", "Beta")] 23 | [string]$ApiVersion = "Beta" 24 | ) 25 | 26 | #Connect to MS-Graph if required 27 | if ($null -eq (Get-MgContext)) { 28 | connect-mggraph -scopes "DeviceManagementApps.ReadWrite.All, DeviceManagementConfiguration.ReadWrite.All, DeviceManagementServiceConfig.ReadWrite.All, DeviceManagementManagedDevices.ReadWrite.All" 29 | } 30 | 31 | # Get all assignments from all policies 32 | $groupPolicyConfigurations = Invoke-MgGraphRequest -Uri "$ApiVersion/deviceManagement/groupPolicyConfigurations" | Get-MgGraphAllPages 33 | 34 | if ($groupPolicyConfigurations.value -ne "") { 35 | 36 | # Create folder if not exists 37 | if (-not (Test-Path "$Path\Administrative Templates\Assignments")) { 38 | $null = New-Item -Path "$Path\Administrative Templates\Assignments" -ItemType Directory 39 | } 40 | 41 | foreach ($groupPolicyConfiguration in $groupPolicyConfigurations) { 42 | $assignments = Invoke-MgGraphRequest -Uri "$ApiVersion/deviceManagement/groupPolicyConfigurations/$($groupPolicyConfiguration.id)/assignments" | Get-MgGraphAllPages 43 | 44 | if ($assignments) { 45 | $fileName = ($groupPolicyConfiguration.displayName).Split([IO.Path]::GetInvalidFileNameChars()) -join '_' 46 | $assignments | ConvertTo-Json | Out-File -LiteralPath "$path\Administrative Templates\Assignments\$fileName.json" 47 | 48 | [PSCustomObject]@{ 49 | "Action" = "Backup" 50 | "Type" = "Administrative Template Assignments" 51 | "Name" = $groupPolicyConfiguration.displayName 52 | "Path" = "Administrative Templates\Assignments\$fileName.json" 53 | } 54 | } 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /IntuneBackupAndRestore/Public/Invoke-IntuneRestoreAppProtectionPolicy.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-IntuneRestoreAppProtectionPolicy { 2 | <# 3 | .SYNOPSIS 4 | Restore Intune App Protection Policy 5 | 6 | .DESCRIPTION 7 | Restore Intune App Protection Policies from JSON files per App Protection Policy from the specified Path. 8 | 9 | .PARAMETER Path 10 | Root path where backup files are located, created with the Invoke-IntuneBackupAppProtectionPolicy function 11 | 12 | .EXAMPLE 13 | Invoke-IntuneRestoreAppProtectionPolicy -Path "C:\temp" -RestoreById $true 14 | #> 15 | [CmdletBinding()] 16 | param( 17 | [Parameter(Mandatory = $true)] 18 | [string]$Path, 19 | 20 | [Parameter(Mandatory = $false)] 21 | [ValidateSet("v1.0", "Beta")] 22 | [string]$ApiVersion = "Beta" 23 | ) 24 | 25 | #Connect to MS-Graph if required 26 | if ($null -eq (Get-MgContext)) { 27 | connect-mggraph -scopes "DeviceManagementApps.ReadWrite.All, DeviceManagementConfiguration.ReadWrite.All, DeviceManagementServiceConfig.ReadWrite.All, DeviceManagementManagedDevices.ReadWrite.All" 28 | } 29 | 30 | # Get all App Protection Policies 31 | $appProtectionPolicies = Get-ChildItem -Path "$path\App Protection Policies" -File -ErrorAction SilentlyContinue 32 | 33 | foreach ($appProtectionPolicy in $appProtectionPolicies) { 34 | $appProtectionPolicyContent = Get-Content -LiteralPath $appProtectionPolicy.FullName | convertfrom-json 35 | $appProtectionPolicyDisplayName = $appProtectionPolicyContent.displayName 36 | 37 | # Remove properties that are not available for creating a new configuration 38 | $requestBodyObject = $appProtectionPolicyContent 39 | # Set SupportsScopeTags to $false, because $true currently returns an HTTP Status 400 Bad Request error. 40 | if ($requestBodyObject.supportsScopeTags) { 41 | $requestBodyObject.supportsScopeTags = $false 42 | } 43 | 44 | $requestBodyObject.PSObject.Properties | Foreach-Object { 45 | if ($null -ne $_.Value) { 46 | if ($_.Value.GetType().Name -eq "DateTime") { 47 | $_.Value = (Get-Date -Date $_.Value -Format s) + "Z" 48 | } 49 | } 50 | } 51 | 52 | $requestBody = $requestBodyObject | Select-Object -Property * -ExcludeProperty id, createdDateTime, lastModifiedDateTime, version | ConvertTo-Json -Depth 100 53 | 54 | # Restore the App Protection Policy 55 | try { 56 | $null = Invoke-MgGraphRequest -Method POST -Body $requestBody.toString() -Uri "$ApiVersion/deviceAppManagement/managedAppPolicies" -ErrorAction Stop 57 | 58 | [PSCustomObject]@{ 59 | "Action" = "Restore" 60 | "Type" = "App Protection Policy" 61 | "Name" = $appProtectionPolicyDisplayName 62 | "Path" = "App Protection Policies\$($appProtectionPolicy.Name)" 63 | } 64 | } 65 | catch { 66 | Write-Verbose "$appProtectionPolicyDisplayName - Failed to restore App Protection Policy" -Verbose 67 | Write-Error $_ -ErrorAction Continue 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /IntuneBackupAndRestore/Public/Invoke-IntuneRestoreAppProtectionPolicyAssignment.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-IntuneRestoreAppProtectionPolicyAssignment { 2 | <# 3 | .SYNOPSIS 4 | Restore App Protection Policy Assignments (excluding managedAndroidStoreApp and managedIOSStoreApp) 5 | 6 | .DESCRIPTION 7 | Restore App Protection Policy Assignments from JSON files per App Protection Policy from the specified Path. 8 | 9 | .PARAMETER Path 10 | Root path where backup files are located, created with the Invoke-IntuneBackupAppProtectionPolicyAssignment function 11 | 12 | .PARAMETER RestoreById 13 | If RestoreById is set to false, assignments will be restored to App Protection Policies that match the file name. 14 | This is necessary if the App Protection Policy was restored from backup, because then a new App Protection Policy is created with a new unique ID. 15 | 16 | .EXAMPLE 17 | Invoke-IntuneRestoreAppProtectionPolicyAssignment -Path "C:\temp" -RestoreById $true 18 | #> 19 | 20 | [CmdletBinding()] 21 | param( 22 | [Parameter(Mandatory = $true)] 23 | [string]$Path, 24 | 25 | [Parameter(Mandatory = $false)] 26 | [bool]$RestoreById = $true, 27 | 28 | [Parameter(Mandatory = $false)] 29 | [ValidateSet("v1.0", "Beta")] 30 | [string]$ApiVersion = "Beta" 31 | ) 32 | 33 | #Connect to MS-Graph if required 34 | if ($null -eq (Get-MgContext)) { 35 | connect-mggraph -scopes "DeviceManagementApps.ReadWrite.All, DeviceManagementConfiguration.ReadWrite.All, DeviceManagementServiceConfig.ReadWrite.All, DeviceManagementManagedDevices.ReadWrite.All" 36 | } 37 | 38 | # Get all policies with assignments 39 | $appProtectionPolicies = Get-ChildItem -Path "$Path\App Protection Policies\Assignments" -File -ErrorAction SilentlyContinue 40 | foreach ($appProtectionPolicy in $appProtectionPolicies) { 41 | $appProtectionPolicyAssignments = Get-Content -LiteralPath $appProtectionPolicy.FullName | ConvertFrom-Json 42 | $appProtectionPolicyId = ($appProtectionPolicy.BaseName -split " - ")[0] 43 | $appProtectionPolicyName = (($appProtectionPolicy.BaseName -split " - ", 2)[-1]) 44 | 45 | # Create the base requestBody 46 | $requestBody = @{ 47 | assignments = @() 48 | } 49 | 50 | # Add assignments to restore to the request body 51 | foreach ($appProtectionPolicyAssignment in $appProtectionPolicyAssignments.Value) { 52 | $requestBody.assignments += @{ 53 | "target" = $appProtectionPolicyAssignment.target | Select-Object -Property * -ExcludeProperty deviceAndAppManagementAssignmentFilterId, deviceAndAppManagementAssignmentFilterType 54 | } 55 | } 56 | 57 | # Convert the PowerShell object to JSON 58 | $requestBody = $requestBody | ConvertTo-Json -Depth 100 59 | 60 | # Get the App Protection Policy we are restoring the assignments for 61 | try { 62 | if ($restoreById) { 63 | $appProtectionPolicyObject = Invoke-MgGraphRequest -Uri "/$ApiVersion/deviceAppManagement/managedAppPolicies/$appProtectionPolicyId" 64 | } 65 | else { 66 | $appProtectionPolicyObject = Invoke-MgGraphRequest -Uri "/$ApiVersion/deviceAppManagement/managedAppPolicies/" | Get-MGGraphAllPages | Where-Object { $_.displayName -eq $appProtectionPolicyName } 67 | if (-not ($appProtectionPolicyObject)) { 68 | Write-Warning "Error retrieving App Protection Policy for $appProtectionPolicyName. Skipping assignment restore" 69 | continue 70 | } 71 | } 72 | } 73 | catch { 74 | Write-Verbose "Error retrieving App Protection Policy for $appProtectionPolicyName, does it exist in the Intune tenant? Skipping assignment restore ..." -Verbose 75 | Write-Error $_ -ErrorAction Continue 76 | continue 77 | } 78 | 79 | # Restore the assignments 80 | try { 81 | # If Android 82 | if ($appProtectionPolicyObject.'@odata.type' -eq '#microsoft.graph.androidManagedAppProtection') { 83 | $null = Invoke-MgGraphRequest -Method POST -Body $requestBody.toString() -Uri "$ApiVersion/deviceAppManagement/androidManagedAppProtections/$($appProtectionPolicyObject.id)/assign" -ErrorAction Stop 84 | } 85 | # Elseif iOS 86 | elseif ($appProtectionPolicyObject.'@odata.type' -eq '#microsoft.graph.iosManagedAppProtection') { 87 | $null = Invoke-MgGraphRequest -Method POST -Body $requestBody.toString() -Uri "$ApiVersion/deviceAppManagement/iosManagedAppProtections/$($appProtectionPolicyObject.id)/assign" -ErrorAction Stop 88 | } 89 | # Elseif Windows 10 with enrollment 90 | elseif ($appProtectionPolicyObject.'@odata.type' -eq '#microsoft.graph.mdmWindowsInformationProtectionPolicy') { 91 | $null = Invoke-MgGraphRequest -Method POST -Body $requestBody.toString() -Uri "$ApiVersion/deviceAppManagement/mdmWindowsInformationProtectionPolicies/$($appProtectionPolicyObject.id)/assign" -ErrorAction Stop 92 | } 93 | # Elseif Windows 10 without Enrollment 94 | elseif ($appProtectionPolicyObject.'@odata.type' -eq '#microsoft.graph.windowsInformationProtectionPolicy') { 95 | $null = Invoke-MgGraphRequest -Method POST -Body $requestBody.toString() -Uri "$ApiVersion/deviceAppManagement/windowsInformationProtectionPolicies/$($appProtectionPolicyObject.id)/assign" -ErrorAction Stop 96 | } 97 | 98 | [PSCustomObject]@{ 99 | "Action" = "Restore" 100 | "Type" = "App Protection Policy Assignments" 101 | "Name" = $appProtectionPolicyName 102 | "Path" = "App Protection Policies\Assignments\$($appProtectionPolicy.Name)" 103 | } 104 | } 105 | catch { 106 | if ($_.Exception.Message -match "The App Protection Policy Assignment already exist") { 107 | Write-Verbose "$($appProtectionPolicyObject.displayName) - The App Protection Policy Assignment already exists" -Verbose 108 | } 109 | else { 110 | Write-Verbose "$($appProtectionPolicyObject.displayName) - Failed to restore App Protection Policy Assignment(s)" -Verbose 111 | Write-Error $_ -ErrorAction Continue 112 | } 113 | } 114 | } 115 | } -------------------------------------------------------------------------------- /IntuneBackupAndRestore/Public/Invoke-IntuneRestoreAutopilotDeploymentProfile.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-IntuneRestoreAutopilotDeploymentProfile { 2 | <# 3 | .SYNOPSIS 4 | Restore Intune Autopilot Deployment Profiles 5 | 6 | .DESCRIPTION 7 | Restore Intune Autopilot Deployment Profiles from JSON files per Deployment Profile from the specified Path. 8 | 9 | .PARAMETER Path 10 | Root path where backup files are located, created with the Invoke-IntuneBackupAutopilotDeploymentProfile function 11 | 12 | .EXAMPLE 13 | Invoke-IntuneRestoreAutopilotDeploymentProfile -Path "C:\temp" 14 | #> 15 | 16 | [CmdletBinding()] 17 | param( 18 | [Parameter(Mandatory = $true)] 19 | [string]$Path, 20 | 21 | [Parameter(Mandatory = $false)] 22 | [ValidateSet("v1.0", "Beta")] 23 | [string]$ApiVersion = "Beta" 24 | ) 25 | 26 | #Connect to MS-Graph if required 27 | if($null -eq (Get-MgContext)){ 28 | Connect-MgGraph -Scopes "DeviceManagementApps.ReadWrite.All, DeviceManagementConfiguration.ReadWrite.All, DeviceManagementServiceConfig.ReadWrite.All, DeviceManagementManagedDevices.ReadWrite.All" 29 | } 30 | 31 | # Get all device health scripts 32 | $winAutopilotDeploymentProfiles = Get-ChildItem -Path "$Path\Autopilot Deployment Profiles" -File -ErrorAction SilentlyContinue 33 | 34 | foreach ($winAutopilotDeploymentProfile in $winAutopilotDeploymentProfiles) { 35 | $winAutopilotDeploymentProfileContent = Get-Content -LiteralPath $winAutopilotDeploymentProfile.FullName -Raw 36 | $winAutopilotDeploymentProfileDisplayName = ($winAutopilotDeploymentProfileContent | ConvertFrom-Json).displayName 37 | 38 | # Remove properties that are not available for creating a new profile 39 | $requestBodyObject = $winAutopilotDeploymentProfileContent | ConvertFrom-Json 40 | $requestBody = $requestBodyObject | Select-Object -Property * -ExcludeProperty id, createdDateTime, lastModifiedDateTime | ConvertTo-Json 41 | 42 | # Restore the Deployment Profile 43 | try { 44 | $null = Invoke-MgGraphRequest -Method POST -Body $requestBody.toString() -Uri "$ApiVersion/deviceManagement/windowsAutopilotDeploymentProfiles" -ErrorAction Stop 45 | [PSCustomObject]@{ 46 | "Action" = "Restore" 47 | "Type" = "Autopilot Deployment Profile" 48 | "Name" = $winAutopilotDeploymentProfileDisplayName 49 | "Path" = "Autopilot Deployment Profiles\$($winAutopilotDeploymentProfile.Name)" 50 | } 51 | } 52 | catch { 53 | Write-Verbose "$winAutopilotDeploymentProfile - Failed to restore Autopilot Deployment Profile" -Verbose 54 | Write-Error $_ -ErrorAction Continue 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /IntuneBackupAndRestore/Public/Invoke-IntuneRestoreAutopilotDeploymentProfileAssignment.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-IntuneRestoreAutopilotDeploymentProfileAssignment { 2 | <# 3 | .SYNOPSIS 4 | Restore Intune Autopilot Deployment Profile Assignments 5 | 6 | .DESCRIPTION 7 | Restore Intune Autopilot Deployment Profile Assignments from JSON files per profile from the specified Path. 8 | 9 | .PARAMETER Path 10 | Root path where backup files are located, created with the Invoke-IntuneBackupAutopilotDeploymentProfileAssignment function 11 | 12 | .PARAMETER RestoreById 13 | If RestoreById is set to true, assignments will be restored to Intune Autopilot Deployment Profiles that match the id. 14 | 15 | If RestoreById is set to false, assignments will be restored to Intune Autopilot Deployment Profiles that match the file name. 16 | This is necessary if the Autopilot Deployment Profile was restored from backup, because then a new Autopilot Deployment Profile is created with a new unique ID. 17 | 18 | .EXAMPLE 19 | Invoke-IntuneRestoreDeviceHealthScriptAssignment -Path "C:\temp" -RestoreById $true 20 | #> 21 | 22 | [CmdletBinding()] 23 | param( 24 | [Parameter(Mandatory = $true)] 25 | [string]$Path, 26 | 27 | [Parameter(Mandatory = $false)] 28 | [bool]$RestoreById = $false, 29 | 30 | [Parameter(Mandatory = $false)] 31 | [ValidateSet("v1.0", "Beta")] 32 | [string]$ApiVersion = "Beta" 33 | ) 34 | 35 | # Get all profiles with assignments 36 | $winAutopilotDeploymentProfiles = Get-ChildItem -Path "$Path\Autopilot Deployment Profiles\Assignments" -File -ErrorAction SilentlyContinue 37 | 38 | foreach ($winAutopilotDeploymentProfile in $winAutopilotDeploymentProfiles) { 39 | $winAutopilotDeploymentProfileAssignments = Get-Content -LiteralPath $winAutopilotDeploymentProfile.FullName | ConvertFrom-Json 40 | $winAutopilotDeploymentProfileId = ($winAutopilotDeploymentProfileAssignments[0]).id.Split(":")[0] 41 | 42 | # Create the base requestBody 43 | $requestBody = @{ 44 | winAutopilotDeploymentProfileAssignments = @() 45 | } 46 | 47 | # Add assignments to restore to the request body 48 | foreach ($winAutopilotDeploymentProfileAssignment in $winAutopilotDeploymentProfileAssignments) { 49 | $requestBody.winAutopilotDeploymentProfileAssignments += @{ 50 | "target" = $winAutopilotDeploymentProfileAssignment.target 51 | "source" = $winAutopilotDeploymentProfileAssignment.source 52 | "sourceId" = $winAutopilotDeploymentProfileAssignment.sourceId 53 | } 54 | } 55 | 56 | # Convert the PowerShell object to JSON 57 | $requestBody = $requestBody | ConvertTo-Json -Depth 100 58 | 59 | # Get the Autopilot Deployment Profile we are restoring the assignments for 60 | try { 61 | if ($restoreById) { 62 | $winAutopilotDeploymentProfileObject = Invoke-MgGraphRequest -Uri "deviceManagement/windowsAutopilotDeploymentProfiles/$winAutopilotDeploymentProfileId" 63 | } 64 | else { 65 | $winAutopilotDeploymentProfileObject = Invoke-MgGraphRequest -Uri "deviceManagement/windowsAutopilotDeploymentProfiles" | Get-MGGraphAllPages | Where-Object displayName -eq "$($winAutopilotDeploymentProfile.BaseName)" 66 | if (-not ($winAutopilotDeploymentProfileObject)) { 67 | Write-Verbose "Error retrieving Intune Autopilot Deployment Profile for $($winAutopilotDeploymentProfile.FullName). Skipping assignment restore" -Verbose 68 | continue 69 | } 70 | } 71 | } 72 | catch { 73 | Write-Verbose "Error retrieving Intune Autopilot Deployment Profile for $($winAutopilotDeploymentProfile.FullName). Skipping assignment restore" -Verbose 74 | Write-Error $_ -ErrorAction Continue 75 | continue 76 | } 77 | 78 | # Restore the assignments 79 | try { 80 | # FIXME: look into why POSTing to this Graph API endpoint currently results in error "403 Forbidden - FeatureNotEnabled", 81 | # although the user account has the required permissions as documented in https://docs.microsoft.com/en-us/graph/api/intune-shared-windowsautopilotdeploymentprofile-assign?view=graph-rest-beta 82 | $null = Invoke-MgGraphRequest -Method POST -Content $requestBody.toString() -Uri "deviceManagement/windowsAutopilotDeploymentProfiles/$($winAutopilotDeploymentProfileObject.id)/assign" -ErrorAction Stop 83 | [PSCustomObject]@{ 84 | "Action" = "Restore" 85 | "Type" = "Autopilot Deployment Profile Assignments" 86 | "Name" = $winAutopilotDeploymentProfileObject.displayName 87 | "Path" = "Autopilot Deployment Profiles\Assignments\$($winAutopilotDeploymentProfile.Name)" 88 | } 89 | } 90 | catch { 91 | Write-Verbose "$($winAutopilotDeploymentObject.displayName) - Failed to restore Autopilot Deployment Profile Assignment(s)" -Verbose 92 | Write-Error $_ -ErrorAction Continue 93 | } 94 | } 95 | } -------------------------------------------------------------------------------- /IntuneBackupAndRestore/Public/Invoke-IntuneRestoreClientAppAssignment.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-IntuneRestoreClientAppAssignment { 2 | <# 3 | .SYNOPSIS 4 | Restore Intune Client App Assignments (excluding managedAndroidStoreApp and managedIOSStoreApp) 5 | 6 | .DESCRIPTION 7 | Restore Intune Client App Assignments from JSON files per Client App from the specified Path. 8 | 9 | .PARAMETER Path 10 | Root path where backup files are located, created with the Invoke-IntuneBackupClientAppAssignment function 11 | 12 | .PARAMETER RestoreById 13 | If RestoreById is set to false, assignments will be restored to Intune Client Apps that match the file name. 14 | This is necessary if the Client App was restored from backup, because then a new Client App is created with a new unique ID. 15 | 16 | .EXAMPLE 17 | Invoke-IntuneRestoreClientAppAssignment -Path "C:\temp" -RestoreById $true 18 | #> 19 | 20 | [CmdletBinding()] 21 | param( 22 | [Parameter(Mandatory = $true)] 23 | [string]$Path, 24 | 25 | [Parameter(Mandatory = $false)] 26 | [bool]$RestoreById = $true, 27 | 28 | [Parameter(Mandatory = $false)] 29 | [ValidateSet("v1.0", "Beta")] 30 | [string]$ApiVersion = "Beta" 31 | ) 32 | 33 | #Connect to MS-Graph if required 34 | if ($null -eq (Get-MgContext)) { 35 | connect-mggraph -scopes "DeviceManagementApps.ReadWrite.All, DeviceManagementConfiguration.ReadWrite.All, DeviceManagementServiceConfig.ReadWrite.All, DeviceManagementManagedDevices.ReadWrite.All" 36 | } 37 | 38 | # Get all policies with assignments 39 | $clientAppsAssignmentItems = Get-ChildItem -Path "$Path\Client Apps\Assignments" -File -ErrorAction SilentlyContinue 40 | $clientApps = Get-ChildItem -Path "$Path\Client Apps" -File -ErrorAction SilentlyContinue 41 | 42 | foreach ($clientApp in $clientAppsAssignmentItems) { 43 | $clientAppAssignments = Get-Content -LiteralPath $clientApp.FullName | ConvertFrom-Json 44 | $clientAppId = ($clientApp.BaseName -split " - ")[0] 45 | $clientAppName = ($clientApp.BaseName -split " - ",2)[-1] 46 | 47 | # Create the base requestBody 48 | $requestBody = @{ 49 | mobileAppAssignments = @() 50 | } 51 | 52 | # Add assignments to restore to the request body 53 | foreach ($clientAppAssignment in $clientAppAssignments) { 54 | 55 | $clientAppAssignment.settings.installTimeSettings.PSObject.Properties | Foreach-Object { 56 | if ($null -ne $_.Value) { 57 | if ($_.Value.GetType().Name -eq "DateTime") { 58 | $_.Value = (Get-Date -Date $_.Value -Format s) + "Z" 59 | } 60 | } 61 | } 62 | 63 | $requestBody.mobileAppAssignments += @{ 64 | "target" = $clientAppAssignment.target 65 | "intent" = $clientAppAssignment.intent 66 | "settings" = $clientAppAssignment.settings 67 | } 68 | } 69 | 70 | # Convert the PowerShell object to JSON 71 | $requestBody = $requestBody | ConvertTo-Json -Depth 100 72 | 73 | # Get the Client App we are restoring the assignments for 74 | try { 75 | if ($restoreById) { 76 | $clientAppObject = Invoke-MgGraphRequest -Uri "$apiversion/deviceAppManagement/mobileApps/$clientAppId" 77 | } 78 | else { 79 | $clientAppObject = Invoke-MgGraphRequest -Uri "$apiversion/deviceAppManagement/mobileApps/" | Get-MgGraphAllPages | Where-Object { $_.displayName -eq "$($clientAppName)" -and $_.'@odata.type' -ne "#microsoft.graph.managedAndroidStoreApp" -and $_.'@odata.type' -ne "#microsoft.graph.managedIOSStoreApp" } 80 | if (-not ($clientAppObject)) { 81 | Write-Warning "Error retrieving Intune Client App for $($clientApp.FullName). Skipping assignment restore" 82 | continue 83 | } 84 | } 85 | } 86 | catch { 87 | Write-Verbose "Error retrieving Intune Client App for $($clientApp.FullName), does it exist in the Intune tenant? Skipping assignment restore ..." -Verbose 88 | Write-Error $_ -ErrorAction Continue 89 | continue 90 | } 91 | 92 | # Restore the assignments 93 | try { 94 | $null = Invoke-MgGraphRequest -Method POST -Body $requestBody.toString() -Uri "$ApiVersion/deviceAppManagement/mobileApps/$($clientAppObject.id)/assign" -ErrorAction Stop 95 | [PSCustomObject]@{ 96 | "Action" = "Restore" 97 | "Type" = "Client App Assignments" 98 | "Name" = $clientAppObject.displayName 99 | "Path" = "Client Apps\Assignments\$($clientApp.Name)" 100 | } 101 | } 102 | catch { 103 | if ($_.Exception.Message -match "The MobileApp Assignment already exist") { 104 | Write-Verbose "$($clientAppObject.displayName) - The Client App Assignment already exists" -Verbose 105 | } 106 | else { 107 | Write-Verbose "$($clientAppObject.displayName) - Failed to restore Client App Assignment(s)" -Verbose 108 | Write-Error $_ -ErrorAction Continue 109 | } 110 | } 111 | } 112 | } -------------------------------------------------------------------------------- /IntuneBackupAndRestore/Public/Invoke-IntuneRestoreConfigurationPolicy.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-IntuneRestoreConfigurationPolicy { 2 | <# 3 | .SYNOPSIS 4 | Restore Intune Settings Catalog Policies 5 | 6 | .DESCRIPTION 7 | Restore Intune Settings Catalog Policies from JSON files per Settings Catalog Policy from the specified Path. 8 | 9 | .PARAMETER Path 10 | Root path where backup files are located, created with the Invoke-IntuneBackupConfigurationPolicy function 11 | 12 | .EXAMPLE 13 | Invoke-IntuneRestoreConfigurationPolicy -Path "C:\temp" 14 | #> 15 | 16 | [CmdletBinding()] 17 | param( 18 | [Parameter(Mandatory = $true)] 19 | [string]$Path, 20 | 21 | [Parameter(Mandatory = $false)] 22 | [ValidateSet("v1.0", "Beta")] 23 | [string]$ApiVersion = "Beta" 24 | ) 25 | 26 | #Connect to MS-Graph if required 27 | if($null -eq (Get-MgContext)){ 28 | connect-mggraph -scopes "DeviceManagementApps.ReadWrite.All, DeviceManagementConfiguration.ReadWrite.All, DeviceManagementServiceConfig.ReadWrite.All, DeviceManagementManagedDevices.ReadWrite.All" 29 | } 30 | 31 | # Get all Settings Catalog Policies 32 | $configurationPolicies = Get-ChildItem -Path "$Path\Settings Catalog" -File -ErrorAction SilentlyContinue 33 | 34 | foreach ($configurationPolicy in $configurationPolicies) { 35 | $configurationPolicyContent = Get-Content -LiteralPath $configurationPolicy.FullName -Raw | ConvertFrom-Json 36 | 37 | # Remove properties that are not available for creating a new configuration 38 | $requestBody = $configurationPolicyContent | Select-Object -Property * -ExcludeProperty id, createdDateTime, lastModifiedDateTime, settingCount, creationSource | ConvertTo-Json -Depth 100 39 | 40 | # Restore the Settings Catalog Policy 41 | try { 42 | $null = Invoke-MgGraphRequest -Method POST -Body $requestBody.toString() -Uri "$ApiVersion/deviceManagement/configurationPolicies" -ErrorAction Stop 43 | [PSCustomObject]@{ 44 | "Action" = "Restore" 45 | "Type" = "Settings Catalog" 46 | "Name" = $configurationPolicy.BaseName 47 | "Path" = "Settings Catalog\$($configurationPolicy.Name)" 48 | } 49 | } 50 | catch { 51 | Write-Verbose "$($configurationPolicy.FullName) - Failed to restore Settings Catalog Policy" -Verbose 52 | Write-Error $_ -ErrorAction Continue 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /IntuneBackupAndRestore/Public/Invoke-IntuneRestoreConfigurationPolicyAssignment.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-IntuneRestoreConfigurationPolicyAssignment { 2 | <# 3 | .SYNOPSIS 4 | Restore Intune Configuration Policy Assignments 5 | 6 | .DESCRIPTION 7 | Restore Intune Configuration Policy Assignments from JSON files per Configuration Policy from the specified Path. 8 | 9 | .PARAMETER Path 10 | Root path where backup files are located, created with the Invoke-IntuneBackupConfigurationPolicyAssignment function 11 | 12 | .PARAMETER RestoreById 13 | If RestoreById is set to true, assignments will be restored to Intune Device Management Scripts that match the id. 14 | 15 | If RestoreById is set to false, assignments will be restored to Intune Device Management Scripts that match the file name. 16 | This is necessary if the Device Management Script was restored from backup, because then a new Device Management Script is created with a new unique ID. 17 | 18 | .EXAMPLE 19 | Invoke-IntuneBackupConfigurationPolicyAssignment -Path "C:\temp" -RestoreById $true 20 | #> 21 | [CmdletBinding()] 22 | param( 23 | [Parameter(Mandatory = $true)] 24 | [string]$Path, 25 | 26 | [Parameter(Mandatory = $false)] 27 | [bool]$RestoreById = $false, 28 | 29 | [Parameter(Mandatory = $false)] 30 | [ValidateSet("v1.0", "Beta")] 31 | [string]$ApiVersion = "Beta" 32 | ) 33 | 34 | #Connect to MS-Graph if required 35 | if($null -eq (Get-MgContext)){ 36 | connect-mggraph -scopes "DeviceManagementApps.ReadWrite.All, DeviceManagementConfiguration.ReadWrite.All, DeviceManagementServiceConfig.ReadWrite.All, DeviceManagementManagedDevices.ReadWrite.All" 37 | } 38 | 39 | # Get all policies with assignments 40 | $configurationPolicies = Get-ChildItem -Path "$Path\Settings Catalog\Assignments" -File -ErrorAction SilentlyContinue 41 | 42 | foreach ($configurationPolicy in $configurationPolicies) { 43 | $configurationPolicyAssignments = Get-Content -LiteralPath $configurationPolicy.FullName | ConvertFrom-Json 44 | $configurationPolicyId = ($configurationPolicyAssignments[0]).id.Split("_")[0] 45 | $configurationPolicyName = $configurationPolicy.BaseName 46 | 47 | # Create the base requestBody 48 | $requestBody = @{ 49 | assignments = @() 50 | } 51 | 52 | # Add assignments to restore to the request body 53 | foreach ($configurationPolicyAssignment in $configurationPolicyAssignments) { 54 | $requestBody.assignments += @{ 55 | "target" = $configurationPolicyAssignment.target 56 | } 57 | } 58 | 59 | # Convert the PowerShell object to JSON 60 | $requestBody = $requestBody | ConvertTo-Json -Depth 100 61 | 62 | # Get the Configuration Policy we are restoring the assignments for 63 | try { 64 | if ($restoreById) { 65 | $configurationPolicyObject = Invoke-MgGraphRequest -method GET -Uri "$apiVersion/deviceManagement/configurationPolicies/$configurationPolicyId" 66 | } 67 | else { 68 | $configurationPolicyObject = Invoke-MgGraphRequest -method GET -Uri "$apiVersion/deviceManagement/configurationPolicies" | Get-MgGraphAllPages | Where-Object name -eq $configurationPolicyName 69 | if (-not ($configurationPolicyObject)) { 70 | Write-Verbose "Error retrieving Intune Session Catalog for $($configurationPolicy.FullName). Skipping assignment restore" -Verbose 71 | continue 72 | } 73 | } 74 | } 75 | catch { 76 | Write-Verbose "Error retrieving Intune Session Catalog for $($configurationPolicy.FullName). Skipping assignment restore" -Verbose 77 | Write-Error $_ -ErrorAction Continue 78 | continue 79 | } 80 | 81 | # Restore the assignments 82 | try { 83 | $null = Invoke-MgGraphRequest -method POST -body $requestBody.toString() -Uri "$apiVersion/deviceManagement/configurationPolicies/$($configurationPolicyObject.id)/assign" -ErrorAction Stop 84 | [PSCustomObject]@{ 85 | "Action" = "Restore" 86 | "Type" = "Settings Catalog Assignments" 87 | "Name" = $configurationPolicyObject.name 88 | "Path" = "Settings Catalog\Assignments\$($configurationPolicy.Name)" 89 | } 90 | } 91 | catch { 92 | Write-Verbose "$($configurationPolicyObject.name) - Failed to restore Settings Catalog Assignment(s)" -Verbose 93 | Write-Error $_ -ErrorAction Continue 94 | } 95 | } 96 | } -------------------------------------------------------------------------------- /IntuneBackupAndRestore/Public/Invoke-IntuneRestoreDeviceCompliancePolicy.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-IntuneRestoreDeviceCompliancePolicy { 2 | <# 3 | .SYNOPSIS 4 | Restore Intune Device Compliance Policies 5 | 6 | .DESCRIPTION 7 | Restore Intune Device Compliance Policies from JSON files per Device Compliance Policy from the specified Path. 8 | 9 | .PARAMETER Path 10 | Root path where backup files are located, created with the Invoke-IntuneBackupDeviceCompliancePolicy function 11 | 12 | .EXAMPLE 13 | Invoke-IntuneRestoreDeviceCompliance -Path "C:\temp" -RestoreById $true 14 | #> 15 | 16 | [CmdletBinding()] 17 | param( 18 | [Parameter(Mandatory = $true)] 19 | [string]$Path, 20 | 21 | [Parameter(Mandatory = $false)] 22 | [ValidateSet("v1.0", "Beta")] 23 | [string]$ApiVersion = "Beta" 24 | ) 25 | 26 | #Connect to MS-Graph if required 27 | if($null -eq (Get-MgContext)){ 28 | connect-mggraph -scopes "DeviceManagementApps.ReadWrite.All, DeviceManagementConfiguration.ReadWrite.All, DeviceManagementServiceConfig.ReadWrite.All" 29 | } 30 | 31 | # Get all Device Compliance Policies 32 | $deviceCompliancePolicies = Get-ChildItem -Path "$Path\Device Compliance Policies" -File -ErrorAction SilentlyContinue 33 | 34 | foreach ($deviceCompliancePolicy in $deviceCompliancePolicies) { 35 | $deviceCompliancePolicyContent = Get-Content -LiteralPath $deviceCompliancePolicy.FullName -Raw | ConvertFrom-Json 36 | 37 | $deviceCompliancePolicyDisplayName = $deviceCompliancePolicyContent.displayName 38 | 39 | # Remove properties that are not available for creating a new configuration 40 | $requestBody = $deviceCompliancePolicyContent | Select-Object -Property * -ExcludeProperty id, createdDateTime, lastModifiedDateTime 41 | 42 | # If missing, adds a default required block scheduled action to the compliance policy request body, as this value is not returned when retrieving compliance policies. 43 | if (-not ($requestBody.scheduledActionsForRule)) { 44 | $scheduledActionsForRule = @( 45 | @{ 46 | ruleName = "PasswordRequired" 47 | scheduledActionConfigurations = @( 48 | @{ 49 | actionType = "block" 50 | gracePeriodHours = 0 51 | notificationTemplateId = "" 52 | } 53 | ) 54 | } 55 | ) 56 | $requestBody | Add-Member -NotePropertyName scheduledActionsForRule -NotePropertyValue $scheduledActionsForRule 57 | } 58 | 59 | $requestBodyJson = $requestBody | ConvertTo-Json -Depth 100 60 | 61 | # Restore the Device Compliance Policy 62 | try { 63 | $null = Invoke-MgGraphRequest -Method POST -body $requestBodyJson.toString() -Uri "beta/deviceManagement/deviceCompliancePolicies" -ErrorAction Stop 64 | [PSCustomObject]@{ 65 | "Action" = "Restore" 66 | "Type" = "Device Compliance Policy" 67 | "Name" = $deviceCompliancePolicyDisplayName 68 | "Path" = "Device Compliance Policies\$($deviceCompliancePolicy.Name)" 69 | } 70 | } 71 | catch { 72 | Write-Verbose "$deviceCompliancePolicyDisplayName - Failed to restore Device Compliance Policy" -Verbose 73 | Write-Error $_ -ErrorAction Continue 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /IntuneBackupAndRestore/Public/Invoke-IntuneRestoreDeviceCompliancePolicyAssignment.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-IntuneRestoreDeviceCompliancePolicyAssignment { 2 | <# 3 | .SYNOPSIS 4 | Restore Intune Device Compliance Policy Assignments 5 | 6 | .DESCRIPTION 7 | Restore Intune Device Compliance Policy Assignments from JSON files per Device Compliance Policy from the specified Path. 8 | 9 | .PARAMETER Path 10 | Root path where backup files are located, created with the Invoke-IntuneBackupDeviceCompliancePolicyAssignment function 11 | 12 | .PARAMETER RestoreById 13 | If RestoreById is set to true, assignments will be restored to Intune Device Management Scripts that match the id. 14 | 15 | If RestoreById is set to false, assignments will be restored to Intune Device Management Scripts that match the file name. 16 | This is necessary if the Device Management Script was restored from backup, because then a new Device Management Script is created with a new unique ID. 17 | 18 | .EXAMPLE 19 | Invoke-IntuneRestoreDeviceCompliancePolicyAssignment -Path "C:\temp" -RestoreById $true 20 | #> 21 | [CmdletBinding()] 22 | param( 23 | [Parameter(Mandatory = $true)] 24 | [string]$Path, 25 | 26 | [Parameter(Mandatory = $false)] 27 | [bool]$RestoreById = $false, 28 | 29 | [Parameter(Mandatory = $false)] 30 | [ValidateSet("v1.0", "Beta")] 31 | [string]$ApiVersion = "Beta" 32 | ) 33 | 34 | #Connect to MS-Graph if required 35 | if ($null -eq (Get-MgContext)) { 36 | connect-mggraph -scopes "DeviceManagementApps.ReadWrite.All, DeviceManagementConfiguration.ReadWrite.All, DeviceManagementServiceConfig.ReadWrite.All, DeviceManagementManagedDevices.ReadWrite.All" 37 | } 38 | 39 | # Get all policies with assignments 40 | $deviceCompliancePolicies = Get-ChildItem -Path "$Path\Device Compliance Policies\Assignments" -File -ErrorAction SilentlyContinue 41 | 42 | foreach ($deviceCompliancePolicy in $deviceCompliancePolicies) { 43 | $deviceCompliancePolicyAssignments = Get-Content -LiteralPath $deviceCompliancePolicy.FullName | ConvertFrom-Json 44 | $deviceCompliancePolicyId = ($deviceCompliancePolicyAssignments[0]).id.Split("_")[0] 45 | $deviceCompliancePolicyName = $deviceCompliancePolicy.BaseName 46 | 47 | # Create the base requestBody 48 | $requestBody = @{ 49 | assignments = @() 50 | } 51 | 52 | # Add assignments to restore to the request body 53 | foreach ($deviceCompliancePolicyAssignment in $deviceCompliancePolicyAssignments) { 54 | $requestBody.assignments += @{ 55 | "target" = $deviceCompliancePolicyAssignment.target 56 | } 57 | } 58 | 59 | # Convert the PowerShell object to JSON 60 | $requestBody = $requestBody | ConvertTo-Json -Depth 100 61 | 62 | # Get the Device Compliance Policy we are restoring the assignments for 63 | try { 64 | if ($restoreById) { 65 | $deviceCompliancePolicyObject = Invoke-MgGraphRequest -Uri "$ApiVersion/deviceManagement/deviceCompliancePolicies/$deviceCompliancePolicyId" | Get-MGGraphAllPages 66 | } 67 | else { 68 | $deviceCompliancePolicyObject = Invoke-MgGraphRequest -Uri "$ApiVersion/deviceManagement/deviceCompliancePolicies" | Get-MGGraphAllPages | Where-Object displayName -eq $deviceCompliancePolicyName 69 | if (-not ($deviceCompliancePolicyObject)) { 70 | Write-Verbose "Error retrieving Intune Compliance Policy for $($deviceCompliancePolicy.FullName). Skipping assignment restore" -Verbose 71 | continue 72 | } 73 | } 74 | } 75 | catch { 76 | Write-Verbose "Error retrieving Intune Device Compliance Policy for $($deviceCompliancePolicy.FullName). Skipping assignment restore" -Verbose 77 | Write-Error $_ -ErrorAction Continue 78 | continue 79 | } 80 | 81 | # Restore the assignments 82 | try { 83 | $null = Invoke-MgGraphRequest -Method POST -Body $requestBody.toString() -Uri "$ApiVersion/deviceManagement/deviceCompliancePolicies/$($deviceCompliancePolicyObject.id)/assign" -ErrorAction Stop 84 | [PSCustomObject]@{ 85 | "Action" = "Restore" 86 | "Type" = "Device Compliance Policy Assignments" 87 | "Name" = $deviceCompliancePolicyObject.displayName 88 | "Path" = "Device Compliance Policies\Assignments\$($deviceCompliancePolicy.Name)" 89 | } 90 | } 91 | catch { 92 | Write-Verbose "$($deviceCompliancePolicyObject.displayName) - Failed to restore Device Compliance Policy Assignment(s)" -Verbose 93 | Write-Error $_ -ErrorAction Continue 94 | } 95 | } 96 | } -------------------------------------------------------------------------------- /IntuneBackupAndRestore/Public/Invoke-IntuneRestoreDeviceConfiguration.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-IntuneRestoreDeviceConfiguration { 2 | <# 3 | .SYNOPSIS 4 | Restore Intune Device Configurations 5 | 6 | .DESCRIPTION 7 | Restore Intune Device Configurations from JSON files per Device Configuration Policy from the specified Path. 8 | 9 | .PARAMETER Path 10 | Root path where backup files are located, created with the Invoke-IntuneBackupDeviceConfigurations function 11 | 12 | .EXAMPLE 13 | Invoke-IntuneRestoreDeviceConfiguration -Path "C:\temp" -RestoreById $true 14 | #> 15 | 16 | [CmdletBinding()] 17 | param( 18 | [Parameter(Mandatory = $true)] 19 | [string]$Path, 20 | 21 | [Parameter(Mandatory = $false)] 22 | [ValidateSet("v1.0", "Beta")] 23 | [string]$ApiVersion = "Beta" 24 | ) 25 | 26 | #Connect to MS-Graph if required 27 | if($null -eq (Get-MgContext)){ 28 | connect-mggraph -scopes "DeviceManagementApps.ReadWrite.All, DeviceManagementConfiguration.ReadWrite.All, DeviceManagementServiceConfig.ReadWrite.All, DeviceManagementManagedDevices.ReadWrite.All" 29 | } 30 | 31 | # Get all device configurations 32 | $deviceConfigurations = Get-ChildItem -Path "$path\Device Configurations" -File -ErrorAction SilentlyContinue 33 | 34 | foreach ($deviceConfiguration in $deviceConfigurations) { 35 | $deviceConfigurationContent = Get-Content -LiteralPath $deviceConfiguration.FullName -Raw | ConvertFrom-Json 36 | 37 | $deviceConfigurationDisplayName = $deviceConfigurationContent.displayName 38 | 39 | # Remove properties that are not available for creating a new configuration 40 | $requestBodyObject = $deviceConfigurationContent | Select-Object -Property * -ExcludeProperty id, createdDateTime, lastModifiedDateTime, version 41 | # Set SupportsScopeTags to $false, because $true currently returns an HTTP Status 400 Bad Request error. 42 | if ($requestBodyObject.supportsScopeTags) { 43 | $requestBodyObject.supportsScopeTags = $false 44 | } 45 | 46 | $requestBodyObject.PSObject.Properties | Foreach-Object { 47 | if ($null -ne $_.Value) { 48 | if ($_.Value.GetType().Name -eq "DateTime") { 49 | $_.Value = (Get-Date -Date $_.Value -Format s) + "Z" 50 | } 51 | } 52 | } 53 | 54 | $requestBody = $requestBodyObject | ConvertTo-Json -Depth 100 55 | 56 | # Restore the device configuration 57 | try { 58 | $null = Invoke-MgGraphRequest -Method POST -body $requestBody.toString() -Uri "$ApiVersion/deviceManagement/deviceConfigurations" -ErrorAction Stop 59 | [PSCustomObject]@{ 60 | "Action" = "Restore" 61 | "Type" = "Device Configuration" 62 | "Name" = $deviceConfigurationDisplayName 63 | "Path" = "Device Configurations\$($deviceConfiguration.Name)" 64 | } 65 | } 66 | catch { 67 | Write-Verbose "$deviceConfigurationDisplayName - Failed to restore Device Configuration" -Verbose 68 | Write-Error $_ -ErrorAction Continue 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /IntuneBackupAndRestore/Public/Invoke-IntuneRestoreDeviceConfigurationAssignment.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-IntuneRestoreDeviceConfigurationAssignment { 2 | <# 3 | .SYNOPSIS 4 | Restore Intune Device Configuration Assignments 5 | 6 | .DESCRIPTION 7 | Restore Intune Device Configuration Assignments from JSON files per Device Configuration Policy from the specified Path. 8 | 9 | .PARAMETER Path 10 | Root path where backup files are located, created with the Invoke-IntuneBackupDeviceConfigurationAssignment function 11 | 12 | .PARAMETER RestoreById 13 | If RestoreById is set to true, assignments will be restored to Intune Device Management Scripts that match the id. 14 | 15 | If RestoreById is set to false, assignments will be restored to Intune Device Management Scripts that match the file name. 16 | This is necessary if the Device Management Script was restored from backup, because then a new Device Management Script is created with a new unique ID. 17 | 18 | .EXAMPLE 19 | Invoke-IntuneRestoreDeviceConfigurationAssignment -Path "C:\temp" -RestoreById $true 20 | #> 21 | 22 | [CmdletBinding()] 23 | param( 24 | [Parameter(Mandatory = $true)] 25 | [string]$Path, 26 | 27 | [Parameter(Mandatory = $false)] 28 | [bool]$RestoreById = $false, 29 | 30 | [Parameter(Mandatory = $false)] 31 | [ValidateSet("v1.0", "Beta")] 32 | [string]$ApiVersion = "Beta" 33 | ) 34 | 35 | #Connect to MS-Graph if required 36 | if($null -eq (Get-MgContext)){ 37 | connect-mggraph -scopes "DeviceManagementApps.ReadWrite.All, DeviceManagementConfiguration.ReadWrite.All, DeviceManagementServiceConfig.ReadWrite.All, DeviceManagementManagedDevices.ReadWrite.All" 38 | } 39 | 40 | # Get all policies with assignments 41 | $deviceConfigurations = Get-ChildItem -Path "$Path\Device Configurations\Assignments" -File -ErrorAction SilentlyContinue 42 | 43 | foreach ($deviceConfiguration in $deviceConfigurations) { 44 | $deviceConfigurationAssignments = Get-Content -LiteralPath $deviceConfiguration.FullName | ConvertFrom-Json 45 | $deviceConfigurationName = $deviceConfiguration.BaseName 46 | 47 | # Create the base requestBody 48 | $requestBody = @{ 49 | assignments = @() 50 | } 51 | 52 | # Add assignments to restore to the request body 53 | foreach ($deviceConfigurationAssignment in $deviceConfigurationAssignments) { 54 | $requestBody.assignments += @{ 55 | "target" = $deviceConfigurationAssignment.target 56 | } 57 | } 58 | 59 | # Convert the PowerShell object to JSON 60 | $requestBody = $requestBody | ConvertTo-Json -Depth 100 61 | 62 | # Get the Device Configuration we are restoring the assignments for 63 | try { 64 | if ($restoreById) { 65 | $deviceConfigurationObject = Invoke-MgGraphRequest -Uri "$apiVersion/deviceManagement/deviceConfigurations/$($deviceConfigurationAssignment.sourceid)" | Get-MGGraphAllPages 66 | } 67 | else { 68 | $deviceConfigurationObject = Invoke-MgGraphRequest -Uri "$apiVersion/deviceManagement/deviceConfigurations" | Get-MGGraphAllPages | Where-Object displayName -eq $deviceConfigurationName 69 | if (-not ($deviceConfigurationObject)) { 70 | Write-Verbose "Error retrieving Intune Device Configuration for $($deviceConfiguration.FullName). Skipping assignment restore" -Verbose 71 | continue 72 | } 73 | } 74 | } 75 | catch { 76 | Write-Verbose "Error retrieving Intune Device Configuration for $($deviceConfiguration.FullName). Skipping assignment restore" -Verbose 77 | Write-Error $_ -ErrorAction Continue 78 | continue 79 | } 80 | 81 | # Restore the assignments 82 | try { 83 | $null = Invoke-MgGraphRequest -Method POST -body $requestBody.toString() -Uri "$apiVersion/deviceManagement/deviceConfigurations/$($deviceConfigurationObject.id)/assign" -ErrorAction Stop 84 | [PSCustomObject]@{ 85 | "Action" = "Restore" 86 | "Type" = "Device Configuration Assignments" 87 | "Name" = $deviceConfigurationObject.displayName 88 | "Path" = "Device Configurations\Assignments\$($deviceConfiguration.Name)" 89 | } 90 | } 91 | catch { 92 | Write-Verbose "$($deviceConfigurationObject.displayName) - Failed to restore Device Configuration Assignment(s)" -Verbose 93 | Write-Error $_ -ErrorAction Continue 94 | } 95 | } 96 | } -------------------------------------------------------------------------------- /IntuneBackupAndRestore/Public/Invoke-IntuneRestoreDeviceHealthScript.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-IntuneRestoreDeviceHealthScript { 2 | <# 3 | .SYNOPSIS 4 | Restore Intune Device Health Scripts 5 | 6 | .DESCRIPTION 7 | Restore Intune Device Health Scripts from JSON files per Device Helth Script from the specified Path. 8 | 9 | .PARAMETER Path 10 | Root path where backup files are located, created with the Invoke-IntuneBackupDeviceHealthScript function 11 | 12 | .EXAMPLE 13 | Invoke-IntuneRestoreDeviceHealthScript -Path "C:\temp" -RestoreById $true 14 | #> 15 | 16 | [CmdletBinding()] 17 | param( 18 | [Parameter(Mandatory = $true)] 19 | [string]$Path, 20 | 21 | [Parameter(Mandatory = $false)] 22 | [ValidateSet("v1.0", "Beta")] 23 | [string]$ApiVersion = "Beta" 24 | ) 25 | 26 | #Connect to MS-Graph if required 27 | if($null -eq (Get-MgContext)){ 28 | Connect-MgGraph -Scopes "DeviceManagementApps.ReadWrite.All, DeviceManagementConfiguration.ReadWrite.All, DeviceManagementServiceConfig.ReadWrite.All, DeviceManagementManagedDevices.ReadWrite.All" 29 | } 30 | 31 | # Get all device health scripts 32 | $deviceHealthScripts = Get-ChildItem -Path "$Path\Device Health Scripts" -File -ErrorAction SilentlyContinue 33 | 34 | foreach ($deviceHealthScript in $deviceHealthScripts) { 35 | $deviceHealthScriptContent = Get-Content -LiteralPath $deviceHealthScript.FullName -Raw 36 | $deviceHealthScriptDisplayName = ($deviceHealthScriptContent | ConvertFrom-Json).displayName 37 | 38 | # Remove properties that are not available for creating a new configuration 39 | $requestBodyObject = $deviceHealthScriptContent | ConvertFrom-Json 40 | $requestBody = $requestBodyObject | Select-Object -Property * -ExcludeProperty id, createdDateTime, lastModifiedDateTime | ConvertTo-Json 41 | 42 | # Restore the device health script (excluding Microsoft builtin scripts) 43 | if (-not ($requestBodyObject.publisher -eq "Microsoft")) { 44 | try { 45 | $null = Invoke-MgGraphRequest -Method POST -body $requestBody.toString() -Uri "$ApiVersion/deviceManagement/deviceHealthScripts" -ErrorAction Stop 46 | [PSCustomObject]@{ 47 | "Action" = "Restore" 48 | "Type" = "Device Health Script" 49 | "Name" = $deviceHealthScriptDisplayName 50 | "Path" = "Device Health Scripts\$($deviceHealthScript.Name)" 51 | } 52 | } 53 | catch { 54 | Write-Verbose "$deviceHealthScriptDisplayName - Failed to restore Device Health Script" -Verbose 55 | Write-Error $_ -ErrorAction Continue 56 | } 57 | } else { 58 | Write-Verbose "$deviceHealthScriptDisplayName - skipped (Microsoft builtin script)" -Verbose 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /IntuneBackupAndRestore/Public/Invoke-IntuneRestoreDeviceHealthScriptAssignment.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-IntuneRestoreDeviceHealthScriptAssignment { 2 | <# 3 | .SYNOPSIS 4 | Restore Intune Health Script (remediation Scripts) Assignments 5 | 6 | .DESCRIPTION 7 | Restore Intune Health Script (remediation Scripts) Assignments from JSON files per Health Script from the specified Path. 8 | 9 | .PARAMETER Path 10 | Root path where backup files are located, created with the Invoke-IntuneBackupDeviceHealthScriptAssignment function 11 | 12 | .PARAMETER RestoreById 13 | If RestoreById is set to true, assignments will be restored to Intune Device Health Scripts that match the id. 14 | 15 | If RestoreById is set to false, assignments will be restored to Intune Device Health Scripts that match the file name. 16 | This is necessary if the Device Health Script was restored from backup, because then a new Device Health Script is created with a new unique ID. 17 | 18 | .EXAMPLE 19 | Invoke-IntuneRestoreDeviceHealthScriptAssignment -Path "C:\temp" -RestoreById $true 20 | #> 21 | 22 | [CmdletBinding()] 23 | param( 24 | [Parameter(Mandatory = $true)] 25 | [string]$Path, 26 | 27 | [Parameter(Mandatory = $false)] 28 | [bool]$RestoreById = $false, 29 | 30 | [Parameter(Mandatory = $false)] 31 | [ValidateSet("v1.0", "Beta")] 32 | [string]$ApiVersion = "Beta" 33 | ) 34 | 35 | # Get all policies with assignments 36 | $deviceHealthScripts = Get-ChildItem -Path "$Path\Device Health Scripts\Assignments" -File -ErrorAction SilentlyContinue 37 | 38 | foreach ($deviceHealthScript in $deviceHealthScripts) { 39 | $deviceHealthScriptAssignments = Get-Content -LiteralPath $deviceHealthScript.FullName | ConvertFrom-Json 40 | $deviceHealthScriptId = ($deviceHealthScriptAssignments[0]).id.Split(":")[0] 41 | 42 | # Create the base requestBody 43 | $requestBody = @{ 44 | deviceHealthScriptAssignments = @() 45 | } 46 | 47 | # Add assignments to restore to the request body 48 | foreach ($deviceHealthScriptAssignment in $deviceHealthScriptAssignments) { 49 | $requestBody.deviceHealthScriptAssignments += @{ 50 | "target" = $deviceHealthScriptAssignment.target 51 | "runSchedule" = $deviceHealthScriptAssignment.runSchedule 52 | "runRemediationScript" = $deviceHealthScriptAssignment.runRemediationScript 53 | } 54 | } 55 | 56 | # Convert the PowerShell object to JSON 57 | $requestBody = $requestBody | ConvertTo-Json -Depth 100 58 | 59 | # Get the Device Health Script we are restoring the assignments for 60 | try { 61 | if ($restoreById) { 62 | $deviceHealthScriptObject = Invoke-MgGraphRequest -Uri "$ApiVersion/deviceManagement/deviceHealthScripts/$deviceHealthScriptId" 63 | } 64 | else { 65 | $deviceHealthScriptObject = Invoke-MgGraphRequest -Uri "$ApiVersion/deviceManagement/deviceHealthScripts" | Get-MGGraphAllPages | Where-Object displayName -eq "$($deviceHealthScript.BaseName)" 66 | if (-not ($deviceHealthScriptObject)) { 67 | Write-Verbose "Error retrieving Intune Device Health Script for $($deviceHealthScript.FullName). Skipping assignment restore" -Verbose 68 | continue 69 | } 70 | } 71 | } 72 | catch { 73 | Write-Verbose "Error retrieving Intune Device Health Script for $($deviceHealthScript.FullName). Skipping assignment restore" -Verbose 74 | Write-Error $_ -ErrorAction Continue 75 | continue 76 | } 77 | 78 | # Restore the assignments 79 | try { 80 | $null = Invoke-MgGraphRequest -Method POST -body $requestBody.toString() -Uri "$ApiVersion/deviceManagement/deviceHealthScripts/$($deviceHealthScriptObject.id)/assign" -ErrorAction Stop 81 | [PSCustomObject]@{ 82 | "Action" = "Restore" 83 | "Type" = "Device Health Script Assignments" 84 | "Name" = $deviceHealthScriptObject.displayName 85 | "Path" = "Device Health Scripts\Assignments\$($deviceHealthScript.Name)" 86 | } 87 | } 88 | catch { 89 | Write-Verbose "$($deviceHealthScriptObject.displayName) - Failed to restore Device Health Script Assignment(s)" -Verbose 90 | Write-Error $_ -ErrorAction Continue 91 | } 92 | } 93 | } -------------------------------------------------------------------------------- /IntuneBackupAndRestore/Public/Invoke-IntuneRestoreDeviceManagementIntent.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-IntuneRestoreDeviceManagementIntent { 2 | <# 3 | .SYNOPSIS 4 | Restore Intune Device Management Intents 5 | 6 | .DESCRIPTION 7 | Restore Intune Device Management Intents from JSON files per Device Management Intent from the specified Path. 8 | 9 | .PARAMETER Path 10 | Root path where backup files are located, created with the Invoke-IntuneBackupDeviceManagementIntent function 11 | 12 | .EXAMPLE 13 | Invoke-IntuneRestoreDeviceManagementIntent -Path "C:\temp" -RestoreById $true 14 | #> 15 | 16 | [CmdletBinding()] 17 | param( 18 | [Parameter(Mandatory = $true)] 19 | [string]$Path, 20 | 21 | [Parameter(Mandatory = $false)] 22 | [ValidateSet("v1.0", "Beta")] 23 | [string]$ApiVersion = "Beta" 24 | 25 | ) 26 | 27 | #Connect to MS-Graph if required 28 | if ($null -eq (Get-MgContext)) { 29 | connect-mggraph -scopes "DeviceManagementApps.ReadWrite.All, DeviceManagementConfiguration.ReadWrite.All, DeviceManagementServiceConfig.ReadWrite.All, DeviceManagementManagedDevices.ReadWrite.All" 30 | } 31 | 32 | # Get all device management intents 33 | $deviceManagementIntents = Get-ChildItem -Path "$Path\Device Management Intents" -Recurse -File -ErrorAction SilentlyContinue 34 | 35 | #Used to exclude Onboarding/Offboarding blob settings if AutoPopulateOnboardingBlob is set to $true 36 | $excludedEDRDefinitions = @( 37 | "deviceConfiguration--windowsDefenderAdvancedThreatProtectionConfiguration_advancedThreatProtectionBlobType" 38 | "deviceConfiguration--windowsDefenderAdvancedThreatProtectionConfiguration_advancedThreatProtectionOffboardingBlob" 39 | "deviceConfiguration--windowsDefenderAdvancedThreatProtectionConfiguration_advancedThreatProtectionOnboardingBlob" 40 | "deviceConfiguration--windowsDefenderAdvancedThreatProtectionConfiguration_advancedThreatProtectionOnboardingFilename" 41 | "deviceConfiguration--windowsDefenderAdvancedThreatProtectionConfiguration_advancedThreatProtectionOffboardingFilename" 42 | ) 43 | 44 | foreach ($deviceManagementIntent in $deviceManagementIntents) { 45 | if($deviceManagementIntent.DirectoryName -match "Assignments"){continue} 46 | $deviceManagementIntentContent = Get-Content -LiteralPath $deviceManagementIntent.FullName | ConvertFrom-Json 47 | $templateId = $deviceManagementIntent.Name.Split("_")[0] 48 | $templateDisplayName = ($deviceManagementIntent).DirectoryName.Split('\')[-1] 49 | 50 | $deviceManagementIntentDisplayName = $deviceManagementIntentContent.displayName 51 | 52 | #When importing an EDR policy, if AutoPopulateOnboardingBlob is set to true, the onboarding blob policies need to be set to null or removed. 53 | If ($templateId -eq "e44c2ca3-2f9a-400a-a113-6cc88efd773d") { 54 | $AutoPopulateOnboardingBlob = ($deviceManagementIntentContent.settingsDelta | ? { $_.definitionId -eq "deviceConfiguration--windowsDefenderAdvancedThreatProtectionConfiguration_advancedThreatProtectionAutoPopulateOnboardingBlob" }).value 55 | If ($AutoPopulateOnboardingBlob) { 56 | $deviceManagementIntentContent.settingsDelta = $deviceManagementIntentContent.settingsDelta | ? { $excludedEDRDefinitions -notcontains $_.definitionId } 57 | } 58 | } 59 | 60 | $deviceManagementIntentJson = $($deviceManagementIntentContent | convertto-json -Depth 100) 61 | # Restore the device management intent 62 | try { 63 | New-MgBetaDeviceManagementTemplateInstance -DeviceManagementTemplateId $templateId -BodyParameter $deviceManagementIntentJson 64 | [PSCustomObject]@{ 65 | "Action" = "Restore" 66 | "Type" = "Device Management Intent" 67 | "Name" = $deviceManagementIntentDisplayName 68 | "Path" = "Device Management Intents\$($deviceManagementIntent.Name)" 69 | } 70 | } 71 | catch { 72 | Write-Verbose "$deviceManagementIntentDisplayName - Failed to restore Device Management Intent ($templateDisplayName)" -Verbose 73 | Write-Error $_ -ErrorAction Continue 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /IntuneBackupAndRestore/Public/Invoke-IntuneRestoreDeviceManagementScript.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-IntuneRestoreDeviceManagementScript { 2 | <# 3 | .SYNOPSIS 4 | Restore Intune Device Management Scripts 5 | 6 | .DESCRIPTION 7 | Restore Intune Device Management Scripts from JSON files per Device Management Script from the specified Path. 8 | 9 | .PARAMETER Path 10 | Root path where backup files are located, created with the Invoke-IntuneBackupDeviceManagementScript function 11 | 12 | .EXAMPLE 13 | Invoke-IntuneRestoreDeviceManagementScript -Path "C:\temp" -RestoreById $true 14 | #> 15 | 16 | [CmdletBinding()] 17 | param( 18 | [Parameter(Mandatory = $true)] 19 | [string]$Path, 20 | 21 | [Parameter(Mandatory = $false)] 22 | [bool]$RestoreById = $false, 23 | 24 | [Parameter(Mandatory = $false)] 25 | [ValidateSet("v1.0", "Beta")] 26 | [string]$ApiVersion = "Beta" 27 | ) 28 | 29 | #Connect to MS-Graph if required 30 | if ($null -eq (Get-MgContext)) { 31 | connect-mggraph -scopes "DeviceManagementApps.ReadWrite.All, DeviceManagementConfiguration.ReadWrite.All, DeviceManagementServiceConfig.ReadWrite.All, DeviceManagementManagedDevices.ReadWrite.All" 32 | } 33 | 34 | # Get all device management scripts 35 | $deviceManagementScripts = Get-ChildItem -Path "$Path\Device Management Scripts" -File -ErrorAction SilentlyContinue 36 | 37 | foreach ($deviceManagementScript in $deviceManagementScripts) { 38 | $deviceManagementScriptContent = Get-Content -LiteralPath $deviceManagementScript.FullName | Convertfrom-Json 39 | 40 | $deviceManagementScriptDisplayName = $deviceManagementScriptContent.displayName 41 | 42 | # Remove properties that are not available for creating a new configuration 43 | $requestBody = $deviceManagementScriptContent | Select-Object -Property * -ExcludeProperty id, createdDateTime, lastModifiedDateTime | ConvertTo-Json 44 | 45 | # Restore the device management script 46 | try { 47 | $null = Invoke-MgGraphRequest -Method POST -Body $requestBody.toString() -Uri "$apiVersion/deviceManagement/deviceManagementScripts" -ErrorAction Stop 48 | [PSCustomObject]@{ 49 | "Action" = "Restore" 50 | "Type" = "Device Management Script" 51 | "Name" = $deviceManagementScriptDisplayName 52 | "Path" = "Device Management Scripts\$($deviceManagementScript.Name)" 53 | } 54 | } 55 | catch { 56 | Write-Verbose "$deviceManagementScriptDisplayName - Failed to restore Device Management Script" -Verbose 57 | Write-Error $_ -ErrorAction Continue 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /IntuneBackupAndRestore/Public/Invoke-IntuneRestoreDeviceManagementScriptAssignment.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-IntuneRestoreDeviceManagementScriptAssignment { 2 | <# 3 | .SYNOPSIS 4 | Restore Intune Device Management Script Assignments 5 | 6 | .DESCRIPTION 7 | Restore Intune Device Management Script Assignments from JSON files per Device Management Script from the specified Path. 8 | 9 | .PARAMETER Path 10 | Root path where backup files are located, created with the Invoke-IntuneBackupDeviceManagementScriptAssignment function 11 | 12 | .PARAMETER RestoreById 13 | If RestoreById is set to true, assignments will be restored to Intune Device Management Scripts that match the id. 14 | 15 | If RestoreById is set to false, assignments will be restored to Intune Device Management Scripts that match the file name. 16 | This is necessary if the Device Management Script was restored from backup, because then a new Device Management Script is created with a new unique ID. 17 | 18 | .EXAMPLE 19 | Invoke-IntuneRestoreDeviceManagementScriptAssignment -Path "C:\temp" -RestoreById $true 20 | #> 21 | 22 | [CmdletBinding()] 23 | param( 24 | [Parameter(Mandatory = $true)] 25 | [string]$Path, 26 | 27 | [Parameter(Mandatory = $false)] 28 | [bool]$RestoreById = $false, 29 | 30 | [Parameter(Mandatory = $false)] 31 | [ValidateSet("v1.0", "Beta")] 32 | [string]$ApiVersion = "Beta" 33 | 34 | ) 35 | 36 | #Connect to MS-Graph if required 37 | if ($null -eq (Get-MgContext)) { 38 | connect-mggraph -scopes "DeviceManagementApps.ReadWrite.All, DeviceManagementConfiguration.ReadWrite.All, DeviceManagementServiceConfig.ReadWrite.All, DeviceManagementManagedDevices.ReadWrite.All" 39 | } 40 | 41 | # Get all policies with assignments 42 | $deviceManagementScripts = Get-ChildItem -Path "$Path\Device Management Scripts\Assignments" -File -ErrorAction SilentlyContinue 43 | 44 | foreach ($deviceManagementScript in $deviceManagementScripts) { 45 | $deviceManagementScriptAssignments = Get-Content -LiteralPath $deviceManagementScript.FullName | ConvertFrom-Json 46 | $deviceManagementScriptId = ($deviceManagementScriptAssignments[0]).id.Split(":")[0] 47 | $deviceManagementScriptName = $deviceManagementScript.BaseName 48 | 49 | # Create the base requestBody 50 | $requestBody = @{ 51 | deviceManagementScriptAssignments = @() 52 | } 53 | 54 | # Add assignments to restore to the request body 55 | foreach ($deviceManagementScriptAssignment in $deviceManagementScriptAssignments) { 56 | $requestBody.deviceManagementScriptAssignments += @{ 57 | "target" = $deviceManagementScriptAssignment.target 58 | } 59 | } 60 | 61 | # Convert the PowerShell object to JSON 62 | $requestBody = $requestBody | ConvertTo-Json -Depth 100 63 | 64 | # Get the Device Management Script we are restoring the assignments for 65 | try { 66 | if ($restoreById) { 67 | $deviceManagementScriptObject = Invoke-MgGraphRequest -Uri "$apiVersion/deviceManagement/deviceManagementScripts/$deviceManagementScriptId" 68 | } 69 | else { 70 | $deviceManagementScriptObject = Invoke-MgGraphRequest -Uri "$apiVersion/deviceManagement/deviceManagementScripts" | Get-MGGraphAllPages | Where-Object displayName -eq $deviceManagementScriptName 71 | if (-not ($deviceManagementScriptObject)) { 72 | Write-Verbose "Error retrieving Intune Device Management Script for $deviceManagementScriptName. Skipping assignment restore" -Verbose 73 | continue 74 | } 75 | } 76 | } 77 | catch { 78 | Write-Verbose "Error retrieving Intune Device Management Script for $deviceManagementScriptName. Skipping assignment restore" -Verbose 79 | Write-Error $_ -ErrorAction Continue 80 | continue 81 | } 82 | 83 | # Restore the assignments 84 | try { 85 | $null = Invoke-MgGraphRequest -Method POST -Body $requestBody.toString() -Uri "$apiVersion/deviceManagement/deviceManagementScripts/$($deviceManagementScriptObject.id)/assign" -ErrorAction Stop 86 | [PSCustomObject]@{ 87 | "Action" = "Restore" 88 | "Type" = "Device Management Script Assignments" 89 | "Name" = $deviceManagementScriptObject.displayName 90 | "Path" = "Device Management Scripts\Assignments\$($deviceManagementScript.Name)" 91 | } 92 | } 93 | catch { 94 | Write-Verbose "$deviceManagementScriptName - Failed to restore Device Management Script Assignment(s)" -Verbose 95 | Write-Error $_ -ErrorAction Continue 96 | } 97 | } 98 | } -------------------------------------------------------------------------------- /IntuneBackupAndRestore/Public/Invoke-IntuneRestoreGroupPolicyConfiguration.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-IntuneRestoreGroupPolicyConfiguration { 2 | <# 3 | .SYNOPSIS 4 | Restore Intune Group Policy Configurations 5 | 6 | .DESCRIPTION 7 | Restore Intune Group Policy Configurations from JSON files per Group Policy Configuration Policy from the specified Path. 8 | 9 | .PARAMETER Path 10 | Root path where backup files are located, created with the Invoke-IntuneBackupGroupPolicyConfigurations function 11 | 12 | .EXAMPLE 13 | Invoke-IntuneRestoreGroupPolicyConfiguration -Path "C:\temp" -RestoreById $true 14 | #> 15 | 16 | [CmdletBinding()] 17 | param( 18 | [Parameter(Mandatory = $true)] 19 | [string]$Path, 20 | 21 | [Parameter(Mandatory = $false)] 22 | [ValidateSet("v1.0", "Beta")] 23 | [string]$ApiVersion = "Beta" 24 | ) 25 | 26 | #Connect to MS-Graph if required 27 | if ($null -eq (Get-MgContext)) { 28 | connect-mggraph -scopes "DeviceManagementApps.ReadWrite.All, DeviceManagementConfiguration.ReadWrite.All, DeviceManagementServiceConfig.ReadWrite.All, DeviceManagementManagedDevices.ReadWrite.All" 29 | } 30 | 31 | # Get all Group Policy Configurations 32 | $groupPolicyConfigurations = Get-ChildItem -Path "$Path\Administrative Templates" -File -ErrorAction SilentlyContinue 33 | 34 | foreach ($groupPolicyConfiguration in $groupPolicyConfigurations) { 35 | $groupPolicyConfigurationContent = Get-Content -LiteralPath $groupPolicyConfiguration.FullName -Raw | ConvertFrom-Json 36 | $groupPolicyConfigurationDisplayName = $groupPolicyConfiguration.BaseName 37 | 38 | # Restore the Group Policy Configuration 39 | try { 40 | $groupPolicyConfigurationRequestBody = @{ 41 | displayName = $groupPolicyConfigurationDisplayName 42 | } 43 | $groupPolicyConfigurationObject = Invoke-MgGraphRequest -Method POST -Uri "$apiVersion/deviceManagement/groupPolicyConfigurations" -Body ($groupPolicyConfigurationRequestBody | ConvertTo-Json).toString() -ErrorAction Stop 44 | [PSCustomObject]@{ 45 | "Action" = "Restore" 46 | "Type" = "Administrative Template" 47 | "Name" = $groupPolicyConfigurationObject.displayName 48 | "Path" = "Administrative Templates\$($groupPolicyConfiguration.Name)" 49 | } 50 | 51 | foreach ($groupPolicyConfigurationSetting in $groupPolicyConfigurationContent) { 52 | $groupPolicyDefinitionValue = Invoke-MgGraphRequest -Method POST -Uri "$apiVersion/deviceManagement/groupPolicyConfigurations/$($groupPolicyConfigurationObject.id)/definitionValues" -Body ($groupPolicyConfigurationSetting | ConvertTo-Json -Depth 100).toString() -ErrorAction Stop 53 | $groupPolicyDefinition = Invoke-MgGraphRequest -Method GET -Uri "$apiVersion/deviceManagement/groupPolicyConfigurations/$($groupPolicyConfigurationObject.id)/definitionValues/$($groupPolicyDefinitionValue.id)/definition" 54 | [PSCustomObject]@{ 55 | "Action" = "Restore" 56 | "Type" = "Administrative Template Setting" 57 | "Name" = $groupPolicyDefinition.displayName 58 | "Path" = "Administrative Templates\$($groupPolicyConfiguration.Name)" 59 | } 60 | } 61 | } 62 | catch { 63 | Write-Verbose "$($groupPolicyConfiguration.BaseName) - Failed to restore Group Policy Configuration and/or (one or more) Settings" -Verbose 64 | Write-Error $_ -ErrorAction Continue 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /IntuneBackupAndRestore/Public/Invoke-IntuneRestoreGroupPolicyConfigurationAssignment.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-IntuneRestoreGroupPolicyConfigurationAssignment { 2 | <# 3 | .SYNOPSIS 4 | Restore Intune Group Policy Configuration Assignments 5 | 6 | .DESCRIPTION 7 | Restore Intune Group Policy Configuration Assignments from JSON files per Group Policy Configuration from the specified Path. 8 | 9 | .PARAMETER Path 10 | Root path where backup files are located, created with the Invoke-IntuneBackupGroupPolicyConfigurationAssignment function 11 | 12 | .PARAMETER RestoreById 13 | If RestoreById is set to true, assignments will be restored to Intune Device Management Scripts that match the id. 14 | 15 | If RestoreById is set to false, assignments will be restored to Intune Device Management Scripts that match the file name. 16 | This is necessary if the Device Management Script was restored from backup, because then a new Device Management Script is created with a new unique ID. 17 | 18 | .EXAMPLE 19 | Invoke-IntuneRestoreGroupPolicyConfigurationAssignment -Path "C:\temp" -RestoreById $true 20 | #> 21 | 22 | [CmdletBinding()] 23 | param( 24 | [Parameter(Mandatory = $true)] 25 | [string]$Path, 26 | 27 | [Parameter(Mandatory = $false)] 28 | [bool]$RestoreById = $false, 29 | 30 | [Parameter(Mandatory = $false)] 31 | [ValidateSet("v1.0", "Beta")] 32 | [string]$ApiVersion = "Beta" 33 | ) 34 | 35 | #Connect to MS-Graph if required 36 | if ($null -eq (Get-MgContext)) { 37 | connect-mggraph -scopes "DeviceManagementApps.ReadWrite.All, DeviceManagementConfiguration.ReadWrite.All, DeviceManagementServiceConfig.ReadWrite.All, DeviceManagementManagedDevices.ReadWrite.All" 38 | } 39 | 40 | # Create the base requestBody 41 | $requestBody = @{ 42 | deviceManagementScriptAssignments = @() 43 | } 44 | 45 | # Get all policies with assignments 46 | $groupPolicyConfigurations = Get-ChildItem -Path "$Path\Administrative Templates\Assignments" -File -ErrorAction SilentlyContinue 47 | 48 | foreach ($groupPolicyConfiguration in $groupPolicyConfigurations) { 49 | $groupPolicyConfigurationAssignments = Get-Content -LiteralPath $groupPolicyConfiguration.FullName | ConvertFrom-Json 50 | $groupPolicyConfigurationId = ($groupPolicyConfigurationAssignments[0]).id.Split("_")[0] 51 | $groupPolicyConfigurationName = $groupPolicyConfiguration.BaseName 52 | 53 | # Create the base requestBody 54 | $requestBody = @{ 55 | assignments = @() 56 | } 57 | 58 | # Add assignments to restore to the request body 59 | foreach ($groupPolicyConfigurationAssignment in $groupPolicyConfigurationAssignments) { 60 | $requestBody.assignments += @{ 61 | "target" = $groupPolicyConfigurationAssignment.target 62 | } 63 | } 64 | 65 | # Convert the PowerShell object to JSON 66 | $requestBody = $requestBody | ConvertTo-Json -Depth 100 67 | 68 | # Get the Group Policy Configuration we are restoring the assignments for 69 | try { 70 | if ($restoreById) { 71 | $groupPolicyConfigurationObject = Invoke-MgGraphRequest -Method GET -Uri "$apiVersion/deviceManagement/groupPolicyConfigurations/$groupPolicyConfigurationId" 72 | } 73 | else { 74 | $groupPolicyConfigurationObject = Invoke-MgGraphRequest -Method GET -Uri "$apiVersion/deviceManagement/groupPolicyConfigurations" | Get-MGGraphAllPages | Where-Object displayName -eq $groupPolicyConfigurationName 75 | if (-not ($groupPolicyConfigurationObject)) { 76 | Write-Verbose "Error retrieving Intune Administrative Template for $groupPolicyConfigurationName. Skipping assignment restore" -Verbose 77 | continue 78 | } 79 | } 80 | } 81 | catch { 82 | Write-Verbose "Error retrieving Intune Administrative Template for $groupPolicyConfigurationName. Skipping assignment restore" -Verbose 83 | Write-Error $_ -ErrorAction Continue 84 | continue 85 | } 86 | 87 | # Restore the assignments 88 | try { 89 | $null = Invoke-MgGraphRequest -body $requestBody.toString() -Method POST -Uri "$apiVersion/deviceManagement/groupPolicyConfigurations/$($groupPolicyConfigurationObject.id)/assign" -ErrorAction Stop 90 | [PSCustomObject]@{ 91 | "Action" = "Restore" 92 | "Type" = "Administrative Template Assignments" 93 | "Name" = $groupPolicyConfigurationObject.displayName 94 | "Path" = "Administrative Templates\Assignments\$groupPolicyConfigurationName" 95 | } 96 | } 97 | catch { 98 | Write-Verbose "$groupPolicyConfigurationName - Failed to restore Administrative Template Assignment(s)" -Verbose 99 | Write-Error $_ -ErrorAction Continue 100 | } 101 | } 102 | } -------------------------------------------------------------------------------- /IntuneBackupAndRestore/Public/Start-IntuneBackup.ps1: -------------------------------------------------------------------------------- 1 | function Start-IntuneBackup() { 2 | <# 3 | .SYNOPSIS 4 | Backup Intune Configuration 5 | 6 | .DESCRIPTION 7 | Backup Intune Configuration 8 | 9 | .PARAMETER Path 10 | Path to store backup (JSON) files. 11 | 12 | .EXAMPLE 13 | Start-IntuneBackup -Path C:\temp 14 | 15 | .NOTES 16 | Requires the MSGraph SDK PowerShell Module 17 | 18 | Connect to MSGraph first, using the 'Connect-MgGraph' cmdlet and the scopes: 'DeviceManagementApps.ReadWrite.All, DeviceManagementConfiguration.ReadWrite.All, DeviceManagementServiceConfig.ReadWrite.All, DeviceManagementManagedDevices.ReadWrite.All'. 19 | #> 20 | 21 | [CmdletBinding()] 22 | param( 23 | [Parameter(Mandatory = $true)] 24 | [string]$Path 25 | ) 26 | 27 | [PSCustomObject]@{ 28 | "Action" = "Backup" 29 | "Type" = "Intune Backup and Restore Action" 30 | "Name" = "IntuneBackupAndRestore - Start Intune Backup Config and Assignments" 31 | "Path" = $Path 32 | } 33 | 34 | #Connect to MS-Graph if required 35 | if ($null -eq (Get-MgContext)) { 36 | connect-mggraph -scopes "EntitlementManagement.ReadWrite.All, DeviceManagementApps.ReadWrite.All, DeviceManagementConfiguration.ReadWrite.All, DeviceManagementServiceConfig.ReadWrite.All, DeviceManagementManagedDevices.ReadWrite.All" 37 | }else{ 38 | Write-Host "MS-Graph already connected, checking scopes" 39 | $scopes = Get-MgContext | Select-Object -ExpandProperty Scopes 40 | $IncorrectScopes = $false 41 | if ($scopes -notcontains "DeviceManagementApps.ReadWrite.All") {$IncorrectScopes = $true} 42 | if ($scopes -notcontains "DeviceManagementConfiguration.ReadWrite.All") {$IncorrectScopes = $true} 43 | if ($scopes -notcontains "DeviceManagementServiceConfig.ReadWrite.All") {$IncorrectScopes = $true} 44 | if ($scopes -notcontains "DeviceManagementManagedDevices.ReadWrite.All") {$IncorrectScopes = $true} 45 | if ($IncorrectScopes) { 46 | Write-Host "Incorrect scopes, please sign in again" 47 | connect-mggraph -scopes "DeviceManagementApps.ReadWrite.All, DeviceManagementConfiguration.ReadWrite.All, DeviceManagementServiceConfig.ReadWrite.All, DeviceManagementManagedDevices.ReadWrite.All" 48 | }else{ 49 | Write-Host "MS-Graph scopes are correct" 50 | } 51 | Write-Host "" 52 | } 53 | 54 | Invoke-IntuneBackupAutopilotDeploymentProfile -Path $Path 55 | Invoke-IntuneBackupAutopilotDeploymentProfileAssignment -Path $Path 56 | Invoke-IntuneBackupClientApp -Path $Path 57 | Invoke-IntuneBackupClientAppAssignment -Path $Path 58 | Invoke-IntuneBackupConfigurationPolicy -Path $Path 59 | Invoke-IntuneBackupConfigurationPolicyAssignment -Path $Path 60 | Invoke-IntuneBackupDeviceCompliancePolicy -Path $Path 61 | Invoke-IntuneBackupDeviceCompliancePolicyAssignment -Path $Path 62 | Invoke-IntuneBackupDeviceConfiguration -Path $Path 63 | Invoke-IntuneBackupDeviceConfigurationAssignment -Path $Path 64 | Invoke-IntuneBackupDeviceHealthScript -Path $Path 65 | Invoke-IntuneBackupDeviceHealthScriptAssignment -Path $Path 66 | Invoke-IntuneBackupDeviceManagementScript -Path $Path 67 | Invoke-IntuneBackupDeviceManagementScriptAssignment -Path $Path 68 | Invoke-IntuneBackupGroupPolicyConfiguration -Path $Path 69 | Invoke-IntuneBackupGroupPolicyConfigurationAssignment -Path $Path 70 | Invoke-IntuneBackupDeviceManagementIntent -Path $Path 71 | Invoke-IntuneBackupAppProtectionPolicy -Path $Path 72 | Invoke-IntuneBackupDeviceHealthScript -Path $Path 73 | Invoke-IntuneBackupDeviceHealthScriptAssignment -Path $Path 74 | } 75 | -------------------------------------------------------------------------------- /IntuneBackupAndRestore/Public/Start-IntuneRestoreAssignments.ps1: -------------------------------------------------------------------------------- 1 | function Start-IntuneRestoreAssignments() { 2 | <# 3 | .SYNOPSIS 4 | Restore Intune Configuration Assignments 5 | 6 | .DESCRIPTION 7 | Restore Intune Configuration Assignments 8 | 9 | .PARAMETER Path 10 | Path where backup (JSON) files are located. 11 | 12 | .EXAMPLE 13 | Start-IntuneRestoreAssignments -Path C:\temp -RestoreById $false 14 | 15 | .NOTES 16 | Requires the MSGraph SDK PowerShell Module 17 | 18 | Connect to MSGraph first, using the 'Connect-MgGraph' cmdlet. 19 | 20 | Set $RestoreById to $true, if the Configuration itself was not restored from backup. Set $RestoreById to $false if the configurations have been re-created (new unique ID's). 21 | #> 22 | 23 | [CmdletBinding()] 24 | param( 25 | [Parameter(Mandatory = $true)] 26 | [string]$Path, 27 | 28 | [Parameter(Mandatory = $false)] 29 | [bool]$RestoreById = $false 30 | ) 31 | 32 | [PSCustomObject]@{ 33 | "Action" = "Restore" 34 | "Type" = "Intune Backup and Restore Action" 35 | "Name" = "IntuneBackupAndRestore - Start Intune Restore Assignments" 36 | "Path" = $Path 37 | } 38 | 39 | #Connect to MS-Graph if required 40 | if ($null -eq (Get-MgContext)) { 41 | connect-mggraph -scopes "EntitlementManagement.ReadWrite.All, DeviceManagementApps.ReadWrite.All, DeviceManagementConfiguration.ReadWrite.All, DeviceManagementServiceConfig.ReadWrite.All, DeviceManagementManagedDevices.ReadWrite.All" 42 | }else{ 43 | Write-Host "MS-Graph already connected, checking scopes" 44 | $scopes = Get-MgContext | Select-Object -ExpandProperty Scopes 45 | $IncorrectScopes = $false 46 | if ($scopes -notcontains "DeviceManagementApps.ReadWrite.All") {$IncorrectScopes = $true} 47 | if ($scopes -notcontains "DeviceManagementConfiguration.ReadWrite.All") {$IncorrectScopes = $true} 48 | if ($scopes -notcontains "DeviceManagementServiceConfig.ReadWrite.All") {$IncorrectScopes = $true} 49 | if ($scopes -notcontains "DeviceManagementManagedDevices.ReadWrite.All") {$IncorrectScopes = $true} 50 | if ($IncorrectScopes) { 51 | Write-Host "Incorrect scopes, please sign in again" 52 | connect-mggraph -scopes "DeviceManagementApps.ReadWrite.All, DeviceManagementConfiguration.ReadWrite.All, DeviceManagementServiceConfig.ReadWrite.All, DeviceManagementManagedDevices.ReadWrite.All" 53 | }else{ 54 | Write-Host "MS-Graph scopes are correct" 55 | } 56 | 57 | } 58 | 59 | Invoke-IntuneRestoreAutopilotDeploymentProfileAssignment -Path $path -RestoreById $restoreById 60 | Invoke-IntuneRestoreConfigurationPolicyAssignment -Path $path -RestoreById $restoreById 61 | Invoke-IntuneRestoreClientAppAssignment -Path $path -RestoreById $restoreById 62 | Invoke-IntuneRestoreDeviceCompliancePolicyAssignment -Path $path -RestoreById $restoreById 63 | Invoke-IntuneRestoreDeviceConfigurationAssignment -Path $path -RestoreById $restoreById 64 | Invoke-IntuneRestoreDeviceHealthScriptAssignment -Path $Path -RestoreById $restoreById 65 | Invoke-IntuneRestoreDeviceManagementScriptAssignment -Path $path -RestoreById $restoreById 66 | Invoke-IntuneRestoreGroupPolicyConfigurationAssignment -Path $path -RestoreById $restoreById 67 | 68 | } -------------------------------------------------------------------------------- /IntuneBackupAndRestore/Public/Start-IntuneRestoreConfig.ps1: -------------------------------------------------------------------------------- 1 | function Start-IntuneRestoreConfig() { 2 | <# 3 | .SYNOPSIS 4 | Restore Intune Configuration 5 | 6 | .DESCRIPTION 7 | Restore Intune Configuration 8 | 9 | .PARAMETER Path 10 | Path where backup (JSON) files are located. 11 | 12 | .EXAMPLE 13 | Start-IntuneRestore -Path C:\temp 14 | 15 | .NOTES 16 | Requires the MSGraph SDK PowerShell Module 17 | 18 | Connect to MSGraph first, using the 'Connect-MgGraph' cmdlet. 19 | #> 20 | 21 | [CmdletBinding()] 22 | param( 23 | [Parameter(Mandatory = $true)] 24 | [string]$Path 25 | ) 26 | 27 | [PSCustomObject]@{ 28 | "Action" = "Restore" 29 | "Type" = "Intune Backup and Restore Action" 30 | "Name" = "IntuneBackupAndRestore - Start Intune Restore Config" 31 | "Path" = $Path 32 | } 33 | 34 | #Connect to MS-Graph if required 35 | if ($null -eq (Get-MgContext)) { 36 | connect-mggraph -scopes "EntitlementManagement.ReadWrite.All, DeviceManagementApps.ReadWrite.All, DeviceManagementConfiguration.ReadWrite.All, DeviceManagementServiceConfig.ReadWrite.All, DeviceManagementManagedDevices.ReadWrite.All" 37 | }else{ 38 | Write-Host "MS-Graph already connected, checking scopes" 39 | $scopes = Get-MgContext | Select-Object -ExpandProperty Scopes 40 | $IncorrectScopes = $false 41 | if ($scopes -notcontains "DeviceManagementApps.ReadWrite.All") {$IncorrectScopes = $true} 42 | if ($scopes -notcontains "DeviceManagementConfiguration.ReadWrite.All") {$IncorrectScopes = $true} 43 | if ($scopes -notcontains "DeviceManagementServiceConfig.ReadWrite.All") {$IncorrectScopes = $true} 44 | if ($scopes -notcontains "DeviceManagementManagedDevices.ReadWrite.All") {$IncorrectScopes = $true} 45 | if ($IncorrectScopes) { 46 | Write-Host "Incorrect scopes, please sign in again" 47 | connect-mggraph -scopes "DeviceManagementApps.ReadWrite.All, DeviceManagementConfiguration.ReadWrite.All, DeviceManagementServiceConfig.ReadWrite.All, DeviceManagementManagedDevices.ReadWrite.All" 48 | }else{ 49 | Write-Host "MS-Graph scopes are correct" 50 | } 51 | 52 | } 53 | 54 | Invoke-IntuneRestoreAutopilotDeploymentProfile -Path $Path 55 | Invoke-IntuneRestoreConfigurationPolicy -Path $Path 56 | Invoke-IntuneRestoreDeviceCompliancePolicy -Path $Path 57 | Invoke-IntuneRestoreDeviceConfiguration -Path $Path 58 | Invoke-IntuneRestoreDeviceHealthScript -Path $Path 59 | Invoke-IntuneRestoreDeviceManagementScript -Path $Path 60 | Invoke-IntuneRestoreGroupPolicyConfiguration -Path $Path 61 | Invoke-IntuneRestoreDeviceManagementIntent -Path $Path 62 | Invoke-IntuneRestoreAppProtectionPolicy -Path $Path 63 | } 64 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 John Seerden 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Intune Backup & Restore 2 | 3 | ![PowerShell Gallery](https://img.shields.io/powershellgallery/v/IntuneBackupAndRestore.svg?label=PSGallery%20Version&logo=PowerShell&style=flat-square) 4 | ![PowerShell Gallery](https://img.shields.io/powershellgallery/dt/IntuneBackupAndRestore.svg?label=PSGallery%20Downloads&logo=PowerShell&style=flat-square) 5 | 6 | This PowerShell Module queries Microsoft Graph, and allows for cross-tenant Backup & Restore actions of your Intune Configuration. 7 | 8 | Intune Configuration is backed up as (json) files in a given directory. 9 | 10 | ## Credits 11 | Thanks https://github.com/mhu4711 for updating this PowerShell module to the Microsoft.Graph PowerShell module. 12 | 13 | ## Installing IntuneBackupAndRestore 14 | 15 | ```powershell 16 | # Install IntuneBackupAndRestore from the PowerShell Gallery 17 | Install-Module -Name IntuneBackupAndRestore 18 | ``` 19 | 20 | ## Updating IntuneBackupAndRestore 21 | 22 | ```powershell 23 | # Update IntuneBackupAndRestore from the PowerShell Gallery 24 | Update-Module -Name IntuneBackupAndRestore 25 | ``` 26 | 27 | ## Prerequisites 28 | - Requires [Microsoft.Graph](https://github.com/microsoftgraph/msgraph-sdk-powershell) PowerShell Module (`Install-Module -Name Microsoft.Graph`, `Install-Module Microsoft.Graph.Beta -AllowClobber`) 29 | - Make sure to import the IntuneBackupAndRestore PowerShell module before using it with the `Import-Module IntuneBackupAndRestore` cmdlet. 30 | 31 | ## Features 32 | 33 | ### Backup actions 34 | - Administrative Templates (Device Configurations) 35 | - Administrative Template Assignments 36 | - App Protection Policies 37 | - App Protection Policy Assignments 38 | - Autopilot Deployment Profiles 39 | - Autopilot Deployment Profile Assignments 40 | - Client Apps 41 | - Client App Assignments 42 | - Device Compliance Policies 43 | - Device Compliance Policy Assignments 44 | - Device Configurations 45 | - Device Configuration Assignments 46 | - Device Management Scripts (Device Configuration -> PowerShell Scripts) 47 | - Device Management Script Assignments 48 | - Proactive Remediations 49 | - Proactive Remediation Assignments 50 | - Settings Catalog Policies 51 | - Settings Catalog Policy Assignments 52 | - Software Update Rings 53 | - Software Update Ring Assignments 54 | - Endpoint Security Configurations 55 | - Security Baselines 56 | - Windows 10 Security Baselines 57 | - Microsoft Defender ATP Baselines 58 | - Microsoft Edge Baseline 59 | - Antivirus 60 | - Disk encryption 61 | - Firewall 62 | - Endpoint detection and response 63 | - Attack surface reduction 64 | - Account protection 65 | - Device compliance 66 | 67 | ### Restore actions 68 | - Administrative Templates (Device Configurations) 69 | - Administrative Template Assignments 70 | - App Protection Policies 71 | - App Protection Policy Assignments 72 | - Autopilot Deployment Profiles 73 | - Autopilot Deployment Profile Assignments 74 | - Client App Assignments 75 | - Device Compliance Policies 76 | - Device Compliance Policy Assignments 77 | - Device Configurations 78 | - Device Configuration Assignments 79 | - Device Management Scripts (Device Configuration -> PowerShell Scripts) 80 | - Device Management Script Assignments 81 | - Proactive Remediations 82 | - Proactive Remediation Assignments 83 | - Settings Catalog Policies 84 | - Settings Catalog Policy Assignments 85 | - Software Update Rings 86 | - Software Update Ring Assignments 87 | - Endpoint Security Configurations 88 | - Security Baselines 89 | - Windows 10 Security Baselines 90 | - Microsoft Defender ATP Baselines 91 | - Microsoft Edge Baseline 92 | - Antivirus 93 | - Disk encryption 94 | - Firewall 95 | - Endpoint detection and response 96 | - Attack surface reduction 97 | - Account protection 98 | - Device compliance 99 | 100 | > Please note that some Client App settings can be backed up, for instance the retrieval of Win32 (un)install cmdlets, requirements, etcetera. The Client App itself is not backed up and this module does not support restoring Client Apps at this time. 101 | 102 | ## Examples 103 | 104 | ### Example 01 - Full Intune Backup 105 | ```powershell 106 | Start-IntuneBackup -Path C:\temp\IntuneBackup 107 | ``` 108 | 109 | ### Example 02 - Full Intune Restore 110 | ```powershell 111 | Start-IntuneRestoreConfig -Path C:\temp\IntuneBackup 112 | Start-IntuneRestoreAssignments -Path C:\temp\IntuneBackup 113 | ``` 114 | 115 | ### Example 03 - Restore Intune Assignments 116 | If configurations have been restored: 117 | ```powershell 118 | Start-IntuneRestoreAssignments -Path C:\temp\IntuneBackup 119 | ``` 120 | 121 | If reassigning assignments to existing (non-restored) configurations. In this case the assignments match the configuration id to restore to. 122 | This allows for restoring if display names have changed. 123 | ```powershell 124 | Start-IntuneRestoreAssignments -Path C:\temp\IntuneBackup -RestoreById $true 125 | ``` 126 | 127 | ### Example 04 - Restore only Intune Compliance Policies 128 | 129 | ```powershell 130 | Invoke-IntuneRestoreDeviceCompliancePolicy -Path C:\temp\IntuneBackup 131 | ``` 132 | 133 | ```powershell 134 | Invoke-IntuneRestoreDeviceCompliancePolicyAssignment -Path C:\temp\IntuneBackup 135 | ``` 136 | 137 | ### Example 05 - Restore Only Intune Device Configurations 138 | ```powershell 139 | Invoke-IntuneRestoreDeviceConfiguration -Path C:\temp\IntuneBackup 140 | ``` 141 | 142 | ```powershell 143 | Invoke-IntuneRestoreDeviceConfigurationAssignment -Path C:\temp\IntuneBackup 144 | ``` 145 | 146 | ### Example 06 - Backup Only Intune Endpoint Security Configurations 147 | ```powershell 148 | Invoke-IntuneBackupDeviceManagementIntent -Path C:\temp\IntuneBackup 149 | ``` 150 | 151 | ### Example 07 - Restore Only Intune Endpoint Security Configurations 152 | ```powershell 153 | Invoke-IntuneRestoreDeviceManagementIntent -Path C:\temp\IntuneBackup 154 | ``` 155 | 156 | ### Example 08 - Compare two Backup Files for changes 157 | ```powershell 158 | # The DifferenceFilePath should point to the latest Intune Backup file, as it might contain new properties. 159 | Compare-IntuneBackupFile -ReferenceFilePath 'C:\temp\IntuneBackup\Device Configurations\Windows - Endpoint Protection.json' -DifferenceFilePath 'C:\temp\IntuneBackupLatest\Device Configurations\Windows - Endpoint Protection.json' 160 | ``` 161 | 162 | ### Example 09 - Compare all files in two Backup Directories for changes 163 | ```powershell 164 | # The DifferenceFilePath should point to the latest Intune Backup file, as it might contain new properties. 165 | Compare-IntuneBackupDirectories -ReferenceDirectory 'C:\temp\IntuneBackup' -DifferenceDirectory 'C:\temp\IntuneBackup2' 166 | ``` 167 | 168 | ## Known Issues 169 | - Does not support backing up Intune configuration items with duplicate Display Names. Files may be overwritten. 170 | --------------------------------------------------------------------------------