variable(入力変数)— 型・デフォルト値・バリデーション・機密値の完全解説

1. 概要

この記事では、以下の内容を解説します。

  • variableブロックの基本構文と全引数(type / default / description / sensitive / nullable / validation)
  • 型制約の書き方(string / number / bool / list / map / object / tuple / any)
  • 変数に値を渡す方法(tfvars / -var / 環境変数)の優先順位
  • 機密値を扱うためのsensitiveオプション
  • validationブロックによる入力チェック
  • AWSを使った実践的なコード例
  • localsとの使い分け

variableブロックは、Terraformコードを環境(dev/stg/prd)やプロジェクトごとに柔軟に使い回すための仕組みです。コードに値をハードコードするのではなく、変数として外部から受け取ることで、再利用性と保守性が大幅に向上します。


2. Terraformにおける位置付け

variableブロックとは

variable(入力変数)はTerraformの外部から値を渡すための仕組みです。

dev.tfvars / -var オプション / 環境変数
            ↓
      variable ブロック(変数の受け口)
            ↓
      resource ブロックで参照(var.xxxで参照)

例えば「環境によってEC2のインスタンスタイプを変えたい」という場合、インスタンスタイプをvariableで定義しておき、dev環境ではt3.micro、prd環境ではt3.mediumを渡すことができます。

localsとの違い

variableと混同されやすいlocalsとの主な違いを以下に示します。

比較項目variablelocals
外部から値を変えられるか✅ 変えられる(tfvars等)❌ コード内で固定
Terraformの外部から渡すか✅ ユーザーや CI が渡す❌ コード内で計算
動的な式を定義できるか❌ 式は書けない(値のみ)✅ 式・計算結果を定義できる
主な用途環境・設定値の切り替え繰り返しの式に名前を付ける

判断の目安:

  • terraform applyのたびに違う値を渡す可能性がある」→ variable
  • 「コード内で同じ式や計算を何度も書いている」→ locals

詳細は「locals vs variableの違い」を参照してください。


3. 基本構文

variable "<変数名>" {
  # 引数(すべて省略可能)
  type        = <型>
  default     = <デフォルト値>
  description = "<説明>"
  sensitive   = <true/false>
  nullable    = <true/false>

  validation {
    condition     = <条件式>
    error_message = "<エラーメッセージ>"
  }
}

変数の値は var.<変数名> で参照します。

variable "instance_type" {
  type    = string
  default = "t3.micro"
}

resource "aws_instance" "web" {
  instance_type = var.instance_type  # ← var.変数名 で参照
}

4. 詳細解説

全引数一覧

引数必須デフォルト説明
type型制約任意any受け取る値の型を制限する
default任意任意なしデフォルト値。指定なしの場合、apply時に入力を求められる
descriptionstring任意""変数の説明(terraform-docs等のドキュメント生成に使われる)
sensitivebool任意falsetrueにすると、plan/apply出力でマスクされる
nullablebool任意truefalseにすると、この変数にnullを渡すことを禁止する
validationブロック任意なし入力値のカスタムバリデーション

type — 型制約

type引数で受け取る値の型を指定します。型が一致しない値が渡された場合はエラーになります。

プリミティブ型

variable "env_name" {
  type = string   # "dev", "stg", "prd" など
}

variable "instance_count" {
  type = number   # 1, 2, 3 など(整数・小数を含む)
}

variable "enable_deletion_protection" {
  type = bool     # true / false
}

コレクション型

# list: 順序があり、同じ型の要素が並ぶ
variable "availability_zones" {
  type    = list(string)
  default = ["ap-northeast-1a", "ap-northeast-1c"]
}

# map: 文字列をキーとして値を持つ
variable "instance_types" {
  type = map(string)
  default = {
    dev = "t3.micro"
    stg = "t3.small"
    prd = "t3.medium"
  }
}

# set: 重複を許さないリスト(for_eachによく使う)
variable "allowed_cidr_blocks" {
  type = set(string)
  default = ["10.0.0.0/8", "172.16.0.0/12"]
}

構造型

# object: 属性ごとに型を指定できる構造体
variable "database_config" {
  type = object({
    instance_class    = string
    allocated_storage = number
    multi_az          = bool
  })
  default = {
    instance_class    = "db.t3.medium"
    allocated_storage = 20
    multi_az          = false
  }
}

# tuple: 要素ごとに型が異なるリスト(使用頻度は低い)
variable "network_config" {
  type = tuple([string, number])
  # 例: ["10.0.0.0/16", 24]
}

optional() — objectの省略可能属性(Terraform 1.3以降)

object型の属性を省略可能にするにはoptional()を使います。

