localhost
or 127.0.0.1
and you will get errors from the parser. In the case of the latter:'LocalMachine'
to [Microsoft.Win32.RegistryHive]::LocalMachine
at Runtime because OpenRemoteBaseKey
requires the first parameter to be of type Microsoft.Win32.RegistryHive
.OpenRemoteBaseKey
, PowerShell will evaulate the variable rather than a string:OpenRemoteBaseKey(RegistryHive, String)OpenRemoteBaseKey(RegistryHive, String)OpenRemoteBaseKey(RegistryHive, String)OpenRemoteBaseKey(RegistryHive, String) | Opens a new RegistryKey that represents the requested key on a remote machine. |
OpenRemoteBaseKey(RegistryHive, String, RegistryView)OpenRemoteBaseKey(RegistryHive, String, RegistryView)OpenRemoteBaseKey(RegistryHive, String, RegistryView)OpenRemoteBaseKey(RegistryHive, String, RegistryView) | Opens a new registry key that represents the requested key on a remote machine with the specified view. |
hKey
is invalid.machineName
is not found.machineName
is null
.machineName
is String.Empty. The requested key must be a root key on the remote machine, and is identified by the appropriate RegistryHive value. hKey
or view
is invalid.machineName
is not found.machineName
is null
.machineName
is String.Empty. The requested key must be a root key on the remote machine, and is identified by the appropriate RegistryHive value. view
is Registry64 but the remote machine is running a 32-bit operating system, the returned key will use the Registry32 view.functionGet-RdpSessions { |
[CmdletBinding()] |
param ( |
[Parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] |
$ComputerName |
, |
[Parameter()] |
[switch]$Force |
<# not yet implememnted |
, |
[Parameter()] |
[System.Management.Automation.Credential()] |
[System.Management.Automation.PSCredential]$Credential = [System.Management.Automation.PSCredential]::Empty |
#> |
) |
process { |
#force logic |
[bool]$forced=$false |
[string]$previousValue=''#hold as string in case this property's held as something other than int; i.e. we don't want to cause side effects |
if ($Force.IsPresent) { |
try { |
$previousValue= [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine',$ComputerName).OpenSubKey('SYSTEMCurrentControlSetControlTerminal Server').GetValue('AllowRemoteRPC') |
write-verbose'$ComputerName's AllowRDP = $previousValue' |
if($previousValue-ne'1') { |
[Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine',$ComputerName).OpenSubKey('SYSTEMCurrentControlSetControlTerminal Server',$true).SetValue('AllowRemoteRPC',1) |
$forced=$true |
write-verbose'$ComputerName's AllowRDP Forced to 1' |
} |
} catch { |
write-verbose'$ComputerName's AllowRDP failed to use the force :(' |
#even if we fail here, we still proceed; as we may be able to see sessions on the remote machine even if we can't read from its registry |
} |
} |
#query session logic |
try { |
#https://ss64.com/nt/query-session.html |
#pull the results into a variable |
[string[]]$querySessionResult= (qwinsta '/server:$ComputerName') |
#it would be nice to figute out the fixed-width column sizes automatically, in case they vary; but as the ID column is right aligned, that's not so easy: |
#[string]$regex = '^' + (($querySessionResult[0] -split 'sb' | select -skip 1 | %{'(?<$($_.trim())>.{$($_.Length+1)}'}) -join ')') + '.*)$' |
#so instead using a fixed defintion: |
[string]$regex='^(?<SESSIONNAME>.{18})(?<USERNAME>.{18})(?<ID>.{11})(?<STATE>.{8})(?<TYPE>.{7})(?<DEVICE>.*)$'#column sizes from https://superuser.com/a/1000089/156700 |
$querySessionResult| select -skip 1|?{$_-match$regex} |%{ |
$matches.remove(0) |
$properties=@{} |
$Matches.Keys|%{$properties[$_] ='$($Matches[$_])'.Trim()} |
(New-Object-TypeName PSObject -Property $properties) |
} |
} finally { #regardless of whether the above works, ensure that if we forced things, we put them back how they were |
if($forced) { |
[Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine',$ComputerName).OpenSubKey('SYSTEMCurrentControlSetControlTerminal Server',$true).SetValue('AllowRemoteRPC',$previousValue) |
write-verbose'$ComputerName's AllowRDP reset to $previousValue' |
} |
} |
} |
} |
#demo for one computer |
Get-RdpSessions-ComputerName $MyComputer-Force -Verbose |
#demo for all servers in AD: |
#uses Test-ConnectionQuickly workflow from here to avoid running against offline machines https://codereview.stackexchange.com/questions/97726/powershell-to-quickly-ping-a-number-of-machines |
$servers=Get-AdComputer {(OperatingSystem -like'*server*') -and (Enabled -eq$true)} |
$servers=Test-ConnectionQuickly$servers|? Online |
$servers|Get-RdpSessions-Force -Verbose |? Username |