HCP Terraform の変数: 2 種類の変数の置き場所と run での使われ方

HCP Terraform の run は HashiCorp のクラウドの仮想マシンで動くので、AWS の認証情報や環境ごとに変えたい値は、.tf に書かずに HCP Terraform に渡しておく。その置き場所が workspace の変数で、種類は Environment variable と Terraform variable の 2 つ。
この記事では、workspace の変数に AWS のアクセスキーとバケット名の一部を置き、AWS provider で S3 バケットを 1 つ作る。2 種類の変数が run の中でどう使われるかを、次の 3 つで見る。
- 変数が足りないときの run の失敗
- 変数が揃ったときの plan と apply
.tfを変えずに、画面の値だけを変えた run
前提
次の状態から始める。
- HCP Terraform の organization に workspace
tfc-study-stgがある(作り方は「HCP Terraform の workspace と run: plan から apply までの流れと state の置き場所」を参照) - その state に
random_pet.nameが 1 つ入っていて、名前はnational-starfish - workspace の Terraform のバージョンは 1.13.3
- ローカルの開発環境に
terraformv1.13.3 がある - AWS のアカウントがあり、コンソールにログインできる IAM ユーザーと、ローカルの
awsコマンドが使える
workspace の変数
workspace には変数を置ける。種類は 2 つあり、違いは run の中でその値を誰が読むか。
| 種類 | run の中で誰が読むか | .tf との関係 | 例 |
|---|---|---|---|
| Environment variable | terraform を動かす前に run の実行環境の shell に export され、provider や terraform 自体が環境変数として読む | .tf には出てこない | provider の認証情報(AWS_ACCESS_KEY_ID など)、terraform の動作を変える値(TF_LOG など) |
| Terraform variable | terraform が variable ブロックの値として受け取り、.tf が var.名前 で読む | .tf に variable ブロックの宣言がある | バケットの名前、タグ、リージョン、リソースの数など、.tf の中で使う値 |
迷ったときは、ローカルで terraform apply を実行するなら export で渡す値か、-var で渡す値か、で分けられる。前者が Environment variable、後者が Terraform variable。
どちらも workspace ごとに置くので、環境ごとに値を変えられる点は同じ。stg と prod で別の AWS アカウントを使うなら、認証情報の Environment variable も workspace ごとに別の値を置く。
どちらの変数にも Sensitive の印を付けられる。付けると値は書き込み専用になり、画面でも API でも読めなくなる。
provider の認証情報の渡し方
HCP Terraform の run が AWS を操作するには、AWS provider に認証情報が要る。渡し方は 2 つある。
- static credentials
- IAM ユーザーのアクセスキーを、workspace の sensitive な Environment variable に置く
- 設定が簡単で、ほとんどの provider で使える
- アクセスキーを定期的に入れ替える必要があり、漏れたときの影響範囲が大きい
- Dynamic provider credentials
- アクセスキーを置かない。run のたびに HCP Terraform が AWS に身元を証明し、一時的な認証情報を受け取る
- 公式ドキュメントが推奨する形
この記事は static credentials で進める。workspace の変数に何を置くと run の中で何が起きるかを、変数の種類ごとに見るため。
Dynamic provider credentials は、この記事の変数を消して置き換える形で別の記事に書く。
AWS に run 用の IAM ユーザーを作る
run が AWS に入るための身元を、AWS のコンソールで作る。作るものは 3 つ。
- IAM ポリシー
tfc-study: 名前がtfc-study-で始まる S3 バケットに対して、すべての操作を許可する - IAM ユーザー
tfc-study: run が名乗るユーザー。1. のポリシーを付ける - アクセスキー: 2. のユーザーの鍵。HCP Terraform に置く
IAM ポリシー
IAM の ポリシー → ポリシーの作成 で、JSON タブに次を貼り、ポリシー名を tfc-study にして作成する。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "StudyBuckets",
"Effect": "Allow",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::tfc-study-*",
"arn:aws:s3:::tfc-study-*/*"
]
}
]
}
- Action
s3:*は S3 のすべての操作
- Resource
- 操作してよい相手。ARN(AWS の中のものを一意に指す文字列)で書く
arn:aws:s3:::tfc-study-*は、名前がtfc-study-で始まるバケットそのものarn:aws:s3:::tfc-study-*/*は、その中のオブジェクト
ポリシーに書いていない操作は拒否される。ほかのサービスや、ほかの名前のバケットには何もできない。
IAM ユーザーとアクセスキー
IAM ユーザー tfc-study を、コンソールへのアクセスなしで作り、ポリシー tfc-study を直接アタッチする。そのユーザーのアクセスキーを、ユースケース「サードパーティーサービス」で作る。
アクセスキーとシークレットアクセスキーの 2 つは、.tf にもローカルの ~/.aws にも書かない。置く場所は workspace の変数だけ。
S3 バケットと変数の定義
Terraform に作らせる S3 バケットと、workspace から値を受け取る変数を .tf に定義する。ファイルは役割ごとに 5 つに分ける。Terraform は同じディレクトリの .tf をすべて読むので、分け方は人が読みやすいように決めてよい。
terraform {
cloud {
organization = "ritolabo"
workspaces {
name = "tfc-study-stg"
}
}
required_providers {
random = {
source = "hashicorp/random"
version = "~> 3.9"
}
aws = {
source = "hashicorp/aws"
version = "~> 6.0"
}
}
}
cloud ブロックは、すでにある workspace tfc-study-stg を指す。required_providers に hashicorp/aws を追加した。
provider "aws" {
region = "ap-northeast-1"
}
AWS provider には、操作するリージョンだけを書く。認証情報は書かない。
AWS provider は、設定に認証情報がなければ、環境変数 AWS_ACCESS_KEY_ID と AWS_SECRET_ACCESS_KEY を探す(認証情報を探す順番では環境変数が最初)。
variable "environment" {
description = "環境の名前。S3 バケットの名前と Environment タグに使う"
type = string
}
variable "owner" {
description = "Owner タグの値。バケットを管理している人"
type = string
}
変数は 2 つ。どちらも default がないので、値がどこからも来なければ terraform plan はエラーで止まる。値は workspace の Terraform variable として置く。
resource "random_pet" "name" {
length = 2
}
resource "aws_s3_bucket" "study" {
bucket = "tfc-study-${var.environment}-${random_pet.name.id}"
tags = {
Environment = var.environment
Owner = var.owner
}
}
data "aws_caller_identity" "current" {}
- random_pet.name
- state にあるものと同じ定義。plan に差分は出ない
- aws_s3_bucket.study
- この記事で作るもの。名前は
tfc-study-<environment>-<random_pet の名前> tagsはバケットに付けるキー = 値のメモ。値は 2 つとも変数から来る
- この記事で作るもの。名前は
- data.aws_caller_identity.current
- AWS に「今、誰として入っているか」を問い合わせる。何も作らない
- run がどの身元で AWS に入ったかを出力で確かめるために置く
output "pet_name" {
value = random_pet.name.id
}
output "bucket_name" {
value = aws_s3_bucket.study.bucket
}
output "caller_arn" {
description = "run が AWS に入ったときの身元(誰として API を呼んだか)"
value = data.aws_caller_identity.current.arn
}
アクセスキーを sensitive な Environment variable として置く
HCP Terraform の画面(ブラウザで https://app.terraform.io を開き、workspace tfc-study-stg を開いた状態)で、左のメニューの Variables を開く。
Workspace variables の + Add variable で、次の 2 つを追加する。
| Key | Value | 種類 | Sensitive |
|---|---|---|---|
AWS_ACCESS_KEY_ID | アクセスキー | Environment variable | 付ける |
AWS_SECRET_ACCESS_KEY | シークレットアクセスキー | Environment variable | 付ける |
保存すると、一覧の Value は Sensitive - write only になり、自分でも値を読めない。

この変数は、run の実行環境で terraform が動く前に、shell の export で環境変数になる。run の中では、次を実行してから terraform plan を実行したのと同じ状態になる。
$ export AWS_ACCESS_KEY_ID="(アクセスキーの値)"
$ export AWS_SECRET_ACCESS_KEY="(シークレットアクセスキーの値)"
AWS provider はこの環境変数を読んで AWS の API を呼ぶ。.tf には何も書かなくてよい。
Sensitive にすると変わること
- 値が書き込み専用になる。画面でも API でも誰も読めない。新しい値での上書きはできる
- Key や Description のような値以外の属性は変えられない。変えたいときは削除して作り直す
- Sensitive でもそうでなくても、HCP Terraform は変数の値を暗号化して保存する。Sensitive は保存後に読めるかどうかの違い
- Description は暗号化されず、平文で保存される。秘密を Description に書かない
認証情報を Environment variable に置く理由
Terraform variable に置いて、provider "aws" の access_key に var.〜 で渡すこともできる。それでも公式ドキュメントが Environment variable を推奨する理由は次のとおり。
- Terraform variable の値は
terraformに全文が渡る。outputやリソースの引数に使うと、state やログに残ることがある - Environment variable の値は state に入らない
Sensitive の印が守るのは、Variables の画面と API から値を読まれること。state からは守らない。
Terraform variable を Sensitive にしても、その値を使ったリソースの属性は state に平文で入る。
Terraform variable がないと plan は失敗する
.tf を置いたディレクトリで terraform init を実行する。workspace はすでにあるので新しく作られず、provider が 2 つローカルに入る。
$ terraform init
Initializing HCP Terraform...
Initializing provider plugins...
- Finding hashicorp/aws versions matching "~> 6.0"...
- Finding hashicorp/random versions matching "~> 3.9"...
- Installing hashicorp/aws v6.72.0...
- Installed hashicorp/aws v6.72.0 (signed by HashiCorp)
- Installing hashicorp/random v3.9.1...
- Installed hashicorp/random v3.9.1 (signed by HashiCorp)
Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.
HCP Terraform has been successfully initialized!
この時点で workspace には Environment variable しかない。variables.tf の 2 つの変数に値がない状態で terraform plan を実行する。
$ terraform plan
Running plan in HCP Terraform. Output will stream here. Pressing Ctrl-C
will stop streaming the logs, but will not stop the plan running remotely.
Preparing the remote plan...
To view this run in a browser, visit:
https://app.terraform.io/app/ritolabo/tfc-study-stg/runs/run-k9s2yFhX1WcyhBet
Waiting for the plan to start...
Terraform v1.13.3
on linux_amd64
Initializing plugins and modules...
╷
│ Error: No value for required variable
│
│ on variables.tf line 1:
│ 1: variable "environment" {
│
│ The root module input 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.
╵
╷
│ Error: No value for required variable
│
│ on variables.tf line 6:
│ 6: variable "owner" {
│
│ The root module input variable "owner" is not set, and has no default
│ value. Use a -var or -var-file command line argument to provide a value for
│ this variable.
╵
Operation failed: failed running terraform plan (exit 1)
エラーを出したのは、ローカルの terraform コマンドではなく、run の実行環境の terraform。
- ローカルの
terraformコマンドは、変数が足りないことを確かめずに.tf一式を送る - 実行環境の
terraformが.tfを読んだ時点で、defaultのない変数に値がないことに気づく
run は Plan errored の状態で終わり、Runs の一覧に赤く残る。run のページを開くと、Diagnostics の欄(terraform が出したエラーと警告の一覧)に、ターミナルと同じ 2 つのエラーが出ている。

エラーの文面は、-var か -var-file で値を渡すよう言っている。ローカルの terraform plan -var="environment=stg" のように渡した値は、その run だけの変数として HCP Terraform に送られる。
ただし、run を GitHub への push で始める形にすると、ローカルの terraform コマンドは使わなくなる。そのときに値を置ける場所は workspace の変数だけなので、この記事も workspace に置く。
Terraform variable を置いて plan を通す
Variables の + Add variable で、Terraform variable を 2 つ追加する。Sensitive と HCL のチェックは付けない(HCL は list や map のような値を書くときに付ける)。
| Key | Value | 種類 |
|---|---|---|
environment | stg | Terraform variable |
owner | rio | Terraform variable |
一覧は 4 行になる。Category の列で、Environment variable は env、Terraform variable は terraform と見分けられる。

同じ .tf のまま、もう一度 terraform plan を実行する。
$ terraform plan
Running plan in HCP Terraform. Output will stream here. Pressing Ctrl-C
will stop streaming the logs, but will not stop the plan running remotely.
Preparing the remote plan...
To view this run in a browser, visit:
https://app.terraform.io/app/ritolabo/tfc-study-stg/runs/run-FBYcbQF8LqPM8Pxz
Waiting for the plan to start...
Terraform v1.13.3
on linux_amd64
Initializing plugins and modules...
random_pet.name: Refreshing state... [id=national-starfish]
data.aws_caller_identity.current: Refreshing...
data.aws_caller_identity.current: Refresh complete after 1s [id=123456789012]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# aws_s3_bucket.study will be created
+ resource "aws_s3_bucket" "study" {
+ acceleration_status = (known after apply)
+ acl = (known after apply)
+ arn = (known after apply)
+ bucket = "tfc-study-stg-national-starfish"
+ bucket_domain_name = (known after apply)
+ bucket_namespace = (known after apply)
+ bucket_prefix = (known after apply)
+ bucket_region = (known after apply)
+ bucket_regional_domain_name = (known after apply)
+ force_destroy = false
+ hosted_zone_id = (known after apply)
+ id = (known after apply)
+ object_lock_enabled = (known after apply)
+ policy = (known after apply)
+ region = "ap-northeast-1"
+ request_payer = (known after apply)
+ tags = {
+ "Environment" = "stg"
+ "Owner" = "rio"
}
+ tags_all = {
+ "Environment" = "stg"
+ "Owner" = "rio"
}
+ website_domain = (known after apply)
+ website_endpoint = (known after apply)
+ cors_rule (known after apply)
+ grant (known after apply)
+ lifecycle_rule (known after apply)
+ logging (known after apply)
+ object_lock_configuration (known after apply)
+ replication_configuration (known after apply)
+ server_side_encryption_configuration (known after apply)
+ versioning (known after apply)
+ website (known after apply)
}
Plan: 1 to add, 0 to change, 0 to destroy.
Changes to Outputs:
+ bucket_name = "tfc-study-stg-national-starfish"
+ caller_arn = "arn:aws:iam::123456789012:user/tfc-study"
このとき run の実行環境で起きたことは 4 つ。
sequenceDiagram
participant tf as terraform コマンド<br>(ローカル)
participant hcp as HCP Terraform
participant vm as run の実行環境<br>(Linux の仮想マシン)
participant aws as AWS
tf->>hcp: .tf 一式を送る(configuration version)
hcp->>vm: Environment variable を export する<br>AWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEY
hcp->>vm: Terraform variable を渡す<br>environment = stg、owner = rio
vm->>vm: terraform plan を実行する
vm->>aws: AWS provider が環境変数の鍵で<br>「今、誰か」を問い合わせる(aws_caller_identity)
aws-->>vm: user/tfc-study
vm-->>tf: plan のログ
- HCP Terraform が、workspace の Environment variable を実行環境の shell に
exportする - HCP Terraform が、workspace の Terraform variable を
terraformに渡す。.tfのvar.environmentとvar.ownerに値が入る - AWS provider が環境変数の鍵を使い、
data "aws_caller_identity"の問い合わせを AWS に送る。Refresh complete ... [id=123456789012]の行がそれ。plan の時点で認証が通っている bucketがtfc-study-stg-national-starfishになる。var.environmentのstgとrandom_petの名前をつないだ値。tagsの 2 つの値も Terraform variable から来ている
apply で S3 バケットができる
terraform apply を実行する。plan の内容は上と同じなので、承認待ちから先を示す。
$ terraform apply
(plan の内容)
Plan: 1 to add, 0 to change, 0 to destroy.
Changes to Outputs:
+ bucket_name = "tfc-study-stg-national-starfish"
+ caller_arn = "arn:aws:iam::123456789012:user/tfc-study"
Do you want to perform these actions in workspace "tfc-study-stg"?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
aws_s3_bucket.study: Creating...
aws_s3_bucket.study: Creation complete after 5s [id=tfc-study-stg-national-starfish]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Outputs:
bucket_name = "tfc-study-stg-national-starfish"
caller_arn = "arn:aws:iam::123456789012:user/tfc-study"
pet_name = "national-starfish"
caller_arn が user/tfc-study になっている。run は、コンソールにログインしている自分のユーザーではなく、IAM ユーザー tfc-study として AWS に入っていた。
この身元は、workspace の Environment variable に置いた鍵で決まっている。
ローカルの aws コマンドで、バケットができたことを確かめる。
$ aws s3 ls | grep tfc-study
2026-11-08 11:31:53 tfc-study-stg-national-starfish
$ aws s3api get-bucket-tagging --bucket tfc-study-stg-national-starfish
{
"TagSet": [
{
"Key": "Environment",
"Value": "stg"
},
{
"Key": "Owner",
"Value": "rio"
}
]
}
AWS のコンソールの S3 でも、バケットのプロパティに ARN とリージョン、タグに 2 つの値が見える。

HCP Terraform の workspace は、Resources が 3 になる。state に入っているものの数で、内訳は次の 3 つ。
random_pet.nameaws_s3_bucket.studydata.aws_caller_identity.current
data ブロックの結果も state に入るので、この数に含まれる。無料プランの上限 500 の対象になる managed resources(organization の Usage に出る数)は data を含まず、2 になる。
変数の値を変えて run し直す
.tf を変えずに、workspace の Terraform variable の値を変えるだけで、バケットのタグを変える。
Variables で owner の行の … → Edit を開き、Value を rio から ritolabo に変えて保存する。
この時点では、バケットのタグはまだ rio のまま。変数は次の run が読む値で、保存しただけでは run は始まらない。
$ aws s3api get-bucket-tagging --bucket tfc-study-stg-national-starfish --query "TagSet[?Key=='Owner'].Value" --output text
rio
ローカルの .tf は変わっていないので、terraform apply を実行し直す理由はない。画面の + New run → Plan and apply (standard) → Start run で run を始める。
公式ドキュメントも、変数の値を変えたが構成は変わっていないときの手動の run を、この用途として挙げている。
plan の結果は aws_s3_bucket.study の 1 to change。
tagsとtags_allのOwnerが"rio" → "ritolabo"に変わる- ほかの属性は変わらない
- 作り直しではなく、その場で属性だけを変える update in-place の変更
plan が終わると承認待ちで止まるので、Confirm & Apply を押す。

Applied になったあとにタグを見ると、値が変わっている。
$ aws s3api get-bucket-tagging --bucket tfc-study-stg-national-starfish --query "TagSet[?Key=='Owner'].Value" --output text
ritolabo
この run の前後で変わったものと変わっていないものを分ける。
- configuration version
- apply のときに送ったもののまま
- state
- apply の結果のまま
- var.owner に入る値
rioからritolaboに変わった
変わったのは workspace の変数 1 つだけで、それで plan に差分が出た。
同じ .tf を複数の workspace で使い、変数だけを変えて環境ごとに違う結果にする形は、この延長にある。
もし environment のほうを変えていたら、バケットの名前が変わる。名前は後から変えられない属性なので、plan は消して作り直す -/+ destroy and then create replacement になる。
全体像
この記事で置いた 4 つの変数が、run の中でどう扱われたかを整理する。表の内容はこの 4 つについてのもので、種類の一般的な決まりではない。
AWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEY(Environment variable) | environment、owner(Terraform variable) | |
|---|---|---|
| run の中でどうなったか | terraform を動かす前に、shell の環境変数として export された | terraform が variable ブロックの値として受け取った |
| 値を読んだプログラム | AWS provider。環境変数から認証情報を探した | terraform。.tf の var.environment と var.owner に入れた |
.tf に書いたもの | なし | variable ブロックの宣言。値は書いていない |
| 足りないとどうなるか | AWS provider が認証情報を見つけられず、plan が失敗する | terraform が No value for required variable のエラーを出し、plan が失敗する |
| Sensitive | 付けた。値は誰も読めない | 付けていない。値は画面で見える |
| state に入ったか | 入っていない | 使った先の属性(bucket、tags)として入った |
Environment variable を読むのは AWS provider に限らない。run の実行環境の環境変数なので、ほかの provider も terraform 自体も読める(TF_LOG を置けば terraform がログの量を変える)。この記事では、環境変数を読む側に AWS provider しかいなかった。
apply のときに動いたものは次のとおり。
flowchart LR
me[自分]
tfcmd[["terraform コマンド"]]
subgraph hcp[HCP Terraform]
subgraph ws["workspace: tfc-study-stg"]
envvars["Environment variables<br>AWS_ACCESS_KEY_ID(Sensitive)<br>AWS_SECRET_ACCESS_KEY(Sensitive)"]
tfvars["Terraform variables<br>environment = stg<br>owner = rio"]
cv["configuration version<br>(送った .tf の写し)"]
state[state]
end
subgraph vm["run の実行環境(Linux)"]
terraform[["terraform v1.13.3"]]
provider[["AWS provider"]]
end
end
subgraph aws[AWS]
user["IAM ユーザー tfc-study<br>+ ポリシー tfc-study"]
bucket[("S3 バケット<br>tfc-study-stg-national-starfish")]
end
me -- "apply を実行" --> tfcmd
me -- "変数を置く・変える" --> tfvars
tfcmd -- ".tf 一式を送る" --> cv
cv -- "読む" --> terraform
tfvars -- "var.〜 として渡る" --> terraform
envvars -- "export される" --> provider
terraform -- "aws_s3_bucket を作る指示" --> provider
provider -- "tfc-study として API を呼ぶ" --> user
user -- "許可の範囲で作る" --> bucket
terraform -- "読む・書く" --> state
Environment variable は AWS provider に、Terraform variable は terraform に渡る。どちらもローカルの .tf には書かれておらず、HCP Terraform の中にだけある。
まとめ
- workspace の変数には Environment variable と Terraform variable の 2 種類がある
- Environment variable は run の実行環境に
exportされ、provider が読む - Terraform variable は
.tfのvariableブロックの値としてterraformが受け取る
- Environment variable は run の実行環境に
- AWS provider の認証情報は、IAM ユーザーのアクセスキーを Sensitive な Environment variable に置けば、
.tfに何も書かずに渡せる。run はこの鍵の持ち主として AWS に入る - Sensitive にした値は画面でも API でも読めなくなる。守られるのは Variables の画面と API からで、state からではない
defaultのない変数に値がないと、run の実行環境のterraformが No value for required variable を出し、run は Plan errored で終わる- 画面で Terraform variable の値を変えても、run を始めるまでは何も変わらない。
.tfが同じでも、変数が変われば plan に差分が出る - workspace の Resources の数は
dataブロックの結果も含む。無料プランの上限の対象になる managed resources はdataを含まない
参考 URL
- [official] Variables in HCP Terraform | Terraform | HashiCorp Developer
- [official] Manage variables and variable sets in HCP Terraform | Terraform | HashiCorp Developer
- [official] HCP Terraform's run environment | Terraform | HashiCorp Developer
- [official] The CLI-driven remote run workflow for HCP Terraform | Terraform | HashiCorp Developer
- [official] UI and VCS-driven run workflow in HCP Terraform | Terraform | HashiCorp Developer
- [official] Run states and stages in HCP Terraform | Terraform | HashiCorp Developer
- [official] Manage workspace state in HCP Terraform | Terraform | HashiCorp Developer
- [official] Dynamic provider credentials in HCP Terraform | Terraform | HashiCorp Developer
- [official] Use input variables to add module arguments | Terraform | HashiCorp Developer
- [official] Docs overview | hashicorp/aws | Terraform Registry
- [official] aws_s3_bucket | Resources | hashicorp/aws | Terraform Registry
- [official] aws_caller_identity | Data Sources | hashicorp/aws | Terraform Registry
- [official] Create IAM policies (console) - AWS Identity and Access Management
- [official] Manage access keys for IAM users - AWS Identity and Access Management
- [official] General purpose bucket naming rules - Amazon Simple Storage Service