WinRMを使ってWindowsを遠隔操作してみた
はじめに
こんにちは、DevOpsエンジニアのユです。
普段はWindowsOSを使用していますが、開発は殆どLinuxベースなので、Windows PowerShellは初心者です。
案件より、環境のVDI(Windowsサーバーのバーチャル作業インターフェース)から他のWindowsにアクセスしてPowerShellを操作し、スクリプトを実行したいということで、WinRMの操作を勉強しました。
今回はPowerShellでWindowsから遠隔で他のWindowsのを操作するイロハを紹介したいと思います。
WinRMとは?
WinRMはWindows PowerShellを遠隔から操作する機能です。
他のWindowsコンピュータのPowerShellコマンドラインを呼び出してコマンドの実行やプログラムの起動などを行うことができます。
詳細はMicrosoftのドキュメントを参照してください。
WinRMのメリット
RDP(リモートデスクトップ)はWinRMと同じくWindowsのリモート機能です。WindowsのUIを介してリモート操作できて便利ですが、ライセンスを購入しないとセッション数が1(Windows Serverならセッション制限が2)に制限されてます。
原則としては1on1のリモート機能なので、複数ユーザーが同じリモート先で作業したいなら、お互いにセッション切断されてしまうことがよくあると思います(筆者も実体験してしまいました)。
WinRMならその制限がありません(※)。
WinRMはSSHのようにコマンドでPowerShell介して殆どのWindowsサービスを操作できるし、Linuxのように同時に複数ユーザーがリモート先にアクセスして作業できます。
そして、事前にWinRM利用するPowerShellスクリプト(以降PSスクリプトと呼ぶ)を書いておきましたら、Windowsスケジューラーなどと併せて使うとローカル側から自動にリモートしてPSスクリプトを実行することが可能です。PSスクリプトでWinRM利用する方法も紹介しますので、ご参考になれば幸いです。
※:デフォルトPowershellの同時実行数は5ですが、無制限にすることが可能です。
始める前に
PowerShellのエンコーディング
早速始めたいですが、その前に確認したいことがあります。エンコーディングのことです。リモート先(以降ホストと呼ぶ)とローカル側(以降クライアントと呼ぶ)のエンコーディングが一致されているかどうか先に確認しないと、変な状況が起きるかもしれません。
特に、日本語版Windows10のPowerShell ISEのデフォルトエンコードがシフトJIS、PowerShellのデフォルトエンコードがUS-ASCIIのようです。すべてUTF-8にすればいいのに…(日本語作業環境でエンコーディングをUTF-8にしても文字化けしない)。
文字化けが発生したら、PowerShell ISEとスクリプトファイルの文字コード両方ともUTF-8に統一しましょう。
ホストとクライアント両方共統一することをお勧めします。
# Windows PowerShellもしくはWindows PowerShell ISEでPowerShellのエンコーディングを確認する
PS C:\WINDOWS\system32> $OutputEncoding.EncodingName
日本語 (シフト JIS)
# エンコーディングをUTF-8に変更する
PS C:\WINDOWS\system32> $OutputEncoding = New-Object System.Text.UTF8Encoding
ネットワーク接続の種類を確認
ホスト、クライアント両方ともWindowsのネットワーク接続の種類がPublicだと利用できません。 セキュリティ性を考慮して、同じプライベートネットワークもしくはイントラネットの設定が必要です。
PowerShellを管理者として実行
この記事のすべてのPowerShellは管理者として実行する前提です。
WinRMの起動と設定
WinRMの使用はPowerShellで起動と信頼できるホストを設定する必要があります。
WinRM起動(ホスト・クライアント両方)
ホスト、クライアント両方ともWinRM機能を起動する必要があります。
ネットの種類がPublicでしたらWinRMを起動するとWinRM ファイアウォール例外は機能しませんエラーが出てWinRMを使えませんのでご注意ください。
# WinRM起動する
PS C:\WINDOWS\system32> Enable-PSRemoting
信頼ホストリストを設定する(クライアント)
リモートする対象を信頼できるホストに設定することです。
少し違いがありますが、ホストは(private, domain)ネット上の名前、もしくはIPアドレスで設定できます。
信頼ホストが複数の場合、ホストをカンマ区切りで記述します。
PS C:\WINDOWS\system32> Set-Item WSMan:\localhost\Client\TrustedHosts -Value "host.domain,hoge.fuga"
# TrustHostの変更の確認が出て、Yにすれば変更する
信頼ホストリスト確認(クライアント)
PS C:\Windows\system32> Get-Item WSMan:\localhost\client\trustedhosts
WSManConfig: Microsoft.WSMan.Management\WSMan::localhost\Client
Type Name SourceOfValue Value
---- ---- ------------- -----
System.String TrustedHosts host.domain,hoge.fuga
ホストを信頼ホストリストに追加する(クライアント)
信頼ホストリストは上書きしかできないので、新しい信頼ホストを追加したい時、元のホストリスト文字列に新ホストを追加してください。
# 元のホストリストhost.domain,hoge.fugaにnew.hostを追加
PS C:\WINDOWS\system32> Set-Item WSMan:\localhost\Client\TrustedHosts -Value "host.domain,hoge.fuga,new.host"
WinRMで遠隔操作
さて、上記の準備を完成したら、WinRMを利用できるようになりました。
操作シチュエーションを想定して操作方法を説明します。
※以下の操作は、PSスクリプトの.ps1ファイルとして保存してISEで実行してもいいし、直接にPowerShellのコマンドラインで行ってもいいので、サンプルスクリプトを記載しておきます。
手動操作(クライアント)
LinuxのSSHリモート操作する感覚でホストのPowerShellを操作すること。
非対話型認証を作成
非対話式認証を作成しておかないと、アクセス途中に対話ウィンドウが出てしまいます。
# useraccount=ユーザーアカウント名、userpassword=ユーザーパスワード
$password = ConvertTo-SecureString "userpassword" -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential ("DOMAIN\useraccount", $password)
セッションでリモートアクセス
IPがxx.xx.xx.xxのホストへアクセスする。
※上記で作成した認証用の変数$credを使います。
$remoteHost = "xx.xx.xx.xx"
Enter-PSSession -ComputerName $remoteHost -Credential $cred
直接操作
これでホストのPowerShellをリモートで直接操作できるようになりました。
# リモートでWFE2のPS操作
[xx.xx.xx.xx]: PS C:\Users\spadmin\Documents> $Env:ComputerName
ホストコンピューター名
# セッション切断
[xx.xx.xx.xx]: PS C:\Users\spadmin\Documents> Exit-PSSession
PSスクリプトをリモート実行(クライアント)
WinRMを行うPSスクリプト
HOST(ホスト名)へアクセスしてクライアントにあるC:\work\remote_script_utf8.ps1を実行する
# HOSTにログイン用パスワード設定:useraccount=ユーザーアカウント名、userpassword=ユーザーパスワード
$password = ConvertTo-SecureString "userpassword" -AsPlainText -Force
# 非対話型認証作成
$cred = New-Object System.Management.Automation.PSCredential ("DOMAIN\useraccount", $password)
# リモート対象設定(信頼できるホストリストにある登録ホストと一致する値を設定)
$remoteHost = "HOST"
# PSセッションを作成
$session = New-PSSession -ComputerName $remoteHost -Credential $cred
# クライアント(ローカル側)にある、リモートで実行したいPSスクリプトのパス
$localps = "C:\work\remote_script_utf8.ps1"
# リモートでクライアントにあるPSを実行して結果を出力される
Invoke-Command -Session $session -FilePath $localps
リモートで実行したいPSスクリプト例
※ローカルに置く(上記の$localpsがファイルパス)
Write-Output "Excute local ps"
Write-Output "現在のデバイス:$Env:ComputerName"
Write-Output "End local ps"
まとめ
筆者はLinuxベースの開発がメインですが、色んな事情で配布された開発用パソコンが大体Windows OSです(何ででしょうかね…)。
その原因でVSCodeやWSL(Windows Subsystem Linux)、DockerなどWindows OSを基づいてLinux・オープンソース開発向けるツール・サービスをよく使っていましたが、Windowsベースのコマンドライン操作やスクリプト開発は殆ど経験ありません。
今回WinRMを利用することより、「WindowsのUI操作で出来ることなら全部できる」PowerShellパワーを感じました。
特に「目視+マウス+キーボードのウィンドウズ操作」が要らなくなりまして、RDPより「セッション切断しまった・されてしまった」のチームワーク阻害も無くなりましたし、何というか、解放感がありましすね。
そして、自動化テストのように、手動操作をPSスクリプトで代わるのは最初コード開発に時間掛りますが、重複作業やミスが生じやすい操作は明らかに減りましたし、チームの統一化(PSスクリプト基準で操作の共有)もしやすくなりました。
Linux操作の感覚で行けそう!と思いましたが、実際Windowsの特性より、筆者はWinRMを利用したスクリプト開発の途中に、リモートのリモート(ダブルホップ)問題で一時的にはまってしまったので、次回はWindowsの間に資格情報の渡し問題と解決を共有したいと思います。
請うご期待を!
お問合せはお気軽に
https://service.shiftinc.jp/contact/
SHIFTについて(コーポレートサイト)
https://www.shiftinc.jp/
SHIFTのサービスについて(サービスサイト)
https://service.shiftinc.jp/
SHIFTの導入事例
https://service.shiftinc.jp/case/
お役立ち資料はこちら
https://service.shiftinc.jp/resources/
SHIFTの採用情報はこちら
https://recruit.shiftinc.jp/career/