bicep moduleを使う

最近、bicepが気に入って使ってる。まだ、かなり初期段階の開発中のプロダクトだが、ARM Template のファイルを生成するので、記述ツールという位置付なら今でも十分便利に使える。

先日、vscode-remote-try-bicepがいつまでも空のままなのでvs code dev containerのテンプレートとしてdevcontainer-bicepを作って公開した。個人の嗜好の部分をどこまで入れるのかは微妙な感じではある。

Stone

話がズレた、10/16に、masterに、0.2でリリースされる予定のbicep module[1]がマージされたので動かしてみた。まだリリースされてないのでソースを持ってきてビルドして、path の切れてるディレクトリにリンクを張リ、実行属性を付ける。最後に、versionを確認。2020/10/17 時点では、0.1.149-alpha (1ca02b52de)だった。ビルドは、こんな感じのコマンドになる。

% git clone git@github.com:Azure/bicep.git
% cd bicep/src/Bicep.Cli
% dotnet publish -r linux-x64 -c Release --self-contained true /p:PublishTrimmed=true -p:PublishReadyToRun=true -p:PublishSingleFile=true -p:PublishReadyToRunShowWarnings=true
% ln -s $PWD/bin/Release/netcoreapp3.1/linux-x64/publish/bicep ~/.local/bin/bicep
% chmod +x ~/.local/bin/bicep
% bicep --version
Bicep CLI version 0.1.149-alpha (1ca02b52de)

ビルド後のbicepサイズを見たら、74MBほどある。今のリリースが45MBなのでちょっとでかい。

.github/workflows/build.ymlを見たら、PublishReadyToRunが指定されていなかったので外してみる。

run: dotnet publish --configuration ${{ matrix.configuration }} --self-contained true -p:PublishTrimmed=true -p:PublishSingleFile=true -r ${{ matrix.rid }} ./src/Bicep.Cli/Bicep.Cli.csproj

オプションを合わせてコンパイルし直す。

% dotnet publish -r linux-x64 -c Release --self-contained true /p:PublishTrimmed=true -p:PublishSingleFile=true -p:PublishReadyToRunShowWarnings=true
% ls -lh ./bin/Release/netcoreapp3.1/linux-x64/publish/bicep -rwxr-xr-x 1 takekazu takekazu 54M 10 17 17:41 ./bin/Release/netcoreapp3.1/linux-x64/publish/bicep

54MBになった。AOTは無くても良さそうなので外すことにする。また、VS Code Pluginはとりあえず、無くて良いので後で試す。

Modulesを試す

ドキュメントでは、このあたりModulesに書いてある。

まずは使ってみる、モジュール利用のシナリオは色々あると思うが、今回は仮想マシンのデプロイをやって、ポイントを整理する。

仮想マシン(VM)をデプロイする場合、ポータルやAzure Quickstart Templateを見ると、ほとんどの場合は仮想ネットワーク(VNet)とVMは固定の構成で作成されることが前提となっている。一方、実業務では既存のVNetに追加でVMをデプロイしたり、異なったVNetに同一構成のVMをデプロイすることが頻繁にある。実際、仮想マシンと仮想ネットワークのライフサイクルは別と考えた方が良く、このようなケースは、テンプレートのモジュール利用の典型的な例になる。

bicep で、vmとvnetをmoduleに分けてデプロイするレートを書いた例をレポジトリにあげたbisep module examples

簡単にポイントをだけ説明する。

下記のようにメインのテンプレートを作成し、module を利用する。従来のARM Templateでは、テンプレート参照する場合は、URL経由である必要があったが、bicep moduleではローカルファイルにモジュールを置くことができる。また、vnet: vnetMod.outputs.results.vnetのように、モジュールの入力として他のモジュールの出力を使うことで、モジュール間の依存関係をもたせることができる。

param vmSize string
param adminUsername string
param adminPassword string {
    secure: true
}

module vnetMod './vnet.bicep' = {
    name: 'vnetMod'
    params: {
        vnetName: 'vNet'
    }
}

module vmMod './vm.bicep' = {
    name: 'vmMod'
    params: {
        vnet: vnetMod.outputs.results.vnet
        adminUsername: adminUsername
        adminPassword: adminPassword
        vmSize: vmSize
        vmName: 'vm1'
    }
}

output results object = {
    vnet: vnetMod
    vm: vmMod
}

module vnetMod ‘./vnet.bicep’ = {と書くと、vnet.bicepが読み込まれる。下記は内容の抜粋。

param vnetName string = 'vNet'
param addressPrefix string = '10.1.0.0/16'
param location string = resourceGroup().location

output results object = {
    vnet: vnet
}

... snip ....

// https://docs.microsoft.com/en-us/azure/templates/microsoft.network/virtualnetworks
resource vnet 'Microsoft.Network/virtualNetworks@2020-05-01' = {
    name: vnetName
    location: location
    properties: {
        addressSpace: {
            addressPrefixes: [
                addressPrefix
            ]
        }
        subnets: [
            subnet1
            subnet2
        ]
    }
}

モジュールは、Microsoft.Resources/deploymentsのインラインのテンプレートに展開される。該当部分、main.json#L20-L104。 以下抜粋

"type": "Microsoft.Resources/deployments",
     "apiVersion": "2019-10-01",
     "name": "vnetMod",

