ふり返る暇なんて無いね

日々のメモ書きをつらつらと。メインブログに書くほどでもないことを流してます

雑: Terraformの絶妙な記述力がインフラの複雑化を防いでくれる

普段、手続き型のプログラミング言語に慣れていると、Terraformの記述言語であるHCLにもどかしさを感じることがあります。もっと柔軟にループや条件分岐が書ければと感じるかもしれません。

その書きにくさ、記述力の弱さこそがTerraformの利点だと私は考えています。

宣言型という名のガードレール

HCLは、「どうやるか(How)」を記述する手続き型とは異なり、「どうあるべきか(What)」という最終的な状態を定義する宣言型の言語です。

  • 手続き型: 「もしサーバーAがなければ作る。次に、サーバーBを立てる…」
  • 宣言型: 「サーバーAとサーバーBが存在している状態が理想」

この宣言型という制約は、意図的に複雑なロジックを書きにくくするガードレールとして機能します。その結果、誰が読んでもインフラの最終形を理解しやすく、意図しない挙動(副作用)が起きにくいコードになるのです。

(というのが理想ですが、往々にして読みにくいコードができあがることはあります)

書きにくいは "複雑すぎる" のサイン

逆説的ですが、Terraformの表現力の制約が、インフラ構成の無秩序な複雑化を防いでいます。

もしあなたのTerraformコードが、複雑なlocal値*1、何重にもネストした三項演算子で埋め尽くされているとしたら、一度立ち止まってみましょう。それはツールの限界ではなく、インフラのアーキテクチャそのものが複雑になりすぎている危険信号かもしれません。

Terraformはその構成は本当に運用できますか?と問いかけてくれる複雑さの検知器となります。

道具の思想を活かすために

もちろん、この恩恵を最大限に受けるには、私たちエンジニアの側にも工夫が必要です。

Terraformは適切なモジュール化やStateの分割といったベストプラクティスと組み合わせることで真価を発揮します。

まとめ

Terraformの絶妙な記述力は、私たちをよりシンプルでメンテナンスしやすいインフラ設計へと導いてくれる優れたガイドです。次に「書きにくい」と感じたときは、ぜひインフラ構成そのものに目を向けるときっと新しい発見があるかもしれません。

*1:local値と組み込み関数を組み合わせると複雑な処理が書けます

雑: 歯の治療をしてる

左下の奥歯2本の銀歯が外れて1年くらい放置していたらいよいよしみるようになってしまって、治療してもらいました。 今まで、おそるおそる右側だけで咀嚼していたり、しみるのを恐れながら歯磨きしていたことから開放されてQoLが爆上がりしました。素晴らしい。

ただ、右上の奥歯の前側歯茎が慢性的に膿んでいて、歯科医いわく歯根が折れててそこから細菌が増殖しているのではないかとのこと。で、治療法としては抜歯しかないとのこと。

親知らず以外での抜歯は初めてなので、怖いなと思いつつ、抜歯した後どうするのか、というところが不安。入れ歯、ブリッヂ、インプラントの選択肢があるらしいけど、自分の場合はどれが適切なのだろうか。不安は尽きぬ。

雑: 最近の生活

最近早起きになりました。

大体6時前後に起きて、そこから間髪入れずにお仕事です。布団からデスクまでゼロ距離なのでなせる業です。リモートワーク万歳

ただ、ここ2ヶ月くらいお仕事がだいぶ忙しいので、寝る時間が1時くらいになってるので、睡眠不足が懸念されます。

4月に受けた健康診断の結果もあまり良くないので、この辺注意したいところです。ちょっと時間が経ってますが、二次検査予約もしておきました。何もなければいいんですが、何かあったら直すだけです。今年はもっと健康になります。

そんな感じで元気に過ごしてます。

diary.masasuzu.net

というか5日前にこんな記事書いてたことを忘れてました。若年性認知症かもしれぬ。。。。気をつけたい。

豆: Terraformのlocal変数化による繰り返し削減とformat()の使いどころ

TerraformでS3バケットを定義する際、同じ値を複数箇所で使うことがあります。例えば、以下のようなコードです。

resource "aws_s3_bucket" "main" {
  bucket = "${var.env}-${var.component}-${var.service}-app-log"

  tags = {
    Name        = "${var.env}-${var.component}-${var.service}-app-log"
    Environment = var.env
  }
}

この例では、buckettags.Name の両方で同じ文字列を使っており、バケット名を変更したい場合に 2箇所を同時に修正 する必要が出てきてしまいます。こうした繰り返しは、保守性を下げる要因になります。

local変数でまとめて再利用する

このようなケースでは、local値で変数化しておくと便利です。以下のようにすれば、変更箇所を1箇所に集約できます。

locals {
  bucket_name = "${var.env}-${var.component}-${var.service}-app-log"
}

resource "aws_s3_bucket" "main" {
  bucket = local.bucket_name
  tags = {
    Name        = local.bucket_name
    Environment = var.env
  }
}

format()を使って見通しを良くする

上記のような変数展開が複数連なると、見た目がややごちゃごちゃしてきます。その場合は format() 関数を使うことで、よりスッキリと書くことができます。

locals {
  bucket_name = format("%s-%s-%s-app-log", ${var.env}, ${var.component}, ${var.service})
}

複数のバケットを扱いたい場合は?

ここまではバケットが1つの場合でしたが、例えばログ用・データ用など、複数バケットを定義したいケースもよくあります。単純に書くと以下のようになります。

locals {
  app_log_bucket_name = format("%s-%s-%s-app-log", var.component, var.service, each.key)
  app_data_bucket_name = format("%s-%s-%s-app-data", var.component, var.service, each.key)
}

