問題:すべて正しく見えるのに、エラーが発生する
3つのEC2インスタンスを展開するための複雑なTerraformモジュールを作成し終えたところだとしましょう。シェルスクリプトに動的な環境変数を注入するためにtemplatefile()関数を使用しています。IDE上ではすべてが完璧に見えます。user_data.tplファイルも、あるべき場所であるtemplatesフォルダに保存されています。
しかし、terraform planを実行すると、次のようなエラーに突き当たります:
Error: Error in function call
Call to function "templatefile" failed: open ./templates/user_data.tpl: no such file or directory.
ターミナルからls ./templates/user_data.tplを実行すれば、ファイルが存在することを確認できるかもしれません。では、なぜTerraformはそれを認識しないのでしょうか?問題はファイルが存在しないことではなく、Terraformが間違った場所を探していることにあります。
なぜパスが壊れるのか
Terraformは、./templates/user_data.tplのような相対パスを**現在の作業ディレクトリ(カレントワーキングディレクトリ)**に基づいて解決します。これはterraformコマンドを実行したフォルダのことであり、.tfコードが実際に置かれているフォルダではありません。
プロジェクトの構造が以下のようになっていると仮定します:
.
├── main.tf (ルート設定ファイル)
└── modules/
└── web_server/
├── main.tf (モジュールコード)
└── templates/
└── user_data.tpl
もしmodules/web_server/main.tfの中でtemplatefile("./templates/user_data.tpl", {})を呼び出すと、Terraformはルートディレクトリを起点としてそのファイルを検索します。ファイルはモジュールフォルダの中に深く階層化されているため、検索は失敗します。この実行コンテキストのズレは、GitHub ActionsやJenkinsなどのCI/CDパイプラインにおけるパスエラーの最大の原因です。
まず確認すべきこと
コードを変更する前に、ベテランのエンジニアでも陥りやすい単純なミスを確認しましょう:
- 大文字と小文字の区別: LinuxベースのCIランナーでは、
Templates/とtemplates/は別のフォルダとして扱われます。 - 隠れた拡張子: ファイル名が実際には
user_data.tpl.txtになっていないか確認してください。 - 実行コンテキスト:
-chdirフラグを使用してサブフォルダからTerraformを実行していませんか?これにより、Terraformが相対パスを探す場所が変わります。
確実な解決策:path.moduleを使用する
コードのポータビリティ(移植性)を高めるには、モジュール自体を基準としてファイルを探すようTerraformに指示する必要があります。Terraformには、まさにこの目的のためにpath.moduleという組み込み変数が用意されています。この変数は、その.tfファイルが存在するディレクトリのファイルシステムパスを常に指し示します。
リソースブロックを以下のように更新してください:
# 確実なアプローチ
resource "aws_instance" "web" {
count = 3
# ...
user_data = templatefile("${path.module}/templates/user_data.tpl", {
admin_email = var.admin_email
server_id = count.index
})
}
${path.module}を使用すると、Terraformは絶対パスを生成します。ルートディレクトリ、サブフォルダ、あるいはリモートのビルドエージェントのどこから実行しても関係ありません。パスは常に正しく解決されます。
その他の便利なパス変数
path.root: Terraformの実行を開始したディレクトリを指します。path.cwd: 現在のターミナルの作業ディレクトリを指します。
モジュール内でのtemplatefileの呼び出しには、ほとんどの場合path.moduleが最適なツールです。
Terraform Consoleで素早くデバッグする
パスのテストのためだけに、terraform planの完了を60秒も待つ必要はありません。terraform consoleを使えば、ロジックを即座に検証できます。これは、何もデプロイせずにパスの問題をデバッグする最短の方法です。
- プロジェクトのルートでターミナルを開きます。
terraform consoleを実行します。- 次のコマンドを入力して絶対パスをテストします:
abspath("${path.module}/modules/web_server/templates/user_data.tpl") - ファイルの内容を直接読み取ってみます:
file("${path.module}/modules/web_server/templates/user_data.tpl")
file()関数がエラーを出さずにスクリプトの内容を返せば、パスの問題は解決です。Ctrl+Cを押して終了し、デプロイを進めましょう。
まとめ
「no such file or directory」エラーは、実際にファイルがないことは稀で、ほとんどの場合はTerraformが実行コンテキストをどのように処理するかについての誤解が原因です。ハードコードされた相対パスをやめ、${path.module}を活用することで、インフラ構成コードの耐障害性とポータビリティが高まり、デバッグも格段に容易になります。

