はじめに
Terraformで簡単にS3を作りたいよね。ということで module 作ってみました。
どうせ作ったなら皆さんの参考になればと共有することに。
バージョンは以下の通り。
このバージョン以外での動作は確認してません。
terraform {
required_version = "1.5.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "5.3.0"
}
}
}
GitHubにソースコードを上げておりますので、もしよろしければ参考に。
⇒ こちらをクリック
参考にしていただくためにGitHubに残しております。
いかなる損失が発生しても一切責任は負いませんのでよろしくお願いしますね。
module
コードが多いので一部抜粋してややこしいところのみ説明していきます。
基本的には見たままです。
main.tf
まずは main.tf から。
ややこしいのはLifecycle Rule
のリソースのみですかね。
#
# Lifecycle Rule
#
resource "aws_s3_bucket_lifecycle_configuration" "this" {
count = var.rule_created ? 1 : 0 ※1
bucket = aws_s3_bucket.this.id
rule {
id = var.rule_id
status = var.rule_status
# AND条件のフィルター
dynamic "filter" { ※2
for_each = length(local.lifecycle_and_filter) > 0 ? local.lifecycle_and_filter : [] ※3
content {
dynamic "and" {
for_each = local.lifecycle_and_filter
content {
object_size_greater_than = and.value.object_size_greater_than
object_size_less_than = and.value.object_size_less_than
prefix = and.value.prefix
}
}
}
}
# OR条件のフィルター
dynamic "filter" {
for_each = length(local.lifecycle_and_filter) == 0 ? var.filter : []
content {
object_size_greater_than = filter.value.object_size_greater_than
object_size_less_than = filter.value.object_size_less_than
prefix = filter.value.prefix
}
}
dynamic "transition" {
for_each = var.transition ※4
content {
date = transition.value.date
days = transition.value.days
storage_class = transition.value.storage_class
}
}
・・・
}
}
※1
count = var.rule_created ? 1 : 0
こちらは変数の rule_created が true の場合に、aws_s3_bucket_lifecycle_configuration を1回繰り返すよ。というループ処理になります。
terraform では IF文ではなく三項演算子を使用します。(三項演算子はググってください。。)
ちなみに、ループには count や for_each 、for があるのですが、
0 or 1 の時は count で 0 or 1以上の場合は for_each がいいって記事を見ました。
まあ、今回の場合は rule_created を true にすればライフサイクルルールが作成され、
rule_created を false にすればライフサイクルルールは作成されないということです。
※2
dynamic "filter" { ・・・ }
こちらは filter というブロックの定義を動的に変更する場合に使用します。
dynamic についての説明は コチラ
ライフサイクルルールでフィルターを定義したい場合としたくない場合があると思うので
変数が渡されているか否かで filter ブロックの処理を制御します。
ライフサイクルルールのフィルター設定には AND条件と OR条件の設定が可能です。
フィルターに引っかかったオブジェクトのみがライフサイクルの対象となります。
ANDの例:オブジェクトサイズが10MB以上 かつ error/ フォルダ配下のオブジェクト
ORの例:オブジェクトサイズが10MB以上 もしくは error/ フォルダ配下のオブジェクト
※3
for_each = length(local.lifecycle_and_filter) > 0 ? local.lifecycle_and_filter : []
こちらは※2とセットです。ループの分だけ filter ブロックを回します。
こちらは1回しかループさせたくない場合でも for_each を使用します。
※4
for_each = var.transition
dynamic の後の for_each はこんな感じで簡潔に書くことも可能です。
※3の場合は、AND条件とOR条件の二つの dynamic "filter" {}
を定義していたので少し複雑になりました。
variables.tf
次は module 内で使用する変数の定義ファイル
こちらも大したことはしてないです。
#
# aws_s3_bucket_lifecycle_configuration
#
variable "rule_created" {
type = bool
default = false
description = "ライフサイクルルール作成の有効可否"
}
variable "filter" {
type = list(object({ ※1
and = bool # AND条件適用の有効可否
object_size_greater_than = number # ルールが適用されるオブジェクトの最小サイズ
object_size_less_than = number # ルールが適用されるオブジェクトの最大サイズ
prefix = string # ルールが適用される1つまたは複数のオブジェクトを識別するプレフィックス
}))
default = []
description = "ライフサイクルルールのフィルター設定"
}
variable "transition" {
type = list(object({
date = number # 指定されたストレージクラスに移動する日。日付の値はRFC3339形式
days = number # 指定されたストレージクラスに移動するまでの日数。正の整数のみ指定可能
storage_class = string # 移動先のストレージクラス
}))
default = [] ※2
description = "現行バージョンのストレージ移動アクション"
}
※1
type = list(object({・・・}))
この形式で値を渡しなさいね!って感じで定義できます。
ただ、定義されている値が存在しない場合はエラーが発生します。
変数filter の場合は以下のような感じ。
filter = [
{
and = true ### AND条件 の dynamic "filter" で処理される
object_size_greater_than = null ### null を渡すことも可能
object_size_less_than = null
prefix = "error/"
}
]
※2
default = []
何も渡されなかった場合のデフォルト値を定義できます。
type = list(object~)って感じなので、デフォルト値もリスト型にする必要があります。(nullも可)
main.tf の ※4ですが、デフォルトは空のリストなので for_each がループせず
dynamic "transition" {・・・}
は処理しないという流れになります。
outputs.tf
こちらは記述少ないです。
ご覧の通り、作成したS3のバケットIDとバケットARNを返り値として使用できます。
後ほど使用しているところが出てきます。
output "bucket_id" {
value = aws_s3_bucket.this.id
description = "バケットのID"
}
output "bucket_arn" {
value = aws_s3_bucket.this.arn
description = "バケットのARN"
}
module はこんな感じです。
module 呼び出し元
module 呼び出し元の説明です。
デフォルト値でS3を作成する場合と、色々と設定を変更している場合の2種類をGitHubにはあげてます。
storage.tf
まずはデフォルト値でS3を作成する場合
prefix と subname については、module の変数内でデフォルト値が定義されていない変数です。
なので module 呼び出し元での代入は必須となります。
module "s3_logs" { ※1
source = "./modules/s3" ※2
# common
prefix = "${var.system}-${var.env}"
subname = "logs"
}
※1
module "s3_logs" { }
module を呼び出す場合はmodule "XXX" { }
の形式で記述する必要があります。
※2
source = "./modules/s3"
どのmoduleを使用するか指定する必要があります。相対パスです。
色々と設定を変更している場合。
基本的には module の変数ファイルで指定されている型で代入していけばOK。
module "s3_sample_bucket" {
source = "./modules/s3"
# common
prefix = "${var.system}-${var.env}"
subname = "sample-bucket"
# Transfer Acceleration
accelerate_status = "Enabled"
# Bucket Ownership
object_ownership = "ObjectWriter"
# Public Access
public_access_block_config = {
block_public_acls = false
ignore_public_acls = false
block_public_policy = false
restrict_public_buckets = false
}
# Logging
logging_enabled = true
target_bucket = module.s3_logs.bucket_id ※3
target_prefix = "sample-bucket/"
# SSE Encrypt
sse_algorithm = "aws:kms"
kms_master_key_id = "aws/s3"
# Versioning
versioning_status = "Enabled"
# Lifecycle Rule
rule_created = true
rule_id = "sample-lifecycle-rule"
filter = [{
and = true
object_size_greater_than = null
object_size_less_than = null
prefix = "error/"
}]
transition = [{
date = null
days = 2
storage_class = "GLACIER"
}]
expiration = [{
date = null
days = 4
expired_object_delete_marker = false
}]
# non_ver_transition = [{
# newer_noncurrent_versions = 6
# noncurrent_days = 10
# storage_class = "GLACIER"
# }]
non_ver_expiration = [{
newer_noncurrent_versions = 4
noncurrent_days = 12
}]
}
※3
target_bucket = module.s3_logs.bucket_id
先ほどチラッと出てきた outputs.tf で定義した内容は上記のように使用することが可能です。
module.s3_logs はデフォルト値で作成した S3。
その S3 の バケットIDを、変数target_bucket に代入しています。
AWSリソース
実際に storage.tf の内容でリソースを作成するとこんな感じになります。
想定通り2つのバケットが作成されてる。
terra-dev-s3-sample-bucketバケットはバージョニングの設定なども問題なく出来てる。
サーバアクセスログは先ほどの outputs.tf で取得したバケットIDを使用して指定しています。
問題ないですね。
2023/06/23 追記
バケットポリシーを設定できるように module を修正しました。
コードは GitHub をご覧ください。
さいごに
最後までご覧いただきありがとうございました。
一度 module を作ってしまえばその後はとても楽なので、ぜひ皆さんも module を活用しましょう!
今後もGitHubは更新していく予定なので役に立ったらコメントでもしてくれると嬉しくて泣きます。
module の変更案などもコメントしてください。
では、良いTerraformライフをお送りください。