TerraformでEC2インスタンスを構築してみた。（Terraform, AWS, EC2）

#Terraformとは
インフラ構築や設定をコードを使ってするためのものみたいです。
EC2を構築するときに、Terraformを使うと、効率よく構築することができます。
１個テンプレートを作ってしまえば、他のEC2を構築するときに使いまわせます。


#やること
- ローカル（Mac）にTerraformをインストール
- Terraformを利用して、AWSにtestEC2という名前のインスタンスを作成する


#前提
- ローカルにXcodeインストール済み
- IAMを登録済み（AWSを操作するために、ACCESS_KEY,SECRET_KEYが必要になります。）
- セキュリティグループは既存のものを適用します
- キーペアを作成済み


#Terraformのインストール
1.Homebrewのインストール

```
$ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
```


2.バージョン確認

```
$ brew -v
Homebrew 1.1.11
```


3.Terraformインストール

```
$ brew install terraform
==> Downloading https://homebrew.bintray.com/bottles/terraform-0.9.1_1.sierra.bo
######################################################################## 100.0%
==> Pouring terraform-0.9.1_1.sierra.bottle.tar.gz
==> Caveats
zsh completion has been installed to:
  /usr/local/share/zsh/site-functions
==> Summary
🍺  /usr/local/Cellar/terraform/0.9.1_1: 7 files, 131.6MB
```


4.バージョン確認

```
$ terraform --version
Terraform v0.9.1
```


#テンプレートファイルの作成
1.作業用ディレクトリを作成

```
$ mkdir terraform
```

2.テンプレートファイルの作成
>Terraformのテンプレートファイルの拡張子は```.tf```です。

```
$ cd terraform
$ touch main.tf
```

#プロバイダーの設定を記載
>Terraformでは、様々なプロバイダーをサポートしていますので最初にどのプロバイダーを使用するのかを定義します。

```
$ vi main.tf
provider "aws" {
    access_key = "ACCESS_KEY"
    secret_key = "SECRET_KEY"
    region = "ap-northeast-1"
}
```


#リソースの設定
>Terraformは、リソースブロック毎に様々な設定を行なっていきます。
>VPCを作りたい場合は```aws_vpc```、EC2の場合は```aws_instance```と定義します。
>構文は、```resource "リソースの種類" "リソースの名前"```です。
>リソースを定義した後に、そのリソースの設定を行なっていくという流れです。
>リソースの詳細はこちら→https://www.terraform.io/docs/providers/aws/index.html

```
$ vi main.tf
resource "aws_instance" "testEC2" {　　　
    ami = "ami-eec1c380"　　
    instance_type = "t2.micro"　　　
    key_name = "AWS-takahashi"　　　
    vpc_security_group_ids = [
      "sg-161a9f71"　　　
    ]
    associate_public_ip_address = "true"
    root_block_device = {
      volume_type = "gp2"
      volume_size = "20"
    }
    ebs_block_device = {
      device_name = "/dev/sdf"
      volume_type = "gp2"
      volume_size = "10"
    }
    tags {
        Name = "testEC2"
    }
}
 
output "public ip of testEC2" {
  value = "${aws_instance.testEC2.public_ip}"
```

###全体はこんな感じ

```
$ cat main.tf
provider "aws" {
    access_key = "任意のアクセスキー"
    secret_key = "任意のシークレットキー"
    region = "ap-northeast-1"
}
 
resource "aws_instance" "testEC2" {
    ami = "ami-eec1c380"
    instance_type = "t2.micro"
    key_name = "AWS-takahashi"
    vpc_security_group_ids = [
      "sg-161a9f71"
    ]
    associate_public_ip_address = "true"
    root_block_device = {
      volume_type = "gp2"
      volume_size = "20"
    }
    ebs_block_device = {
      device_name = "/dev/sdf"
      volume_type = "gp2"
      volume_size = "10"
    }
    tags {
        Name = "testEC2"
    }
}
 
output "public ip of testEC2" {
  value = "${aws_instance.testEC2.public_ip}"
}

```


#AWS上に構築！
1.その前に、テンプレートに間違いがないか確認します。
>**ブロックに設定したパラメータ誤りについてのみのチェック**
>存在しない、amiなどを指定しても、```terraform plan```実行時にはエラーになりません・・・！

