LoginSignup
3
3

More than 5 years have passed since last update.

クソみたいなリソースのterraform定義

Posted at

なるべくvariableで定義した内容で構築出来るようにしたかったので、クソみたいなリソース定義になったけどある程度設定で定義出来る形を考えてみた。

定義例

instance.tf
variable "project" {
  type = "map"
  default {
    prefix = "main"
  }
}

variable "ec2_list" {
  type = "list"
  default = [
    "bastion-1",
    "bastion-2",
  ]
}

variable "ec2_options" {
  type = "map"
  default {
    // デフォルトの定義
    default.ami = "ami-ceafcba8"
    default.availability_zone = "ap-northeast-1a"
    default.instance_type = "t2.micro"
    default.key_name = "default"

    default.tags_keys  = "amirotate"
    default.tags_value = "{\"NoReboot\": true, \"Retention\": {\"Count\": 2}}"

    // グループ "bastion" の定義
    bastion.tags_keys  = "Type"
    bastion.tags_value = "bastion"

    // インスタンス "bastion-1" の定義
    bastion-1.group = "bastion"
    bastion-1.tags_keys  = "Mode"
    bastion-1.tags_value = "master"

    // インスタンス "bastion-2" の定義
    bastion-2.group = "bastion"
    bastion-2.tags_keys  = "Mode"
    bastion-2.tags_value = "standby"
    bastion-2.availability_zone = "ap-northeast-1c"
  }
}


resource "aws_instance" "main" {
    count = "${length(var.ec2_list)}"

    ami = "${lookup(
        var.ec2_options,
        "${element(var.ec2_list, count.index)}.ami",
        lookup(
            var.ec2_options,
            "${lookup(var.ec2_options, "${element(var.ec2_list, count.index)}.group")}.ami",
            lookup(var.ec2_options, "default.ami", "false")
        )
    )}"

    instance_type = "${lookup(
        var.ec2_options,
        "${element(var.ec2_list, count.index)}.instance_type",
        lookup(
            var.ec2_options,
            "${lookup(var.ec2_options, "${element(var.ec2_list, count.index)}.group")}.instance_type",
            lookup(var.ec2_options, "default.instance_type", "false")
        )
    )}"
    key_name = "${lookup(
        var.ec2_options,
        "${element(var.ec2_list, count.index)}.key_name",
        lookup(
            var.ec2_options,
            "${lookup(var.ec2_options, "${element(var.ec2_list, count.index)}.group")}.key_name",
            lookup(var.ec2_options, "default.key_name", "default")
        )
    )}"
    availability_zone = "${lookup(
        var.ec2_options,
        "${element(var.ec2_list, count.index)}.availability_zone",
        lookup(
            var.ec2_options,
            "${lookup(var.ec2_options, "${element(var.ec2_list, count.index)}.group")}.availability_zone",
            lookup(var.ec2_options, "default.availability_zone", "ap-northeast-1a")
        )
    )}"

    tags = "${merge(
        map("Name", "${lookup(var.project, "prefix", "default")}-${element(var.ec2_list, count.index)}"),
        zipmap(
            compact(split("#", lookup(var.ec2_options, "${element(var.ec2_list, count.index)}.tags_keys", ""))),
            compact(split("#", lookup(var.ec2_options, "${element(var.ec2_list, count.index)}.tags_value", "")))
        ),
        zipmap(
            compact(split("#", lookup(var.ec2_options, "${lookup(var.ec2_options, "${element(var.ec2_list, count.index)}.group")}.tags_keys", ""))),
            compact(split("#", lookup(var.ec2_options, "${lookup(var.ec2_options, "${element(var.ec2_list, count.index)}.group")}.tags_value", "")))
        ),
        zipmap(
            compact(split("#", lookup(var.ec2_options, "default.tags_keys", ""))),
            compact(split("#", lookup(var.ec2_options, "default.tags_value", "")))
        )
    )}"
}

