<# Manage-RemoteDevices.ps1 GUI to View, enable and disable devices on remote computer using WMSMAN or DCOM Delete device is WSMAN only or View connected USB devices Alan Kaplan www.akaplan.com 12/10/21 #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string] $ComputerName, [switch]$ViewConnectedUSB ) Add-Type -assemblyname Microsoft.visualBasic #Returns FQDN of current computer $MyPCname = ([System.Net.Dns]::GetHostByName(($env:computerName))).Hostname #This avoids unauthorized operation when using FQDN for the script localhost if ($computerName -eq $MyPCname) { $computerName = 'localhost' } Function CimExit() { Clear-Host $msg = "Do you want to rescan and view a refreshed list of devices on $ComputerName`?" $retval = [Microsoft.VisualBasic.Interaction]::MsgBox($msg, 'YesNoCancel,defaultbutton2,Question', "Refresh List?") switch ($retval) { 'Yes' { Main } Default { Get-CimSession | Remove-CimSession Exit } } } function New-CimSession2 ($computer) { <# New-CimSession2 Create new Cim-Session with DCOM fallback simplified version New-MrCimSession, http://mikefrobbins.com #> #Parameter hash for splatting $SessionParams = @{ ErrorAction = 'Stop' OutVariable = 'Session' ComputerName = $computer } if ((Test-WSMan -ComputerName $Computer -ErrorAction SilentlyContinue).productversion -match 'Stack: ([3-9]|[1-9][0-9]+)\.[0-9]+') { $script:bWSMAN = $true try { Write-Verbose -Message "Attempting to connect to $Computer using the WSMAN protocol." New-CimSession @SessionParams Write-Verbose -Message "Connected to $Computer with WSMAN." } catch { Write-Warning -Message "Unable to connect to $Computer using the WSMAN protocol. Verify your credentials and try again." } } else { $script:bWSMAN = $false #Add alternative connection with DCOM $SessionParams.SessionOption = New-CimSessionOption -Protocol Dcom try { Write-Verbose -Message "Attempting to connect to $Computer using the DCOM protocol." New-CimSession @SessionParams Write-Verbose -Message "Connected to $Computer with DCOM." } catch { Write-Warning -Message "Unable to connect to $Computer using the WSMAN or DCOM protocol. Verify $Computer is online and try again." Pause Exit } $SessionParams.Remove('SessionOption') } } Function ToTitleCase($txt) { $TextInfo = (Get-Culture).TextInfo $TextInfo.ToTitleCase($txt.toLower()) } Function Set-DeviceState ($Dev, $NewState) { write-host "Setting $($dev.FriendlyName) to $newState`d " $params = @{ CimSession = $Session InstanceId = $dev.InstanceId Confirm = $true ErrorAction = 'Stop' } switch ($NewState) { 'disable' { Try { Disable-PnpDevice @params Write-host "Disabled $($dev.FriendlyName)" -ForegroundColor Green Pause CimExit } Catch { Write-Warning $_ Pause CimExit } } 'enable' { Try { Enable-PnpDevice @params Write-host "Enabled $($dev.FriendlyName)" -ForegroundColor Green Pause CimExit } Catch { Write-Warning $_ Pause CimExit } } } } Function Remove-Device($Dev) { $id = $dev.instanceID $msg = "Are you sure you want to delete $($dev.FriendlyName) on $ComputerName`?" $retval = [Microsoft.VisualBasic.Interaction]::MsgBox($msg, 'YesNoCancel,defaultbutton2,Question', "Confirm") if ($retVal -ne 'Yes') { CimExit } #The pnputil command is a locally available command which runs remotely using WSMAN only #Delete syntax is: pnputil.exe /remove-device $ID[/subtree] [/reboot] $cmd = "pnputil.exe /remove-device `"$ID`" /subtree" $msg = "Do you want to reboot $ComputerName if required by deletion?" $retval = [Microsoft.VisualBasic.Interaction]::MsgBox($msg, 'YesNoCancel,defaultbutton2,Question', "Reboot") switch ($retval) { 'Yes' { $cmd += ' /reboot' } Default { CimExit } } Try { $retVal = Invoke-Command -ComputerName $ComputerName -ErrorAction Stop -ScriptBlock { $cmd } $retval } Catch { Write-Warning $Error[0].exception.message } Pause CimExit } Function Main() { $pnp = Get-PnpDevice -CimSession $Session $List = $pnp | Where-Object { $_.FriendlyName } | Sort-Object PNPClass, FriendlyName | Select-Object PNPClass, FriendlyName, Manufacturer, Description, Status, @{Name = "Problem"; Expression = { $ProbTxt = $_.Problem -Replace ('CM_PROB_', '') -Replace ('_', ' '); ToTitleCase $ProbTxt } }, Service, instanceID if ($ViewConnectedUSB){ $list | Where-Object { ($_.Problem -eq 'None') -and ($($_.instanceID).startswith('US', 'CurrentCultureIgnoreCase'))} | Select-Object PNPClass, FriendlyName, Manufacturer, Description, Service, instanceID | Out-GridView -Title "Connected USB Devices on $computername`. Selected items are copied to your clipboard as CSV" -PassThru | ConvertTo-Csv -NoTypeInformation | Set-Clipboard Stop-Process $PID } $List | Out-GridView -Title "Select a Device on $ComputerName to Enable, Disable or Delete, [Cancel] to exit" -OutputMode Single #Nothing selected if ($null -eq $Dev) { CimExit } $CurrentValue = ($dev).Problem $2msg = if ($script:bWSMAN) { "Delete it" } Else { "Not Available" } switch ($Currentvalue) { 'Disabled' { $CurrentState = 'disabled' $newState = 'Enable' $1msg = "$newState it" Break } 'None' { $CurrentState = 'enabled' $newState = 'Disable' $1msg = "$newState it" Break } 'Phantom' { $CurrentState = 'not currently attached' $newState = 'Skip' $1msg = 'Not Applicable' } } $msg = @" $($Dev.FriendlyName) is $currentState, do you want to: 1) $1msg 2) $2msg 3) Exit "@ $retval = [Microsoft.VisualBasic.Interaction]::InputBox($msg, "Choose Action", 3) switch ($retval) { 1 { if ($newState -eq 'Skip') { Return } Set-DeviceState $dev $newState $msg = "Device is $newState`d" } 2 { Remove-Device $dev } Default { $msg = "Nothing Done" } } CimExit } #Script begins with connection $Session = New-CimSession2 -computer $ComputerName if ($null -eq $Session) { Exit } Main Exit