feat(toolkit): complete macOS Intune Toolkit v1

Core enhancements:
- Expanded default export/import scope to ~45 object types including DeviceManagementIntents
- Added -AllPages pagination support across Graph queries for large tenants
- Invoke-GraphRequest now throws on 4xx/5xx instead of silently returning null
- Added macOS Keychain fallback for secret retrieval in headless auth flow
- Added NameSearchPattern/NameReplacePattern mutation support through export/import forms

New toolkit scripts:
- Bulk-AppAssignment.ps1: bulk-assign apps to groups/All Users/All Devices
- Bulk-AssignmentManager.ps1: add/remove assignments for any policy type with correct @odata.type
- Backup-Restore-Assignments.ps1: JSON backup with cross-tenant group resolution
- Export-AssignmentsToCsv.ps1: CSV/Markdown documentation output
- Bulk-RenamePolicies.ps1: regex search/replace and prefix mutations
- Bulk-DeviceOperations.ps1: delete/retire/wipe/lock/sync with -WhatIf safeguards
- Start-IntuneManagementTui.ps1: interactive terminal UI for headless operations
- Create-IntuneManagementApp.ps1: helper for app registration setup

Updated existing scripts:
- Export-Policies.ps1 / Import-Policies.ps1: wired mutation params through
- Start-HeadlessIntune.ps1: integrated TUI and new parameter forwarding
This commit is contained in:
2026-04-14 15:11:09 +02:00
parent 0ddd21ab14
commit e13d14edcb
18 changed files with 3649 additions and 69 deletions

View File

