KMaks.RDShadow.psm1
<#
.Synopsis Gets current setting for shadowing Remote Desktop sessions. .Description The Get-RDSHadow cmdlet queries target system using PSRemoting. It returns an object containing of a computer name and it's current session shadowing setting. .Parameter ComputerName Specifies the remote computer. .Example # Get Remote Desktop session shadowing configuration on local system. C:\PS>Get-RDShadow ComputerName Access ------------ ------ LOCALHOST Disable .Example # Get Remote Desktop session shadowing configuration on a remote system. C:\PS>Get-RDShadow -ComputerName Workstation01 ComputerName Access ------------ ------ Workstation1 Disable #> Function Get-RDShadow { [CmdletBinding()] param ( [Parameter()] [string]$ComputerName = $Env:COMPUTERNAME ) $AccessDictionary = @{ "Disable" = $Null "FullControllRequireConsent" = 1 "FullControlNoConsent" = 2 "ViewOnlyRequireConsent" = 3 "ViewOnlyNoConsent" = 4 } $ScriptBlock = { $OldValue = Get-ItemProperty -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services\' Return [PsCustomObject]@{ OldValue = $OldValue.Shadow } } try { Invoke-Command -ComputerName $ComputerName -ScriptBlock $ScriptBlock -ErrorAction Stop | ForEach-Object { [PSCustomObject]@{ ComputerName = $ComputerName Access = $AccessDictionary.GetEnumerator() | Where-Object Value -eq $PSItem.OldValue | Select-Object -ExpandProperty Name } } } catch { Write-Error "Unable to execute commands on $ComputerName." } } <# .Synopsis Modifies current setting for shadowing Remote Desktop sessions. .Description The Set-RDSHadow cmdlet modifies the setting responsible for shadowing Remote Desktop sessions. .Parameter ComputerName Specifies the remote computer. .Parameter Access Specifies the desired access level. .Example # Disable Remote Desktop session shadowing on local system. C:\PS>Set-RDShadow -Access Disable ComputerName Access ------------ ------ LOCALHOST Disable .Example # Allow Remote Desktop session shadowing without consent. C:\PS>Set-RDShadow -ComputerName Workstation01 -Access FullControlNoConsent ComputerName Access ------------ ------ Workstation1 FullControlNoConsent #> Function Set-RDShadow { [CmdletBinding()] param ( [Parameter()] [string]$ComputerName = $Env:COMPUTERNAME, [Parameter(Mandatory)] [ValidateSet("Disable","FullControllRequireConsent","FullControlNoConsent","ViewOnlyRequireConsent","ViewOnlyNoConsent")] [string]$Access ) $AccessDictionary = @{ "Disable" = $Null "FullControllRequireConsent" = 1 "FullControlNoConsent" = 2 "ViewOnlyRequireConsent" = 3 "ViewOnlyNoConsent" = 4 } $ScriptBlock = { param($AccessValue) $OldValue = Get-ItemProperty -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services\' If ($AccessValue) { New-ItemProperty -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services\' -Name 'Shadow' -Value $AccessValue -PropertyType DWORD -Force | Out-Null } Else { Remove-ItemProperty -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services\' -Name 'Shadow' -ErrorAction SilentlyContinue | Out-Null } $NewValue = Get-ItemProperty -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services\' Return [PsCustomObject]@{ OldValue = $OldValue.Shadow NewValue = $NewValue.Shadow } } try { Invoke-Command -ComputerName $ComputerName -ScriptBlock $ScriptBlock -ArgumentList $AccessDictionary[$Access] | ForEach-Object { [PSCustomObject]@{ ComputerName = $ComputerName Access = $AccessDictionary.GetEnumerator() | Where-Object Value -eq $PSItem.NewValue | Select-Object -ExpandProperty Name OldAccess = $AccessDictionary.GetEnumerator() | Where-Object Value -eq $PSItem.OldValue | Select-Object -ExpandProperty Name } } } catch { Write-Error "Unable to execute commands on $ComputerName." } } <# .Synopsis Lists all user sessions on the target system. .Description The Get-RDShadowSessions cmdlet gets a list of all user sessions on the target machine. .Parameter ComputerName Specifies the remote computer. .Example # Get Remote Desktop sessions on local system. C:\PS>Set-RDShadowSessions ComputerName : LOOCALHOST UserName : localuser SessionName : console ID : 1 State : Active IdleTime : none LogonTime : 4/22/2021 7:19:00 PM .Example # Get Remote Desktop sessions on a remote system. C:\PS>Get-RDShadowSessions -ComputerName Workstation01 ComputerName : RDServer1 UserName : User1 SessionName : ID : 2 State : Disc IdleTime : 20:13:00 LogonTime : 8/4/2021 10:06:00 AM ComputerName : RDServer1 UserName : User2 SessionName : ID : 4 State : Active IdleTime : 20:26:00 LogonTime : 9/4/2021 9:07:00 AM #> Function Get-RDShadowSessions { [CmdletBinding()] param ( [Parameter()] [string]$ComputerName = $Env:COMPUTERNAME ) $ScriptBlock = { $Return = & query.exe user | Select-Object -Skip 1 | ForEach-Object { If ($PSItem -Match '^.(?<UserName>.{22})(?<SessionName>.{18})(?<ID>.{5})(?<State>.{8})(?<IdleTime>.{11})(?<LogonTime>.*)$') { [PsCustomObject]@{ UserName = [String]$Matches.UserName.Trim() SessionName = [String]$Matches.SessionName.Trim() ID = [int]$Matches.ID.Trim() State = [String]$Matches.State.Trim() IdleTime = If ($Matches.IdleTime.Trim() -eq "none") {"none"} Else { [TimeSpan]$Matches.IdleTime.Trim().Replace('+','.') } LogonTime = $Matches.LogonTime.Trim() } } } Return $Return } try { Invoke-Command -ComputerName $ComputerName -ScriptBlock $ScriptBlock | ForEach-Object { [PSCustomObject]@{ ComputerName = $PSItem.PSComputerName UserName = $PSItem.UserName SessionName = $PSItem.SessionName ID = $PSItem.ID State = $PSItem.State IdleTime = $PSItem.IdleTime LogonTime = $PSItem.LogonTime } } } catch { Write-Error "Unable to execute commands on $ComputerName." } } <# .Synopsis Shadow target session. .Description The Invoke-RDShadow cmdlet launches Remote Desktop Client with specific parameters to shadow desired user session. .Parameter ComputerName Specifies the remote computer. .Parameter SessionId Specifies the user session ID to shadow on the target system. .Parameter NoConsentPrompt Switch hiding a connection consent prompt form the target session owner. .Parameter Control Switch required to control the target session. By default the session is view only. .Example # Get Remote Desktop sessions on local system. C:\PS>Set-RDShadowSessions ComputerName : LOOCALHOST UserName : localuser SessionName : console ID : 1 State : Active IdleTime : none LogonTime : 4/22/2021 7:19:00 PM .Example # Get Remote Desktop sessions on a remote system. C:\PS>Get-RDShadowSessions -ComputerName Workstation01 ComputerName : RDServer1 UserName : User1 SessionName : ID : 2 State : Disc IdleTime : 20:13:00 LogonTime : 8/4/2021 10:06:00 AM ComputerName : RDServer1 UserName : User2 SessionName : ID : 4 State : Active IdleTime : 20:26:00 LogonTime : 9/4/2021 9:07:00 AM #> Function Invoke-RDShadow { [CmdletBinding()] param( [Parameter()] [string]$ComputerName, [Parameter(Mandatory)] [int]$SessionId, [Parameter()] [switch]$ConsentPrompt, [Parameter()] [switch]$Admin, [Parameter()] [switch]$Control ) [System.Collections.ArrayList]$ProcessParameters = @() If ($ComputerName) { [void]$ProcessParameters.Add("/v:$ComputerName") } [void]$ProcessParameters.Add("/shadow:$SessionId") If ($Admin) { [void]$ProcessParameters.Add("/Admin") } If (-not $ConsentPrompt) { [void]$ProcessParameters.Add("/NoConsentPrompt") } If ($Control) { [void]$ProcessParameters.Add("/Control") } Start-Process -Path "mstsc.exe" -ArgumentList $ProcessParameters } |