はじめに
インフラのリソースをコードで管理したい。
さて、Terraformを使おう… となった際に、どのようなディレクトリ構成で、どのようにワークスペースを区切るべきか、悩むことかと思います。
今回は小規模な組織がサクッとシンプルにTerraformを扱う際の個人的ベストな構成を紹介します。
前提
本記事では、以下のような方々をターゲットとしております。
- AWSのリソースをTerraformで管理したい
- 全てのインフラの変更を1, 2人で面倒見きれる程度の組織規模である(多少のコンフリクトは問題にならない)
- なるべくお金をかけたくないので。なのでTerraform Cloudは利用しないで、AWS上のS3でTerraformのstateを管理する
逆に、大規模な組織においてTerraform導入を考えている場合、このケースは適さないと思います。
理由としては以下の通りです。
多人数の組織の場合は頻繁にTerraform Applyが発生し、コンフリクトや意図せぬリソース変更の原因となる
- 現状、Terraform Apply時にS3上のTerraformのstateファイルにロックをかけることはできる
- とはいえ、(CI/CDを構築した場合)mainブランチの状態を元にTerraform applyが適用されることになる
- 複数人がmainにマージしてしまうと、意図しない事故が発生する可能性が高区なる
- この場合には、素直にTerraform Cloudに移行して、state管理を任せましょう
概要
結論として、以下のような構成をお勧めします。
link: https://github.com/gonkunkun/terraform-template
本構成におけるポイントは以下の通りです。
- TerraformのstateファイルはTerraformで管理せずに、Cloudformationを利用
- これは言わずもがなです。
- TerraformからTerraformのstateファイルを消せるのは論外
- 環境はstgとprdの2環境を想定
- 各環境毎にstateファイルを分けて管理
それぞれの環境毎にtfファイルを分けない
- ここは一番結論が分かれるポイントだと思います
- tfファイルを環境毎に分けないことで以下のメリットが生まれます
- stgとprd環境の差異をなるべく発生させない
- 仮に環境差異がある場合、コードをパッと見れば分かる
- 2重メンテが発生しない
- 反面、デメリットもあります
- stgにのみ、prdにのみ適用したい変更がある場合には手間が増える
- 具体的には、都度都度条件分岐やcountを利用して片面にのみ変更を当てる必要がある
詳細
ディレクトリ構成
ディレクトリ構成としては以下の通りです。
~/D/g/terraform-template ❯❯❯ tree . main
.
├── README.md
├── cloudformation
│ └── S3.yaml
└── workloads
├── data.tf
├── envs
│ ├── prd
│ │ ├── backend.hcl
│ │ └── terraform.tfvars
│ └── stg
│ ├── backend.hcl
│ └── terraform.tfvars
├── locals.tf
├── main.tf
├── providers.tf
├── terraform.tfvars
├── variables.tf
├── versions.tf
└── vpc.tf
それでは、いくつかのポイント毎に見ていきましょう。
Stateファイルの配置先
本来はこの設定もCI/CDから変更したいですが、そこまで考えられていないです。
バケット名やTerraformの権限を持ったユーザを設定した上で、CloudformationでS3バケットを作成します。
Resources:
StateBucketStg:
Type: "AWS::S3::Bucket"
Properties:
BucketName: "xxxxx-terraform-state-stg"
VersioningConfiguration:
Status: Enabled
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
StateFileBucketPolicyForStg:
Type: "AWS::S3::BucketPolicy"
Properties:
Bucket: !Ref StateBucketStg
PolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: "AllowSpecificIAMUser"
Effect: "Allow"
Principal:
AWS: "arn:aws:iam::xxxxxxxxx:user/terraform"
Action:
- "s3:GetObject"
- "s3:PutObject"
- "s3:ListBucket"
- "s3:DeleteObject"
Resource:
- !Sub "arn:aws:s3:::${StateBucketStg}/*"
- !Sub "arn:aws:s3:::${StateBucketStg}"
TerraformPlanOnlyPolicyForStg:
Type: "AWS::IAM::Policy"
Properties:
PolicyName: "TerraformPlanOnlyPolicy"
Roles:
- !Ref TerraformUserRole
PolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: "AllowPlanActions"
Effect: "Allow"
Action:
- "s3:GetObject"
- "s3:ListBucket"
Resource:
- !Sub "arn:aws:s3:::${StateBucket}"
- !Sub "arn:aws:s3:::${StateBucket}/*"
- Sid: "DenyApplyAndDestroy"
Effect: "Deny"
Action:
- "s3:PutObject"
- "s3:DeleteObject"
Resource:
- !Sub "arn:aws:s3:::${StateBucket}"
- !Sub "arn:aws:s3:::${StateBucket}/*"
StateBucketPrd:
Type: "AWS::S3::Bucket"
Properties:
BucketName: "xxxxx-terraform-state-prd"
VersioningConfiguration:
Status: Enabled
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
StateFileBucketPolicyForPrd:
Type: "AWS::S3::BucketPolicy"
Properties:
Bucket: !Ref StateBucketPrd
PolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: "AllowSpecificIAMUser"
Effect: "Allow"
Principal:
AWS: "arn:aws:iam::xxxxxxxxx:user/terraform"
Action:
- "s3:GetObject"
- "s3:PutObject"
- "s3:ListBucket"
- "s3:DeleteObject"
Resource:
- !Sub "arn:aws:s3:::${StateBucketPrd}/*"
- !Sub "arn:aws:s3:::${StateBucketPrd}"
ref: https://github.com/gonkunkun/terraform-template/blob/main/cloudformation/S3.yaml
backend (stateファイルの保存先)の設定
環境毎にbackendの設定を記載しています。
bucket = "xxxxx-terraform-state-stg"
region = "ap-northeast-1"
key = "stg/terraform.tfstate"
use_lockfile = true
encrypt = true
stg: https://github.com/gonkunkun/terraform-template/blob/main/workloads/envs/stg/backend.hcl
bucket = "xxxxx-terraform-state-prd"
region = "ap-northeast-1"
key = "prd/terraform.tfstate"
use_lockfile = true
encrypt = true
prd: https://github.com/gonkunkun/terraform-template/blob/main/workloads/envs/prd/backend.hcl
DynamoDBを用いたstate lockが不要になったのが嬉しいですね。
ref: https://github.com/hashicorp/terraform/blob/5279e432611752a9bcba27baaf657a3305c226f8/website/docs/language/backend/s3.mdx#state-locking
ちなみに、plan/applyを実行する際には以下のようなコマンドとなります。
# In the case of stg
## Specify the backend
terraform init -backend-config="envs/stg/backend.hcl"
## Plan
terraform plan -var="env=stg"
# In the case of prd
## Specify the backend
terraform init -backend-config="envs/prd/backend.hcl"
## Plan
terraform plan -var="env=prd"
stgとprd環境のリソース定義方法
環境毎に変数を定義し、terraformリソースに渡します。
locals {
cidr_block = {
stg : "10.10.0.0/16",
prd : "10.20.0.0/16",
}[var.env]
}
#trivy:ignore:AVD-AWS-0178
resource "aws_vpc" "sample" {
cidr_block = local.cidr_block
tags = {
"Name" = "sample-${var.env}-vpc"
}
tags_all = {
"Name" = "sample-${var.env}-vpc"
}
lifecycle {
prevent_destroy = true
}
}
ref: https://github.com/gonkunkun/terraform-template/blob/main/workloads/vpc.tf
セットアップ(Terraform planまで)
それでは、ローカル環境からplanを打てるようになるまでの設定を見ていきます。
事前準備
以下は事前に済ませておいてください。
- 対象となるAWSアカウントの作成
- state管理用のS3バケットの作成
- Terraform実行用のIAM or Roleの作成 / ローカル環境上でのプロファイルの設定
設定
リポジトリをCloneしてきます。
# Install Terraform (using brew)
brew install terraform
# Clone the repository
git clone https://github.com/gonkunkun/terraform-template
バックエンドのS3バケット名を正しく設定します。
stg: https://github.com/gonkunkun/terraform-template/blob/main/workloads/envs/stg/backend.hcl
prd: https://github.com/gonkunkun/terraform-template/blob/main/workloads/envs/prd/backend.hcl
自分の環境に合わせてregionを設定します。
https://github.com/gonkunkun/terraform-template/blob/main/workloads/providers.tf
バックエンドを選択して、initしましょう。
(このタイミングでS3上にstateファイルが生成されるはず)
# Initialize the provider (for the staging environment)
cd ./terraform-template/workloads
terraform init -backend-config="envs/stg/backend.hcl"
あとはplanを打てることを確認します。
terraform plan -var="env=stg"
おわりに
本記事では、Terraformのディレクトリ構成と初期設定についてまとめました。
CI/CDの構築については以下で紹介しています。
ここまで読んでいただきありがとうございます。