Views/Apps.ps1
|
function Show-InTUIAppsView { <# .SYNOPSIS Displays the Apps management view mimicking the Intune Apps blade. #> [CmdletBinding()] param() $exitView = $false while (-not $exitView) { Clear-Host Show-InTUIHeader Show-InTUIBreadcrumb -Path @('Home', 'Apps') $appChoices = @( 'All Apps', 'Windows Apps', 'iOS/iPadOS Apps', 'macOS Apps', 'Android Apps', 'Web Apps', 'Microsoft 365 Apps', 'App Install Status Monitor', 'Bulk Assign Apps', 'Search Apps', '-------------', 'Back to Home' ) $selection = Show-InTUIMenu -Title "[green]Apps[/]" -Choices $appChoices Write-InTUILog -Message "Apps view selection" -Context @{ Selection = $selection } switch ($selection) { 'All Apps' { Show-InTUIAppList } 'Windows Apps' { Show-InTUIAppList -PlatformFilter 'windows' } 'iOS/iPadOS Apps' { Show-InTUIAppList -PlatformFilter 'ios' } 'macOS Apps' { Show-InTUIAppList -PlatformFilter 'macos' } 'Android Apps' { Show-InTUIAppList -PlatformFilter 'android' } 'Web Apps' { Show-InTUIAppList -TypeFilter 'webApp' } 'Microsoft 365 Apps' { Show-InTUIAppList -TypeFilter 'officeSuite' } 'App Install Status Monitor' { Show-InTUIAppInstallStatusMonitor } 'Bulk Assign Apps' { Invoke-InTUIBulkAppAssignment } 'Search Apps' { $searchTerm = Read-InTUITextInput -Message "[green]Search apps by name[/]" if ($searchTerm) { Write-InTUILog -Message "Searching apps" -Context @{ SearchTerm = $searchTerm } Show-InTUIAppList -SearchTerm $searchTerm } } 'Back to Home' { $exitView = $true } default { continue } } } } function Show-InTUIAppList { <# .SYNOPSIS Displays a list of managed apps with filtering. #> [CmdletBinding()] param( [Parameter()] [string]$PlatformFilter, [Parameter()] [string]$TypeFilter, [Parameter()] [string]$SearchTerm ) $exitList = $false while (-not $exitList) { Clear-Host Show-InTUIHeader $breadcrumb = @('Home', 'Apps') if ($PlatformFilter) { $breadcrumb += "$PlatformFilter Apps" } elseif ($TypeFilter) { $breadcrumb += "$TypeFilter" } elseif ($SearchTerm) { $breadcrumb += "Search: $SearchTerm" } else { $breadcrumb += 'All Apps' } Show-InTUIBreadcrumb -Path $breadcrumb $filter = @() if ($SearchTerm) { $safe = ConvertTo-InTUISafeFilterValue -Value $SearchTerm $filter += "contains(displayName,'$safe')" } elseif ($PlatformFilter) { switch ($PlatformFilter) { 'windows' { $filter += "(isof('microsoft.graph.win32LobApp') or isof('microsoft.graph.windowsMobileMSI') or isof('microsoft.graph.windowsUniversalAppX') or isof('microsoft.graph.windowsMicrosoftEdgeApp') or isof('microsoft.graph.windowsStoreApp'))" } 'ios' { $filter += "(isof('microsoft.graph.iosVppApp') or isof('microsoft.graph.iosStoreApp') or isof('microsoft.graph.iosLobApp') or isof('microsoft.graph.managedIOSStoreApp'))" } 'macos' { $filter += "(isof('microsoft.graph.macOSLobApp') or isof('microsoft.graph.macOSDmgApp') or isof('microsoft.graph.macOSMicrosoftEdgeApp'))" } 'android' { $filter += "(isof('microsoft.graph.androidStoreApp') or isof('microsoft.graph.androidLobApp') or isof('microsoft.graph.managedAndroidStoreApp') or isof('microsoft.graph.androidManagedStoreApp'))" } } } elseif ($TypeFilter -eq 'webApp') { $filter += "isof('microsoft.graph.webApp')" } elseif ($TypeFilter -eq 'officeSuite') { $filter += "isof('microsoft.graph.officeSuiteApp')" } $params = @{ Uri = '/deviceAppManagement/mobileApps' Beta = $true PageSize = 25 Select = 'id,displayName,description,publisher,createdDateTime,lastModifiedDateTime' } if ($filter.Count -gt 0) { $params['Filter'] = $filter -join ' and ' } $apps = Show-InTUILoading -Title "[green]Loading apps...[/]" -ScriptBlock { Get-InTUIPagedResults @params } if ($null -eq $apps -or $apps.Results.Count -eq 0) { Show-InTUIWarning "No apps found." Read-InTUIKey $exitList = $true continue } $appChoices = @() foreach ($app in $apps.Results) { $appType = Get-InTUIAppTypeFriendlyName -ODataType $app.'@odata.type' $modified = Format-InTUIDate -DateString $app.lastModifiedDateTime $publisher = if ($app.publisher) { $app.publisher } else { 'Unknown' } $displayName = "[white]$(ConvertTo-InTUISafeMarkup -Text $app.displayName)[/] [grey]| $appType | $publisher | $modified[/]" $appChoices += $displayName } $choiceMap = Get-InTUIChoiceMap -Choices $appChoices $menuChoices = @($choiceMap.Choices + '─────────────' + 'Back') Show-InTUIStatusBar -Total $apps.TotalCount -Showing $apps.Results.Count $selection = Show-InTUIMenu -Title "[green]Select an app[/]" -Choices $menuChoices if ($selection -eq 'Back') { $exitList = $true } elseif ($selection -ne '─────────────') { $idx = $choiceMap.IndexMap[$selection] if ($null -ne $idx -and $idx -lt $apps.Results.Count) { Show-InTUIAppDetail -AppId $apps.Results[$idx].id } } } } function Get-InTUIAppTypeFriendlyName { <# .SYNOPSIS Converts OData type to friendly app type name. #> param([string]$ODataType) switch -Wildcard ($ODataType) { '*win32LobApp' { return 'Win32' } '*windowsMobileMSI' { return 'MSI' } '*windowsUniversalAppX' { return 'APPX/MSIX' } '*windowsMicrosoftEdgeApp' { return 'Edge' } '*windowsStoreApp' { return 'Store (Win)' } '*officeSuiteApp' { return 'M365 Apps' } '*iosVppApp' { return 'iOS VPP' } '*managedIOSStoreApp' { return 'iOS Managed' } '*iosStoreApp' { return 'iOS Store' } '*iosLobApp' { return 'iOS LOB' } '*macOSLobApp' { return 'macOS LOB' } '*macOSDmgApp' { return 'macOS DMG' } '*macOSMicrosoftEdgeApp' { return 'macOS Edge' } '*managedAndroidStoreApp' { return 'Android Managed' } '*androidManagedStoreApp' { return 'Managed Google Play' } '*androidStoreApp' { return 'Android Store' } '*androidLobApp' { return 'Android LOB' } '*webApp' { return 'Web App' } '*microsoftStoreForBusinessApp' { return 'Store for Business' } default { return ($ODataType -replace '#microsoft\.graph\.', '') } } } function Show-InTUIAppDetail { <# .SYNOPSIS Displays detailed information about a specific app. #> [CmdletBinding()] param( [Parameter(Mandatory)] [string]$AppId ) $exitDetail = $false while (-not $exitDetail) { Clear-Host Show-InTUIHeader $app = Show-InTUILoading -Title "[green]Loading app details...[/]" -ScriptBlock { Invoke-InTUIGraphRequest -Uri "/deviceAppManagement/mobileApps/$AppId" -Beta } if ($null -eq $app) { Show-InTUIError "Failed to load app details." Read-InTUIKey return } Show-InTUIBreadcrumb -Path @('Home', 'Apps', $app.displayName) Add-InTUIHistoryEntry -ViewType 'App' -ViewId $AppId -DisplayName $app.displayName $appType = Get-InTUIAppTypeFriendlyName -ODataType $app.'@odata.type' $propsContent = @" [bold white]$(ConvertTo-InTUISafeMarkup -Text $app.displayName)[/] [grey]Type:[/] $appType [grey]Publisher:[/] $($app.publisher ?? 'N/A') [grey]Description:[/] $(if ($app.description) { $app.description.Substring(0, [Math]::Min(200, $app.description.Length)) } else { 'N/A' }) [grey]Created:[/] $(Format-InTUIDate -DateString $app.createdDateTime) [grey]Last Modified:[/] $(Format-InTUIDate -DateString $app.lastModifiedDateTime) [grey]Is Featured:[/] $($app.isFeatured ?? $false) [grey]Privacy URL:[/] $($app.privacyInformationUrl ?? 'N/A') [grey]Info URL:[/] $($app.informationUrl ?? 'N/A') [grey]Owner:[/] $($app.owner ?? 'N/A') [grey]Developer:[/] $($app.developer ?? 'N/A') [grey]Notes:[/] $($app.notes ?? 'N/A') "@ Show-InTUIPanel -Title "[green]App Properties[/]" -Content $propsContent -BorderColor Green if ($app.'@odata.type' -match 'win32LobApp') { $win32Content = @" [grey]File Name:[/] $($app.fileName ?? 'N/A') [grey]Install Command:[/] $($app.installCommandLine ?? 'N/A') [grey]Uninstall Command:[/] $($app.uninstallCommandLine ?? 'N/A') [grey]Setup File Path:[/] $($app.setupFilePath ?? 'N/A') [grey]Min OS Version:[/] $($app.minimumSupportedOperatingSystem ?? 'N/A') "@ Show-InTUIPanel -Title "[cyan]Win32 App Details[/]" -Content $win32Content -BorderColor Cyan1 } $actionChoices = @( 'View Assignments', 'Create Assignment', 'View Device Install Status', 'View User Install Status', '─────────────', 'Back to Apps' ) $action = Show-InTUIMenu -Title "[green]App Actions[/]" -Choices $actionChoices Write-InTUILog -Message "App detail action" -Context @{ AppId = $AppId; AppName = $app.displayName; Action = $action } switch ($action) { 'View Assignments' { Show-InTUIAppAssignments -AppId $AppId -AppName $app.displayName } 'Create Assignment' { New-InTUIAppAssignment -AppId $AppId -AppName $app.displayName } 'View Device Install Status' { Show-InTUIAppDeviceStatus -AppId $AppId -AppName $app.displayName } 'View User Install Status' { Show-InTUIAppUserStatus -AppId $AppId -AppName $app.displayName } 'Back to Apps' { $exitDetail = $true } default { continue } } } } function Select-InTUIGroup { <# .SYNOPSIS Displays a group picker and returns the selected group. #> [CmdletBinding()] param( [Parameter()] [string]$Title = "Select a group" ) $groups = Show-InTUILoading -Title "[green]Loading groups...[/]" -ScriptBlock { Get-InTUIPagedResults -Uri '/groups' -PageSize 50 -Select 'id,displayName,description' } if ($null -eq $groups -or $groups.Results.Count -eq 0) { Show-InTUIWarning "No groups found." return $null } $groupChoices = @() foreach ($group in $groups.Results) { $desc = if ($group.description) { $group.description.Substring(0, [Math]::Min(50, $group.description.Length)) } else { 'No description' } $groupChoices += "[white]$(ConvertTo-InTUISafeMarkup -Text $group.displayName)[/] [grey]| $desc[/]" } $choiceMap = Get-InTUIChoiceMap -Choices $groupChoices $menuChoices = @($choiceMap.Choices + '─────────────' + 'Cancel') $selection = Show-InTUIMenu -Title "[green]$Title[/]" -Choices $menuChoices if ($selection -eq 'Cancel' -or $selection -eq '─────────────') { return $null } $idx = $choiceMap.IndexMap[$selection] if ($null -ne $idx -and $idx -lt $groups.Results.Count) { return $groups.Results[$idx] } return $null } function New-InTUIAppAssignment { <# .SYNOPSIS Creates a new app assignment. #> [CmdletBinding()] param( [Parameter(Mandatory)] [string]$AppId, [Parameter()] [string]$AppName ) Clear-Host Show-InTUIHeader Show-InTUIBreadcrumb -Path @('Home', 'Apps', $AppName, 'Create Assignment') # Select intent $intentChoices = @( 'Required (auto-install)', 'Available (user choice)', 'Uninstall', '─────────────', 'Cancel' ) $intentSelection = Show-InTUIMenu -Title "[green]Select Assignment Intent[/]" -Choices $intentChoices if ($intentSelection -eq 'Cancel' -or $intentSelection -eq '─────────────') { return } $intent = switch ($intentSelection) { 'Required (auto-install)' { 'required' } 'Available (user choice)' { 'available' } 'Uninstall' { 'uninstall' } } # Select target type $targetChoices = @( 'All Users', 'All Devices', 'Select Group', '─────────────', 'Cancel' ) $targetSelection = Show-InTUIMenu -Title "[green]Select Target[/]" -Choices $targetChoices if ($targetSelection -eq 'Cancel' -or $targetSelection -eq '─────────────') { return } $group = $null $target = $null $targetDisplay = $null switch ($targetSelection) { 'All Users' { $target = @{ '@odata.type' = '#microsoft.graph.allLicensedUsersAssignmentTarget' } $targetDisplay = 'All Users' } 'All Devices' { $target = @{ '@odata.type' = '#microsoft.graph.allDevicesAssignmentTarget' } $targetDisplay = 'All Devices' } 'Select Group' { $group = Select-InTUIGroup -Title "Select target group" if (-not $group) { return } $target = @{ '@odata.type' = '#microsoft.graph.groupAssignmentTarget' groupId = $group.id } $targetDisplay = "Group: $(ConvertTo-InTUISafeMarkup -Text $group.displayName)" } } $confirm = Show-InTUIConfirm -Message "[yellow]Create $intent assignment for $AppName to $targetDisplay?[/]" if (-not $confirm) { return } # Create assignment $body = @{ mobileAppAssignments = @( @{ '@odata.type' = '#microsoft.graph.mobileAppAssignment' intent = $intent target = $target settings = $null } ) } Write-InTUILog -Message "Creating app assignment" -Context @{ AppId = $AppId AppName = $AppName Intent = $intent Target = $targetDisplay } $result = Show-InTUILoading -Title "[green]Creating assignment...[/]" -ScriptBlock { Invoke-InTUIGraphRequest -Uri "/deviceAppManagement/mobileApps/$AppId/assign" -Method POST -Body $body -Beta } if ($null -ne $result) { Show-InTUISuccess "Assignment created successfully." } else { Show-InTUIError "Failed to create assignment." } Read-InTUIKey } function Show-InTUIAppAssignments { <# .SYNOPSIS Displays app assignments. #> [CmdletBinding()] param( [Parameter(Mandatory)] [string]$AppId, [Parameter()] [string]$AppName ) Clear-Host Show-InTUIHeader Show-InTUIBreadcrumb -Path @('Home', 'Apps', $AppName, 'Assignments') $assignments = Show-InTUILoading -Title "[green]Loading assignments...[/]" -ScriptBlock { Invoke-InTUIGraphRequest -Uri "/deviceAppManagement/mobileApps/$AppId/assignments" -Beta } if (-not $assignments.value) { Show-InTUIWarning "No assignments found for this app." Read-InTUIKey return } $rows = @() $groupAssignments = @() foreach ($assignment in $assignments.value) { $intent = switch ($assignment.intent) { 'required' { '[green]Required[/]' } 'available' { '[blue]Available[/]' } 'uninstall' { '[red]Uninstall[/]' } 'availableWithoutEnrollment' { '[yellow]Available (No Enrollment)[/]' } default { $assignment.intent } } $targetType = switch ($assignment.target.'@odata.type') { '#microsoft.graph.allLicensedUsersAssignmentTarget' { 'All Users' } '#microsoft.graph.allDevicesAssignmentTarget' { 'All Devices' } '#microsoft.graph.groupAssignmentTarget' { "Group: $($assignment.target.groupId)" } '#microsoft.graph.exclusionGroupAssignmentTarget' { "[red]Exclude:[/] $($assignment.target.groupId)" } default { $assignment.target.'@odata.type' -replace '#microsoft\.graph\.', '' } } $rows += , @($intent, $targetType) # Track group assignments for cross-reference if ($assignment.target.groupId) { $groupAssignments += $assignment.target.groupId } } Render-InTUITable -Title "App Assignments" -Columns @('Intent', 'Target') -Rows $rows if ($groupAssignments.Count -gt 0) { $groupChoices = @() foreach ($gId in ($groupAssignments | Sort-Object -Unique)) { $groupChoices += $gId } $groupChoices += 'Back' $selection = Show-InTUIMenu -Title "[green]View group detail[/]" -Choices $groupChoices if ($selection -ne 'Back') { Show-InTUIGroupDetail -GroupId $selection } } else { Read-InTUIKey } } function Show-InTUIAppDeviceStatus { <# .SYNOPSIS Shows app device install statuses. #> [CmdletBinding()] param( [Parameter(Mandatory)] [string]$AppId, [Parameter()] [string]$AppName ) Clear-Host Show-InTUIHeader Show-InTUIBreadcrumb -Path @('Home', 'Apps', $AppName, 'Device Install Status') $statuses = Show-InTUILoading -Title "[green]Loading device install status...[/]" -ScriptBlock { Invoke-InTUIGraphRequest -Uri "/deviceAppManagement/mobileApps/$AppId/deviceStatuses?`$top=50" -Beta } if (-not $statuses.value) { Show-InTUIWarning "No device install status data available." Read-InTUIKey return } $rows = @() foreach ($status in $statuses.value) { $installColor = Get-InTUIInstallStateColor -State $status.installState $rows += , @( ($status.deviceName ?? 'N/A'), "[$installColor]$($status.installState)[/]", ($status.userPrincipalName ?? 'N/A'), ($status.osVersion ?? 'N/A'), (Format-InTUIDate -DateString $status.lastSyncDateTime) ) } Show-InTUITable -Title "Device Install Status" -Columns @('Device', 'Status', 'User', 'OS Version', 'Last Sync') -Rows $rows Read-InTUIKey } function Show-InTUIAppUserStatus { <# .SYNOPSIS Shows app user install statuses. #> [CmdletBinding()] param( [Parameter(Mandatory)] [string]$AppId, [Parameter()] [string]$AppName ) Clear-Host Show-InTUIHeader Show-InTUIBreadcrumb -Path @('Home', 'Apps', $AppName, 'User Install Status') $statuses = Show-InTUILoading -Title "[green]Loading user install status...[/]" -ScriptBlock { Invoke-InTUIGraphRequest -Uri "/deviceAppManagement/mobileApps/$AppId/userStatuses?`$top=50" -Beta } if (-not $statuses.value) { Show-InTUIWarning "No user install status data available." Read-InTUIKey return } $rows = @() foreach ($status in $statuses.value) { $rows += , @( ($status.userPrincipalName ?? $status.userName ?? 'N/A'), ($status.installedDeviceCount ?? 0), ($status.failedDeviceCount ?? 0), ($status.notInstalledDeviceCount ?? 0) ) } Show-InTUITable -Title "User Install Status" -Columns @('User', 'Installed', 'Failed', 'Not Installed') -Rows $rows Read-InTUIKey } function Show-InTUIAppInstallStatusMonitor { <# .SYNOPSIS Shows overview of app install status across all apps. #> [CmdletBinding()] param() Clear-Host Show-InTUIHeader Show-InTUIBreadcrumb -Path @('Home', 'Apps', 'Install Status Monitor') $apps = Show-InTUILoading -Title "[green]Loading app status overview...[/]" -ScriptBlock { Get-InTUIPagedResults -Uri '/deviceAppManagement/mobileApps' -Beta -PageSize 25 -Select 'id,displayName' } if ($null -eq $apps -or $apps.Results.Count -eq 0) { Show-InTUIWarning "No apps found." Read-InTUIKey return } Write-InTUIText "[bold]App Install Status Summary[/]" Write-InTUIText "[grey]Select an app to view detailed install status[/]" Write-Host "" $choices = @() foreach ($app in $apps.Results) { $appType = Get-InTUIAppTypeFriendlyName -ODataType $app.'@odata.type' $choices += "$(ConvertTo-InTUISafeMarkup -Text $app.displayName) [grey]($appType)[/]" } $choiceMap = Get-InTUIChoiceMap -Choices $choices $menuChoices = @($choiceMap.Choices + '─────────────' + 'Back') $selection = Show-InTUIMenu -Title "[green]Select app for status[/]" -Choices $menuChoices if ($selection -eq 'Back' -or $selection -eq '─────────────') { return } $idx = $choiceMap.IndexMap[$selection] if ($null -ne $idx -and $idx -lt $apps.Results.Count) { Show-InTUIAppDeviceStatus -AppId $apps.Results[$idx].id -AppName $apps.Results[$idx].displayName } } function Invoke-InTUIBulkAppAssignment { <# .SYNOPSIS Assigns multiple apps to a group in bulk. #> [CmdletBinding()] param() Clear-Host Show-InTUIHeader Show-InTUIBreadcrumb -Path @('Home', 'Apps', 'Bulk Assign Apps') # Load apps $apps = Show-InTUILoading -Title "[green]Loading apps...[/]" -ScriptBlock { Get-InTUIPagedResults -Uri '/deviceAppManagement/mobileApps' -Beta -PageSize 50 -Select 'id,displayName' } if ($null -eq $apps -or $apps.Results.Count -eq 0) { Show-InTUIWarning "No apps found." Read-InTUIKey return } Write-InTUIText "[bold]Bulk App Assignment[/]" Write-InTUIText "[grey]Select multiple apps to assign to a group[/]" Write-Host "" # Multi-select apps $appChoices = @() foreach ($app in $apps.Results) { $appType = Get-InTUIAppTypeFriendlyName -ODataType $app.'@odata.type' $appChoices += "$(ConvertTo-InTUISafeMarkup -Text $app.displayName) [grey]($appType)[/]" } $selectedApps = Show-InTUIMultiSelect -Title "[green]Select apps (Space to select, Enter to confirm)[/]" -Choices $appChoices -PageSize 15 if (-not $selectedApps -or $selectedApps.Count -eq 0) { Show-InTUIWarning "No apps selected." Read-InTUIKey return } # Map selections back to app objects $selectedAppObjects = @() foreach ($selection in $selectedApps) { for ($i = 0; $i -lt $appChoices.Count; $i++) { if ($appChoices[$i] -eq $selection) { $selectedAppObjects += $apps.Results[$i] break } } } Write-Host "" Write-InTUIText "[white]Selected $($selectedAppObjects.Count) app(s)[/]" # Select intent $intentChoices = @( 'Required (auto-install)', 'Available (user choice)', 'Uninstall', '─────────────', 'Cancel' ) $intentSelection = Show-InTUIMenu -Title "[green]Select Assignment Intent[/]" -Choices $intentChoices if ($intentSelection -eq 'Cancel' -or $intentSelection -eq '─────────────') { return } $intent = switch ($intentSelection) { 'Required (auto-install)' { 'required' } 'Available (user choice)' { 'available' } 'Uninstall' { 'uninstall' } } # Select target $targetChoices = @( 'All Users', 'All Devices', 'Select Group', '─────────────', 'Cancel' ) $targetSelection = Show-InTUIMenu -Title "[green]Select Target[/]" -Choices $targetChoices if ($targetSelection -eq 'Cancel' -or $targetSelection -eq '─────────────') { return } $group = $null $target = $null $targetDisplay = $null switch ($targetSelection) { 'All Users' { $target = @{ '@odata.type' = '#microsoft.graph.allLicensedUsersAssignmentTarget' } $targetDisplay = 'All Users' } 'All Devices' { $target = @{ '@odata.type' = '#microsoft.graph.allDevicesAssignmentTarget' } $targetDisplay = 'All Devices' } 'Select Group' { $group = Select-InTUIGroup -Title "Select target group" if (-not $group) { return } $target = @{ '@odata.type' = '#microsoft.graph.groupAssignmentTarget' groupId = $group.id } $targetDisplay = "Group: $(ConvertTo-InTUISafeMarkup -Text $group.displayName)" } } # Confirm $appNames = ($selectedAppObjects | ForEach-Object { $_.displayName }) -join ", " if ($appNames.Length -gt 100) { $appNames = $appNames.Substring(0, 100) + "..." } $confirm = Show-InTUIConfirm -Message "[yellow]Create $intent assignments for $($selectedAppObjects.Count) apps to $targetDisplay?[/]" if (-not $confirm) { return } # Execute bulk assignment Write-InTUILog -Message "Starting bulk app assignment" -Context @{ AppCount = $selectedAppObjects.Count Intent = $intent Target = $targetDisplay } $successCount = 0 $failCount = 0 $body = @{ mobileAppAssignments = @( @{ '@odata.type' = '#microsoft.graph.mobileAppAssignment' intent = $intent target = $target settings = $null } ) } foreach ($app in $selectedAppObjects) { $result = Invoke-InTUIGraphRequest -Uri "/deviceAppManagement/mobileApps/$($app.id)/assign" -Method POST -Body $body -Beta if ($null -ne $result) { $successCount++ } else { $failCount++ Write-InTUILog -Level 'WARN' -Message "Failed to assign app" -Context @{ AppName = $app.displayName } } } Write-InTUILog -Message "Bulk assignment completed" -Context @{ Success = $successCount Failed = $failCount } if ($failCount -eq 0) { Show-InTUISuccess "Successfully assigned $successCount app(s) to $targetDisplay." } else { Show-InTUIWarning "Assigned $successCount app(s), $failCount failed." } Read-InTUIKey } |