mirror of
https://github.com/Micke-K/IntuneManagement.git
synced 2026-09-28 19:05:38 +02:00
IntuneManagement 4.0.0-beta1
This commit is contained in:
@@ -0,0 +1,233 @@
|
||||
function Start-GraphBulkImport {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Bulk-import exported Intune policy objects from disk.
|
||||
|
||||
.DESCRIPTION
|
||||
Public, UI-independent driver for bulk import. The WPF and Avalonia
|
||||
bulk-import forms are thin callers of this function; the same function
|
||||
can be invoked from a scheduled task or pipeline.
|
||||
|
||||
Groups are processed in ascending ImportOrder so dependencies (Scope
|
||||
Tags, etc.) import before the objects that reference them.
|
||||
|
||||
.PARAMETER ImportFolder
|
||||
Root folder of a previous export (the folder that contains the
|
||||
per-policy-type sub-folders).
|
||||
|
||||
.PARAMETER Filter
|
||||
Name filter (literal substring, case-insensitive) applied to the file
|
||||
objects before import. Empty = import everything found.
|
||||
|
||||
.PARAMETER PolicyGroup
|
||||
Restrict the import to these PolicyGroup IDs. If omitted, every group
|
||||
that allows Import is processed.
|
||||
|
||||
.PARAMETER ImportAssignments
|
||||
Import object assignments. Persisted to the ImportAssignments setting
|
||||
(the import pipeline reads it from there) when supplied.
|
||||
|
||||
.PARAMETER ImportScopeTags
|
||||
Import scope tags. Persisted to the ImportScopeTags setting when supplied.
|
||||
|
||||
.PARAMETER ReplaceDependencyIDs
|
||||
Translate dependency object IDs via the migration table. Persisted to
|
||||
the ResolveReferenceInfo setting when supplied.
|
||||
|
||||
.PARAMETER ImportType
|
||||
How files are imported: alwaysImport (default), skipIfExist, update,
|
||||
replace, or replace_with_assignments. Persisted to the ImportType
|
||||
setting when supplied. Anything other than alwaysImport resolves each
|
||||
file against the existing objects (Resolve-IntuneImportUpdateTarget)
|
||||
and skips / updates / replaces accordingly.
|
||||
|
||||
.PARAMETER TokenId
|
||||
Authentication token id. Defaults to the current default token.
|
||||
|
||||
.EXAMPLE
|
||||
Start-GraphBulkImport -ImportFolder C:\IntuneExport
|
||||
|
||||
.EXAMPLE
|
||||
Start-GraphBulkImport -ImportFolder C:\IntuneExport -PolicyGroup DeviceConfiguration -Filter "Baseline"
|
||||
|
||||
.OUTPUTS
|
||||
PSCustomObject with summary statistics (Groups, Imported, Duration).
|
||||
#>
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]
|
||||
$ImportFolder,
|
||||
|
||||
[string]
|
||||
$Filter,
|
||||
|
||||
[string[]]
|
||||
$PolicyGroup,
|
||||
|
||||
[Nullable[bool]]
|
||||
$ImportAssignments,
|
||||
|
||||
[Nullable[bool]]
|
||||
$ImportScopeTags,
|
||||
|
||||
[Nullable[bool]]
|
||||
$ReplaceDependencyIDs,
|
||||
|
||||
[string]
|
||||
$ImportType,
|
||||
|
||||
[Int]
|
||||
$TokenId = (Get-DefaultTokenId)
|
||||
)
|
||||
|
||||
$stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
|
||||
|
||||
$ImportFolder = Expand-FileName $ImportFolder
|
||||
if (-not [IO.Directory]::Exists($ImportFolder)) {
|
||||
throw "Import folder not found: $ImportFolder"
|
||||
}
|
||||
|
||||
# The import pipeline (ImportObject / assignment + scope-tag handling) reads
|
||||
# these via Get-SettingValue, which resolves them from their registered
|
||||
# SubPath ("IntuneManager"). Persist to the SAME SubPath so a supplied
|
||||
# per-run value is actually read back (saving to root "" left them orphaned).
|
||||
if ($null -ne $ImportAssignments) { Save-SettingStoreValue "IntuneManager" "ImportAssignments" $ImportAssignments }
|
||||
if ($null -ne $ImportScopeTags) { Save-SettingStoreValue "IntuneManager" "ImportScopeTags" $ImportScopeTags }
|
||||
if ($null -ne $ReplaceDependencyIDs) { Save-SettingStoreValue "IntuneManager" "ResolveReferenceInfo" $ReplaceDependencyIDs }
|
||||
if ($ImportType) { Save-SettingStoreValue "IntuneManager" "ImportType" $ImportType }
|
||||
|
||||
# ---- Resolve target groups ----
|
||||
$selection = Resolve-IntuneTargetSelectors -PolicyGroup $PolicyGroup -Caller 'Start-GraphBulkImport'
|
||||
$unknownSelectors = $selection.Unknown # ids from -PolicyGroup that matched nothing
|
||||
$targetGroups = [System.Collections.Generic.List[object]]::new()
|
||||
$targetGroups.AddRange([object[]]$selection.Groups)
|
||||
if (-not $PolicyGroup) {
|
||||
foreach ($grp in $script:IntuneGroups) {
|
||||
if (-not $grp.Title) { continue }
|
||||
if ($grp.ShowButtons -is [Object[]] -and $grp.ShowButtons -notcontains "Import") { continue }
|
||||
[void]$targetGroups.Add($grp)
|
||||
}
|
||||
}
|
||||
if ($targetGroups.Count -eq 0) {
|
||||
Write-Log "Bulk import: no policy groups selected" 2
|
||||
return [PSCustomObject]@{ Groups = 0; Imported = 0; UnknownSelectors = $unknownSelectors; Duration = $stopwatch.Elapsed }
|
||||
}
|
||||
|
||||
# Honour the ClearCacheBeforeExportImport setting (bit 4 = bulk import).
|
||||
Invoke-GraphCacheClearBeforeOperation -Operation BulkImport -TokenId $TokenId
|
||||
|
||||
Write-Log "****************************************************************"
|
||||
Write-Log "Start bulk import from $ImportFolder"
|
||||
Write-Log "****************************************************************"
|
||||
|
||||
# Effective import type: parameter wins, then the persisted setting.
|
||||
$importTypeEffective = if ($ImportType) { $ImportType } else { [string](Get-SettingValue "ImportType" "alwaysImport") }
|
||||
if (-not $importTypeEffective) { $importTypeEffective = "alwaysImport" }
|
||||
|
||||
$sameTenant = $false
|
||||
if ($importTypeEffective -ne "alwaysImport") {
|
||||
try {
|
||||
$null, $sameTenant = Get-MigrationTableInfo $ImportFolder (Get-CurrentTenantId)
|
||||
} catch { }
|
||||
}
|
||||
|
||||
$totalImported = 0
|
||||
$totalUpdated = 0
|
||||
$totalReplaced = 0
|
||||
$totalSkipped = 0
|
||||
# Ascending ImportOrder so dependencies import before their dependents.
|
||||
$orderedGroups = @($targetGroups | Sort-Object { ($_.PolicyTypes | Measure-Object ImportOrder -Minimum).Minimum })
|
||||
$groupTotal = $orderedGroups.Count
|
||||
$groupIndex = 0
|
||||
|
||||
foreach ($grp in $orderedGroups) {
|
||||
$groupIndex++
|
||||
$policyTypes = $grp.PolicyTypes
|
||||
$subFolders = @($policyTypes | ForEach-Object { $_.Folder } | Where-Object { $_ })
|
||||
|
||||
Write-Log "----------------------------------------------------------------"
|
||||
Write-Log "Import $($grp.Title)"
|
||||
Write-Log "----------------------------------------------------------------"
|
||||
|
||||
try {
|
||||
Write-Status `
|
||||
-Text ("Bulk import - {0} ({1} of {2})" -f $grp.Title, $groupIndex, $groupTotal) `
|
||||
-Detail "Loading objects from folder" `
|
||||
-Force
|
||||
|
||||
$params = @{ Path = $ImportFolder; PolicyTypes = $policyTypes }
|
||||
if ($subFolders.Count -gt 0) { $params["SubFolders"] = $subFolders }
|
||||
|
||||
$policiesToImport = @(Get-PoliciesFromFolder @params)
|
||||
|
||||
if ($Filter) {
|
||||
$policiesToImport = @($policiesToImport | Where-Object { $_.Name -match [RegEx]::Escape($Filter) })
|
||||
}
|
||||
|
||||
if ($policiesToImport.Count -gt 0) {
|
||||
# Any mode other than alwaysImport resolves each file against
|
||||
# the existing objects first (skip / update / replace).
|
||||
if ($importTypeEffective -ne "alwaysImport") {
|
||||
$existing = @(Get-GraphPolicies -PolicyGroup $grp.ID -TokenId $TokenId)
|
||||
$toCreate = @()
|
||||
|
||||
foreach ($importPolicy in $policiesToImport) {
|
||||
$match = Resolve-IntuneImportUpdateTarget -ImportPolicy $importPolicy -ExistingPolicies $existing -SameTenant $sameTenant -ImportType $importTypeEffective
|
||||
|
||||
switch ($match.Action) {
|
||||
"Update" {
|
||||
try {
|
||||
if ($importPolicy.UpdateObject($match.Target, $TokenId)) { $totalUpdated++ }
|
||||
} catch { Write-LogError "UpdateObject failed for $($importPolicy.Name)" $_.Exception }
|
||||
}
|
||||
"Replace" {
|
||||
try {
|
||||
if (Invoke-IntuneImportReplace -ImportPolicy $importPolicy -Target $match.Target -ImportType $importTypeEffective -TokenId $TokenId) { $totalReplaced++ }
|
||||
} catch { Write-LogError "Replace failed for $($importPolicy.Name)" $_.Exception }
|
||||
}
|
||||
"Ambiguous" {
|
||||
$totalSkipped++
|
||||
Write-Log "Skip import for $($importPolicy.Name): $($match.Message)" 2
|
||||
}
|
||||
"Skip" {
|
||||
$totalSkipped++
|
||||
Write-Log "Skip import for $($importPolicy.Name): $($match.Message)"
|
||||
}
|
||||
default { $toCreate += $importPolicy }
|
||||
}
|
||||
}
|
||||
$policiesToImport = $toCreate
|
||||
}
|
||||
|
||||
if ($policiesToImport.Count -gt 0) {
|
||||
Write-Status -Detail ("Importing {0} object(s)" -f $policiesToImport.Count) -SkipLog -Force
|
||||
$imported = @($policiesToImport | Import-GraphPolicy)
|
||||
$totalImported += $imported.Count
|
||||
Write-Log "Imported $($imported.Count) $($grp.Title) object(s)"
|
||||
}
|
||||
}
|
||||
else {
|
||||
Write-Log "No $($grp.Title) files found in $ImportFolder"
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-LogError "Failed when importing $($grp.Title)" $_.Exception
|
||||
}
|
||||
}
|
||||
|
||||
Write-Status $null
|
||||
Write-Log "****************************************************************"
|
||||
Write-Log "Bulk import finished. $totalImported imported, $totalUpdated updated, $totalReplaced replaced, $totalSkipped skipped"
|
||||
Write-Log "****************************************************************"
|
||||
|
||||
return [PSCustomObject]@{
|
||||
Groups = $orderedGroups.Count
|
||||
Imported = $totalImported
|
||||
Updated = $totalUpdated
|
||||
Replaced = $totalReplaced
|
||||
Skipped = $totalSkipped
|
||||
UnknownSelectors = $unknownSelectors
|
||||
Duration = $stopwatch.Elapsed
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user