前の記事はこちら
前の記事ではPowershellをASP.NET APIで動かすことが出来ました。
しかしこの書き方だと自作の関数やモジュールを動かすことができませんでした。
日本語ではこの情報がまったく見つからなかったので海外Webの海に飛び込み必死に調べ、ついに完全に理解したので記事にします。
動作環境
.NET 7.0
Microsoft.Powershell.SDK(7.3.2) NuGetからインストール
実際に自作モジュールを動かしてみよう
自作モジュールを作ってお好みの場所に設置
Function gethostname{ invoke-command -scriptblock {hostname} | out-file C:\log\hostname.txt }
こんな自作関数をpsm1ファイルとして保存することで自作モジュールになります。
[cmdletbinding()]は使うとAPIで実行できなくなるので使わないでください。
この自作モジュールファイルをお好みの場所に置いておきます
コード
前の記事から加えたところはinitialsessionstateという部分と、その中でPowershellの実行ポリシーを指定しているところですね。
// Use Custom Powershell Module in C# using System; using System.Diagnostics; using Microsoft.AspNetCore.Mvc; using System.Management.Automation; using System.Management.Automation.Runspaces; namespace test.Controllers //ここはプロジェクト名.controllerです { [Route("api/[controller]")] [ApiController] public class ValuesController : ControllerBase { //GET hostnameを取得 [HttpGet] [Route("gethost")] public void RunPowerShell() { var initialState = InitialSessionState.CreateDefault2(); initialState.ExecutionPolicy = Microsoft.PowerShell.ExecutionPolicy.Unrestricted; using var ps = PowerShell.Create(initialState); var results = ps.AddScript(@" Import-Module 'C:\Users\User\Documents\WindowsPowerShell\Modules\gethostname' gethostname ").Invoke(); //import-moduleのモジュール部分はフルパスで } } }
Import-Moduleのところは先ほど作成した自作モジュールのパスを入れます。
無事swaggerで200が帰ってきて、かつ画像の通り宛先ホストのhostnameが取得できました。
どうやらInitial Session Stateがキモみたいです
Initial Session Stateはセッションを作るときのコマンド、変数、モジュールなど、実行空間の特性を指定できると書いてあります。
前の記事ではどうやら実行ポリシーの関係上Import-Moduleを実行することができなかったようなので、
Initial Session Stateに実行ポリシーをUnrestrictedに指定することによって、自作モジュールを動かすことができたというわけです。
実行ポリシーが指定されていないと怒られます
InitialSessionState.CreateDefault2メソッドだとMicrosoft.PowerShell.Core モジュールがインポートされています。
InitialSessionState.CreateメソッドだとCoreモジュールもないまっさらな実行空間が作られます。
Runspaceは必須ではないみたいですが、なぜいらないのかあまりわかっていないので一応Runspaceを付けたいですね。。
あと好みの問題ですが、複数行のスクリプトよりもコマンドで実行させたいなと。。
改良してみた
// Use Custom Powershell Module in C# using System; using System.Diagnostics; using Microsoft.AspNetCore.Mvc; using System.Management.Automation; using System.Management.Automation.Runspaces; namespace test.Controllers //ここはプロジェクト名.controllerです { [Route("api/[controller]")] [ApiController] public class ValuesController : ControllerBase { //GET hostnameを取得 [HttpGet] [Route("gethost")] public void RunPowerShell() { var initialState = InitialSessionState.CreateDefault2(); initialState.ExecutionPolicy = Microsoft.PowerShell.ExecutionPolicy.Unrestricted; initialState.ImportPSModulesFromPath(@"C:\Users\User\Documents\WindowsPowerShell\Modules\hosttest"); Runspace runspace = RunspaceFactory.CreateRunspace(initialState); runspace.Open(); using var ps = PowerShell.Create(initialState); ps.Runspace = runspace; var results = ps.AddCommand("hostget").Invoke(); } } }
Initial Session Stateで実行ポリシーとモジュールも先に追加することによってImport-moduleコマンドをなくし、一応Runspaceを追加しています。
InitialSessionState.Createメソッドで一から使うコマンドの指定をするのもありかとは思いますが、一つ一つ作っていかないといけないのでCreateDefault2メソッドが最適だと思います。
とてもセキュリティが厳しい環境ならCreateメソッドを使うのはありだとは思います。。
まとめ
モジュール読み込みができるようになったことでAPIから自作関数を実行できるようになりました。
これでAPIから様々なPowershellの機能を短く便利に叩くことができますね!
一応二つの方法を記事にしましたが、好みですねここは。。
そして、モジュールさえも作らなくても一番目のコードのようにps.AddScript(@””)で囲むと複数行のスクリプトも実行できるのでそれでもいいと思います。。
ただ、様々な機能を含んだ複雑な実装になると管理も可読性も低くなるので、Powershellの部分はモジュール化してしまうのがおすすめでございます。
次の記事はこちらです