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
470 lines
13 KiB
PowerShell
470 lines
13 KiB
PowerShell
$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",
|
|
"CompliancePolicies",
|
|
"CompliancePoliciesV2",
|
|
"EndpointSecurity",
|
|
"DeviceManagementIntents",
|
|
"PolicySets"
|
|
)
|
|
}
|
|
|
|
function Get-DefaultBrowserAppId
|
|
{
|
|
"14d82eec-204b-4c2f-b7e8-296a70dab67e"
|
|
}
|
|
|
|
function Get-IntuneManagementProjectRoot
|
|
{
|
|
Split-Path -Parent $PSScriptRoot
|
|
}
|
|
|
|
function Resolve-HeadlessSettingsPath
|
|
{
|
|
param([string]$SettingsFile)
|
|
|
|
if($SettingsFile)
|
|
{
|
|
return $SettingsFile
|
|
}
|
|
|
|
# Default to the persistent data folder (same location used by Initialize-IntuneAuth)
|
|
Join-Path (Get-CloudApiDataFolder) "Settings.json"
|
|
}
|
|
|
|
function New-TemporaryBatchFile
|
|
{
|
|
param([string]$Prefix)
|
|
|
|
Join-Path ([IO.Path]::GetTempPath()) ("IntuneManagement.{0}.{1}.json" -f $Prefix, [guid]::NewGuid().ToString())
|
|
}
|
|
|
|
function Test-AuthParameters
|
|
{
|
|
param(
|
|
[string]$AuthMode,
|
|
[string]$AppId,
|
|
[string]$Secret,
|
|
[string]$Certificate
|
|
)
|
|
|
|
if($AuthMode -eq "Browser" -or $AuthMode -eq "DeviceCode")
|
|
{
|
|
return
|
|
}
|
|
|
|
if(-not $AppId)
|
|
{
|
|
throw "Specify -AppId for AppOnly auth."
|
|
return
|
|
}
|
|
|
|
if((-not $Secret) -and (-not $Certificate))
|
|
{
|
|
throw "Specify -Secret or -Certificate for AppOnly auth, or use -AuthMode Browser."
|
|
}
|
|
}
|
|
|
|
function Invoke-IntuneHeadlessBatch
|
|
{
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$TenantId,
|
|
|
|
[string]$AppId,
|
|
|
|
[string]$Secret,
|
|
|
|
[string]$Certificate,
|
|
|
|
[ValidateSet("AppOnly","Browser","DeviceCode")]
|
|
[string]$AuthMode = "AppOnly",
|
|
|
|
[string]$RedirectUri,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[psobject]$BatchConfig,
|
|
|
|
[string]$SettingsFile,
|
|
|
|
[string]$BatchFile
|
|
)
|
|
|
|
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
|
|
$runtimeModule = Join-Path $projectRoot "Runtime/IntuneManagement.Runtime.psd1"
|
|
|
|
if(-not (Test-Path $runtimeModule))
|
|
{
|
|
throw "Could not find IntuneManagement.Runtime.psd1 in $projectRoot"
|
|
}
|
|
|
|
$deleteBatchFile = $false
|
|
if(-not $BatchFile)
|
|
{
|
|
$BatchFile = New-TemporaryBatchFile "Batch"
|
|
$deleteBatchFile = $true
|
|
}
|
|
|
|
try
|
|
{
|
|
$BatchConfig | ConvertTo-Json -Depth 20 | Out-File -LiteralPath $BatchFile -Encoding utf8 -Force
|
|
|
|
$invokeParams = @{
|
|
Silent = $true
|
|
JSonSettings = $true
|
|
JSonFile = $settingsPath
|
|
TenantId = $TenantId
|
|
AppId = $AppId
|
|
SilentBatchFile = $BatchFile
|
|
AuthMode = $AuthMode
|
|
}
|
|
|
|
if($RedirectUri)
|
|
{
|
|
$invokeParams.RedirectUri = $RedirectUri
|
|
}
|
|
|
|
if($AuthMode -eq "AppOnly" -and $Secret)
|
|
{
|
|
$invokeParams.Secret = $Secret
|
|
}
|
|
elseif($AuthMode -eq "AppOnly")
|
|
{
|
|
$invokeParams.Certificate = $Certificate
|
|
}
|
|
|
|
Import-Module $runtimeModule -Force
|
|
Initialize-IntuneManagementRuntime -View "IntuneGraphAPI" @invokeParams
|
|
}
|
|
finally
|
|
{
|
|
if($deleteBatchFile -and (Test-Path $BatchFile))
|
|
{
|
|
Remove-Item -LiteralPath $BatchFile -Force -ErrorAction SilentlyContinue
|
|
}
|
|
}
|
|
}
|
|
|
|
function Export-IntunePolicies
|
|
{
|
|
[CmdletBinding()]
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$TenantId,
|
|
|
|
[string]$AppId,
|
|
|
|
[string]$Secret,
|
|
|
|
[string]$Certificate,
|
|
|
|
[ValidateSet("AppOnly","Browser","DeviceCode")]
|
|
[string]$AuthMode = "AppOnly",
|
|
|
|
[string]$RedirectUri,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$ExportPath,
|
|
|
|
[string]$SettingsFile,
|
|
|
|
[string]$BatchFile,
|
|
|
|
[string]$NameFilter = "",
|
|
|
|
[string]$NameSearchPattern = "",
|
|
|
|
[string]$NameReplacePattern = "",
|
|
|
|
[string[]]$ObjectTypes = (Get-DefaultIntunePolicyObjectTypes),
|
|
|
|
[switch]$IncludeAssignments,
|
|
|
|
[switch]$AddCompanyName
|
|
)
|
|
|
|
$batchConfig = [PSCustomObject]@{
|
|
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 },
|
|
[PSCustomObject]@{ Name = "ObjectTypes"; Type = "Custom"; ObjectTypes = @($ObjectTypes) }
|
|
)
|
|
}
|
|
|
|
Invoke-IntuneHeadlessBatch `
|
|
-TenantId $TenantId `
|
|
-AppId $AppId `
|
|
-Secret $Secret `
|
|
-Certificate $Certificate `
|
|
-AuthMode $AuthMode `
|
|
-RedirectUri $RedirectUri `
|
|
-BatchConfig $batchConfig `
|
|
-SettingsFile $SettingsFile `
|
|
-BatchFile $BatchFile
|
|
}
|
|
|
|
function Import-IntunePolicies
|
|
{
|
|
[CmdletBinding()]
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$TenantId,
|
|
|
|
[string]$AppId,
|
|
|
|
[string]$Secret,
|
|
|
|
[string]$Certificate,
|
|
|
|
[ValidateSet("AppOnly","Browser","DeviceCode")]
|
|
[string]$AuthMode = "AppOnly",
|
|
|
|
[string]$RedirectUri,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$ImportPath,
|
|
|
|
[string]$SettingsFile,
|
|
|
|
[string]$BatchFile,
|
|
|
|
[string]$NameFilter = "",
|
|
|
|
[string]$NameSearchPattern = "",
|
|
|
|
[string]$NameReplacePattern = "",
|
|
|
|
[ValidateSet("alwaysImport","skipIfExist","replace","replace_with_assignments","update")]
|
|
[string]$ImportType = "alwaysImport",
|
|
|
|
[string[]]$ObjectTypes = (Get-DefaultIntunePolicyObjectTypes),
|
|
|
|
[switch]$IncludeAssignments,
|
|
|
|
[switch]$IncludeScopeTags,
|
|
|
|
[switch]$ReplaceDependencyIds
|
|
)
|
|
|
|
$batchConfig = [PSCustomObject]@{
|
|
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 },
|
|
[PSCustomObject]@{ Name = "chkReplaceDependencyIDs"; Value = $ReplaceDependencyIds.IsPresent },
|
|
[PSCustomObject]@{ Name = "cbImportType"; Value = $ImportType },
|
|
[PSCustomObject]@{ Name = "ObjectTypes"; Type = "Custom"; ObjectTypes = @($ObjectTypes) }
|
|
)
|
|
}
|
|
|
|
Invoke-IntuneHeadlessBatch `
|
|
-TenantId $TenantId `
|
|
-AppId $AppId `
|
|
-Secret $Secret `
|
|
-Certificate $Certificate `
|
|
-AuthMode $AuthMode `
|
|
-RedirectUri $RedirectUri `
|
|
-BatchConfig $batchConfig `
|
|
-SettingsFile $SettingsFile `
|
|
-BatchFile $BatchFile
|
|
}
|
|
|
|
function Invoke-IntunePolicyAction
|
|
{
|
|
[CmdletBinding(DefaultParameterSetName = 'Export')]
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[ValidateSet("Export","Import")]
|
|
[string]$Action,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$TenantId,
|
|
|
|
[string]$AppId,
|
|
|
|
[string]$Secret,
|
|
|
|
[string]$Certificate,
|
|
|
|
[ValidateSet("AppOnly","Browser","DeviceCode")]
|
|
[string]$AuthMode = "AppOnly",
|
|
|
|
[string]$RedirectUri,
|
|
|
|
[string]$SettingsFile,
|
|
|
|
[string]$BatchFile,
|
|
|
|
[string]$NameFilter = "",
|
|
|
|
[string]$NameSearchPattern = "",
|
|
|
|
[string]$NameReplacePattern = "",
|
|
|
|
[string[]]$ObjectTypes = (Get-DefaultIntunePolicyObjectTypes),
|
|
|
|
[string]$ExportPath,
|
|
|
|
[string]$ImportPath,
|
|
|
|
[ValidateSet("alwaysImport","skipIfExist","replace","replace_with_assignments","update")]
|
|
[string]$ImportType = "alwaysImport",
|
|
|
|
[switch]$IncludeAssignments,
|
|
|
|
[switch]$AddCompanyName,
|
|
|
|
[switch]$IncludeScopeTags,
|
|
|
|
[switch]$ReplaceDependencyIds
|
|
)
|
|
|
|
switch($Action)
|
|
{
|
|
"Export"
|
|
{
|
|
if(-not $ExportPath) { throw "Export requires -ExportPath." }
|
|
Export-IntunePolicies `
|
|
-TenantId $TenantId `
|
|
-AppId $AppId `
|
|
-Secret $Secret `
|
|
-Certificate $Certificate `
|
|
-AuthMode $AuthMode `
|
|
-RedirectUri $RedirectUri `
|
|
-ExportPath $ExportPath `
|
|
-SettingsFile $SettingsFile `
|
|
-BatchFile $BatchFile `
|
|
-NameFilter $NameFilter `
|
|
-NameSearchPattern $NameSearchPattern `
|
|
-NameReplacePattern $NameReplacePattern `
|
|
-ObjectTypes $ObjectTypes `
|
|
-IncludeAssignments:$IncludeAssignments `
|
|
-AddCompanyName:$AddCompanyName
|
|
}
|
|
"Import"
|
|
{
|
|
if(-not $ImportPath) { throw "Import requires -ImportPath." }
|
|
Import-IntunePolicies `
|
|
-TenantId $TenantId `
|
|
-AppId $AppId `
|
|
-Secret $Secret `
|
|
-Certificate $Certificate `
|
|
-AuthMode $AuthMode `
|
|
-RedirectUri $RedirectUri `
|
|
-ImportPath $ImportPath `
|
|
-SettingsFile $SettingsFile `
|
|
-BatchFile $BatchFile `
|
|
-NameFilter $NameFilter `
|
|
-NameSearchPattern $NameSearchPattern `
|
|
-NameReplacePattern $NameReplacePattern `
|
|
-ImportType $ImportType `
|
|
-ObjectTypes $ObjectTypes `
|
|
-IncludeAssignments:$IncludeAssignments `
|
|
-IncludeScopeTags:$IncludeScopeTags `
|
|
-ReplaceDependencyIds:$ReplaceDependencyIds
|
|
}
|
|
}
|
|
}
|