初めてのTerraform

# 初めてのTerraform

## 背景

お仕事でTerraformを使うことになりました。
CloudFormationも使ったことがないのに．．．とちょっと戸惑いました。
ですがIaCを実践で身につける良いチャンスです。
お仕事で取り扱うまえに、まずは予習をしてみます。

## 目標

TerraformでAWSにEC2インスタンスを構築する

## 環境

これまで愛用していたaws-cliが利用可能なDockerコンテナにTerraformをインストールし、Dockerコンテナから実行します。

## 参考

・ [Install Terraform](https://learn.hashicorp.com/tutorials/terraform/install-cli)
・ [10分で理解するTerraform](https://qiita.com/Chanmoro/items/55bf0da3aaf37dc26f73)
・ [AWSでTerraformに入門](https://dev.classmethod.jp/articles/terraform-getting-started-with-aws/)

## ファイル構成

terraform
  └terraform.tfvars
  └variables.tf
  └ec2.tf

## 手順

### 環境構築

Dockerfileに以下を追加し、コンテナを再構築しました。

```
# install terraform
RUN apt-get update && apt-get install -y gnupg software-properties-common curl
RUN curl -fsSL https://apt.releases.hashicorp.com/gpg | apt-key add -
RUN apt-add-repository "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main"
RUN apt-get update && apt-get install terraform
```

### 変数定義ファイル「terraform.tfvars」

各定義ファイルに変数を直接記述する方法もありますが、汎用性を持たせるために変数を外出しにする方法を採用してみました。
「terraform.tfvars」というファイル名で変数を定義しておけばterraform実行時に自動的に読み込んでくれるようです。

今回、aws接続に必要なアクセスキーとシークレットキーを定義しました。

```
aws_access_key = "xxxxxxxxxx"
aws_secret_key = "xxxxxxxxxx"
```

### 環境設定ファイル「variables.tf」

こちらのファイルには接続する環境の情報を記述するようです。
サイトによっては「main.tf」というファイルで作成されている方もいました。

今回はアクセスキーとシークレットキーは別ファイルで定義された変数から読み込むようにしました。

リージョンも外出しにしても良かったのですが、変数定義のバリエーションを確認したいため、リージョンはこのファイルの先頭で変数定義を行い、実際はアクセスキーやシークレットキーのように変数から値を読み込むような使用方法を試してみました。

```
variable "aws_access_key" {}
variable "aws_secret_key" {}
variable "region" {
    default = "us-west-2"
}

provider "aws" {
    access_key = "${var.aws_access_key}"
    secret_key = "${var.aws_secret_key}"
    region = "${var.region}"
}
```

### EC2インスタンス作成用定義ファイル「ec2.tf」

今回はとにかく動かしてみることを目的にしていますので、簡単そうなEC2のインスタンスを構築してみます。

使用するamiはUbuntu 20.04を使用してみます。
構築台数は1台、サイズはt2.microで構築します。

```
resource "aws_instance" "xxxxxxxxxx_tf-ec2" {
  count         = 1
  ami           = "ami-03d5c68bab01f3496" # Ubuntu 20.04 LTS official ami
  instance_type = "t2.micro"

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

### terraformの初期設定

上記ファイルを格納しているフォルダで初期設定コマンドを実行します。
このコマンドを実行することでterraformが使用するファイルが認識されるようになるよです。
gitコマンドを使う前に実行する「git init」によく似ています。

```
# terraform init

Initializing the backend...

Initializing provider plugins...
- Reusing previous version of hashicorp/aws from the dependency lock file
- Using previously-installed hashicorp/aws v2.70.0

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
```

### ログ出力設定

どうやってログを確認するのか確認したところ、どうやら実行前に以下の設定を行っておくと良いらしいです。

```
$ export TF_LOG=1
$ export TF_LOG_PATH='./terraform.log'
```

「terraform apply」実行後に、「cat terraform.log」でログを確認できるようです。

### terraformの実行計画を事前に確認する

実際に構築する前に、どのような内容で構築されるのかを確認することができるようです。
ここで出力された結果をよく見て、意図した内容かを確認しておくのが良さそうです。
構築前のレビューの材料にも使えそうな情報でした。

```
# terraform plan

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with
the following symbols:
  + create

Terraform will perform the following actions:

  # aws_instance.xxxxxxxxxx_tf-ec2[0] will be created
  + resource "aws_instance" "xxxxxxxxxx_tf-ec2" {
:
:
:
Plan: 1 to add, 0 to change, 0 to destroy.

─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if
you run "terraform apply" now.
```

### terraformで実際に構築してみる

内容を確認したら、実際に構築してみます。
planを実行したときのような情報が出力されますが、最後に構築してもよいか問われますので「yes」と入力してエンターを押下します。

```
# terraform apply

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with
the following symbols:
  + create

Terraform will perform the following actions:

  # aws_instance.xxxxxxxxxx_tf-ec2[0] will be created
  + resource "aws_instance" "xxxxxxxxxx_tf-ec2" {
:
:
:
Plan: 1 to add, 0 to change, 0 to destroy.

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.xxxxxxxxxx_tf-ec2[0]: Creating...
aws_instance.xxxxxxxxxx_tf-ec2[0]: Still creating... [10s elapsed]
:
:
:

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

### terraformの実行結果を確認

実行結果をコマンドでも確認することができるようです。

```
# terraform show
# aws_instance.xxxxxxxxxx_tf-ec2[0]:
resource "aws_instance" "xxxxxxxxxx_tf-ec2" {
    ami                          = "ami-03d5c68bab01f3496"
    arn                          = "arn:aws:ec2:us-west-2:854542366722:instance/i-0e75162145dc7f328"
    associate_public_ip_address  = true
    availability_zone            = "us-west-2a"
```

### 念のためにAWS Management Consoleでも確認してみる

コマンドでも確認しましたが、念のためにManagement Consoleにログインして確認してみます。
確かに構築できているようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/589067/32f5ee69-b880-a758-f6a7-209bc1ca5d0b.png)


### terraformで構築した環境をすべて削除する

今回は構築してみることが目的ですし、費用削減のために今構築した環境を削除します。
今回はDestroyコマンドを使い一括で削除しましたが、個別に削除できるのかどうかは別途調査が必要です。

```
# terraform destroy
aws_instance.xxxxxxxxxx_tf-ec2[0]: Refreshing state... [id=i-0e75162145dc7f328]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with
the following symbols:
  - destroy

Terraform will perform the following actions:

  # aws_instance.xxxxxxxxxx_tf-ec2[0] will be destroyed
  - resource "aws_instance" "xxxxxxxxxx_tf-ec2" {
:
:
:
Plan: 0 to add, 0 to change, 1 to destroy.

Do you really want to destroy all resources?
  Terraform will destroy all your managed infrastructure, as shown above.
  There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value: yes

aws_instance.xxxxxxxxxx_tf-ec2[0]: Destroying... [id=i-0e75162145dc7f328]
aws_instance.xxxxxxxxxx_tf-ec2[0]: Still destroying... [id=i-0e75162145dc7f328, 10s elapsed]
aws_instance.xxxxxxxxxx_tf-ec2[0]: Still destroying... [id=i-0e75162145dc7f328, 20s elapsed]
:
:
:
Destroy complete! Resources: 1 destroyed.
```

### 念のためにAWS Management Consoleでも確認してみる

当然の結果ではありますが、ちゃんとインスタンスは削除されていました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/589067/25e29075-a3d6-292a-c15b-0b1dd14c7259.png)

## おわりに

今回は非常に簡単な内容のEC2インスタンスを1つ構築するだけでしたが、IaCの一端に触れることできました。
Terraformのテンプレートファイルはこちらでも公開されているので、自分が構築したい環境に応じて値を修正し実行することで環境を構築できそうです。
指定する情報はテンプレートに直接記述せず、変数化し、汎用性を上げると良いと思います。

[Docs overview | hashicorp/aws | Terraform Registry](https://registry.terraform.io/providers/hashicorp/aws/latest/docs)

今はローカルにあるファイルですが、gitLabなどに登録し、そこからcloneして実行するような環境もできれば作ってみたいと思いました。