$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 } } }