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 のコンセプトが来たら体験が変わるのを期待したいところです。