mirror of
https://github.com/Micke-K/IntuneManagement.git
synced 2026-09-28 19:05:38 +02:00
383 lines
14 KiB
PowerShell
383 lines
14 KiB
PowerShell
#ImportOrder 30
|
|
|
|
class CompareProviderBase
|
|
{
|
|
[string] $Name
|
|
[string] $Value
|
|
[string] $OptionsXaml
|
|
[string[]] $RemoveProperties = @()
|
|
[bool] $IgnoreGroups = $false
|
|
# Skip objects that exist on only one side. Source = the reverse pass
|
|
# (objects only in the counterpart set); Destination = the forward pass
|
|
# (objects whose looked-up counterpart is missing). Same semantics as the
|
|
# original project's Skip Missing Source/Destination Policies checkboxes.
|
|
[bool] $SkipMissingSourcePolicies = $false
|
|
[bool] $SkipMissingDestinationPolicies = $false
|
|
|
|
CompareProviderBase([string]$name, [string]$value, [string]$optionsXaml)
|
|
{
|
|
$this.Name = $name
|
|
$this.Value = $value
|
|
$this.OptionsXaml = $optionsXaml
|
|
}
|
|
|
|
[object[]] GetComparePairs([object[]]$groups) { return @() }
|
|
[bool] Validate() { return $true }
|
|
[void] SaveSettings() {}
|
|
[string] ToString() { return $this.Name }
|
|
}
|
|
|
|
class CompareExportFilesProvider : CompareProviderBase
|
|
{
|
|
[string] $ExportPath
|
|
[string] $NameFilter
|
|
|
|
CompareExportFilesProvider() : base(
|
|
"Exported Files with Intune Objects (Id)",
|
|
"export",
|
|
"CompareExportOptions"
|
|
) {}
|
|
|
|
[object[]] GetComparePairs([object[]]$groups)
|
|
{
|
|
$pairs = @()
|
|
|
|
foreach($group in $groups)
|
|
{
|
|
foreach($policyType in $group.PolicyTypes)
|
|
{
|
|
$folder = [IO.Path]::Combine($this.ExportPath, $policyType.Folder)
|
|
if(-not [IO.Directory]::Exists($folder))
|
|
{
|
|
Write-Log "Folder '$folder' not found. Skipping." 2
|
|
continue
|
|
}
|
|
|
|
Save-SettingStoreValue "" "LastUsedFullPath" $folder
|
|
Write-Status "Compare $($policyType.Title)" -Force -SkipLog
|
|
|
|
$fileObjs = @(Get-PoliciesFromFolder -Path $folder -PolicyTypes @($policyType))
|
|
$intuneObjs = @(Get-GraphPolicies -PolicyGroup $group.ID | Where-Object { $_.PolicyType.ID -eq $policyType.ID })
|
|
|
|
$pairedFileIds = @()
|
|
foreach($fileObj in $fileObjs)
|
|
{
|
|
$objName = $fileObj.Name
|
|
if($this.NameFilter -and $objName -notmatch [RegEx]::Escape($this.NameFilter)) { continue }
|
|
if(-not $fileObj.ID) { Write-Log "File '$objName' has no Id. Skipping." 2; continue }
|
|
|
|
$intuneObj = $intuneObjs | Where-Object { $_.ID -eq $fileObj.ID }
|
|
if($intuneObj) { $policyType.GetFullObject($intuneObj) | Out-Null }
|
|
elseif($this.SkipMissingDestinationPolicies) { continue }
|
|
else { Write-Log "Object '$objName' with id $($fileObj.ID) not found in Intune. Deleted?" 2 }
|
|
|
|
$pairedFileIds += [string]$fileObj.ID
|
|
$pairs += [PSCustomObject]@{
|
|
Name = $objName
|
|
Id = $fileObj.ID
|
|
PolicyType = $policyType
|
|
SaveFolder = $folder
|
|
Policy1 = $intuneObj
|
|
Policy2 = $fileObj
|
|
}
|
|
}
|
|
|
|
if(-not $this.SkipMissingSourcePolicies)
|
|
{
|
|
# Objects that exist in Intune but were never exported.
|
|
foreach($intuneObj in $intuneObjs)
|
|
{
|
|
if([string]$intuneObj.ID -in $pairedFileIds) { continue }
|
|
$objName = $intuneObj.Name
|
|
if($this.NameFilter -and $objName -notmatch [RegEx]::Escape($this.NameFilter)) { continue }
|
|
|
|
Write-Log "Object '$objName' with id $($intuneObj.ID) exists in Intune but has no exported file. New object?" 2
|
|
$pairs += [PSCustomObject]@{
|
|
Name = $objName
|
|
Id = $intuneObj.ID
|
|
PolicyType = $policyType
|
|
SaveFolder = $folder
|
|
Policy1 = $intuneObj
|
|
Policy2 = $null
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return $pairs
|
|
}
|
|
|
|
[void] SaveSettings()
|
|
{
|
|
# No persisted options: the "Compare\ExportPath" write that used to live here
|
|
# was never read back anywhere (dead since the port).
|
|
}
|
|
}
|
|
|
|
class CompareIntuneWithExportProvider : CompareProviderBase
|
|
{
|
|
[string] $ExportPath
|
|
[string] $NameFilter
|
|
|
|
CompareIntuneWithExportProvider() : base(
|
|
"Intune Objects with Exported Files (Name)",
|
|
"IntuneWithExport",
|
|
"CompareExportOptions"
|
|
) {}
|
|
|
|
[object[]] GetComparePairs([object[]]$groups)
|
|
{
|
|
$pairs = @()
|
|
|
|
foreach($group in $groups)
|
|
{
|
|
foreach($policyType in $group.PolicyTypes)
|
|
{
|
|
$folder = [IO.Path]::Combine($this.ExportPath, $policyType.Folder)
|
|
if(-not [IO.Directory]::Exists($folder))
|
|
{
|
|
Write-Log "Folder '$folder' not found. Skipping." 2
|
|
continue
|
|
}
|
|
|
|
Save-SettingStoreValue "" "LastUsedFullPath" $folder
|
|
Write-Status "Compare $($policyType.Title)" -Force -SkipLog
|
|
|
|
$fileObjs = @(Get-PoliciesFromFolder -Path $folder -PolicyTypes @($policyType))
|
|
$intuneObjs = @(Get-GraphPolicies -PolicyGroup $group.ID | Where-Object { $_.PolicyType.ID -eq $policyType.ID })
|
|
|
|
$pairedFileNames = @()
|
|
foreach($intuneObj in $intuneObjs)
|
|
{
|
|
$objName = $intuneObj.Name
|
|
if($this.NameFilter -and $objName -notmatch [RegEx]::Escape($this.NameFilter)) { continue }
|
|
|
|
$fileObj = $fileObjs | Where-Object { $_.Name -eq $objName }
|
|
if(($fileObj | Measure-Object).Count -gt 1)
|
|
{
|
|
Write-Log "Multiple file objects with name '$objName'. Skipping." 2
|
|
continue
|
|
}
|
|
|
|
if($fileObj) {
|
|
$policyType.GetFullObject($intuneObj) | Out-Null
|
|
$pairedFileNames += [string]$objName
|
|
}
|
|
elseif($this.SkipMissingDestinationPolicies) { continue }
|
|
else { Write-Log "Object '$objName' with id $($intuneObj.ID) not found in exported folder. New object?" 2 }
|
|
|
|
$pairs += [PSCustomObject]@{
|
|
Name = $objName
|
|
Id = $intuneObj.ID
|
|
PolicyType = $policyType
|
|
SaveFolder = $folder
|
|
Policy1 = $intuneObj
|
|
Policy2 = $fileObj
|
|
}
|
|
}
|
|
|
|
if(-not $this.SkipMissingSourcePolicies)
|
|
{
|
|
# Exported files with no matching Intune object.
|
|
foreach($fileObj in $fileObjs)
|
|
{
|
|
$objName = $fileObj.Name
|
|
if([string]$objName -in $pairedFileNames) { continue }
|
|
if($this.NameFilter -and $objName -notmatch [RegEx]::Escape($this.NameFilter)) { continue }
|
|
|
|
Write-Log "File object '$objName' has no matching Intune object. Deleted?" 2
|
|
$pairs += [PSCustomObject]@{
|
|
Name = $objName
|
|
Id = $fileObj.ID
|
|
PolicyType = $policyType
|
|
SaveFolder = $folder
|
|
Policy1 = $null
|
|
Policy2 = $fileObj
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return $pairs
|
|
}
|
|
|
|
[void] SaveSettings()
|
|
{
|
|
# No persisted options: the "Compare\ExportPath" write that used to live here
|
|
# was never read back anywhere (dead since the port).
|
|
}
|
|
}
|
|
|
|
class CompareNamedObjectsProvider : CompareProviderBase
|
|
{
|
|
[string] $SourcePattern
|
|
[string] $ComparePattern
|
|
[string] $SavePath
|
|
[string[]] $RemoveProperties = @("Id")
|
|
|
|
CompareNamedObjectsProvider() : base(
|
|
"Named Objects in Intune",
|
|
"name",
|
|
"CompareNamedOptions"
|
|
)
|
|
{
|
|
$this.RemoveProperties = @("Id")
|
|
}
|
|
|
|
[object[]] GetComparePairs([object[]]$groups)
|
|
{
|
|
if(-not $this.SourcePattern -or -not $this.ComparePattern)
|
|
{
|
|
throw "Both source and compare name patterns must be specified"
|
|
}
|
|
|
|
$outputFolder = $this.SavePath
|
|
if(-not $outputFolder) { $outputFolder = [Environment]::GetFolderPath("MyDocuments") }
|
|
|
|
$pairs = @()
|
|
|
|
foreach($group in $groups)
|
|
{
|
|
Write-Status "Compare $($group.Title) objects" -Force -SkipLog
|
|
$intuneObjs = @(Get-GraphPolicies -PolicyGroup $group.ID)
|
|
|
|
foreach($intuneObj in ($intuneObjs | Where-Object { $_.Name -imatch [regex]::Escape($this.SourcePattern) }))
|
|
{
|
|
$sourceName = $intuneObj.Name
|
|
$compareName = $sourceName -ireplace [regex]::Escape($this.SourcePattern), $this.ComparePattern
|
|
|
|
$compareObj = $intuneObjs | Where-Object { $_.Name -eq $compareName -and $_.Object.'@OData.Type' -eq $intuneObj.Object.'@OData.Type' }
|
|
if(($compareObj | Measure-Object).Count -gt 1)
|
|
{
|
|
Write-Log "Multiple objects named '$compareName'. Skipping." 2
|
|
continue
|
|
}
|
|
|
|
if($compareObj)
|
|
{
|
|
$intuneObj.PolicyType.GetFullObject($intuneObj) | Out-Null
|
|
$compareObj.PolicyType.GetFullObject($compareObj) | Out-Null
|
|
}
|
|
|
|
$pairs += [PSCustomObject]@{
|
|
Name = $sourceName
|
|
Id = $intuneObj.ID
|
|
PolicyType = $intuneObj.PolicyType
|
|
SaveFolder = $outputFolder
|
|
Policy1 = $intuneObj
|
|
Policy2 = $compareObj
|
|
}
|
|
}
|
|
}
|
|
|
|
return $pairs
|
|
}
|
|
|
|
[void] SaveSettings()
|
|
{
|
|
# No persisted options: the CompareSource / CompareWith / SavePath writes that
|
|
# used to live here were never read back anywhere (dead since the port).
|
|
}
|
|
}
|
|
|
|
class CompareExportedFoldersProvider : CompareProviderBase
|
|
{
|
|
[string] $SourcePath
|
|
[string] $ComparePath
|
|
[string] $NameFilter
|
|
|
|
CompareExportedFoldersProvider() : base(
|
|
"Files in Exported Folders",
|
|
"exportedFolders",
|
|
"CompareExportedFilesOptions"
|
|
) {}
|
|
|
|
[object[]] GetComparePairs([object[]]$groups)
|
|
{
|
|
if(-not $this.SourcePath -or -not $this.ComparePath)
|
|
{
|
|
throw "Both source and compare folders must be specified"
|
|
}
|
|
if(-not [IO.Directory]::Exists($this.SourcePath))
|
|
{
|
|
throw "Source folder '$($this.SourcePath)' does not exist"
|
|
}
|
|
if(-not [IO.Directory]::Exists($this.ComparePath))
|
|
{
|
|
throw "Compare folder '$($this.ComparePath)' does not exist"
|
|
}
|
|
|
|
$pairs = @()
|
|
|
|
foreach($group in $groups)
|
|
{
|
|
foreach($policyType in $group.PolicyTypes)
|
|
{
|
|
$folderSrc = [IO.Path]::Combine($this.SourcePath, $policyType.Folder)
|
|
$folderCmp = [IO.Path]::Combine($this.ComparePath, $policyType.Folder)
|
|
|
|
if(-not [IO.Directory]::Exists($folderSrc)) { Write-Log "Source folder '$folderSrc' not found. Skipping." 2; continue }
|
|
|
|
Save-SettingStoreValue "" "LastUsedFullPath" $folderSrc
|
|
Write-Status "Compare $($policyType.Title)" -Force -SkipLog
|
|
|
|
$srcObjs = @(Get-PoliciesFromFolder -Path $folderSrc -PolicyTypes @($policyType))
|
|
$cmpObjs = @()
|
|
if([IO.Directory]::Exists($folderCmp))
|
|
{
|
|
$cmpObjs = @(Get-PoliciesFromFolder -Path $folderCmp -PolicyTypes @($policyType))
|
|
}
|
|
|
|
$addedIds = @()
|
|
|
|
foreach($srcObj in $srcObjs)
|
|
{
|
|
$objName = $srcObj.Name
|
|
if($this.NameFilter -and $objName -notmatch [RegEx]::Escape($this.NameFilter)) { continue }
|
|
if(-not $srcObj.ID) { Write-Log "File '$objName' has no Id. Skipping." 2; continue }
|
|
|
|
$cmpObj = $cmpObjs | Where-Object { $_.ID -eq $srcObj.ID }
|
|
$addedIds += $srcObj.ID
|
|
if(-not $cmpObj -and $this.SkipMissingDestinationPolicies) { continue }
|
|
|
|
$pairs += [PSCustomObject]@{
|
|
Name = $objName
|
|
Id = $srcObj.ID
|
|
PolicyType = $policyType
|
|
SaveFolder = $folderSrc
|
|
Policy1 = $srcObj
|
|
Policy2 = $cmpObj
|
|
}
|
|
}
|
|
|
|
foreach($cmpObj in $cmpObjs)
|
|
{
|
|
if($this.SkipMissingSourcePolicies) { continue }
|
|
if($cmpObj.ID -in $addedIds) { continue }
|
|
$objName = $cmpObj.Name
|
|
if($this.NameFilter -and $objName -notmatch [RegEx]::Escape($this.NameFilter)) { continue }
|
|
|
|
$pairs += [PSCustomObject]@{
|
|
Name = $objName
|
|
Id = $cmpObj.ID
|
|
PolicyType = $policyType
|
|
SaveFolder = $folderSrc
|
|
Policy1 = $null
|
|
Policy2 = $cmpObj
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return $pairs
|
|
}
|
|
|
|
[void] SaveSettings()
|
|
{
|
|
# No persisted options: the SourcePath / ComparePath writes that used to live
|
|
# here were never read back anywhere (dead since the port).
|
|
}
|
|
}
|