resource "aws_s3_bucket" "app_log" {
  bucket = local.app_log_bucket_name
  tags = {
    Name        = local.app_log_bucket_name
    Environment = var.env
  }
}

resource "aws_s3_bucket" "app_data" {
  bucket = local.app_data_bucket_name
  tags = {
    Name        = local.app_data_bucket_name
    Environment = var.env
  }
}

この方法でも動きますが、バケットの種類が増えるたびにコードがどんどん膨らんでしまいます。

for_each + format()でスケーラブルにする

こうした繰り返しを避けるためには、for_each を使ってループ処理にするのが効果的です。

Terraformでは ループ内でlocal値を定義出来ないので、ループ内で直接 format() を使ってバケット名を構築します。

locals {
  buckets = [ "app-log", "app-data", "some-data1", "some-data2" ]

  bucket_name_format = "%s-%s-%s-%s"
}

resource "aws_s3_bucket" "main" {
  for_each = toset(local.buckets)

  bucket = format(local.bucket_name_format, var.env, var.component, var.service, each.key)
  tags = {
    Name        = format(local.bucket_name_format, var.env, var.component, var.service, each.key)
    Environment = var.env
  }
}

format() の呼び出しが2回出てきて若干冗長に見えるかもしれませんが、共通のフォーマット文字列をlocal値で管理している ため、バケット名の規則を変更したい場合は bucket_name_format を直すだけで済みます。

このようにしておけば、バケットの数が増えてもコードの保守性を高く保ちつつ、Terraform構成をスケーラブルに保てます。


それでは良いTerraformライフを引き続きお過ごしください。

参考

2025 Japan All AWS Certifications Engineersに選定されました

だいぶ古い話ですが、2025 Japan All AWS Certifications Engineersに選定されました。

aws.amazon.com

こっそり全資格取ってました。

1月に今年は全資格取るぞ!と決めてから怒涛で取っていきました。一番痛かったのはProfessional資格を失効したことで、下位のAssociate資格も取り直さなければならず、3ヶ月の間に8個資格を取り直すことになりました。しんどかった。

期限切れになってから、再取得すると別のバッヂになる関係で、credlyの表記が水増しされた形になってます。期限切れと再取得を繰り返すと無限にバッヂが増える仕様なので、賑やかしをしたいときはいいかもしれません(?)

www.credly.com

資格は資格として、資格を取ったことがゴールではなく、資格を通した学習で得た体系的な知識を業務でどう活かしていくかが大切だと思ってます。自分なりには通過点だと考え、日々業務に取り組んでいきます。

また今年はAWS Community Builder、Japan All AWS Certifications Engineersと2ついただいているので、来年はもう一つAWS Top Engineerをいただいて3冠になりたいと思ってます。がんばります。

まだまだやるぞ!

今年もAWS Community Builder (Cloud Operations) に選定されました。

2024年に引き続きAWS Community Builder (Cloud Operations) に選定されました。

だいぶ時間が経っているので今更感がありますが、、、、、こういうのをもう少しタイムリーに書いていきたいところ。

今年からディレクトリページが変わって、パーマリンクで個々のプロフィールを表示できる様になった模様。SNS感ありますね。

さて、今年も選定いただいたので引き続きアウトプット頑張っていきます。2024年は登壇という意味では自分が望んでるペースでできていなかったので、もっと積極的に登壇活動取り組んでいきたいところです。

がんばります。

雑: 朝5時起き

最近は朝5時に起きて仕事してます。 先月までは鬼のようにいそがしかったので、5-22とかざらにあったが、6-16くらいで抑えるようにしてます。標準労働時間の8時間をちゃんと働いたとしても、6時出勤なら15時くらいで上がれてしまうので、時間の余裕があっていい感じだと感じてます。 このまま継続していきたいきもちではあります。

豆: Terraformのcurlプロバイダーよりhttpプロバイダーを使ったほうが良さそう

diary.masasuzu.net

前回 curl プロバイダーを紹介したのですが、 httpプロバイダーを使ったほうが望ましいので前回のサンプルコードを書き換えてみます。パラメータが多少変わるくらいですね。

ドキュメントを見ればわかるのですが、プロバイダー名はcurlなのですが、書き方としてAzure ADを前提にしたような書き方をされていて、品質に不安を覚えます。

それに対して、httpプロバイダーは汎用的にhttpリクエストを送るために書かれており、またhashicorp公式が出しているという安心感があります。

data "http" "github_meta" {
  method = "GET"
  url    = "https://api.github.com/meta"
}

locals {
  github_meta              = jsondecode(data.http.github_meta.response_body)
  github_webhook_cidr      = local.github_meta.hooks
  github_webhook_cidr_ipv4 = [for x in local.github_meta.hooks : x if length(split(".", x)) == 4]
  github_webhook_cidr_ipv6 = [for x in local.github_meta.hooks : x if length(split(":", x)) > 1]

}

resource "aws_ec2_managed_prefix_list" "main" {
  name           = "GitHub Hooks"
  address_family = "IPv4"
  max_entries    = length(local.github_webhook_cidr_ipv4)

  dynamic "entry" {
    for_each = local.github_webhook_cidr_ipv4
    content {
      cidr = entry.value
    }
  }
}

resource "aws_security_group" "main" {
  name   = "Allow GitHub Webhooks"
  vpc_id = var.vpc_id
}

resource "aws_vpc_security_group_ingress_rule" "main" {
  security_group_id = aws_security_group.main.id
  ip_protocol       = "tcp"
  from_port         = 443
  to_port           = 443
  prefix_list_id    = aws_ec2_managed_prefix_list.main.id
}

output "github_webhook_cidr_ipv4" {
  value = local.github_webhook_cidr_ipv4
}

output "github_webhook_cidr_ipv6" {
  value = local.github_webhook_cidr_ipv6
}