tech.guitarrapc.cóm

Technical updates

MSIを使ったStorage Account(Blob, Queue) の認証を使ってQueueの監視を行う

Azure の Storage Account アクセスといえばConnection String ですが、Managed Service Identity (MSI) による AzureAD認証が可能です。(2019/3/25 に GAしたはず.... あれ?)

ここでは、Storage Account ではなく MSI を使ったAzure Functions からのアクセスについてみてみます。

目次

TL;DR

Azure Functions の Binding でのMSI認証はできない。

StorageCredentials を作ってから、CloudBlobClient/CloudQueueClient を作ることになる。

ローカルのMSI認証は現在バグあり。

なぜMSIなのか

MSIを使うことで認証情報をいちいちStorage Account からひっぱてきて参照可能な形で渡す必要がなくなります。 KeyVaultを使うにしても、AppSettingsを使うにしてもConnectionStrings を埋めるのは嫌ですし、IAM = RBAC = AzureAD の認証下でアプリケーションのアクセスが制御されるのは望ましいでしょう。

とくにTerraform を使って構成している場合は、MSIの有効化、IAM でのStorageへの割り当てまで構成時点で完了するのでアプリケーションから見ると透過的に扱えてより効果的でしょう。

AppServiceでMSIを使う

AppService (AzureFunctions) でMSIを使うには、SystemIdentity を有効にします。

Azure Portal で設定する

Azure Portal だと「アクセスを持ちたい側でMSIを設定」、「アクセスされる側のIAMでロール設定」を行います。

まずは、Function App でMSIを設定します。

FunctionApp > AppSettings > Identity

SystemAssigned を Onにする

これでAzureAD のAppRegistration にアプリケーションが登録されたので、アクセスされるStorage Account のIAMでアクセス権限を設定してあげます。

アクセスを許可するリソースでIAM > Role assignments でロール設定する

IAM は Subscription > Resource Group > Resource で継承されるので、そのResourceに限定したいなら ResourceのIAMでロール設定すればokです。もしResourceGroup全体で利用したいなら、 Resource Group のIAMでルール設定すればok です。

Terraform で構成する

こんなことをやっていたら時間がなくなるので、Terraform でFunctionAppの作成からロール設定をします。

gist.github.com

Terraform でMSIを設定するポイントについて説明します。MSIを使う側であるazurerm_function_app でMSIを有効化するのはidentityです。これはVMでもACIでも変わらないのでまず設定するといいでしょう。

  identity = {
    type = "SystemAssigned"
  }

続いて、azurerm_role_assignment を使ってIAMを設定します。ここではわかりやすいように、Resource Groupに対してContributerロールを設定しています。AzureRM の返り値的に、principal_idがlookup必須なのがあんまりイケテマセンが公式です。

resource "azurerm_role_assignment" "main" {
  scope                = "${data.azurerm_resource_group.current.id}"
  role_definition_name = "Contributor"
  principal_id         = "${lookup(azurerm_function_app.main.identity[0], "principal_id")}"
}

さぁこれでMSIの準備はできました。

AzureFunctions でMSIを使ってStorage Account にアクセスする

前回の記事で、StorageAccountのConnection String を使って認証をとりましたが、これをMSIに切り替えます。

tech.guitarrapc.com

とはいえ、MSIはAzure 上のリソースで利用可能な他、ローカルでも az login や Visual Studio の Option > Account から Azureに接続していれば利用できます。と思うじゃないですか? Storage Account に関しては現状動かないです。

https://github.com/Azure/azure-libraries-for-net/issues/557 Local MSI Login using AAD account · Issue #557 · Azure/azure-libraries-for-net · GitHub

認証ヘッダがうまく動いていないので、直るまでローカルはConnection String にバイパスします。バイパスはAzure環境かどうかを WEBSITE_INSTANCE_ID環境変数でチェックして分岐することにしましょう。

gist.github.com

前回のコードから、CloudQueueClient の取得部分をMSIに変えるようにしてみます。変更箇所はCloudQueueClientの作成部分だけです。

gist.github.com

MSI から取得するときのポイントは、3つあります。

  1. AzureServiceTokenProvider を使って認証TokenのProvider経由で認証を作ります
    • MSIは認証プロバイダー経由で認証を作りましょう
  2. azureServiceTokenProvider.GetAccessTokenAsync("アクセストークンの請求先") で適切なURIを指定する必要があります
  3. new CloudQueueClient(new StorageUri(new Uri($"https://{storageAccountName}.queue.core.windows.net")), storageCredentials);のように、URIとCredentialを指定してクライアントを作成します
    • ConnectionString を使っている時は接続先のStorageAccountが明示されていたので、account.CreateCloudQueueClient(); と書けましたが、MSIではStorageAccountが不明なので接続先が作れません
    • このため、StorageCredential からStorageAccountを作るのではなく、URIでアクセス先のStorageAccountNameと一緒に指定してあげます

MSIがローカルで使えるようにバグ修正されるまで、Azure環境とローカルで分岐してあげます。

// connect to Azure Storage
var queueClient = context.IsAzureEnvironment()
    ? await CreateQueueClientAsync("YOUR_STORAGE_ACCOUNT_NAME")
    : CreateQueueClient(Environment.GetEnvironmentVariable("queue_storage_connection_string"));

ということでMSIに対応したコード全体像です。

gist.github.com

まとめ

MSI を使えるなら使いましょう。ConnectionStringとは使うのやめましょ。

MSI、Azureの仕組みは分かりやすいです。一方で、アプリからの利用が分かりにくいというか整理されたドキュメントがないので認証先のドキュメント把握、仕組みの整理に手間取ったです。

余談

2019/3/25 にStorage Account (Blob, Queue) の AzureAD認証がGAしたはずの記事が出ているのですが、現在アクセスできません。

https://azure.microsoft.com/en-us/blog/azure-storage-support-for-azure-ad-based-access-control-now-generally-available/

これがキャッシュです。

Google Cache : Azure Storage support for Azure Active Directory based access control generally available | Blog | Microsoft Azure

ブログ一覧にもないのと、アクセス先URIでもPreviewになっているので、GAキャンセルでまだPreview っぽい?

Azure Services that support managed identities for Azure resources | Microsoft Docs

Azure updates | Microsoft Azure

Ref:

Azure リソース (プレビュー) の Azure Active Directory 管理 ID を持つ blob およびキューへのアクセスの認証 - Azure Storage | Microsoft Docs

仮想マシン上で Azure リソースのマネージド ID を使用してアクセス トークンを取得する方法 | Microsoft Docs

c# - Azure Storage authentication via AzureServiceTokenProvider for CloudTableClient - Stack Overflow

Azure AD Authentication with Azure Storage + Managed Service Identity - Joonas W's blog

Local MSI Login using AAD account · Issue #557 · Azure/azure-libraries-for-net · GitHub

*1:びっくりするぐらいわかりにくい。