planはこんな感じに。

  + aws_instance.main[0]
      id:                           <computed>
      ami:                          "ami-ceafcba8"
      associate_public_ip_address:  <computed>
      availability_zone:            "ap-northeast-1a"
      ebs_block_device.#:           <computed>
      ephemeral_block_device.#:     <computed>
      instance_state:               <computed>
      instance_type:                "t2.micro"
      ipv6_address_count:           <computed>
      ipv6_addresses.#:             <computed>
      key_name:                     "default"
      network_interface.#:          <computed>
      network_interface_id:         <computed>
      placement_group:              <computed>
      primary_network_interface_id: <computed>
      private_dns:                  <computed>
      private_ip:                   <computed>
      public_dns:                   <computed>
      public_ip:                    <computed>
      root_block_device.#:          <computed>
      security_groups.#:            <computed>
      source_dest_check:            "true"
      subnet_id:                    <computed>
      tags.%:                       "4"
      tags.Mode:                    "master"
      tags.Name:                    "main-bastion-1"
      tags.Type:                    "bastion"
      tags.amirotate:               "{\"NoReboot\": true, \"Retention\": {\"Count\": 2}}"
      tenancy:                      <computed>
      volume_tags.%:                <computed>
      vpc_security_group_ids.#:     <computed>

  + aws_instance.main[1]
      id:                           <computed>
      ami:                          "ami-ceafcba8"
      associate_public_ip_address:  <computed>
      availability_zone:            "ap-northeast-1c"
      ebs_block_device.#:           <computed>
      ephemeral_block_device.#:     <computed>
      instance_state:               <computed>
      instance_type:                "t2.micro"
      ipv6_address_count:           <computed>
      ipv6_addresses.#:             <computed>
      key_name:                     "default"
      network_interface.#:          <computed>
      network_interface_id:         <computed>
      placement_group:              <computed>
      primary_network_interface_id: <computed>
      private_dns:                  <computed>
      private_ip:                   <computed>
      public_dns:                   <computed>
      public_ip:                    <computed>
      root_block_device.#:          <computed>
      security_groups.#:            <computed>
      source_dest_check:            "true"
      subnet_id:                    <computed>
      tags.%:                       "4"
      tags.Mode:                    "standby"
      tags.Name:                    "main-bastion-2"
      tags.Type:                    "bastion"
      tags.amirotate:               "{\"NoReboot\": true, \"Retention\": {\"Count\": 2}}"
      tenancy:                      <computed>
      volume_tags.%:                <computed>
      vpc_security_group_ids.#:     <computed>

使用している技

lookupでの値取得

lookup(<変数名>, <mapキー名>, <デフォルト値>)でmapに定義したデータを引っ張る。
これを多段にする事で、bastion-1.amibastion.amidefault.amiのような優先順位で設定値を取っている。

    ami = "${lookup(
        var.ec2_options,
        "${element(var.ec2_list, count.index)}.ami",
        lookup(
            var.ec2_options,
            "${lookup(var.ec2_options, "${element(var.ec2_list, count.index)}.group")}.ami",
            lookup(var.ec2_options, "default.ami", "false")
        )
    )}"

グループの値を取得しているときにデフォルト値にdefaultと入れてdefault.amiを引っ張らせようとしたけど、なぜか上手く展開してくれなくて泣いた。

動的なmap構築

タグには役割とか各種ツール用の情報入れたりをするんだけども、一つ一つで書くのが面倒なので、全体定義・グループ定義・個別定義をマージしたmapを作るように書いてみた。

    tags = "${merge(
        map("Name", "${lookup(var.project, "prefix", "default")}-${element(var.ec2_list, count.index)}"),
        zipmap(
            compact(split("#", lookup(var.ec2_options, "${element(var.ec2_list, count.index)}.tags_keys", ""))),
            compact(split("#", lookup(var.ec2_options, "${element(var.ec2_list, count.index)}.tags_value", "")))
        ),
        zipmap(
            compact(split("#", lookup(var.ec2_options, "${lookup(var.ec2_options, "${element(var.ec2_list, count.index)}.group")}.tags_keys", ""))),
            compact(split("#", lookup(var.ec2_options, "${lookup(var.ec2_options, "${element(var.ec2_list, count.index)}.group")}.tags_value", "")))
        ),
        zipmap(
            compact(split("#", lookup(var.ec2_options, "default.tags_keys", ""))),
            compact(split("#", lookup(var.ec2_options, "default.tags_value", "")))
        )
    )}"

mergeはそのまんまで、羅列したmapをマージしてくれるので定義別にmapを渡してあげれば良い。
でも、map定義の中で値がstringのものとmapのものを混ぜるとエラーになってしまうので、キーと値を別々にstringで定義してsplitで分割。今回は途中にカンマを入れたかったので#を区切り文字として書いてみた。
splitで作成したlistをzipmapでmapに変換している。

で、定義が無い時は空文字列を取ってくるようにしているけど、これをそのままsplitに通すと空文字が含まれたリストになって、zipmapでmap化する際に空文字キーと空文字値のmapになってしまうので、compactに通して空リストにしてあげている。

空リスト同士でzipmapを通すと空mapになるので、mergeを通した時は無視されて、定義されている部分のみがちゃんとmapになる。

思った事

最初の定義は大変だけど、terraformをあまり知らない人にちょっとした修正をお願いする時とかにはvariables.tfのここ直してーと言えば済むのが良いとは思うんだけど、あまりに読みづらい。

あとinterpolation組み合わせまくるの大変なので関数定義ほしい。

参考資料

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3