読者です 読者をやめる 読者になる 読者になる

ようへいの日々精進XP

よかろうもん

terraform 独りハンズオン(3)〜 tfstate ファイルを S3 で管理する 〜

はじめに

terraform.tfstate とは

Terraform で管理しているインフラの状態を管理しているファイルで中身は以下のような JSON ファイルとなっている。

{
    "version": 1,
    "serial": 1,
    "modules": [
        {
            "path": [
                "root"
            ],
            "outputs": {},
            "resources": {
                "aws_s3_bucket.b": {
                    "type": "aws_s3_bucket",
                    "primary": {
                        "id": "foo-bucket",
                        "attributes": {
                            "acl": "private",
                            "bucket": "foo-bucket",
                            "force_destroy": "true",
                            "hosted_zone_id": "xxxxxxxxxxxxxxxxxxxxxxx",
                            "id": "foo-bucket",
                            "policy": "",
                            "region": "ap-northeast-1",
                            "tags.#": "0",
                            "website.#": "0",
                            "website_endpoint": ""
                        }
                    }
                }
            }
        }
    ]
}

上記は S3 Bucket を terraform apply で作成した後の terraform.tfstate ファイル(以降 tfstate ファイル)。デフォルトでは tf ファイルと同じディレクトリに生成されるが terraform remote コマンドを利用することで、Atlas や Consul 等に保存することが出来る。尚、terraform 0.5.0 からは Amazon S3 に保存することも出来るようになっているので、今回は Amazon S3 への保存を試してみたいと思う。

参考

stormcat.hatenablog.com

tfstate ファイルの運用管理について参考にさせて頂いた。

qiita.com

tfstate ファイルの S3 への保存について参考にさせて頂いた。

terraform のバージョン

利用する terraform のバージョンは以下の通り。

% terraform -version
Terraform v0.6.1

試す

教材

tfstate ファイルを保存する S3 バケットを作成する tf ファイルを利用する。

github.com

やること

  • tfstate ファイル保存先の S3 バケットを作成する
  • tfstate ファイルの保存先を S3 に設定する
  • tfstate ファイルを S3 バケットに保存して確認する
  • tfstate ファイルの保存先を S3 からデフォルトに戻す
  • tfstate ファイルを保存していた S3 バケットを削除する

tfstate ファイルを S3 で扱う IAM ユーザーの作成

tfstate ファイルを S3 で扱う為の IAM ユーザーを作成する(これも terraform でやって良かったな...と反省)。また、tfstate ファイルを保存する為のバケット名を指定して、以下のような inline policy を適用する。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "s3:*",
            "Resource": [
                "arn:aws:s3:::${your_bucket_name}",
                "arn:aws:s3:::${your_bucket_name}/*"
            ]
        }
    ]
}

作成したユーザーのアクセスキーとシークレットアクセスキーを取得しておく。

terraform plan

% terraform plan \
-var "access_key=AKzzzzzzzzzzzzzzzzzzz" \
-var "secret_key=zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" \
-var "s3_bucket_name=${your_key_name}"

以下のように出力される。

(略)

+ aws_s3_bucket.b
    acl:              "" => "private"
    bucket:           "" => "oreno-terraform-state-files"
    force_destroy:    "" => "1"
    hosted_zone_id:   "" => "<computed>"
    region:           "" => "<computed>"
    website_endpoint: "" => "<computed>"


Plan: 1 to add, 0 to change, 0 to destroy.

同じディレクトリに terraform.tfstate ファイルが作成されているので、念のため確認すると以下のような状態になっている。

{
    "version": 1,
    "serial": 0,
    "modules": [
        {
            "path": [
                "root"
            ],
            "outputs": {},
            "resources": {}
        }
    ]
}

terraform apply で S3 バケットを作成

% terraform apply \
-var "access_key=AKzzzzzzzzzzzzzzzzzzz" \
-var "secret_key=zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" \
-var "s3_bucket_name=${your_key_name}"

以下のように出力される。

aws_s3_bucket.b: Creating...
  acl:              "" => "private"
  bucket:           "" => "oreno-terraform-state-files"
  force_destroy:    "" => "1"
  hosted_zone_id:   "" => "<computed>"
  region:           "" => "<computed>"
  website_endpoint: "" => "<computed>"
aws_s3_bucket.b: Creation complete

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

terraform.tfstate ファイルは以下のように更新されている。

{
    "version": 1,
    "serial": 1,
    "modules": [
        {
            "path": [
                "root"
            ],
            "outputs": {},
            "resources": {
                "aws_s3_bucket.b": {
                    "type": "aws_s3_bucket",
                    "primary": {
                        "id": "${your_bucket_name}",
                        "attributes": {
                            "acl": "private",
                            "bucket": "${your_bucket_name}",
                            "force_destroy": "true",
                            "hosted_zone_id": "XXXXXXXXXXXXXXXXX",
                            "id": "${your_bucket_name}",
                            "policy": "",
                            "region": "ap-northeast-1",
                            "tags.#": "0",
                            "website.#": "0",
                            "website_endpoint": ""
                        }
                    }
                }
            }
        }
    ]
}

更に terraform.tfstate.backup ファイルが作成されているので確認してみる。

