🎉

Azure Functions から Log Analytics へクエリを投げる

2021/08/30に公開

先日 C# から Log Analytics にクエリを投げる記事を書きました。

https://zenn.dev/okazuki/articles/execute-query-to-loganalytics-on-dotnet

今回は、これを Azure Functions 空やってみたいと思います。

その前に…

多くの場合 Log Analytics にアラート設定して Azure Functions を呼び出すように構成しておけば特定のログがあったときの処理を書くことが出来ます。なので Azure Functions から Log Analytics にクエリをするような機会は一般的には少ないと思います。

詳しくは、以下のドキュメントあたりを確認してください。

https://docs.microsoft.com/ja-jp/azure/azure-monitor/alerts/action-groups

クエリをしてみよう

ということで、クエリしたくなった時のことを考えてやっていきましょう。
前の記事ではローカルのコンソール アプリケーションから Azure AD のサービス プリンシパルで認証を行う形でやりました。

今回は Azure Functions なので、システム割り当てのマネージド ID で認証をするようにしたいと思います。
なので、アクセストークンの取得の部分のコードがコンソールアプリだと以下のようになっていたので、この部分をサービスプリンシパルから取得するようにして、Azure Functions の流儀に従うように書き直してやれば良さそうです。

var creds = await ApplicationTokenProvider.LoginSilentAsync(
    appSettings.AADDomain,
    appSettings.ClientId,
    appSettings.ClientSecret,
    adSettings);

var client = new OperationalInsightsDataClient(creds);

上記コード内の creds 変数は ServiceClientCredentials 型なのですが、これの派生クラスでマネージド ID 対応しているものを知らない(あるのかもしれませんが…)ので、今回はさくっと自作しようと思います。なので Azure Functions のプロジェクトを作ったら以下のパッケージを追加して、あとはガリっとコードを書いていきます。

  • Microsoft.Azure.OperationalInsights
    • Log Analytics のクライアントライブラリ
  • Microsoft.Azure.Services.AppAuthentication
    • マネージドIDで認証してトークンをとってくれる AzureServiceTokenProvider がいる

では、ServiceClientCredentials を継承して Bearer トークンを AzureServiceTokenProvider から取得してリクエストヘッダーに設定する処理を実装します。これは ProcessHttpRequestAsync をオーバーライドすればいいだけなのでこんな感じで。

class AzureServiceTokenProviderCredentials : ServiceClientCredentials
{
    private readonly AzureServiceTokenProvider _azureServiceTokenProvider;
    private readonly string _resource;
    private readonly string _tenantId;

    public AzureServiceTokenProviderCredentials(
        AzureServiceTokenProvider azureServiceTokenProvider, 
        string resource, 
        string tenantId)
    {
        _azureServiceTokenProvider = azureServiceTokenProvider;
        _resource = resource;
        _tenantId = tenantId;
    }

    public override async Task ProcessHttpRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        request.Headers.Authorization = new AuthenticationHeaderValue(
            "Bearer", 
            await _azureServiceTokenProvider.GetAccessTokenAsync(_resource, _tenantId, cancellationToken));
    }
}

とりあえず動作確認が目的なので、まずは全部ハードコードした状態で関数を実装してみましょう。

public static class QueryFunction
{
    [FunctionName("QueryFunction")]
    public static async Task<IActionResult> Run(
        [HttpTrigger(AuthorizationLevel.Function, "get")] HttpRequest req,
        ILogger log)
    {
        // Log Analytics にクエリをするためのクライアントを作成
        var client = new OperationalInsightsDataClient(new AzureServiceTokenProviderCredentials(
            new AzureServiceTokenProvider(),
            "https://api.loganalytics.io/",
            "AADのテナントID"))
        {
            WorkspaceId = "クエリをする Log Analytics のワークスペース ID",
        };

        // 適当にクエリを投げて
        var results = await client.QueryAsync("union * | take 5");
        // 結果を返す
        return new OkObjectResult(results);
    }
}

これで実行すると、ちゃんと権限が設定されていると(ローカル実行の場合は Visual Studio の設定でちゃんと設定されていると)Log Analytics の中のデータが 5 件取れてくるはずです。

結構簡単でした。

Microsoft (有志)

Discussion