はじめに
今回の記事では、Windows のリモートデスクトップ機能を PowerShell で有効化する手順を紹介します。
バッチファイルでレジストリを設定する記事はよく見かけますが、昨今の Windows リモートデスクトップはそれだけでは有効化されないのでまとめていこうと思います。
レジストリの設定
まず、リモートデスクトップに関連したレジストリを設定するスクリプトを記述します。
# このコンピューターへのリモート接続を許可する → ON
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server" -Name "fDenyTSConnections" -Value 0 -Type DWord
# GUIで設定時、このキーの値も一緒 に更新される
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server" -Name "updateRDStatus" -Value 1 -Type DWord
# デバイスが接続にネットワークレベル認証を使用することを要求する → OFF
Set-ItemProperty -Path "HKLM:\SYSTEM\ControlSet001\Control\Terminal Server\WinStations\RDP-Tcp" -Name "UserAuthentication" -Value 0 -Type DWord
# ユーザーのパスワードを設定していないときに実行する
# ローカルアカウントの空のパスワードの使用をコンソールログオンのみに制限する -> 無効
#\Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa" -Name "LimitBlankPasswordUse" -Value 0 -Type DWord
デバイスが接続にネットワークレベル認証を使用することを要求する
は、ネットワークレベル認証(NLA)をサポートしていないサードパーティ製のツールなどで接続できないときに OFF にしてください。
今回のスクリプトでは OFF にしています。
ファイヤーウォールの設定
通常の設定画面でリモートデスクトップを有効化した場合、先程のレジストリの変更とファイヤーウォールの設定が有効化されます。
そのため、PowerShell からファイヤーウォールを有効化できるようにルールの名前を確認します。
> Get-NetFirewallRule | Where-Object { $_.DisplayName -like "*リモート デスクトップ*" }
Name : RemoteDesktop-Shadow-In-TCP # ←これ
DisplayName : リモート デスクトップ - シャドウ (TCP 受信)
Description : 既存のリモート デスクトップ セッションのシャドウ処理を許可するリモート デスクトップ サービスの受信規則です。(TCP 受信)
# 省略
Name : RemoteDesktop-UserMode-In-TCP # ←これ
DisplayName : リモート デスクトップ - ユーザー モード (TCP 受信)
Description : RDP トラフィックを許可するためのリモート デスクトップ サービスの受信規則です。[TCP 3389]
# 省略
Name : RemoteDesktop-UserMode-In-UDP # ←これ
DisplayName : リモート デスクトップ - ユーザー モード (UDP 受信)
Description : RDP トラフィックを許可するためのリモート デスクトップ サービスの受信規則です。[UDP 3389]
# 省略
ルールの名前が分かったところで PowerShell で有効化するコマンドを記述します。
# 特定の規則名を有効にする
# リモート デスクトップ - シャドウ (TCP 受信)
Enable-NetFirewallRule -Name "RemoteDesktop-Shadow-In-TCP"
# リモート デスクトップ - ユーザー モード (TCP 受信)
Enable-NetFirewallRule -Name "RemoteDesktop-UserMode-In-TCP"
# リモート デスクトップ - ユーザー モード (UDP 受信)
Enable-NetFirewallRule -Name "RemoteDesktop-UserMode-In-UDP"
スクリプトを最適化する
これまでのコマンドを実行しても目的は達成できますが、今後設定したいレジストリが増えることも考えて関数に切り出して再利用できるようにします。
適当なフォルダを作りFunctions.ps1
というファイルを作成して以下のコードを記述します。
################################################
# パスが存在しなければ作成する
################################################
function Set-RegistryPathIfMissing([string] $path, [string] $logFilePath) {
try {
# レジストリキーのパスが存在しなかった場合は新しく作成する
if (-not (Get-Item -Path $path -ErrorAction SilentlyContinue)) {
New-Item -Path $path -Force
Write-Host "新しくパスを作成しました。$($path)`n"
"新しく作成したパス $($path)" | Out-File -Append -FilePath $logFilePath
}
} catch {
Write-Host "エラー: $($_.Exception.Message)"
"エラー: $($_.Exception.Message)" | Out-File -Append -FilePath $logFilePath
}
}
################################################
# レジストリを設定する
################################################
function Set-RegistrySetting([string] $path, [string] $key, $value, [string] $logFilePath) {
try {
# 型によって処理を分岐
if ($value -is [int]) {
Set-ItemProperty -Path $path -Name $key -Value $value -Type DWord
"変更後の値→ Path: $path, Key: $key, Value: $value" | Out-File -Append -FilePath $logFilePath
} elseif ($value -is [string]) {
Set-ItemProperty -Path $path -Name $key -Value $value -Type String
"変更後の値→ Path: $path, Key: $key, Value: $value" | Out-File -Append -FilePath $logFilePath
}
} catch {
Write-Host "エラー: $($_.Exception.Message)"
"エラー: $($_.Exception.Message)" | Out-File -Append -FilePath $logFilePath
}
}
レジストリはパスやキーを自分で作成する場合があるので、それらを考慮した処理を加えています。
更に、メインの処理としてrdp_setting.ps1
というファイルを作成してFunctions.ps1
をモジュールとして読み込みます。
# 外部ファイルを使用するための設定
. "$($PSScriptRoot)\Functions.ps1"
# ログファイルの名前を設定する
$CurrentDate = Get-Date -Format "yyyy-MM-dd"
$LogFilePath = "$([Environment]::GetFolderPath('Desktop'))\$CurrentDate.txt"
# ログファイルが既に存在する場合内容をクリアする
if (Test-Path -Path $LogFilePath) {
Clear-Content -Path $LogFilePath
}
# スクリプトの開始をログに記録する
"Script execution started on $(Get-Date)" | Out-File -FilePath $LogFilePath
# レジストリ設定の配列
$registrySettings = @(
@{
# このコンピューターへのリモート接続を許可する → ON
Path = "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server"
Key = "fDenyTSConnections"
Value = 0
},
@{
# GUIで設定時、このキーの値も一緒に更新される
Path = "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server"
Key = "updateRDStatus"
Value = 1
},
@{
# デバイスが接続にネットワークレベル認証を使用することを要求する → OFF
Path = "HKLM:\SYSTEM\ControlSet001\Control\Terminal Server\WinStations\RDP-Tcp"
Key = "UserAuthentication"
Value = 0
},
@{
# ローカルアカウントの空のパスワードの使用をコンソールログオンのみに制限する -> 無効
Path = "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa"
Key = "LimitBlankPasswordUse"
Value = 0
}
)
# レジストリ設定を反復処理
foreach ($setting in $registrySettings) {
# パスが存在しなければ作成
Set-RegistryPathIfMissing -Path $setting.Path -LogFilePath $logFilePath
# レジストリ設定を適用
Set-RegistrySetting -Path $setting.Path -Key $setting.Key -Value $setting.Value -LogFilePath $logFilePath
}
####################################
# ファイヤーウォールの設定
####################################
# リモート デスクトップ - シャドウ (TCP 受信)
Enable-NetFirewallRule -Name "RemoteDesktop-Shadow-In-TCP"
# リモート デスクトップ - ユーザー モード (TCP 受信)
Enable-NetFirewallRule -Name "RemoteDesktop-UserMode-In-TCP"
# リモート デスクトップ - ユーザー モード (UDP 受信)
Enable-NetFirewallRule -Name "RemoteDesktop-UserMode-In-UDP"
レジストリを変更するようなコマンドは、デフォルトのポリシーでは実行できないのでバッチファイルからポリシーを一時的に変更した後に、PowerShell スクリプトを呼び出すようにします。
setting.bat
というファイルを作成して以下のように記述します。
@echo off
setlocal
echo *********************注意事項*********************
echo このバッチファイルは管理者権限で実行して下さい
echo **************************************************
:: ====================================
:: rdp_setting.ps1 と同じフォルダにで
:: このバッチファイルを実行して下さい
:: ====================================
:: 管理者以外では実行できないようにする
openfiles > nul 2>&1
if not %errorlevel% equ 0 (
echo;
echo ※このバッチファイルは管理者権限で実行してください。
echo;
echo Enterで終了
pause > nul
exit
)
:: %~dp0は、バッチファイル自身のディレクトリパスを指す
set PowerShellScriptPath=%~dp0rdp_setting.ps1
PowerShell -NoProfile -ExecutionPolicy RemoteSigned -File "%PowerShellScriptPath%"
:end
pause
endlocal
今回以下の 3 つのファイルを作成しました。
- setting.bat
- Functions.ps1
- rdp_setting.ps1
これらのファイルは同じフォルダ内に配置して実行します。