@@ -1,11 +1,57 @@
$script:coreModulePath = Join-Path (Split-Path -Parent $PSScriptRoot) "Core.psm1"
if (Test-Path $script:coreModulePath)
{
Import-Module $script:coreModulePath -Force
}
function Get-DefaultIntunePolicyObjectTypes
{
@(
"ScopeTags",
"AssignmentFilters",
"ReusableSettings",
"RoleDefinitions",
"Notifications",
"DeviceHealthScripts",
"ComplianceScripts",
"PowerShellScripts",
"MacScripts",
"MacCustomAttributes",
"ADMXFiles",
"IntuneBranding",
"AzureBranding",
"TermsAndConditions",
"TermsOfUse",
"EnrollmentStatusPage",
"EnrollmentRestrictions",
"AppleEnrollmentTypes",
"AutoPilot",
"AndroidOEMConfig",
"DeviceCategories",
"AuthenticationStrengths",
"AuthenticationContext",
"NamedLocations",
"ConditionalAccess",
"CoManagementSettings",
"Applications",
"AppProtection",
"AppConfigurationManagedApp",
"AppConfigurationManagedDevice",
"UpdatePolicies",
"FeatureUpdates",
"QualityUpdates",
"DriverUpdateProfiles",
"HardwareConfigurations",
"InventoryPolicies",
"W365ProvisioningPolicies",
"W365UserSettings",
"AdministrativeTemplates",
"DeviceConfiguration",
"SettingsCatalog",
"AdministrativeTemplates",
"CompliancePolicies",
"CompliancePoliciesV2",
"EndpointSecurity",
"DeviceManagementIntents",
"PolicySets"
)
}
@@ -29,7 +75,8 @@ function Resolve-HeadlessSettingsPath
return $SettingsFile
}
Join-Path ([IO.Path]::GetTempPath()) "IntuneManagement.Settings.json"
# Default to the persistent data folder (same location used by Initialize-IntuneAuth)
Join-Path (Get-CloudApiDataFolder) "Settings.json"
}
function New-TemporaryBatchFile
@@ -48,7 +95,7 @@ function Test-AuthParameters
[string]$Certificate
)
if($AuthMode -eq "Browser")
if($AuthMode -eq "Browser" -or $AuthMode -eq "DeviceCode")
{
return
}
@@ -77,7 +124,7 @@ function Invoke-IntuneHeadlessBatch
[string]$Certificate,
[ValidateSet("AppOnly","Browser")]
[ValidateSet("AppOnly","Browser","DeviceCode")]
[string]$AuthMode = "AppOnly",
[string]$RedirectUri,
@@ -90,11 +137,50 @@ function Invoke-IntuneHeadlessBatch
[string]$BatchFile
)
if($AuthMode -eq "Browser" -and -not $AppId)
if(($AuthMode -eq "Browser" -or $AuthMode -eq "DeviceCode") -and -not $AppId)
{
$AppId = Get-DefaultBrowserAppId
}
# Pre-load settings to fill missing AppId/Secret before auth validation
$settingsPath = Resolve-HeadlessSettingsPath $SettingsFile
if($AuthMode -eq "AppOnly" -and (Test-Path $settingsPath) -and (-not $AppId -or -not $Secret -and -not $Certificate))
{
try
{
$raw = Get-Content -Path $settingsPath -Raw -ErrorAction Stop
$settingsObj = ConvertFrom-Json $raw -AsHashtable -ErrorAction Stop
if($settingsObj -and $settingsObj.ContainsKey($TenantId))
{
$tenantNode = $settingsObj[$TenantId]
if(-not $AppId -and $tenantNode.ContainsKey("GraphAzureAppId"))
{
$AppId = $tenantNode["GraphAzureAppId"]
}
if(-not $Secret -and $tenantNode.ContainsKey("GraphAzureAppSecret"))
{
$Secret = $tenantNode["GraphAzureAppSecret"]
}
if(-not $Certificate -and $tenantNode.ContainsKey("GraphAzureAppCert"))
{
$Certificate = $tenantNode["GraphAzureAppCert"]
}
}
# macOS Keychain fallback for secret
if(-not $Secret -and $IsMacOS -and $AppId)
{
try
{
$keychainSecret = security find-generic-password -a "IntuneManagement" -s "IntuneMgmt-$AppId" -w 2>$null
if($keychainSecret) { $Secret = $keychainSecret }
}
catch { }
}
}
catch { }
}
Test-AuthParameters -AuthMode $AuthMode -AppId $AppId -Secret $Secret -Certificate $Certificate
$projectRoot = Get-IntuneManagementProjectRoot
@@ -105,8 +191,6 @@ function Invoke-IntuneHeadlessBatch
throw "Could not find IntuneManagement.Runtime.psd1 in $projectRoot"
}
$settingsPath = Resolve-HeadlessSettingsPath $SettingsFile
$deleteBatchFile = $false
if(-not $BatchFile)
{
@@ -167,7 +251,7 @@ function Export-IntunePolicies
[string]$Certificate,
[ValidateSet("AppOnly","Browser")]
[ValidateSet("AppOnly","Browser","DeviceCode")]
[string]$AuthMode = "AppOnly",
[string]$RedirectUri,
@@ -181,6 +265,10 @@ function Export-IntunePolicies
[string]$NameFilter = "",
[string]$NameSearchPattern = "",
[string]$NameReplacePattern = "",
[string[]]$ObjectTypes = (Get-DefaultIntunePolicyObjectTypes),
[switch]$IncludeAssignments,
@@ -192,6 +280,8 @@ function Export-IntunePolicies
BulkExport = @(
[PSCustomObject]@{ Name = "txtExportPath"; Value = $ExportPath },
[PSCustomObject]@{ Name = "txtExportNameFilter"; Value = $NameFilter },
[PSCustomObject]@{ Name = "txtExportNameSearchPattern"; Value = $NameSearchPattern },
[PSCustomObject]@{ Name = "txtExportNameReplacePattern"; Value = $NameReplacePattern },
[PSCustomObject]@{ Name = "chkAddObjectType"; Value = $true },
[PSCustomObject]@{ Name = "chkExportAssignments"; Value = $IncludeAssignments.IsPresent },
[PSCustomObject]@{ Name = "chkAddCompanyName"; Value = $AddCompanyName.IsPresent },
@@ -224,7 +314,7 @@ function Import-IntunePolicies
[string]$Certificate,
[ValidateSet("AppOnly","Browser")]
[ValidateSet("AppOnly","Browser","DeviceCode")]
[string]$AuthMode = "AppOnly",
[string]$RedirectUri,
@@ -238,6 +328,10 @@ function Import-IntunePolicies
[string]$NameFilter = "",
[string]$NameSearchPattern = "",
[string]$NameReplacePattern = "",
[ValidateSet("alwaysImport","skipIfExist","replace","replace_with_assignments","update")]
[string]$ImportType = "alwaysImport",
@@ -254,6 +348,8 @@ function Import-IntunePolicies
BulkImport = @(
[PSCustomObject]@{ Name = "txtImportPath"; Value = $ImportPath },
[PSCustomObject]@{ Name = "txtImportNameFilter"; Value = $NameFilter },
[PSCustomObject]@{ Name = "txtImportNameSearchPattern"; Value = $NameSearchPattern },
[PSCustomObject]@{ Name = "txtImportNameReplacePattern"; Value = $NameReplacePattern },
[PSCustomObject]@{ Name = "chkAddObjectType"; Value = $true },
[PSCustomObject]@{ Name = "chkImportScopes"; Value = $IncludeScopeTags.IsPresent },
[PSCustomObject]@{ Name = "chkImportAssignments"; Value = $IncludeAssignments.IsPresent },
@@ -292,7 +388,7 @@ function Invoke-IntunePolicyAction
[string]$Certificate,
[ValidateSet("AppOnly","Browser")]
[ValidateSet("AppOnly","Browser","DeviceCode")]
[string]$AuthMode = "AppOnly",
[string]$RedirectUri,
@@ -303,6 +399,10 @@ function Invoke-IntunePolicyAction
[string]$NameFilter = "",
[string]$NameSearchPattern = "",
[string]$NameReplacePattern = "",
[string[]]$ObjectTypes = (Get-DefaultIntunePolicyObjectTypes),
[string]$ExportPath,
@@ -337,6 +437,8 @@ function Invoke-IntunePolicyAction
-SettingsFile $SettingsFile `
-BatchFile $BatchFile `
-NameFilter $NameFilter `
-NameSearchPattern $NameSearchPattern `
-NameReplacePattern $NameReplacePattern `
-ObjectTypes $ObjectTypes `
-IncludeAssignments:$IncludeAssignments `
-AddCompanyName:$AddCompanyName
@@ -355,6 +457,8 @@ function Invoke-IntunePolicyAction
-SettingsFile $SettingsFile `
-BatchFile $BatchFile `
-NameFilter $NameFilter `
-NameSearchPattern $NameSearchPattern `
-NameReplacePattern $NameReplacePattern `
-ImportType $ImportType `
-ObjectTypes $ObjectTypes `
-IncludeAssignments:$IncludeAssignments `