{
    "version": 1,
    "serial": 0,
    "modules": [
        {
            "path": [
                "root"
            ],
            "outputs": {},
            "resources": {}
        }
    ]
}

先ほどの terraform plan 実行時に出力された結果が保存されている。

バケットが作成されているかを念のために確認。

% aws s3api list-buckets --output text | grep ${your_bucket_name}
BUCKETS 2015-07-26T04:17:42.000Z        ${your_bucket_name}

terraform remote config で tfstate ファイルの保存先を S3 バケットに設定する

作成した S3 バケットを tfstate ファイルの保存先として設定する為の設定を行う。

% terraform remote config \
-backend=S3 \
-backend-config="access_key=AKxxxxxxxxxxxxxxxxxxx" \
-backend-config="secret_key=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-backend-config="region=ap-northeast-1" \
-backend-config="bucket=${your_bucket_name}" \
-backend-config="key=${your_key_name}"

bucket= には S3 バケット名を指定、key= にはオブジェクトのキーを指定する。

実行後に以下のように出力される。

Remote configuration updated
Remote state configured and pulled.

terraform remote config 実行後に terraform.tfstate ファイルは .terraform 以下に移動されている。

% ls -l .terraform
total 8
-rw-r--r--  1 kappa  staff  1348 Jul 26 13:34 terraform.tfstate

中身を確認する。

% cat .terraform/terraform.tfstate 
{
    "version": 1,
    "serial": 1,
    "remote": {
        "type": "s3",
        "config": {
            "access_key": "AKxxxxxxxxxxxxxxxxxxx",
            "bucket": "${your_bucket_name}",
            "key": "oreno_tf_s3",
            "region": "ap-northeast-1",
            "secret_key": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
        }
    },
    "modules": [
        {
            "path": [
                "root"
            ],
            "outputs": {},
            "resources": {
                "aws_s3_bucket.b": {
                    "type": "aws_s3_bucket",
                    "primary": {
                        "id": "${your_bucket_name}",
                        "attributes": {
                            "acl": "private",
                            "bucket": "${your_bucket_name}",
                            "force_destroy": "true",
                            "hosted_zone_id": "XXXXXXXXXXXXXXXXX",
                            "id": "${your_bucket_name}",
                            "policy": "",
                            "region": "ap-northeast-1",
                            "tags.#": "0",
                            "website.#": "0",
                            "website_endpoint": ""
                        }
                    }
                }
            }
        }
    ]
}

注意点として terraform remote config を実行した際に利用したアクセスキーとシークレットアクセスキーが平文で JSON ファイルに保存されている点。

terraform remote push で S3 に保存

terraform remote push を実行して terraform.tfstate を S3 バケットに保存する。

% terraform remote push

以下のように出力される。

State successfully pushed!

バケットに保存されているか確認する。

% aws s3 ls s3://${your_bucket_name}/ --recursive
2015-07-26 13:41:16       1348 oreno_tf_s3

tfstate ファイルの保存先をデフォルトに戻す

tfstate ファイルの保存先を S3 からデフォルト(tf ファイルと同じディレクトリ)に戻す場合には以下のように実行する。

% terraform remote config -disable

実行後は tfstate ファイルが tf ファイルと同じディレクトリに作成されているので中身を確認する。

{
    "version": 1,
    "serial": 1,
    "modules": [
        {
            "path": [
                "root"
            ],
            "outputs": {},
            "resources": {
                "aws_s3_bucket.b": {
                    "type": "aws_s3_bucket",
                    "primary": {
                        "id": "${your_bucket_name}",
                        "attributes": {
                            "acl": "private",
                            "bucket": "${your_bucket_name}",
                            "force_destroy": "true",
                            "hosted_zone_id": "Z2M4EHUR26P7ZW",
                            "id": "${your_bucket_name}",
                            "policy": "",
                            "region": "ap-northeast-1",
                            "tags.#": "0",
                            "website.#": "0",
                            "website_endpoint": ""
                        }
                    }
                }
            }
        }
    ]
}

S3 を指定していた際には記載されていたアクセスキー等の情報が削除されている。

S3 バケットを削除する

terraform destroy \
-var "access_key=AKzzzzzzzzzzzzzzzzzzz" \
-var "secret_key=zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" \
-var "s3_bucket_name=${your_key_name}"

以下のように出力されるので yes を入力してリソース(S3)を削除する。

Do you really want to destroy?
  Terraform will delete all your managed infrastructure.
  There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value: yes

aws_s3_bucket.b: Refreshing state... (ID: ${your_key_name})
aws_s3_bucket.b: Destroying...
aws_s3_bucket.b: Destruction complete

Apply complete! Resources: 0 added, 0 changed, 1 destroyed.

といふことで

メリデメ

tfstate ファイルを S3 に管理する方法を試してみた。参考にさせて頂いた Blog 記事等にも記載されているが、

  • tfstate ファイル S3 に保存出来ることで CI ツールとの連携
  • IAM を設定して公開範囲を限定出来る

等のメリットは大きいとと思うが、tfstate ファイルにアクセスキーやシークレットアクセスキーが平文で保存されている点については .gitignore には .terraform を含める等の注意が必要だと感じた。