
Terraform v1.10.0 で導入された S3 Backend の use_lockfile オプションを使うと,Amazon S3 バケットで tfstate を管理しつつ,Amazon S3 のネイティブ機能 (conditional writes) でステートロックも実現できる👌今までステートロックのために使っていた Amazon DynamoDB が不要になるというメリットがある❗️
Terraform v1.10.0 は2024年11月にリリースされて,use_lockfile オプション自体もまだ Experimental(実験的)ではあるけど,既に use_lockfile オプションが Experimental ではなくなって,今度は逆に Amazon DynamoDB を使ったステートロックの仕組みが Deprecated(非推奨)になるというプルリクエストが merge されている📝
そして Terraform v1.11.0-beta1 のリリースノートに S3 native state locking is now generally available. と載っていた.よって,Terraform v1.11.0 からは use_lockfile オプションを使うことが一般的になりそう.
最近 S3 Backend の use_lockfile オプションを紹介する機会があって,デモ環境を作ったため,簡単にまとめておこうと思う👌
use_lockfile オプションを試す
Amazon S3 バケットを作る
まず,tfstate を管理する Amazon S3 バケットを作る🗑️
$ aws s3api create-bucket \ --bucket kakakakakku-sandbox-tfstates \ --create-bucket-configuration LocationConstraint=ap-northeast-1 $ aws s3api put-bucket-versioning \ --bucket kakakakakku-sandbox-tfstates \ --versioning-configuration Status=Enabled
ドキュメントに警告として載っている通り,Amazon S3 バケットのバージョニング機能も有効化しておく👌あと今回は割愛しているけど,ライフサイクルポリシーで古くなったバージョニングを消しておくと良いと思う.
Warning! It is highly recommended that you enable Bucket Versioning on the S3 bucket to allow for state recovery in the case of accidental deletions and human error.
👾 backend.tf
backend.tf は以下のようにした.ポイントは S3 Backend で use_lockfile = true を設定しているところ💡
terraform { backend "s3" { region = "ap-northeast-1" bucket = "kakakakakku-sandbox-tfstates" key = "terraform.tfstate" use_lockfile = true } }
👾 main.tf
main.tf は何でも良くて,今回は Amazon CloudWatch ロググループを作る📝
resource "aws_cloudwatch_log_group" "main" { name = "sandbox-use-lockfile" }
init / plan / apply(1回目)
普段通りに init / plan を実行する.
$ terraform init Initializing the backend... Successfully configured the backend "s3"! Terraform will automatically use this backend unless the backend configuration changes. (中略) Terraform has been successfully initialized! $ terraform plan (中略) Plan: 1 to add, 0 to change, 0 to destroy.
そして次に apply を実行して Enter a value を入力するときにステートロックを確認できる👌
$ terraform apply (中略) Plan: 1 to add, 0 to change, 0 to destroy. Do you want to perform these actions? Terraform will perform the actions described above. Only 'yes' will be accepted to approve. Enter a value:
yes を入力せず Amazon S3 バケットを確認すると terraform.tfstate.tflock ファイルが保存されていた❗️(正確には plan 実行時にも terraform.tfstate.tflock ファイルは作られていて,バージョニングを確認するとわかる)

最後に yes を入力して apply を完了しておく.
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
すると今度は Amazon S3 バケットに terraform.tfstate ファイルが保存されて,terraform.tfstate.tflock ファイルは削除されていた👌

plan / apply(2回目)
今度はステートロックを確認する.Amazon CloudWatch Logs に retention_in_days = 7 を追加する.
resource "aws_cloudwatch_log_group" "main" { name = "sandbox-use-lockfile" retention_in_days = 7 }
同じく plan / apply を実行して,Enter a value で止めておく🛑
$ terraform plan Plan: 0 to add, 1 to change, 0 to destroy. $ terraform apply (中略) Plan: 0 to add, 1 to change, 0 to destroy. Do you want to perform these actions? Terraform will perform the actions described above. Only 'yes' will be accepted to approve. Enter a value:

そして別のターミナルから plan を実行すると Error: Error acquiring the state lock というエラーが出てステートロックを確認できた👏 期待通り〜 \( 'ω')/
$ terraform plan ╷ │ Error: Error acquiring the state lock │ │ Error message: operation error S3: PutObject, https response error StatusCode: 412, RequestID: AXHQ4SP42S1DE2XQ, HostID: Xf6uqAz2Oo+c2+QXKdrL0tzBi6GKHVFjCg6gz/WH9HXt/yZEm9f+82en89RHGRZzoWvQSxjCifQ=, api error PreconditionFailed: At least one of the │ pre-conditions you specified did not hold │ Lock Info: │ ID: 41f52805-3896-a963-70e3-a4bc02de9ea8 │ Path: kakakakakku-sandbox-tfstates/terraform.tfstate │ Operation: OperationTypeApply │ Who: kakakakakku@MacBookAir.local │ Version: 1.10.3 │ Created: 2025-01-12 01:48:20.240611 +0000 UTC │ Info: │ │ │ Terraform acquires a state lock to protect the state from being written │ by multiple users at the same time. Please resolve the issue above and try │ again. For most commands, you can disable locking with the "-lock=false" │ flag, but this is not recommended. ╵
さらにエラーを確認すると「ステータスコード 412」と「メッセージ PreconditionFailed」になっていて,Amazon S3 の conditional writes(条件付き書き込み)のレスポンスになっていることもわかる.
Amazon DynamoDB を使わずにステートロックを実現できた❗️
仕組み
ステートロックの仕組みとしては,2024年8月にリリースされた Amazon S3 の conditional writes(条件付き書き込み)を使っていて,terraform.tfstate.tflock ファイルを保存するときの条件として --if-none-match を指定している.よって,Amazon S3 バケットに既に terraform.tfstate.tflock ファイルが存在していたら保存に失敗して,競合を検知できるようになっている.
実際の Terraform の実装だと internal/backend/remote-state/s3/client.go が参考になる📝
input := &s3.PutObjectInput{
ContentType: aws.String("application/json"),
Body: bytes.NewReader(lockFileJson),
Bucket: aws.String(c.bucketName),
Key: aws.String(c.lockFilePath),
IfNoneMatch: aws.String("*"),
}
まとめ
Terraform v1.10.0 で導入された S3 Backend の use_lockfile オプションを使うと,ステートロックのために使っていた Amazon DynamoDB が不要になるので覚えておこう👌