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
に指定する外部プログラムは以下のような仕様で実装する必要があります (ドキュメントより).
stdin
で JSON 文字列が渡される為, これを解析出来るようにすること- 出力は
stdout
に JSON フォーマットで出力すること - 正常終了は, ステータスコード
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 を利用する際に実行されるプログラム (今回だと先述の bash や ruby スクリプト)や, それに付随するライブラリによって低下する可能性があるので, 用法用量は守って使う必要がありそうです.
確かに, 作り込んだ bash スクリプトが全ての OS や環境で動作するか保証出来ませんし, 動作しても必ずしも同じ結果を返すとは限りませんよね...
SSM Parameter Store ならば...
わざわざ External Data Source を使わなくても, 以下のような Data Source が用意されています!
こちらを利用した方が, 上述のようなデメリットは払拭出来ると思います.
以上
- Terraform は奥深い