はじめに
Terraformを使っていた際に、ローカルやS3でterraform.tfstateを管理する方法に結構つまづきました。
そのため管理する各場所に対し、どんなコードを書いたりコマンドを実行したりするのかまとめました。
つまづきと解決
※管理方法のみを見たい人は、この項目はスキップしてください。
つまづき
いろんなサイトでterraform.tfstateを管理する方法が記載してあったため、それらのサイトを基に、S3のバケットで保存する方法を試していました。その際、紹介されていたコマンド(terrafrom init
)を実行しても、S3のバケット内にterraform.tfstateが作成されませんでした。
なぜだろうと考え、以下2つの視点で原因を探りました。
- 誰かが経験したかもしれないのでGitHub Issueやブログ等を確認する
- ログを見ながらTerraformのソースコードを追いかける
1に関しては、以下のような点に着目した記事がありました。ですが実際に試しても問題は解決しませんでした。
- この記事で書いてあるようなファイルの書き方
- バケットのポリシー設定方法
2に関しては、Terraformの公式ページを基にして、terraformコマンド実行時にTF_LOG=TRACE
をつけてログを確認しました。そのログを使ってTerraformのソースを見始めましたが、Go初心者である私にはざっとした流れしか分かりませんでした。
そのため1、2のどちらの方法でも、解決には至りませんでした。
解決
試行錯誤しながら進めていたとき、S3のバケットをふと見てみると、terraform.tfstateができている事に気づきました。そのためそれまで自分が行った修正やコマンドを振り返りながら、1つずつ切り分けし、ここに記載したパターンに行き着きました。
Goが分かる人であればコードで理解でき、パターンがすぐに分かったと思います。私はまだGoが分からない状態だったので、このような方法で解決に至りました。
環境
- Mac:macOS Catalina Version 10.15.5
- Terraform:v0.12.28
前提
ここではTerraformでVPCを作成する例をあげています。
また、この記事で紹介しているIAMユーザのポリシーやバケットの設定は、記事用としての設定です。実際の環境では、必要に応じたポリシーや設定を行ってください。
IAMユーザを作成する
TerraformでVPCを作成するため、以下の2つを満たしたIAMユーザを作成しておきます。
- AmazonVPCFullAccessのアタッチ
- AWSアクセスの種類に「プログラムによるアクセス・アクセスキーを使用」を設定
IAMユーザ作成時にアクセスキー、シークレットキーが表示されるので、後述するaccess_key、secret_keyに設定します。
ローカルで管理する
ファイルの作成とコマンド実行
以下のようにmain.tfを作成します。
provider "aws" {
region = "ap-northeast-1"
access_key = "xxxxx"
secret_key = "yyyyy"
}
# VPC
resource "aws_vpc" "vpc" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
tags = {
Name = "vpc-test"
}
}
main.tfを作成したら、以下3つのコマンドを実行します。
※ここではterraform apply
時に-auto-approve
オプションをつけていますが、なくても問題ありません。
$ terraform init
$ terraform plan
$ terraform apply -auto-approve
terraform apply
実行後、以下のように出力され、terraform.tfstateがローカルに作成されます。
$ terraform apply -auto-approve
aws_vpc.vpc: Creating...
aws_vpc.vpc: Creation complete after 4s [id=vpc-aaaaa]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
S3で管理する
IAMユーザにバケットアクセスのポリシーを追加する
Terraformで新たにバケットにアクセスするため、IAMユーザにポリシーを追加します。
アタッチするポリシーはAmazonS3FullAccessです。
S3でバケットを作成する
ここではバケット名をbucket-for-tfとして、「次へ」を押します。
「パブリックアクセスをすべてブロック」のチェックを外し、「現在の設定により・・・を了承します。」にチェックをつけます。
確認画面が出るので、「バケットを作成」ボタンを押してバケットを作成します。
S3上に、指定したバケット(buekct-for-tf)が作成されます。
バケットにバケットポリシーを設定する
terraformがterraform.tfstateをS3に配置するためには、バケットポリシーの設定が必要です。terrformの公式ページを参照し、作成したIAMユーザやバケット名を使用して、以下のようにバケットポリシーを設定します。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111111:user/terraform-bucket-manager"
},
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::bucket-for-tf"
},
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::11111:user/terraform-bucket-manager"
},
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::bucket-for-tf/*"
}
]
}

ファイルの作成とコマンド実行
以下のようにmain.tfを作成します。
基本的には「ローカルでterraformを管理する」項目と同じです。異なる点は、backendにs3を指定している点です。
provider "aws" {
region = "ap-northeast-1"
access_key = "xxxxx"
secret_key = "yyyyy"
}
terraform {
backend "s3" {
access_key = "xxxxx"
secret_key = "yyyyy"
bucket = "bucket-for-tf"
region = "ap-northeast-1"
key = "terraform.tfstate"
encrypt = true
}
}
# VPC
resource "aws_vpc" "vpc" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
tags = {
Name = "vpc-test"
}
}
作成したら以下3つのコマンドを実行します。
$ terraform init
$ terraform plan
$ terraform apply
terraform init
を行うと、バックエンドにs3を設定完了のメッセージが表示されます。
$ terraform init
Initializing the backend...
Successfully configured the backend "s3"! Terraform will automatically
use this backend unless the backend configuration changes.
・・・略・・・
terraform apply
まで行うと、main.tfで指定したS3のバケット(bucket-for-tf)にterraform.tfstateが出来上がります。
ローカルでの管理からS3での管理に移す
ローカルでの管理
以下のようにmain.tfを作成します。
基本的には「S3でterraform.tfstateを管理する」項目と同じです。異なる点は、backendにlocalを指定している点です。
provider "aws" {
region = "ap-northeast-1"
access_key = "xxxxx"
secret_key = "yyyyy"
}
terraform {
backend "local" {
path = "terraform.tfstate"
}
}
# VPC
resource "aws_vpc" "vpc" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
tags = {
Name = "vpc-test"
}
}
作成したら以下3つのコマンドを実行します。
$ terraform init
$ terraform plan
$ terraform apply -auto-approve
terraform init
を実行すると、バックエンドにlocalを設定完了のメッセージが表示されます。
$ terraform init
Initializing the backend...
Successfully configured the backend "local"! Terraform will automatically
use this backend unless the backend configuration changes.
・・・略・・・
S3での管理へ変更
terraform apply
完了後、main.tfのbackendをs3に書き換えます。
ここで使用するIAMユーザ、S3のバケットについては、「IAMでユーザを作成する」、「S3でバケットを作成する」を参照してください。
provider "aws" {
region = "ap-northeast-1"
access_key = "xxxxx"
secret_key = "yyyyy"
}
terraform {
backend "s3" {
access_key = "xxxxx"
secret_key = "yyyyy"
bucket = "bucket-for-tf"
region = "ap-northeast-1"
key = "terraform.tfstate"
encrypt = true
}
}
# VPC
resource "aws_vpc" "vpc" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
tags = {
Name = "vpc-test"
}
}
backendを変更したので以下コマンドを再実行します。
$ terraform init
terraform.tfstateをs3にコピーして良いか尋ねられるのでyesを入力します。
$ terraform init
Initializing the backend...
Backend configuration changed!
Terraform has detected that the configuration specified for the backend
has changed. Terraform will now check for existing state in the backends.
Terraform detected that the backend type changed from "local" to "s3".
・・・略・・・
Do you want to copy this state to the new "s3"
backend? Enter "yes" to copy and "no" to start with an empty state.
Enter a value: yes
yesを入力後、バックエンドにs3を設定完了のメッセージが表示されます。
$ terraform init
・・・略・・・
Enter a value: yes
Successfully configured the backend "s3"! Terraform will automatically
use this backend unless the backend configuration changes.
・・・略・・・
このタイミングで、S3のバケットにterraform.tfstateが作成されます。
この後にterraform apply
を実行します。
S3のバケットを見てみると、terraform.tfstateが更新されていることが分かります。
$ terraform apply -auto-approve
aws_vpc.vpc: Refreshing state... [id=vpc-aaaaa]
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

おわりに
ローカルやS3でterraform.tfstateを管理する方法を記載しました。
Goが分かっていればもっと楽に解決できたと思います。
この記事が、未来の私や困っている誰かのお役に立てれば幸いです。
参考資料
terraformのbackendをs3にしてinitするまでの最低限の手順
Backend の S3 や DynamoDB 自体を terraform で管理するセットアップ方法
Terraformをしばらく書いて覚えた個人的なTipsについて
terraformを利用したtfstateファイルのS3へのアップロードができない