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

@@ -1069,11 +1069,11 @@ function Connect-MSALUser
{
$headlessAuthMode = ?? $global:HeadlessAuthMode "AppOnly"
if($headlessAuthMode -eq "Browser")
if($headlessAuthMode -eq "Browser" -or $headlessAuthMode -eq "DeviceCode")
{
if(-not $global:AzureAppId -or -not $global:TenantId)
{
Write-Log "Azure AppId and Tenant Id must be specified for browser auth" 3
Write-Log "Azure AppId and Tenant Id must be specified for $headlessAuthMode auth" 3
return
}
@@ -1315,8 +1315,74 @@ function Connect-MSALUser
Write-LogError "Failed to perform silent login" $_.Exception
}
if($global:hideUI -eq $true -and $global:HeadlessAuthMode -eq "DeviceCode" -and ((-not $authResult -and $Silent -ne $true) -or $prompConsent))
{
#########################################################################################################
### Device Code Login
#########################################################################################################
Write-Log "Initiate device code logon"
if($useDefaultPermissions -eq $false)
{
[string[]]$Scopes = Get-MSALRequiredScopes
}
Write-Log "Scopes: $(($Scopes -join ","))"
$msalDllPath = Join-Path (Split-Path -Parent $PSScriptRoot) "Bin/Microsoft.Identity.Client.dll"
$consoleDllPath = [System.Console].Assembly.Location
if (-not ("DeviceCodeHelper" -as [type]))
{
Add-Type -Path $msalDllPath -ErrorAction SilentlyContinue
Add-Type -TypeDefinition @"
using System;
using System.Threading.Tasks;
using Microsoft.Identity.Client;
public static class DeviceCodeHelper
{
public static Task ShowDeviceCode(DeviceCodeResult result)
{
Console.WriteLine("");
Console.WriteLine("To sign in, use a web browser to open the page " + result.VerificationUrl + " and enter the code " + result.UserCode + " to authenticate.");
Console.WriteLine("");
return Task.CompletedTask;
}
}
"@ -ReferencedAssemblies @($msalDllPath, $consoleDllPath)
}
$method = [DeviceCodeHelper].GetMethod("ShowDeviceCode")
$delegateType = [System.Func[Microsoft.Identity.Client.DeviceCodeResult, System.Threading.Tasks.Task]]
$callback = [System.Delegate]::CreateDelegate($delegateType, $method)
$aquireTokenObj = $global:MSALApp.AcquireTokenWithDeviceCode($Scopes, $callback)
if ($tenantId)
{
Write-Log "Tenant id: $tenantId"
[void]$aquireTokenObj.WithAuthority("https://$((Get-MSALAppAuthority))/$tenantId/")
}
else
{
Write-Log "Authority: $($global:MSALApp.Authority)"
[void]$aquireTokenObj.WithAuthority($global:MSALApp.Authority)
}
if($script:authenticationFailure.Claims)
{
Write-Log "Login claims: $($script:authenticationFailure.Claims))"
[void]$AquireTokenObj.WithClaims($script:authenticationFailure.Claims)
}
$authResult = Get-MsalAuthenticationToken $aquireTokenObj
if($authResult)
{
Write-Log "$($authResult.Account.UserName) authenticated successfully (Device Code). CorrelationId: $($authResult.CorrelationId)"
}
}
# Interactive login is only allowed once the app has started. Skip if silent login failed during startup
if($global:MainAppStarted -and ((-not $authResult -and $Silent -ne $true) -or $prompConsent))
if($global:MainAppStarted -and $global:HeadlessAuthMode -ne "DeviceCode" -and ((-not $authResult -and $Silent -ne $true) -or $prompConsent))
{
#########################################################################################################
### Interactive Login