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 <noreply@anthropic.com>
This commit is contained in:
2026-08-07 11:31:22 +02:00
parent 2f0d937ca1
commit ae71f8458d
7 changed files with 421 additions and 21 deletions
+17
View File
@@ -1,5 +1,22 @@
# macOS Intune Toolkit Changelog # 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 ## 2026-06-23 — v4.2.0 — Entra directory role membership export
### Added ### Added
+2 -1
View File
@@ -2,7 +2,7 @@
Cross-platform, headless Intune policy export/import with PowerShell. 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: 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. * **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. * **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 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. * **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. * **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. * **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.
+354
View File
@@ -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
+43 -17
View File
@@ -8,7 +8,9 @@
Uses fzf on macOS/Linux when available; falls back to numbered menus. Uses fzf on macOS/Linux when available; falls back to numbered menus.
#> #>
[CmdletBinding()] [CmdletBinding()]
param() param(
[string]$TenantId
)
$ErrorActionPreference = "Stop" $ErrorActionPreference = "Stop"
@@ -164,11 +166,19 @@ while($true)
if($action -eq "DeployCISBaseline") if($action -eq "DeployCISBaseline")
{ {
# 2a. TenantId # 2a. TenantId
$tenantPrompt = "Enter Tenant ID" if($TenantId)
if($preloadedTenantId) { $tenantPrompt += " (default: $preloadedTenantId)" } {
$tenantId = Read-Host $tenantPrompt $tenantId = $TenantId
if([string]::IsNullOrWhiteSpace($tenantId)) { $tenantId = $preloadedTenantId } Write-Host "Tenant: $tenantId" -ForegroundColor DarkGray
if([string]::IsNullOrWhiteSpace($tenantId)) { Write-Host "Tenant ID is required." -ForegroundColor Red; continue } }
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 # 2b. Baseline path
$defaultBaseline = Join-Path (Split-Path -Parent (Split-Path -Parent $PSScriptRoot)) "Baselines/CISM365-v7-Generated.yaml" $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*") if($dataSource -like "*fresh*")
{ {
$tenantPrompt = "Enter Tenant ID" if($TenantId)
if($preloadedTenantId) { $tenantPrompt += " (default: $preloadedTenantId)" } {
$tenantIdForReport = Read-Host $tenantPrompt $tenantIdForReport = $TenantId
if([string]::IsNullOrWhiteSpace($tenantIdForReport)) { $tenantIdForReport = $preloadedTenantId } Write-Host "Tenant: $tenantIdForReport" -ForegroundColor DarkGray
if([string]::IsNullOrWhiteSpace($tenantIdForReport)) { Write-Host "Tenant ID is required." -ForegroundColor Red; continue } }
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)" $exportPath = Read-Host "Export path (where to save fresh data)"
if([string]::IsNullOrWhiteSpace($exportPath)) { Write-Host "Export path is required." -ForegroundColor Red; continue } if([string]::IsNullOrWhiteSpace($exportPath)) { Write-Host "Export path is required." -ForegroundColor Red; continue }
@@ -312,11 +330,19 @@ while($true)
} }
# 2. TenantId # 2. TenantId
$tenantPrompt = "Enter Tenant ID" if($TenantId)
if($preloadedTenantId) { $tenantPrompt += " (default: $preloadedTenantId)" } {
$tenantId = Read-Host $tenantPrompt $tenantId = $TenantId
if([string]::IsNullOrWhiteSpace($tenantId)) { $tenantId = $preloadedTenantId } Write-Host "`nTenant: $tenantId" -ForegroundColor DarkGray
if([string]::IsNullOrWhiteSpace($tenantId)) { Write-Host "Tenant ID is required." -ForegroundColor Red; continue } }
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 # 3. Object Types
Write-Host "`nObject type selection..." -ForegroundColor Cyan 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 } if([string]::IsNullOrWhiteSpace($path)) { Write-Host "Path is required." -ForegroundColor Red; return $null }
# 5. Name Filter # 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 # 6. Name Mutation
$nameSearchPattern = Read-Host "Name search regex for mutation (optional, e.g. '^Win-OIB-')" $nameSearchPattern = Read-Host "Name search regex for mutation (optional, e.g. '^Win-OIB-')"
+1 -1
View File
@@ -84,7 +84,7 @@ if($Interactive)
$tuiScript = Join-Path (Split-Path -Parent $PSScriptRoot) "Scripts/Private/Start-IntuneManagementTui.ps1" $tuiScript = Join-Path (Split-Path -Parent $PSScriptRoot) "Scripts/Private/Start-IntuneManagementTui.ps1"
if(Test-Path $tuiScript) if(Test-Path $tuiScript)
{ {
$tuiResult = & $tuiScript $tuiResult = & $tuiScript -TenantId $TenantId
if(-not $tuiResult) { Write-Host "No selection made. Exiting." -ForegroundColor Yellow; exit 0 } if(-not $tuiResult) { Write-Host "No selection made. Exiting." -ForegroundColor Yellow; exit 0 }
foreach($prop in $tuiResult.PSObject.Properties) foreach($prop in $tuiResult.PSObject.Properties)
{ {
+3 -1
View File
@@ -249,7 +249,7 @@ if(-not $TenantId)
$tenantOptions += "[Exit]" $tenantOptions += "[Exit]"
$selectedTenantDisplay = Select-MenuItem -Items $tenantOptions -Header "Select a tenant" $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 exit 0
} }
@@ -308,6 +308,7 @@ $commonParams = @{
} }
$menuItems = @( $menuItems = @(
"21. Bulk delete policies"
"20. Export Entra role membership" "20. Export Entra role membership"
"19. Document Conditional Access policies" "19. Document Conditional Access policies"
"18. Rotate app secret" "18. Rotate app secret"
@@ -371,6 +372,7 @@ while($true)
6 { $script = "Scripts/Backup-Restore-Assignments.ps1" } 6 { $script = "Scripts/Backup-Restore-Assignments.ps1" }
7 { $script = "Scripts/Export-AssignmentsToCsv.ps1" } 7 { $script = "Scripts/Export-AssignmentsToCsv.ps1" }
8 { $script = "Scripts/Bulk-RenamePolicies.ps1" } 8 { $script = "Scripts/Bulk-RenamePolicies.ps1" }
21 { $script = "Scripts/Bulk-DeletePolicies.ps1" }
9 { $script = "Scripts/Bulk-DeviceOperations.ps1" } 9 { $script = "Scripts/Bulk-DeviceOperations.ps1" }
10 { $script = "Scripts/Deploy-IntuneBaseline.ps1" } 10 { $script = "Scripts/Deploy-IntuneBaseline.ps1" }
11 { $script = "Scripts/Deploy-IntuneBaseline.ps1" } 11 { $script = "Scripts/Deploy-IntuneBaseline.ps1" }
+1 -1
View File
@@ -1 +1 @@
4.2.0 4.3.0