   ... snip ....

       "template": {

   ... snip ....

         "resources": [
           {
             "type": "Microsoft.Network/virtualNetworks",
             "apiVersion": "2020-05-01",
             "name": "[parameters('vnetName')]",
             "location": "[parameters('location')]",
             "properties": {
               "addressSpace": {
                 "addressPrefixes": [
                   "[parameters('addressPrefix')]"
                 ]
               },
               "subnets": [
                 "[variables('subnet1')]",
                 "[variables('subnet2')]"
               ]
             }
           }
         ],

  ... snip ....

bicep全般の動きとして、ネイティブのARM Tempateとの関係は、ほぼ1:1で非常にシンプルな展開で、moduleでも同様な動きになっている。

ポイント1:依存関係

vmは、vnetに依存している、これは、普通ARM Template では、ネストしたリソースで書くか、dependsOnで記述する。[2]bicepには、自動依存性解決[3]の機能があり、bicep resources 定義内で、別のリソースを参照すると、json生成時に、dependsOnを追加してくれる。

今回のケースだと、vmModvnetパラメータで、vnetMod.outputs.results.vnetを参照している。生成されたmain.jsonでは、下記のmain.json#L329-L331部分に依存関係は記述されているのがわかる。

"dependsOn": [
    "[resourceId('Microsoft.Resources/deployments', 'vnetMod')]"
]

単純に vnetModeのdeploymentsへに依存するように展開されてるので、大規模な展開で展開時間がセンシティブな場合は少々注意が必要だが殆どの場合は問題無い素直な変換になっている。

この依存関係の自動解決(暗黙の依存関係)は、コードをシンプルにするし、dependsOnの書き漏れが軽減されるので非常に気に入っている。

ポイント2:オブジェクトの利用

従来からARM Templateではパラメータにオブジェクトを渡せたが、あまり便利では無かったので殆ど使っていなかった。このスクリプトでは、vnetもモジュールでは、vnet reference を返し。vm モジュールのパラメータに渡している。

vnet.bicep#L5-L7

output results object = {
    vnet: vnet
}

param vnet objectで受け取って、vm.bicep#L25-L26で使っている。ここでは、vnet name 使っていないが、vnetそのものが渡ってくるので他の操作もできる。あと、呼び出し側のmain.bicepで、参照関係が生まれるので、自動依存関係で処理できるという利点もある。[4]

今のbicepだと、仕様のブレと未実装が混ざっていて少し混乱するが、そんな時はIssueを見れば良い。[5]

まとめ

bicepは、単純にARM Templateを各ツールとして捉えると今まであったどのツールより書きやすい。今回、module が実装されたことでかなり実用的に使えるようになった。この手のものを書くにはGUIツールより、テキストベースのツールが向いているように思う。ただ、ARM Template (= Azure Resource API) の難しさは残るのでハマる時はハマる。

bicepのリリースは来年になりそうだが、筋が良いので積極的に使って方が良いと思う、お勧めだ。ただ、copy が実装されいないので要注意、copy は、12月リリース予定の0.3でサポートされる予定だ。

あと、今回実行は基本WSL2内で行い、azコマンドのランナーとしては、昔ながらのMakefileを使っている。Azure PowerShell Cmdletを使うなら、全部Poshスクリプトで書けば良いが、az cliを使う場合、コマンド間を繋ぐ何らかのglue script[6]が必要だ。bashで全部書くのも辛いので、最近は、Makefileを使うことにしている。GNU Make なんて、20年ぐらいまともに使ってなかったのでだいぶ忘れていたがだいぶ思い出してきてる。

2020/10/22 「GitHub Action の Workflow から持ってくると最新のビルドが拾えるよ」、って話が追記された。[7]

参照

[1]Module implementation (#608)
[2]Define the order for deploying resources in ARM templates
[3]Implicit dependency
[4]このあたりコードでちょと変なことをしているのは、Microsoft.Network virtualNetworks template referenceによると、name属性があるはずなのだが、参照すると実行エラーになってしま。仕方が無いので、id からvnet nameを求めていてる
[5]自動依存関係解決だけあれば、module にdependsOn は不要じゃないかという議論どうやら、modeule に dependsOn 構文を追加するつもりらしい。これは、未確認
[6]Glue languages、ぐぐったら、AWS Glue ばかりが出てきた心配になってのでリンクを張っておく。日本語のWikipediaはイマイチだったので、en.wikipedia.orgにした
[7]Installing the “Nightly” build of bicep CLI and VS Code extension