templatefile関数とは?DRYなインフラコードを実現する強力な機能
templatefile関数の基本的な構文と動作原理
Terraform の templatefile 関数は、外部テンプレートファイルを読み込み、変数置換を行うことで動的なコンフィギュレーションを生成する強力な機能です。この関数を活用することで、インフラストラクチャコードの再利用性と保守性を大幅に向上させることができます。
基本的な構文:
templatefile(path, vars)
path: テンプレートファイルへのパス(文字列)vars: テンプレート内で使用する変数のマップ
実際の使用例を見てみましょう:
# main.tf
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
user_data = templatefile("${path.module}/scripts/init.sh.tpl", {
environment = var.environment
region = var.aws_region
db_host = aws_db_instance.main.endpoint
})
}
# scripts/init.sh.tpl
#!/bin/bash
echo "Starting initialization for ${environment} environment in ${region}"
cat << EOF > /etc/application.conf
DATABASE_URL=postgresql://${db_host}:5432/myapp
ENVIRONMENT=${environment}
EOF
従来の実装方法と比較した際のメリット
従来の実装方法と templatefile 関数を使用した実装を比較してみましょう:
- 従来の実装方法:
# 直接リソース内にスクリプトを記述
resource "aws_instance" "web" {
user_data = <<-EOF
#!/bin/bash
echo "Starting initialization"
cat << EOF > /etc/application.conf
DATABASE_URL=postgresql://${aws_db_instance.main.endpoint}:5432/myapp
ENVIRONMENT=${var.environment}
EOF
EOF
}
- templatefile関数を使用した実装:
# より整理された実装
resource "aws_instance" "web" {
user_data = templatefile("${path.module}/scripts/init.sh.tpl", {
db_host = aws_db_instance.main.endpoint
environment = var.environment
})
}
templatefile関数を使用する主なメリット:
- コードの再利用性
- テンプレートファイルを複数のリソースで共有可能
- 環境やリージョンごとの設定を容易に管理
- 保守性の向上
- ロジックとテンプレートの分離
- バージョン管理が容易
- チーム開発での変更追跡が簡単
- 可読性の向上
- 複雑なスクリプトや設定を別ファイルで管理
- メインのTerraformコードがすっきり
- エラー検出の容易さ
- テンプレート構文のバリデーション
- 変数の型チェック
- 未定義変数の早期発見
高度な使用例:条件分岐とループの活用
# config.json.tpl
{
"environment": "${environment}",
"services": [
%{ for service in services ~}
{
"name": "${service.name}",
"port": ${service.port},
"enabled": ${service.enabled ? "true" : "false"}
}%{ if !is_last(service) ~},
%{ endif ~}
%{ endfor ~}
]
}
このような高度な機能を活用することで、より柔軟で保守性の高いインフラストラクチャコードを実現できます。
templatefile関数の実践的な使用例7選
EC2インスタンスの起動スクリプト自動生成
EC2インスタンスの起動時に必要な設定を動的に生成する例です:
# main.tf
resource "aws_instance" "application" {
ami = var.ami_id
instance_type = var.instance_type
user_data = templatefile("${path.module}/templates/user_data.sh.tpl", {
app_name = var.application_name
environment = var.environment
region = var.aws_region
log_level = var.environment == "prod" ? "ERROR" : "DEBUG"
env_vars = var.environment_variables
})
}
# templates/user_data.sh.tpl
#!/bin/bash
# アプリケーション初期設定スクリプト
echo "Starting ${app_name} in ${environment} environment"
# 環境変数の設定
%{ for key, value in env_vars ~}
export ${key}="${value}"
%{ endfor ~}
# ロギング設定
cat << EOF > /etc/cloudwatch-agent.json
{
"agent": {
"region": "${region}",
"logLevel": "${log_level}"
}
}
EOF
複数環境向けIAMポリシー管理
環境ごとに異なるIAMポリシーを生成する例:
# main.tf
resource "aws_iam_role_policy" "service_policy" {
name = "${var.environment}-service-policy"
role = aws_iam_role.service_role.id
policy = templatefile("${path.module}/templates/service_policy.json.tpl", {
environment = var.environment
account_id = data.aws_caller_identity.current.account_id
resources = var.allowed_resources
actions = local.allowed_actions[var.environment]
})
}
# templates/service_policy.json.tpl
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
%{ for action in actions ~}
"${action}"%{ if !is_last(action) ~},%{ endif ~}
%{ endfor ~}
],
"Resource": [
%{ for resource in resources ~}
"arn:aws:s3:::${resource}/*"%{ if !is_last(resource) ~},%{ endif ~}
%{ endfor ~}
]
}
]
}
動的なセキュリティグループルールの設定
アプリケーションの要件に応じてセキュリティグループルールを動的に生成:
# main.tf
resource "aws_security_group" "application" {
name_prefix = "${var.app_name}-sg"
vpc_id = var.vpc_id
dynamic "ingress" {
for_each = jsondecode(templatefile("${path.module}/templates/security_rules.json.tpl", {
environment = var.environment
ports = var.service_ports
cidrs = var.allowed_cidrs
}))
content {
from_port = ingress.value.port
to_port = ingress.value.port
protocol = "tcp"
cidr_blocks = ingress.value.cidrs
}
}
}
# templates/security_rules.json.tpl
[
%{ for port in ports ~}
{
"port": ${port},
"cidrs": [
%{ for cidr in cidrs ~}
"${cidr}"%{ if !is_last(cidr) ~},%{ endif ~}
%{ endfor ~}
]
}%{ if !is_last(port) ~},%{ endif ~}
%{ endfor ~}
]
環境変数を活用したRDS設定の管理
環境ごとのRDS設定を効率的に管理:
# main.tf
resource "aws_db_instance" "database" {
identifier = var.db_identifier
engine = "postgres"
engine_version = var.engine_version
allocated_storage = var.allocated_storage
instance_class = var.instance_class
db_name = var.database_name
username = var.master_username
password = var.master_password
parameter_group_name = aws_db_parameter_group.custom.name
}
resource "aws_db_parameter_group" "custom" {
name_prefix = "${var.environment}-db-params"
family = "postgres13"
parameter {
name = "shared_preload_libraries"
value = templatefile("${path.module}/templates/db_params.tpl", {
extensions = var.postgres_extensions
environment = var.environment
})
}
}
# templates/db_params.tpl
%{ for ext in extensions ~}
'${ext}'%{ if !is_last(ext) ~},%{ endif ~}
%{ endfor ~}
ECSタスク定義のテンプレート化
マイクロサービスのECSタスク定義を動的に生成:
# main.tf
resource "aws_ecs_task_definition" "service" {
family = "${var.service_name}-task"
requires_compatibilities = ["FARGATE"]
network_mode = "awsvpc"
cpu = var.cpu
memory = var.memory
container_definitions = templatefile("${path.module}/templates/container_definition.json.tpl", {
name = var.service_name
image = "${var.ecr_repository_url}:${var.image_tag}"
cpu = var.cpu
memory = var.memory
environment = var.environment
secrets = var.secrets
port_mappings = var.port_mappings
})
}
# templates/container_definition.json.tpl
[
{
"name": "${name}",
"image": "${image}",
"cpu": ${cpu},
"memory": ${memory},
"essential": true,
"portMappings": [
%{ for mapping in port_mappings ~}
{
"containerPort": ${mapping.container_port},
"hostPort": ${mapping.host_port},
"protocol": "${mapping.protocol}"
}%{ if !is_last(mapping) ~},%{ endif ~}
%{ endfor ~}
],
"environment": [
%{ for key, value in environment ~}
{
"name": "${key}",
"value": "${value}"
}%{ if !is_last(key) ~},%{ endif ~}
%{ endfor ~}
],
"secrets": [
%{ for secret in secrets ~}
{
"name": "${secret.name}",
"valueFrom": "${secret.arn}"
}%{ if !is_last(secret) ~},%{ endif ~}
%{ endfor ~}
]
}
]
マルチリージョンデプロイメントの設定
複数リージョンへのデプロイメント設定を管理:
# main.tf
locals {
region_configs = jsondecode(templatefile("${path.module}/templates/region_config.json.tpl", {
primary_region = var.primary_region
backup_regions = var.backup_regions
environment = var.environment
service_config = var.service_configuration
}))
}
# templates/region_config.json.tpl
{
"primary": {
"region": "${primary_region}",
"config": {
"instance_type": "${service_config.instance_type}",
"min_size": ${service_config.min_size},
"max_size": ${service_config.max_size},
"environment": "${environment}"
}
},
"backup": [
%{ for region in backup_regions ~}
{
"region": "${region}",
"config": {
"instance_type": "${service_config.dr_instance_type}",
"min_size": ${service_config.dr_min_size},
"max_size": ${service_config.dr_max_size},
"environment": "${environment}-dr"
}
}%{ if !is_last(region) ~},%{ endif ~}
%{ endfor ~}
]
}
カスタムバックエンド設定の動的生成
環境やリージョンに応じたバックエンド設定を動的に生成:
# backend.tf
terraform {
backend "s3" {
key = templatefile("${path.module}/templates/backend_key.tpl", {
environment = var.environment
component = var.component_name
region = var.aws_region
})
}
}
# templates/backend_key.tpl
terraform/${environment}/${region}/${component}/terraform.tfstate
これらの実践的な使用例は、実際の運用シーンで頻繁に遭遇する課題に対する解決策を提供します。各例は必要に応じてカスタマイズして使用できます。
templatefile関数使用時の実装のベストプラクティス
変数のスコープと命名規則の統一
テンプレートファイルで使用する変数の管理と命名規則について、以下のベストプラクティスを紹介します:
# 推奨される変数定義の方法
# variables.tf
variable "environment" {
description = "環境名(dev/stg/prod)"
type = string
validation {
condition = contains(["dev", "stg", "prod"], var.environment)
error_message = "環境名はdev、stg、prodのいずれかである必要があります。"
}
}
variable "service_config" {
description = "サービスの設定"
type = object({
name = string
instance_type = string
min_size = number
max_size = number
})
}
# locals.tf
locals {
# テンプレートで使用する共通変数の標準化
common_tags = {
Environment = var.environment
Project = var.project_name
ManagedBy = "terraform"
}
# 環境ごとの設定値のマッピング
environment_configs = {
dev = { log_level = "DEBUG", backup_retention = 7 }
stg = { log_level = "INFO", backup_retention = 14 }
prod = { log_level = "WARN", backup_retention = 30 }
}
}
テンプレートファイルでの使用例:
# templates/app_config.json.tpl
{
"application": {
"name": "${service_config.name}",
"environment": "${environment}",
"logLevel": "${local.environment_configs[environment].log_level}",
"tags": {
%{ for key, value in common_tags ~}
"${key}": "${value}"%{ if !is_last(key) ~},%{ endif ~}
%{ endfor ~}
}
}
}
エラーハンドリングと型安全性の確保
テンプレートファイルでのエラーハンドリングと型安全性を確保するためのベストプラクティス:
# 型安全性を確保した変数定義
variable "port_mappings" {
description = "コンテナのポートマッピング設定"
type = list(object({
container_port = number
host_port = number
protocol = string
}))
validation {
condition = alltrue([for p in var.port_mappings : contains(["tcp", "udp"], p.protocol)])
error_message = "プロトコルはtcpまたはudpである必要があります。"
}
}
# エラーハンドリングを含むテンプレート
# templates/container_config.json.tpl
{
"containerDefinitions": [
{
"portMappings": [
%{ for mapping in port_mappings ~}
{
%{ if mapping.container_port == null ~}
${format("Container port cannot be null for %s", mapping)}
%{ endif ~}
"containerPort": ${mapping.container_port},
"hostPort": ${mapping.host_port},
"protocol": "${mapping.protocol}"
}%{ if !is_last(mapping) ~},%{ endif ~}
%{ endfor ~}
]
}
]
}
テンプレートファイルの構造化と管理方法
プロジェクトにおけるテンプレートファイルの効果的な管理方法:
project/
├── main.tf
├── variables.tf
├── outputs.tf
├── locals.tf
├── templates/
│ ├── app/
│ │ ├── config.json.tpl
│ │ └── init.sh.tpl
│ ├── iam/
│ │ ├── policies/
│ │ │ ├── s3_access.json.tpl
│ │ │ └── rds_access.json.tpl
│ │ └── roles/
│ │ └── service_role.json.tpl
│ └── monitoring/
│ ├── cloudwatch_agent.json.tpl
│ └── alerts.json.tpl
└── modules/
├── app/
│ └── templates/
│ └── specific_config.json.tpl
└── database/
└── templates/
└── parameter_group.json.tpl
テンプレートの再利用性を高めるためのモジュール化の例:
# modules/iam/main.tf
module "service_role" {
source = "./modules/iam"
template_vars = {
service_name = var.service_name
environment = var.environment
permissions = var.required_permissions
}
}
# modules/iam/templates/role.json.tpl
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
%{ for permission in permissions ~}
"${permission}"%{ if !is_last(permission) ~},%{ endif ~}
%{ endfor ~}
],
"Resource": [
"arn:aws:${service_name}:*:*:${environment}-*"
]
}
]
}
テンプレートファイルの変更管理とバージョニングの例:
# versions.tf
terraform {
required_version = ">= 1.0.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
}
}
}
# template_versions.tf
locals {
template_versions = {
app_config = "v1.2.0"
iam_policies = "v2.0.1"
init_scripts = "v1.0.3"
}
template_path = "${path.module}/templates/${local.template_versions[var.template_type]}"
}
これらのベストプラクティスを適用することで、より保守性が高く、安全なTerraformコードを実現できます。特に大規模なプロジェクトや、チームでの開発において、これらの規約は重要な役割を果たします。
運用効率を高めるtemplatefile活用術
テストとバリデーションの自動化手法
templatefile関数を使用したインフラコードのテストと検証を自動化する方法を解説します:
# tests/templates_test.go
package test
import (
"testing"
"encoding/json"
"github.com/gruntwork-io/terratest/modules/terraform"
"github.com/stretchr/testify/assert"
)
func TestTemplateRendering(t *testing.T) {
t.Run("IAM Policy Template", func(t *testing.T) {
terraformOptions := &terraform.Options{
TerraformDir: "../",
Vars: map[string]interface{}{
"environment": "test",
"service_name": "api",
"allowed_actions": []string{"s3:GetObject", "s3:PutObject"},
},
}
output := terraform.Output(t, terraformOptions, "rendered_policy")
var policy map[string]interface{}
err := json.Unmarshal([]byte(output), &policy)
assert.NoError(t, err)
assert.Contains(t, policy["Statement"], "s3:GetObject")
})
}
CIパイプラインでのテンプレート検証:
# .github/workflows/terraform-validate.yml
name: Validate Templates
on:
push:
paths:
- '**.tpl'
- '**.tf'
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup Terraform
uses: hashicorp/setup-terraform@v1
- name: Validate Templates
run: |
for tpl in $(find . -name "*.tpl"); do
echo "Validating $tpl"
terraform console <<EOF
templatefile("$tpl", {
environment = "test"
region = "us-west-2"
service_name = "test-service"
})
EOF
done
チーム開発における標準化アプローチ
チームでの効率的な開発を実現するための標準化手法:
# modules/template-validator/main.tf
module "template_validator" {
source = "./modules/template-validator"
templates_path = "${path.module}/templates"
validation_rules = {
required_variables = ["environment", "service_name"]
allowed_environments = ["dev", "stg", "prod"]
naming_convention = "^[a-z][a-z0-9-]*$"
}
}
# Pre-commit hook for template validation
# .pre-commit-config.yaml
repos:
- repo: local
hooks:
- id: terraform-template-validate
name: Terraform Template Validation
entry: scripts/validate-templates.sh
language: script
files: \.(tpl|tf)$
テンプレート生成のための補助ツール:
#!/bin/bash
# scripts/create-template.sh
template_type=$1
name=$2
case $template_type in
"iam")
template_content='
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
%{ for action in actions ~}
"${action}"%{ if !is_last(action) ~},%{ endif ~}
%{ endfor ~}
],
"Resource": ["${resource_arn}"]
}
]
}'
;;
"config")
template_content='
{
"app": {
"name": "${app_name}",
"environment": "${environment}",
"region": "${region}",
"config": {
%{ for key, value in config ~}
"${key}": "${value}"%{ if !is_last(key) ~},%{ endif ~}
%{ endfor ~}
}
}
}'
;;
esac
echo "$template_content" > "templates/${name}.tpl"
既存インフラコードのリファクタリング戦略
既存のインフラコードをtemplatefile関数を使用してリファクタリングする戦略:
# Before: 直接埋め込まれたポリシー
resource "aws_iam_role_policy" "example" {
name = "example-policy"
role = aws_iam_role.example.id
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": [
"arn:aws:s3:::example-bucket/*"
]
}
]
}
EOF
}
# After: テンプレート化されたポリシー
# templates/policies/s3_access.json.tpl
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
%{ for action in allowed_actions ~}
"${action}"%{ if !is_last(action) ~},%{ endif ~}
%{ endfor ~}
],
"Resource": [
%{ for bucket in buckets ~}
"arn:aws:s3:::${bucket}/*"%{ if !is_last(bucket) ~},%{ endif ~}
%{ endfor ~}
]
}
]
}
# main.tf
resource "aws_iam_role_policy" "example" {
name = "example-policy"
role = aws_iam_role.example.id
policy = templatefile("${path.module}/templates/policies/s3_access.json.tpl", {
allowed_actions = var.s3_actions
buckets = var.target_buckets
})
}
リファクタリングのためのチェックリスト:
- テンプレート化の候補を特定
- 複数の場所で使用される似たような設定
- 環境やリージョンによって異なる値
- 長い文字列やJSON/YAML形式の設定
- 変数の抽出と整理
- 共通の変数をvariables.tfに定義
- 環境固有の値をterraform.tfvarsで管理
- ローカル変数による中間データの整理
- テンプレートの作成と検証
- 適切なディレクトリ構造の作成
- テンプレートのバリデーション
- テストケースの作成
- 段階的な移行
- 1つのリソースずつテンプレート化
- バックアップの作成
- apply計画の慎重な確認
このように、運用効率を高めるためには、自動化されたテスト、標準化されたアプローチ、そして計画的なリファクタリングが重要です。これらの施策により、チーム全体の生産性が向上し、より信頼性の高いインフラ管理が可能になります。
よくあるエラーと解決方法
構文エラーの主な原因と対処法
templatefile関数使用時によく遭遇する構文エラーとその解決方法を解説します:
- 変数参照エラー
# エラーパターン1: 未定義変数の参照
# template.tpl
${undefined_variable} # Error: Reference to undeclared input variable
# 解決方法
variable "my_variable" {
description = "Template で使用する変数"
type = string
}
# 修正後のテンプレート使用例
templatefile("${path.module}/template.tpl", {
my_variable = var.my_variable
})
- 制御構文のシンタックスエラー
# エラーパターン2: for文の構文ミス
# bad_template.tpl
%{ for item in items } # Missing ~
${item}
%{ endfor } # Missing ~
# 解決方法: 正しい構文
# good_template.tpl
%{ for item in items ~}
${item}
%{ endfor ~}
# 使用例
templatefile("${path.module}/good_template.tpl", {
items = ["item1", "item2", "item3"]
})
- JSONフォーマットエラー
# エラーパターン3: JSON構文の不正
# bad_json.tpl
{
"items": [
%{ for item in items ~}
"${item}" # 最後のカンマ処理が不適切
%{ endfor ~}
]
}
# 解決方法: is_last関数の使用
# good_json.tpl
{
"items": [
%{ for item in items ~}
"${item}"%{ if !is_last(item) ~},%{ endif ~}
%{ endfor ~}
]
}
変数参照に関するトラブルシューティング
変数参照で発生する一般的な問題とその解決アプローチ:
- スコープの問題
# エラーパターン4: 変数スコープの誤り
module "example" {
source = "./modules/example"
# 直接templatefileを呼び出そうとする
template_content = templatefile("${path.module}/template.tpl", {
local_var = local.some_value # Error: Reference to undeclared local value
})
}
# 解決方法: 変数の適切な受け渡し
# modules/example/variables.tf
variable "template_vars" {
description = "テンプレートで使用する変数のマップ"
type = map(any)
}
# modules/example/main.tf
locals {
template_content = templatefile("${path.module}/template.tpl", var.template_vars)
}
- 型の不一致
# エラーパターン5: 型の不一致
# template.tpl
"port": ${port} # 文字列として使用したい数値
# 解決方法: 明示的な型変換
# 数値を文字列として扱う場合
"port": "${tostring(port)}"
# リストを文字列として結合する場合
"commands": "${join(" && ", commands)}"
パフォーマンス最適化のためのヒント
templatefile関数使用時のパフォーマンス最適化のためのベストプラクティス:
- テンプレートのキャッシュ化
# 非効率な実装
resource "aws_instance" "example" {
count = length(var.instances)
user_data = templatefile("${path.module}/init.sh.tpl", {
instance_name = var.instances[count.index].name
# 毎回テンプレートを読み込む
})
}
# 最適化された実装
locals {
template_cache = templatefile("${path.module}/init.sh.tpl", {
instances = var.instances
})
}
resource "aws_instance" "example" {
count = length(var.instances)
user_data = local.template_cache
}
- 条件付きテンプレート読み込み
# 効率的な条件付きテンプレート使用
locals {
template_file = var.environment == "prod" ? "prod.tpl" : "dev.tpl"
config = templatefile("${path.module}/templates/${local.template_file}", {
environment = var.environment
settings = var.environment_settings[var.environment]
})
}
- 大きなテンプレートの分割
# 大きなテンプレートを管理可能なサイズに分割
locals {
base_config = templatefile("${path.module}/templates/base.tpl", {
environment = var.environment
})
service_config = templatefile("${path.module}/templates/service.tpl", {
services = var.service_definitions
})
monitoring_config = templatefile("${path.module}/templates/monitoring.tpl", {
alerts = var.alert_definitions
})
# 必要に応じて結合
combined_config = jsonencode({
base = jsondecode(local.base_config)
services = jsondecode(local.service_config)
monitoring = jsondecode(local.monitoring_config)
})
}
これらの問題解決アプローチとパフォーマンス最適化手法を適用することで、より安定した運用が可能になります。特に大規模なインフラ構成では、これらのベストプラクティスが重要な役割を果たします。
応用:大規模システムにおけるtemplatefile活用事例
マイクロサービスアーキテクチャでのマルチ活用例
マイクロサービスアーキテクチャにおけるtemplatefile関数の効果的な活用方法を紹介します:
# microservices/service.tf
module "microservice" {
source = "./modules/microservice"
for_each = local.services
name = each.key
configuration = each.value
environment = var.environment
region = var.aws_region
}
# modules/microservice/templates/task-definition.json.tpl
{
"family": "${service_name}",
"containerDefinitions": [
{
"name": "${service_name}",
"image": "${ecr_repository}:${image_tag}",
"cpu": ${cpu},
"memory": ${memory},
"essential": true,
"portMappings": [
%{ for port in port_mappings ~}
{
"containerPort": ${port.container},
"hostPort": ${port.host},
"protocol": "${port.protocol}"
}%{ if !is_last(port) ~},%{ endif ~}
%{ endfor ~}
],
"environment": [
%{ for key, value in environment_variables ~}
{
"name": "${key}",
"value": "${value}"
}%{ if !is_last(key) ~},%{ endif ~}
%{ endfor ~}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/${service_name}",
"awslogs-region": "${region}",
"awslogs-stream-prefix": "${environment}"
}
},
"healthCheck": {
"command": ${jsonencode(health_check_command)},
"interval": ${health_check_interval},
"timeout": ${health_check_timeout},
"retries": ${health_check_retries}
}
}
],
"executionRoleArn": "${execution_role_arn}",
"taskRoleArn": "${task_role_arn}",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "${cpu}",
"memory": "${memory}"
}
アカウント戦略での実現パターン
マルチアカウント環境でのtemplatefile活用パターン:
# organization/account_setup.tf
locals {
account_config = jsondecode(templatefile("${path.module}/templates/account_config.json.tpl", {
organization_id = var.organization_id
environments = var.environments
service_names = var.service_names
}))
}
# templates/account_config.json.tpl
{
"accounts": {
%{ for env in environments ~}
"${env}": {
"name": "${env}-environment",
"email": "aws+${env}@example.com",
"services": {
%{ for service in service_names ~}
"${service}": {
"vpc_cidr": "10.${index(environments, env)}.${index(service_names, service)}.0/24",
"private_subnets": [
"10.${index(environments, env)}.${index(service_names, service)}.0/26",
"10.${index(environments, env)}.${index(service_names, service)}.64/26"
]
}%{ if !is_last(service) ~},%{ endif ~}
%{ endfor ~}
}
}%{ if !is_last(env) ~},%{ endif ~}
%{ endfor ~}
}
}
# IAMポリシーテンプレート
# templates/cross_account_policy.json.tpl
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
%{ for action in allowed_actions ~}
"${action}"%{ if !is_last(action) ~},%{ endif ~}
%{ endfor ~}
],
"Resource": [
%{ for account in target_accounts ~}
"arn:aws:${service}:${region}:${account}:${resource_path}"
%{ if !is_last(account) ~},%{ endif ~}
%{ endfor ~}
]
}
]
}
継続的なデリバリーパイプラインでの統合方法
CI/CDパイプラインでのtemplatefile活用例:
# pipeline/main.tf
module "codepipeline" {
source = "./modules/pipeline"
for_each = local.service_pipelines
pipeline_config = templatefile("${path.module}/templates/pipeline_config.json.tpl", {
service_name = each.key
environments = var.deployment_environments
repository = each.value.repository
branch = each.value.branch
build_spec = each.value.build_spec
})
}
# templates/pipeline_config.json.tpl
{
"pipeline": {
"name": "${service_name}-pipeline",
"stages": [
{
"name": "Source",
"actions": [
{
"name": "Source",
"category": "Source",
"provider": "CodeCommit",
"configuration": {
"RepositoryName": "${repository}",
"BranchName": "${branch}"
}
}
]
},
%{ for env in environments ~}
{
"name": "Deploy-to-${env}",
"actions": [
{
"name": "Deploy",
"category": "Deploy",
"provider": "CloudFormation",
"configuration": {
"StackName": "${service_name}-${env}",
"TemplatePath": "BuildOutput::template.yaml",
"Capabilities": "CAPABILITY_IAM",
"ParameterOverrides": {
"Environment": "${env}",
"ServiceName": "${service_name}"
}
}
}
]%{ if !is_last(env) ~},%{ endif ~}
}
%{ endfor ~}
]
}
}
# デプロイメント設定テンプレート
# templates/deployment_config.json.tpl
{
"version": 1.0,
"Resources": [
%{ for resource in deployment_resources ~}
{
"name": "${resource.name}",
"type": "${resource.type}",
"properties": {
%{ for key, value in resource.properties ~}
"${key}": ${jsonencode(value)}%{ if !is_last(key) ~},%{ endif ~}
%{ endfor ~}
}
}%{ if !is_last(resource) ~},%{ endif ~}
%{ endfor ~}
],
"Hooks": [
%{ for hook in deployment_hooks ~}
{
"name": "${hook.name}",
"command": ${jsonencode(hook.command)},
"timeout": "${hook.timeout}"
}%{ if !is_last(hook) ~},%{ endif ~}
%{ endfor ~}
]
}
これらのパターンを組み合わせることで、大規模システムにおいても効率的なインフラストラクチャ管理が可能になります。特に以下の点に注意して実装することをお勧めします:
- モジュール化とテンプレートの再利用
- 共通のパターンをモジュール化
- テンプレートの階層構造化
- バージョン管理の徹底
- 環境間の一貫性確保
- 環境固有の設定の分離
- 共通設定の一元管理
- 変更履歴の追跡
- セキュリティとコンプライアンス
- アクセス権限の適切な設定
- 監査ログの確保
- セキュリティベストプラクティスの適用
これらの実装パターンは、組織の規模や要件に応じてカスタマイズすることができます。