🤖

【TerraForm】入門して1週間、本気で考えた最適構成

2024/06/20に公開

概要

諸事情によりAWS環境用にTerraFormを作成したので構成を記す。
moduleを利用。

対象者

入門記事を読み終わって構成どうしようかな~と迷ってる方向け

構成

ファイル構成

.
├── modules
│   ├── ec2
│   │   ├── ec2.tf
│   │   ├── variable.tf
│   │   └── output.tf
│   └── vpc
│       ├── vpc.tf
│       ├── subnet.tf
│       :(省略)
│       ├── variable.tf
│       └── output.tf
└── env
    ├── dis
    │   └── ** proと同じ構成
    └── pro
        ├── main.tf
        ├── var_ec2.tf
        ├── var_vpc.tf
        :(省略)         
        ├── data.tf         
        └── var_common.tf

root module

./env/pro/*をルートモジュールとして機能させる。
var_common.tfには全子モジュールで利用する共通の変数を定義する。
var_${module_name}には各子モジュールで利用する変数を定義する(本書ではvariable.tfとして紹介)
data.tfには全子モジュールで利用する既存のリソースデータを定義する。
main.tfにはリソース定義を記載する。

child module

./modules/*を子モジュールとして機能させる。
ec2、vpcなどAWSサービス単位でディレクトリを切る(正直ここは規模による)
variable.tfにはルートモジュールから引き継いだ変数を定義する。
output.tfには作成したリソース情報を他モジュールで利用する場合に定義する。
${service_name}.tfにはリソース定義を記載する。

ファイル別記載内容

root module

main.tf

providerブロックには対象プラットフォームへの接続情報等を記述する(本記事はAWS)。
moduleブロックには子モジュールの定義がされているファイルパスや、子モジュールに渡す変数等を定義する。

locals {
  profile = "hoge_profile"
  region  = "ap-northeast-1"
}

provider "aws" {
  region                   = local.region
  shared_credentials_files = ["~/.aws/credentials"]
  profile                  = local.profile
}

module "vpc" {
  source          = "../../../modules/vpc"
  tags            = local._tags
  vpc_settings    = local._vpc_settings
  subnet_settings = local._subnet_settings
  :(省略)
  flow_log_s3_arn = data.aws_s3_bucket.s3_02.arn
}

module "ec2" {
  source             = "../../../modules/ec2"
  tags               = local._tags
  key_settings       = local._key_settings
  ec2_settings       = local._ec2_settings
  :(省略)
  aws_subnet         = module.vpc.aws_subnet
  aws_security_group = module.vpc.aws_security_group
}

variable.tf

_vpc_settingsには純粋なVPCの設定だけでなくサブネットやルートテーブル情報などを記載すると可読性が上がる(気がする)。
可読性の良いオブジェクトを作成し、そのオブジェクトを加工して子モジュールに引き渡すのも良いと思った。

基本的には可変値はルートモジュールで定義し、固定値は子モジュールのリソース定義にべた書きするのが良い(と思う)。
変数はルートモジュールではlocalsで定義し、子モジュールで受け取るときにvariableで型チェックするのが良い(よね?)。

locals {
  _vpc_settings = {
    vpc_01 = {
      cidr = "192.168.100.0/23"
      subnets = {
        subnet_01 = {
          cidr_block = "192.168.100.0/28"
          :(省略)
        }
        :(省略)
      }
      :(省略)
    }
    vpc_02 = {
      cidr = "192.168.103.0/23"
      :(省略)
    }
  }
}

# mapオブジェクトで渡す場合
locals {
  _subnet_settings = {
    for vpc_key, vpc in local._vpc_settings :
    subnets = {
      for subnet_key, subnet in vpc.subnets :
      subnet_key => {
        vpc_id_key = vpc_key
        cidr_block = subnet.cidr_block
        :(省略)
      }
    }
  }
}

# listオブジェクトで渡す場合
locals {
  _subnet_settings = flatten([
    for vpc_key, vpc in local._vpc_settings :
    [
      for subnet_key, subnet in vpc.subnets :
      {
        vpc_id_key = vpc_key
        name       = subnet_key
        cidr_block = subnet.cidr_block
        :(省略)
      }
    ]
  ])
}

data.tf

TerraForm管理外のリソースはdataブロックで定義する。
別にdata.tfとしてファイルを分けなくても良いと思う。
※筆者用に三項演算子を利用。

data "aws_s3_bucket" "s3" {
  bucket = local.profile == "hoge_profile" ? "hoge_bucket" : "piyo_bucket"
}

child module

${service_name}.tf

# var.subnet_settingsがmapオブジェクトの場合
resource "aws_subnet" "subnet" {
  for_each   = var.subnet_settings
  vpc_id     = aws_vpc.vpc[each.value.vpc_id_key].id
  cidr_block = each.value.cidr_block
  :(省略)
  tags = {
    Name = each.key
  }
}

# var.subnet_settingsがlistオブジェクトの場合
resource "aws_subnet" "subnet" {
  for_each = {
    for index, subnet in var.subnet_settings :
    subnet.name => subnet #リソースキーにサブネット名を代入
  }
  :(以下省略)
}

variable.tf

ここで型チェック。
defaultでデフォルト値の設定もできるが、あまり必要性を感じなかった。
descriptionで変数の説明が書けるので仕事の場合は必須。

# subnet_settingsがmapオブジェクトの場合
variable "subnet_settings" {
  type = list(object({
    vpc_id_key = string
    cidr_block = string
    :(省略)
  }))
}

# subnet_settingsがlistオブジェクトの場合
variable "subnet_settings" {
  type = list(object({
    vpc_id_key = string
    name       = string
    cidr_block = string
    :(省略)    
  }))
}

output.tf

カレントモジュールで定義したリソースを他の子モジュールで利用する場合に利用する。
descriptionで変数の説明が書けるので仕事の場合は必須。

output "id_subnet_01" {
  value = aws_subnet.subnet_01.id
}

# まとめて定義
output "aws_subnet" {
  value = aws_subnet.subnet
}

その他メモ

import

既存リソースをTerraForm管理下に置く際に利用する(本書では紹介しない)
既存リソースのHCLを自動生成できるので、筆者は既存リソースをヒントにHCLを書き進めるために利用した。

provider.tf を作成

provider.tf
provider "aws" {
  region                   = "ap-northeast-1"
  shared_credentials_files = ["~/.aws/credentials"]
  profile                  = "hoge_profile"
}

import.tf を作成

import.tf
import {
  id = "hoge_lambda"
  to = aws_lambda_function.test
}

実行

本来はlambda.tfとすべきなのだろうが、筆者は参考用に利用するため.txtとしている。

bash
terraform plan -generate-config-out=lambda.txt

Discussion