mirror of
https://github.com/Micke-K/IntuneManagement.git
synced 2026-09-28 19:05:38 +02:00
211 lines
9.3 KiB
PowerShell
211 lines
9.3 KiB
PowerShell
# Bulk Export form.
|
|
# Split out of IntuneManagerUIWPF.ps1 on 2026-06-03 to match the Avalonia tree's
|
|
# per-feature layout (architecture rule R9 — keep the WPF shell from growing further).
|
|
|
|
function Show-GraphBulkExportForm
|
|
{
|
|
$script:bulkExportForm = $script:UIProvider.GetXamlObject(($script:AppUIRootFolder + "\Xaml\BulkExportForm.xaml"), $true)
|
|
if(-not $script:bulkExportForm) { return }
|
|
|
|
$script:dgObjectsToExport = $script:bulkExportForm.FindName("dgObjectsToExport")
|
|
|
|
$exportSettings = [IntuneManagerExportSettings]::new()
|
|
|
|
$script:bulkExportForm.DataContext = $exportSettings
|
|
|
|
$script:UIProvider.AddXamlEvent($script:bulkExportForm, "browseExportPath", "add_click", {
|
|
$folder = Get-Folder ($script:UIProvider.GetXamlProperty($script:bulkExportForm, "txtExportPath", "Text")) "Select root folder for export"
|
|
if($folder)
|
|
{
|
|
$script:bulkExportForm.DataContext.ExportFolder = $folder
|
|
$tmp = $script:bulkExportForm.DataContext
|
|
$script:bulkExportForm.DataContext = $null
|
|
$script:bulkExportForm.DataContext = $tmp
|
|
}
|
|
})
|
|
|
|
# Register dynamic export-properties / UI extensions for every exportable
|
|
# policy type up front. Done once (not per mode switch) so toggling
|
|
# Group/API doesn't re-register and we never miss a type that only appears
|
|
# in the other view.
|
|
$script:bulkExportExportableGroups = @($script:IntuneGroups | Where-Object {
|
|
$_.Title -and (-not ($_.ShowButtons -is [Object[]]) -or $_.ShowButtons -contains "Export")
|
|
})
|
|
foreach($intuneGroup in $script:bulkExportExportableGroups)
|
|
{
|
|
$intuneGroup.PolicyTypes | Add-IntuneManagerExportProperties -ExportSettings $exportSettings
|
|
$intuneGroup.PolicyTypes | Add-IntuneManagerExportUIExtensions -Form $script:bulkExportForm
|
|
}
|
|
|
|
$column = Get-GridCheckboxColumn "Selected"
|
|
$script:dgObjectsToExport.Columns.Add($column)
|
|
|
|
$column.Header.IsChecked = $true # All items are checked by default
|
|
$column.Header.add_Click({
|
|
foreach($Item in $script:dgObjectsToExport.ItemsSource)
|
|
{
|
|
$Item.Selected = $this.IsChecked
|
|
}
|
|
$script:dgObjectsToExport.Items.Refresh()
|
|
}
|
|
)
|
|
|
|
# Add Object type column
|
|
$binding = [System.Windows.Data.Binding]::new("Title")
|
|
$column = [System.Windows.Controls.DataGridTextColumn]::new()
|
|
$column.Header = "Object type"
|
|
$column.IsReadOnly = $true
|
|
$column.Binding = $binding
|
|
$script:dgObjectsToExport.Columns.Add($column)
|
|
|
|
# Default view is "Group" (matches the IsChecked=True on the XAML radio).
|
|
$script:bulkExportMode = "Group"
|
|
Update-BulkExportObjectList
|
|
|
|
# Repopulate the grid when the user flips between Group and API views.
|
|
$script:UIProvider.AddXamlEvent($script:bulkExportForm, "rbBulkExportViewGroup", "add_Checked", {
|
|
$script:bulkExportMode = "Group"
|
|
Update-BulkExportObjectList
|
|
})
|
|
$script:UIProvider.AddXamlEvent($script:bulkExportForm, "rbBulkExportViewType", "add_Checked", {
|
|
$script:bulkExportMode = "Type"
|
|
Update-BulkExportObjectList
|
|
})
|
|
|
|
$script:UIProvider.AddXamlEvent($script:bulkExportForm, "btnClose", "add_click", {
|
|
$script:bulkExportForm = $null
|
|
Show-ModalObject
|
|
})
|
|
|
|
# NOTE: the "Save settings for batch job" button was removed 2026-06-12 per
|
|
# user decision — scheduled/CI runs use the public drivers directly
|
|
# (Start-GraphBulkExport -SettingsFile files can still be produced with
|
|
# Save-GraphBulkExportSettings from a script).
|
|
|
|
# btnExport: collect the selected rows from the DataGrid and call the public
|
|
# driver. The UI does NOT contain export logic — it's a thin caller of
|
|
# Start-GraphBulkExport so the same operation is scriptable headlessly.
|
|
$script:UIProvider.AddXamlEvent($script:bulkExportForm, "btnExport", "add_click", {
|
|
$selection = Get-BulkExportSelectedIds
|
|
$unit = if ($script:bulkExportMode -eq "Type") { "policy type" } else { "object group" }
|
|
|
|
if ($selection.Count -eq 0) {
|
|
$script:UIProvider.ShowMessageBox("Select at least one $unit to export.", "Bulk Export", "OK", "Warning") | Out-Null
|
|
return
|
|
}
|
|
|
|
# Read the path straight from the TextBox rather than relying on the binding —
|
|
# the field is bound TwoWay/PropertyChanged so they should agree, but pulling
|
|
# from the control directly is bullet-proof. Trim and unquote (users sometimes
|
|
# paste paths with surrounding quotes).
|
|
$rawPath = ($script:UIProvider.GetXamlProperty($script:bulkExportForm, "txtExportPath", "Text"))
|
|
if ($rawPath) { $rawPath = ([string]$rawPath).Trim().Trim('"').Trim("'") }
|
|
if (-not $rawPath) {
|
|
$script:UIProvider.ShowMessageBox("Select an export folder.", "Bulk Export", "OK", "Warning") | Out-Null
|
|
return
|
|
}
|
|
# Resolve to a full path so the success message shows the canonical location,
|
|
# not whatever relative-ish text the user typed.
|
|
try { $resolvedPath = [IO.Path]::GetFullPath($rawPath) }
|
|
catch {
|
|
$script:UIProvider.ShowMessageBox("Invalid export folder path: $($_.Exception.Message)", "Bulk Export", "OK", "Error") | Out-Null
|
|
return
|
|
}
|
|
$script:bulkExportForm.DataContext.ExportFolder = $resolvedPath
|
|
Write-Log "Bulk export starting. Folder: $resolvedPath"
|
|
|
|
$startParams = @{
|
|
ExportSettings = $script:bulkExportForm.DataContext
|
|
}
|
|
if ($script:bulkExportMode -eq "Type") {
|
|
$startParams.PolicyType = $selection
|
|
}
|
|
else {
|
|
$startParams.PolicyGroup = $selection
|
|
}
|
|
|
|
try {
|
|
$summary = Start-GraphBulkExport @startParams
|
|
Write-Status $null
|
|
$msg = ("Exported {0} policies across {1} types ({2} failed) in {3:hh\:mm\:ss}.`n`nFolder:`n{4}" -f `
|
|
$summary.Policies, $summary.Types, $summary.Failed, $summary.Duration, $resolvedPath)
|
|
$unknownNote = Get-IntuneUnknownSelectorSummary $summary.UnknownSelectors
|
|
if($unknownNote) { $msg += "`n`n$unknownNote" }
|
|
$script:UIProvider.ShowMessageBox($msg, "Bulk Export", "OK", "Information") | Out-Null
|
|
}
|
|
catch {
|
|
Write-LogError "Bulk export failed" $_.Exception
|
|
$script:UIProvider.ShowMessageBox("Bulk export failed: $($_.Exception.Message)", "Bulk Export", "OK", "Error") | Out-Null
|
|
}
|
|
|
|
$script:bulkExportForm = $null
|
|
Show-ModalObject
|
|
})
|
|
|
|
$script:UIProvider.ShowModalForm("Bulk Export", $script:bulkExportForm, $true)
|
|
}
|
|
|
|
# Rebuild the bulk-export DataGrid for the current $script:bulkExportMode.
|
|
# Group mode lists policy groups (Configuration, Compliance, ...); Type mode
|
|
# lists individual policy types (APIs). Both sort by Title so the order is
|
|
# stable instead of class-load order.
|
|
function Update-BulkExportObjectList
|
|
{
|
|
if (-not $script:dgObjectsToExport) { return }
|
|
|
|
$script:exportObjects = @()
|
|
|
|
if ($script:bulkExportMode -eq "Type") {
|
|
$exportableGroupIds = @($script:bulkExportExportableGroups | ForEach-Object { $_.Id })
|
|
$sortedTypes = $script:IntuneTypes |
|
|
Where-Object { $_.PolicyGroup -and ($exportableGroupIds -contains $_.PolicyGroup.Id) } |
|
|
Sort-Object Title
|
|
foreach ($intuneType in $sortedTypes) {
|
|
$script:exportObjects += New-Object PSObject -Property @{
|
|
Title = $intuneType.Title
|
|
Selected = $true
|
|
ObjectGroup = $null
|
|
ObjectType = $intuneType
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
$sortedGroups = $script:bulkExportExportableGroups | Sort-Object Title
|
|
foreach ($intuneGroup in $sortedGroups) {
|
|
$script:exportObjects += New-Object PSObject -Property @{
|
|
Title = $intuneGroup.Title
|
|
Selected = (?? $intuneGroup.BulkExport $true)
|
|
ObjectGroup = $intuneGroup
|
|
ObjectType = $null
|
|
}
|
|
}
|
|
}
|
|
|
|
$script:dgObjectsToExport.ItemsSource = $script:exportObjects
|
|
}
|
|
|
|
# Pull the Id list out of the currently-checked rows. In Group mode that's
|
|
# PolicyGroup IDs (for -PolicyGroup); in Type mode it's PolicyType IDs (for
|
|
# -PolicyType). Returns @() when nothing is selected.
|
|
function Get-BulkExportSelectedIds
|
|
{
|
|
if ($script:bulkExportMode -eq "Type") {
|
|
return @($script:exportObjects |
|
|
Where-Object { $_.Selected -eq $true -and $_.ObjectType -and $_.ObjectType.Id } |
|
|
ForEach-Object { $_.ObjectType.Id })
|
|
}
|
|
return @($script:exportObjects |
|
|
Where-Object { $_.Selected -eq $true -and $_.ObjectGroup -and $_.ObjectGroup.Id } |
|
|
ForEach-Object { $_.ObjectGroup.Id })
|
|
}
|
|
|
|
# ─── Bulk Scope Tags ──────────────────────────────────────────────────────────
|
|
# Thin UI wrapper around Set-GraphBulkScopeTags. The form mirrors Bulk Export's
|
|
# Group / API selector for picking object types, adds an action selector
|
|
# (Add / Replace / Remove), an orphan-cleanup checkbox, and a dual-list scope
|
|
# tag picker. All business logic lives in the public command — the UI only
|
|
# collects inputs, calls it, and shows the summary.
|
|
# Get-BulkScopeTagCatalog moved to Internal/IntuneScopeTags.ps1 so the
|
|
# Avalonia tree can share it.
|
|
|