はじめに
こんにちは。
今年の春に入社した新卒エンジニアです。現在はクラウドを扱う部署に所属しています。
クラウドの世界ではIaCが必須スキルだと痛感し、10月頃から一念発起してTerraformの勉強を始めました。
教材として評判の高い『Terraformではじめる実践IaC』を選んだのですが、実は私は文系学部出身で、プログラミングとは全く縁のない人生を送ってきました。そのため、コードの理解がとにかく難しい。
そこで、この良書で学習を進める中で特に「なんやこれ」と感じたポイントを、自分のための備忘録として、ここに書き残していきたいと思います。私と同じような初心者の方の助けにもなれば幸いです。
for_each
疑問
まずは以下のコードをご覧ください。
resource "aws_subnet" "public_subnets" {
for_each = toset(var.subnet_cidrs.public)
cidr_block = each.key
vpc_id = aws_vpc.vpc.id
}
この時点でもうわかりません。何がわからないかというと、for_eachで何をしているのか、cidr_blockにeach.keyをぶち込んで何をしているのか。そもそもeach.keyのeachってどこから来たんや。
var.subnet_cidrs.publicはvariables.tfで定義しており、リストであることはわかっています。私がつまづいたのは、each.keyの部分でした。
解決
そもそもこれは私の悪い癖なのですが、文法をよく理解せずにハンズオンの章で勉強していたことがダメでした。for_eachの文法については前の章にしっかりと書いてありました。
resource "aws_iam_user" "users"{
for_each = toset(["alice", "bob", "carol"])
name = each.key
}
これはalice、bob、carolという名前のIAMユーザーを作成するコードで、nameにはalice、bob、carolが順番に代入されます。つまり、each.keyは配列の要素そのものなわけですね。これを配列の先頭から順にnameに代入していくわけです。
そして私はfor_eachとeach.key(each.value)がセットで使われるということを理解していませんでした。「eachはどこから来たんや」と言っていた原因はそもそもセット運用されることを知らなかったからでした。
いやぁ、今見たら全然難しくない当たり前のことを書いていただけなんですけどね。早とちりしないよう気を付けます。
属性
疑問
次の疑問です。実は最初に提示したコードブロックでわからない部分がもうひとつありました。
resource "aws_subnet" "public_subnets" {
for_each = toset(var.subnet_cidrs.public)
cidr_block = each.key
vpc_id = aws_vpc.vpc.id
}
aws_vpc.vpc.idのidです。
aws_vpc.vpcまではわかります。"aws_vpc"というリソースの"vpc"という名前で管理しているヤツ。このくらいの理解ですが、まあ十分でしょう。問題はid。お前どこから湧いた???
解決
というわけでこれはGeminiに聞いてみました。
Geminiが言うには、どうやらリソースの参照は「リソース名.ローカル名.属性名」というかたちで行われているらしく、今回の場合はidが属性名にあたるわけですね。ほんでもう少し深掘りしてみました。「idって勝手に生成されるんか?」と聞いてみると、「リソース作成したときに生成される一意のIDのことです」と。確かに思い返してみれば、VPCを作ったときに「vpc-1q2w3e4r5t6y7u8i9o」のような形式でIDが生成されます。idというのはまさにこのことを指していたということですね。
属性名はidに限らず、arnなどもありますね。これでまたひとつ障壁を越えられました。めでたしめでたし。
ディレクトリ構成
疑問
ディレクトリ構成がわからん。
というのもですね、『Terraformではじめる実践IaC』はルートディレクトリ直下にリソースを直接書き込むところから始まるんですね。要するに最初はmodulesなんてものを用意しないんですよ。なので初心者の私は以下のディレクトリ構成が普通だと思っていました。
/
├── env/
│ └── var.tfvars
├── vpc.tf
└── variables.tf
そして、これで良いのか~と思って読み進めた矢先、立ちはだかるmodulesディレクトリ。待て、お前誰だよ。数ページ先ではこんな構成に変わっていました。
/
├── env/
│ ├── var.tfvars
│ ├── prod.tfvars
│ └── ...
├── modules/
│ └── vpc/
│ ├── variables.tf
│ ├── vpc.tf
│ └── outputs.tf
├── variables.tf
└── main.tf
この構成を見て、私は訳が分からなくなりました。なぜなら、各階層に同じファイル名が存在するからです。しかも全体の構成を見せてくれるのはこの序盤だけで、これからさらに読み進めていくとサブネットやAZ、ゲートウェイまでコード化しています。さらに悪いことに、モジュールの概念を理解していない私はterraform applyをどこで実行すれば良いかもわからなくなりました。
解決
そもそもモジュールって何だ
モジュールについて以下の説明がありました。
Terraformにおけるモジュールとは、一緒に利用される複数のリソースをまとめて再利用するためのものです。-『Terraformではじめる実践IaC』p.193より引用
要するに「よく使うリソースは再利用できる形にしてまとめておこうぜ」ってことらしいです。なるほど。ちなみにmain.tfとmodules/vpc/vpc.tfはこんな感じになっていました。
module "vpc"{
source = "./modules/vpc"
vpc_cidr_block = "10.0.0.0/16"
}
# (一部省略)
resource "aws_vpc" "vpc"{
cidr_block = var.vpc_cidr_block
}
# (一部省略)
modules/vpc/vpc.tfで作成したいリソースの定義を書き、main.tfのsourceで呼び出したいリソース定義が書かれているディレクトリを指定しています。なるほど、こうやって分けるんですな。これでディレクトリ構成の謎は一件落着。
で、まだ残っているどこでterraform applyを実行するか問題なんですが、結論は簡単です。(このディレクトリ構成の場合は)main.tfがあるディレクトリ、すなわちルートディレクトリで実行すれば良いです。括弧で補足を入れた(というか保険をかけた)のは、環境ごとにディレクトリを分けてそれぞれの直下で実行するパターンもあるからです。
私はその辺についてはまだわからないので、詳しく知りたい方はQiitaで「Terraform ディレクトリ」と調べてみてください。多くの記事が出てくるはずです。
おわりに
本記事ではTerraformを勉強していくうえでつまづいたところをピックアップし、私なりの解釈も入れてみました。タイトルに「①」とついている通り、今回書ききれなかったものがあるのでそのうち書く予定です。
この記事が私と同じ初心者の方の助けになれば幸いです。最後までお読みいただきありがとうございました。