はじめに
開発検証するリソースをセットで用意するのを効率化したくて、Azureの分析系サービスをワンクリックでデプロイできるテンプレートを開発してみました。
Bicep完全に理解したと調子に乗って作ったわけですが、無事躓きポイントにぶつかったので、少々の備忘録を残します。
テンプレートはぜひ皆さん使ってください。
パラメータによりリソースの作成有無を決定するテンプレートで起きたこと
問題:条件をつけてデプロイされない状態にしたリソースで参照が発生した
パラメータでリソースをデプロイするかどうか制御する、いわゆるコンディショナルデプロイ自体は非常に簡単です。
参考:https://docs.microsoft.com/ja-jp/azure/azure-resource-manager/bicep/conditional-resource-deployment
通常、たとえばData Factory であれば、このように記述します。
param location string = 'japaneast'
resource datafactory 'Microsoft.DataFactory/factories@2018-06-01' = {
name: 'test-adf-qiita'
location: location
identity: {
type: 'SystemAssigned'
}
}
これをある条件、たとえばisNeedDataFactoryのようなパラメータで制御すると以下のようになります。
param location string = 'japaneast'
param isNeedDataFactory bool = false
resource datafactory 'Microsoft.DataFactory/factories@2018-06-01' = if(isNeedDataFactory == true){
name: 'test-adf-qiita'
location: location
identity: {
type: 'SystemAssigned'
}
}
単体ならこれでいいのですが、リソースのoutputを利用してデプロイするような構成だとどうでしょうか?
例でいえばData FactoryのIDを使ってデプロイするなどです。
よくやるのは、生成したdatafactoryに対してstorage accountの権限を割当てるようなケースです。
param location string = 'japaneast'
param isNeedDataFactory bool = false
param lakeName string
var storageBlobDataContributorRoleId = 'ba92f5b4-2d11-453d-a403-e96b0029c9fe'
resource datafactory 'Microsoft.DataFactory/factories@2018-06-01' = if(isNeedDataFactory == true){
name: 'test-adf-qiita'
location: location
identity: {
type: 'SystemAssigned'
}
}
resource datalake 'Microsoft.Storage/storageAccounts@2021-09-01' existing = {
name: lakeName
}
resource datafactoryTolandingRawLake 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' =if(isNeedDataFactory == true) {
name: guid(lakeName,storageBlobDataContributorRoleId,'datafactoryToLake')
scope: datalake
properties: {
roleDefinitionId:resourceId('Microsoft.Authorization/roleDefinitions', storageBlobDataContributorRoleId)
principalId: datafactory.identity.principalId
principalType: 'ServicePrincipal'
}
}
このbicepはうまくいきません。
エラーとなり、原因はData factoryのリソースが見つからないという旨のメッセージが返されます。
ResourceNotFound
挙動から判断したことですが、
なんらかのリソースを参照したデプロイを条件制御する場合、デプロイ条件をみたさない場合でも参照が発生します。
解決策
参照が発生している箇所もif条件で切り替えるようにする
基本的な考え方は、リソースの参照を発生させないことです。
参照が発生するのは、principalIdの部分ですので以下のように変更します。
principalId:(isNeedDataFactory == true)? datafactory.identity.principalId : ''
全文
param location string = 'japaneast'
param isNeedDataFactory bool = false
param lakeName string
var storageBlobDataContributorRoleId = 'ba92f5b4-2d11-453d-a403-e96b0029c9fe'
resource datafactory 'Microsoft.DataFactory/factories@2018-06-01' = if(isNeedDataFactory == true){
name: 'test-adf-qiita'
location: location
identity: {
type: 'SystemAssigned'
}
}
resource datalake 'Microsoft.Storage/storageAccounts@2021-09-01' existing = {
name: lakeName
}
resource datafactoryTolandingRawLake 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' =if(isNeedDataFactory == true) {
name: guid(lakeName,storageBlobDataContributorRoleId,'datafactoryToLake')
scope: datalake
properties: {
roleDefinitionId:resourceId('Microsoft.Authorization/roleDefinitions', storageBlobDataContributorRoleId)
principalId: (isNeedDataFactory == true)? datafactory.identity.principalId : ''
principalType: 'ServicePrincipal'
}
}
これで回避できます。
上記は単純化していますが、今回作成したテンプレートはかなり複雑で、参照箇所を切り分けていくために当初考えていたよりもmoduleを分割する必要に迫られました。(この例だと伝わりにくいんですが、後処理的に設定しているものはかなり構成を再検討させられました)
今回の例で言うと、以下のように分割する感じです。
datafactory.bicep
param location string = 'japaneast'
param isNeedDataFactory bool = false
param lakeName string
var storageBlobDataContributorRoleId = 'ba92f5b4-2d11-453d-a403-e96b0029c9fe'
resource datafactory 'Microsoft.DataFactory/factories@2018-06-01' = if(isNeedDataFactory == true){
name: 'test-adf-qiita'
location: location
identity: {
type: 'SystemAssigned'
}
}
module rbac 'rbac.bicep' = if(isNeedDataFactory == true){
name: 'rbac'
params:
{
lakeName:lakeName
principalId:(isNeedDataFactory == true)? datafactory.identity.principalId : ''
}
}
rbac.bicep
param lakeName string
param principalId string
resource datalake 'Microsoft.Storage/storageAccounts@2021-09-01' existing = {
name: lakeName
}
// ifが不要になる
resource datafactoryTolandingRawLake 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = {
name: guid(lakeName,storageBlobDataContributorRoleId,'datafactoryToLake')
scope: datalake
properties: {
roleDefinitionId:resourceId('Microsoft.Authorization/roleDefinitions', storageBlobDataContributorRoleId)
principalId: principalId // ifが不要になる
principalType: 'ServicePrincipal'
}
}
今回紹介したテンプレートはこの仕様をあとから対応したので依存関係が多少汚くなっちゃいましたが、
これから始める方は、こういった点を踏まえて、bicep のmodule分割を検討していただければと思います。