countを使うことで簡単に同じリソースを複数作成できる。
元ネタは以下で作成した定義ファイル。
count
を使った個数指定
例えばn人分の環境を作りたい場合で、とにかく数を指定してその個数のリソースを作るというパターン。
countの指定
resource "aws_instance" "bastion" { count = 3 ami = data.aws_ssm_parameter.amzn2_ami.value instance_type = "t3.nano" key_name = aws_key_pair.my_key.id subnet_id = aws_subnet.prac_public.id security_groups = [aws_security_group.allow_ssh_icmp.id] associate_public_ip_address = true tags = { Name = "bastion" } }
count = 3
を追加している。
[zaki@cloud-dev practice (main)]$ ssh ec2-user@**.**.**.** -i ~/.ssh/id_aws_terraform The authenticity of host '**.**.**.** (**.**.**.**)' can't be established. ECDSA key fingerprint is SHA256:lWRWcx/vBbr3QHTyJf10zxImxSfxY0l1V41k/u/wrqM. ECDSA key fingerprint is MD5:7f:bd:48:2f:44:3a:8c:c3:cf:e3:6b:88:63:da:0e:c6. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added '**.**.**.**' (ECDSA) to the list of known hosts. __| __|_ ) _| ( / Amazon Linux 2 AMI ___|\___|___| https://aws.amazon.com/amazon-linux-2/ [ec2-user@ip-172-25-10-145 ~]$
ログインもちゃんとできる。
ただ、一覧で見てもわかるとおり、全て同じtag名になっている。
インデックス値参照
count.index
でいわゆるループ内のインデックス値を参照できる。
これをtag名に付与してやればOK。
tags = { Name = "bastion-${count.index}" }
これで連番をsuffixに付けることができた。
for_each
を使ったリスト指定
作成するリソースは同じだけど、用途が異なるためそれぞれ別の名前を付けたい、などのパターン。
例として「app」と「db」というEC2を作る場合。
リスト定義
ホスト一覧として、app
とdb
という文字列型のリスト(配列)を定義。
variable "host_list" { description = "host count" type = list(string) default = [ "app", "db", ] }
for_eachの指定
resource "aws_instance" "server" { for_each = toset(var.host_list) ami = data.aws_ssm_parameter.amzn2_ami.value instance_type = "t3.nano" key_name = aws_key_pair.my_key.id subnet_id = aws_subnet.prac_priv2.id security_groups = [aws_security_group.allow_ssh_icmp.id] associate_public_ip_address = false tags = { Name = each.value } }
count
と同じような要領で、for_each
を使ってホスト一覧を指定する。
for_each
を使うことで、リストの要素ごとにリソースが作成される。
また、それぞれホスト毎の名前を指定したいtag名については、each.value
を指定することで要素の内容に置き換わる。
残課題
「count数の環境ごとにfor_eachの要素を作成 (bastion/app/dbのセットをcount数分作る)」をやりたくて、
resource "aws_instance" "server" { for_each = toset(var.host_list) count = var.host_count ami = data.aws_ssm_parameter.amzn2_ami.value instance_type = "t3.nano" key_name = aws_key_pair.my_key.id subnet_id = aws_subnet.prac_priv2.id security_groups = [aws_security_group.allow_ssh_icmp.id] associate_public_ip_address = false tags = { Name = "${each.value}-${count.index}" } }
みたいに書いてみたけど、Terraformはcount
とfor_each
を併用できなかった。
[zaki@cloud-dev practice (main)]$ terraform plan ╷ │ Error: Invalid combination of "count" and "for_each" │ │ on ec2.tf line 26, in resource "aws_instance" "server": │ 26: for_each = toset(var.host_list) │ │ The "count" and "for_each" meta-arguments are mutually-exclusive, only one should be used to be explicit about the number of resources to be │ created. ╵
これはまた別のやり方で作る必要がある(未調査)
つかいわけ
同じサイズのコンピュートインスタンスを指定数分だけ並べたい程度であればcount
でよさそうだが、たとえばサブネットの定義みたいに、CIDRとかのパラメタが異なるものを複数作る必要がある場合は、ListやMapで定義してfor_each
を使う方がよさそう。