自分がTerraformをデバッグしている時に感じたコツを残しておきます。
Terraform適用までの流れ
Terraform for さくらのクラウド入門チュートリアル
に絵があったのでこちら
考え方
Terraformでエラーが出た場合原因として大まかに7パターン(実質6パターン)が考えられます。上から順に遭遇する確率は大きいです。
特に4以降に遭遇することはあまりないでしょう。
- Terraformの実行環境の問題
- Terraformのコードの問題
- Terraformでプロビジョニングする対象(AWS側)の問題
- Terraform自体の問題
- Terraformが内部で使用するAWS SDK Goの問題
- AWSのAPIの問題->わからなかったらAWSに問い合わせ
- Go言語の問題(ここまでくることはほぼ0なので対象外)
切り分け方
Terraformの実行環境の問題
terraform -vでバージョンが出れば実行環境としては問題ないです。
これが出ないときは環境構築やterraformのバイナリへのパスが通っていない問題が考えられるので確認します。
Terraformのコードの問題
- terraform validateコマンドで構文チェック
- terraform plan
を実行すると大体解決します。
Terraform自体のバリデーションが甘いこともあるのでこれで問題が解決しないこともあるのですがplanまでエラーなく通ればほぼ問題ないです
なおTerraformの書き方についてはTerraform providerのドキュメントに書き方が載っていることが多いです。
Terraformでプロビジョニングする対象(AWS側)の問題
おそらく、これが遭遇するエラーで一番多いです。
Terraformの定義ファイルは正しいのにエラーとなる場合はほぼ間違いなくAWS側の制約が原因でエラーになっています。
例えば特定のオプションの組み合わせはAWS側として許されていない等のエラーです。
この場合はまずエラーメッセージを読みます。
このエラーメッセージは英語で出るのですが、このメッセージを読まないと原因に到達することはほぼ無理です。
なお、このメッセージが出る段階ではAWSのAPIをTerraform内部で実行した結果が出ていることが多いのでAWSのAPIドキュメントを確認したり、AWSのコンソールから同じ操作をしたときにエラーとなるか確認すると原因が判明することもあります。
詳細ログを見たい場合はDebugging Terraformにあるように
TF_LOGを環境変数に追加してあげるとログレベルを変えられます。
Terraform自体の問題
ここからはTerraform内部の話になるのであんまりググっても情報が出てこないことが多いです。
いわゆるTerraform側のバグの可能性です。
こちらに関連するのは主に以下の3つのリポジトリです。
それぞれ
- provider =>AWS等対象リソースのプロビジョニングの処理が書かれている、だいたいこちらを見ると各リソースをどのように作成/定義しているのかが記載されている
- Terraform本体=>Terraform共通の処理planとかapplyとかの処理が書かれているが本体が原因となっている場合はあまりなく優先度低め
- plugin sdk=>provider側のコードを読んでいるとなんでこの処理で色々な操作ができるのだろうと思うことがあります。この秘密はこちらで定義されていることが多いです。こちらもほとんど読む必要はないです。
特にprovider側のISSUEやPull Requestに同じような問題がないかうまいキーワードを考えて検索します。だいたい捜査対象のリソースで絞ると問題一覧が出てくるのでここにもなければ新規の問題の可能性が高いです。
Terraformが内部で使用するAWS SDK Goの問題
*AWSを例にしています。
Terraform provider awsではaws-sdk-goを使用しています。2022/12/15の時点ではV1使っています。こちらの問題の可能性もあります。
調べ方
Terraform provider aws側でどのようにリソースを作成しているのか調べます。
まず当該リソースについて記載されたコードを探します。
コードの命名規則は
/internal/service/<調べたいサービス名>/<調べたい定義名>.go
のようになっているので見つけるのはそんなに難しくないです。
例えばAuroraのインスタンスの場合
https://github.com/hashicorp/terraform-provider-aws/blob/main/internal/service/rds/cluster_instance.go
注目は
resourceClusterInstanceCreate
のようにCreateと記載があるメソッドです、これがリソースの作成処理が書かれています。
terraform-provider-aws各リソース処理のコードは基本的に
- 当該リソースの定義(ResourceClusterInstance:Schemaの箇所)
- CRUD
の構成になっています。
なのでリソースの定義情報を確認したい場合はSchemaの箇所を確認する、作成処理を確認したい場合はCreateとついたメソッドを確認します。
その形でみていくと
https://github.com/hashicorp/terraform-provider-aws/blob/main/internal/service/rds/cluster_instance.go#L300
に
conn.CreateDBInstanceWithContext(ctx, input)
のようにconn.<なんかのメソッド>
の処理が出てきます。
ここがAWS SDK経由でAWSのAPIを呼び出している箇所です。
この場合はCreateDBInstanceというSDK側のコードを呼び出しています。
実際aws-sdk-go側に
https://raw.githubusercontent.com/aws/aws-sdk-go/main/service/rds/api.go
// CreateDBInstanceWithContext is the same as CreateDBInstance with the addition of
// the ability to pass a context and additional request options.
//
// See CreateDBInstance for details on how to use this API operation.
//
// The context must be non-nil and will be used for request cancellation. If
// the context is nil a panic will occur. In the future the SDK may create
// sub-contexts for http.Requests. See https://golang.org/pkg/context/
// for more information on using Contexts.
func (c *RDS) CreateDBInstanceWithContext(ctx aws.Context, input *CreateDBInstanceInput, opts ...request.Option) (*CreateDBInstanceOutput, error) {
req, out := c.CreateDBInstanceRequest(input)
req.SetContext(ctx)
req.ApplyOptions(opts...)
return out, req.Send()
}
のように同じ名前のメソッドがあります。
あとはこのメソッドの中身を確認して実装有無を調べます。
AWSのAPIの問題
AWS SDK Goまで調べたのに問題がみつからないケースもあります。
この場合はAWS SDK Goで呼び出しているAWS APIの情報も確認して最終的にそれでもわからなければAWS側に原因の問い合わせすることになります。
例えば
https://github.com/hashicorp/terraform-provider-aws/issues/20789
のケースだとAWS SDK Go側にタグの定義がなくさらにAWSのAPI側の処理を調べる必要がありました。
https://docs.aws.amazon.com/cloudhsm/latest/APIReference/API_CreateHsm.html
APIの定義情報を確認するとそもそもタグが用意されていないかったということがわかりました。
まとめ
- Terraformで問題が起きたらTerraformの書き方に問題がないかterraform validate + planで調べる
- その上でエラーが起きたらエラーメッセージを読む
- それでもエラーメッセージの意味がわからなかったら結構難しい問題の可能性があるのでみんなにとりあえず聞いてみよう
くらいでデバッグを進めるとよかったです。