IntuneManagement 4.0.0-beta1

This commit is contained in:
Mikael Karlsson
2026-09-23 19:13:09 +10:00
commit 7869619510
892 changed files with 577109 additions and 0 deletions
+415
View File
@@ -0,0 +1,415 @@
function Connect-IntuneManagement {
<#
.SYNOPSIS
Authenticate to Microsoft Graph for use with IntuneManagement.
.DESCRIPTION
Supports four non-interactive authentication methods:
- App registration with a client secret
- App registration with a certificate (thumbprint, X509Certificate2 object, or .pfx file)
- Bring-your-own token (pass a raw Bearer token string)
- Managed Identity (Azure VM / Azure Function workload identity)
The auth backend is selected via -Provider:
- MSAL (default; uses the bundled MSAL.NET DLLs)
- MgGraph (uses the Microsoft.Graph.Authentication PowerShell module; the
module must be installed: Install-Module Microsoft.Graph.Authentication)
If -Provider is omitted, the value of the "ActiveAuthProvider" setting is
used (default MSAL).
.EXAMPLE
# Client secret with the default provider (MSAL)
Connect-IntuneManagement -TenantId "contoso.onmicrosoft.com" -AppId "00000000-..." -Secret "abc123"
.EXAMPLE
# Client secret via the Microsoft.Graph SDK provider
Connect-IntuneManagement -Provider MgGraph -TenantId "contoso.onmicrosoft.com" -AppId "00000000-..." -Secret "abc123"
.EXAMPLE
# Certificate thumbprint (looked up in Cert:\CurrentUser\My then Cert:\LocalMachine\My)
Connect-IntuneManagement -TenantId "contoso.onmicrosoft.com" -AppId "00000000-..." -Certificate "A1B2C3..."
.EXAMPLE
# Certificate from .pfx file
Connect-IntuneManagement -TenantId "contoso.onmicrosoft.com" -AppId "00000000-..." `
-CertificatePath "C:\certs\app.pfx" -CertificatePassword (ConvertTo-SecureString "pass" -AsPlainText -Force)
.EXAMPLE
# Bring your own Graph Bearer token
Connect-IntuneManagement -Token $myToken
.EXAMPLE
# System-assigned managed identity (Azure VM / Azure Function)
Connect-IntuneManagement -Provider MgGraph -ManagedIdentity
.EXAMPLE
# User-assigned managed identity
Connect-IntuneManagement -Provider MgGraph -ManagedIdentity -AppId "00000000-..."
.EXAMPLE
# Direct-OAuth provider (no SDK, no DLL): client secret
Connect-IntuneManagement -Provider OAuth -TenantId "contoso.onmicrosoft.com" -AppId "00000000-..." -Secret "abc"
.EXAMPLE
# Direct-OAuth provider: workload identity federation (AKS / GitHub Actions OIDC)
Connect-IntuneManagement -Provider OAuth -TenantId "..." -AppId "..." `
-FederatedTokenFile $env:AZURE_FEDERATED_TOKEN_FILE
.EXAMPLE
# Direct-OAuth provider: PSCredential (ROPC; non-MFA accounts only)
Connect-IntuneManagement -Provider OAuth -TenantId "..." -AppId "..." -Credential (Get-Credential)
.EXAMPLE
# Device code sign-in on the default provider (MSAL). MFA / FIDO2 /
# YubiKey capable; the browser auth happens on any other device.
Connect-IntuneManagement -DeviceCode
.EXAMPLE
# Device code sign-in on the Direct-OAuth provider. Uses the app id
# selected in Settings -> Entra, unless -AppId is supplied.
Connect-IntuneManagement -Provider OAuth -DeviceCode
.EXAMPLE
# Device code sign-in on the Microsoft.Graph SDK provider
Connect-IntuneManagement -Provider MgGraph -DeviceCode
.EXAMPLE
# Interactive sign-in (browser popup / WAM broker on the default MSAL
# provider). Silent-from-cache first, falls back to browser prompt.
Connect-IntuneManagement -Interactive
.EXAMPLE
# Interactive against a specific tenant, force a fresh browser prompt
Connect-IntuneManagement -Interactive -TenantId "contoso.onmicrosoft.com" -ForceInteractive
.EXAMPLE
# Interactive against a sovereign cloud
Connect-IntuneManagement -Interactive -Cloud USGov
.EXAMPLE
# Interactive against the Direct-OAuth provider — no browser popup path
# exists in that provider, so this transparently routes to device code
# (headless-friendly, MFA/FIDO2 capable). Uses the app id selected in
# Settings -> Entra, unless -AppId is supplied.
Connect-IntuneManagement -Provider OAuth -Interactive
#>
[CmdletBinding(DefaultParameterSetName = 'Interactive')]
[OutputType([PSCustomObject])]
param(
[Parameter(Mandatory = $true, ParameterSetName = 'Secret')]
[Parameter(Mandatory = $true, ParameterSetName = 'Certificate')]
[Parameter(Mandatory = $true, ParameterSetName = 'CertificatePath')]
[Parameter(Mandatory = $true, ParameterSetName = 'OAuthFederated')]
[Parameter(Mandatory = $true, ParameterSetName = 'OAuthCredential')]
[Parameter(Mandatory = $false, ParameterSetName = 'DeviceCode')]
[Parameter(Mandatory = $false, ParameterSetName = 'Interactive')]
[Parameter(Mandatory = $false, ParameterSetName = 'Token')]
[Parameter(Mandatory = $false, ParameterSetName = 'ManagedIdentity')]
[string]$TenantId,
[Parameter(Mandatory = $true, ParameterSetName = 'Secret')]
[Parameter(Mandatory = $true, ParameterSetName = 'Certificate')]
[Parameter(Mandatory = $true, ParameterSetName = 'CertificatePath')]
[Parameter(Mandatory = $true, ParameterSetName = 'OAuthFederated')]
[Parameter(Mandatory = $true, ParameterSetName = 'OAuthCredential')]
[Parameter(Mandatory = $false, ParameterSetName = 'DeviceCode')]
[Parameter(Mandatory = $false, ParameterSetName = 'Interactive')]
[Parameter(Mandatory = $false, ParameterSetName = 'ManagedIdentity')]
[string]$AppId,
[Parameter(Mandatory = $true, ParameterSetName = 'Secret')]
[string]$Secret,
# Thumbprint string or X509Certificate2 object
[Parameter(Mandatory = $true, ParameterSetName = 'Certificate')]
$Certificate,
[Parameter(Mandatory = $true, ParameterSetName = 'CertificatePath')]
[string]$CertificatePath,
[Parameter(Mandatory = $false, ParameterSetName = 'CertificatePath')]
[SecureString]$CertificatePassword,
[Parameter(Mandatory = $true, ParameterSetName = 'Token')]
[string]$Token,
# System-assigned (no -AppId) or user-assigned (with -AppId) managed identity.
# MgGraph and OAuth providers support this; MSAL rejects with a clear error.
[Parameter(Mandatory = $true, ParameterSetName = 'ManagedIdentity')]
[switch]$ManagedIdentity,
# OAuth provider only — workload identity federation. Path to a file
# containing an OIDC JWT to exchange at the /token endpoint as
# client_assertion (jwt-bearer). Used by AKS Workload Identity, GitHub
# Actions OIDC, Azure DevOps OIDC, etc.
[Parameter(Mandatory = $true, ParameterSetName = 'OAuthFederated')]
[string]$FederatedTokenFile,
# OAuth provider only — pass the federated assertion inline (alternative
# to -FederatedTokenFile when the caller already has the JWT in memory).
[Parameter(Mandatory = $false, ParameterSetName = 'OAuthFederated')]
[string]$FederatedToken,
# OAuth provider only — username/password (ROPC) via PSCredential. Limited
# to non-MFA accounts; intended for legacy automation. Use a managed
# identity / federated credential / cert / secret in preference.
[Parameter(Mandatory = $true, ParameterSetName = 'OAuthCredential')]
[PSCredential]$Credential,
# Device code sign-in (RFC 8628). Prints a code + verification URL;
# the user completes auth (MFA / FIDO2 / YubiKey all work) in a browser
# on any device while this call polls for the token. Supported by every
# provider: MSAL uses MSAL.NET's AcquireTokenWithDeviceCode (token lands
# in the MSAL cache and refreshes silently); MgGraph uses Connect-MgGraph
# -UseDeviceCode; OAuth speaks the RFC 8628 flow directly. TenantId is
# optional on MSAL/OAuth ('organizations' / 'common' as appropriate).
[Parameter(Mandatory = $true, ParameterSetName = 'DeviceCode')]
[switch]$DeviceCode,
# Interactive sign-in — browser popup / WAM broker (MSAL) or device code
# (OAuth). Silent-from-cache first, falls back to interactive prompt
# when the cache is cold. Default parameter set — you can call
# `Connect-IntuneManagement` with no args and get an interactive prompt.
[Parameter(Mandatory = $false, ParameterSetName = 'Interactive')]
[switch]$Interactive,
# Interactive-only: pin the account to sign in with (MSAL only). Same
# semantics as passing $global:MSALLoginHint.
[Parameter(Mandatory = $false, ParameterSetName = 'Interactive')]
[string]$User,
# Interactive-only: bypass the token cache and force a fresh browser
# prompt even when a cached token exists (MSAL only).
[Parameter(Mandatory = $false, ParameterSetName = 'Interactive')]
[switch]$ForceInteractive,
# Interactive-only: use the Windows broker (WAM) instead of a browser
# popup. Requires PS7+ on Windows (MSAL only).
[Parameter(Mandatory = $false, ParameterSetName = 'Interactive')]
[switch]$AuthenticationBroker,
# Interactive-only: force the OAuth provider's browser (Authorization Code +
# PKCE, loopback redirect) flow explicitly, even headless. Without this,
# -Interactive uses the browser only when a GUI is present, else device code.
# (OAuth only; ignored by MSAL/MgGraph.)
[Parameter(Mandatory = $false, ParameterSetName = 'Interactive')]
[switch]$Browser,
# New flat Cloud taxonomy (Phase 1 of the cloud redesign, 2026-05-22). Replaces
# -GraphEnvironment + -GCCType. If omitted, falls back to the "DefaultCloud"
# setting (defaults to Public). The legacy pair is still accepted for one
# release with a deprecation warning — see resolution block below.
[Parameter(Mandatory = $false)]
[ValidateSet("Public", "USGov", "USGovDOD", "China")]
[string]$Cloud,
[Parameter(Mandatory = $false)]
[ValidateSet("public", "usGov", "china")]
[string]$GraphEnvironment = "public",
[Parameter(Mandatory = $false)]
[AllowNull()]
[ValidateSet("", "gcc", "gccHigh", "gccDoD")]
[string]$GCCType,
[switch]$DefaultToken,
# Pick the auth backend. If omitted, uses the value of the "ActiveAuthProvider"
# setting (default MSAL). The named provider must be registered (MgGraph requires
# Microsoft.Graph.Authentication installed). Valid values are enumerated
# dynamically from every registered AuthenticationProvider — adding a new
# provider via Register-AuthProvider is sufficient, no edit here needed.
[Parameter(Mandatory = $false)]
# Completion (not [ValidateSet([AuthProviderValues])]) so the module still
# imports on Windows PowerShell 5.1 - the generator implements the PS7-only
# IValidateSetValuesGenerator. Unknown providers are handled at runtime below.
[ArgumentCompleter({ param($commandName, $parameterName, $wordToComplete) @(& (Get-Module IntuneManagement) { Get-AuthProviderValues }) | Where-Object { $_ -like "$wordToComplete*" } })]
[string]$Provider
)
# Resolve the provider. If -Provider is explicitly set, use it; otherwise use the
# currently active provider from AuthenticationCore. Parameter sets that ONLY make
# sense for the OAuth provider (workload-identity federation, ROPC PSCredential)
# auto-route to OAuth — saves the caller from having to also pass -Provider OAuth.
# DeviceCode used to be OAuth-only ('OAuthDeviceCode'); every provider now
# supports it, so the auto-route no longer needs to force -Provider OAuth
# for that case. Federated / ROPC (Credential) remain OAuth-exclusive.
$oauthOnlySets = @('OAuthFederated','OAuthCredential')
if(-not $Provider -and $PSCmdlet.ParameterSetName -in $oauthOnlySets) {
$Provider = "OAuth"
Write-LogDebug "Connect-IntuneManagement auto-selected -Provider OAuth (parameter set: $($PSCmdlet.ParameterSetName))"
}
if($Provider) {
$authProvider = Get-AuthProvider -Id $Provider
if(-not $authProvider) {
Write-Log "Provider '$Provider' is not registered. For MgGraph, install Microsoft.Graph.Authentication." 3
return
}
}
else {
$authProvider = Get-AuthProvider
if(-not $authProvider) {
Write-Log "No authentication provider is active. Cannot continue." 3
return
}
}
Write-LogDebug "Connect-IntuneManagement routing to provider '$($authProvider.Id)' (parameter set: $($PSCmdlet.ParameterSetName))"
# Resolve the target cloud once, here, so every downstream branch sees a consistent
# answer. Precedence: -Cloud wins; explicit -GraphEnvironment/-GCCType is the legacy
# path (deprecated, warns); otherwise read the DefaultCloud setting. Also derive the
# legacy GraphEnvironment/GCCType pair from the resolved Cloud so MSAL functions that
# still take the old form (Phase 4 will migrate them) keep working.
if($PSBoundParameters.ContainsKey('Cloud')) {
$resolvedCloud = $Cloud
}
elseif($PSBoundParameters.ContainsKey('GraphEnvironment') -or $PSBoundParameters.ContainsKey('GCCType')) {
Write-Log "-GraphEnvironment and -GCCType are deprecated; use -Cloud (Public/USGov/USGovDOD/China) instead. They will be removed in a future release." 2
$resolvedCloud = Convert-LegacyToCloud -GraphEnvironment $GraphEnvironment -GCCType $GCCType
}
else {
$resolvedCloud = Get-DefaultCloud
}
$cloudEntry = Get-CloudByValue $resolvedCloud
$GraphEnvironment = $cloudEntry.LegacyEnv
$GCCType = if([string]::IsNullOrWhiteSpace($cloudEntry.LegacyGCC)) { $null } else { $cloudEntry.LegacyGCC }
Write-LogDebug "Connect-IntuneManagement resolved Cloud=$resolvedCloud (legacy GraphEnvironment='$GraphEnvironment', GCCType='$GCCType')"
# Path A - providers wired into the built-in Connect-* entry points
# (UsesBuiltInConnectPath, i.e. MSAL) are driven through them directly. Their
# Connect() ALSO forwards to these functions, but skipping the extra hop keeps
# stack traces clean and behaviour identical to pre-refactor.
if($authProvider.UsesBuiltInConnectPath) {
$sharedParams = @{
GraphEnvironment = $GraphEnvironment
GCCType = $GCCType
DefaultToken = $DefaultToken
}
switch ($PSCmdlet.ParameterSetName) {
'Secret' {
return (Connect-WithClientCredentials -TenantId $TenantId -AppId $AppId -Secret $Secret @sharedParams)
}
'Certificate' {
$cert = Resolve-MSALCertificate $Certificate
if (-not $cert) {
Write-Log "Cannot resolve certificate '$Certificate'. Provide a valid thumbprint or X509Certificate2 object." 3
return
}
return (Connect-WithClientCredentials -TenantId $TenantId -AppId $AppId -Certificate $cert @sharedParams)
}
'CertificatePath' {
$cert = Resolve-MSALCertificate -CertificatePath $CertificatePath -Password $CertificatePassword
if (-not $cert) {
Write-Log "Cannot load certificate from '$CertificatePath'." 3
return
}
return (Connect-WithClientCredentials -TenantId $TenantId -AppId $AppId -Certificate $cert @sharedParams)
}
'Token' {
return (Add-BYOTokenInfo -Token $Token -TenantId $TenantId @sharedParams)
}
'ManagedIdentity' {
Write-Log "MSAL provider does not support -ManagedIdentity. Use -Provider MgGraph." 3
return
}
'DeviceCode' {
# Full MSAL device-code flow via Connect-EntraEnvironment's
# -DeviceCode switch (uses MSAL.NET's AcquireTokenWithDeviceCode
# under the hood). Token lands in the MSAL cache so subsequent
# silent refreshes work identically to interactive sign-in.
# Splat matches Connect-EntraEnvironment's parameter surface —
# GraphEnvironment/GCCType are NOT its parameters (used by
# the client-credentials helpers), so -Cloud carries the cloud.
$dcArgs = @{
DefaultToken = $DefaultToken
DeviceCode = $true
Cloud = $resolvedCloud
}
if($TenantId) { $dcArgs['TenantId'] = $TenantId }
if($AppId) { $dcArgs['AppId'] = $AppId }
return (Connect-EntraEnvironment @dcArgs)
}
'Interactive' {
# Interactive MSAL flow — browser popup, or WAM broker when
# -AuthenticationBroker is set. Delegates to Connect-EntraEnvironment
# which owns the MSAL public-client PCA plumbing. Splat only the
# keys Connect-EntraEnvironment declares; -Cloud drives the
# sovereign-cloud selection there (the legacy Environment param
# is derived from Cloud downstream). GCCType is not a
# Connect-EntraEnvironment parameter (it's used by the
# client-credentials helpers only).
$iArgs = @{
DefaultToken = $DefaultToken
ForceInteractive = $ForceInteractive
AuthenticationBroker = $AuthenticationBroker
Cloud = $resolvedCloud
}
if($TenantId) { $iArgs['TenantId'] = $TenantId }
if($AppId) { $iArgs['AppId'] = $AppId }
if($User) { $iArgs['User'] = $User }
return (Connect-EntraEnvironment @iArgs)
}
}
}
# Path B — provider-aware path. Pack the parameters into a hashtable and let the
# provider class translate. This is the route for MgGraph, OAuth, and any future
# provider.
$providerArgs = @{}
if($TenantId) { $providerArgs['TenantId'] = $TenantId }
if($AppId) { $providerArgs['AppId'] = $AppId }
if($Secret) { $providerArgs['Secret'] = $Secret }
if($Certificate) { $providerArgs['Certificate'] = $Certificate }
if($CertificatePath) { $providerArgs['CertificatePath'] = $CertificatePath }
if($CertificatePassword) { $providerArgs['CertificatePassword'] = $CertificatePassword }
if($Token) { $providerArgs['Token'] = $Token }
if($ManagedIdentity) { $providerArgs['ManagedIdentity'] = $true }
if($FederatedTokenFile) { $providerArgs['FederatedTokenFile'] = $FederatedTokenFile }
if($FederatedToken) { $providerArgs['FederatedToken'] = $FederatedToken }
if($Credential) { $providerArgs['Credential'] = $Credential }
if($DeviceCode) { $providerArgs['DeviceCode'] = $true }
if($Interactive -or $PSCmdlet.ParameterSetName -eq 'Interactive') {
$providerArgs['Interactive'] = $true
}
if($User) { $providerArgs['User'] = $User }
if($ForceInteractive) { $providerArgs['ForceInteractive'] = $true }
if($AuthenticationBroker){ $providerArgs['AuthenticationBroker'] = $true }
if($Browser) { $providerArgs['Browser'] = $true }
if($DeviceCode) { $providerArgs['DeviceCode'] = $true }
$providerArgs['Cloud'] = $resolvedCloud
$providerArgs['GraphEnvironment'] = $GraphEnvironment
$providerArgs['GCCType'] = $GCCType
$providerArgs['DefaultToken'] = $DefaultToken.IsPresent
$result = $authProvider.Connect($providerArgs)
# MgGraph fallback: if the non-default provider failed (user declined the SDK
# install, or install failed), fall back to MSAL so the user is still signed in.
# ManagedIdentity / OAuth-only parameter sets have no MSAL equivalent —
# don't fall back for them.
$noFallbackSets = @('ManagedIdentity','OAuthFederated','OAuthCredential','DeviceCode','Interactive')
if(-not $result -and $authProvider.Id -ne "MSAL" -and $PSCmdlet.ParameterSetName -notin $noFallbackSets) {
$msal = Get-AuthProvider -Id "MSAL"
if($msal) {
Write-Log "Provider '$($authProvider.Id)' failed to connect - falling back to MSAL"
# Strip provider-specific fields before retrying.
$providerArgs.Remove('ManagedIdentity') | Out-Null
$providerArgs.Remove('FederatedTokenFile') | Out-Null
$providerArgs.Remove('FederatedToken') | Out-Null
$providerArgs.Remove('Credential') | Out-Null
$result = $msal.Connect($providerArgs)
if($result) {
# Make MSAL the active provider for the rest of the session — otherwise
# subsequent Invoke-MSGraphAPI calls would still target the failed
# provider.
Set-ActiveAuthProvider -Id "MSAL"
}
}
}
return $result
}