しばやん雑記

ASP.NET Core とメイドさんが大好きなフリーランスのプログラマーのブログ

Azure Resources に対して Kusto を使ってクエリが書ける Resource Graph で遊んだ

Azure にデプロイ済みのリソースをいい感じにクエリしたかったので、その辺りについてぶちぞう RD に聞いたら Resource Graph を使えと言われたので色々触って遊んでみました。

割と前からあるサービスでした。ドキュメントを読む限りでは、Azure Resource Manager のデータを Data Explorer に詰めて、柔軟にクエリを実行出来るようにしたもののようです。

変更通知を使ってデータを更新してるらしいので、ほぼリアルタイムで情報は反映されるようです。

肝心のデータは Kusto を使って取得することになります。Azure のクエリでは Kusto は避けて通れない感ありますが、まあ得意な人がクエリを書けばよいと思います。

Azure Monitor では Kusto は必須なので、興味があればいくつかパターンを覚えておけば良いです。

最近、またいろんな KQL を書いたので暇な時にチートシート的に残しておこうと思います。

とりあえず Resource Graph を Cloud Shell と C# から使ってみたので、使い方を残しておきます。

Cloud Shell から利用する

Cloud Shell の Azure CLI には Resource Graph の拡張が入っていないので、最初にインストールします。

az extension add --name resource-graph

Resource Graph の API はクエリ実行しかないので簡単です。az graph query だけ覚えておけば使えます。

例としてリソース名に "daru" が含まれているものを 3 件取得するクエリを書いてみました。

# リソース名に daru が含まれているものを 3 件取得
az graph query -q "where name contains 'daru' | project name, type | take 3"

Azure Monitor の時のようにテーブルを最初に指定する必要はないです。いきなりオペレータを書きます。

Cloud Shell で実行すると、ちゃんと指定したクエリの結果が返って来ます。

f:id:shiba-yan:20190709231738p:plain

使い方はこれだけです。多少の Azure Resource Manager の理解と Kusto が書ければ自由な条件で、リソースに対するクエリを柔軟に書けるので便利です。

Resource Provider を絞り込めば、そのリソースが持つ固有のプロパティも簡単に参照できます。

f:id:shiba-yan:20190709234315p:plain

上のクエリでは App Service が使っている Plan の SKU をテーブル表示しています。条件を工夫すれば、設定抜けのチェックなどをクエリで書くことも出来そうです。

C# SDK から利用する

NuGet で公開されている以下のパッケージを使うと、C# からも同じように Kusto で Azure Resources へのクエリを実行することが出来ます。

少し使い方に癖があるので、簡単なクエリを実行するサンプルを載せておきます。

サブスクリプション ID は最低でも 1 つ必要なのと、QueryRequestOptions#ResultFormatObjectArray を指定する部分がポイントです。特に ResultFormat はデフォルトでは DataTable のようなカラムとデータが分かれた形で返ってくるので扱いにくいです。

class Program
{
    static async Task Main(string[] args)
    {
        var resourceGraphClient = new ResourceGraphClient(new TokenCredentials(new AppAuthenticationTokenProvider()));

        var query = new QueryRequest
        {
            Subscriptions = new[] { "<subscription_id>" },
            Query = "where type =~ 'microsoft.web/sites' | project name, location, properties.sku",
            Options = new QueryRequestOptions
            {
                ResultFormat = ResultFormat.ObjectArray
            }
        };

        var resources = await resourceGraphClient.ResourcesAsync(query);

        var result = ((JToken)resources.Data).ToObject<QueryResultModel[]>();

        foreach (var item in result)
        {
            Console.WriteLine($"{item.Name},{item.Location},{item.Sku}");
        }
    }
}

public class QueryResultModel
{
    [JsonProperty("name")]
    public string Name { get; set; }

    [JsonProperty("location")]
    public string Location { get; set; }

    [JsonProperty("properties_sku")]
    public string Sku { get; set; }
}

クエリの実行結果は Data プロパティに入っているので、適当にキャストしてモデルにバインドしてあげれば扱いやすい形で取り出せます。resourcesAsync が Generics Method になっていれば便利でしたね。

上のコードを実行すると、Azure CLI で実行したのと同じ結果が返って来ます。

本題とは関係ないですが、Managed Identities を使って各 ARM クライアントを使う際には、以下のような ITokenProvider を実装したクラスを用意しておけば便利に使えます。

internal class AppAuthenticationTokenProvider : ITokenProvider
{
    private static readonly AzureServiceTokenProvider TokenProvider = new AzureServiceTokenProvider();

    public async Task<AuthenticationHeaderValue> GetAuthenticationHeaderAsync(CancellationToken cancellationToken)
    {
        var accessToken = await TokenProvider.GetAccessTokenAsync("https://management.azure.com/", cancellationToken);

        return new AuthenticationHeaderValue("Bearer", accessToken);
    }
}

ASP.NET Core や Azure Functions などで DI を使ってクライアントを取りたい時など、Task ベースの非同期が使えない場面でも ITokenProvider を用意すれば非同期のままアクセストークンを取得できます。

取得したアクセストークンはキャッシュされているので、何回呼び出しても有効期限内は同じものが返って来ます。DI で都度インスタンスを作成しても安心です。