⚙️

PowerShell モジュールで Application Insights を使用する

に公開

前提条件として、こちらの記事を読むことをおすすめします。

https://docs.microsoft.com/ja-jp/powershell/scripting/dev-cross-plat/resolving-dependency-conflicts?WT.mc_id=M365-MVP-5002941

C# で開発した PowerShell モジュールに Application Insights を導入する場合、PowerShell はコンソールアプリと考えることができます。そのため、Application Insights については以下の記事の手順に従って導入できます。

https://docs.microsoft.com/ja-jp/azure/azure-monitor/app/worker-service?WT.mc_id=M365-MVP-5002941

コード例は次のとおりです。

[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 です。

https://github.com/PowerShell/PowerShell/blob/v7.0.0/src/System.Management.Automation/System.Management.Automation.csproj

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 に公開されているため、同じことを実装すれば問題ありません。

https://github.com/microsoft/ApplicationInsights-dotnet/blob/main/NETCORE/src/Microsoft.ApplicationInsights.WorkerService/ApplicationInsightsExtensions.cs

先ほどのコードを修正します。

[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