こんにちは、サイオステクノロジーの遠藤です。
今回はAzureのBicepを利用してCosmosDBとFunctionsのリソースを作成し、FunctionsのEntra IDに対してCosmosDBの操作が行えるロールを付与する方法について確認します。
Bicepにまだ慣れていない自分としてはいくつかハマったポイントがあったのでその部分に焦点を当てて解説していければと思います。
Azureのコントロール プレーンとデータ プレーン
まず今回の本題に入る前にコントロール プレーンとデータ プレーンについて確認します。
Azureの操作はコントロール プレーンとデータ プレーンの2つのカテゴリに分類することが出来ます。
まずコントロールプレーンについてですが、サブスクリプション内のリソースを管理するために使用します。 そしてデータ プレーンは、リソースの種類のインスタンスによって公開される機能を使用するために使用します。
Azureが掲載している例だと、
- コントロール プレーンを使用して仮想マシンを作成します。 仮想マシンを作成した後は、リモート デスクトップ プロトコル (RDP) などのデータ プレーン操作を通じて、仮想マシンと対話します。
- コントロール プレーンを使用してストレージ アカウントを作成します。 ストレージ アカウント内のデータの読み取りと書き込みを行うには、データ プレーンを使用します。
- コントロール プレーンを使って Azure Cosmos DB データベースを作成します。 Azure Cosmos DB データベースのデータに対してクエリを実行するには、データ プレーンを使います。
といった形で、リソースの作成といった操作ではコントロールプレーンが必要になり、実際に作成したリソースに対してデータ更新をかけるといった操作を行うにはデータプレーンのロールが必要になります。
今回は、FunctionsのアプリケーションからCosmosDBに対してデータの書き込みを行うといったシチュエーションを考えているため、Functionsに対してCosmosDBの操作を行えるデータプレーンのロールを持たせるといったことを行おうとしています。
ここからが私が少しハマったポイントなのですが、コントロールプレーンはAzureポータル上のRBACで付与することが可能なのですが、少なくとも私が試した限りはCosmosDBのデータプレーンの権限をAzureポータル上から付与したり確認が出来なさそうといったところです。
Microsoft Entra ID を使用して Azure Cosmos DB アカウントのロールベースのアクセス制御を構成する の公式ドキュメントでも
Q. Azure portal からロールの定義とロールの割り当てを管理することはできますか。
A. ロール管理の Azure portal サポートはまだ利用できません。
となっているので、2025/01/30現在は出来なさそうです。
今回付与するロール
CosmosDBで使用できる組み込みロールには以下の2つのロールが用意されています。
ID | 名前 | 含まれるアクション |
---|---|---|
00000000-0000-0000-0000-000000000001 | Cosmos DB 組み込みデータ リーダー | Microsoft.DocumentDB/databaseAccounts/readMetadataMicrosoft.DocumentDB/databaseAccounts/sqlDatabases/containers/items/readMicrosoft.DocumentDB/databaseAccounts/sqlDatabases/containers/executeQueryMicrosoft.DocumentDB/databaseAccounts/sqlDatabases/containers/readChangeFeed |
00000000-0000-0000-0000-000000000002 | Cosmos DB 組み込みデータ共同作成者 | Microsoft.DocumentDB/databaseAccounts/readMetadataMicrosoft.DocumentDB/databaseAccounts/sqlDatabases/containers/*Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/items/* |
データベースの書き込みを行うためには2つ目の「Cosmos DB 組み込みデータ共同作成者」のロールが必要となります。
Bicepを作成する
今回は以下のような形のディレクトリ階層でbicepコードを作成しました。
bicep-sample
├── modules
│ └── app
│ └── func.bicep
│ └── database
│ └── cosmos.bicep
└── main.bicep
まずCosmosDBをデプロイするためのモジュールです。
modules/database/cosmos.bicep
param location string
param accountName string
param databaseName string
param containerName string
resource cosmosDbAccount 'Microsoft.DocumentDB/databaseAccounts@2024-11-15' = {
name: accountName
location: location
properties: {
databaseAccountOfferType: 'Standard'
locations: [
{
locationName: location
failoverPriority: 0
isZoneRedundant: false
}
]
capabilities: [
{
name: 'EnableServerless'
}
]
}
}
resource database 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases@2024-11-15' = {
name: databaseName
parent: cosmosDbAccount
properties: {
resource: {
id: databaseName
}
}
}
resource container 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers@2024-11-15' = {
name: containerName
parent: database
properties: {
resource: {
id: containerName
partitionKey: {
paths: [
'/user_id'
]
kind: 'Hash'
}
}
}
}
output accountName string = cosmosDbAccount.name
次にfunctions用のモジュールですね。こちらはFunctionsをデプロイするクイックスタートをベースに作成しました。
cosmosRoleDefinitionIds 以下からロールの付与処理を行っております。ロールを付与するにはMicrosoft.DocumentDB databaseAccounts/sqlRoleAssignments を利用することで付与することが出来ます。このあたりもコントロールプレーンとは書き方が違い、最初は書き方がわからなかったりとハマりやすいポイントだと感じました。
modules/app/func.bicep
param location string
@description('The name of the function app that you wish to create.')
param appName string = 'fnapp${uniqueString(resourceGroup().id)}'
@description('Storage Account type')
@allowed([
'Standard_LRS'
'Standard_GRS'
'Standard_RAGRS'
])
param storageAccountType string = 'Standard_LRS'
param cosmosDBAccountName string
param runtime string = 'node'
var functionAppName = appName
var hostingPlanName = appName
var storageAccountName = '${uniqueString(resourceGroup().id)}azfunctions'
var functionWorkerRuntime = runtime
resource storageAccount 'Microsoft.Storage/storageAccounts@2022-05-01' = {
name: storageAccountName
location: location
sku: {
name: storageAccountType
}
kind: 'Storage'
properties: {
supportsHttpsTrafficOnly: true
defaultToOAuthAuthentication: true
}
}
resource hostingPlan 'Microsoft.Web/serverfarms@2021-03-01' = {
name: hostingPlanName
location: location
sku: {
name: 'Y1'
tier: 'Dynamic'
}
properties: {}
}
resource func 'Microsoft.Web/sites@2021-03-01' = {
name: functionAppName
location: location
kind: 'functionapp'
identity: {
type: 'SystemAssigned'
}
properties: {
serverFarmId: hostingPlan.id
siteConfig: {
appSettings: [
{
name: 'AzureWebJobsStorage'
value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccountName};EndpointSuffix=${environment().suffixes.storage};AccountKey=${storageAccount.listKeys().keys[0].value}'
}
{
name: 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING'
value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccountName};EndpointSuffix=${environment().suffixes.storage};AccountKey=${storageAccount.listKeys().keys[0].value}'
}
{
name: 'WEBSITE_CONTENTSHARE'
value: toLower(functionAppName)
}
{
name: 'FUNCTIONS_EXTENSION_VERSION'
value: '~4'
}
{
name: 'WEBSITE_NODE_DEFAULT_VERSION'
value: '~14'
}
{
name: 'FUNCTIONS_WORKER_RUNTIME'
value: functionWorkerRuntime
}
]
ftpsState: 'FtpsOnly'
minTlsVersion: '1.2'
}
httpsOnly: true
}
}
var cosmosRoleDefinitionIds = [
'00000000-0000-0000-0000-000000000002'
]
var cosmosRoleAssignmentsToCreate = [for cosmosRoleDefinitionId in cosmosRoleDefinitionIds:{
name: guid(func.id, resourceGroup().id, cosmosRoleDefinitionId)
roleDefinitionId: cosmosRoleDefinitionId
}]
resource cosmosDBAccount 'Microsoft.DocumentDB/databaseAccounts@2024-11-15' existing = {
name: cosmosDBAccountName
}
resource cosmosDBRoleAssignment 'Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments@2023-04-15' = [for cosmosRoleAssignmentToCreate in cosmosRoleAssignmentsToCreate: {
name: cosmosRoleAssignmentToCreate.name
parent: cosmosDBAccount
properties:{
principalId: func.identity.principalId
roleDefinitionId: '/${subscription().id}/resourceGroups/${resourceGroup().name}/providers/Microsoft.DocumentDB/databaseAccounts/${cosmosDBAccount.name}/sqlRoleDefinitions/${cosmosRoleAssignmentToCreate.roleDefinitionId}'
scope: cosmosDBAccount.id
}
}]
最後にmain.bicepですね。上記2つのモジュールを呼び出す処理を入れます。
param location string = resourceGroup().location
param accountName string
param databaseName string
param containerName string
module cosmosdb 'modules/database/cosmos.bicep' = {
name: 'cosmosdbModule'
params: {
location: location
accountName: accountName
databaseName: databaseName
containerName: containerName
}
}
module api 'modules/app/func.bicep' ={
name: 'apiFunc'
params: {
location: location
cosmosDBAccountName: cosmosdb.outputs.accountName
}
}
権限が付与できているかの確認方法
では実際にbicepを走らせてみて、デプロイされたFunctionsに対して正しい権限が付与されているかを確認しましょう。
ここも個人的なハマりポイントで、上のほうでも書いていますが私調べではCosmosDBのデータプレーンの権限をAzureポータル上から確認することが出来ませんでした。
以下のazコマンドを実行することでCosmosDBアカウントに対してどのマネージドIDにどのデータプレーンのロールが付与されているかの確認をすることができます。
az cosmosdb sql role assignment list --account-name <cosmosdbアカウント名>--resource-group <リソースグループ名>
まとめ
今回はBicepを利用してCosmosDBとFunctionsのリソースを作成し、FunctionsのEntraIDに対してCosmosDBの操作が行えるロールを付与する方法を確認しました。ロールを付与するための書き方がデータプレーンのロール用にしたり、Azureポータル上からは権限の付与が確認ができないなどハマりやすいポイントが多い部分だと思いますので同じく躓いた方の参考になればと思います!
Bicep自体は1コマンドでインフラ一式デプロイを行うことができ、非常に便利に感じているのでこれからも知見をためていきたいと思います。
ではまた~