10分で理解するTerraform

# [Terraform](https://www.terraform.io/) とは
`インフラストラクチャ定義ツール` に分類されるツールで、クラウド上のリソースを定義ファイルの状態になるように生成・操作してくれます

インフラの構成を宣言的に定義できるところが特徴で、構築手順を書くのと違って構成定義のみに集中することができます
※`宣言的に定義できる` とは「t2.micro インスタンスを1つ」と書くだけで、作成手順を意識しなくてもその通りに出来上がるということです
単純にスクリプト化するだけだと、手続き的に構築手順を書く必要があります

Vagrant を開発している [HashiCorp社](https://www.hashicorp.com/) が開発しているツールなので使用感は Vagrant と同じような感じです


# TL;DR
.tf ファイルに定義内容を記述し、```$ terraform apply``` を実行するだけで定義内容の通りに自動でインフラを構築してくれます

# インストール
以下に従って OS 毎のビルド済みバイナリをダウンロードしてきてパスを通すだけ
https://www.terraform.io/intro/getting-started/install.html

## Mac の場合
Homebrew でインストールできます

```text:Homebrewでインストール
$ brew update
$ brew install terraform
```

# AWS での使い方
## 事前準備 
###  IAM ユーザーを作る
aws cli を利用するための アクセスキーを取得します
管理者に依頼して IAM ユーザーを作成してもらってください
https://console.aws.amazon.com/iam/home?#/home

利用したい AWS サービス(EC2、S3とか)に対しての操作権限をつけてもらう必要があります

### AWS CLI をセットアップする
以下を参考に aws コマンドを使えるように設定する
http://docs.aws.amazon.com/ja_jp/streams/latest/dev/kinesis-tutorial-cli-installation.html

```$ aws configure``` で認証情報をセットした後であれば自動でこのAWS認証情報を参照してくれます
※```~/.aws/credentials``` にAWSの認証情報が保存されます

## EC2インスタンスを作成する
## 1. .tf ファイルを作成する
### AWS の認証情報をセットする
```variables.tf``` ファイルを作成し、操作したいリージョン を以下のように記述します

``` variables.tf
provider "aws" {
  version = "~> 2.0"
  region  = "ap-northeast-1"
}
```

### EC2 のスペックを定義する

``` ec2.tf
resource "aws_instance" "sandbox" {
  count         = 2
  ami           = "ami-785c491f" # Ubuntu 16.04 LTS official ami
  instance_type = "t2.micro"

  tags = {
    Name = "${format("sandbox-%02d", count.index + 1)}"
  }
}
```

※ terraform v0.11 までは `tags` に `=` は不要でしたが、v0.12 より一部の構文が変更となり `tags =` と書くようになりました
(参考) https://www.terraform.io/upgrade-guides/0-12.html#attributes-vs-blocks 

上記は以下を表してます

- 2つインスタンスを生成
- AMI: ami-785c491f (Ubuntu 16.04 LST)
- t2.micro
- Name に "sandbox-xx"

## 2. 定義内容をチェックする
1 . ```variable.tf``` と ```ec2.tf``` が置かれているディレクトリ内で以下を実行

初めて実行する場合のみ初期化のために `init` を実行します

```text:init
$ terraform init
```

定義内容のチェックは `plan` を実行します

```text:plan
$ terraform plan
```

2 . 実行されるプランの内容が出力される
定義内容が意図した通りに反映されるかを確認します

```text:出力結果
〜
+ aws_instance.sandbox.0
    ami:                          "ami-785c491f"
〜
    tags.Name:                    "sandbox-01"
〜
+ aws_instance.sandbox.1
    ami:                          "ami-785c491f"
〜
    tags.Name:                    "sandbox-02"
〜
Plan: 2 to add, 0 to change, 0 to destroy.
```

## 3. 定義を適用してインスタンスを作成する

1 .  同ディレクトリ内で以下を実行

```text:apply
$ terraform apply
```

2 . 出力を確認
ずらーーーーーっと出るので、2オブジェクトが add されたことを確認

```text:出力結果
〜
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
〜
``` 
3 . terraform show を見てみる
terraform が作成したオブジェクトの内容が出力されます

```text:出力結果
$ terraform show
aws_instance.sandbox.0:
  id = i-02bbfece7cbf56027
〜
aws_instance.sandbox.1:
  id = i-0aad82a9b81951965
〜
``` 
4 . EC2 コンソールを確認
```sandbox-01``` ```sandbox-02``` の 2つのインスタンスが作成されていることが確認できます

![aws_console.png](https://qiita-image-store.s3.amazonaws.com/0/43699/5a06eb69-48ab-99de-8934-ccbdb71bedf2.png)


## 4. 定義内容を変更する
### 今あるインスタンスの設定値を変更する
1 . インスタンスの名称を ```modified sandbox-xx``` に変更する

```text:ec2.tf
〜
     Name = "${format("modified sandbox-%02d", count.index + 1)}"
〜
```

2 . 変更内容をチェック

```text:plan
$ terraform plan
〜
~ aws_instance.sandbox.0
    tags.Name: "sandbox-01" => "modified sandbox-01"
〜
```

tag の差分が表示されていることがわかります

3 . 変更を適用

```text:apply
$ terraform apply
〜
Apply complete! Resources: 0 added, 2 changed, 0 destroyed.
〜
```

4 . terraform show で確認

```text:show
$ terraform show
aws_instance.sandbox.0:
〜
  tags.Name = modified sandbox-01
〜
aws_instance.sandbox.1:
〜
  tags.Name = modified sandbox-02
〜
```

tag の変更が反映されてます

5 . ec2 コンソールを確認
コンソールからも変更が確認できます
![aws_console_2.png](https://qiita-image-store.s3.amazonaws.com/0/43699/ec6d0576-5385-31c0-71d9-a2749ef9c67d.png)


### インスタンスの数を変更する
1 . count を 1 に変更する

```text:ec2.tf
〜
  count         = 1
〜
```

2 . 変更内容をチェック

```text:plan
$ terraform plan
〜
- aws_instance.sandbox.1

Plan: 0 to add, 0 to change, 1 to destroy.
```
sandbox.1 が削除されるということがわかります

3 . 変更を適用

```text:apply
$ terraform apply
〜
Apply complete! Resources: 0 added, 0 changed, 1 destroyed.
```
sandbox.1 が削除されたログが出力されています

4 . ec2 コンソールを確認
```modified sandbox-02``` が削除されたことがわかります
![aws_console_3.png](https://qiita-image-store.s3.amazonaws.com/0/43699/1537c45b-b1e8-efb5-059b-634f69f78321.png)


## 5. terraform で定義したリソースを全て削除する
1 . terraform destroy 
確認を促すプロンプトが出るので "yes" を入力すると削除が開始されます

```text:destroy
$ terraform destroy
Do you really want to destroy?
  Terraform will delete all your managed infrastructure.
  There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value: yes

aws_instance.sandbox: Refreshing state... (ID: i-02bbfece7cbf56027)
aws_instance.sandbox: Destroying... (ID: i-02bbfece7cbf56027)
aws_instance.sandbox: Still destroying... (ID: i-02bbfece7cbf56027, 10s elapsed)
aws_instance.sandbox: Still destroying... (ID: i-02bbfece7cbf56027, 20s elapsed)
aws_instance.sandbox: Still destroying... (ID: i-02bbfece7cbf56027, 30s elapsed)
aws_instance.sandbox: Still destroying... (ID: i-02bbfece7cbf56027, 40s elapsed)
aws_instance.sandbox: Still destroying... (ID: i-02bbfece7cbf56027, 50s elapsed)
aws_instance.sandbox: Destruction complete

Destroy complete! Resources: 1 destroyed.
```

2 . terraform show で確認

```text:show
$ terraform show

```
管理下のオブジェクトが何もないことが確認できます

3 . ec2 コンソールで確認
![ec2.png](https://qiita-image-store.s3.amazonaws.com/0/43699/41a29df9-5dc1-4f10-315e-453d72ae018a.png)

全て削除されていることがわかります