From ae71f8458d3181b9f9dc2886de5cb9a288e85d97 Mon Sep 17 00:00:00 2001 From: Tomas Kracmar Date: Fri, 7 Aug 2026 11:31:22 +0200 Subject: [PATCH] feat(toolkit): add bulk delete tool, fix TUI exit hang and tenant re-prompt - New Scripts/Bulk-DeletePolicies.ps1: type-DELETE confirmation, -WhatIf dry-run - Fix infinite loop in Start-IntuneToolkit.ps1 when fzf missing (numbered-menu Exit returned "EXIT" but check only matched fzf's "[Exit]" string) - Start-IntuneManagementTui.ps1 no longer re-prompts for TenantId when the launcher already resolved one; also fixes misleading "regex" wording on the Name filter prompt (it's a literal substring match) Co-Authored-By: Claude Sonnet 5 --- CHANGELOG_macOS_IntuneToolkit.md | 17 + README.md | 3 +- Scripts/Bulk-DeletePolicies.ps1 | 354 ++++++++++++++++++ Scripts/Private/Start-IntuneManagementTui.ps1 | 60 ++- Scripts/Start-HeadlessIntune.ps1 | 2 +- Start-IntuneToolkit.ps1 | 4 +- VERSION | 2 +- 7 files changed, 421 insertions(+), 21 deletions(-) create mode 100644 Scripts/Bulk-DeletePolicies.ps1 diff --git a/CHANGELOG_macOS_IntuneToolkit.md b/CHANGELOG_macOS_IntuneToolkit.md index c88ac92..efa3d0a 100644 --- a/CHANGELOG_macOS_IntuneToolkit.md +++ b/CHANGELOG_macOS_IntuneToolkit.md @@ -1,5 +1,22 @@ # macOS Intune Toolkit Changelog +## 2026-08-07 — v4.3.0 — Bulk delete tool and TUI fixes + +### Added +- **`Scripts/Bulk-DeletePolicies.ps1`** + - Select object type, filter by name, multi-select objects, and delete them from the tenant. + - Requires typing `DELETE` to confirm since the operation is irreversible. Supports `-WhatIf` dry-run. + - Wired into `Start-IntuneToolkit.ps1` as menu item **21. Bulk delete policies**. + +### Fixed +- **`Start-IntuneToolkit.ps1`** + - Fixed an infinite loop when `fzf` is not installed: the numbered-menu fallback returned `"EXIT"` for the tenant-selection Exit option, but the check only matched the fzf path's `"[Exit]"` string, so choosing Exit was silently ignored and the tenant became the literal string `"EXIT"`. +- **`Scripts/Private/Start-IntuneManagementTui.ps1`** + - The interactive Export/Import/GenerateReports/DeployCISBaseline flow always re-prompted for a Tenant ID even when the tenant was already selected in the outer launcher, because it was invoked with no parameters and had an empty `param()` block. It now accepts `-TenantId` and skips its three tenant prompts when the value is already supplied. + - Clarified the "Name filter" prompt text — it's a plain case-insensitive substring match, not a regex (the underlying match escapes the value via `[RegEx]::Escape()`), so the old `'^Win-OIB-'` example was misleading. +- **`Scripts/Start-HeadlessIntune.ps1`** + - Passes `-TenantId` through to `Start-IntuneManagementTui.ps1` so the fix above takes effect. + ## 2026-06-23 — v4.2.0 — Entra directory role membership export ### Added diff --git a/README.md b/README.md index 31cce55..b3f1cec 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Cross-platform, headless Intune policy export/import with PowerShell. -**Current version:** `4.2.0` — see [`CHANGELOG_macOS_IntuneToolkit.md`](CHANGELOG_macOS_IntuneToolkit.md) for recent changes. +**Current version:** `4.3.0` — see [`CHANGELOG_macOS_IntuneToolkit.md`](CHANGELOG_macOS_IntuneToolkit.md) for recent changes. This repository is now CLI-first. The old WPF application surface has been removed from the repo. The supported workflow is: @@ -146,6 +146,7 @@ pwsh ./Scripts/Start-HeadlessIntune.ps1 ` * **Bulk assignments** — [`Bulk-AssignmentManager.ps1`](Scripts/Bulk-AssignmentManager.ps1) adds or removes assignments for any policy type using the bulk `/assign` endpoint. [`Bulk-AppAssignment.ps1`](Scripts/Bulk-AppAssignment.ps1) does the same for applications. * **Backup / restore assignments** — [`Backup-Restore-Assignments.ps1`](Scripts/Backup-Restore-Assignments.ps1) saves assignments to JSON and can restore them with cross-tenant group name resolution. * **Bulk rename** — [`Bulk-RenamePolicies.ps1`](Scripts/Bulk-RenamePolicies.ps1) performs search/replace or prefix mutations across policy names and descriptions. +* **Bulk delete** — [`Bulk-DeletePolicies.ps1`](Scripts/Bulk-DeletePolicies.ps1) deletes selected policies/apps by object type and name filter, requires typing `DELETE` to confirm, supports `-WhatIf`. * **Device operations** — [`Bulk-DeviceOperations.ps1`](Scripts/Bulk-DeviceOperations.ps1) supports delete, retire, wipe, lock, and sync with `-WhatIf` safeguards. * **Assignment documentation** — [`Export-AssignmentsToCsv.ps1`](Scripts/Export-AssignmentsToCsv.ps1) exports assignments to CSV and Markdown. * **Reporting utilities** — [`Export-SettingsReport.py`](Scripts/Export-SettingsReport.py), [`Export-AssignmentReport.py`](Scripts/Export-AssignmentReport.py), and [`Export-ObjectInventoryReport.py`](Scripts/Export-ObjectInventoryReport.py) generate CSV/Markdown reports from local exports. diff --git a/Scripts/Bulk-DeletePolicies.ps1 b/Scripts/Bulk-DeletePolicies.ps1 new file mode 100644 index 0000000..c9832a4 --- /dev/null +++ b/Scripts/Bulk-DeletePolicies.ps1 @@ -0,0 +1,354 @@ +#requires -Version 5.1 +<# +.SYNOPSIS + Bulk delete Intune policies/apps by object type and name filter. +.DESCRIPTION + Select an object type, optionally filter by name, multi-select objects, + and delete them from the tenant. Requires typing DELETE to confirm since + this is irreversible. Supports -WhatIf dry-run. +.EXAMPLE + ./Scripts/Bulk-DeletePolicies.ps1 -TenantId "contoso.onmicrosoft.com" -WhatIf +#> +[CmdletBinding()] +param( + [Parameter(Mandatory = $true)] + [string]$TenantId, + + [string]$AppId, + + [string]$Secret, + + [string]$Certificate, + + [ValidateSet("AppOnly","Browser","DeviceCode")] + [string]$AuthMode = "AppOnly", + + [string]$RedirectUri, + + [string]$SettingsFile, + + [switch]$WhatIf +) + +$ErrorActionPreference = "Stop" + +#region Helper functions +function Test-FzfAvailable +{ + return [bool](Get-Command fzf -ErrorAction SilentlyContinue) +} + +function Show-FzfMenu +{ + param( + [Parameter(Mandatory)] + [string[]]$Items, + [string]$Header = "Select one", + [switch]$Multi + ) + $argsList = @("--header=$Header") + if($Multi) { $argsList += "--multi" } + $selected = $Items | fzf @argsList + if(-not $selected) { return $null } + if($Multi) { return @($selected -split "`r?`n" | Where-Object { $_ }) } + return $selected +} + +function Show-NumberedMenu +{ + param( + [Parameter(Mandatory)] + [string[]]$Items, + [string]$Header = "Select one or more", + [switch]$Multi + ) + Write-Host "`n$Header" -ForegroundColor Cyan + for($i=0; $i -lt $Items.Count; $i++) + { + Write-Host " $($i+1). $($Items[$i])" + } + if($Multi) + { + $prompt = "Enter numbers separated by commas (e.g. 1,3,5) or 'all'" + } + else + { + $prompt = "Enter a number" + } + $choice = Read-Host $prompt + if($choice -eq "all" -and $Multi) { return $Items } + $indices = $choice -split "," | ForEach-Object { $_.Trim() } | Where-Object { $_ -match "^\d+$" } | ForEach-Object { [int]$_ - 1 } | Where-Object { $_ -ge 0 -and $_ -lt $Items.Count } + if($Multi) + { + return $Items[$indices] | Select-Object -Unique + } + else + { + if($indices.Count -eq 0) { return $null } + return $Items[$indices[0]] + } +} + +function Select-MenuItem +{ + param( + [Parameter(Mandatory)] + [string[]]$Items, + [string]$Header = "Select one", + [switch]$Multi + ) + if(Test-FzfAvailable) + { + return Show-FzfMenu -Items $Items -Header $Header -Multi:$Multi + } + return Show-NumberedMenu -Items $Items -Header $Header -Multi:$Multi +} + +function Get-DefaultSettingsPath +{ + if($IsWindows -or $env:OS -eq "Windows_NT") + { + if($env:LOCALAPPDATA) { return (Join-Path $env:LOCALAPPDATA "macOS_IntuneManagement\Settings.json") } + return (Join-Path $env:USERPROFILE "AppData\Local\macOS_IntuneManagement\Settings.json") + } + if($IsMacOS) { return (Join-Path $HOME "Library/Application Support/macOS_IntuneManagement/Settings.json") } + return (Join-Path $HOME ".local/share/macOS_IntuneManagement/Settings.json") +} +#endregion + +#region Initialize Runtime +$projectRoot = Split-Path -Parent $PSScriptRoot +$runtimeModule = Join-Path $projectRoot "Runtime/IntuneManagement.Runtime.psd1" +if(-not (Test-Path $runtimeModule)) +{ + throw "Could not find IntuneManagement.Runtime.psd1 in $projectRoot" +} + +$settingsPath = $SettingsFile +if(-not $settingsPath) +{ + $settingsPath = Get-DefaultSettingsPath +} + +# Pre-load auth from settings +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"] + } + } + + 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 { } +} + +$invokeParams = @{ + Silent = $true + JSonSettings = $true + JSonFile = $settingsPath + TenantId = $TenantId + AppId = $AppId + 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 +#endregion + +#region Ensure Graph connectivity +if(-not (Get-Command Invoke-GraphRequest -ErrorAction SilentlyContinue)) +{ + throw "Graph runtime did not load Invoke-GraphRequest. Aborting." +} + +Write-Host "`nConnecting to Microsoft Graph..." -ForegroundColor Cyan +try +{ + $org = Invoke-GraphRequest "/organization" + Write-Host "Connected to tenant: $($org.value[0].displayName) ($($org.value[0].id))" -ForegroundColor Green +} +catch +{ + throw "Failed to connect to Graph. Ensure auth parameters are correct. Error: $_" +} +#endregion + +#region Object type registry (deletable types) +$deletableTypes = @( + [PSCustomObject]@{ Title = "Applications"; API = "/deviceAppManagement/mobileApps"; NameProp = "displayName" }, + [PSCustomObject]@{ Title = "Device Configuration"; API = "/deviceManagement/deviceConfigurations"; NameProp = "displayName" }, + [PSCustomObject]@{ Title = "Settings Catalog"; API = "/deviceManagement/configurationPolicies"; NameProp = "name" }, + [PSCustomObject]@{ Title = "Compliance Policies"; API = "/deviceManagement/deviceCompliancePolicies"; NameProp = "displayName" }, + [PSCustomObject]@{ Title = "Administrative Templates"; API = "/deviceManagement/groupPolicyConfigurations"; NameProp = "displayName" }, + [PSCustomObject]@{ Title = "Endpoint Security"; API = "/deviceManagement/intents"; NameProp = "displayName" }, + [PSCustomObject]@{ Title = "App Protection"; API = "/deviceAppManagement/managedAppPolicies"; NameProp = "displayName" }, + [PSCustomObject]@{ Title = "App Configuration (Device)"; API = "/deviceAppManagement/mobileAppConfigurations"; NameProp = "displayName" }, + [PSCustomObject]@{ Title = "Platform Scripts"; API = "/deviceManagement/deviceManagementScripts"; NameProp = "displayName" }, + [PSCustomObject]@{ Title = "macOS Scripts"; API = "/deviceManagement/deviceShellScripts"; NameProp = "displayName" }, + [PSCustomObject]@{ Title = "Device Health Scripts"; API = "/deviceManagement/deviceHealthScripts"; NameProp = "displayName" }, + [PSCustomObject]@{ Title = "macOS Custom Attributes"; API = "/deviceManagement/deviceCustomAttributeShellScripts"; NameProp = "displayName" }, + [PSCustomObject]@{ Title = "Enrollment Restrictions"; API = "/deviceManagement/deviceEnrollmentConfigurations"; NameProp = "displayName" }, + [PSCustomObject]@{ Title = "Autopilot"; API = "/deviceManagement/windowsAutopilotDeploymentProfiles"; NameProp = "displayName" }, + [PSCustomObject]@{ Title = "Terms and Conditions"; API = "/deviceManagement/termsAndConditions"; NameProp = "displayName" }, + [PSCustomObject]@{ Title = "Policy Sets"; API = "/deviceAppManagement/policySets"; NameProp = "displayName" }, + [PSCustomObject]@{ Title = "Update Policies"; API = "/deviceManagement/windowsUpdateForBusinessConfigurations"; NameProp = "displayName" }, + [PSCustomObject]@{ Title = "Feature Updates"; API = "/deviceManagement/windowsFeatureUpdateProfiles"; NameProp = "displayName" }, + [PSCustomObject]@{ Title = "Quality Updates"; API = "/deviceManagement/windowsQualityUpdateProfiles"; NameProp = "displayName" }, + [PSCustomObject]@{ Title = "Device Management Intents"; API = "/deviceManagement/intents"; NameProp = "displayName" } +) +#endregion + +Clear-Host +Write-Host "========================================" -ForegroundColor Cyan +Write-Host " Intune Bulk Delete Tool" -ForegroundColor Cyan +Write-Host "========================================" -ForegroundColor Cyan + +#region Select object type +$typeTitles = $deletableTypes | ForEach-Object { $_.Title } +$selectedTypeTitle = Select-MenuItem -Items $typeTitles -Header "Select object type" +if(-not $selectedTypeTitle) { Write-Host "Cancelled." -ForegroundColor Yellow; exit 0 } +$objectType = $deletableTypes | Where-Object { $_.Title -eq $selectedTypeTitle } | Select-Object -First 1 +#endregion + +#region Load objects +Write-Host "`nLoading $($objectType.Title) objects..." -ForegroundColor Cyan +$api = "$($objectType.API)?`$select=id,$($objectType.NameProp)&`$orderby=$($objectType.NameProp)" +$objectsResponse = Invoke-GraphRequest $api -AllPages +$objects = $objectsResponse.value | Where-Object { $_ } | Sort-Object $objectType.NameProp +Write-Host "Found $($objects.Count) objects." -ForegroundColor Green + +$filter = Read-Host "`nFilter by current name (optional, press Enter to skip)" +if(-not [string]::IsNullOrWhiteSpace($filter)) +{ + $objects = $objects | Where-Object { $_."$($objectType.NameProp)" -like "*$filter*" } + Write-Host "Filtered to $($objects.Count) objects." -ForegroundColor Green +} + +if($objects.Count -eq 0) +{ + Write-Host "No objects found. Exiting." -ForegroundColor Yellow + exit 0 +} + +$objectDisplays = $objects | ForEach-Object { "$($_."$($objectType.NameProp)") [$($_.id)]" } +$selectedDisplays = Select-MenuItem -Items $objectDisplays -Header "Select objects to DELETE (multi-select)" -Multi +if(-not $selectedDisplays) +{ + Write-Host "No objects selected. Exiting." -ForegroundColor Yellow + exit 0 +} + +$selectedObjects = @() +foreach($disp in $selectedDisplays) +{ + $id = $disp -replace '.*\[(.*?)\]$', '$1' + $obj = $objects | Where-Object { $_.id -eq $id } | Select-Object -First 1 + if($obj) { $selectedObjects += $obj } +} +#endregion + +#region Confirm +Write-Host "`nThe following $($selectedObjects.Count) $($objectType.Title) object(s) will be PERMANENTLY DELETED:" -ForegroundColor Red +foreach($obj in $selectedObjects) +{ + Write-Host " - $($obj."$($objectType.NameProp)") [$($obj.id)]" -ForegroundColor DarkGray +} + +if(-not $WhatIf) +{ + $typed = Read-Host "`nThis cannot be undone. Type DELETE to confirm" + if($typed -cne "DELETE") + { + Write-Host "Confirmation text did not match. Cancelled." -ForegroundColor Yellow + exit 0 + } +} +#endregion + +#region Execute +$success = 0 +$failed = 0 + +foreach($obj in $selectedObjects) +{ + $name = $obj."$($objectType.NameProp)" + try + { + if($WhatIf) + { + Write-Host " WHATIF: Would delete $name [$($obj.id)]" -ForegroundColor Magenta + $success++ + } + else + { + $maxRetries = 3 + $retryDelay = 2 + $deleted = $false + for($r = 1; $r -le $maxRetries; $r++) + { + try + { + $null = Invoke-GraphRequest "$($objectType.API)/$($obj.id)" -HttpMethod DELETE + Write-Host " OK: Deleted '$name'" -ForegroundColor Green + $success++ + $deleted = $true + break + } + catch + { + $statusCode = $_.Exception.Response.StatusCode + if($r -lt $maxRetries -and ($statusCode -ge 500 -or $statusCode -eq 429)) + { + Write-Host " Retry $r/$maxRetries after $retryDelay`s (HTTP $statusCode)..." -ForegroundColor DarkYellow + Start-Sleep -Seconds $retryDelay + } + else + { + throw + } + } + } + if(-not $deleted) { throw "Delete failed after $maxRetries attempts." } + } + } + catch + { + Write-Host " ERROR: Failed to delete '$name'. $($_.Exception.Message)" -ForegroundColor Red + $failed++ + } +} + +Write-Host "`n========================================" -ForegroundColor Cyan +Write-Host " Bulk Delete Complete" -ForegroundColor Cyan +Write-Host "========================================" -ForegroundColor Cyan +Write-Host " Success : $success" +Write-Host " Failed : $failed" +#endregion diff --git a/Scripts/Private/Start-IntuneManagementTui.ps1 b/Scripts/Private/Start-IntuneManagementTui.ps1 index 740dbd5..a2a3f77 100644 --- a/Scripts/Private/Start-IntuneManagementTui.ps1 +++ b/Scripts/Private/Start-IntuneManagementTui.ps1 @@ -8,7 +8,9 @@ Uses fzf on macOS/Linux when available; falls back to numbered menus. #> [CmdletBinding()] -param() +param( + [string]$TenantId +) $ErrorActionPreference = "Stop" @@ -164,11 +166,19 @@ while($true) if($action -eq "DeployCISBaseline") { # 2a. TenantId - $tenantPrompt = "Enter Tenant ID" - if($preloadedTenantId) { $tenantPrompt += " (default: $preloadedTenantId)" } - $tenantId = Read-Host $tenantPrompt - if([string]::IsNullOrWhiteSpace($tenantId)) { $tenantId = $preloadedTenantId } - if([string]::IsNullOrWhiteSpace($tenantId)) { Write-Host "Tenant ID is required." -ForegroundColor Red; continue } + if($TenantId) + { + $tenantId = $TenantId + Write-Host "Tenant: $tenantId" -ForegroundColor DarkGray + } + else + { + $tenantPrompt = "Enter Tenant ID" + if($preloadedTenantId) { $tenantPrompt += " (default: $preloadedTenantId)" } + $tenantId = Read-Host $tenantPrompt + if([string]::IsNullOrWhiteSpace($tenantId)) { $tenantId = $preloadedTenantId } + if([string]::IsNullOrWhiteSpace($tenantId)) { Write-Host "Tenant ID is required." -ForegroundColor Red; continue } + } # 2b. Baseline path $defaultBaseline = Join-Path (Split-Path -Parent (Split-Path -Parent $PSScriptRoot)) "Baselines/CISM365-v7-Generated.yaml" @@ -243,11 +253,19 @@ while($true) if($dataSource -like "*fresh*") { - $tenantPrompt = "Enter Tenant ID" - if($preloadedTenantId) { $tenantPrompt += " (default: $preloadedTenantId)" } - $tenantIdForReport = Read-Host $tenantPrompt - if([string]::IsNullOrWhiteSpace($tenantIdForReport)) { $tenantIdForReport = $preloadedTenantId } - if([string]::IsNullOrWhiteSpace($tenantIdForReport)) { Write-Host "Tenant ID is required." -ForegroundColor Red; continue } + if($TenantId) + { + $tenantIdForReport = $TenantId + Write-Host "Tenant: $tenantIdForReport" -ForegroundColor DarkGray + } + else + { + $tenantPrompt = "Enter Tenant ID" + if($preloadedTenantId) { $tenantPrompt += " (default: $preloadedTenantId)" } + $tenantIdForReport = Read-Host $tenantPrompt + if([string]::IsNullOrWhiteSpace($tenantIdForReport)) { $tenantIdForReport = $preloadedTenantId } + if([string]::IsNullOrWhiteSpace($tenantIdForReport)) { Write-Host "Tenant ID is required." -ForegroundColor Red; continue } + } $exportPath = Read-Host "Export path (where to save fresh data)" if([string]::IsNullOrWhiteSpace($exportPath)) { Write-Host "Export path is required." -ForegroundColor Red; continue } @@ -312,11 +330,19 @@ while($true) } # 2. TenantId - $tenantPrompt = "Enter Tenant ID" - if($preloadedTenantId) { $tenantPrompt += " (default: $preloadedTenantId)" } - $tenantId = Read-Host $tenantPrompt - if([string]::IsNullOrWhiteSpace($tenantId)) { $tenantId = $preloadedTenantId } - if([string]::IsNullOrWhiteSpace($tenantId)) { Write-Host "Tenant ID is required." -ForegroundColor Red; continue } + if($TenantId) + { + $tenantId = $TenantId + Write-Host "`nTenant: $tenantId" -ForegroundColor DarkGray + } + else + { + $tenantPrompt = "Enter Tenant ID" + if($preloadedTenantId) { $tenantPrompt += " (default: $preloadedTenantId)" } + $tenantId = Read-Host $tenantPrompt + if([string]::IsNullOrWhiteSpace($tenantId)) { $tenantId = $preloadedTenantId } + if([string]::IsNullOrWhiteSpace($tenantId)) { Write-Host "Tenant ID is required." -ForegroundColor Red; continue } + } # 3. Object Types Write-Host "`nObject type selection..." -ForegroundColor Cyan @@ -329,7 +355,7 @@ $path = Read-Host $pathPrompt if([string]::IsNullOrWhiteSpace($path)) { Write-Host "Path is required." -ForegroundColor Red; return $null } # 5. Name Filter -$nameFilter = Read-Host "Name filter regex (optional, e.g. '^Win-OIB-')" +$nameFilter = Read-Host "Name filter, text to match anywhere in name, case-insensitive (optional, e.g. 'Win-OIB-')" # 6. Name Mutation $nameSearchPattern = Read-Host "Name search regex for mutation (optional, e.g. '^Win-OIB-')" diff --git a/Scripts/Start-HeadlessIntune.ps1 b/Scripts/Start-HeadlessIntune.ps1 index be6ec34..1376488 100644 --- a/Scripts/Start-HeadlessIntune.ps1 +++ b/Scripts/Start-HeadlessIntune.ps1 @@ -84,7 +84,7 @@ if($Interactive) $tuiScript = Join-Path (Split-Path -Parent $PSScriptRoot) "Scripts/Private/Start-IntuneManagementTui.ps1" if(Test-Path $tuiScript) { - $tuiResult = & $tuiScript + $tuiResult = & $tuiScript -TenantId $TenantId if(-not $tuiResult) { Write-Host "No selection made. Exiting." -ForegroundColor Yellow; exit 0 } foreach($prop in $tuiResult.PSObject.Properties) { diff --git a/Start-IntuneToolkit.ps1 b/Start-IntuneToolkit.ps1 index 8a0ac4b..6b9ae8b 100644 --- a/Start-IntuneToolkit.ps1 +++ b/Start-IntuneToolkit.ps1 @@ -249,7 +249,7 @@ if(-not $TenantId) $tenantOptions += "[Exit]" $selectedTenantDisplay = Select-MenuItem -Items $tenantOptions -Header "Select a tenant" - if(-not $selectedTenantDisplay -or $selectedTenantDisplay -eq "[Exit]") + if(-not $selectedTenantDisplay -or $selectedTenantDisplay -eq "[Exit]" -or $selectedTenantDisplay -eq "EXIT") { exit 0 } @@ -308,6 +308,7 @@ $commonParams = @{ } $menuItems = @( + "21. Bulk delete policies" "20. Export Entra role membership" "19. Document Conditional Access policies" "18. Rotate app secret" @@ -371,6 +372,7 @@ while($true) 6 { $script = "Scripts/Backup-Restore-Assignments.ps1" } 7 { $script = "Scripts/Export-AssignmentsToCsv.ps1" } 8 { $script = "Scripts/Bulk-RenamePolicies.ps1" } + 21 { $script = "Scripts/Bulk-DeletePolicies.ps1" } 9 { $script = "Scripts/Bulk-DeviceOperations.ps1" } 10 { $script = "Scripts/Deploy-IntuneBaseline.ps1" } 11 { $script = "Scripts/Deploy-IntuneBaseline.ps1" } diff --git a/VERSION b/VERSION index 6aba2b2..8089590 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -4.2.0 +4.3.0