最近は Terraform で AWS リソースを作る → Ansible で EC2 インスタンスの中身をプロビジョニング、とすることが多いですが、Terraform で作った AWS リソースの情報を EC2 インスタンスの中で使いたいこと、よくあります。
例えば次のようなものです。
- RDS 接続情報
- SES クレデンシャル
ある程度の命名規則があったり固定の名前だったりするなら Terraform の tf ファイルと Ansible のプレイブックとかにベタに書いても良いと思いますが、ランダムで生成される場合、
- Terraform でリソースを作成
- 目で見て Ansible の inventory に仕込む
みたいにする必要があり泥臭いです。
これを解消する方法、いくつか考えられますが。。。
terraform-inventory
Terraform の tfstate ファイルをインベントリとして使う Ansible Dynamic Inventory です。
が、たぶんリモートバックエンドに対応していないため、使え無さそうです。
Terraform でリモートバックエンドを使わないなら、使えるのかも、しれない?
local-exec provisioner
Terraform の local-exec プロビジョナで、作成されたリソースの情報をローカルのファイルに書き出します。
resource "aws_iam_access_key" "smtp" {
user = "${aws_iam_user.smtp.name}"
provisioner "local-exec" {
command = "echo '${aws_iam_access_key.smtp.id}:${aws_iam_access_key.smtp.ses_smtp_password}' > smtp_credential.txt"
}
}
これ、使ったことないですけど、リソースを作成したときだけ実行されるものなので複数人でコードを共有している場合はダメな気がする。
あるいは local-exec プロビジョナで作成したファイルをリポジトリに含めるとか? SES クレデンシャルとかをどうにかしたいわけなのでそれは辛い。
remote-exec provisioner
Terraform の remote-exec プロビジョナで、作成された EC2 インスタンスにファイルを書き出します。
resource "aws_instance" "web" {
# ...
provisioner "remote-exec" {
inline = [
"echo '${aws_iam_access_key.smtp.id}:${aws_iam_access_key.smtp.ses_smtp_password}' > smtp_credential.txt",
]
}
}
これ、使ったことないですけど、Ansible 使わずに Terraform だけで完結させる、とかでない限りは併用は辛そう。
Terraform のリソースとしてどこかに保存
後から参照できる場所に Terraform のリソースとして保存します。
例えば S3 とか。
resource "aws_s3_bucket_object" "secret" {
bucket = "oreore-bucket"
key = "secret"
content = <<EOS
SMTP_USERNAME=${aws_iam_access_key.smtp.id}
SMTP_PASSWORD=${aws_iam_access_key.smtp.ses_smtp_password}
EOS
}
SSM のパラメータストアでも良いかも。
resource "aws_ssm_parameter" "secret" {
name = "/oreore/smtp/username"
type = "SecureString"
value = "${aws_iam_access_key.smtp.id}"
}
resource "aws_ssm_parameter" "secret" {
name = "/oreore/smtp/password"
type = "SecureString"
value = "${aws_iam_access_key.smtp.ses_smtp_password}"
}
EC2 インスタンスのユーザーデータに入れて /etc/environment に書き出す
EC2 インスタンスのユーザーデータに入れて cloud-init で /etc/environment とかに書き出す。
resource "aws_instance" "redmine" {
# ...
user_data = <<EOS
#cloud-config
timezone: "Asia/Tokyo"
write_files:
- path: "/etc/environment"
content: |
SMTP_USERNAME=${aws_iam_access_key.smtp.id}
SMTP_PASSWORD=${aws_iam_access_key.smtp.ses_smtp_password}
EOS
}
さいごに
S3 や SSM Parameter store に保存する方法は保存した値をどうやって使うかも考える必要がありますが、EC2 ユーザーデータからの /etc/environment なら Ansible から次のように簡単に参照できます。
- name: generate sasl_passwd
copy:
dest: /etc/postfix/sasl_passwd
mode: 0600
content: |
email-smtp.us-west-2.amazonaws.com:587 {{ansible_env.SMTP_USERNAME}}:{{ansible_env.SMTP_PASSWORD}}
notify:
- postmap sasl_passwd
- postfix restart
ただ、Terraform だと EC2 ユーザーデータに変更があるときはインスタンスを作り直そうとしてしまうのと、cloud-init による書き出しもインスタンスの開始時にしか行われないので、S3 や SSM Parameter store から値を取ってくる Dynamic Inventory があるならそっちのほうが運用性は高いかもしれません。