# ------- Helpers ------- function Invoke-Remote { param( [Parameter(Mandatory)][string]$Computer, [Parameter(Mandatory)][scriptblock]$ScriptBlock, [Parameter()][object[]]$ArgumentList ) try { return Invoke-Command -ComputerName $Computer -ScriptBlock $ScriptBlock -ArgumentList $ArgumentList -ErrorAction Stop } catch { $currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name Write-Warning "Implicit auth to '$Computer' failed: $($_.Exception.Message)" $cred = Get-Credential -UserName $currentUser -Message "Enter the password for $currentUser" return Invoke-Command -ComputerName $Computer -Credential $cred -ScriptBlock $ScriptBlock -ArgumentList $ArgumentList -ErrorAction Stop } } function Remove-ProvisionedAndExisting { <# .SYNOPSIS Preview + remove provisioned and installed Appx packages, locally or remotely. .PARAMETER Pattern -Like pattern, e.g. 'Microsoft.MSPaint*' .PARAMETER Computer If provided, runs on remote via Invoke-Remote; otherwise runs locally. #> param( [Parameter(Mandatory)][string]$Pattern, [string]$Computer ) # Scriptblocks reused for both local and remote $sbPreview = { param($p) $prov = Get-AppxProvisionedPackage -Online | Where-Object DisplayName -Like $p | Select-Object @{n='Type';e={'Provisioned'}}, DisplayName, PackageName $inst = Get-AppxPackage -AllUsers | Where-Object Name -Like $p | Select-Object @{n='Type';e={'Installed'}}, Name, PackageFullName [PSCustomObject]@{ Provisioned = $prov; Installed = $inst } } $sbRemove = { param($p) $errs = @() # Remove from provisioned image (new profiles) try { Get-AppxProvisionedPackage -Online | Where-Object DisplayName -Like $p | Remove-AppxProvisionedPackage -Online -ErrorAction Stop | Out-Null Write-Host "Removed provisioned packages matching '$p'." } catch { $errs += "Provisioned removal: $($_.Exception.Message)" } # Remove from existing users try { $pkgs = Get-AppxPackage -AllUsers | Where-Object Name -Like $p foreach ($pkg in $pkgs) { try { Remove-AppxPackage -Package $pkg.PackageFullName -AllUsers -ErrorAction Stop Write-Host "Removed installed package: $($pkg.PackageFullName)" } catch { # Fallback on older systems without -AllUsers support try { Get-AppxPackage -AllUsers | Where-Object Name -eq $pkg.Name | ForEach-Object { Remove-AppxPackage -Package $_.PackageFullName -ErrorAction Stop } Write-Host "Removed per-user instances of: $($pkg.Name)" } catch { $errs += "Installed removal ($($pkg.Name)): $($_.Exception.Message)" } } } } catch { $errs += "Installed discovery: $($_.Exception.Message)" } if ($errs.Count) { Write-Warning ("Errors:`n - " + ($errs -join "`n - ")) } else { Write-Host "Removal complete." -ForegroundColor Green } } $isLocal = [string]::IsNullOrWhiteSpace($Computer) -or $Computer -in @('localhost','127.0.0.1','.') # --- Preview --- if ($isLocal) { $preview = & $sbPreview $Pattern } else { $preview = Invoke-Remote -Computer $Computer -ScriptBlock $sbPreview -ArgumentList $Pattern } $prov = $preview.Provisioned $inst = $preview.Installed if (-not $prov -and -not $inst) { if ($isLocal) { $scope = "local machine" } else { $scope = $Computer } Write-Host "No matches for '$Pattern' on $scope." -ForegroundColor Yellow return } if ($prov) { Write-Host "`nProvisioned packages matching '$Pattern':" -ForegroundColor Cyan $prov | Format-Table -AutoSize } else { Write-Host "`nNo provisioned packages matched '$Pattern'." } if ($inst) { Write-Host "`nInstalled packages (existing profiles) matching '$Pattern':" -ForegroundColor Cyan $inst | Select-Object Name, PackageFullName | Format-Table -AutoSize } else { Write-Host "`nNo installed packages (existing profiles) matched '$Pattern'." } $confirm = Read-Host "`nProceed to remove from provisioned image AND existing profiles? (Y/N)" if ($confirm -notin @('Y','y')) { Write-Host "Cancelled."; return } # --- Remove --- if ($isLocal) { & $sbRemove $Pattern } else { Invoke-Remote -Computer $Computer -ScriptBlock $sbRemove -ArgumentList $Pattern | Out-Host } } # ------- task functions (menu sections) ------- function Do-ConnectRemote { $computer = Read-Host "Enter computer name (e.g. USERS-LAPTOP)" if ([string]::IsNullOrWhiteSpace($computer)) { return } try { Enter-PSSession -ComputerName $computer } catch { Write-Warning "Implicit auth to '$computer' failed: $($_.Exception.Message)" $currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name $cred = Get-Credential -UserName $currentUser -Message "Enter the password for $currentUser" Enter-PSSession -ComputerName $computer -Credential $cred } } function Do-RemoveAppxMenu { # Choose Local or Remote first $targetType = $null $computer = $null do { Clear-Host Write-Host "=== Remove Windows Apps ===" -ForegroundColor Cyan Write-Host " 1) Local machine" Write-Host " 2) Remote machine" Write-Host " 0) Back`n" $t = Read-Host "Choose target" switch ($t) { '1' { $targetType = 'Local' } '2' { $targetType = 'Remote'; $computer = Read-Host "Enter remote computer name"; if ([string]::IsNullOrWhiteSpace($computer)) { $targetType = $null } } '0' { return } default { Write-Host "Invalid choice." -ForegroundColor Yellow; Start-Sleep -Milliseconds 800 } } } until ($targetType) if ($targetType -eq 'Local') { $scopeLabel = 'local machine' } else { $scopeLabel = $computer } # Presets $presets = @( @{ Label = 'Paint 3D'; Pattern = 'Microsoft.MSPaint*' } @{ Label = '3D Viewer'; Pattern = 'Microsoft.Microsoft3DViewer*' } @{ Label = 'Mail & Calendar'; Pattern = 'microsoft.windowscommunicationsapps*' } @{ Label = 'Sticky Notes'; Pattern = 'Microsoft.MicrosoftStickyNotes*' } @{ Label = 'Solitaire Collection'; Pattern = 'Microsoft.MicrosoftSolitaireCollection*' } @{ Label = 'Xbox (Gaming App)'; Pattern = 'Microsoft.GamingApp*' } @{ Label = 'Xbox Overlay/Identity'; Pattern = 'Microsoft.Xbox*' } @{ Label = 'Clipchamp'; Pattern = 'Microsoft.Clipchamp*' } @{ Label = 'Weather'; Pattern = 'Microsoft.BingWeather*' } @{ Label = 'News'; Pattern = 'Microsoft.BingNews*' } ) do { Clear-Host Write-Host "=== Remove Apps on $scopeLabel ===" -ForegroundColor Cyan $i = 1 foreach ($p in $presets) { Write-Host (" {0,2}) {1} (pattern: {2})" -f $i, $p.Label, $p.Pattern); $i++ } Write-Host (" {0,2}) {1}" -f $i, 'Custom pattern...') Write-Host " 0) Back`n" $sub = Read-Host "Choose an option" if ($sub -eq '0') { break } if ($sub -as [int]) { $idx = [int]$sub if ($idx -ge 1 -and $idx -le $presets.Count) { $pat = $presets[$idx-1].Pattern if ($targetType -eq 'Local') { Remove-ProvisionedAndExisting -Pattern $pat } else { Remove-ProvisionedAndExisting -Pattern $pat -Computer $computer } Read-Host "`nPress Enter to continue" | Out-Null continue } elseif ($idx -eq ($presets.Count + 1)) { $pat = Read-Host "Enter -Like pattern (use * wildcards, e.g. Microsoft.Todos*)" if ($pat) { if ($targetType -eq 'Local') { Remove-ProvisionedAndExisting -Pattern $pat } else { Remove-ProvisionedAndExisting -Pattern $pat -Computer $computer } } Read-Host "`nPress Enter to continue" | Out-Null continue } } Write-Host "Invalid choice." -ForegroundColor Yellow Start-Sleep -Milliseconds 800 } while ($true) } function Do-ShowIP { Get-NetIPAddress -AddressFamily IPv4 | Select-Object InterfaceAlias, IPAddress | Sort-Object InterfaceAlias | Format-Table -AutoSize } function Do-PingHost { $h = Read-Host "Enter host or IP" if ($h) { Test-Connection -TargetName $h -Count 4 | Format-Table Address, Latency -Auto } } function Do-RestartServiceMenu { do { Clear-Host Write-Host "=== Restart a Service ===" -ForegroundColor Cyan Write-Host " 1) Local machine" Write-Host " 2) Remote machine" Write-Host " 0) Back`n" $opt = Read-Host "Choose an option" switch ($opt) { '1' { $svc = Read-Host "Enter service name (e.g., Spooler)" if ([string]::IsNullOrWhiteSpace($svc)) { break } try { Restart-Service -Name $svc -Force -ErrorAction Stop Get-Service -Name $svc | Select-Object Status, Name, DisplayName | Format-Table -AutoSize Write-Host "Local service restarted." -ForegroundColor Green } catch { Write-Warning "Local restart failed: $($_.Exception.Message)" } Read-Host "`nPress Enter to continue" | Out-Null } '2' { $computer = Read-Host "Enter remote computer name" if ([string]::IsNullOrWhiteSpace($computer)) { break } $svc = Read-Host "Enter service name on $computer" if ([string]::IsNullOrWhiteSpace($svc)) { break } try { $res = Invoke-Remote -Computer $computer -ScriptBlock { param($name) $null = Get-Service -Name $name -ErrorAction Stop Restart-Service -Name $name -Force -ErrorAction Stop Get-Service -Name $name | Select-Object Status, Name, DisplayName } -ArgumentList $svc if ($res) { $res | Format-Table -AutoSize } Write-Host "Remote service restarted on $computer." -ForegroundColor Green } catch { Write-Warning "Remote restart failed on $($computer): $($_.Exception.Message)" } Read-Host "`nPress Enter to continue" | Out-Null } '0' { return } default { Write-Host "Invalid choice." -ForegroundColor Yellow Start-Sleep -Milliseconds 800 } } } while ($true) } function do-wingetupgrades { winget upgrade --all --silent --accept-source-agreements --accept-package-agreements } # ------- Main Menu ------- do { Clear-Host Write-Host "=== My Admin Menu ===" -ForegroundColor Cyan Write-Host " 1) Connect to a machine (Enter-PSSession as current user)" Write-Host " 2) Remove Windows apps (local or remote)" Write-Host " 3) Show IP addresses" Write-Host " 4) Ping a host" Write-Host " 5) Restart a service (submenu)" write-Host " 6) Run Winget Upgrades" Write-Host " 0) Quit`n" $choice = Read-Host "Choose an option" switch ($choice) { '1' { Do-ConnectRemote } '2' { Do-RemoveAppxMenu } '3' { Do-ShowIP; Read-Host "`nPress Enter to return to menu" | Out-Null } '4' { Do-PingHost; Read-Host "`nPress Enter to return to menu" | Out-Null } '5' { Do-RestartServiceMenu } '6' { do-wingetupgrades } '0' { exit } default { Write-Host "Invalid choice." -ForegroundColor Yellow Start-Sleep -Milliseconds 800 } } } while ($true)