variable "server_config" {
  type = object({
    instance_type = string                       # 必須
    volume_size   = optional(number, 20)         # 省略時は 20 が使われる
    tags          = optional(map(string), {})    # 省略時は空のmapが使われる
  })
  default = {
    instance_type = "t3.micro"
    # volume_size と tags は省略可能(defaultが適用される)
  }
}

any

variable "tags" {
  type    = any       # 型制約なし
  default = {}
}

⚠️ 注意: any型は型チェックが行われないため、意図しない型の値が渡されてもエラーになりません。型が明確な場合はanyを避け、具体的な型を指定することを推奨します。


default — デフォルト値

defaultを設定すると、値が渡されなかった場合にそのデフォルト値が使われます。defaultを設定しない場合は、terraform apply時に値の入力が求められます。

# デフォルトあり: 値が渡されなければ "dev" が使われる
variable "environment" {
  type    = string
  default = "dev"
}

# デフォルトなし: apply時に必ず値を指定する必要がある
variable "aws_account_id" {
  type        = string
  description = "AWSアカウントID"
  # defaultなし → apply時に入力を求められる
}

sensitive — 機密値のマスク

sensitive = trueを設定すると、terraform planterraform applyの出力でその変数の値がマスク((sensitive value)と表示)されます。

variable "db_password" {
  type      = string
  sensitive = true  # plan/apply出力でマスクされる
}

⚠️ 重要: sensitive = trueにしても、terraform.tfstateファイルには平文で記録されます。Stateファイルの取り扱いには注意が必要です。詳細は「state管理とは」を参照してください。


nullable — nullの許可・禁止

nullable = falseを設定すると、この変数にnullを渡すことができなくなります(Terraform 1.1以降)。

variable "environment" {
  type     = string
  nullable = false  # null を渡すと即座にエラー
}

nullableのデフォルトはtrueです。変数にデフォルト値が設定されており、かつnullを渡したときにデフォルト値にフォールバックしてほしい場合はnullable = trueのままにします。


validation — 入力値のカスタムバリデーション

validationブロックで、受け取る値の条件を独自に設定できます。

variable "environment" {
  type        = string
  description = "デプロイ環境名"

  validation {
    condition     = contains(["dev", "stg", "prd"], var.environment)
    error_message = "environment は dev / stg / prd のいずれかを指定してください。"
  }
}

variable "vpc_cidr" {
  type        = string
  description = "VPCのCIDRブロック"

  validation {
    condition     = can(cidrhost(var.vpc_cidr, 0))
    error_message = "有効なCIDR形式(例: 10.0.0.0/16)を指定してください。"
  }
}

variable "instance_type" {
  type        = string
  description = "EC2インスタンスタイプ"

  validation {
    condition     = startswith(var.instance_type, "t3.") || startswith(var.instance_type, "t4g.")
    error_message = "instance_type は t3.xxxxx または t4g.xxxxx 系を指定してください。"
  }
}

validationブロックは複数設定できます。条件式(condition)にはtrueまたはfalseを返す任意のTerraform式が使えます。


変数に値を渡す方法と優先順位

変数に値を渡す方法は複数あります。複数の方法が重複した場合は、数字が大きいほど優先度が高いです。

優先度方法書き方
1(低)default引数variableブロック内に記述
2環境変数export TF_VAR_environment=prd
3terraform.tfvars変数名 = "値" の形式で記述
4terraform.tfvars.jsonJSON形式で記述
5<em>.auto.tfvars / </em>.auto.tfvars.json自動読み込み(dev.auto.tfvars等)
6(高)-var-file / -varオプション(HCP Terraform変数)terraform apply -var="environment=prd"

⚠️ 重要: CLIオプション(-var-var-file)とHCP Terraform変数が最高優先度です。環境変数(TF_VAR_)はterraform.tfvarsより低い優先度です。*.auto.tfvarsterraform.tfvarsより高い優先度です。

terraform.tfvars の書き方

# terraform.tfvars
environment   = "dev"
instance_type = "t3.micro"
vpc_cidr      = "10.0.0.0/16"

複数環境への対応(*.tfvarsを環境ごとに分ける)

# ディレクトリ構成例
project/
├── main.tf
├── variables.tf
├── dev.tfvars   # dev環境用
├── stg.tfvars   # stg環境用
└── prd.tfvars   # prd環境用
# 使い方
terraform apply -var-file="dev.tfvars"   # dev環境
terraform apply -var-file="prd.tfvars"   # prd環境

⚠️ 注意: terraform.tfvarsや機密情報を含む*.tfvarsファイルはGitリポジトリに含めないよう.gitignoreに追加してください。


5. 実践例

