Invoke-Command を使用するとリモートコンピューター上でコマンドレットを実行することができます。このとき、コマンドレット単体ではなく、スクリプトファイル(.ps1)を使用することができます。
Invoke-Command で .ps1 ファイルを使用する方法は2つあります。
Invoke-Command -ComputerName SV01 -FilePath .\hogehoge.ps1 Invoke-Command -ComputerName SV02 {.\hogehoge.ps1}
Invoke-Command -ComputerName SV01 -FilePath .\hogehoge.ps1
Invoke-Command -ComputerName SV02 {.\hogehoge.ps1}
前者と後者の違い、わかりますか?
前者の場合は、スクリプトファイルのパスはローカルコンピューターです。後者の場合は、リモートコンピューター内のパスになります。
リモートコンピューターにコマンドレットが存在することさえ確認できていれば、スクリプトファイルをいちいち送信しなくてもよいというのは非常に便利なことです。
(参考)【PowerShell】Hyper-V 系コマンドレットを手元の Windows 7 から実行するには ~ Import-PSSession
ちなみに、指定した ps1 ファイルに引数を指定したい場合にはどうしたらよいでしょう?
以下のように、-ArgumentList パラメタを使用して指定します。
Invoke-Command -ComputerName SV01 -FilePath .\hogehoge.ps1 -ArgumentList “Arg1",“Arg2",“Arg3"
PowerShell...知れば知るほど奥が深いです。
Windows PowerShell には Restart-Computer という便利なコマンドレットが用意されています。
Restart-Computer –ComputerName TARGETCOMPUTER とすれば、リモートコンピューターを再起動することができます。
もしリモートコンピューターに誰かがログオンしていても、-Force を指定することで強制的に再起動できます。もちろん管理者権限が必須です。
さて、ここで問題です。
リモートコンピューターに対して複雑な処理をしているとき、一度再起動が必要な処理があったとします。
もちろん、再起動後にも処理は継続しなければなりません。
どうしましょう?
例えば、以下のようなスクリプトを作れば、待ち合わせは可能です。
※ 2012.6.18 14:21 スクリプトに不備があったので修正しました...すみません
New-PSSession -ComputerName $ServerName Restart-Computer -ComputerName $ServerName -Force do #セッションが切断されるまで待ち合わせ { Start-Sleep -Seconds 1 } Until ((Get-PSSession -ComputerName $ServerName).count -eq 0) do #セッションが再接続されるまで待ち合わせ { Start-Sleep -Seconds 1 $S = New-PSSession -ComputerName $ServerName } Until ((Get-PSSession -ComputerName $ServerName).count -ne 0) echo 再接続されました $S Enter-PSSession $S
比較的簡単に待ち合わせができることに、PowerShell 初心者の方は驚くかもしれませんが、まだ驚くのは早いです。
PowerShell 3.0 では、たった1行で待ち合わせが可能です。
Restart-Computer –ComputerName SERVERNAME –wait
これだけです。
すばらしすぎて腰が抜けそうです。
Windows PowerShell を使用してリモートコンピューターに対して処理を行うには、-ComputerName パラメタを使用するのが一般的です。
例えば、リモートコンピューターのサービス一覧を取得するには、以下のように入力します。
Get-Service –ComputerName Target01
結果は以下の通りです。
Status Name DisplayName ------ ---- ----------- Stopped AeLookupSvc Application Experience Stopped ALG Application Layer Gateway Service Running AppHostSvc Application Host Helper Service Running AppIDSvc Application Identity ・ ・ ・
出力結果にコンピューター名を含めたい場合には、以下のように書きます。
Get-Service –ComputerName Target01 | Select-Object MachineName,Name,Status MachineName Name Status ----------- ---- ------ Target01 AeLookupSvc Stopped Target01 ALG Stopped Target01 AppHostSvc Running Target01 AppIDSvc Running
Get-Service –ComputerName Target01 | Select-Object MachineName,Name,Status
MachineName Name Status ----------- ---- ------ Target01 AeLookupSvc Stopped Target01 ALG Stopped Target01 AppHostSvc Running Target01 AppIDSvc Running
では、リモートコンピューターのサービスを停止したい場合はどうでしょう。
サービスを停止するのは Stop-Service コマンドレットを使用しますが、Stop-Service には –ComputerName パラメタが用意されていません。
じゃ、どうするか。
Invoke-Command コマンドレットを使用して、以下のように書きます。以下は、Windows Update サービスを停止しています。
Invoke-Command –ComputerName TARGET01 { Stop-Service wuauserv }
ただ、これだと戻り値が無いので、本当に止まったのかどうかがわかりません。そこで、Get-Service wuauserv も一緒に実行します。
Invoke-Command -ComputerName TARGET01 {Stop-Service wuauserv; Get-Service wuauser}
Status Name DisplayName PSComputerName ------ ---- ----------- -------------- Stopped wuauserv Windows Update tfdc02
ここで、PSComputerName に注目してください。
ためしに、以下のコマンドレットで Get-Service の出力結果に PSComputerName が含まれているか確認してみましょう。
Get-Service | Get-Member -Name PSComputerName
何も出力されません。つまり、PSComputerName は Get-Service の出力結果ではないということがわかります。
実は、PSComputerName は Invoke-Command の出力結果に含まれるプロパティの1つです。
Invoke-Command はリモートコンピューターに接続する際に PSセッションを作成しますが、このとき PSComputerName には接続先のコンピューター名が格納されます。
では、MachineName はどうなったのでしょう?
以下のコマンドレットを入力してみてください。
Invoke-Command -ComputerName TARGET01 {Start-Service wuauserv; Get-Service wuauser} | Select-Object MachineName,Name,Status
MachineName Name Status ----------- ---- ------ . wuauserv Stopped
MachineName には「.(ドット)」が格納されています。勘の良い方ならばおわかりのように、これは「Current Computer」を意味しています。
Invoke-Command は、コマンドをリモートコンピューターに投げて、リモートで実行した結果を受け取ります。だから MachineName には Current Computer を意味する「.」が格納されているわけです。
一方で、Get-Service –ComputerName Target01 はといえば、Get-Service コマンドレットが実行されるのはローカルです。MachineName プロパティには Get-Service のターゲットコンピューターが格納されるので、-ComputerName パラメタと同じ値を取得することができます。
このように、PowerShell リモーティングでは、そのコマンドがどこで実行されるのかを意識しておく必要があります。
PCComputerName は、リモーティングではとても重要なぱらめたです。ぜひ覚えておいてください。
Windows PowerShell のコマンドレットには、-ComputerName というパラメタを持つものが多く存在します。
これは、WMIを使用してリモートコンピューターのクラスに接続して、情報を取得したり処理を行うためのものです。
-ComputerName には以下のようにして複数のコンピューターを指定することができます。
Get-Service -ComputerName tfdc01,tfsv01 |Select-Object name,machinename Name MachineName ---- ----------- adfssrv tfsv01 AdtAgent tfdc01 AdtAgent tfsv01 ADWS tfdc01 AeLookupSvc tfsv01 AeLookupSvc tfdc01 ・ ・ ・
Get-Service -ComputerName tfdc01,tfsv01 |Select-Object name,machinename
Name MachineName ---- ----------- adfssrv tfsv01 AdtAgent tfdc01 AdtAgent tfsv01 ADWS tfdc01 AeLookupSvc tfsv01 AeLookupSvc tfdc01 ・ ・ ・
結果は、キーとなる Name の昇順で表示されていることがわかります。
もしコンピューターごとにまとめて出力するのであれば、以下のように指定すればOkです。
Get-Service -ComputerName tfdc01,tfsv01 |Sort-Object MachineName |Select-Object MachineName,Name,Status
さて、ここで疑問です。
複数のコンピューター名を指定した場合、処理はパラレルで行われるのでしょうか?それとも ForEach のように順番に処理されるのでしょうか?
ためしに複数のコンピューターに対して以下のコマンドレットを実行してみます。これはリモートコンピューターに接続して3秒後に現在の日時をミリ秒まで取得し表示しています。
$List = "tfsv01","tfsv02","tfsv03","tfdc01","tfdc02","tfops","tfsql2"
Invoke-Command -ComputerName $List {Start-Sleep -Seconds 3 ;$d = Get-Date -Format "yyyy/MM/dd hh:mm:ss.ffffff" ; "$Env:ComputerName $d" }
TFDC01 2012/06/15 12:22:08.706333 TFOPS 2012/06/15 12:22:08.911051 TFSV02 2012/06/15 12:22:08.754154 TFSV03 2012/06/15 12:22:08.789182 TFSQL2 2012/06/15 12:22:08.875165 TFDC02 2012/06/15 12:22:09.124391 TFSV01 2012/06/15 12:22:09.032098
もしシーケンシャルに実行されるのであれば、それぞれの時刻に3秒の差があるはずですが、どうもそうではないようです。
また、シーケンシャルであれば、Listの頭から(もしくはお尻から)実行されるのが普通でしょう。
と考えると、以下のように実行されていることがわかります。
指定したサーバーの順に、シーケンシャルに実行されるわけではない..という点が何とも素敵ですが、逆にこれが面倒を起こすこともありますね。
シーケンシャルに処理を行いたい場合には、ForEach 等を使用して前の処理が終わるまで次の処理に移動しないように制御する必要があることに注意してください。
2012年6月は、毎週1回、10:00~11:30に Windows PowerShell 3.0 のセミナーを継続的に開催しています。
これまでに、2回開催し、残すところあと2回となりました。
既に満席となっておりお申込みいただくことはできないのですが、ひとまずワークフロー編までの資料を以下に公開しました。
随時アップデートしていく予定ですので、興味のある方はダウンロードしてお使いください。
Windows PowerShell 3.0 にはワークフロー機能が実装されています。もちろん、そのベースとなっているのは Windows Workflow Foundation 4.0 です。
例えば、以下のようなワークフローがあったとします。このワークフローでは、UserList.csv ファイルにの保存されたユーザー一覧を読みこんで、大量のユーザーを順次作成する処理を想定していると考えてください。
Workflow CreateUser { Get-Content -Path \\junichia-vdi\tools\ps\wf\UserList.csv -Encoding String | ` Out-File -Path .\UserList_Unicode.csv -Encoding unicode $UserList = Import-Csv -Path .\UserList_Unicode.csv foreach ($u in $UserList) { $UserID = $u.userID Echo -InputObject "$(Get-Date) $UserID を作成します" $password = convertto-securestring -Input $u.initialpassword -asplaintext -force $Department = $u.Department $FirstName = $u.FirstName $LastName = $u.LastName $HomeDirectory = \\Home\Share\$userID InlineScript { New-ADUser ` -Name $using:UserID ` -AccountExpirationDate 2012/12/31 ` -AccountPassword $using:password ` -ChangePasswordAtLogon $using:true ` -Department $using:Department ` -GivenName $using:FirstName ` -Surname $using:LastName ` -HomeDirectory $using:HomeDirectory ` -HomeDrive "Z:" ` -ErrorAction SilentlyContinue ` -ErrorVariable ERRORMESSAGE
If ($ERRORMESSAGE.Count -eq 0) { Echo -InputObject "$(Get-Date),$using:UserID を作成しました" } else { Echo -InputObject "$(Get-Date),$using:UserID $ERRORMESSAGE" } } Checkpoint-Workflow } }
ワークフローの実行までの流れは以下の通りです。
この例では、Invoke-Command を使用してリモートのドメインコントローラー上で、ワークフローをバックグラウンドジョブとして実行しています。
上図の左側に「Stopped」や「Running」と書かれているのはワークフローの状態です。
処理も半ばに差し掛かったころ、なんと停電の影響によりUPSからの指示でサーバーがシャットダウンを始めてしまいました。
このとき、ワークフローは自動的に「Syspended」という状態に移行します。
つまり、実行中のコマンドレットを、その状態ごとフリーズし保存してくれます。
ワークフロー内部で扱っていた変数や出力結果もそのまま残されています。
復電してドメインコントローラーが起動すると、ワークフローは自分ではレジュームできません。
管理者が再度ドメインコントローラーに接続して Resume-Job コマンドレットによりジョブを再起動してあげる必要があります。
通常、ジョブはサスペンドする直後からの処理を再開するので、ユーザー登録は漏れ無く実行することができます。
もし、UPS管理ソフトがコマンドを発行できるタイプであれば、シャットダウン前にジョブをサスペンドし、次回起動時に自動的にレジュームするようスケジューリングすることもできます。
VBSを使用していた時代は、サーバーが落ちたりクライアントが落ちると、ログを解析して続きのバッチを組みなおさなければなりませんでしたが、PowerShell 3.0 ではワークフローによって不測の事態の復旧が、とても簡単になりました。
Windows PowerShell 3.0 では、PowerShell のセッションから一旦切断して、別のコンピューターから再度接続する...ということが可能になっています。
PowerShell Web Accessを使用すれば自宅からの再接続も可能となり、どこからでも仕事がしたいエンジニアにとっては垂涎の機能です。PSWAを経由すればスマートフォンからでも接続できます。すばらしいです。
ここで、こんな疑問が出てきます。
普通はサーバーに「ローカルログオン」して作業しているから、 そこに再接続できるとうれしいんですけど
はい、可能です。
それがループバックPSセッションです。
仕組みも使い方も、リモーティングと全く同じです。
PowerShell コンソールを開いたら、以下のようにしてローカルコンピューターに対して PS セッションを開設します。
PS C:\> $Session = New-PSSession PS C:\> Enter-PSSession $Session [LOCALHOST] PS C:\>
PS C:\> $Session = New-PSSession
PS C:\> Enter-PSSession $Session
[LOCALHOST] PS C:\>
プロンプトが [LOCALHOST] となったことに注意してください。これで、ローカルコンピューターに PS セッションを経由して入り込んだことになります。
作業が完了したら PowerShell コンソールを閉じる前に以下のコマンドを入力します。
[LOCALHOST] PS C:\> Exit PS C:\> Disconnect-PSSession $Session Id Name ComputerName State ConfigurationName Availability -- ---- ------------ ----- ----------------- -------- 1 Session1 localhost Disconnected Microsoft.PowerShell None
[LOCALHOST] PS C:\> Exit
PS C:\> Disconnect-PSSession $Session
Id Name ComputerName State ConfigurationName Availability -- ---- ------------ ----- ----------------- -------- 1 Session1 localhost Disconnected Microsoft.PowerShell None
そうすると、これまで行っていた環境は、Session1 という名前で、State=Disconnected(切断中)、Availability=None(誰も使っていない) となり、未使用のセッションとして保存されます。
あとは、別のマシンから以下のように入力して再接続することができます。
PS C:\> $MySession = Connect-PSSession –ComputerName <サーバー> –Name Session1
PS C:\> Enter-PSSession $MySession
[サーバー] PS C:\>
で、ですね。
おそらく、こう思うはずなのです。
めんどくせっ!
そうなんです。せめて、PowerShell コンソールを開いたら、自動的にループバックセッションが有効になるようにできれば...と思うわけです。
そこで、この処理をプロファイルに埋め込んでしまう方法を考えてみます。プロファイルに埋め込めば、起動時に自動的にセッションの作成や接続を行ってくれます。
PowerShell プロファイルの場所は、$PROFILE に格納されていますので、確認してください。おそらく、以下のパスだと思います。
C:\Users\<ユーザーID>\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1
フォルダが無い場合には自分で作り、その下に Microsoft.PowerShell_profile.ps1 ファイルを作成します。テキストファイルです。
メモ帳か PowerShell ISE で開いたら、以下のように入力してください。
$LoopbackPSSessioin = New-PSSession Enter-PSSession $LoopbackPSSessioin
function quit () { Disconnect-PSSession $LoopbackPSSessioin exit }
何やってるかわかりますか?
1行目と2行目は、コンソールの起動時に実行されます。つまり、ローカルに PSセッションを張って、そこに乗り込んでいるわけですね。コンソールを起動すると、自動的に以下のような表示になります。
[localhost]: PS C:\Users\administrator.TF\Documents>
そして閉じるときは....
[localhost]: PS C:\> Exit ← これは PS セッションから抜けるため C:\PS > quit ← $LoopbackPSSession を残すためにこのコマンドで抜ける
[localhost]: PS C:\> Exit ← これは PS セッションから抜けるため
C:\PS > quit ← $LoopbackPSSession を残すためにこのコマンドで抜ける
と入力します。 Quit コマンドにより、Disconnect-PSSession $LoopbackPSSessioin が実行され、セッションが正常に切断されてコンソールが閉じます。
コンソールを右上の[×]で閉じてしまったり、Exit コマンドで閉じてしまった場合は残念ながら保存されません。
・・・・・・・・・・・・・・・・・・・
うーん、いまいちだなぁ...と金曜日の夜に物思いにふけるのでした...。
すっかりBLOG投稿をさぼってしまっていまして、すみません。
そろそろ落ち着くので、再開いたします。もう書きたいことがたまりまくっていて、どこから手を付けたらよいやら....
さて、既に6400名に登録いただいている Microsoft Virtual Academy ですが、お得なポイント加算キャンペーンを開催しております。
以下のキャンペーンコードを、Microsoft Virtual Academy のサイトにある「キーワードを登録する」から登録していただくと、??ポイントが加算されます。
キャンペーンコード : junichia8945
https://www.microsoftvirtualacademy.com/MyMVA/Dashboard.aspx
ちりも積もれば山となる!
6月末までに登録してください!