woshidan's blog

そんなことよりコードにダイブ。

Terraformで外部の設定ファイルから設定を読み込む(file, template_file, template_cloudinit_config)

今日はとりあえずTerraformのTemplateプロバイダーを少しさわってみようと思います*1

TerraformのTemplateプロバイダーには、データリソース*2として

  • template_file
  • template_cloudinit_config

リソースとして

  • template_dir*3

がありますが、今回は template_file を試してみます。

また、これらの前にファイル利用つながりで file 組み込み関数もためして、 template_cloudinit_config データリソースにも少しだけふれます。

TL;DR

  • 外部に書いた設定ファイルの内容を file 組み込み関数で読み出す
  • template_file データリソースを使って一部の設定を変数で置き換えられるようにする
  • EC2の起動時の設定で userdatacloud-init の両方を使う場合は template_cloudinit_config データリソースを使うと書きやすい

外部に書いた設定ファイルの内容を file 組み込み関数で読み出す

TerraformでAWSのリソースの設定を書いていると、JSONなどHCLで書きにくい部分があると思います。 そういった部分を外部ファイルに書き出して、 file 組み込み関数で必要な箇所で読みだすことができます。

たとえば、

resource "aws_iam_role" "test_role" {
  name = "test_role"
  assume_role_policy = <<EOF
{
    "Version": "2012-10-17",
    "Statement": [
    {
       "Action": "sts:AssumeRole",
       "Principal": {
         "Service": "ec2.amazonaws.com"
        },
        "Effect": "Allow",
        "Sid": ""
    }
    ]
}
EOF
}

というtfファイルを書いた時、 <<EOF から EOF の部分を外部ファイルに抜き出して、

{
    "Version": "2012-10-17",
    "Statement": [
    {
       "Action": "sts:AssumeRole",
       "Principal": {
         "Service": "ec2.amazonaws.com"
        },
        "Effect": "Allow",
        "Sid": ""
    }
    ]
}
resource "aws_iam_role" "test_role" {
  name = "test_role"
  assume_role_policy = "${file("./test_policy.json")}"
}

のように書くことができます。こうすることで、設定の方もシンタックスハイライトが効く環境で編集できるので捗りそうですね。

template_fileデータリソースを使って一部の設定を変数で置き換えられるようにする

設定は外部ファイルで書きたいのだけれど、設定の中で一部だけ変数を利用したい、という場合があります。 この場合は template_file データリソースを利用すると便利です。

たとえば、

resource "aws_iam_policy" "test_role_app" {
  name = "test_role_app"

  policy = "${file("./test_policy.json")}"
}
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ecs:Describe*",
        "ecs:List*"
        ],
      "Resource": "arn:aws:ecs:region:account:cluster/my-cluster"
    }
  ]
}

のようなIAMポリシーがあったとして、対象のリソースだけを変えて同じようなアクションを許可するポリシーを他にも作りたい場合、template_file データリソースが使えます。

// ファイル名は test_policy.json から test_policy.json.tpl に変更
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ecs:Describe*",
        "ecs:List*"
        ],
      "Resource": "arn:aws:ecs:region:account:cluster/${cluster_name}"
    }
  ]
}

のように外部設定ファイルを編集すると、 ${cluster_name} の部分を template_file データリソースのブロック中で設定できます。

data "template_file" "test_policy" {
  template = "${file("./test_policy.json.tpl")}"

  vars {
    cluster_name = "my_cluster"
  }
}

resource "aws_iam_policy" "test_role_app" {
  name = "test_role_app"

  policy = "${data.template_file.test_policy.rendered}"
}

EC2の起動時の設定でuserdataとcloud-initの両方を使う場合は template_cloudinit_config データリソースを使うと書きやすい

先ほどの template_file の設定は、EC2の起動時の設定で userdata を利用してECSのクラスターを指定する、といった設定の一部だけ置き換えたい、といった場合に便利そうですが、 ECSの設定は userdata だけでなく cloud-init を利用して行うことも可能です*4

そして、単純に userdatacloud-init のそれぞれの設定を書いた場合、両者の設定を共存させることはできません。

dev.classmethod.jp

userdatacloud-init の設定を共存させたい場合、両者の設定をマルチパート形式のテキストに変換し、userdata に書き込む必要があります。

これはなかなか大変ですが、 template_cloudinit_config データリソースの part 属性を利用するとこの変換作業を行う必要がなくなります。

www.terraform.io

現場からは以上です。

*1:今日からコマンドじゃなくてソフトウェアとしてのTerraformはTerraformと表記することにする

*2:プロバイダのページの見出し(たとえば、Templateプロバイダ https://www.terraform.io/docs/providers/template/index.html )ではData Sourcesと書いてあるが、だいたい日本語だとデータリソースと出てくる(たとえば、Terraform for さくらのクラウド データリソースとは https://sacloud.github.io/terraform-provider-sakuracloud/configuration/resources/data_resource/ )

*3:http://febc-yamamoto.hatenablog.jp/entry/2018/02/05/193735 の記事がTemplateプロバイダーのデータリソース、リソースについて詳しいです

*4:https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/user-data.html#user-data-cloud-init