これまでHyper-Vへのプログラミングアプローチ方法として、Windows Management Instrumentation(WMI)を使った方法を紹介してきました。WMIとマイクロソフトの開発ツールVisual Studioを使って、サービス向けのHyper-Vのプロビジョニングシステムを開発してきましたが、Visual StudioはWMIとの相性は抜群で、デバッグ処理やコード作成の自動化などが充実しており、システム開発には最適なツールです。しかし、WMIを使ったシステム開発は、使い慣れていないとなかなか挑戦しづらいもの。では、普段みなさんはどうやってHyper-Vの開発をしているのでしょうか?
PowerShellによるHyper-Vの開発
PowerShellはサンプルも豊富で日本語対応もばっちり、メモ帳でも書くことのできるお気軽かつお手軽な開発言語です。Hyper-Vに関していえば、Windows Server 2012 R2 現在、やりたいことはほぼできてしまうという優れものです。私自身、普段の簡単な操作や管理にはPowerShellを使っていますが、Hyper-Vのプロビジョニングシステムには、WMIとVisual Studioの組み合わせを選択しました。
理由の1つは、開発当初のWindows Server 2008 R2 のHyper-Vに対して、PowerShellがすべての機能を制御できなかったということです。ある程度一般的な機能については、コマンドが用意されており、確かに簡単にHyper-Vを制御することが可能でした。ですが、サービス向けのHyper-Vの開発ではコマンドで提供されていない機能もあり、それらに関しては結局PowerShellでWMIを使ってアプローチすることになってしまいました。
2つ目は、Visual Studioの開発効率の高さにあります。PowerShellのコード開発では、残念ながら、Visual Studioを使ったデバックやインテリセンスなどの開発補助機能が使えません。PowerShell ISEというPowerShell用のエディタがありますが、あくまでもPowerShell専用の開発ツールとなっています。
3つ目は、サービス開発においては、Hyper-Vの制御だけではなく、ユーザー管理や課金処理などHyper-V以外の機能も必要となるためです。データーベースとの接続や、既存のシステムとの結合処理、これまでのコードライブラリを使えるというのも、Visual Studioを選択した理由になりました。Visual Studioの使いやすさに慣れてしまうと、スピード重視の開発現場ではなかなかPowerShellをメインでという選択はできませんでした。
PowerShell ISE
Visual Studioによる開発
PowerShellの手軽さや便利さと、Visual Studioの開発効率の良さ、これらを両立することができれば、Hyper-Vの開発はもっとやりやすくなるはずです。Visual StudioのVBやC#からPowerShellのコマンドを呼び出す方法、既存のPowerShellのコード資産を再利用可能なこと、リモート先のHyper-Vに対してもきちんと実行可能なこと、実行結果をきちんと取得できること、デバッグもできること、これらが条件となりそうです。
Visual Studio、.Net Frameworkには、これらを実現する方法が用意されています。Hyper-Vの開発を、より簡単に、よりお手軽に実現する方法をご紹介しましょう。
リモート実行のための事前準備
Windowsには、リモートから管理を行うためのインターフェースが元々用意されています。DCOMやWMI、Windows リモート管理 (WinRM)がこれにあたります。PowerShellをVisual Studioを使ってリモートで実行するには、WinRMを利用するのが簡単です。
WinRMは、HTTPやHTTPSの一般的なプロトコルを使ってリモート先Windowsをコントロールすることができます。デフォルトではWinRMのリモート設定は有効になっていないため、まずはWinRMのリモート設定を有効にします。次に、リモート接続先のサーバー(Hyper-Vホスト)上で、管理者権限でコマンドプロンプトを開き、「winrm qc」と入力します。すでにWinRMサービスは起動しているので、「y」を選択してWinRMをリモート管理用に更新します。
WinRMをリモート管理用に更新
続いて、「WinRM get WinRM/config」を入力してWinRMの設定内容が表示されれば成功です。WinRMはHTTPとHTTPSのプロトコルを使用して通信するので、設定内容には通信するためのポート番号が記載されています。HTTPは5985番、HTTPSは5986番となっています。
Windows ファイヤーウォールが有効になっている場合は、WinRMが有効になったタイミングでファイヤーウォールの設定も自動的に完了しています。今回紹介するのはHTTP接続の方法なので、5985番で通信できるようファイヤーウォールの設定を確認してください。
ファイヤーウォールの設定
コードからの呼び出し方 その1
次は実際のコーディングです。基本的にはSystem.Management.Automation名前空間とSystem.Management.Automation.Runspace名前空間を利用します。
Windows Server 2012 R2であれば、C:\Program Files (x86)\Reference Assemblies\Microsoft\WindowsPowerShell\3.0\System.Management.Automation.dllを参照設定で追加します。
Imports System
Imports System.Text
Imports System.Management.Automation
Imports System.Management.Automation.Runspaces
Imports System.Security
Imports System.Collections.ObjectModel
Imports System.Diagnostics
Imports System.Collections
[ C# ]
using System;
using System.Text;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Security;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Collections;
まずはPowerShellのコマンド「Get-VM」をシンプルに実行してみましょう。PowerShellのみで実行した場合はこのような結果となります。
PowerShellのみで実行した場合
実際にコードからコマンドを呼び出す場合はSystem.Management.Automation.RunspaceInvokeクラスのInvokeメソッドで呼び出します。
実際にコードからコマンドを呼び出す場合はこのようになります。
Public Sub Main()
Dim strCmd As String
strCmd = "Get-VM"
RunPowerShell(strCmd)
End Sub
Public Sub RunPowerShell(ByVal strCmd As String)
'PowerShellのエラーを取得するためのオブジェクト
Dim objErrors As IList = Nothing
Dim objRunspace As Runspace = RunspaceFactory.CreateRunspace()
objRunspace.Open()
Dim objRunspaceInvoke As RunspaceInvoke = New RunspaceInvoke(objRunspace)
'PowerShellコマンドを実行
Dim objResultCollection As Collection(Of PSObject) = objRunspaceInvoke.Invoke(strCmd, Nothing, objErrors)
'PowerShellコマンドの実行結果を出力
For Each objResult As PSObject In objResultCollection
Debug.WriteLine(objResult.Properties("Name").Value)
Next
'PowerShellのエラーを出力
For Each objError As PSObject In objErrors
Dim objRecord As ErrorRecord = objError.BaseObject
Debug.WriteLine(objRecord.Exception.Message)
Next
objRunspace.Close()
End Sub
static void Main()
{
string strCmd;
strCmd = "Get-VM";
RunPowerShell(strCmd);
}
static void RunPowerShell(string strCmd)
{
//PowerShellのエラーを取得するためのオブジェクト
IList objErrors = null;
using (Runspace objRunspace = RunspaceFactory.CreateRunspace())
{
objRunspace.Open();
RunspaceInvoke objRunspaceInvoke = new RunspaceInvoke(objRunspace);
//PowerShellコマンドを実行
Collection<PSObject> objResultCollection = objRunspaceInvoke.Invoke(strCmd, null, out objErrors);
//PowerShellコマンドの実行結果を出力
foreach (var objResult in objResultCollection)
{
Debug.WriteLine(objResult.Properties["Name"].Value);
}
//PowerShellのエラーを出力
foreach (PSObject objError in objErrors)
{
ErrorRecord objRecord = objError.BaseObject as ErrorRecord;
Debug.WriteLine(objRecord.Exception.Message);
}
}
}
実行結果
エラーの取得方法
Invokeメソッドでは、戻り値として、PowerShellのコマンド実行時のエラーをIList 型で取得できます。試しに「Get-VM2」という架空のコマンドを実行してみると、同様のエラーを取得できることが確認できます。
PowerShellでのエラー
Invokeメソッドで取得したエラー
リモートからの実行方法
ここまで紹介したPowerShellコマンドの実行方法は、ローカルのHyper-Vに対して有効な方法となります。リモート先のHyper-Vに対してコードからPowerShellを実行したい場合は、上記の「リモート実行のための事前準備」をしたうえで、2つの方法があります。
1つ目はPowerShellのコマンド自体にリモート実行のコマンドを含める方法です。もう1つは、.Net FrameworkのSystem.Management.Automation.Runspaces. WSManConnectionInfoクラスを利用する方法です。
まずは、PowerShellのコマンドにリモート実行を含めた例をご紹介します。なお、リモート実行のコードをローカルで実行しても、エラーにはならずに、そのまま利用することができます。
Invoke-Commandコマンドでターゲットのサーバーに対してコマンドを実行します。
Set-Item WSMan:\LocalHost\Client\TrustedHosts -Value * -Force;$AdminPassword = ConvertTo-SecureString 'PASSWORD' -AsPlainText -Force;$ObjectTypeName = 'System.Management.Automation.PSCredential';$Cred = New-Object -TypeName $ObjectTypeName -ArgumentList 'DOMAIN\ADMIN', $AdminPassword;Invoke-Command -ComputerName vNext-TP1 -ScriptBlock {Get-VM | Out-String} -Cred $Cred;
コマンドにはリモート先のHyper-Vに対する資格情報として、アカウントとパスワードを引数として渡して、実行するコマンドを指定します。このコマンドをコードに埋め込みます。
Public Sub Main()
'管理者アカウントを指定
Dim strAccount As String = "DOMAIN\Admin"
'パスワードを指定
Dim strPassword As String = "PASSWORD"
'Hyper-Vサーバーを指定
Dim strServer As String = "HvHost01"
'実行するコマンドを指定
Dim strRunCmd As String = "Get-VM | Out-String"
'Invoke-Commandに埋め込み
Dim strCmd As String = ""
strCmd += "Set-Item WSMan:\LocalHost\Client\TrustedHosts -Value * -Force;"
strCmd += "$AdminPassword = ConvertTo-SecureString '" & strPassword & "' -AsPlainText -Force;"
strCmd += "$ObjectTypeName = 'System.Management.Automation.PSCredential';"
strCmd += "$Cred = New-Object -TypeName $ObjectTypeName -ArgumentList '" & strAccount & "', $AdminPassword;"
strCmd += "Invoke-Command -ComputerName " & strServer & " -ScriptBlock {" & strRunCmd & "} -Cred $Cred;"
RunPowerShell(strCmd)
End Sub
{
//管理者アカウントを指定
string strAccount = "DOMAIN\\Admin";
//パスワードを指定
string strPassword = "PASSWORD";
//Hyper-Vサーバーを指定
string strServer= "HvHost01";
//実行するコマンドを指定
string strRunCmd = "Get-VM | Out-String";
//Invoke-Commandに埋め込み
string strCmd = "";
strCmd += "Set-Item WSMan:\\LocalHost\\Client\\TrustedHosts -Value * -Force;";
strCmd += "$AdminPassword = ConvertTo-SecureString '" + strPassword + "' -AsPlainText -Force;";
strCmd += "$ObjectTypeName = 'System.Management.Automation.PSCredential';";
strCmd += "$Cred = New-Object -TypeName $ObjectTypeName -ArgumentList '" + strAccount + "', $AdminPassword;";
strCmd += "Invoke-Command -ComputerName " + strServer + " -ScriptBlock {" + strRunCmd + "} -Cred $Cred;";
RunPowerShell(strCmd);
}
リモートで実行した場合、Invoke-Commandの部分でOut-Stringを追加する場所やVisual Studioで実行するなどによって出力結果が異なる場合があります。
「Get-VM | Out-String」とした場合
Invoke-Command -ComputerName vNext-TP1 -ScriptBlock {Get-VM | Out-String} -Cred $Cred;
Invoke-Commandの最後に「Out-String」とつけた場合
Invoke-Command -ComputerName vNext-TP1 -ScriptBlock {Get-VM} -Cred $Cred | Out-String;
③(②)のコマンドをVisual Studioで実行した場合
どれも戻り値をテキストして取得することは可能ですが、①「Get-VM | Out-String」とした場合は、ローカルで実行した結果をテキストとして出力しています。
②Invoke-Commandの最後に「Out-String」とつけた場合はリモートで実行した結果としてテキスト出力します。
③また、②のコマンドをVisual Studioで戻り値を取得した場合も出力結果が異なります。
PowerShellのコマンドが出力時に情報を整形しているようですが、Visual Studioで出力結果を取得した場合は、整形前の情報が取得できるようです。場合によって使い分けするのがよいでしょう。
まずはPowerShell自体の実行結果の違いです。
①の実行結果(通常の実行結果)
②の実行結果(実行結果にPSComputerName列が増えています)
⑩の実行結果(省略しているが、すべての仮想マシンごとに詳細な情報が出力される)
コードからの呼び出し方 その2
次に紹介するのは、.Net FrameworkのSystem.Management.Automation.Runspaces. WSManConnectionInfoクラスを利用する方法です。この場合はPowerShellのコマンドにはリモート実行のコマンド「Invoke-Comman」や認証情報は必要ありません。WinRMの接続先のURLが「http://" + strServer + ":5985/wsman」となります。5985というポート番号は、最初に設定確認したHTTP接続するためのポート番号となります。実行結果はローカルでコマンドを実行した場合と同じ結果となります。
Public Sub Main()
'管理者アカウントを指定
Dim strAccount As String = "DOMAIN\Admin"
'パスワードを指定
Dim strPassword As String = "PASSWORD"
'Hyper-Vサーバーを指定
Dim strServer As String = "HvHost01"
'実行するコマンドを指定
Dim strRunCmd As String = "Get-VM | Out-String"
'コマンドを実行
RemotePowershell(strServer, strAccount, strPassword, strRunCmd)
End Sub
Public Sub RemotePowershell(ByVal strServer As String, ByVal strAccount As String, ByVal strPassword As String, strCmd As String)
'パスワードを暗号化
Dim objSecureString As New SecureString()
For Each c As String In strPassword.ToCharArray()
objSecureString.AppendChar(c)
Next
Dim objPSCredential As New PSCredential(strAccount, objSecureString)
'WinRMを利用してリモートでPowerShellを実行
Dim objUri As New Uri("http://" + strServer + ":5985/wsman")
Dim objWSManConnectionInfo As New WSManConnectionInfo(objUri, "http://schemas.microsoft.com/powershell/Microsoft.PowerShell", objPSCredential)
Dim objRunspace As Runspace = RunspaceFactory.CreateRunspace(objWSManConnectionInfo)
objRunspace.Open()
Dim objPowerShell As PowerShell = PowerShell.Create()
objPowerShell.Runspace = objRunspace
objPowerShell.AddScript(strCmd)
'PowerShellコマンドの実行結果を出力
Dim objResultCollection As Collection(Of PSObject) = objPowerShell.Invoke()
For Each objResult As PSObject In objResultCollection
'Debug.WriteLine(objResult.Properties("Name").Value)
Debug.WriteLine(objResult.ToString)
Next
'PowerShellのエラーを出力
Dim objErrors As Collection(Of ErrorRecord) = objPowerShell.Streams.Error.ReadAll
For Each objErrorRecord As ErrorRecord In objErrors
Debug.WriteLine(objErrorRecord.Exception.Message)
Next
objPowerShell.Dispose()
objRunspace.Close()
End Sub
static void Main()
{
//管理者アカウントを指定
string strAccount = "DOMAIN\\Admin";
//パスワードを指定
string strPassword = "PASSWORD";
//Hyper-Vサーバーを指定
string strServer= "HvHost01";
//実行するコマンドを指定
string strRunCmd = "Get-VM | Out-String";
//コマンドを実行
RemotePowershell(strServer, strAccount, strPassword, strRunCmd);
}
static void RemotePowershell(string strServer, string strAccount, string strPassword, string strCmd)
{
//パスワードを暗号化
SecureString objSecureString = new SecureString();
foreach (char c in strPassword.ToCharArray()) { objSecureString.AppendChar(c); }
PSCredential objPSCredential = new PSCredential(strAccount, objSecureString);
//WinRMを利用してリモートでPowerShellを実行
Uri objUri = new Uri("http://" + strServer + ":5985/wsman");
WSManConnectionInfo objWSManConnectionInfo = new WSManConnectionInfo(objUri, "http://schemas.microsoft.com/powershell/Microsoft.PowerShell", objPSCredential);
using (Runspace objRunspace = RunspaceFactory.CreateRunspace(objWSManConnectionInfo))
{
objRunspace.Open();
using (PowerShell objPowerShell = PowerShell.Create())
{
objPowerShell.Runspace = objRunspace;
objPowerShell.AddScript(strCmd);
//PowerShellコマンドの実行結果を出力
Collection<PSObject> objResultCollection = objPowerShell.Invoke();
foreach (var objResult in objResultCollection)
{
//Debug.WriteLine(objResult.Properties["Name"].Value);
Debug.WriteLine(objResult.ToString());
}
//PowerShellのエラーを出力
Collection<ErrorRecord> objErrors = objPowerShell.Streams.Error.ReadAll();
foreach (ErrorRecord objErrorRecord in objErrors)
{
Debug.WriteLine(objErrorRecord.Exception.Message);
}
}
}
}
実行結果
さらに、「Pipeline」クラスを使うことで、コマンドをテキストだけではなく、オブジェクトとして実行することができます。Commands.Addメソッドで追加された順番に実行され、次のコマンドに実行結果がパイプラインとして渡されます。
WinRMを利用してリモートでPowerShellを実行
Dim objUri As New Uri("http://" + strServer + ":5985/wsman")
Dim objWSManConnectionInfo As New WSManConnectionInfo(objUri, "http://schemas.microsoft.com/powershell/Microsoft.PowerShell", objPSCredential)
Dim objRunspace As Runspace = RunspaceFactory.CreateRunspace(objWSManConnectionInfo)
objRunspace.Open()
Dim objPipeline As Pipeline = objRunspace.CreatePipeline
objPipeline.Commands.Add(strCmd)
objPipeline.Commands.Add("Out-String")
Try
'PowerShellコマンドの実行結果を出力
Dim objResultCollection As Collection(Of PSObject) = objPipeline.Invoke()
For Each objResult As PSObject In objResultCollection
Debug.WriteLine(objResult.ToString)
Next
Catch ex As Exception
'PowerShellのエラーを出力
Debug.WriteLine(ex.ToString)
End Try
/WinRMを利用してリモートでPowerShellを実行
Uri objUri = new Uri("http://" + strServer + ":5985/wsman");
WSManConnectionInfo objWSManConnectionInfo = new WSManConnectionInfo(objUri, "http://schemas.microsoft.com/powershell/Microsoft.PowerShell", objPSCredential);
using (Runspace objRunspace = RunspaceFactory.CreateRunspace(objWSManConnectionInfo))
{
objRunspace.Open();
using (Pipeline objPipeline = objRunspace.CreatePipeline())
{
objPipeline.Commands.AddScript(strCmd);
objPipeline.Commands.Add("Out-String");
try
{
//PowerShellコマンドの実行結果を出力
Collection<PSObject> objResultCollection = objPipeline.Invoke();
foreach (var objResult in objResultCollection)
{
Debug.WriteLine(objResult.ToString());
}
}
catch (Exception ex)
{
//PowerShellのエラーを出力
Debug.WriteLine(ex.ToString());
}
}
}
既存コード資産の活用
これまでPowerShellでHyper-Vの制御やプロビジョニングを行っていた方も多いと思います。そのような方におすすめなのが、これまでため込んできたコードをそのままVisual Studioから利用する方法です。既存のオリジナルのPowerShellのコードを*.ps1ファイルとして保存するか、もし、*.ps1ファイルとして保存している場合は、引数として渡していたコマンドの代わりに、*.ps1ファイルのパスを指定することでそのまま実行することができます。実行結果をテキストとして取得した場合は、*.ps1ファイルのコードの最後に「Get-VM | Out-String」を追加するか、「Pipeline」クラスを利用して、Commands.Addメソッドで「Out-String」を追加することで、これまでと同様にテキストで取得可能です。
Dim objPowerShell As PowerShell = PowerShell.Create()
objPowerShell.Runspace = objRunspace
objPowerShell.AddScript("C:\MyPowerShell.ps1")
Dim objPipeline As Pipeline = objRunspace.CreatePipeline
objPipeline .Commands.AddScript("C:\MyPowerShell.ps1")
objPipeline .Commands.Add("Out-String")
PowerShell objPowerShell = PowerShell.Create();
objPowerShell.Runspace = objRunspace;
objPowerShell.AddScript("C:\MyPowerShell.ps1");
Pipeline objPipeline = objRunspace.CreatePipeline();
objPipeline .Commands.AddScript("C:\MyPowerShell.ps1");
objPipeline .Commands.Add("Out-String");
以上、Visual StudioでのPowerShellの活用方法をご紹介しました。
Visual StudioとPowerShellを組み合わせることで、以下実現可能となりれまで以上にHyper-Vの開発を効率化して使いやすくしていくことができます。
リモートからも実行可能実行結果も取得可能エラーも取得可能既存資産も活用可能
サンプルコード全体
Imports System
Imports System.Text
Imports System.Management.Automation
Imports System.Management.Automation.Runspaces
Imports System.Security
Imports System.Collections.ObjectModel
Imports System.Diagnostics
Imports System.Collections
Module Main
Sub Main()
'管理者アカウントを指定
Dim strAccount As String = "DOMAIN\Admin"
'パスワードを指定
Dim strPassword As String = "PASSWORD"
'Hyper-Vサーバーを指定
Dim strServer As String = "HvHost01"
'実行するコマンドを指定
Dim strRunCmd As String = "Get-VM | Out-String"
Dim strRunCmd2 As String = "Get-VM" 'or C:\MyPowerShell.ps1
'Invoke-Commandに埋め込み
Dim strCmd As String = "" 'or C:\MyPowerShell.ps1
strCmd += "Set-Item WSMan:\LocalHost\Client\TrustedHosts -Value * -Force;"
strCmd += "$AdminPassword = ConvertTo-SecureString '" & strPassword & "' -AsPlainText -Force;"
strCmd += "$ObjectTypeName = 'System.Management.Automation.PSCredential';"
strCmd += "$Cred = New-Object -TypeName $ObjectTypeName -ArgumentList '" & strAccount & "', $AdminPassword;"
strCmd += "Invoke-Command -ComputerName " & strServer & " -ScriptBlock {" & strRunCmd & "} -Cred $Cred;"
'コマンドを実行
'RunPowerShell(strCmd)
'RemotePowershell(strServer, strAccount, strPassword, strRunCmd)
'RemotePipeline(strServer, strAccount, strPassword, strRunCmd2)
End Sub
Public Sub RemotePipeline(ByVal strServer As String, ByVal strAccount As String, ByVal strPassword As String, strCmd As String)
'パスワードを暗号化
Dim objSecureString As New SecureString()
For Each c As String In strPassword.ToCharArray()
objSecureString.AppendChar(c)
Next
Dim objPSCredential As New PSCredential(strAccount, objSecureString)
'WinRMを利用してリモートでPowerShellを実行
Dim objUri As New Uri("http://" + strServer + ":5985/wsman")
Dim objWSManConnectionInfo As New WSManConnectionInfo(objUri, "http://schemas.microsoft.com/powershell/Microsoft.PowerShell", objPSCredential)
Dim objRunspace As Runspace = RunspaceFactory.CreateRunspace(objWSManConnectionInfo)
objRunspace.Open()
Dim objPipeline As Pipeline = objRunspace.CreatePipeline
objPipeline.Commands.Add(strCmd)
objPipeline.Commands.Add("Out-String")
Try
'PowerShellコマンドの実行結果を出力
Dim objResultCollection As Collection(Of PSObject) = objPipeline.Invoke()
For Each objResult As PSObject In objResultCollection
Debug.WriteLine(objResult.ToString)
Next
Catch ex As Exception
'PowerShellのエラーを出力
Debug.WriteLine(ex.ToString)
End Try
objPipeline.Dispose()
objRunspace.Close()
End Sub
Public Sub RemotePowershell(ByVal strServer As String, ByVal strAccount As String, ByVal strPassword As String, strCmd As String)
'パスワードを暗号化
Dim objSecureString As New SecureString()
For Each c As String In strPassword.ToCharArray()
objSecureString.AppendChar(c)
Next
Dim objPSCredential As New PSCredential(strAccount, objSecureString)
'WinRMを利用してリモートでPowerShellを実行
Dim objUri As New Uri("http://" + strServer + ":5985/wsman")
Dim objWSManConnectionInfo As New WSManConnectionInfo(objUri, "http://schemas.microsoft.com/powershell/Microsoft.PowerShell", objPSCredential)
Dim objRunspace As Runspace = RunspaceFactory.CreateRunspace(objWSManConnectionInfo)
objRunspace.Open()
Dim objPowerShell As PowerShell = PowerShell.Create()
objPowerShell.Runspace = objRunspace
objPowerShell.AddScript(strCmd)
'PowerShellコマンドの実行結果を出力
Dim objResultCollection As Collection(Of PSObject) = objPowerShell.Invoke()
For Each objResult As PSObject In objResultCollection
'Debug.WriteLine(objResult.Properties("Name").Value)
Debug.WriteLine(objResult.ToString)
Next
'PowerShellのエラーを出力
Dim objErrors As Collection(Of ErrorRecord) = objPowerShell.Streams.Error.ReadAll
For Each objErrorRecord As ErrorRecord In objErrors
Debug.WriteLine(objErrorRecord.Exception.Message)
Next
objPowerShell.Dispose()
objRunspace.Close()
End Sub
Public Sub RunPowerShell(ByVal strCmd As String)
'PowerShellのエラーを取得するためのオブジェクト
Dim objErrors As IList = Nothing
Dim objRunspace As Runspace = RunspaceFactory.CreateRunspace()
objRunspace.Open()
Dim objRunspaceInvoke As RunspaceInvoke = New RunspaceInvoke(objRunspace)
'PowerShellコマンドを実行
Dim objResultCollection As Collection(Of PSObject) = objRunspaceInvoke.Invoke(strCmd, Nothing, objErrors)
'PowerShellコマンドの実行結果を出力
For Each objResult As PSObject In objResultCollection
Debug.WriteLine(objResult.ToString)
Next
'PowerShellのエラーを出力
For Each objError As PSObject In objErrors
Dim objRecord As ErrorRecord = objError.BaseObject
Debug.WriteLine(objRecord.Exception.Message)
Next
objRunspace.Close()
End Sub
End Module
using System;
using System.Text;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Security;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Collections;
class Program
{
static void Main()
{
//管理者アカウントを指定
string strAccount = "DOMAIN\\Admin";
//パスワードを指定
string strPassword = "PASSWORD";
//Hyper-Vサーバーを指定
string strServer = "HvHost01";
//実行するコマンドを指定
string strRunCmd = "Get-VM | Out-String";
string strRunCmd2 = "Get-VM"; //or C:\MyPowerShell.ps1
//Invoke-Commandに埋め込み
string strCmd = ""; //or C:\MyPowerShell.ps1
strCmd += "Set-Item WSMan:\\LocalHost\\Client\\TrustedHosts -Value * -Force;";
strCmd += "$AdminPassword = ConvertTo-SecureString '" + strPassword + "' -AsPlainText -Force;";
strCmd += "$ObjectTypeName = 'System.Management.Automation.PSCredential';";
strCmd += "$Cred = New-Object -TypeName $ObjectTypeName -ArgumentList '" + strAccount + "', $AdminPassword;";
strCmd += "Invoke-Command -ComputerName " + strServer + " -ScriptBlock {" + strRunCmd + "} -Cred $Cred;";
//コマンドを実行
//RunPowerShell(strCmd);
//RemotePowershell(strServer, strAccount, strPassword, strRunCmd);
//RemotePipeline(strServer, strAccount, strPassword, strRunCmd2);
}
static void RemotePipeline(string strServer, string strAccount, string strPassword, string strCmd)
{
SecureString objSecureString = new SecureString();
foreach (char c in strPassword.ToCharArray()) { objSecureString.AppendChar(c); }
PSCredential objPSCredential = new PSCredential(strAccount, objSecureString);
//WinRMを利用してリモートでPowerShellを実行
Uri objUri = new Uri("http://" + strServer + ":5985/wsman");
WSManConnectionInfo objWSManConnectionInfo = new WSManConnectionInfo(objUri, "http://schemas.microsoft.com/powershell/Microsoft.PowerShell", objPSCredential);
using (Runspace objRunspace = RunspaceFactory.CreateRunspace(objWSManConnectionInfo))
{
objRunspace.Open();
using (Pipeline objPipeline = objRunspace.CreatePipeline())
{
objPipeline.Commands.AddScript(strCmd);
objPipeline.Commands.Add("Out-String");
try
{
//PowerShellコマンドの実行結果を出力
Collection<PSObject> objResultCollection = objPipeline.Invoke();
foreach (var objResult in objResultCollection)
{
Debug.WriteLine(objResult.ToString());
}
}
catch (Exception ex)
{
//PowerShellのエラーを出力
Debug.WriteLine(ex.ToString());
}
}
}
}
static void RemotePowershell(string strServer, string strAccount, string strPassword, string strCmd)
{
//パスワードを暗号化
SecureString objSecureString = new SecureString();
foreach (char c in strPassword.ToCharArray()) { objSecureString.AppendChar(c); }
PSCredential objPSCredential = new PSCredential(strAccount, objSecureString);
//WinRMを利用してリモートでPowerShellを実行
Uri objUri = new Uri("http://" + strServer + ":5985/wsman");
WSManConnectionInfo objWSManConnectionInfo = new WSManConnectionInfo(objUri, "http://schemas.microsoft.com/powershell/Microsoft.PowerShell", objPSCredential);
using (Runspace objRunspace = RunspaceFactory.CreateRunspace(objWSManConnectionInfo))
{
objRunspace.Open();
using (PowerShell objPowerShell = PowerShell.Create())
{
objPowerShell.Runspace = objRunspace;
objPowerShell.AddScript(strCmd);
//PowerShellコマンドの実行結果を出力
Collection<PSObject> objResultCollection = objPowerShell.Invoke();
foreach (var objResult in objResultCollection)
{
//Debug.WriteLine(objResult.Properties["Name"].Value);
Debug.WriteLine(objResult.ToString());
}
//PowerShellのエラーを出力
Collection<ErrorRecord> objErrors = objPowerShell.Streams.Error.ReadAll();
foreach (ErrorRecord objErrorRecord in objErrors)
{
Debug.WriteLine(objErrorRecord.Exception.Message);
}
}
}
}
static void RunPowerShell(string strCmd)
{
//PowerShellのエラーを取得するためのオブジェクト
IList objErrors = null;
using (Runspace objRunspace = RunspaceFactory.CreateRunspace())
{
objRunspace.Open();
RunspaceInvoke objRunspaceInvoke = new RunspaceInvoke(objRunspace);
//PowerShellコマンドを実行
Collection<PSObject> objResultCollection = objRunspaceInvoke.Invoke(strCmd, null, out objErrors);
//PowerShellコマンドの実行結果を出力
foreach (var objResult in objResultCollection)
{
Debug.WriteLine(objResult.ToString());
}
//PowerShellのエラーを出力
foreach (PSObject objError in objErrors)
{
ErrorRecord objRecord = objError.BaseObject as ErrorRecord;
Debug.WriteLine(objRecord.Exception.Message);
}
}
}
}
サンプルコードをこちらからダウンロードいただけます。 → SampleCode.zip(19.4KB)