このブログをご覧のみなさん、こんにちは。

前回に引き続き Terraform を使ってみたので、その概要をまとめてみました。

以下は HashiCorp の公式サイトにある Terraform の Getting Started を実践した内容になります。

Environment

  • Mac Book Pro
    • OS X Yosemite 10.10.5
  • Terraform 0.7.1

Terraform Remote

Terraform はインフラを構築・破壊 (terraform apply, destroy) するとその状態を terraform.tfstate というファイルに書き出します。通常、同じインフラに対して terraform apply を2回実行しても terraform apply によってインフラが構築されるのは1回だけですが、 terraform.tfstate ファイルを共有しないまま同じ環境に対して terraform apply を行うと同じ設定のインフラを構築します。もし、一意制約に該当する場合は2回目の構築時にエラーになります。それを避けるために terraform.tfstate をリモートで管理し、環境に関する状態を共有する仕組みが Terraform Remote になります。しかし、 Terraform Remote の手順に従って、これを実行すると…。

$ terraform remote config -backend-config="name=changeworld/getting-started"
Remote configuration updated
Error while performing the initial pull. The error message is shown
below. Note that remote state was properly configured, so you don't
need to reconfigure. You can now use `push` and `pull` directly.
Error reloading remote state: Unexpected HTTP response code: 400
Body: {"errors":["This feature requires Terraform Enterprise. Please contact sales@hashicorp.com to start or extend your trial."],"success":false}

となります。これは以前は Getting Started の Terraform Remote で説明されている Atlas が無料で使えたのですが、現在は有料でしか使えないためです。 Terraform Enterprise を利用せずにすむ方法は調べたので、別途記載します。

Resource Dependencies

ここまでは1つのリソースの作成・変更・削除でしたが、複数のリソースを指定した場合は、適用順序を制御する必要があります。というわけで、 Resource Dependencies の手順に従って、AWS のインスタンスに Elastic IP を紐付ける構築をします。記載の通りに修正し terraform plan を実行すると、実行計画を確認できます。

$ terraform plan
:
+ aws_eip.ip
    allocation_id: "<computed>"
    association_id: "<computed>"
    domain: "<computed>"
    instance: "${aws_instance.example.id}"
    network_interface: "<computed>"
    private_ip: "<computed>"
    public_ip: "<computed>"
+ aws_instance.example
    ami: "ami-13be557e"
    availability_zone: "<computed>"
    ebs_block_device.#: "<computed>"
    ephemeral_block_device.#: "<computed>"
    instance_state: "<computed>"
    instance_type: "t2.nano"
    key_name: "<computed>"
    network_interface_id: "<computed>"
    placement_group: "<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>"
    tenancy: "<computed>"
    vpc_security_group_ids.#: "<computed>"

Plan: 2 to add, 0 to change, 0 to destroy.

構築時に、Elastic IP リソースに指定されている EC2 インスタンス ID を検出し、 EC2 インスタンスを先に作成しています。

$ terraform apply
aws_instance.example: Creating…
  ami: "" => "ami-13be557e"
  availability_zone: "" => "<computed>"
  ebs_block_device.#: "" => "<computed>"
  ephemeral_block_device.#: "" => "<computed>"
  instance_state: "" => "<computed>"
  instance_type: "" => "t2.nano"
  key_name: "" => "<computed>"
  network_interface_id: "" => "<computed>"
  placement_group: "" => "<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>"
  tenancy: "" => "<computed>"
  vpc_security_group_ids.#: "" => "<computed>"
aws_instance.example: Still creating… (10s elapsed)
aws_instance.example: Still creating… (20s elapsed)
aws_instance.example: Creation complete
aws_eip.ip: Creating…
  allocation_id: "" => "<computed>"
  association_id: "" => "<computed>"
  domain: "" => "<computed>"
  instance: "" => "i-452dcad4"
  network_interface: "" => "<computed>"
  private_ip: "" => "<computed>"
  public_ip: "" => "<computed>"
aws_eip.ip: Creation complete

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

The state of your infrastructure has been saved to the path
below. This state is required to modify and destroy your
infrastructure, so keep it safe. To inspect the complete state
use the `terraform show` command.

State path: terraform.tfstate

Provision

Provision はインスタンスを構築後に実行する処理を指定します。 Provision の手順に従って、インスタンスを構築後にサーバー上で IP アドレスをファイル出力させます。

$ terraform apply
aws_instance.example: Creating…
  ami: "" => "ami-13be557e"
  availability_zone: "" => "<computed>"
  ebs_block_device.#: "" => "<computed>"
  ephemeral_block_device.#: "" => "<computed>"
  instance_state: "" => "<computed>"
  instance_type: "" => "t2.nano"
  key_name: "" => "<computed>"
  network_interface_id: "" => "<computed>"
  placement_group: "" => "<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>"
  tenancy: "" => "<computed>"
  vpc_security_group_ids.#: "" => "<computed>"
aws_instance.example: Still creating… (10s elapsed)
aws_instance.example: Still creating… (20s elapsed)
aws_instance.example: Still creating… (30s elapsed)
aws_instance.example: Provisioning with 'local-exec'…
aws_instance.example (local-exec): Executing: /bin/sh -c "echo 54.167.38.210 > file.txt"
aws_instance.example: Creation complete
aws_eip.ip: Creating…
  allocation_id: "" => "<computed>"
  association_id: "" => "<computed>"
  domain: "" => "<computed>"
  instance: "" => "i-5934d3c8"
  network_interface: "" => "<computed>"
  private_ip: "" => "<computed>"
  public_ip: "" => "<computed>"
aws_eip.ip: Creation complete

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

The state of your infrastructure has been saved to the path
below. This state is required to modify and destroy your
infrastructure, so keep it safe. To inspect the complete state
use the `terraform show` command.

State path: terraform.tfstate
$ cat file.txt
54.167.38.210

Input Variables

ここまでの例では AWS のアクセスキーやシークレットキーなどを example.tf に直接設定していました。しかし、これは実運用では著しく問題があります。 Terraform には別ファイルやコマンド実行時の変数、環境変数から変数を渡すことができます。

Input Variables の手順に従って、実行してみましょう。

上記に記載の方法以外に

  • 変数ファイル terraform.tfvars という名前のファイル内変数は、変数として自動参照
  • 環境変数 TF_VAR_<変数名> の環境変数は、変数として自動参照

があります。

これ以外に Output Variables, Modules もありますが、ちょっと使う程度であれば見る必要はありません。