コンテンツにスキップ

tfstateファイルの分割@プラクティス集

はじめに

本サイトにつきまして、以下をご認識のほど宜しくお願いいたします。


01. tfstateファイルの分割について


02. ローカル/リモートモジュールのディレクトリ構成

ここから先の話は遊びみたいなもんです👶🏻

ローカルモジュールとリモートモジュールは、”汎用的に使用できるresourceブロックやdataブロックのセット” である。

これらは、モジュールがローカルリポジトリまたはリモートリポジトリのいずれにあるかの違いしかない。

そのため、このディレクトリ構成はローカル / リモートモジュールの両方に適用できる。

ローカルモジュールに関して、これをリポジトリ内で汎用的に使い回すユースケースのみ作成するべきである。

そのためローカルモジュールに関しては、

  • そもそもローカルモジュールを制限使用にする
  • ローカルモジュールは自前リモートモジュール化する
  • 公式リモートモジュールを使用する

のほうが良い。

ローカル/リモートモジュールのディレクトリ構成に関して、tfstateファイルは粒度に関係ないので、これといった目安はないが、参考までに今までに観測したことのある例をあげた。


依存先AWSリソース別

依存先の多いAWSリソースに関して、依存先のAWSリソース別にローカルモジュールを分割する。

依存先AWSリソース別の分割方法は、Terraformの公式リモートモジュールに一番多い構成である。

(例)

ローカルモジュールの場合

ただ前述の通り、ローカルモジュールの使用するかどうかは考え直したほうが良い。

aws-repository/
└── modules/ # ローカルモジュール
    └── eks/ # EKS
        ├── auto_scaling/ # AutoScaling
        │   ├── main.tf
        │   ├── outputs.tf
        │   └── variables.tf
        
        ├── iam/ # IAMロール
        │   ├── main.tf
        │   ├── outputs.tf
        │   └── variables.tf
        
        ├── kubernetes/ # Kubernetesリソース(例:RoleBinding、StorageClass、など)
        │   ├── main.tf
        │   ├── outputs.tf
        │   └── variables.tf
        
        ├── launch_template/ # 起動テンプレート
        │   ├── main.tf
        │   ├── outputs.tf
        │   └── variables.tf
        
        ├── security_group/ # セキュリティグループ
        │   ├── main.tf
        │   ├── outputs.tf
        │   └── variables.tf
        
        └── node_group/ # Nodeグループ
            ├── main.tf
            ├── outputs.tf
            └── variables.tf

(例)

リモートモジュールの場合

aws-remote-repository/
└── eks/ # EKS
    ├── auto_scaling/ # AutoScaling
    │   ├── main.tf
    │   ├── outputs.tf
    │   └── variables.tf
    
    ├── iam/ # IAMロール
    │   ├── main.tf
    │   ├── outputs.tf
    │   └── variables.tf
    
    ├── kubernetes/ # Kubernetesリソース(例:RoleBinding、StorageClass、など)
    │   ├── main.tf
    │   ├── outputs.tf
    │   └── variables.tf
    
    ├── launch_template/ # 起動テンプレート
    │   ├── main.tf
    │   ├── outputs.tf
    │   └── variables.tf
    
    ├── security_group/ # セキュリティグループ
    │   ├── main.tf
    │   ├── outputs.tf
    │   └── variables.tf
    
    └── node_group/ # Nodeグループ
        ├── main.tf
        ├── outputs.tf
        └── variables.tf


リソース別

最上層は、AWSリソースで分割する。

またスクリプトを使用するAWSリソース(例:Lambda)では、そのソースコードをモジュール下で管理する。

(例)

ローカルモジュールの場合

ただ前述の通り、ローカルモジュールの使用するかどうかは考え直したほうが良い。

aws-repository/
└── modules/ # ローカルモジュール
    ├── acm/ # ACM
    ├── alb/ # ALB
    ├── lambda/ # Lambda
    │   ├── foo_function/ # とある関数
    ...

(例)

リモートモジュールの場合

aws-remote-repository/
├── acm/ # ACM
├── alb/ # ALB
├── lambda/ # Lambda
│   ├── foo_function/ # とある関数
    ...