例1: 環境ごとに設定を変えるパターン

# variables.tf
variable "environment" {
  description = "デプロイ環境名"
  type        = string

  validation {
    condition     = contains(["dev", "stg", "prd"], var.environment)
    error_message = "environment は dev / stg / prd のいずれかです。"
  }
}

variable "instance_type" {
  description = "EC2インスタンスタイプ"
  type        = string
  default     = "t3.micro"
}

variable "vpc_cidr" {
  description = "VPCのCIDRブロック"
  type        = string
  default     = "10.0.0.0/16"

  validation {
    condition     = can(cidrhost(var.vpc_cidr, 0))
    error_message = "有効なCIDRブロック形式を指定してください。"
  }
}

variable "enable_deletion_protection" {
  description = "RDSの削除保護を有効にするか"
  type        = bool
  default     = false
}
# dev.tfvars
environment                = "dev"
instance_type              = "t3.micro"
vpc_cidr                   = "10.0.0.0/16"
enable_deletion_protection = false
# prd.tfvars
environment                = "prd"
instance_type              = "t3.medium"
vpc_cidr                   = "10.1.0.0/16"
enable_deletion_protection = true
# main.tf(variableを参照)
resource "aws_vpc" "main" {
  cidr_block = var.vpc_cidr    # ← tfvarsの値が入る

  tags = {
    Name        = "${var.environment}-vpc"
    Environment = var.environment
  }
}

resource "aws_instance" "web" {
  ami           = data.aws_ami.amazon_linux_2023.id
  instance_type = var.instance_type    # ← 環境によって変わる

  tags = {
    Name = "${var.environment}-web"
  }
}

例2: map型で環境ごとの設定を一元管理

# variables.tf
variable "environment" {
  type = string
}

variable "ec2_config" {
  description = "EC2の設定(環境ごと)"
  type = map(object({
    instance_type = string
    volume_size   = number
  }))
  default = {
    dev = {
      instance_type = "t3.micro"
      volume_size   = 20
    }
    stg = {
      instance_type = "t3.small"
      volume_size   = 30
    }
    prd = {
      instance_type = "t3.medium"
      volume_size   = 50
    }
  }
}
# main.tf
resource "aws_instance" "web" {
  ami           = data.aws_ami.amazon_linux_2023.id
  instance_type = var.ec2_config[var.environment].instance_type   # dev → "t3.micro"

  root_block_device {
    volume_size = var.ec2_config[var.environment].volume_size      # dev → 20
    volume_type = "gp3"
    encrypted   = true
  }

  tags = {
    Name = "${var.environment}-web"
  }
}

例3: 機密値の管理(DBパスワード、APIキー)

# variables.tf
variable "db_password" {
  description = "RDSのマスターパスワード"
  type        = string
  sensitive   = true   # plan/apply出力でマスクされる
  nullable    = false  # nullは禁止

  validation {
    condition     = length(var.db_password) >= 8
    error_message = "パスワードは8文字以上を指定してください。"
  }
}
# main.tf
resource "aws_db_instance" "main" {
  identifier        = "${var.environment}-db"
  engine            = "mysql"
  engine_version    = "8.0"
  instance_class    = "db.t3.medium"
  allocated_storage = 20
  db_name           = "appdb"
  username          = "admin"
  password          = var.db_password    # ← sensitiveな変数を参照

  skip_final_snapshot = true  # 検証環境用(本番はfalseを推奨)

  tags = {
    Name = "${var.environment}-db"
  }
}

💡 ヒント: 機密値はtfvarsに書かずに環境変数で渡すのがより安全です。

export TF_VAR_db_password="your-secure-password"
terraform apply

例4: object型で複数の設定をまとめる

# variables.tf
variable "alb_config" {
  description = "ALBの設定"
  type = object({
    internal           = bool
    idle_timeout       = number
    access_logs_bucket = string
  })
  default = {
    internal           = false
    idle_timeout       = 60
    access_logs_bucket = ""
  }
}
# main.tf
resource "aws_lb" "main" {
  name               = "${var.environment}-alb"
  internal           = var.alb_config.internal       # オブジェクトの属性にアクセス
  load_balancer_type = "application"
  subnets            = aws_subnet.public[*].id
  idle_timeout       = var.alb_config.idle_timeout

  tags = {
    Name = "${var.environment}-alb"
  }
}

6. よくあるエラー

Error: No value for required variable

