🦔

Bicepで使える構文と使い所をまとめてみる(2)

2023/05/17に公開

bicepで使える構文と使い所を整理してみようと思います。ものによっては、もっとスマートな書き方があるでしょうが、そう感じるようになったらそれはそれで良いのではないでしょうか。

https://learn.microsoft.com/ja-jp/azure/azure-resource-manager/bicep/file

  • 前回の記事

https://zenn.dev/lente/articles/azure-bicep-syntax-example-01

パラメータをオブジェクト型にして扱う

  1. moduleで大量のパラメータを受け渡しする場合、object型にすると記述量が減る。
  2. 更に配列(array型)にしてfor文を使ってで複数デプロイできる
  3. デプロイをシリアル処理にしたい場合はbatchSizeデコレータを使用する
  • 以下は、MetricAlertのデプロイテンプレートの例
    • Alertのデプロイテンプレートを共通モジュールとする
    • Alert定義のparamでの受け渡しはarray型1つにまとめる
    • for文を使ってarrayオブジェクトで受け取ったパラメータを使用してデプロイ
    • デメリットは、コーディング時に型チェックがされないことでしょうか
param alerts array

@batchSize(1)
resource alert_metric 'microsoft.insights/metricalerts@2018-03-01' = [for alert in alerts: {
  name: alert.alert_name
  location: 'global'
  properties: {
    enabled: alert.enabled
    description: alert.description
    scopes: alert.scopes
    severity: alert.severity
    evaluationFrequency: alert.evaluation_frequency
    windowSize: alert.window_size
    criteria: {
      allOf: [
        {
          name: 'metric'
          metricNamespace: 'Microsoft.DBforPostgreSQL/flexibleServers'
          metricName: alert.metric_name
          operator: alert.operator
          threshold: alert.threshold
          timeAggregation: alert.time_aggregation
          skipMetricValidation: true
          criterionType: 'StaticThresholdCriterion'
        }
      ]
      'odata.type': 'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria'
    }
    autoMitigate: true
    targetResourceType: 'Microsoft.DBforPostgreSQL/flexibleServers'
    targetResourceRegion: alert.location
    actions: [
      {
        actionGroupId: alert.action_id
        webHookProperties: {
        }
      }
    ]
  }
}]


  • 以下はMetricAlertのデプロイテンプレートを呼び出す側の例
    • alertsパラメータで複数のAlert定義のパラメータを渡す
    • 可読性も上がるし、Alert定義の追加もしやすい
param now string = utcNow('yyyyMMdd-HHmmss')
param location string = resourceGroup().location
param psql_name string
param action_name string
param action_rg_name string

module main './modules/metric-psql.bicep' = {
  name: 'alert-postgresql-${now}'
  params: {
    alerts: [
      {
        alert_name: 'alert-psql-cpu_percent'
        enabled: true
        description: 'PostgreSQLのCPU(%)Alert'
        scopes: [ psql.id ]
        severity: 2
        evaluation_frequency: 'PT5M'
        window_size: 'PT5M'
        metric_name: 'cpu_percent'
        operator: 'GreaterThanOrEqual'
        threshold: 80
        time_aggregation: 'Average'
        location: location
        action_id: action_group.id
      }
      //
      // 省略: 2つ目以降のAlert定義
      //
    ]
  }
}

resource psql 'Microsoft.DBforPostgreSQL/flexibleServers@2022-01-20-preview' existing = {
  name: psql_name
}

resource action_group 'microsoft.insights/actionGroups@2022-06-01' existing = {
  name: action_name
  scope: resourceGroup(action_rg_name)
}

https://learn.microsoft.com/ja-jp/azure/azure-resource-manager/bicep/loops#deploy-in-batches


contains関数

  1. object型にキーが含まれているかどうかは、contains()を使用する
  2. キーが含まれていなければfalseが返ってくる
  • 以下は、subnetのデプロイテンプレートの例
    • subnetのデプロイテンプレートを共通モジュールとする
    • subnet定義のparamでの受け渡しはarray型1つにまとめる
    • for文を使ってarrayオブジェクトで受け取ったパラメータを使用してデプロイ
    • containsを使ってキーの存在チェックを行い、三項演算子を組み合わせてfalseの場合は、既定としたい値を設定するといった使い方をする
param vnet_name string
param subnets array

@batchSize(1)
resource subnet 'Microsoft.Network/virtualNetworks/subnets@2022-11-01' = [for subnet in subnets: {
  name: subnet.name
  parent: vnet
  properties: {
    addressPrefix: subnet.cidr
    privateLinkServiceNetworkPolicies: contains(subnet, 'pls') ? subnet.pls : 'Enabled'
    privateEndpointNetworkPolicies: contains(subnet, 'pe') ? subnet.pe : 'Enabled'
    networkSecurityGroup: contains(subnet, 'nsg') ? {
      id: resourceId('Microsoft.Network/networkSecurityGroups', subnet.nsg)
    } : null
    serviceEndpoints: contains(subnet, 'se') ? subnet.se : null
    delegations: contains(subnet, 'delegations') ? subnet.delegations : null
    routeTable: contains(subnet, 'udr') ? {
      id: resourceId('Microsoft.Network/routeTables', subnet.udr)
    } : null
  }
}]

