Add headless macOS CLI workflow

This commit is contained in:
2026-04-08 15:18:32 +02:00
parent faffa95d8a
commit 8fe71c0078
12 changed files with 917 additions and 66 deletions

View File

@@ -1,6 +1,21 @@
#region Console functions #region Console functions
function Test-IsWindowsPlatform
{
return ([Environment]::OSVersion.Platform -eq [System.PlatformID]::Win32NT)
}
function Invoke-AppDoEvents
{
if("System.Windows.Forms.Application" -as [type])
{
[System.Windows.Forms.Application]::DoEvents()
}
}
# https://stackoverflow.com/questions/40617800/opening-powershell-script-and-hide-command-prompt-but-not-the-gui # https://stackoverflow.com/questions/40617800/opening-powershell-script-and-hide-command-prompt-but-not-the-gui
if(Test-IsWindowsPlatform)
{
Add-Type -Name Window -Namespace Console -MemberDefinition ' Add-Type -Name Window -Namespace Console -MemberDefinition '
[DllImport("Kernel32.dll")] [DllImport("Kernel32.dll")]
public static extern IntPtr GetConsoleWindow(); public static extern IntPtr GetConsoleWindow();
@@ -17,9 +32,11 @@ public static extern bool SetConsoleIcon(IntPtr hIcon);
[DllImport("user32.dll")] [DllImport("user32.dll")]
public static extern int SendMessage(int hWnd, uint wMsg, uint wParam, IntPtr lParam); public static extern int SendMessage(int hWnd, uint wMsg, uint wParam, IntPtr lParam);
' '
}
function Show-Console function Show-Console
{ {
if(-not (Test-IsWindowsPlatform)) { return }
$consolePtr = [Console.Window]::GetConsoleWindow() $consolePtr = [Console.Window]::GetConsoleWindow()
# Hide = 0, # Hide = 0,
@@ -41,6 +58,7 @@ function Show-Console
function Hide-Console function Hide-Console
{ {
if(-not (Test-IsWindowsPlatform)) { return }
$consolePtr = [Console.Window]::GetConsoleWindow() $consolePtr = [Console.Window]::GetConsoleWindow()
#0 hide #0 hide
[Console.Window]::ShowWindow($consolePtr, 0) | Out-Null [Console.Window]::ShowWindow($consolePtr, 0) | Out-Null
@@ -94,17 +112,19 @@ function Initialize-CloudAPIManagement
) )
$PSModuleAutoloadingPreference = "none" $PSModuleAutoloadingPreference = "none"
$global:hideUI = ($Silent -eq $true)
$global:SilentBatchFile = $SilentBatchFile
$global:wpfNS = "xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'" $global:wpfNS = "xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'"
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") if($global:hideUI -ne $true)
Add-Type -AssemblyName PresentationFramework {
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
Add-Type -AssemblyName PresentationFramework
}
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$global:hideUI = ($Silent -eq $true)
$global:SilentBatchFile = $SilentBatchFile
if($tenantId) if($tenantId)
{ {
Write-Host "Using Tenant Id: $tenantId" Write-Host "Using Tenant Id: $tenantId"
@@ -163,7 +183,7 @@ function Initialize-CloudAPIManagement
$global:txtSplashTitle.Text = ("Initializing Cloud API PowerShell Management") $global:txtSplashTitle.Text = ("Initializing Cloud API PowerShell Management")
$global:SplashScreen.Show() | Out-Null $global:SplashScreen.Show() | Out-Null
[System.Windows.Forms.Application]::DoEvents() Invoke-AppDoEvents
} }
catch catch
{ {
@@ -204,15 +224,15 @@ function Initialize-CloudAPIManagement
{ {
$global:txtSplashText.Text = "Unblock files" $global:txtSplashText.Text = "Unblock files"
} }
[System.Windows.Forms.Application]::DoEvents() Invoke-AppDoEvents
Unblock-AllFiles $PSScriptRoot Unblock-AllFiles $PSScriptRoot
if($global:hideUI -ne $true) if($global:hideUI -ne $true)
{ {
$global:txtSplashText.Text = "Load core module" $global:txtSplashText.Text = "Load core module"
} }
[System.Windows.Forms.Application]::DoEvents() Invoke-AppDoEvents
Import-Module ($PSScriptRoot + "\Core.psm1") -Force -Global Import-Module (Join-Path $PSScriptRoot "Core.psm1") -Force -Global
Start-CoreApp $View Start-CoreApp $View
} }

132
Core.psm1
View File

@@ -14,6 +14,14 @@ function Get-ModuleVersion
'3.9.6' '3.9.6'
} }
function Invoke-AppDoEvents
{
if("System.Windows.Forms.Application" -as [type])
{
[System.Windows.Forms.Application]::DoEvents()
}
}
function Initialize-Window function Initialize-Window
{ {
param($xamlFile) param($xamlFile)
@@ -67,7 +75,27 @@ function Start-CoreApp
$global:AppRootFolder = $PSScriptRoot $global:AppRootFolder = $PSScriptRoot
# Load all modules in the Modules folder # Load all modules in the Modules folder
$global:modulesPath = [IO.Path]::GetDirectoryName($PSCommandPath) + "\Extensions" $global:modulesPath = Join-Path ([IO.Path]::GetDirectoryName($PSCommandPath)) "Extensions"
if($global:hideUI -eq $true)
{
$global:skipModules = @(
"Compare.psm1",
"Copy.psm1",
"Documentation.psm1",
"DocumentationCustom.psm1",
"DocumentationHTML.psm1",
"DocumentationMD.psm1",
"DocumentationWord.psm1",
"IntuneAssignments.psm1",
"IntuneFilterUsage.psm1",
"IntuneTools.psm1"
)
}
else
{
$global:skipModules = @()
}
Add-DefaultSettings Add-DefaultSettings
@@ -122,7 +150,7 @@ function Start-CoreApp
$global:MainAppStarted = $false $global:MainAppStarted = $false
Set-SplashWindowText "Initialize views" Set-SplashWindowText "Initialize views"
[System.Windows.Forms.Application]::DoEvents() Invoke-AppDoEvents
Invoke-ModuleFunction "Invoke-InitializeModule" Invoke-ModuleFunction "Invoke-InitializeModule"
@@ -176,8 +204,6 @@ function Start-CoreApp
Write-Log "SilentBatchFile $($global:SilentBatchFile) not found" 3 Write-Log "SilentBatchFile $($global:SilentBatchFile) not found" 3
return return
} }
Invoke-ModuleFunction "Invoke-ShowMainWindow"
Invoke-ModuleFunction "Invoke-InitSilentBatchJob" Invoke-ModuleFunction "Invoke-InitSilentBatchJob"
Start-RunSilentBatchJob Start-RunSilentBatchJob
@@ -199,13 +225,13 @@ function Start-RunSilentBatchJob
function Import-AllModules function Import-AllModules
{ {
foreach($file in (Get-Item -path "$($global:modulesPath)\*.psm1")) foreach($file in (Get-Item -path (Join-Path $global:modulesPath "*.psm1")))
{ {
$fileName = [IO.Path]::GetFileName($file) $fileName = [IO.Path]::GetFileName($file)
if($skipModules -contains $fileName) { Write-Warning "Module $fileName excluded"; continue; } if($skipModules -contains $fileName) { Write-Warning "Module $fileName excluded"; continue; }
Set-SplashWindowText "Import module $fileName" Set-SplashWindowText "Import module $fileName"
[System.Windows.Forms.Application]::DoEvents() Invoke-AppDoEvents
$module = Import-Module $file -PassThru -Force -Global -ErrorAction SilentlyContinue $module = Import-Module $file -PassThru -Force -Global -ErrorAction SilentlyContinue
if($module) if($module)
@@ -422,7 +448,14 @@ function Set-XamlProperty
{ {
if($obj) if($obj)
{ {
$obj."$propertyName" = $value if(-not ($obj.PSObject.Properties.Name -contains $propertyName))
{
$obj | Add-Member -MemberType NoteProperty -Name $propertyName -Value $value -Force
}
else
{
$obj."$propertyName" = $value
}
} }
else else
{ {
@@ -571,15 +604,15 @@ function Set-BatchProperties
try try
{ {
if($obj -is [System.Windows.Controls.CheckBox]) if($obj.PSObject.Properties.Name -contains "IsChecked")
{ {
$obj.IsChecked = $prop.Value -eq $true $obj.IsChecked = $prop.Value -eq $true
} }
elseif($obj -is [System.Windows.Controls.TextBox]) elseif($obj.PSObject.Properties.Name -contains "Text")
{ {
$obj.Text = $prop.Value $obj.Text = $prop.Value
} }
elseif($obj -is [System.Windows.Controls.ComboBox]) elseif($obj.PSObject.Properties.Name -contains "SelectedValue")
{ {
$obj.SelectedValue = $prop.Value $obj.SelectedValue = $prop.Value
} }
@@ -601,6 +634,60 @@ function Set-BatchProperties
} }
#endregion #endregion
function New-HeadlessControl
{
param(
[string]$Name,
[ValidateSet("TextBox","CheckBox","ComboBox","Label","DataGrid")]
[string]$Type = "TextBox"
)
$control = [PSCustomObject]@{
Name = $Name
Focusable = $true
Visibility = "Visible"
IsEnabled = $true
Text = ""
IsChecked = $false
SelectedValue = $null
ItemsSource = @()
Content = ""
}
if($Type -eq "DataGrid")
{
$control.Focusable = $false
}
$control
}
function New-HeadlessForm
{
param($Controls)
$form = [PSCustomObject]@{
Controls = @{}
}
foreach($control in $Controls)
{
$form.Controls[$control.Name] = $control
}
$form | Add-Member -MemberType ScriptMethod -Name FindName -Value {
param($name)
$this.Controls[$name]
}
$form | Add-Member -MemberType ScriptMethod -Name RegisterName -Value {
param($name, $control)
$this.Controls[$name] = $control
}
$form
}
#region Dialogs #region Dialogs
function Show-AboutDialog function Show-AboutDialog
@@ -1204,6 +1291,21 @@ function Expand-FileName
#endregion #endregion
function Get-CloudApiDataFolder
{
if($env:LOCALAPPDATA)
{
return (Join-Path $env:LOCALAPPDATA "CloudAPIPowerShellManagement")
}
if(Test-IsWindowsPlatform)
{
return (Join-Path $env:USERPROFILE "AppData/Local/CloudAPIPowerShellManagement")
}
return (Join-Path $HOME "Library/Application Support/CloudAPIPowerShellManagement")
}
#region Save/Read Settings functions #region Save/Read Settings functions
######################################################################## ########################################################################
# #
@@ -1231,7 +1333,7 @@ function Initialize-JsonSettings
{ {
if(-not $global:JSonSettingFile) if(-not $global:JSonSettingFile)
{ {
$global:JSonSettingFile = "$($env:LOCALAPPDATA)\CloudAPIPowerShellManagement\Settings.json" $global:JSonSettingFile = Join-Path (Get-CloudApiDataFolder) "Settings.json"
$fi = [IO.FileInfo]$global:JSonSettingFile $fi = [IO.FileInfo]$global:JSonSettingFile
if($fi.Exists -eq $false) if($fi.Exists -eq $false)
{ {
@@ -1548,7 +1650,7 @@ function Add-RegKeyToSettings
try try
{ {
$keyObj = Get-Item -Path $regKey -ErrorAction SilentlyContinue $keyObj = Get-Item -Path $regKey -ErrorAction SilentlyContinue
foreach($keyValue in ($keyObj.GetValueNames() | Sort)) foreach($keyValue in ($keyObj.GetValueNames() | Sort-Object))
{ {
try try
{ {
@@ -1560,7 +1662,7 @@ function Add-RegKeyToSettings
} }
} }
foreach($subKey in ($keyObj.GetSubKeyNames() | Sort)) foreach($subKey in ($keyObj.GetSubKeyNames() | Sort-Object))
{ {
$settingObjSub = [ordered]@{} $settingObjSub = [ordered]@{}
@@ -1967,7 +2069,7 @@ function Add-DefaultSettings
Value = "" Value = ""
} }
foreach($color in ([System.Drawing.Color].GetProperties() | Where { $_.PropertyType -eq [System.Drawing.Color] } | Sort -Property Name | Select Name).Name) foreach($color in ([System.Drawing.Color].GetProperties() | Where { $_.PropertyType -eq [System.Drawing.Color] } | Sort-Object -Property Name | Select Name).Name)
{ {
$script:lstColors += [PSCustomObject]@{ $script:lstColors += [PSCustomObject]@{
Name = $color Name = $color
@@ -2286,7 +2388,7 @@ function Add-ViewItem
if($viewObject.ViewInfo.Permissions -is [Object[]] -and $viewObject.ViewInfo.Permissions -notcontains $scope) { $viewObject.ViewInfo.Permissions += $scope } if($viewObject.ViewInfo.Permissions -is [Object[]] -and $viewObject.ViewInfo.Permissions -notcontains $scope) { $viewObject.ViewInfo.Permissions += $scope }
} }
if($viewItem.Icon -or [IO.File]::Exists(($global:AppRootFolder + "\Xaml\Icons\$($viewItem.Id).xaml"))) if($global:hideUI -ne $true -and ($viewItem.Icon -or [IO.File]::Exists(($global:AppRootFolder + "\Xaml\Icons\$($viewItem.Id).xaml"))))
{ {
$ctrl = Get-XamlObject ($global:AppRootFolder + "\Xaml\Icons\$((?? $viewItem.Icon $viewItem.Id)).xaml") $ctrl = Get-XamlObject ($global:AppRootFolder + "\Xaml\Icons\$((?? $viewItem.Icon $viewItem.Id)).xaml")
$viewItem | Add-Member -NotePropertyName "IconImage" -NotePropertyValue $ctrl $viewItem | Add-Member -NotePropertyName "IconImage" -NotePropertyValue $ctrl

View File

@@ -105,9 +105,12 @@ function Invoke-InitializeModule
Write-Log "Microsoft Intune PowerShell is being decomissioned. Please change to a supported app eg Microsoft Graph or a custom app!" 2 Write-Log "Microsoft Intune PowerShell is being decomissioned. Please change to a supported app eg Microsoft Graph or a custom app!" 2
} }
$viewPanel = Get-XamlObject ($global:AppRootFolder + "\Xaml\EndpointManagerPanel.xaml") -AddVariables $viewPanel = $null
if($global:hideUI -ne $true)
Set-EMViewPanel $viewPanel {
$viewPanel = Get-XamlObject (Join-Path $global:AppRootFolder "Xaml/EndpointManagerPanel.xaml") -AddVariables
Set-EMViewPanel $viewPanel
}
#Add menu group and items #Add menu group and items
$global:EMViewObject = (New-Object PSObject -Property @{ $global:EMViewObject = (New-Object PSObject -Property @{

View File

@@ -138,8 +138,8 @@ function Invoke-InitializeModule
}) "MSAL" }) "MSAL"
$script:MSALUseWAM = Get-SettingValue "UseWAM" $script:MSALUseWAM = Get-SettingValue "UseWAM"
if($script:MSALUseWAM -and $PSVersionTable.PSVersion.Major -lt 7) { if($script:MSALUseWAM -and ($PSVersionTable.PSVersion.Major -lt 7 -or -not (Test-IsWindowsPlatform))) {
Write-Log "WAM is only supported in PowerShell 7 and later. Disabling WAM" 2 Write-Log "WAM is only supported on Windows with PowerShell 7 and later. Disabling WAM" 2
$script:MSALUseWAM = $false $script:MSALUseWAM = $false
} }
@@ -268,7 +268,7 @@ function Get-MSALUserInfo
### Only get user info from home tenant ### Only get user info from home tenant
$global:Me = $tmpMe $global:Me = $tmpMe
Write-Log "Get profile picture" Write-Log "Get profile picture"
$global:profilePhoto = "$($env:LOCALAPPDATA)\CloudAPIPowerShellManagement\$($global:Me.Id).jpeg" $global:profilePhoto = Join-Path (Get-CloudApiDataFolder) "$($global:Me.Id).jpeg"
MSGraph\Invoke-GraphRequest "me/photos/48x48/`$value" -OutFile $global:profilePhoto -SkipAuthentication -NoError | Out-Null MSGraph\Invoke-GraphRequest "me/photos/48x48/`$value" -OutFile $global:profilePhoto -SkipAuthentication -NoError | Out-Null
} }
} }
@@ -517,17 +517,17 @@ function Add-MSALPrereq
$DLLFiles = @() $DLLFiles = @()
if($PSVersionTable.PSVersion.Major -ge 7) { if($PSVersionTable.PSVersion.Major -ge 7) {
$DLLFiles += [IO.FileInfo]($ScriptRoot + "\Bin\Microsoft.IdentityModel.Abstractions.dll") $DLLFiles += [IO.FileInfo](Join-Path $ScriptRoot "Bin/Microsoft.IdentityModel.Abstractions.dll")
} }
else { else {
$DLLFiles += [IO.FileInfo]($ScriptRoot + "\Bin\6.35.0\Microsoft.IdentityModel.Abstractions.dll") $DLLFiles += [IO.FileInfo](Join-Path $ScriptRoot "Bin/6.35.0/Microsoft.IdentityModel.Abstractions.dll")
} }
$DLLFiles += [IO.FileInfo]($ScriptRoot + "\Bin\Microsoft.Identity.Client.dll") $DLLFiles += [IO.FileInfo](Join-Path $ScriptRoot "Bin/Microsoft.Identity.Client.dll")
if($script:MSALUseWAM) { if($script:MSALUseWAM) {
$DLLFiles += [IO.FileInfo]($ScriptRoot + "\BIN\$MSALDLLPath\Microsoft.Identity.Client.Extensions.Msal.dll") $DLLFiles += [IO.FileInfo](Join-Path $ScriptRoot "Bin/$MSALDLLPath/Microsoft.Identity.Client.Extensions.Msal.dll")
$DLLFiles += [IO.FileInfo]($ScriptRoot + "\BIN\$MSALDLLPath\Microsoft.Identity.Client.Broker.dll") $DLLFiles += [IO.FileInfo](Join-Path $ScriptRoot "Bin/$MSALDLLPath/Microsoft.Identity.Client.Broker.dll")
$DLLFiles += [IO.FileInfo]($ScriptRoot + "\BIN\$MSALDLLPath\Microsoft.Identity.Client.Desktop.dll") $DLLFiles += [IO.FileInfo](Join-Path $ScriptRoot "Bin/$MSALDLLPath/Microsoft.Identity.Client.Desktop.dll")
$DLLFiles += [IO.FileInfo]($ScriptRoot + "\BIN\$MSALDLLPath\Microsoft.Identity.Client.NativeInterop.dll") $DLLFiles += [IO.FileInfo](Join-Path $ScriptRoot "Bin/$MSALDLLPath/Microsoft.Identity.Client.NativeInterop.dll")
} }
$DLLFiles | ForEach-Object { $DLLFiles | ForEach-Object {
@@ -570,7 +570,7 @@ function Add-MSALPrereq
try try
{ {
Add-Type -Path ($ScriptRoot + "\CS\TokenCacheHelperEx.cs") -ReferencedAssemblies $RequiredAssemblies -IgnoreWarnings Add-Type -Path (Join-Path $ScriptRoot "CS/TokenCacheHelperEx.cs") -ReferencedAssemblies $RequiredAssemblies -IgnoreWarnings
} }
catch catch
{ {
@@ -809,7 +809,7 @@ function Get-MsalAuthenticationToken
{ {
# Login hung on rare occations # Login hung on rare occations
# Workaround: Added DoEvents # Workaround: Added DoEvents
[System.Windows.Forms.Application]::DoEvents() Invoke-AppDoEvents
Start-Sleep -Seconds 1 Start-Sleep -Seconds 1
} }
} }
@@ -954,7 +954,7 @@ function Get-MSALApp
if($global:SkipTokenCacheHelperEx -ne $true -and (Get-SettingValue "CacheMSALToken")) if($global:SkipTokenCacheHelperEx -ne $true -and (Get-SettingValue "CacheMSALToken"))
{ {
[TokenCacheHelperEx]::EnableSerialization($msalApp.UserTokenCache, "%LOCALAPPDATA%\CloudAPIPowerShellManagement\msalcahce.bin3") [TokenCacheHelperEx]::EnableSerialization($msalApp.UserTokenCache, (Join-Path (Get-CloudApiDataFolder) "msalcahce.bin3"))
} }
$script:MSALAllApps += $msalApp $script:MSALAllApps += $msalApp
} }
@@ -1350,7 +1350,7 @@ function Connect-MSALUser
if((Get-SettingValue "CacheMSALToken")) if((Get-SettingValue "CacheMSALToken"))
{ {
[TokenCacheHelperEx]::EnableSerialization($app.UserTokenCache, "%LOCALAPPDATA%\CloudAPIPowerShellManagement\msalcahce.bin3") [TokenCacheHelperEx]::EnableSerialization($app.UserTokenCache, (Join-Path (Get-CloudApiDataFolder) "msalcahce.bin3"))
} }
### Silent login ### Silent login

View File

@@ -782,7 +782,7 @@ function Add-GraphObjectProperties
if($objects.Count -gt 0 -and $SortProperty -and ($objects[0] | GM -MemberType NoteProperty -Name $SortProperty)) if($objects.Count -gt 0 -and $SortProperty -and ($objects[0] | GM -MemberType NoteProperty -Name $SortProperty))
{ {
$objects = $objects | sort -Property $SortProperty $objects = $objects | Sort-Object -Property $SortProperty
} }
$objects $objects
@@ -1400,12 +1400,77 @@ function Invoke-SilentBatchJob
} }
} }
function New-GraphSilentBatchForm
{
param($Controls)
$form = New-HeadlessForm $Controls
foreach($control in $Controls)
{
New-Variable -Name $control.Name -Value $control -Force -Scope Global
}
$form
}
function New-GraphSilentExportForm
{
$controls = @(
(New-HeadlessControl -Name "txtExportPath" -Type "TextBox"),
(New-HeadlessControl -Name "txtExportNameFilter" -Type "TextBox"),
(New-HeadlessControl -Name "chkAddObjectType" -Type "CheckBox"),
(New-HeadlessControl -Name "chkExportAssignments" -Type "CheckBox"),
(New-HeadlessControl -Name "chkAddCompanyName" -Type "CheckBox"),
(New-HeadlessControl -Name "dgObjectsToExport" -Type "DataGrid")
)
$form = New-GraphSilentBatchForm $controls
Set-XamlProperty $form "txtExportPath" "Text" (?? (Get-Setting "" "LastUsedRoot") (Get-SettingValue "RootFolder"))
Set-XamlProperty $form "chkAddObjectType" "IsChecked" $true
Set-XamlProperty $form "chkExportAssignments" "IsChecked" (Get-SettingValue "ExportAssignments")
Set-XamlProperty $form "chkAddCompanyName" "IsChecked" (Get-SettingValue "AddCompanyName")
$form
}
function New-GraphSilentImportForm
{
$path = Get-Setting "" "LastUsedFullPath"
if($path)
{
$path = [IO.Directory]::GetParent($path).FullName
}
$controls = @(
(New-HeadlessControl -Name "txtImportPath" -Type "TextBox"),
(New-HeadlessControl -Name "txtImportNameFilter" -Type "TextBox"),
(New-HeadlessControl -Name "lblMigrationTableInfo" -Type "Label"),
(New-HeadlessControl -Name "chkAddObjectType" -Type "CheckBox"),
(New-HeadlessControl -Name "chkImportScopes" -Type "CheckBox"),
(New-HeadlessControl -Name "chkImportAssignments" -Type "CheckBox"),
(New-HeadlessControl -Name "chkReplaceDependencyIDs" -Type "CheckBox"),
(New-HeadlessControl -Name "cbImportType" -Type "ComboBox"),
(New-HeadlessControl -Name "dgObjectsToImport" -Type "DataGrid")
)
$form = New-GraphSilentBatchForm $controls
Set-XamlProperty $form "txtImportPath" "Text" (?? $path (Get-SettingValue "RootFolder"))
Set-XamlProperty $form "chkAddObjectType" "IsChecked" $true
Set-XamlProperty $form "chkImportAssignments" "IsChecked" (Get-SettingValue "ImportAssignments")
Set-XamlProperty $form "chkImportScopes" "IsChecked" (Get-SettingValue "ImportScopeTags")
Set-XamlProperty $form "chkReplaceDependencyIDs" "IsChecked" $true
Set-XamlProperty $form "cbImportType" "ItemsSource" $script:lstImportTypes
Set-XamlProperty $form "cbImportType" "SelectedValue" (Get-SettingValue "ImportType" "alwaysImport")
$form
}
function Start-GraphSilentBulkExport function Start-GraphSilentBulkExport
{ {
param($settingsObj) param($settingsObj)
$script:exportForm = Get-XamlObject ($global:AppRootFolder + "\Xaml\BulkExportForm.xaml") -AddVariables $script:exportForm = New-GraphSilentExportForm
if(-not $script:exportForm) { return }
$script:exportObjects = Get-GraphBatchObjectTypes $settingsObj.BulkExport $script:exportObjects = Get-GraphBatchObjectTypes $settingsObj.BulkExport
@@ -1414,8 +1479,6 @@ function Start-GraphSilentBulkExport
if(-not $viewObj.Title) { continue } if(-not $viewObj.Title) { continue }
if($viewObj.ObjectType.ShowButtons -is [Object[]] -and $viewObj.ObjectType.ShowButtons -notcontains "Export") { continue } if($viewObj.ObjectType.ShowButtons -is [Object[]] -and $viewObj.ObjectType.ShowButtons -notcontains "Export") { continue }
Add-GraphExportExtensions $script:exportForm 0 $viewObj.ObjectType
} }
Set-BatchProperties $settingsObj.BulkExport $script:exportForm Set-BatchProperties $settingsObj.BulkExport $script:exportForm
@@ -1449,8 +1512,7 @@ function Start-GraphSilentBulkImport
{ {
param($settingsObj) param($settingsObj)
$script:importForm = Get-XamlObject ($global:AppRootFolder + "\Xaml\BulkImportForm.xaml") -AddVariables $script:importForm = New-GraphSilentImportForm
if(-not $script:importForm) { return }
# Get all objects but not selected # Get all objects but not selected
# This will allow dependencies # This will allow dependencies
@@ -1479,11 +1541,10 @@ function Start-GraphSilentBulkImport
if(-not $viewObj.Title) { continue } if(-not $viewObj.Title) { continue }
if($viewObj.ObjectType.ShowButtons -is [Object[]] -and $viewObj.ObjectType.ShowButtons -notcontains "Import") { continue } if($viewObj.ObjectType.ShowButtons -is [Object[]] -and $viewObj.ObjectType.ShowButtons -notcontains "Import") { continue }
Add-GraphImportExtensions $script:importForm 0 $viewObj.ObjectType
} }
Set-BatchProperties $settingsObj.BulkImport $script:importForm Set-BatchProperties $settingsObj.BulkImport $script:importForm
Set-XamlProperty $script:importForm "lblMigrationTableInfo" "Content" (Get-MigrationTableInfo)
$global:dgObjectsToImport.ItemsSource = @($script:importObjects) $global:dgObjectsToImport.ItemsSource = @($script:importObjects)
@@ -2358,7 +2419,7 @@ function Get-GraphFileObjects
} }
$fileArr = @() $fileArr = @()
foreach($file in (Get-Item -path "$path\*.json" @params)) foreach($file in (Get-Item -path (Join-Path $path "*.json") @params))
{ {
if($ObjectType.LoadObject) if($ObjectType.LoadObject)
{ {
@@ -2864,7 +2925,7 @@ function Add-GroupMigrationObject
# Export group info to json file for possible import # Export group info to json file for possible import
$grouspPath = Join-Path $path "Groups" $grouspPath = Join-Path $path "Groups"
if(-not (Test-Path $grouspPath)) { mkdir -Path $grouspPath -Force -ErrorAction SilentlyContinue | Out-Null } if(-not (Test-Path $grouspPath)) { mkdir -Path $grouspPath -Force -ErrorAction SilentlyContinue | Out-Null }
$fileName = "$grouspPath\$((Remove-InvalidFileNameChars $groupObj.displayName)).json" $fileName = Join-Path $grouspPath "$((Remove-InvalidFileNameChars $groupObj.displayName)).json"
Save-GraphObjectToFile $groupObj $fileName Save-GraphObjectToFile $groupObj $fileName
} }
} }
@@ -2907,7 +2968,7 @@ function Add-GraphMigrationObject
# Export group info to json file for possible import # Export group info to json file for possible import
$grouspPath = Join-Path $path "Groups" $grouspPath = Join-Path $path "Groups"
if(-not (Test-Path $grouspPath)) { mkdir -Path $grouspPath -Force -ErrorAction SilentlyContinue | Out-Null } if(-not (Test-Path $grouspPath)) { mkdir -Path $grouspPath -Force -ErrorAction SilentlyContinue | Out-Null }
$fileName = "$grouspPath\$((Remove-InvalidFileNameChars $graphObj.displayName)).json" $fileName = Join-Path $grouspPath "$((Remove-InvalidFileNameChars $graphObj.displayName)).json"
Save-GraphObjectToFile $graphObj $fileName Save-GraphObjectToFile $graphObj $fileName
} }
} }
@@ -3050,7 +3111,7 @@ function Get-GraphMigrationObjectsFromFile
if($global:GraphMigrationTable) if($global:GraphMigrationTable)
{ {
$fi = [IO.FileInfo]$global:GraphMigrationTable $fi = [IO.FileInfo]$global:GraphMigrationTable
$groupFi = [IO.FileInfo]($fi.DirectoryName + "\Groups\$((Remove-InvalidFileNameChars $migTableGroupName)).json") $groupFi = [IO.FileInfo](Join-Path (Join-Path $fi.DirectoryName "Groups") "$((Remove-InvalidFileNameChars $migTableGroupName)).json")
} }
if($groupFi.Exists -eq $true) if($groupFi.Exists -eq $true)
@@ -3197,13 +3258,13 @@ function Add-GraphDependencyObjects
continue continue
} }
if([IO.Directory]::Exists(($importPath + "\" + $dep))) if([IO.Directory]::Exists((Join-Path $importPath $dep)))
{ {
$path = ($importPath + "\" + $dep) $path = (Join-Path $importPath $dep)
} }
elseif([IO.Directory]::Exists(($parentPath + "\" + $dep))) elseif([IO.Directory]::Exists((Join-Path $parentPath $dep)))
{ {
$path = ($parentPath + "\" + $dep) $path = (Join-Path $parentPath $dep)
} }
else else
{ {
@@ -3674,7 +3735,7 @@ function Invoke-GraphBatchRequest
$retryAfter = 0 $retryAfter = 0
$tmpResults = Invoke-GraphRequest -Url "`$batch" -Body $json -Method "POST" $tmpResults = Invoke-GraphRequest -Url "`$batch" -Body $json -Method "POST"
foreach($batchResult in ($tmpResults.responses | Sort -Property Id)) foreach($batchResult in ($tmpResults.responses | Sort-Object -Property Id))
{ {
if($batchResult.Status -ge 300 -or -not $batchResult.body) if($batchResult.Status -ge 300 -or -not $batchResult.body)
{ {
@@ -4151,7 +4212,7 @@ function Show-GraphObjectInfo
if($prop.Name.Contains('@') -or $prop.Name.Contains('#')) { continue } if($prop.Name.Contains('@') -or $prop.Name.Contains('#')) { continue }
$objProps += ([PSCustomObject]@{ Name=$prop.Name;Value=$prop }) $objProps += ([PSCustomObject]@{ Name=$prop.Name;Value=$prop })
} }
$objProps = $objProps | sort -Property Name $objProps = $objProps | Sort-Object -Property Name
Set-XamlProperty $script:detailsForm "lstObjectProperties" "ItemsSource" $objProps Set-XamlProperty $script:detailsForm "lstObjectProperties" "ItemsSource" $objProps
Add-XamlEvent $script:detailsForm "btnObjectColumnsReset" "Add_Click" -scriptBlock ([scriptblock]{ Add-XamlEvent $script:detailsForm "btnObjectColumnsReset" "Add_Click" -scriptBlock ([scriptblock]{

View File

@@ -0,0 +1,17 @@
@{
RootModule = 'IntuneManagement.Headless.psm1'
ModuleVersion = '0.1.0'
GUID = 'b5b4183d-8d6b-4b31-bbde-f2f0f0a0739d'
Author = 'OpenAI Codex'
Copyright = '(c) OpenAI. Adapter module for headless Intune policy migration.'
Description = 'Headless export/import wrapper for IntuneManagement.'
FunctionsToExport = @(
'Get-DefaultIntunePolicyObjectTypes',
'Export-IntunePolicies',
'Import-IntunePolicies',
'Invoke-IntunePolicyAction'
)
AliasesToExport = @()
VariablesToExport = @()
CmdletsToExport = @()
}

View File

@@ -0,0 +1,311 @@
function Get-DefaultIntunePolicyObjectTypes
{
@(
"DeviceConfiguration",
"SettingsCatalog",
"AdministrativeTemplates",
"CompliancePolicies",
"EndpointSecurity",
"PolicySets"
)
}
function Get-IntuneManagementProjectRoot
{
Split-Path -Parent $PSScriptRoot
}
function Resolve-HeadlessSettingsPath
{
param([string]$SettingsFile)
if($SettingsFile)
{
return $SettingsFile
}
Join-Path ([IO.Path]::GetTempPath()) "IntuneManagement.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]$Secret,
[string]$Certificate
)
if((-not $Secret) -and (-not $Certificate))
{
throw "Specify -Secret or -Certificate."
}
}
function Invoke-IntuneHeadlessBatch
{
param(
[Parameter(Mandatory = $true)]
[string]$TenantId,
[Parameter(Mandatory = $true)]
[string]$AppId,
[string]$Secret,
[string]$Certificate,
[Parameter(Mandatory = $true)]
[psobject]$BatchConfig,
[string]$SettingsFile,
[string]$BatchFile
)
Test-AuthParameters -Secret $Secret -Certificate $Certificate
$projectRoot = Get-IntuneManagementProjectRoot
$startScript = Join-Path $projectRoot "Start-IntuneManagement.ps1"
if(-not (Test-Path $startScript))
{
throw "Could not find Start-IntuneManagement.ps1 in $projectRoot"
}
$settingsPath = Resolve-HeadlessSettingsPath $SettingsFile
$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
}
if($Secret)
{
$invokeParams.Secret = $Secret
}
else
{
$invokeParams.Certificate = $Certificate
}
& $startScript @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,
[Parameter(Mandatory = $true)]
[string]$AppId,
[string]$Secret,
[string]$Certificate,
[Parameter(Mandatory = $true)]
[string]$ExportPath,
[string]$SettingsFile,
[string]$BatchFile,
[string]$NameFilter = "",
[string[]]$ObjectTypes = (Get-DefaultIntunePolicyObjectTypes),
[switch]$IncludeAssignments,
[switch]$AddCompanyName
)
$batchConfig = [PSCustomObject]@{
BulkExport = @(
[PSCustomObject]@{ Name = "txtExportPath"; Value = $ExportPath },
[PSCustomObject]@{ Name = "txtExportNameFilter"; Value = $NameFilter },
[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 `
-BatchConfig $batchConfig `
-SettingsFile $SettingsFile `
-BatchFile $BatchFile
}
function Import-IntunePolicies
{
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$TenantId,
[Parameter(Mandatory = $true)]
[string]$AppId,
[string]$Secret,
[string]$Certificate,
[Parameter(Mandatory = $true)]
[string]$ImportPath,
[string]$SettingsFile,
[string]$BatchFile,
[string]$NameFilter = "",
[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 = "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 `
-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,
[Parameter(Mandatory = $true)]
[string]$AppId,
[string]$Secret,
[string]$Certificate,
[string]$SettingsFile,
[string]$BatchFile,
[string]$NameFilter = "",
[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 `
-ExportPath $ExportPath `
-SettingsFile $SettingsFile `
-BatchFile $BatchFile `
-NameFilter $NameFilter `
-ObjectTypes $ObjectTypes `
-IncludeAssignments:$IncludeAssignments `
-AddCompanyName:$AddCompanyName
}
"Import"
{
if(-not $ImportPath) { throw "Import requires -ImportPath." }
Import-IntunePolicies `
-TenantId $TenantId `
-AppId $AppId `
-Secret $Secret `
-Certificate $Certificate `
-ImportPath $ImportPath `
-SettingsFile $SettingsFile `
-BatchFile $BatchFile `
-NameFilter $NameFilter `
-ImportType $ImportType `
-ObjectTypes $ObjectTypes `
-IncludeAssignments:$IncludeAssignments `
-IncludeScopeTags:$IncludeScopeTags `
-ReplaceDependencyIds:$ReplaceDependencyIds
}
}
}

40
Headless/README.md Normal file
View File

@@ -0,0 +1,40 @@
# IntuneManagement Headless
This is the CLI-first surface for a cross-platform fork of the original IntuneManagement project.
The original project is still a Windows WPF application. This layer treats that codebase as an implementation backend and exposes a smaller product surface focused on:
* app-only authentication
* headless export/import
* macOS/Linux/Windows execution with `pwsh`
* automation and CI usage
## Entry points
* [Start-HeadlessIntune.ps1](/Users/avedelphina/Local/IntuneManagement/Start-HeadlessIntune.ps1)
* [Scripts/Export-Policies.ps1](/Users/avedelphina/Local/IntuneManagement/Scripts/Export-Policies.ps1)
* [Scripts/Import-Policies.ps1](/Users/avedelphina/Local/IntuneManagement/Scripts/Import-Policies.ps1)
* [Headless/IntuneManagement.Headless.psd1](/Users/avedelphina/Local/IntuneManagement/Headless/IntuneManagement.Headless.psd1)
## Default policy scope
The default object types are:
* `DeviceConfiguration`
* `SettingsCatalog`
* `AdministrativeTemplates`
* `CompliancePolicies`
* `EndpointSecurity`
* `PolicySets`
## Example
```powershell
pwsh ./Start-HeadlessIntune.ps1 `
-Action Export `
-TenantId "<source-tenant-id>" `
-AppId "<app-id>" `
-Secret "<client-secret>" `
-ExportPath "/tmp/intune-export" `
-IncludeAssignments
```

134
README.md
View File

@@ -1,4 +1,14 @@
# IntuneManagement with PowerShell and WPF UI # IntuneManagement
This repository now contains two usable surfaces:
* the original Windows WPF application
* a newer headless CLI-first surface for cross-platform export/import
The CLI-first fork shape starts here:
* [Start-HeadlessIntune.ps1](/Users/avedelphina/Local/IntuneManagement/Start-HeadlessIntune.ps1)
* [Headless/README.md](/Users/avedelphina/Local/IntuneManagement/Headless/README.md)
<p align="center"> <p align="center">
<a href="https://twitter.com/Micke_K_72"> <a href="https://twitter.com/Micke_K_72">
@@ -64,6 +74,128 @@ The tool will by default generate the files; `BulkExport.json` and `BulkImport.j
The app authentication can either be passed on the command line or stored in the settings. Tennant Settings is required for multiple environments. The app authentication can either be passed on the command line or stored in the settings. Tennant Settings is required for multiple environments.
### Headless use on macOS/Linux
The WPF UI is still Windows-only, but the **silent batch** mode can be used headlessly with `pwsh` on macOS/Linux.
For non-Windows use:
* Run `Start-IntuneManagement.ps1` with `-Silent`
* Use app-only authentication with `-TenantId`, `-AppId` and `-Secret` or `-Certificate`
* Use `-JSonSettings` and preferably specify `-JSonFile`
* Pass a batch configuration JSON with `-SilentBatchFile`
Common policy object type IDs:
* `DeviceConfiguration`
* `SettingsCatalog`
* `AdministrativeTemplates`
* `CompliancePolicies`
* `EndpointSecurity`
* `PolicySets`
* `ConditionalAccess`
**Example export batch file**
```json
{
"BulkExport": [
{ "Name": "txtExportPath", "Value": "/tmp/intune-export" },
{ "Name": "txtExportNameFilter", "Value": "" },
{ "Name": "chkAddObjectType", "Value": true },
{ "Name": "chkExportAssignments", "Value": true },
{ "Name": "chkAddCompanyName", "Value": false },
{
"Name": "ObjectTypes",
"Type": "Custom",
"ObjectTypes": [
"DeviceConfiguration",
"SettingsCatalog",
"AdministrativeTemplates",
"CompliancePolicies",
"EndpointSecurity",
"PolicySets"
]
}
]
}
```
**Example export command**
```powershell
pwsh ./Start-IntuneManagement.ps1 `
-Silent `
-JSonSettings -JSonFile "/tmp/intune-settings.json" `
-TenantId "<source-tenant-id>" `
-AppId "<app-id>" `
-Secret "<client-secret>" `
-SilentBatchFile "/tmp/BulkExport.json"
```
**Example import batch file**
```json
{
"BulkImport": [
{ "Name": "txtImportPath", "Value": "/tmp/intune-export/SourceTenantName" },
{ "Name": "txtImportNameFilter", "Value": "" },
{ "Name": "chkAddObjectType", "Value": true },
{ "Name": "chkImportScopes", "Value": true },
{ "Name": "chkImportAssignments", "Value": true },
{ "Name": "chkReplaceDependencyIDs", "Value": true },
{ "Name": "cbImportType", "Value": "alwaysImport" },
{
"Name": "ObjectTypes",
"Type": "Custom",
"ObjectTypes": [
"DeviceConfiguration",
"SettingsCatalog",
"AdministrativeTemplates",
"CompliancePolicies",
"EndpointSecurity",
"PolicySets"
]
}
]
}
```
**Example import command**
```powershell
pwsh ./Start-IntuneManagement.ps1 `
-Silent `
-JSonSettings -JSonFile "/tmp/intune-settings.json" `
-TenantId "<target-tenant-id>" `
-AppId "<app-id>" `
-Secret "<client-secret>" `
-SilentBatchFile "/tmp/BulkImport.json"
```
Wrapper scripts are also included:
```powershell
pwsh ./Scripts/Export-Policies.ps1 `
-TenantId "<source-tenant-id>" `
-AppId "<app-id>" `
-Secret "<client-secret>" `
-ExportPath "/tmp/intune-export" `
-IncludeAssignments
```
```powershell
pwsh ./Scripts/Import-Policies.ps1 `
-TenantId "<target-tenant-id>" `
-AppId "<app-id>" `
-Secret "<client-secret>" `
-ImportPath "/tmp/intune-export/SourceTenantName" `
-ImportType alwaysImport `
-IncludeAssignments `
-IncludeScopeTags `
-ReplaceDependencyIds
```
**Command line example:** **Command line example:**
Start-IntuneManagement.ps1 -Silent -TenantId "<*TenantID*>" -SilentBatchFile <*PathToFile*> [-AppId <*AppId*>] [-Secret <*Secret*> | -Certificate <*CertThumb*>] Start-IntuneManagement.ps1 -Silent -TenantId "<*TenantID*>" -SilentBatchFile <*PathToFile*> [-AppId <*AppId*>] [-Secret <*Secret*> | -Certificate <*CertThumb*>]

View File

@@ -0,0 +1,43 @@
<#
.SYNOPSIS
Headless Intune policy export wrapper for macOS/Linux/Windows.
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$TenantId,
[Parameter(Mandatory = $true)]
[string]$AppId,
[string]$Secret,
[string]$Certificate,
[Parameter(Mandatory = $true)]
[string]$ExportPath,
[string]$SettingsFile,
[string]$BatchFile,
[string]$NameFilter = "",
[string[]]$ObjectTypes = @(
"DeviceConfiguration",
"SettingsCatalog",
"AdministrativeTemplates",
"CompliancePolicies",
"EndpointSecurity",
"PolicySets"
),
[switch]$IncludeAssignments,
[switch]$AddCompanyName
)
$modulePath = Join-Path (Split-Path -Parent $PSScriptRoot) "Headless/IntuneManagement.Headless.psd1"
Import-Module $modulePath -Force
Export-IntunePolicies @PSBoundParameters

View File

@@ -0,0 +1,48 @@
<#
.SYNOPSIS
Headless Intune policy import wrapper for macOS/Linux/Windows.
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$TenantId,
[Parameter(Mandatory = $true)]
[string]$AppId,
[string]$Secret,
[string]$Certificate,
[Parameter(Mandatory = $true)]
[string]$ImportPath,
[string]$SettingsFile,
[string]$BatchFile,
[string]$NameFilter = "",
[ValidateSet("alwaysImport","skipIfExist","replace","replace_with_assignments","update")]
[string]$ImportType = "alwaysImport",
[string[]]$ObjectTypes = @(
"DeviceConfiguration",
"SettingsCatalog",
"AdministrativeTemplates",
"CompliancePolicies",
"EndpointSecurity",
"PolicySets"
),
[switch]$IncludeAssignments,
[switch]$IncludeScopeTags,
[switch]$ReplaceDependencyIds
)
$modulePath = Join-Path (Split-Path -Parent $PSScriptRoot) "Headless/IntuneManagement.Headless.psd1"
Import-Module $modulePath -Force
Import-IntunePolicies @PSBoundParameters

74
Start-HeadlessIntune.ps1 Normal file
View File

@@ -0,0 +1,74 @@
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[ValidateSet("Export","Import")]
[string]$Action,
[Parameter(Mandatory = $true)]
[string]$TenantId,
[Parameter(Mandatory = $true)]
[string]$AppId,
[string]$Secret,
[string]$Certificate,
[string]$SettingsFile,
[string]$BatchFile,
[string]$NameFilter = "",
[string[]]$ObjectTypes,
[string]$ExportPath,
[string]$ImportPath,
[ValidateSet("alwaysImport","skipIfExist","replace","replace_with_assignments","update")]
[string]$ImportType = "alwaysImport",
[switch]$IncludeAssignments,
[switch]$AddCompanyName,
[switch]$IncludeScopeTags,
[switch]$ReplaceDependencyIds
)
$modulePath = Join-Path $PSScriptRoot "Headless/IntuneManagement.Headless.psd1"
Import-Module $modulePath -Force
$invokeParams = @{
Action = $Action
TenantId = $TenantId
AppId = $AppId
SettingsFile = $SettingsFile
BatchFile = $BatchFile
NameFilter = $NameFilter
ExportPath = $ExportPath
ImportPath = $ImportPath
ImportType = $ImportType
IncludeAssignments = $IncludeAssignments
AddCompanyName = $AddCompanyName
IncludeScopeTags = $IncludeScopeTags
ReplaceDependencyIds = $ReplaceDependencyIds
}
if($PSBoundParameters.ContainsKey("ObjectTypes"))
{
$invokeParams.ObjectTypes = $ObjectTypes
}
if($Secret)
{
$invokeParams.Secret = $Secret
}
elseif($Certificate)
{
$invokeParams.Certificate = $Certificate
}
Invoke-IntunePolicyAction @invokeParams