```
$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.

The Terraform execution plan has been generated and is shown below.
Resources are shown in alphabetical order for quick scanning. Green resources
will be created (or destroyed and then created if an existing resource
exists), yellow resources are being changed in-place, and red resources
will be destroyed. Cyan entries are data sources to be read.

Note: You didn't specify an "-out" parameter to save this plan, so when
"apply" is called, Terraform can't guarantee this is what will execute.

+ aws_instance.testEC2
    ami:                                               "ami-eec1c38"
    associate_public_ip_address:                       "true"
    availability_zone:                                 "<computed>"
    ebs_block_device.#:                                "1"
    ebs_block_device.2659407853.delete_on_termination: "true"
    ebs_block_device.2659407853.device_name:           "/dev/sdf"
    ebs_block_device.2659407853.encrypted:             "<computed>"
    ebs_block_device.2659407853.iops:                  "<computed>"
    ebs_block_device.2659407853.snapshot_id:           "<computed>"
    ebs_block_device.2659407853.volume_size:           "10"
    ebs_block_device.2659407853.volume_type:           "gp2"
    ephemeral_block_device.#:                          "<computed>"
    instance_state:                                    "<computed>"
    instance_type:                                     "t2.micro"
    ipv6_addresses.#:                                  "<computed>"
    key_name:                                          "AWS-takahashi"
    network_interface_id:                              "<computed>"
    placement_group:                                   "<computed>"
    private_dns:                                       "<computed>"
    private_ip:                                        "<computed>"
    public_dns:                                        "<computed>"
    public_ip:                                         "<computed>"
    root_block_device.#:                               "1"
    root_block_device.0.delete_on_termination:         "true"
    root_block_device.0.iops:                          "<computed>"
    root_block_device.0.volume_size:                   "20"
    root_block_device.0.volume_type:                   "gp2"
    security_groups.#:                                 "sg-161a9f71"
    source_dest_check:                                 "true"
    subnet_id:                                         "<computed>"
    tags.%:                                            "1"
    tags.Name:                                         "testEC2"
    tenancy:                                           "<computed>"
    vpc_security_group_ids.#:                          "<computed>"


Plan: 1 to add, 0 to change, 0 to destroy.
```


2.構築開始！

```
$ terraform apply
```

>ちなみに、存在しないamiを指定するとこんなエラーが出ます！

```
Error applying plan:

1 error(s) occurred:

* aws_instance.testEC2: 1 error(s) occurred:

* aws_instance.testEC2: InvalidAMIID.NotFound: The image id '[ami-takahashiiiii]' does not exist
	status code: 400, request id: 6a9d7118-5f23-4cae-9521-0ccc47cc775e

Terraform does not automatically rollback in the face of errors.
Instead, your Terraform state file has been partially updated with
any resources that successfully completed. Please address the error
above and apply again to incrementally change your infrastructure.
```

3.AWSコンソール上で確認！
>作成されていました！
<img width="1280" alt="スクリーンショット 2017-03-26 19.25.47.png" src="https://qiita-image-store.s3.amazonaws.com/0/168635/31280edc-4d2f-5bc7-d785-554a4b2e8777.png">




#アウトプット
> main.tfの最下部に、outputというブロックがありますが、これは構築した際に知りたい情報をコンソールに表示するというものです。
>今回は、作成した際にIPアドレスを表示するようにしてみました。
>構文は、```output "<アウトプットする属性の説明>" { value = "<アウトプットする属性値>"}```

>こんな感じ↓で書くと。。。

```
$ vi main.tf
output "public ip of testEC2" {
  value = "${aws_instance.testEC2.public_ip}"
```


>こんな感じ↓になりました！

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

Outputs:

public ip of testEC2 = 54.250.156.45```
```


#おわりに
コマンド一個でEC2が作成されるのは気持ち良いですね！
他にもテンプレートファイルを分けたり、変数を使ってアクセスキー、シークレットキーなどを別ファイルに記載しておくなどできるみたいです。
今回はEC2の作成のみを行いましたが、今度はvpcやセキュリティグループの作成をしてみます！