TerraformとWerckerを使って、git pushしたらEC2を立てる

## 概要

こんにちは。[HashiCorp Advent Calendar 2015](http://qiita.com/advent-calendar/2015/hashicorp) 12日目の@ikemonnです。
この記事では、[Wercker](http://wercker.com/)と[Terraform](https://terraform.io/)を使ってEC2を立てる方法について書きます。

フローとしては下記のような感じです。

1. 開発用のdevelopブランチにgit push
- pushをhookして、Wercker上でterraform plan
- terraform planが通ったら自動的にpull reqを作成
- pull reqがマージされたら、Wercker上でdeployボタンを押し、terraform applyしてEC2インスタンスをたてる

## 下準備
まず下記を済ませておきます。

1. Werckerでアカウント登録をする
2. Werckerで対象のリポジトリを登録する
3. curlを叩いてpull reqを作れるように[GitHubのアクセストークンを取得する](https://help.github.com/articles/creating-an-access-token-for-command-line-use/)
4. tfstateを管理するためのbucketをS3に用意する([Amazon S3 で Terraform の状態管理ファイル terraform.tfstate を管理 / 共有する - Qiita](http://qiita.com/dtan4/items/04632f1c2f35388a3283))

## Terraformの準備
今回作るのはt2.microのインスタンスです。
こんな感じでtfファイルを準備します。

```sample_ec2.tf
resource "aws_instance" "sample" {
    ami = "ami-908a2f90"
    instance_type = "t2.micro"
    availability_zone = "ap-northeast-1a"
    tags {
      Name = "terraform_sample"
    }
    vpc_security_group_ids = ["${var.default_security_group}"]
    key_name = "${var.lc_key_name}"
}
```

```terraform.tfvars
region = "ap-northeast-1"
default_security_group = “sg-xxxxxxx"
```

```common_variables.tf
variable "aws_access_key" {}
variable "aws_secret_key" {}
variable "region" {}
variable "default_security_group" {}
variable "lc_key_name" {}
provider "aws" {
  access_key = "${var.aws_access_key}"
  secret_key = "${var.aws_secret_key}"
  region = "${var.region}"
}
```

```環境変数
TF_VAR_aws_access_key=“youraccesskey"
TF_VAR_aws_secret_key=“yoursecretkey"
TF_VAR_lc_key_name=“yourkey"
```

開発者が複数人でも大丈夫なように、tfstateは先ほど準備したS3に置くことにします。

```
terraform remote config -backend=S3 -backend-config=“bucket=your-terraform-state-bucket" -backend-config="key=terraform.tfstate" -backend-config="encrypt=true"
```

ここで一度、ローカル環境でTerraformが実行できるか確認しておくと手戻りが少なくて良いと思います。

## Werckerの準備
先ほどWerckerをリポジトリに連携させたので、適当なブランチにpushするとrootディレクトリ直下にあるwercker.yamlで記述した処理が実行されます。

```ディレクトリ構造
.
├── terraform
│   ├── common_variables.tf
│   ├── sample_ec2.tf
│   └── terraform.tfvars
└── wercker.yml
```

今回はterraformをwgetでinstallして、それを使ってterraform planとapplyを実行します。
同時に、その他必要なものを一緒にinstallしておきます。
(buildとdeployの処理が冗長なので、boxやstepを利用するともっと簡潔に書けると思います)

```
box: debian
build:
  steps:
    - script:
        name: install some tools
        code: |
          sudo apt-get update
          sudo apt-get -f install
          sudo apt-get install -y wget unzip curl
    - script:
        name: setup terraform
        code: |
          # create terraform directory and export PATH
          mkdir -p $HOME/terraform
          export PATH=$PATH:$HOME/terraform
          # download terraform
          VERSION=0.6.8
          cd $HOME/terraform
          wget https://releases.hashicorp.com/terraform/${VERSION}/terraform_${VERSION}_linux_amd64.zip
          unzip terraform_${VERSION}_linux_amd64.zip
          rm terraform_${VERSION}_linux_amd64.zip
    - script:
        name: terraform remote config
        code: |
          cd terraform
          terraform remote config -backend=S3 -backend-config="bucket=your-terraform-state-bucket" -backend-config="key=terraform.tfstate" -backend-config="encrypt=true"
          terraform plan
    - script:
        name: create pull request
        code: |
          curl -u "[username]:${GITHUB_TOKEN}" -d '{"title": "Terraform plan Success","body": "You can merge this pull request","head": "[username]:[fromBranch]","base": "[toBranch]"}'  https://api.github.com/repos/[username]/[yourBranch]/pulls
deploy:
  steps:
    - script:
        name: install some tools
        code: |
          sudo apt-get update
          sudo apt-get -f install
          sudo apt-get install -y wget unzip curl
    - script:
        name: setup terraform
        code: |
          # create terraform directory and export PATH
          mkdir -p $HOME/terraform
          export PATH=$PATH:$HOME/terraform
          # download terraform
          VERSION=0.6.8
          cd $HOME/terraform
          wget https://releases.hashicorp.com/terraform/${VERSION}/terraform_${VERSION}_linux_amd64.zip
          unzip terraform_${VERSION}_linux_amd64.zip
          rm terraform_${VERSION}_linux_amd64.zip
    - script:
        name: terraform remote config
        code: |
          cd terraform
          terraform remote config -backend=S3 -backend-config="bucket=your-terraform-state-bucket" -backend-config="key=terraform.tfstate" -backend-config="encrypt=true"
          terraform plan
    - script:
        name: terraform apply
        code: |
          terraform apply

```

その後、Wercker上で[環境変数を設定できる](http://old-devcenter.wercker.com/articles/steps/variables.html)ので、awsのkeyやGitHubのトークンなど必要な環境変数を設定しておきます。

## git pushからデプロイまで

その後、適当なブランチにpushしてみましょう。
Werckerがpushを検知してbuildフェーズに書かれている処理を実行します。

<img width="725" alt="スクリーンショット 2015-12-12 16.29.16.png" src="https://qiita-image-store.s3.amazonaws.com/0/18573/734d1f56-d608-cb58-dbd9-c793823c81b0.png">


terraform planまで成功すると、devからmasterにpull reqが送られます。

<img width="601" alt="スクリーンショット 2015-12-12 14.30.01.png" src="https://qiita-image-store.s3.amazonaws.com/0/18573/b65d965f-d8e5-9f5a-f16a-e671f7e25d86.png">

pull reqをマージするとmergeされたことを検知して、masterブランチでbuildが走ります。

<img width="982" alt="スクリーンショット 2015-12-12 14.40.57.png" src="https://qiita-image-store.s3.amazonaws.com/0/18573/0f925a44-348b-c318-decf-fbbec4f8921a.png">

buildが成功したらdeployボタンを押します。
ここで、deployフェーズの処理にある`terraform apply`が実行されます。

<img width="965" alt="スクリーンショット 2015-12-12 16.17.16.png" src="https://qiita-image-store.s3.amazonaws.com/0/18573/cab217a6-b283-9bb8-a216-e2c1de6eb16e.png">

AWSコンソールを確認すると無事インスタンスができていることがわかります。

<img width="1043" alt="スクリーンショット 2015-12-12 16.32.57.png" src="https://qiita-image-store.s3.amazonaws.com/0/18573/a89c3cb2-e169-d9d6-9dd6-b8b716f31a47.png">



## 改善点

- Werckerの部分が冗長なのでBoxやStepを使ってすっきりさせたい
- terraform planの結果をpull reqのbodyに渡したい

## 参考
- [wercker の step を自作するクイックメモ - ようへいの日々精進XP](http://inokara.hateblo.jp/entry/2015/08/08/132035)