【AWS】Terraformを使ってEC2インスタンス構築

##概要
TerraformのインストールからAWSでのEC2インスタンスの作成までの手順をまとめました。

・Terraformをどう始めるか
・AWSリソースをコードからどうやって作成するのか
上記の2点にフォーカスをあてているため、細かい設定等は省きます。
Terraform学習の一歩目として参考にどうぞ。
##環境や前提条件
###OS
・macOSを使用
・homebrewがインストール済みであること
###AWS
・AWSアカウントが作成済みであること
・IAMユーザーを作成しアクセスキー、シークレットキーを取得済みであること

##目次
1. AWS CLIをインストール
1. Terraformをインストール
1. EC2インスタンスの構築
1. EC2インスタンスの更新

##1. AWS CLIのインストール
###AWS CLIとは?
AWS CLI（コマンドラインインターフェース）とは、コマンドラインからAWSのサービスを管理・操作するためのツール。
使用するメリットとしてはマネジメントコンソールでのGUI操作を自動化できることが挙げられる。

###インストール手順
AWS CLIをインストール。
インストールができていることを確認。

```bash
$ curl "https://awscli.amazonaws.com/AWSCLIV2.pkg" -o "AWSCLIV2.pkg"
$ sudo installer -pkg AWSCLIV2.pkg -target /
$ aws --version
```

###IAMユーザーの設定（CLIとの紐付け）
いくつか方法があるが`aws configure`コマンドでのセットアップが簡単な方法。
このコマンドでは下記の4つの情報の入力が求められる。

```bash
$ aws configure
AWS Access Key ID: #アクセスキーID
AWS Secret Access Key: #シークレットアクセスキー
Default region name: #リージョン
Default output format: #デフォルトはjson

# 環境変数から設定する場合はこちら
$ export AWS_ACCESS_KEY_ID= #アクセスキーID
$ export AWS_SECRET_ACCESS_KEY= #シークレットアクセスキー
$ export AWS_DEFAULT_REGION= #リージョン

#設定後、下記コマンドを実行して確認
$ aws sts get-caller-identity
{ 
    "UserId": "ユーザーID", 
    "Account": "12桁のアカウントID",
    "Arn": "arn:aws:iam::12桁のアカウントID:user/IAMユーザー"
}
```
意図した通りの設定内容が返って来れば、IAMユーザーとAWS CLIの紐付けは成功！

##2. Terraformをインストール
###Terraformとは?
インフラ構成をコードで宣言し、管理の自動化を実現できるツール。
構成初期プロビジョニング、更新、破棄のいずれもTerraformを使ってコードで実行できる。
IaC（Infrastructure as Code）の実現によりインフラ環境のバージョン管理が容易く、DevOpsで開発を進めるには最適。

###インストール手順
今回はhomebrewを使ってインストールする。

```bash
#Terraformのインストール
$ brew install terraform
$ terraform --version

#tfenvもインストール
$ brew install tfenv
$ tfenv --version
```
tfenvを使うことでTerraformのバージョンの管理・切り替えができる。
`tfenv list-remote`コマンドでインストールできるTerraformのバージョンの一覧を表示。
`tfenv install`で使用するバージョンを選択してインストール。
インストールしたバージョンは`tfenv use`コマンドで切り替えができる。
`tfebv list`コマンドでインストール済みのバージョンを確認。

```shell
$ tfenv list-remote #バージョン一覧を表示
1.0.4
1.0.3
1.0.2
...
...
0.2.0
0.1.1
0.1.0
$ tfenv install 1.0.4 #選択したバージョンをインストール
$ tfenv install 0.12.5
$ tfenv list #インストール済みバージョンの確認 1.0.4が設定されている
* 1.0.4 (set by /usr/local/Cellar/tfenv/2.2.2/version)
  0.12.5
$ tfenv use 0.12.5 #0.12.5へ切り替え
Switching default version to v0.12.5
Switching completed
$ tfenv list
  1.0.4
* 0.12.5 (set by /usr/local/Cellar/tfenv/2.2.2/version) #0.12.5が設定された
```

tfenvがインストールできない！という方へ

```shell
...
Error: Cannot install tfenv because conflicting formulae are installed.
  terraform: because tfenv symlinks terraform binaries

Please `brew unlink terraform` before continuing.
...
```
上記のエラーが発生している場合は、`brew unlink terraform`を実行してから、
tfenvのインストールを再実行。

ここまででTerraformのインストールは完了！

##3. EC2インスタンスの構築
###main.tfを作成
作業用フォルダにてmain.tfを作成し、コードを書いていく。
（HCL: HashiCorp Configuration Language）
Terraformに触れることが目的のため、今回はEC2のみを作成しその他サービスは扱わない。