│ Error: No value for required variable
│
│   on variables.tf line 1:
│    1: variable "environment" {
│
│ The root module variable "environment" is not set, and has no default value.
│ Use a -var or -var-file command line argument to provide a value for this variable.

原因: defaultが設定されていない変数に値が渡されていない。

解決方法: terraform.tfvarsに値を記述するか、-varオプションで渡す。

terraform apply -var="environment=dev"
# または terraform.tfvars に environment = "dev" を追記

Error: Invalid value for input variable(validationエラー)

│ Error: Invalid value for input variable
│
│   on terraform.tfvars line 1:
│    1: environment = "production"
│
│ Invalid value for variable "environment" (from terraform.tfvars line 1):
│ environment は dev / stg / prd のいずれかを指定してください。

原因: validationブロックの条件に合わない値が渡された。

解決方法: error_messageに従い、正しい値を渡す。


Error: Variables may not be used here(backendブロック内)

│ Error: Variables may not be used here
│
│   on providers.tf line 8:
│    8:   bucket = var.state_bucket
│
│ Variables may not be used here.

原因: terraformブロックのbackend設定内では変数が使えない。

解決方法: backendに変数を使いたい場合は、-backend-configオプションで渡す。

terraform init -backend-config="bucket=my-terraform-state-bucket"

型の不一致エラー

│ Error: Invalid value for input variable
│
│ Inappropriate value for attribute "instance_type": string required.

原因: type = stringの変数にnumberboolを渡した。

解決方法: 変数の型に合った値を渡す。またはtype = anyにして型制約を緩める(非推奨)。


7. ベストプラクティス

推奨パターン

typeは必ず指定する

型を指定することで、誤った値が渡されたときに即座にエラーになります。anyは型チェックが働かないため、デバッグが難しくなります。

descriptionは必ず書く

変数の意味・用途・設定例をdescriptionに記述します。terraform-docsなどのドキュメント生成ツールを使うと、descriptionの内容が自動でドキュメントに反映されます。

variable "instance_type" {
  description = "EC2インスタンスタイプ。開発環境: t3.micro / 本番: t3.medium を推奨"
  type        = string
  default     = "t3.micro"
}

機密値にはsensitive = trueを付ける

DBパスワード、APIキー、シークレットキーなど機密情報を変数で管理する場合はsensitive = trueを設定します。ただしterraform.tfstateには平文で記録されることを忘れないでください。sensitive値の詳細な挙動・nonsensitive()・ephemeral変数との比較は sensitive変数 — 機密値のマスクとstate記録 を参照してください。

validationで入力値を制限する

想定外の値によるデプロイエラーを防ぐために、可能な限りvalidationブロックを設定します。特にenvironment(dev/stg/prd)やリージョン名はcontains()で制限すると安全です。

変数名はスネークケースで記述する

Terraform公式スタイルガイドに従い、変数名はスネークケース(単語をアンダースコアで繋ぐ形式)で記述します。

# 推奨: スネークケース
variable "instance_type" {}
variable "vpc_cidr_block" {}
variable "enable_deletion_protection" {}

# 非推奨
variable "instanceType" {}      # キャメルケース
variable "instance-type" {}     # ハイフン区切り

variables.tfファイルに集約する

変数の定義はvariables.tfファイルにまとめます。main.tfvariableブロックを書くことは技術的には可能ですが、管理が複雑になります。

避けるべきパターン

機密値をdefaultに直接書く

# NG: デフォルト値にパスワードを直書き
variable "db_password" {
  default = "mysupersecretpassword"  # Gitに漏れる
}

tfvarsに機密情報を書いてGitにコミットする

.gitignoreに以下を追加し、機密情報を含むファイルがGitに含まれないようにします。

terraform.tfvars
*.auto.tfvars

type = anyを多用する

型チェックが機能しないため、誤った型の値が渡されたことに気づきにくくなります。型が決まっている場合は具体的な型を指定してください。


8. 関連記事


9. まとめ

  • variableブロックで入力変数を定義し、var.<変数名>で参照する
  • 主な引数は type(型制約)/ default(デフォルト値)/ description(説明)/ sensitive(機密値)/ nullable(null禁止)/ validation(カスタムチェック)
  • 型には string / number / bool / list / set / map / object / tuple / any が使える
  • 値を渡す方法は terraform.tfvars-var-file-var・環境変数(TF_VAR_)があり、後者ほど優先度が高い
  • sensitive = trueはplan/apply出力をマスクするが、Stateファイルには平文で記録される
  • validationブロックで入力値を事前チェックし、想定外の値によるエラーを防ぐ
  • 機密値をdefaultに直書きしたり、tfvarsをGitにコミットしたりしないこと

動作確認バージョン: Terraform >= 1.9 / AWS Provider ~> 5.0 対象リージョン: ap-northeast-1(東京) 公式ドキュメント: https://developer.hashicorp.com/terraform/language/values/variables