ようへいの日々精進XP

よかろうもん

【細かすぎて伝わらないかもしれない tips】 Terraform で変数 (variables) を動的に設定する方法

tl;dr

Terraform で変数を SSM Parameter Store とかに登録しておいて, terraform apply する時に取得したいというニーズがあったのでメモしておきます. 具体的には, Terraform コードにベタ書きしたくないし, 出来ればリポジトリにも晒しておきたくないようなセキュアな情報を含んだリソースを apply したい時に役立つんじゃないかと思います.

尚, 検証に利用した環境は以下の通りです. 最新でなくてごめんなさい.

$ terraform version
Terraform v0.11.14
+ provider.external v1.2.0

俺はこうした

その前に

SSM Parameter Store に変数の値を入れておきます. 今回はたまたま Slack の WebHook URL を variables.tf にベタ書きしたくなかったという具体的なニーズがありました.

$ pstore -put \
  -name=/slack_incomming_webhook_url/your-webhook-url \
  -value=https://hooks.slack.com/services/XXXXXX/YYYYYYYYYY/zzzzzzzzzzzzzzzzzzzz -secure

Parameter Store の操作には pstore というツールが便利です (自分が作ったんですけどね 😘).

Parameter Store に登録した値は以下のように取得出来ることが前提です.

$ pstore -get -name=/slack_incomming_webhook_url/your-webhook-url
https://hooks.slack.com/services/XXXXXX/YYYYYYYYYY/zzzzzzzzzzzzzzzzzzzz

External Data Source

そして, Terraform の External Data Source を利用します. この External Data Source は, 外部プログラムを実行してその実行結果を Terraform のプロバイダ/データソースとして利用できる機能で, 既に, Terraform v0.8.0 以降に組み込まれている機能です.

以下のように記述します.

data "external" "example" {
  program = ["bash", "${path}/${to}/example-data-source.sh"]

  query = {
    id = "abc123"
  }
}

program に指定する外部プログラムは以下のような仕様で実装する必要があります (ドキュメントより).

  • stdinJSON 文字列が渡される為, これを解析出来るようにすること
  • 出力は stdoutJSON フォーマットで出力すること
  • 正常終了は, ステータスコード 0, エラーが発生した場合には 0 以外のステータスコードを返し, 1 行のエラーメッセージを stderr に出力すること

これらの仕様を満たす為に, 以下のようなシェルスクリプトを書く必要があります. (めっちゃ雑に書いています)

#!/usr/bin/env bash
set -e

eval "$(jq -r '@sh "VAL=\(.id)"')"
ID="${VAL}"
jq -n --arg id "${ID}" '{"id":$id}'

Ruby だと以下のように書けますでしょうか.

#!/usr/bin/env ruby
require 'json'

hash = JSON.load(STDIN.read)
val = hash['id']
puts JSON.dump({'id': val})

ま, どっちでも良いですが, Terraform の変数に渡したい値を JSON で吐くということだけ注意しましょう. 試しにこれらのスクリプトを External Data Source として組み込んでみましょう.

data "external" "example" {
  program = ["ruby", "example.rb"]

  query = {
    id = "abc123"
  }
}

output "example1" {
  value = "${data.external.example.result}"
}

output "example2" {
  value = "${data.external.example.result["id"]}"
}

terraform apply すると以下のように出力されました.

$ terraform apply
data.external.example: Refreshing state...

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

example1 = {
  id = abc123
}
example2 = abc123

実際には

以下のようなシェルスクリプトを作った上で, Slack の Webhook URL を External Datasource として取得して変数に設定しました.

#!/usr/bin/env bash

# 引数のチェック
CMDNAME=$(basename $0)
if [ $# -lt 1 ]; then
  echo "Usage: ${CMDNAME} [parameter path name]" 1>&2
  exit 1
fi

# コマンドのチェック
for cmd in jq pstore
do
  which ${cmd} > /dev/null 2>&1
  if [ ! $? -eq 0 ];then
    echo "Please Install ${cmd}!!"
    exit 1
  fi
done

jq -n --arg webhook_url $(pstore -get -name=/slack_incomming_webhook_url/${1} -insecure) '{"webhook_url":$webhook_url}'

Terraform コードは以下の通りです.

data "external" "sample-external-data" {
  program = [ "bash", "scripts/webhook_url.sh", "sample-external-data" ]
}

# output resouce は念の為に出力を確認する為に定義
output "sample-external-data" {
  value = "${data.external.sample-external-data.result}"
}

resource sakuracloud_simple_monitor "http-monitor12" {
  target      = "xxx.example.com"
  description = "xxx.example.com monitoring"

  health_check {
    protocol       = "sslcertificate"
    remaining_days = 7
  }

  notify_email_enabled = false
  notify_slack_enabled = true
  notify_slack_webhook = "${data.external.sample-external-data.result.webhook_url}"
  enabled              = true
}

これにより, Terraform コード内にあまり晒したくない情報 (今回だと Slack の WebHook URL) を無理やり記述する必要は無くなりました...が...

デメリットもある

ドキュメントよると... External Data Source には以下のような注意書きがありました.

外部プログラムを介してデータソースを実装すると、利用できない可能性のある外部プログラムやライブラリに依存関係を作成することにより、Terraform構成の移植性が損なわれる可能性があります

Terraform を利用する上でのメリットの一つでもある, コードの汎用性が External Data Source を利用する際に実行されるプログラム (今回だと先述の bashruby スクリプト)や, それに付随するライブラリによって低下する可能性があるので, 用法用量は守って使う必要がありそうです.

確かに, 作り込んだ bash スクリプトが全ての OS や環境で動作するか保証出来ませんし, 動作しても必ずしも同じ結果を返すとは限りませんよね...

SSM Parameter Store ならば...

わざわざ External Data Source を使わなくても, 以下のような Data Source が用意されています!

www.terraform.io

こちらを利用した方が, 上述のようなデメリットは払拭出来ると思います.

以上

  • Terraform は奥深い

参考