Ignite は PaaS / Serverless 周りの話が少なかったですが、Cosmos DB に関してはいくつかアップデートがありました。その中でも RBAC サポートはアクセスキーや接続文字列を管理したくない勢としては待望の機能なので、使い勝手を確認しておきました。
Cosmos DB チームは相変わらずドキュメントがしっかり書かれているので、ドキュメント通りに作業を進めるだけでサクッと動きました。なので、あまり細かい使い方は書きません。
今のところは Azure Resource Manager の RBAC のように組み込みのロールは用意されていません。先に必要なカスタムロールを作成してから割り当てる必要がありますが、細かく権限管理が行えるので安心です。
アプリケーションから RBAC を使ってアクセスするためには最新のプレビュー版 SDK が必要になるので、間違えて正式版をインストールしないようにします。バージョンを明示的に指定しないと入りません。
Visual Studio の Azure サービス認証や Managed Identity を使う場合には、Cosmos DB SDK と同時に Azure.Identity
のインストールが必要です。
早速サンプルコードを出していきますが、CosmosClient
に TokenCredential
を受け取るコンストラクタが追加されているので、新しい Azure SDK を使ったことがある方なら余裕だと思います。
class Program { static async Task Main(string[] args) { var tokenCredential = new DefaultAzureCredential(); var cosmosClient = new CosmosClient("https://***.documents.azure.com:443/", tokenCredential, new CosmosClientOptions { SerializerOptions = new CosmosSerializationOptions { PropertyNamingPolicy = CosmosPropertyNamingPolicy.CamelCase } }); var container = cosmosClient.GetContainer("HackAzure", "TodoItem"); var iterator = container.GetItemLinqQueryable<TodoItem>() .ToFeedIterator(); while (iterator.HasMoreResults) { var items = await iterator.ReadNextAsync(); foreach (var todoItem in items) { Console.WriteLine($"{todoItem.Id},{todoItem.Title},{todoItem.Body}"); } } } }
まずは Visual Studio でのデバッグ実行を行いたいので、ログインしているユーザーに対して Azure CLI で Read Write が行えるカスタムロールを割り当てました。
少し迷ったのが --role-definition-id
に指定する値ですが、カスタムロール作成時に返ってきた id
プロパティではなく name
プロパティの値を指定するのが正解です。
az cosmosdb sql role assignment create -a *** -g *** --scope "/" --principal-id "ユーザーの Object ID" --role-definition-id "作成したカスタムロールの ID (GUID)"
カスタムロールを割り当てると F5 でのデバッグ実行だけで問題なく動作します。ロールを割り当てていない場合は 403 が返ってきてエラーとなります。
接続文字列のために Key Vault Reference などを使う必要がなくなり、Cosmos DB のエンドポイントだけ設定に持っておけば良いので扱いが楽です。
同じように Managed Identity を有効にした App Service / Azure Functions でも利用することができます。使い方はコンソールアプリケーションの時と全く同じですが、最近の Azure Functions っぽい書き方をします。
public class Startup : FunctionsStartup { public override void Configure(IFunctionsHostBuilder builder) { var context = builder.GetContext(); builder.Services.AddSingleton(provider => { var tokenCredential = new DefaultAzureCredential(); return new CosmosClient(context.Configuration["CosmosEndpoint"], tokenCredential); }); } }
public class Function1 { public Function1(CosmosClient cosmosClient) { _cosmosClient = cosmosClient; } private readonly CosmosClient _cosmosClient; [FunctionName("Function1")] public async Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Function, "get")] HttpRequest req, ILogger log) { var container = _cosmosClient.GetContainer("HackAzure", "TodoItem"); var iterator = container.GetItemLinqQueryable<TodoItem>() .Take(3) .ToFeedIterator(); var items = await iterator.ReadNextAsync(); return new OkObjectResult(items); } }
トークンを取得するのに DefaultAzureCredential
を使っているので、同じコードのまま Visual Studio でのデバッグ実行と Azure Function 上の両方で動作します。Azure CLI が入っている環境でも動作します。
実際に Azure Functions にデプロイしてテスト実行をしてみると、接続文字列無しで問題なく動作しました。
基本的な RBAC 周りの動作は確認したので、アプリケーションから操作可能な Container と処理を制限するカスタムロールを作成してみます。
個人的には Change Feed をよく使うので、Change Feed 経由での読み取りのみ可能なカスタムロールを以下のように作りました。readMetadata
は Cosmos DB を使う上で必要なものなので入れておきます。
{ "RoleName": "ChangeFeedOnly", "Type": "CustomRole", "AssignableScopes": ["/"], "Permissions": [{ "DataActions": [ "Microsoft.DocumentDB/databaseAccounts/readMetadata", "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/readChangeFeed" ] }] }
このカスタムロールを割り当てればサクッと Change Feed のみ可能になるかと思い、一般的な Change Feed Processor のコードを書いて試してみましたが実際にはエラーになりました。
エラーになった理由としては単純で、Change Feed Processor は実際には Lease Container を必要としていて、Lease に対しては読み書きの権限が必要だからという話でした。
なので Change Feed Processor を利用するために、2 つのカスタムロールを 2 つの Container をスコープとして設定して割り当てました。Lease に対しては読み書き可能なロールを、Change Feed で読み取る Container に対しては作成した Change Feed のみ可能なロールを割り当てています。
accountName='Cosmos DB アカウント名' resourceGroupName='リソースグループ名' principalId='User / Managed Identity / Service Principal の ObjectId' changeFeedRole='Change Feed のみ許可する Role Definition Id' readWriteRole='Read Write を許可する Role Definition Id' az cosmosdb sql role assignment create -a $accountName -g $resourceGroupName --scope "/dbs/HackAzure/colls/TodoItem" --principal-id $principalId --role-definition-id $changeFeedRole az cosmosdb sql role assignment create -a $accountName -g $resourceGroupName --scope "/dbs/HackAzure/colls/Lease" --principal-id $principalId --role-definition-id $readWriteRole
このロールの割り当てで問題なく Change Feed Processor が動作するようになりました。実際にここまで細かく RBAC で制限するかは要件次第だと思いますが、ガチガチに制限することが出来るのは良いことです。
ちなみに Change Feed Processor ではなく Change Feed の Pull model を使う場合には、Change Feed のみ可能なロールの割り当てだけで動作します。Pull model は Lease を必要としないのが理由ですが、結局はどこかに継続トークンを保存する必要があるので、何かしらの読み書き権限は必要になります。