Terraformハンズオン~基本編~

# はじめに
SREチームでTerraformを利用していくにあたって、周囲の理解が必要なことがありますよね。
この記事の背景として、社内に理解者を増やして、ツールの導入をスムーズにしたいという想いがあります。
ということで、社内のサーバーサイドエンジニアのようなインフラ系専門エンジニアではない人向けに社内Terraform共有会を開催する目的で書いています。

# 目次
- [Infrastructure as Codeとは何か](https://qiita.com/drafts/c214d6fef0e92acc9549/edit#infrastructure-as-code%E3%81%A8%E3%81%AF%E4%BD%95%E3%81%8B)
- [Terraformの説明](https://qiita.com/drafts/c214d6fef0e92acc9549/edit#terraform%E3%81%AE%E8%AA%AC%E6%98%8E)
- [Terraformハンズオン](https://qiita.com/drafts/c214d6fef0e92acc9549/edit#terraform%E3%83%8F%E3%83%B3%E3%82%BA%E3%82%AA%E3%83%B3)

# Infrastructure as Codeとは何か
**What**
オライリー・ジャパンから出版されている『Infrastructure as Code』より引用するとIaCは以下のような定義となる。

> Infrastructure as Codeは、**ソフトウェア開発のプラクティスをインフラのオートメーションに活かす**アプローチだ。[^1]

**Why**
どうしてInfrastructure as Codeを行うかというと以下のような効果を期待するためだ。

> ● ITインフラが変化の障害や制約になるのではなく、**変化を支えて実現する**こと。
● システム変更がユーザーやITスタッフのストレスや大事件ではなく、**日常作業**になること。
● ITスタッフが、日常的反復的なタスクではなく、**彼らの能力を活かした価値のある仕事のために時間を使うこと**。
● ユーザーがITスタッフに頼るのではなく、**自分で必要なリソースを定義、プロビジョニング、管理できるようになる**こと。
● 失敗を完全に予防できるという前提に立つのではなく、**失敗が発生しても簡単かつスピーディに回復できるようになる**こと。
● コストがかかりリスキーな「ビッグバン」プロジェクトではなく、**継続的な作業により改善、改良を実現する**こと。
● 会議やドキュメントでソリューションを論ずることではなく、**実装、テスト、計測を通じてソリューションの効果が証明されるようになる**こと。[^2]

**How**
具体的にどのようなことをしていくかというと、ソフトウェア開発で行ってきた**バージョン管理、自動テストライブラリ、デプロイのオーケストレーション**といったことをインフラ管理にも応用していく。

# Terraformの説明

## Infrastructure as Codeツール分類
Terraformは『Infrastructure as Code』で紹介されるツール分類より引用すると以下の4つ[^3]の中で**インフラストラクチャ定義ツール**に該当する。

- ダイナミックインフラストラクチャプラットフォーム（AWS、GCP、Azure等）
- **インフラストラクチャ定義ツール（Terraform、CloudFormation等）**
- サーバー構成ツール（Ansible、Chef、Puppet等）
- インフラストラクチャサービス（Datadog、NewRelic、CircleCI等）

## Terraformとは
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276477/67a727a0-d263-2c64-5b2a-1d9d739db89f.png)

https://www.terraform.io/

- HashiCorpが作成したオープンソースの構成管理ツール。
- 例えばクラウドベンダー（AWS、GCP、Azure）のクラウドリソースのインフラ構成を**宣言的に定義**できるところが特徴。**構成手順を書くのではなく、構成定義を書くという形になる。**（Ansibleとの違い）
- [Providers](https://www.terraform.io/docs/providers/index.html)には、物理マシン、VM、ネットワークスイッチ、コンテナ、監視ツール、データベース等の色々な選択肢がある。
- ファイルはHashiCorp Configuration Language (HCL) で記述される。

## Terraformの仕組み

Terraformは`tfstate`と呼ばれるファイルとHCLの差分を確認して、必要な部分だけに変更をかけてくれる。
`tfstate`ファイルはTerraformを一度でもapplyすれば作成され、現在の状態を記録してくれる。

図はTakahisa Iwamoto氏の[資料](https://www.slideshare.net/jey0taka/terraforminfrastructure-as-code/12)を見るとイメージが付きやすいのでおすすめ。

## Terraformのメリット
- HCLで宣言的にリソース定義ができ、リソース作成順序を自動的に判断してくれるため、リソース変更作業者のケアレスミスを回避できる。
- クラウドベンダーを選ばずTerraformでソースコード管理することができる。
- 繰り返し行われるインフラ構築作業トイルを巻き取ってくれる可能性がある。

## Terraformのデメリット
- HCLの記法や各構築対象サービスのお作法を学ぶ必要がある。
- ツール自体開発が活発に行われている最中であるため、新しい記法やお作法の情報を追従することが必要になる。
- 破壊的変更を伴う作業ができてしまうので対策が必要になる。

## Terraformを安全に使うためには
Terraformではライフサイクルによる削除抑止定義を行うことができる

```
lifecycle{
  prevent_destroy=true
}
```

このように削除されたくないリソースに関しては設定を入れると削除できなくなります。
ただし、この設定を入れても絶対安全というわけではなく、リソース定義自体を消してapplyすれば削除されてしまう。
apply時は実行者によるチェックも重要になってくる。

# Terraformハンズオン
## AWSのセットアップ
### AWSのIAMユーザーを作成
- AWSマネジメントコンソールのIAMに移動
- ユーザー　→　ユーザーを追加の順にクリック
- ユーザー名を入力
- プログラムによるアクセス　→　次のステップ：アクセス権限
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276477/c1cbc68d-9847-9343-1544-06e94215e210.png)

- 既存のポリシーを直接アタッチ　→　AdministratorAccessをチェック　→　次のステップ：タグ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276477/760d089f-a899-c289-3db7-cf3801d73534.png)

- タグは追加せず　→　次のステップ：確認
- ユーザーの作成

### AWS CLI
AWS CLIをpip3からインストールします。インストールしたらバージョンを確認してみます。

```
$ pip3 install awscli --upgrade

# 確認
$ aws --version
```

### クレデンシャルを環境変数として設定
AWSマネジメントコンソールからアクセスキーID、シークレットアクセスキーをコピーしてexportする。

```
$ export AWS_ACCESS_KEY_ID=XXXXXXXXXXXXXXXXXXXX
$ export AWS_SECRET_ACCESS_KEY=XXXXXXXXXX/XXXXXXXXXX/XXXXXXXXXX
$ export AWS_DEFAULT_REGION=ap-northeast-1
```

## Terraformのセットアップ
そしていよいよTerraformのインストールを行うが、今後のバージョン管理も考えてtfenvでのインストールをここでは記載しておく。

### tfenvのインストール
[tfenv](https://github.com/tfutils/tfenv)はTerraformのバージョンマネージャー。バージョンを上げたり下げたりしたい場面で非常に有効になるのでこちらでのインストールをお勧めする。

```
# Macの場合
$ brew install tfenv
# 確認
$ tfenv --version
tfenv 1.0.1

# それ以外の場合
$ git clone https://github.com/tfutils/tfenv.git ~/.tfenv
$ echo 'export PATH="$HOME/.tfenv/bin:$PATH"' >> ~/.bash_profile
$ source ~/.bash_profile
# 確認
$ tfenv --version
tfenv 1.0.1
```
※[README](https://github.com/tfutils/tfenv)ではシンボリックリンクによるPATH設定の方法もあるので好みで選択できる。

### tfenvの使い方
#### tfenv list-remote
インストールできるバージョンの一覧を取得する。

```
$ tfenv list-remote
0.12.12
0.12.11
0.12.10
0.12.9
...
```

#### tfenv install
対象のバージョンをインストールする。

```
$ tfenv install 0.12.12
```

#### tfenv list
インストール済みのバージョンを確認する。

```
$ tfenv list
* 0.12.12 (set by /usr/local/Cellar/tfenv/1.0.1/version)
  0.12.5
```

#### tfenv use
複数バージョンを切り替える場合は`use`を使う。

```
$ tfenv use 0.12.5
```

## AWS EC2を作成してみる
### 作業ディレクトリの作成
Terraform作業用にディレクトリを用意する。

```
$ mkdir terraform_test
$ cd terraform_test
$ vim main.tf
```

main.tfには以下を記載する。

```
resource "aws_instance" "test" {
  ami           = "ami-04b9e92b5572fa0d1"
  instance_type = "t3.micro"
}
```

### terraform init
コードを記載したら、main.tfがあるディレクトリで`terraform init`を実行して、バイナリファイルをダウンロードする。

```
$ terraform init
Initializing the backend...
Terraform has been successfully initialized!
```

$ terraform plan
`terraform plan`で実行計画を出力させて、どのような変更が行われるか確認する。

```
$ terraform plan
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_instance.test will be created
  + resource "aws_instance" "test" {
      + ami                          = "ami-04b9e92b5572fa0d1"
      + arn                          = (known after apply)
      + associate_public_ip_address  = (known after apply)
      + availability_zone            = (known after apply)
      + cpu_core_count               = (known after apply)
      + cpu_threads_per_core         = (known after apply)
      + get_password_data            = false
      + host_id                      = (known after apply)
      + id                           = (known after apply)
      + instance_state               = (known after apply)
      + instance_type                = "t3.micro"
   ......}

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

**+マーク**がでているところが重要で、**新規にリソースを作成する**という意味になる。

#### terraform apply
`terraform apply`で実行計画からでていた変更を実際に行わせる。

```
$ terraform apply
......
Do you want to perform these actions? 
 Terraform will perform the actions described above.
 Only 'yes' will be accepted to approve.

 Enter a value:
```

ここで`yes`を入力すると実行される。

```
aws_instance.test: Creating...
aws_instance.test: Still creating... [10s elapsed]
aws_instance.test: Creation complete after 18s [id=i-095131324a66cb153]

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

AWSマネジメントコンソールでEC2インスタンスができていることを確認する。

## AWS EC2の情報を更新してみる
EC2の作成に成功したら、変更をしてみる。
先程作成したmain.tfを以下に書き換える。

```
resource "aws_instance" "test" {
  ami           = "ami-04b9e92b5572fa0d1"
  instance_type = "t3.micro"

  tags = {
    Name = "test"
  }
}
```

コードを修正したら`terraform apply`を実行する。

`+`マークから`~`マークに変化して`aws_instance.test will be updated in-place`が表示される。
これは`既存のリソースの設定を変更する`という意味になる。

AWSマネジメントコンソールでも、Nameタグの追加が確認できる。

## AWS EC2を再作成してみる
Nginxをインストールするため、以下のようにmain.tfを変更してapplyする。

```
resource "aws_instance" "test" {
  ami           = "ami-04b9e92b5572fa0d1"
  instance_type = "t3.micro"

  user_data = <<EOF
    #!/bin/bash
    apt-get install nginx
    systemctl start nginx.service
EOF
}
```

今度は`-/+`マークがつき`aws_instance.test must be replaced`というメッセージが表示される。
これは`既存のリソースを削除して新しいリソースを作成する`という意味になる。

`リソースの削除を伴う作業`になるので、本当に実行していいのか確認する必要がある。

## AWS EC2を削除する
`terraform destroy`で作成したリソースを削除できる。

`-`マークがつき`aws_instance.test will be destroyed`というメッセージが表示される。
これは`既存のリソースを削除する`という意味になる。




# 参考
今回は触りしか書かなかったので、もしより詳しくTerraformを知りたい場合はこちらを購入することをオススメする。
[野村 友規. 実践Terraform　AWSにおけるシステム設計とベストプラクティス](https://www.amazon.co.jp/dp/B07XT7LJLC/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1)


[^1]: Kief Morris(2017年03月)『Infrastructure as Code』(宮下 剛輔　監訳、長尾 高弘　訳) オライリー・ジャパン 「1章課題と原則 1.2 Infrastructure as Codeとは何か」 より引用

[^2]: Kief Morris(2017年03月)『Infrastructure as Code』(宮下 剛輔　監訳、長尾 高弘　訳) オライリー・ジャパン 「1章課題と原則 1.2.1 Infrastructure as Codeの目標」 より引用

[^3]: Kief Morris(2017年03月)『Infrastructure as Code』(宮下 剛輔　監訳、長尾 高弘　訳) オライリー・ジャパン 「1章課題と原則 1.8 この後の章について」 より引用