PowerShell モジュールで Application Insights を使用する
前提条件としてこちらの記事を読むことをおすすめします。
C# で開発した PowerShell モジュールに Application Insights を導入するとします。PowerShell はコンソール アプリと考えることができるので Application Insights について以下の記事の手順にしたがって導入すればよさそうです。
コードとしてはこんな感じになります。
[Cmdlet("Invoke", "SampleApplication")]
public class SampleApplicationCommand : PSCmdlet
{
private readonly ServiceProvider ServiceProvider = new ServiceCollection()
.AddApplicationInsightsTelemetryWorkerService("{{instrumentation-key}}")
.BuildServiceProvider();
protected override void ProcessRecord()
{
var telemetryClient = ServiceProvider.GetService<TelemetryClient>();
try
{
telemetryClient.TrackEvent(nameof(SampleApplicationCommand));
}
catch (Exception ex)
{
telemetryClient.TrackException(ex);
}
finally
{
telemetryClient.Flush();
}
}
}
ここで気になるのが Application Insights のバージョンについて。最初の記事を読んでいただければわかるのですが、PowerShell モジュールが読み込む外部 dll のバージョンは PowerShell 本体 (System.Management.Automation.dll) に依存します。PowerShell 7 からは Application Insights を使ってテレメトリを送信する機能が組み込まれており、当然ながら Application Insights の dll も PowerShell 本体によって読み込み済みであるため、新しいバージョンの dll を読み込もうとすると失敗します。特に公開情報はないので、リリース情報から調べてみると、Application Insights のバージョンは 2.13.1 です。
PowerShell 6 については、Application Insights を使っておらず、なによりすでにサポート切れなので、考慮しなくていいでしょう。よって 2.13.1 を使えば解決しそうです。
<ItemGroup>
<PackageReference Include="Microsoft.ApplicationInsights.WorkerService" Version="2.13.1" />
</ItemGroup>
と、ここまでが .NET Core ベースの PowerShell の話。ご存じのこととは思いますが PowerShell には Windows にバンドルされた Windows PowerShell (5.1) があります。Application Insights 2.13.1 で作った PowerShell モジュールを Windows PowerShell で実行するとエラーになります。
Invoke-SampleApplication : ファイルまたはアセンブリ 'Microsoft.Extensions.DependencyInjection.Abstractions, Version=2.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'、またはその依存関係の 1 つが読み込めませんでした。指定されたファイルが見つかりません。
発生場所 行:1 文字:1
+ Invoke-SampleApplication
+ ~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], FileNotFoundException
+ FullyQualifiedErrorId : System.IO.FileNotFoundException
仕方がないので Microsoft.ApplicationInsights.WorkerService のバージョンを下げていくしかないのですが、NuGet で公開されているもっとも古いバージョンでも解決できませんでした。掘り下げていくと AddApplicationInsightsTelemetryWorkerService
メソッドを使うのが駄目ということがわかりました。なので Microsoft.ApplicationInsights.WorkerService そのものを諦めるしかないです。幸いコードは GitHub に公開されているので同じことをやればいいだけの気がします。
先ほどのコードを修正します。
[Cmdlet("Invoke", "SampleApplication")]
public class SampleApplicationCommand : PSCmdlet
{
private readonly ServiceProvider ServiceProvider = new ServiceCollection()
.AddSingleton(provider => new TelemetryConfiguration("{{instrumentation-key}}"))
.AddSingleton<ITelemetryChannel, InMemoryChannel>()
.AddSingleton<TelemetryClient>()
.BuildServiceProvider();
protected override void ProcessRecord()
{
var telemetryClient = ServiceProvider.GetService<TelemetryClient>();
try
{
telemetryClient.TrackEvent(nameof(SampleApplicationCommand));
}
catch (Exception ex)
{
telemetryClient.TrackException(ex);
}
finally
{
telemetryClient.Flush();
}
}
}
念のため Microsoft.ApplicationInsights.WorkerService ではなく Microsoft.ApplicationInsights を参照するようにします。
<ItemGroup>
<PackageReference Include="Microsoft.ApplicationInsights" Version="2.13.1" />
</ItemGroup>
本来であれば PowerShell モジュールごとに任意のバージョンの dll が呼び出せればいいのですが、.NET Core ではアプリケーション ドメインの分離がサポートされません。よって PowerShell のこの仕様はやむを得ないところはあるのですが、正直めちゃくちゃしんどいです。
Discussion