このエントリは2025/02/14現在の情報に基づいています。将来の機能追加や変更に伴い、記載事項からの乖離が発生する発生する可能性があります。
問い合わせ
いつもの問い合わせ主から珍しくCosmos DBに関する問い合わせがあった。
冗長化されたインスタンスのリージョンやURLをJava SDK v3で取得する方法は以前教えてもらったが、v4ではどうなのか?AsyncDocumentClientでは取得できないようなのだが。
問い合わせ主の内容は、以前記載した以下の記事はJava SDK v3用だが、v4の例を教えてくれ(記事にしてくれ)、という圧であった。
確かにアップデートされていたので、まとめておく。
事前準備
Cosmos DBのサービスを作成し、東西日本リージョンで冗長化した上で、片方を書き込み可能、他方を読み取り専用にしておく。
各Regionに冗長化されたCosmos DBのURIを取得するには
v4では、CosmosManagerクラスのインスタンスを作成する必要がある。このクラスを使う場合には、Azure RBACを使う必要があるので、最初にデータプレーンへのRBACを構成する必要がある。
データ プレーンのロールベースのアクセス制御と Azure Cosmos DB for NoSQL を使用する / Use data plane role-based access control with Azure Cosmos DB for NoSQL
https://learn.microsoft.com/azure/cosmos-db/nosql/security/how-to-grant-data-plane-role-based-access
以前RBACについて以下の記事で書いていたが、古いので、今後は使わないのが吉。
Microsoft Entra ID を使用して Azure Cosmos DB アカウントのロールベースのアクセス制御を構成する (日本語のみ)
https://learn.microsoft.com/azure/cosmos-db/how-to-setup-rbac
今回の場合、最低限
Microsoft.DocumentDB/databaseAccounts/readMetadata
の権限が含まれているロールであればよい。
$ az cosmosdb sql role definition list \
-g <resource_group> \
-a <cosmosdb_account>
上記コマンドで確認すると、
- Cosmos DB Built-in Data Contributor
- Cosmos DB Built-in Data Reader
のいずれかでよいので、今回はCosmos DB Built-in Data Readerロール(以下、Readerロール)を使うことにする。ReaderロールのIDは以下のような構造である。
/subscriptions/<SubscriptionId>/resourceGroups/<ResourceGroup>/providers/Microsoft.DocumentDB/databaseAccounts/<CosmosDBAccount>/sqlRoleDefinitions/00000000-0000-0000-0000-000000000001
ちなみに、Contributorロールは以下。
/subscriptions/<SubscriptionId>/resourceGroups/<ResourceGroup>/providers/Microsoft.DocumentDB/databaseAccounts/<CosmosDBAccount>/sqlRoleDefinitions/00000000-0000-0000-0000-000000000002
試してみる
以下の方針で構成し、実際に取得できるか確認する。
- User assigned managed identityを作成
- Readerロールに作成したmanaged identityを追加
1. User assigned managed identityを作成
PrincipalIDを後で使うので、CLIで作成しつつ、PrincipalIDを取得しておく。
principalId=$(az identity create -g <resource_group> -n <managedID_Name> --query "principalId" -o tsv)
2. Readerロールに作成したmanaged identityを追加
ロール割り当て時のスコープは、Cosmos DBアカウントのルートでよい。
scopeId="/"
あとは、先ほどのReaderロールのIDを使って、ロール割り当てすればOK。
az cosmosdb sql role assignment create \
-g <resource_group> \
-a <cosmosdb-account> \
-d <role_id> \
-p $principalId \
-s $scopeId
3. Code
まず、CosmosManagerインスタンスを作成する。今回はUser assigned managed identityを使っているので、Client IDを指定しているが、もしApp Serviceなどで動作させる場合、System assigned managed identityを使うこともできる。その場合はClient IDの指定は不要。
AzureProfile profile = new AzureProfile(AzureEnvironment.AZURE);
DefaultAzureCredential defaultAzureCredential = new DefaultAzureCredentialBuilder()
.managedIdentityClientId(clientId)
.build();
CosmosManager cosmosManager = CosmosManager.authenticate(defaultAzureCredential, profile);
AzureProfile Class
https://learn.microsoft.com/java/api/com.azure.core.management.profile.azureprofile
その上で、DatabaseAccounts(複数形であることに注意)を取得する。
CosmosDBAccounts dbAccounts = cosmosManager.databaseAccounts();
読み取り先を知りたい場合は readableReplications を、書き込み先を知りたい場合は writableReplications を取得する。いずれも、List<Location>を返すので、Listを拡張forで処理するなり、Streamに渡すなりすればよい。以下はreadableReplicationsの例。
for (CosmosDBAccount cosmosDBAccount : dbAccounts.list()) {
for (Location loc : cosmosDBAccount.readableReplications()) {
System.out.printf("Location [%s] Endpoint [%s]\n", loc.locationName(), loc.documentEndpoint());
}
}
4. テスト
AzureProfileでサブスクリプションID、Entra IDのテナントIDを指定していなかったので、実行時には環境変数でTenant ID、Subscription IDを定義する必要がある。もちろんインスタンス作成時に指定していれば不要。
export AZURE_SUBSCRIPTION_ID=xxxx
export AZURE_TENANT_ID=yyyy
その上で、実際に実行してみると、Readableの場合は
Location [Japan East] Endpoint [https://<cosmosdb_account>-japaneast.documents.azure.com:443/]
Location [Japan West] Endpoint [https://<cosmosdb_account>-japanwest.documents.azure.com:443/]
Writableの場合は、マルチリージョン書き込みを許容していないので、1件のみ出力する。
Location [Japan East] Endpoint [https://<cosmosdb_account>-japaneast.documents.azure.com:443/]