Azureの構成をIaCしたいとなると、おおむね選択肢は次の3つになりそうです。
- ARM Template
- Terraform
- Pulumi
TerraformとPulumiはおおむね同じ性質ですが、PulumiはAzureに対しては他のクラウドよりも優先的に機能が入るのでちょっと面白いです。 とはいえ、世の中的にはAzureの構成はARM Templateが一番合ってるといわれるとかいわれないとか。
ARM Templateは人間が使うフォーマットじゃないので一ミリも興味がでなかったのですが、Azure BicepがDSLとして出てきたこともあり、ここしばらくはAzureでBicepを使って構成してみたので設計メモを書いておきます。
tl;dr;
- Bicepを用いて、コードとリソースの一致を保証することはARM Templateの現在の性質ではできない。MicrosoftはDeployment Stacksと呼ばれる仕組みで改善を検討している
- Bicepは、Complete modeを使うとコードとリソースが一致をある程度保障できる。IaCとして使うならComplete一択。(それでもずれる)
- Completeでは、最後に適用したBiepデプロイでリソースが決定されるので、Bicepスコープは狭く維持する。このため、ResourceGroupをtarget scopeにするのがいいだろう。Bicepを分ける = ResourceGroupをライフサイクルで分けることになる
Bicep と ARM Template
bicepは、ARM TemplateのDSLでARM Templateに変換される。あくまでDSLなので、デプロイを含めた仕様、制約はARM Templateに準じる
ARM Templateの特徴は次の通り
Stateless
ARM Templateは、Stateを持たずTemplate = リソース定義のJSONとリソースを一致させる動作を目指している。(Terraform/Pulumi/CloudFormationはStateを持つ)
Bicepは、コードとリソースの一致保障が仕組みとして存在していない。この対策として、bicep及びARM Templateライフサイクル全体への改善としてDeployment Stacksが検討されている。
Any plans for destroy functionality? · Discussion #1680 · Azure/bicep
デプロイのスコープ
デプロイは、target scopeで設定できる。ResourceGroup / Subscription / Tenantと広くなっていく。
デプロイモードによっては、target scopeで1deployしか適用できないケースがあるので、スコープの決定は設計に大きく影響を与える。
基本的に、Bicepを分ける = ResourceGroupを分ける = ライフサイクルごとの管理、になるだろう。
デプロイモード
ARM Template Deployにはモードが2つある。
- Increment (デフォルト)
- Complete
Incrementは増分デプロイで古いリソースはテンプレートからは消えてもリソースは残る。
Completeはテンプレートにないリソースを消すが、消されるリソースと消えないリソースがあり、その動作はリソース次第。
Complete mode deletion - Azure Resource Manager
デプロイによる一致保障
ARM Templateのデプロイの仕組みから、Bicepコードとテンプレートは一致と冪等性が保障されているが、コードとリソースの一致と冪等性は保障されていない。
あくまでも「適用されているテンプレート × デプロイモード」によって、リソースの状態が決定する。
デプロイ方法と適用
それぞれのscopeに対して複数のdeployを適用できる。
ただし、どのdeployが適用されるかは、モードによって変わる。
- Incrementを使うと、増分なのでそれぞれの増分が適用される。(複数のDeployが当たる)
- Completeを使うと、最後に適用したdeployでリソースが構成される。ほかのdeployで適用されたリソースは最後のdeployのテンプレート次第で消える。(複数は当たらない)
Completeを使うとコードとリソースの一致は、デプロイ時は保障できる。だが、Completeによる削除処理はリソースによって「消えない」ため、削除操作によってコードとリソースは乖離していく可能性がある。たとえば、StorageAccountは削除されるが、Blobは消えない、など。
Incrementは、削除を絶対にしないので、コードとリソースはどんどんずれていく。IaCで目指すコードとリソースの一致とは、そもそも目指すところが違うのでIaCをするのであればIncrementは使う理由がない。
IaC としての Bicep
IaCは、コードでリソースの状態を明確に示すことが重要になる。コードでリソースの状態を明確に示すというのは、今のリソースがコード通りです。
これは「コード以外のリソースは認めている」が、一方で「コードで管理されていたリソースが外れるときにそのリソースを削除する」というのも期待している。
つまり、コードとリソースを一致させるには、コードとリソースの同期をどうとるのか、同期が取れないリソースをどう扱うかが重要になる。
削除
Azureではリソース名 = リソースの一意性を意味する。
Bicepで定義したリソース名 (resource id) が変更されたときに、リソースはどのようになるのかかがIaCとしてのBicepを決定する。
デプロイモードで記載した通り、次の挙動になる。
- Increment = 増分デプロイなのでリソースの名前が変わったことを検知せず、ただ「Templateに存在しないリソースが増えたと解釈」される。結果、古いリソースは放置、新しいリソースが構成される。もしテンプレートが既存リソースに対する定義だった場合、リソースの設定をTemplateで上書き構成する
- Complete = Templateとリソースの一致なので、Template通りにリソースを構成する。具体的には、テンプレートにないリソースは、仕様で削除対象になってれば削除、削除対象じゃなければ放置。もしテンプレートが既存リソースに対する定義だった場合、リソースの設定をTemplateで上書き構成する
Incrementでは削除が起こらない。
Completeでは、Templateと一致するように削除が起こるが、削除されないリソースが多く、削除されなかったら自分で削除が必要になる。(コードとリソースの一致を目指していないのでそうなる)
Destroy
削除の挙動から予想できる通り、Destroyは存在しない。
Bicepの定義をまとめて吹き飛ばしたいときは、リソースが空なBicepをCompleteモードでデプロイする。
すると、Completeで削除する仕様のリソースは消える。消えない仕様のリソースは残る。
Bicep や ARM Template は IaC なのか
コードとリソースの一致を目指すIaCというよりは、Templateとリソースの一致を目指しそのTemplateを生成するTaCというのが適切だろう。
Bicep と State
コードとリソースが一致できないのは、 Microsoftも把握している通り、Statelessであるためのライフサイクルマネジメントの困難さに起因している。
このため、Deployments StackがくればBicepはおそらくコードとリソースの一致ができるようになるようにライフサイクルが改良されるだろう。
同時に、Stateを持たないことで、Template通りに構成するControl PlaneがManagedなAzure Resource Managementであったが、Stateをどこに置くかというのを考えることになるだろう。
Bicep で IaC っぽく使う
BicepというよりARM Templateの現状の挙動は、いわゆるIaCに期待する挙動とは乖離しており、コードとリソースの一致も保証できないという意味では難しい。
- Subscription:
--mode
がないのでCompleteの指定はできない。安全優先って感じ - ResourceGroup: 基本的にCompleteで利用し、消えないリソースは都度消す (これが怖い) ようにすれば、おおむねIaCっぽくは利用できるだろう
まとめ
Terraform x Azureは、書きにくいけどまともなIaC体験は得られます。 BicepはARM TemplateがStatelessという性質を指向しているがゆえに、コードとリソースの一致が約束されないとIaC体験としては悪いものがあります。 というか、リソースが残ってしまって、それを消すときに事故臭しかしない。
Bicep自体というより、ARM Templateの性質の問題なので、Deployment StacksによってStackのコンセプトが来たら、よりよい体験になると期待したいところです。