リージョン別

AWSリソースのプロビジョニング先のリージョン別にローカルモジュールを分割する。

例えば、ACMは同じリージョンのAWSリソースにしかアタッチできない制約があるため、AWSリソースによっては複数リージョン必要になる。

(例)

ローカルモジュールの場合

ただ前述の通り、ローカルモジュールの使用するかどうかは考え直したほうが良い。

aws-repository/
└── modules/ # ローカルモジュール
    └── acm/ # ACM
        ├── ap-northeast-1/ # 東京リージョン
        └── us-east-1/      # バージニアリージョン

(例)

リモートモジュールの場合

aws-remote-repository/
└── acm/ # ACM
    ├── ap-northeast-1/ # 東京リージョン
    └── us-east-1/      # バージニアリージョン


実行環境別

環境ごとに差分が大きいAWSリソースに関して、実行環境別にローカルモジュールを分割する。

なおcountで条件分岐しても良い

(例)

ローカルモジュールの場合

ただ前述の通り、ローカルモジュールの使用するかどうかは考え直したほうが良い。

aws-repository/
└── modules/ # ローカルモジュール
    ├── route53/ # Route53
    │   ├── tes/ # テスト環境
    │   ├── stg/ # ステージング環境
    │   └── prd/ # 本番環境
    
    ├── ssm/ # Systems Manager
    │   ├── tes/
    │   ├── stg/
    │   └── prd/
    
    └── waf/ # WAF
        └── alb/
            ├── tes/
            ├── prd/
            └── stg/

(例)

リモートモジュールの場合

aws-remote-repository/
├── route53/ # Route53
│   ├── tes/ # テスト環境
│   ├── stg/ # ステージング環境
│   └── prd/ # 本番環境

├── ssm/ # Systems Manager
│   ├── tes/
│   ├── stg/
│   └── prd/

└── waf/ # WAF
    └── alb/
        ├── tes/
        ├── prd/
        └── stg/


03. モノリスなtfstateファイルを分割する

モノリスなtfstateファイルとは、例えば特定のAWSアカウント内のAWSリソースを全て一つのtfstateファイルで管理している場合である。

AWSリソース値を参照しない関係であれば、これらは別のtfstateファイルに分割できる。

(1)

既存のバックエンド内に新しいディレクトリを作成し、その配下にtfstateファイルを新しく作成する。

ここでは、サブシステムを分割すると仮定する。

repository/
├── foo/
│   ├── backend.tf # バックエンド内の/foo/terraform.tfstate
│   ├── provider.tf
│   ...

├── bar/
│   ├── backend.tf # バックエンド内の/bar/terraform.tfstate
│   ├── remote_state.tfvars # terraform_remote_stateブロックを使用し、fooのtfstateファイルから状態を参照する
│   ├── resource.tf # fooのtfstateファイルから参照した状態を使用する
│   ├── provider.tf
│   ...

...
terraform {
  backend "s3" {
    bucket = "foo-tfstate"
    key    = "foo-sub-system/terraform.tfstate"
    region = "ap-northeast-1"
  }
}
(2)

bar側では、foo側のtfstateファイルからリソース値を取得しつつ、

# 分割した異なるfooというtfstateファイルから取得する
data "terraform_remote_state" "foo" {
  backend = "s3"

  config = {
    bucket = "foo-tfstate"
    key    = "foo/terraform.tfstate"
    region = "ap-northeast-1"
  }
}
(3)

新しいtfstateファイルに、既存のサブシステムの状態をインポートする。

事前に、バックエンドを新しいサブシステムのtfstateファイルに切り替える。

$ terraform init -reconfigure -backend-config=foo-sub-backend.tfvars
$ terraform import
(4)

サブシステムのtfstateファイルで差分がないことを確認する。

$ terraform init -reconfigure -backend-config=foo-sub-backend.tfvars
$ terraform plan
(5)

モノリスなtfstateファイルから、サブシステムの状態を削除する。

事前に、バックエンドをモノリスなtfstateファイルに切り替える。

$ terraform init -reconfigure -backend-config=foo-backend.tfvars
$ terraform state rm <サブシステムの状態>