ごった煮

色々な事を書いてます

App Service を Bicep でデプロイする際に、ポータルで設定した Settings の値が消えないようにしたい

この記事は、Qiita Azure Advent Calendar 2022 の 12/20 分の投稿になります。

はじめに

タイトルの通りですが、Bicep を使って App Service を作成・更新する際、Application settings を更新すると、Bicep でデプロイした値のみが Application settings にセットされます。

常に Bicep で Application settings の値が管理されていて、それがすべてという場合は困らないのですが、実際の運用で Application settings の値を一つだけ更新する為だけにわざわざ Bicep でデプロイするというのは、 過剰なのかなという気もします。

また、Bicep には含めないが一時的に設定しておきたい値というのも、実際の運用では発生し得ると思います。

そこで、ポータルで設定された値と Bicep で設定された値を共存させる方法をご紹介します。

実際の動きを見てみよう

はじめに、大前提である Bicep で Application settings を更新すると本当に Bicep に含まれていない値が消えるのか確かめてみましょう。

前提条件

  • 任意のリソースグループを作っておいてください

次の Bicep を使います。

resource plan 'Microsoft.Web/serverfarms@2022-03-01' = {
  name: 'plan-qiita-2022'
  location: resourceGroup().location
  kind: 'app'
  sku: {
    name: 'B1'
  }
}

resource app 'Microsoft.Web/sites@2022-03-01' = {
  name: 'app-qiita-2022'
  location: resourceGroup().location
  kind: 'app'
  properties: {
    serverFarmId: plan.id
    clientCertEnabled: false
    httpsOnly: true
    clientAffinityEnabled: false
    siteConfig: {
      ftpsState: 'Disabled'
      http20Enabled: true
      alwaysOn: true
      netFrameworkVersion: 'v7.0'
      appSettings: [
        {
          name: 'From_Bicep_01'
          value: 'hoge'
        }
        {
          name: 'From_Bicep_02'
          value: 'huga'
        }
      ]
    }
  }
}

// ランタイムが dotnet 7 になるようする設定(気分)
resource metadata 'Microsoft.Web/sites/config@2022-03-01' = {
  name: 'metadata'
  kind: 'string'
  parent: app
  properties: {
    CURRENT_STACK: 'dotnetcore'
  }
}

デプロイコマンドは、覚えてられないので ps1 ファイルにしておきます。

$resourceGroupName = 'rg-qiita-2022'

az deployment group create `
--resource-group $resourceGroupName `
-f main.bicep

この定義をデプロイすると、App Service が作られて、Application settings に次のように2つの設定が入ります。

では、次にポータルから設定を設定しましょう。

次のように、From_Portal_01From_Portal_02 を設定します。

それでは、もう一度 Bicep をデプロイします。

デプロイすると、やはり Bicep に定義されたもののみになります。

解決編

では、ポータルで設定された値と Bicep からデプロイされた値を共存させる方法の解説です。

まず Application settings を更新する為にモジュールを作成します。

このモジュールでは、Bicep から設定する設定と既にセットされている設定をマージして、デプロイする処理を行います。

param appName string

param currentSettings object
param setting object

resource sitConfig 'Microsoft.Web/sites/config@2021-03-01' = {
  name: '${appName}/appsettings'
  properties: union(currentSettings, setting)
}

次に、App Service を作成する Bicep を次のように変更します。

resource plan 'Microsoft.Web/serverfarms@2022-03-01' = {
  name: 'plan-qiita-2022'
  location: resourceGroup().location
  kind: 'app'
  sku: {
    name: 'B1'
  }
}

resource app 'Microsoft.Web/sites@2022-03-01' = {
  name: 'app-qiita-2022'
  location: resourceGroup().location
  kind: 'app'
  properties: {
    serverFarmId: plan.id
    clientCertEnabled: false
    httpsOnly: true
    clientAffinityEnabled: false
    siteConfig: {
      ftpsState: 'Disabled'
      http20Enabled: true
      alwaysOn: true
      netFrameworkVersion: 'v7.0'
    }
  }
}

// ランタイムが dotnet 7 になるようする設定(気分)
resource metadata 'Microsoft.Web/sites/config@2022-03-01' = {
  name: 'metadata'
  kind: 'string'
  parent: app
  properties: {
    CURRENT_STACK: 'dotnetcore'
  }
}

// 現在 settings に設定されている値を取得する
var currentSettings = list('${app.id}/config/appsettings', '2021-03-01').properties
var newSettings = {
  From_Bicep_01: 'hoge'
  From_Bicep_02: 'huga'
}

module settings 'settings.bicep' = {
  name: 'appsettings'
  params: {
    appName: app.name
    setting: newSettings
    currentSettings: currentSettings
  }
}

変更後の Bicep では、list 関数を使って、既存の Application settings に設定されている値を取得しています。

その値と、Bicep からデプロイしたい設定をマージしてデプロイするモジュールに渡すことによって、ポータルで設定された値と Bicep でデプロイする値の共存をしています。

これをデプロイしてみましょう。

共存できていることが確認できるはずです。

なぜモジュールを使うのか?

同一の Bicep ファイルで自身の情報(今回は、Microsoft.Web/sites/config) を取得して、設定する場合、自身に依存することとなり、循環参照のエラーが返されます。

モジュールに分割して、呼び出し元の Bicep ファイルで取得した値を渡すことで、呼び出し先のモジュールにはオブジェクトが渡される為、循環参照に陥らず処理が進みます。

まとめ

ちょっとしたミニプロジェクト程度でしたらこういった問題はあまり発生しないと思います。

ですが、実際に運用しているとポータルでも設定したいといった場面が出てくるかと思います。

そういった時の為に、この記事が役に立ってくれると幸いです。