Visual StudioとPowerShellの良い関係 for Hyper-V

Visual StudioはWMIとの相性は抜群

これまで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)

著書の紹介欄

Hyper-Vで本格的なサーバー仮想環境を構築。仮想環境を設定・操作できる!

できるPRO Windows Server 2016 Hyper-V

◇Hyper-Vのさまざまな機能がわかる ◇インストールからの操作手順を解説 ◇チェックポイントやレプリカも活用できる Windows Server 2016 Hyper-Vは、仮想化ソフトウェア基盤を提供する機能であり、クラウドの実現に不可欠のものです。 本書では、仮想化の基礎知識から、Hyper-Vでの仮想マシンや仮想スイッチの設定・操作、プライベートクラウドの構築、Azureとの連携などを解説します。

初めてのWindows Azure Pack本が発売

Windows Azure Pack プライベートクラウド構築ガイド

本書は、Windows Azure PackとHyper-Vを利用し、企業内IaaS(仮想マシン提供サービス)を構成するための、IT管理者に向けた手引書です。試用したサーバーは、最小限度の物理サーバーと仮想マシンで構成しています。Windows Azure Packに必要なコンポーネントのダウンロード、実際にプライベートクラウド構築する過程を、手順を追って解説しています。これからプライベートクラウドの構築を検討するうえで、作業負担の軽減に役立つ一冊です。

ブログの著者欄

樋口 勝一

GMOインターネット株式会社

1999年6月GMOインターネットに入社。Windows Serverをプラットフォームとしたサービス開発から運用・保守まで幅広く担当。講演登壇や出版、ネット記事連載などでマイクロソフト社と強い信頼関係を構築。2007年より「マイクロソフトMVPアワード」を受賞し、インターネットソリューションのスペシャリストとして活躍。

採用情報

関連記事