エラーの内容
terraform plan または terraform apply を実行すると、次のエラーで止まります:
│ Error: Invalid function argument
│
│ on main.tf line 14, in resource "aws_security_group" "example":
│ 14: cidr_blocks = tolist(var.allowed_cidr)
│
│ Invalid value for "list" parameter: cannot convert string to list of any
│ single type.
根本原因は型の不一致です。リストまたはセットを期待する関数(tolist()、toset()、length()、element()、join() など)に文字列を渡しています。Terraformは意図を推測せず、プラン時に即座に失敗します。
なぜこのエラーが発生するか
Terraformの型システムは暗黙の型変換を行いません。すべての組み込み関数はパラメータの型が厳密に定義されており、型が間違えると警告やフォールバックなしに即座に失敗します。次の4つの状況が最もよく見られます:
- 変数が
type = stringと宣言されているのに、リストを期待する関数に渡されている - モジュールの出力が単一の文字列を返しているのに、呼び出し元モジュールがリストとして扱っている
"10.0.0.0/8,192.168.0.0/16"のようなカンマ区切りの文字列を書いて、Terraformが自動的に分割してくれると思っている — 実際にはそうならない- データソースの属性がリストではなく文字列スカラーである(
aws_amiIDやARNなどでよく見られる)
手順ごとの修正方法
ステップ1 — 値の実際の型を確認する
エラーに記載されている .tf ファイルを開き、変数または属性の宣言を確認します。典型的な問題例はこちらです:
# 間違い — 文字列として宣言してリストとして使用している
variable "allowed_cidr" {
type = string
default = "10.0.0.0/8"
}
resource "aws_security_group" "example" {
ingress {
cidr_blocks = tolist(var.allowed_cidr) # ← ここでエラー
}
}
var.allowed_cidr は単純な文字列です。tolist() はセットまたはタプルのコレクションを必要とします。この型の不一致が問題のすべてです。
ステップ2 — 変数の型宣言を修正する
最もシンプルな修正方法:最初からリストとして変数を宣言します。
# 正しい — リストとして宣言する
variable "allowed_cidr" {
type = list(string)
default = ["10.0.0.0/8"]
}
resource "aws_security_group" "example" {
ingress {
cidr_blocks = var.allowed_cidr # 変換不要
}
}
tolist() はもう必要ありません。list(string) 型の変数はすでに cidr_blocks に適切な型です — そのまま使えます。
ステップ3 — 文字列を受け取る必要がある場合は split() で変換する
CIパイプライン、環境変数、レガシー設定ではカンマ区切りの文字列として値が渡されることがよくあります。入力形式を変更できない場合は、split() を使って実際のリストに変換します:
variable "allowed_cidr" {
type = string
default = "10.0.0.0/8,192.168.0.0/16"
}
locals {
cidr_list = split(",", var.allowed_cidr)
}
resource "aws_security_group" "example" {
ingress {
cidr_blocks = local.cidr_list
}
}
変換処理を local にまとめることで、リソースブロックをすっきり保ち、後でコードを読む人にも意図が明確になります。
ステップ4 — 一意なコレクションには toset() を使う(for_each など)
for_each はセットまたはマップを必要とします — 文字列でもリストでもありません。変数が string 型のままだと、次のようにエラーになります:
# 間違い
resource "aws_iam_user" "example" {
for_each = toset(var.username) # var.username は文字列 → エラー
name = each.value
}
# 正しい
variable "usernames" {
type = list(string)
default = ["alice", "bob", "carol"]
}
resource "aws_iam_user" "example" {
for_each = toset(var.usernames) # リスト → セット: OK
name = each.value
}
toset() はリストをセットに変換します(その過程で重複値を除去します)。リストに対して機能します — 文字列には使えません。
ステップ5 — 必要に応じて単一の文字列を角括弧で囲む
AMI ID、ARN、アカウントIDなどのデータソース属性は常に文字列スカラーです。ほとんどの場合はそれで問題ありませんが、ダウンストリームのリソースがリストを期待することがあります。その場合は角括弧で囲みます:
data "aws_ami" "ubuntu" {
most_recent = true
# ...
}
# data.aws_ami.ubuntu.id は文字列
resource "aws_launch_template" "example" {
image_id = data.aws_ami.ubuntu.id # そのまま使用して問題なし
# リストが必要な箇所:
# security_group_names = [data.aws_ami.ubuntu.id] ← 角括弧で囲む
}
修正の確認
まず terraform validate を実行します — APIコールなしで即座に型エラーを検出できます:
terraform validate
設定が正しければ次のように出力されます:
Success! The configuration is valid.
次に terraform plan を実行して、実行時の型エラーが残っていないか確認します。プランに正しいCIDRブロック(または修正した属性)がリソースに表示されていれば完了です。
型変換関数クイックリファレンス
split(",", string)— 文字列 → list(string)、区切り文字で分割tolist(set_or_tuple)— セット/タプル → リスト(文字列には使えない)toset(list)— リスト → セット、重複を除去(文字列には使えない)[value]— 任意のスカラーを単一要素のリストリテラルに変換compact(list)— リストから空文字列を除去flatten(list_of_lists)— ネストされたリストを一つのフラットなリストに展開
このエラーを防ぐためのヒント
- 変数の型を明示的に宣言する。
type = anyは避けてください — 型の問題を実行時まで隠してしまい、最悪のタイミングで発覚します。 - 関数を使う前にドキュメントを読む。 Terraformのすべての組み込み関数には、各パラメータが受け付ける型を示す明確なシグネチャがあります。30秒の確認で20分のデバッグを節約できます。
- 設定にコミットする前に
terraform consoleで式をテストする:
$ terraform console
> tolist(["a", "b"])
[
"a",
"b",
]
> tolist("not-a-list")
│ Error: Invalid function argument
│ cannot convert string to list of any single type.
- CIパイプラインに
terraform validateを追加する。 1秒以内に実行でき、認証情報も不要で、型の不一致をplanに到達する前に検出できます。

