Terraformリソースプロバイダプラグインの作り方については@tkakさんのTerraformのProviderを作ったというエントリによくまとめられていて、プラグイン作成の全体の流れを把握することができます。
ここでは、プロバイダプラグインが実際にリソースを操作する方法をまとめていきます。
リソースに対するCRUD
Terraformリソースプロバイダはschema.ResourceData
というstructを利用してリソースを表現します。
ResourceDataは内部にプロパティを保持しており、ユーザが.tfファイルに記述した各種定義はここに格納され、下記のようにGet()
やSet()
を介してアクセスできます。
// 入力
// resource "foo_resource" "bar" {
// your_awesome_name = "baz"
// }
func YourFunc(d *schema.ResourceData) {
// 読み込み
v := d.Get("your_awesome_name").(string)
// v == "baz"
// 書き込み
d.Set("your_awesome_name", "new_value")
}
TerraformリソースプロバイダはこれらのAPIを用いてリソースに対するCRUDを実装します。CRUD用の関数はすべて下記のような同じインターフェイスで実装します。
func createFoo(d *schema.ResourceData, m interface{}) error {
// 引数mはConfigureFuncで返す値が渡されます
// ...
return err
}
これらの関数をschema.Resource
のフィールドにセットすることで、リソースのCRUD関数として動くようになります。
ここから先はCRUDの各関数で行うべきことについて簡単にまとめます。
Create
.tfファイルの定義をもとに、実際のリソースを作成します。
Create関数は実際のリソースがまだ存在していない時のみ呼び出されます。すでにリソースが存在している場合にはUpdate関数が呼び出されます。リソースの存在については後述するRead関数によってチェックされます。
func readFoo(d *schema.ResourceData, m interface{}) error {
foo := PutFoo(d.Get("awesome_name").(string))
d.SetId(foo.Id)
// ...
return err
}
Read
実際のリソースの状態を取得し、引数として渡されてきたResourceDataへ書き込みます。
渡されてきたResourceDataにはユーザが.tfファイルに記述した値があらかじめセットされていて、その値と実際のリソースの状態に差分がある場合にプロバイダが実際の値で上書きするようにします。
Read関数でResourceDataへの上書きを行った場合、Terraformは設定ファイルと実際のリソース状態の差分を埋めるためにUpdate関数を呼び出します。逆に、ResourceDataへ何も書き込まなかった場合にはUpdate関数の呼び出しはスキップされます。
また、対象のリソースが実際には存在していなかった場合にはResourceDataのSetId()
関数で空文字列を渡すようにします。この場合、TerraformはCreate関数を呼び出してリソースを新たに作成しようとします。
func readFoo(d *schema.ResourceData, m interface{}) error {
foo := GetFoo(d.Id())
if foo == nil {
d.SetId("")
} else if d.Get("awesome_name").(string) != foo.AwesomeName {
d.Set("awesome_name", foo.AwesomeName)
}
// ...
return err
}
Update
Read関数を呼び出し、.tfファイルの定義と実際のリソースに差分があった場合にはUpdate関数が呼ばれます。この関数では、引数として渡されるResourceDataの内容にあわせて実際のリソースを変更します。
ResourceDataのプロパティに変更があったかどうかを知るには、HasChange()
関数を使用します。これによって変更のあったプロパティのみを更新することができます。
func updateFoo(d *schema.ResourceData, m interface{}) error {
if d.HasChange("awesome_name") {
EditFooAwesomeName(d.Id(), d.Get("awesome_name").(string))
}
// ...
return err
}
Delete
実際のリソースを削除します。
func deleteFoo(d *schema.ResourceData, m interface{}) error {
DeleteFoo(d.Id())
d.SetId("")
return err
}