Terraform state locking via DynamoDB

TL;DR

Create DynamoDB table.

aws dynamodb create-table \  
    --table-name <Your table name> \
    --attribute-definitions AttributeName=LockID,AttributeType=S \
    --key-schema AttributeName=LockID,KeyType=HASH \
    --provisioned-throughput ReadCapacityUnits=1,WriteCapacityUnits=1

Append lock_table config to backend config.

 terraform {
   backend "s3" {
     bucket     = "<Your bucket name>"
     key        = "<Your key name>"
     region     = "us-east-1"
+    lock_table = "<Your table name>"
   }
 }

Apply backend config.

$ terraform init
...
Do you want to copy the state from "s3"?  
  Would you like to copy the state from your prior backend "s3" to the
  newly configured "s3" backend? If you're reconfiguring the same backend,
  answering "yes" or "no" shouldn't make a difference. Please answer exactly
  "yes" or "no".

  Enter a value: yes
...

Caution!

  • S3 and DynamoDB should be in the same region.

概要

terraformでこのブログに関わるインフラの管理をしています。最近のterraformのアップデートで、stateのlock機能が追加されました。

私はbackendにS3を使っているので、ドキュメントによればDynamoDBと連携させることでstateのlockが可能なはずです。

やってみます。

やりかた

まずDynamoDBのテーブルを作成します。

  • テーブルはLockIDという名前のプライマリキーを持っている必要がある
  • --table-nameは好きに設定する
aws dynamodb create-table \  
    --table-name <Your table name> \
    --attribute-definitions AttributeName=LockID,AttributeType=S \
    --key-schema AttributeName=LockID,KeyType=HASH \
    --provisioned-throughput ReadCapacityUnits=1,WriteCapacityUnits=1

次に既存のbackendの設定にlock_tableの設定を足します。

 terraform {
   backend "s3" {
     bucket     = "<Your bucket name>"
     key        = "<Your key name>"
     region     = "us-east-1"
+    lock_table = "<Your table name>"
   }
 }

backendの設定を変更したら、terraform initし直す必要があります。

  • 既存のstateを引き継ぐか聞かれるので引き継ぎたい場合はyes
$ terraform init
...
Do you want to copy the state from "s3"?  
  Would you like to copy the state from your prior backend "s3" to the
  newly configured "s3" backend? If you're reconfiguring the same backend,
  answering "yes" or "no" shouldn't make a difference. Please answer exactly
  "yes" or "no".

  Enter a value: yes
...

planを実行すると…lockされているようです!!

$ terraform plan
Acquiring state lock. This may take a few moments...  
...
Releasing state lock. This may take a few moments...  

別セッションを開いて同時にplanするとちゃんと失敗します。良い。 ✨

$ terraform plan
Acquiring state lock. This may take a few moments...  
Error locking state: Error acquiring the state lock: ConditionalCheckFailedException: The conditional request failed  
        status code: 400, request id: 9CIPASNL9IG1VGL7LNGUIFQPDFVV4KQNSO5AEMVJF66Q9ASUAAJG
...

注意すること

作成するDynamoDBのテーブルについて

ドキュメントにもありますが、プライマリキーの名前はLockIDにする必要があります。

The table must have a primary key named LockID.

Backend Type: s3 - Terraform by HashiCorp

作成したDynamoDBの概要は以下のコマンドで取得できます。

aws dynamodb describe-table --table-name <Your table name>  

DynamoDBとS3のリージョンを合わせる必要がある

当然といえば当然ですが、S3とDynamoDBのリージョンが同じでないとエラーになります。

  • Requested resource not found(DynamoDBのテーブルが無い)となる
$ terraform plan
Acquiring state lock. This may take a few moments...  
Error locking state: Error acquiring the state lock: 2 error(s) occurred:

* ResourceNotFoundException: Requested resource not found
        status code: 400, request id: H9ENCR4MR29ASAQHIIO3N1EOCJVV4KQNSO5AEMVJF66Q9ASUAAJG
* ResourceNotFoundException: Requested resource not found
        status code: 400, request id: RV75BHGC1F2C45V55V24V76OHRVV4KQNSO5AEMVJF66Q9ASUAAJG

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.  

AWSのアクセスキーのポリシーについて

私はterraform用のIAMを用意してなるべく権限を絞って使うようにしています。今回DynamoDBへのアクセス権限も必要になったので、以下の2つの権限をアクセスキーに付与しています。

  • AmazonS3FullAccess
  • AmazonDynamoDBFullAccess

※ もっと権限絞れるとは思う。

感想

DynamoDBを触ったことがなかったので少し苦戦した。

stateのロックというイカした機能を試せて満足!!個人サーバだけど、CIでtfファイルのテストとか始めたら有用になってくるかも。

参考リンク