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からのアクセスについてみてみます。

概要

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の作成からロール設定をします。

https://gist.github.com/guitarrapc/e54e417361d8a41d9e87eedb7bb52530

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に切り替えます。

https://tech.guitarrapc.com/entry/2019/03/28/020631

とはいえ、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環境変数でチェックして分岐することにしましょう。

https://gist.github.com/guitarrapc/2c556d64e93c1d08f58242313f8c3563

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

https://gist.github.com/guitarrapc/661d4161655c44b9183060994c89d5f3

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に対応したコード全体像です。

https://gist.github.com/guitarrapc/392b5a42a85b3c51faecfa97ae3bdfe1

まとめ

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