【TerraForm】入門して1週間、本気で考えた最適構成
概要
諸事情により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 "aws" {
region = "ap-northeast-1"
shared_credentials_files = ["~/.aws/credentials"]
profile = "hoge_profile"
}
import.tf を作成
import {
id = "hoge_lambda"
to = aws_lambda_function.test
}
実行
本来はlambda.tf
とすべきなのだろうが、筆者は参考用に利用するため.txt
としている。
terraform plan -generate-config-out=lambda.txt
Discussion