resource vnet 'Microsoft.Network/virtualNetworks@2022-11-01' existing = {
  name: vnet_name
}


  • 以下はsubnetのデプロイテンプレートを呼び出す側の例
    • subnet定義を_subnet変数に記述して、module構文のパラメータで配列として渡す
    • 可読性も上がるし、subnet定義の追加もしやすい
    • delegationsrouteTableを設定する場合もデプロイで使用する共通モジュールは変更が不要である
param now string = utcNow('yyyyMMdd-HHmmss')
param vnet_name string

var _subnets = {
  AzureBastionSubnet: {
    name: 'AzureBastionSubnet'
    cidr: '10.0.254.0/26'
    nsg_name: 'nsg-AzureBastionSubnet'
  }
  GatewaySubnet: {
    name: 'GatewaySubnet'
    cidr: '10.0.254.64/26'
  }
  'snet-01': {
    name: 'snet-01'
    cidr: '10.0.0.0/27'
    nsg: 'nsg-snet-01'
    pls: 'Enabled'
    pe: 'Enabled'
    se: [
      {
        service: 'Microsoft.KeyVault'
      }
      {
        service: 'Microsoft.Storage'
      }
    ]
  }
}

module subnets './modules/snet.bicep' = {
  name: 'subnet-${now}'
  params: {
    vnet_name: vnet_name
    subnets: [
      _subnets.AzureBastionSubnet
      _subnets.GatewaySubnet
      _subnets['snet-01']
    ]
  }
}

https://learn.microsoft.com/ja-jp/azure/azure-resource-manager/bicep/bicep-functions-array


if文とconcat関数

  1. if文は真偽判定でデプロイをする/しないを決定したい場合で使用する
  2. 配列の結合はconcat()で行う
  • 以下はFront DoorのSecurity Policiesのデプロイテンプレートを呼び出す側の例
    • Security Policiesは、WAFを適用する対象のエンドポイントをresourceIdをidオブジェクトの配列にして設定する
    • エンドポイントは、Front Doorの既定のエンドポイントとカスタムドメインの2種類があり、それぞれ取得する必要がある
    • そのため、取得結果のidオブジェクト配列をそれぞれ作成してconcat()で結合している
    • Security Policiesデプロイ時にエンドポイントのidオブジェクト配列が空(length()が0)ならばデプロイをしないようにif文を使用
param fd_name string
param fd_ep_names array = []
param fd_domain_names array = []
param fd_sp_name string
param fd_waf_name string

var ep_ids = [for ep_name in fd_ep_names: {
  id: resourceId('Microsoft.Cdn/profiles/afdEndpoints', fd_name, ep_name)
}]

var domain_ids = [for domain_name in fd_domain_names: {
  id: resourceId('Microsoft.Cdn/profiles/customDomains', fd_name, replace(domain_name, '.', '-'))
}]

var ids = concat(ep_ids, domain_ids)

resource fd_sp 'Microsoft.Cdn/profiles/securityPolicies@2022-11-01-preview' = if (0 != length(ids)) {
  name: fd_sp_name
  parent: fd
  properties: {
    parameters: {
      type: 'WebApplicationFirewall'
      associations: [
        {
          domains: ids
          patternsToMatch: [
            '/*'
          ]
        }
      ]
      wafPolicy: {
        id: waf.id
      }
    }
  }
}

resource waf 'Microsoft.Network/FrontDoorWebApplicationFirewallPolicies@2022-05-01' existing = {
  name: fd_waf_name
}

resource fd 'Microsoft.Cdn/profiles@2022-11-01-preview' existing = {
  name: fd_name
}


  • 以下はVirtual Machineをデプロイするテンプレートの例
    • if文はexistingを使用する場合でも同様の使い方が可能
    • 以下の例では、可用性セットと近接配置グループが指定されていれば対象のresourceIdをVMの設定として行っている
param location string = resourceGroup().location
param vm_name string
param aset_name string = ''
param ppg_name string = ''

var _is_aset = !empty(aset_name)
var _is_ppg = !empty(ppg_name)

resource avail_set 'Microsoft.Compute/availabilitySets@2022-11-01' existing = if (_is_aset) {
  name: aset_name
}

resource ppg 'Microsoft.Compute/proximityPlacementGroups@2022-11-01' existing = if (_is_ppg) {
  name: ppg_name
}

resource vm 'Microsoft.Compute/virtualMachines@2022-11-01' = {
  name: vm_name
  location: location
  properties: {
    //省略
    proximityPlacementGroup: _is_ppg ? {
      id: ppg.id
    } : null
    availabilitySet: _is_aset ? {
      id: avail_set.id
    } : null
  }
  //省略
}



以上.

(1), (2)の書き方ぐらいまで身につけると作成できる幅が広がってくると思います。

Discussion