```terraform:main.tf
#Amazon Linux2のAMIベースにt2.microのEC2を作成
resource "aws_instance" "example" {
    ami = "ami-09ebacdc178ae23b7" #Amazon Linux2
    instance_type = "t2.micro"
}
```

###init / plan / apply
main.tfを作成したら、`terraform init`を実行。リソース作成に必要なバイナリファイルをダウンロード。

```bash
$ terraform init
Initializing the backend ...
...
...
Terraform has been successfully initialized! #terraform initが成功
```
続いて、`terraform plan`コマンドで実行計画を表示。

```bash
$ terraform plan
provider.aws.region
  The region where AWS operations will take place. Examples
  are us-east-1, us-west-2, etc.

  Enter a value: #リージョン名を入力

Terraform will perform the following actions:
  # aws_instance.example will be created

...
#リソースの情報が続く...
...

Plan: 1 to add, 0 to change, 0 to destroy. #いくつの追加、変更、削除が実行されるかを表示
```
`terraform apply`コマンドでリソース作成を実行する。

```bash
$ terraform apply #コマンドではplan結果が表示され、実行して良いか確認が行われる
...
#リソースの情報が続く...
...
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 #yesと入力した場合のみリソース実行に進む

...
#進行状況を表示
aws_instance.example: Creating...
aws_instance.example: Still creating... [10s elapsed]
...

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
#Apply完了のメッセージが出力されたらリソース作成の完了
```

マネジメントコンソールでEC2が作成されていたら成功！

##4. EC2インスタンスの更新

###Nameタグ追加
作成したEC2の情報を更新する。
main.tfにNameタグの設定内容を追記し、再び`terraform apply`を実行。

```terraform:main.tf
#Amazon Linux2のAMIベースにt2.microのEC2を作成
resource "aws_instance" "example" {
    ami = "ami-09ebacdc178ae23b7" #Amazon Linux2
    instance_type = "t2.micro"

    #ここから追記
    tags = {
        Name: "my-ec2-001"
    }
}
```
```bash
$ terraform apply
Terraform will perform the following actions:

  # aws_instance.example will be updated in-place
  ~ resource "aws_instance" "example" {
...
...
Apply complete! Resources: 0 added, 1 changed, 0 destroyed.
#リソースが1つ更新（changed）された
```
追記した内容のタグが追加される。
![スクリーンショット 2021-08-14 2.34.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1813991/884b1822-d984-7737-824c-815b3ae196b1.png)

###Apacheをインストール・起動
さらにEC2インスタンス起動時にApacheをインストールして起動させる設定を追記し、
再び`terraform apply`を実行してみる。

```terraform:main.tf
#Amazon Linux2のAMIベースにt2.microのEC2を作成
resource "aws_instance" "example" {
    ami = "ami-09ebacdc178ae23b7" #Amazon Linux2
    instance_type = "t2.micro"

    tags = {
        Name: "my-ec2-001"
    }

    # ここから追記
    user_data = <<EOF
        #!/bin/bash
        sudo su -
        yum install -y httpd
        systemctl start httpd
    EOF
}
```
```bash
$ terraform apply
Terraform will perform the following actions:

Terraform will perform the following actions:

  # aws_instance.example must be replaced
-/+ resource "aws_instance" "example" {
...
...
aws_instance.example: Destroying...
...
...
aws_instance.example: Creating...
...
...
Apply complete! Resources: 1 added, 0 changed, 1 destroyed.
# リソースが1つ追加され、1つ削除された
```
![スクリーンショット 2021-08-14 2.52.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1813991/87547f80-da6c-b356-95c6-2f54c0bbee81.png)
今回のケースでは、既存のEC2インスタンスをそのまま更新するのではなく、
既存のEC2インスタンスは削除され、新規にEC2インスタンスが作成されている。

このようにTerraformでのリソースの更新は、

1. __既存リソースの継続して更新__
1. __既存リソースを削除して新規で再作成__

上記の2ケースで実行されることがあるため、`terraform plan`結果を念入りに確認しよう。
思わぬサービスダウンを引き起こす恐れがある。

###その他
`terraform destroy`コマンドでリソースの削除。

`terraform apply (or destroy) --auto-approve`で実行確認をスキップできる。
yesと毎回入力する必要はなくなるが、意図しないリソースの再作成や誤削除を避けるためにも使わない方が良いと思う。

##終わりに
まだネットワークやアクセス関連のサービスを実装していないので、何かができるものを構築したとは言えないが、、、
今回は「Tearraformを使って、インフラをコードで構築する」という最初の一歩目をまとめてみました。

今後、その他必須のAWSサービスについても記事もまとめようと思います。