Terraform 環境構築 on Linux

# はじめに

最近流行ってる **IaC**(Infrastructure as Code) について携わる機会があったのでそれについての記事。
今後、私が携わる案件で IaC の案件があった場合に見直せるように備忘として投稿しておきます。

久しぶりの投稿なのでなんか緊張する。。


今回使用するのは**Terraform**です。
CloudFormationは運用的な方面で面倒なのでTerraformを使うといった流れらしい。（CloudFormationを触ったことないのでよくわかりませんが。。）

環境構築は AWS EC2 の OS は Linux で行います。
人によって環境が異なるため、手順として確立するためにEC2を選択しました。

また、WindowsよりLinuxの方が個人的に好きなのでLinuxを選んでいます。
どうしてもWindows上で構築したい方は、**WLS2**(WindowsにLinuxの環境を作るやつ)を使用してください。

以下のような順で進めていきます。
１．EC2構築
２．Terraform環境構築
３．動作確認
４．リソース削除
５．その他

**前提**
・AWSアカウントを所持している
・AWSに関する最低限の知識
・Terraformに関する最低限の知識
・Linuxに関する最低限の知識
・VSCodeがインストールされていること

**注意点**
・EC2は起動し続けると料金が発生します。各自管理をお願いします。（本記事では最後にEC2を削除します。）
・重要な情報は漏洩しないように注意してください。（アクセスキーなど）

# １．EC2構築
EC2 を構築していきます。
EC2 の構築に必要なものも作っていくので、順に実施して下さい。

## １－１．セキュリティグループの作成
デフォルトから変更しているパラメータのみを以下に記載します。
**・セキュリティグループ名**：terraform-ec2-sg
**・説明**：terraform ec2 sg
**・インバウンドルール**：ルールを追加 → タイプは SSH、ソースを マイIP に変更
**・タグ**：キーを Name、値を terraform-ec2-sg


## １－２．EC2 の作成

デフォルトから変更しているパラメータのみを以下に記載します。
**・名前**：terraform-ec2
**・AMI**：Amazon Linux 2 AMI (HVM) - Kernel 5.10, SSD Volume Type
　　　(AMI ID: ami-0ffac3e16de16665e)
**・キーペア**：皆さんが使用するキーペア
※ない場合は「新しいキーペアの作成」から作成する。
　キーペアのタイプ・プライベートキーファイル形式はデフォルトでよい。
**・ファイアウォール (セキュリティグループ)**：「既存のセキュリティグループを選択する」を選択し、１－１で作成したセキュリティグループを選択する。

作成した EC2 にパブリックIPが割り当てられていることを確認する。
※ SSH接続する際にパブリックIPが必要になります。

## １－３．EC2 接続確認
※ Windowsユーザ向けの説明になります。Macの方は「Mac EC2 SSH」とかで調べてください。

Tera Term を起動します。

**・ホスト**：<パブリックIPアドレスを入力>
**・サービス**：SSH

OKをクリック。

**・ユーザ名**：ec2-user
**・RSA/DSA/ECDSA/ED25519鍵を使う**：<各自のキーペアを選択>

接続をクリック。

以下のようなのが表示されたらOK。

```
       __|  __|_  )
       _|  (     /   Amazon Linux 2 AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-2/
[ec2-user@ip-172-31-4-57 ~]$
```

# ２．Terraform 環境構築
Terraformの環境構築を行います。
こちらも順に実施して下さい。

## ２－１．IAMユーザ作成
AWS CLIを実行するための IAM を作成します。

デフォルトから変更しているパラメータのみを以下に記載します。

**・ユーザー名**：terraform-user
**・ユーザーグループ**：admin-group を選択
**・タグ**：キーを Name、値を terraform-user

## ２－２．アクセスキーの取得
作成したユーザを選択し、セキュリティ認証情報タブを選択。

アクセスキーを作成を選択
⇒ コマンドラインインターフェイス (CLI) を選択
⇒ 次へ
⇒ アクセスキーを作成
⇒ アクセスキーおよび、シークレットアクセスキーをメモ（もしくは CSV ファイルをダウンロード）
⇒ 完了

## ２－３．AWS CLI の導入
TeraTermに戻ります。

参考：[AWS CLI の最新バージョンをインストールまたは更新します。](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/getting-started-install.htm)

zipファイルダウンロード

```
[ec2-user@ip-172-31-4-57 ~]$ curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 46.8M  100 46.8M    0     0  78.1M      0 --:--:-- --:--:-- --:--:-- 78.2M
```

zipファイルの解凍

```
[ec2-user@ip-172-31-4-57 ~]$ unzip awscliv2.zip
Archive:  awscliv2.zip
・・・
  inflating: aws/dist/cryptography-3.3.2.dist-info/LICENSE
  inflating: aws/dist/cryptography-3.3.2.dist-info/INSTALLER
```

インストール

```
[ec2-user@ip-172-31-4-57 ~]$ sudo ./aws/install
You can now run: /usr/local/bin/aws --version
```

インストール確認

```
[ec2-user@ip-172-31-4-57 ~]$ aws --version
aws-cli/2.9.23 Python/3.9.11 Linux/5.10.165-143.735.amzn2.x86_64 exe/x86_64.amzn.2 prompt/off
```

不要ファイル削除

```
[ec2-user@ip-172-31-4-57 ~]$ rm -rf aws awscliv2.zip
```

接続情報設定

```
[ec2-user@ip-172-31-4-57 ~]$ aws configure
AWS Access Key ID [None]: <２－２で取得したアクセスキーを入力>
AWS Secret Access Key [None]: <２－２で取得したシークレットアクセスキーを入力>
Default region name [None]: ap-northeast-1
Default output format [None]: json
```

設定確認

```
[ec2-user@ip-172-31-4-57 ~]$ cat ~/.aws/config
[default]
region = ap-northeast-1
output = json

[ec2-user@ip-172-31-4-57 ~]$ cat ~/.aws/credentials
[default]
aws_access_key_id = <アクセスキー>
aws_secret_access_key = <シークレットアクセスキー>
```

接続確認
先ほど作成したインスタンスのIDが出力されているのでOK

```
[ec2-user@ip-172-31-4-57 ~]$ aws ec2 describe-instances | grep InstanceId
                    "InstanceId": "i-0d5708d57f64645eb",
```

## ２－４．git インストール

インストール

```
[ec2-user@ip-172-31-4-57 ~]$ sudo yum install git-all
～
Is this ok [y/d/N]: y       ### y/d/N を聞かれるので y を入力
～
Complete!                   ### Complete! が出力されたらOK
```

インストール確認

```
[ec2-user@ip-172-31-4-57 ~]$ git --version
git version 2.39.1
```

## ２－５．Terraform インストール

GitHub から tfenv をクローン

```
[ec2-user@ip-172-31-4-57 ~]$ git clone https://github.com/tfutils/tfenv.git ~/.tfenv
Cloning into '/home/ec2-user/.tfenv'...
remote: Enumerating objects: 1959, done.
remote: Counting objects: 100% (564/564), done.
remote: Compressing objects: 100% (146/146), done.
remote: Total 1959 (delta 464), reused 445 (delta 414), pack-reused 1395
Receiving objects: 100% (1959/1959), 415.01 KiB | 7.98 MiB/s, done.
Resolving deltas: 100% (1268/1268), done.
```

シンボリックリンク作成

```
[ec2-user@ip-172-31-4-57 ~]$ sudo ln -s ~/.tfenv/bin/* /usr/bin/
```

シンボリックリンク確認

```
[ec2-user@ip-172-31-4-57 ~]$ ls -al /usr/bin/tfenv
lrwxrwxrwx 1 root root 31 Feb 15 04:09 /usr/bin/tfenv -> /home/ec2-user/.tfenv/bin/tfenv
```

tfenvバージョン確認

```
[ec2-user@ip-172-31-4-57 ~]$ tfenv --version
tfenv 3.0.0-18-g1ccfddb
```

インストール可能バージョン確認

今回は **1.3.8** を使用します。alpha~ や beta~ はあまり使用しない方がいいかも。

最新のバージョンが異なる方はそちらを使用しても構いません。
異なるバージョンを使用する方は、以降の手順で指定するバージョンを変更してください。

```
[ec2-user@ip-172-31-4-57 ~]$ tfenv list-remote | head
1.4.0-beta1
1.4.0-alpha20221207
1.4.0-alpha20221109
1.3.8
1.3.7
1.3.6
1.3.5
1.3.4
1.3.3
1.3.2
```

インストール

```
[ec2-user@ip-172-31-4-57 ~]$ tfenv install 1.3.8
Installing Terraform v1.3.8
Downloading release tarball from https://releases.hashicorp.com/terraform/1.3.8/terraform_1.3.8_linux_amd64.zip
##################################################################################################################################################################### 100.0%
Downloading SHA hash file from https://releases.hashicorp.com/terraform/1.3.8/terraform_1.3.8_SHA256SUMS
Not instructed to use Local PGP (/home/ec2-user/.tfenv/use-{gpgv,gnupg}) & No keybase install found, skipping OpenPGP signature verification
Archive:  /tmp/tfenv_download.caNm49/terraform_1.3.8_linux_amd64.zip
  inflating: /home/ec2-user/.tfenv/versions/1.3.8/terraform
Installation of terraform v1.3.8 successful. To make this your default version, run 'tfenv use 1.3.8'
```

インストールしたバージョンのリストを表示

```
[ec2-user@ip-172-31-4-57 ~]$ tfenv list
  1.3.8
No default set. Set with 'tfenv use <version>'
```

使用するバージョンを指定

```
[ec2-user@ip-172-31-4-57 ~]$ tfenv use 1.3.8
Switching default version to v1.3.8
Default version (when not overridden by .terraform-version or TFENV_TERRAFORM_VERSION) is now: 1.3.8
```

再度リストを表示

使用するバージョンの先頭に `*` がついているのを確認。

```
[ec2-user@ip-172-31-4-57 ~]$ tfenv list
* 1.3.8 (set by /home/ec2-user/.tfenv/version)
```

ちなみに複数バージョンをインストールすることも可能です。
use でバージョン切り替えができたり。

```
[ec2-user@ip-172-31-4-57 ~]$ tfenv install 1.3.3
[ec2-user@ip-172-31-4-57 ~]$ tfenv list
* 1.3.8 (set by /home/ec2-user/.tfenv/version)
  1.3.3
```

Terraform バージョン確認

tfenv コマンドだけでなく、terraform コマンドが使用できることを確認します。

```
[ec2-user@ip-172-31-4-57 ~]$ terraform --version
Terraform v1.3.8
on linux_amd64
```

## ２－６．VSCode セットアップ
ローカル環境に VSCode がインストールされている前提で進めていきます。
この章は、[Visual Studio Code で Remote SSH する。](https://qiita.com/nlog2n2/items/1d1358f6913249f3e186)を参考に進めました。

`C:\Users\<ユーザ名>\.ssh`フォルダを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/797483/76ea2dfe-cc04-16ac-788e-9dc440863366.png)

作成したフォルダに移動し、下記画像のように、パスの欄に`cmd`と入力し Enter をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/797483/3dc45413-65df-3b1f-fe9d-203464876a6a.png)

コマンドプロンプトが開かれるので下記を入力します。
※ここら辺は各自の環境によって少し異なるかもです。。

```
C:\Users\<ユーザ名>>ssh-keygen -t rsa -b 2048 -f ./terraform_ssh
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase): ### 何も入力しないでEnterでOK
Enter same passphrase again:                ### 何も入力しないでEnterでOK
Your identification has been saved in .ssh/terraform_ssh.
Your public key has been saved in .ssh/terraform_ssh.pub.
The key fingerprint is:
SHA256:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX onish@Win11PD
The key's randomart image is:
+---[RSA 2048]----+
|       ... ..o.  |
|      .   o.+. E |
|     .     oo+o  |
|    o .   . oo   |
| . o + .S. .     |
|. o +...o .      |
| *.o*==...       |
|=.==+O=o         |
|+=+oooo.         |
+----[SHA256]-----+
```

再度フォルダを確認し、以下の２つのファイルが作成されていることを確認します。
・terraform_ssh
・terraform_ssh.pub


次に接続先のEC2インスタンスに公開鍵を作成します。
まず、authorized_keysファイルがあると思うので、そちらの内容を確認します。

```
[ec2-user@ip-172-31-4-57 ~]$ cat ~/.ssh/authorized_keys
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAA・・・XXXXXXXXXXXX/ <キーペア名>
```

一応バックアップを作成しておきます。

```
[ec2-user@ip-172-31-4-57 ~]$ cp -p ~/.ssh/authorized_keys ~/.ssh/authorized_keys_bkup
[ec2-user@ip-172-31-4-57 ~]$ ls -al ~/.ssh/
total 8
drwx------ 2 ec2-user ec2-user  57 Feb 15 04:45 .
drwx------ 6 ec2-user ec2-user 120 Feb 15 04:20 ..
-rw------- 1 ec2-user ec2-user 387 Feb 15 01:29 authorized_keys
-rw------- 1 ec2-user ec2-user 387 Feb 15 01:29 authorized_keys_bkup   ## バックアップ
```

公開鍵追加

terraform_ssh.pub の内容をそのままコピペするだけです。

```
[ec2-user@ip-172-31-4-57 ~]$ sudo vi ~/.ssh/authorized_keys
[ec2-user@ip-172-31-4-57 ~]$ cat ~/.ssh/authorized_keys
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAA・・・XXXXXXXXXXXX/ <キーペア名>
ssh-rsa AAAAB3NzaC1yc2EAAAADIWHBAA・・・XXXXXXXXXXXX/ onish@Win11PD
```

いったん EC2 側の作業は OK なのでローカルに戻ります。

/Users/ユーザ名/.ssh/config に以下の内容を追加します。
configファイルがない人は新規作成してください。

```
# Host terraform
Host terraform
  HostName <パブリックIPアドレス>
  User ec2-user
  Port 22
  UserKnownHostsFile /dev/null
  PreferredAuthentications publickey
  StrictHostKeyChecking no
  PasswordAuthentication no
  IdentityFile /Users/<ユーザ名>/.ssh/terraform_ssh
  IdentitiesOnly yes
  LogLevel FATAL
```


VSCodeを開き以下の拡張機能をインストールします。
・Remote - SSH

Remote-SSH をインストールすると、画面左下に`><`みたいなのが追加されるので、そちらをクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/797483/32cd5b49-8288-80f2-7034-3f0dbb8669c5.png)

「ホストに接続する」をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/797483/e0769d53-57e4-dac1-818d-1c40403fc911.png)

画面左下が、`SSH:terraform`になっていればOKです。

Open Folder から /home/ec2-user/ を開いてみました。
EC2インスタンスの内容と同じなので、きちんと接続できてそうですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/797483/b6166c4f-b088-5de0-fd88-282997a5a556.png)

必要な拡張機能をインストールしていきます。
・Japanese Language Pack for Visual Studio Code
・HashiCorp Terraform

# ３．動作確認

環境構築が完了したので、Terraformを用いてS3バケットを作成してみます。

/home/ec2-user ディレクトリ配下に、terraform というディレクトリを作成して、
そちらにTerraformのコードを記述していこうと思います。

構成は以下の通り。

```
[ec2-user@ip-172-31-4-57 terraform]$ tree
.
├── main.tf
└── provider.tf

0 directories, 2 files
```

プロバイダーは AWS を指定します。
[プロバイダーの一覧](https://registry.terraform.io/browse/providers)から最新のバージョンが確認できます。

```terraform:provider.tf
terraform {
  required_providers {
    aws = {
        source = "hashicorp/aws"
        version = "4.45.0"         ## プロバイダーのバージョン
    }
  }
  required_version = "1.3.8"       ## Terraformのバージョン
}
```

今回はS3を作成するだけなので、簡単な感じで以下の通りです。
ドキュメントは[コチラ](https://registry.terraform.io/providers/hashicorp/aws/latest/docs)を参照してください。

バケット名は一意でなければならないため、誰とも被らなさそうなやつで

```terraform:main.tf
resource "aws_s3_bucket" "this" {
  bucket = "sample-terraform-bucket-abcdefg"
  
  tags = {
    "Name" = "sample-terraform-bucket-abcdefg"
  }
}
```

VSCodeでターミナルを開き、以下のコマンドを順に実行していきます。

init と fmt

```
[ec2-user@ip-172-31-4-57 terraform]$ terraform init
[ec2-user@ip-172-31-4-57 terraform]$ terraform fmt
```

plan（こんな変更が加わりますよ～的なやつ）

```
[ec2-user@ip-172-31-4-57 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_s3_bucket.this will be created
  + resource "aws_s3_bucket" "this" {
      + acceleration_status         = (known after apply)
      + acl                         = (known after apply)
      + arn                         = (known after apply)
      + bucket                      = "sample-terraform-bucket-abcdefg"
      + bucket_domain_name          = (known after apply)
      + bucket_regional_domain_name = (known after apply)
　（以下省略）
```

apply（plan の内容を元にリソース作成）

```
[ec2-user@ip-172-31-4-57 terraform]$ terraform apply -auto-approve
（一部省略）

aws_s3_bucket.this: Creating...
aws_s3_bucket.this: Creation complete after 1s [id=sample-terraform-bucket-abcdefg]

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

どうやら S3 バケットが作成されたぽいので、ターミナル（もしくはマネコン）から確認します。


作成出来てそうですね。

```
[ec2-user@ip-172-31-4-57 terraform]$ aws s3 ls
2023-02-15 05:49:10 sample-terraform-bucket-abcdefg　　　　## 先ほど作成した S3 バケット
```

# ４．リソース削除
今回作成した以下のリソースを削除していきます。
・S3
・EC2
・セキュリティグループ
・IAM ユーザ

## ４－１．S3 バケットの削除
最初に、作成した S3 バケットを削除します。
※ Terraformから削除すること！マネコンから削除すると、Terraform と整合性が取れなくなります。

ターミナルで以下のコマンド実行

```
[ec2-user@ip-172-31-4-57 terraform]$ terraform destroy -auto-approve
（一部省略）

aws_s3_bucket.this: Destroying... [id=sample-terraform-bucket-abcdefg]
aws_s3_bucket.this: Destruction complete after 0s

Destroy complete! Resources: 1 destroyed
```

S3 バケットが本当に削除されたか確認。
何も出力されなければ OK です。心配な方はマネコンから確認してください。

```
[ec2-user@ip-172-31-4-57 terraform]$ aws s3 ls
```

## ４－２．EC2 の削除
EC2に関しては、Terraformではなく、AWSマネコンで作成したので、そちらから削除を行います。
※ 作成した環境を引き続き使用する場合は削除しなくて結構です。その場合は停止しましょう。

インスタンスの状態 > インスタンスを終了 で EC2 インスタンスが削除されます。

## ４－３．セキュリティグループの削除
※不要な場合のみ実施

EC2 > セキュリティグループ > terraform-ec2-sg > アクション > セキュリティグループの削除 で削除


## ４－４．IAM ユーザの削除
※不要な場合のみ実施

IAM > ユーザ > terraform-user > 削除 で削除

# ５．その他

今回作成した EC2 インスタンスを引き続き利用される方向けです。

今回作成した EC2 はパブリックアドレスを割り当てているので、
パブリックアドレスに対してSSHすることで、EC2インスタンスに接続が可能でした。

ただ、パブリックアドレスは EC2 停止⇒起動ごとに変わるという性質を持っております。（再起動はセーフ）
実際に試してみます。現在のパブリックアドレスは、`13.231.228.156`です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/797483/d62b5aab-fdff-37de-f06f-c7d63d2e1990.png)

停止後、パブリックアドレスは消えました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/797483/5738c580-4402-44bf-aefa-92440c910a8f.png)

起動後、パブリックアドレスが`54.238.21.140`に変わりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/797483/a34ac68b-dba4-32c3-49bc-49f0d0fe43a5.png)

つまり、EC2が停止するたびに、Remote-SSHで使用する
configファイルのIPアドレスを変更しなければなりません。。。

これは面倒なのでいくつか対処策を記述しておきます。

１．Elastic IP を使用する。
Elastic IP を作成し、EC2 に割り当てることで以下の画像でいう`13.114.162.161`が固定で割り当てられます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/797483/3fc4692e-0674-7936-1bfa-ef63ba3e73c7.png)

ただ、EIPはインスタンス起動時は料金が発生しないものの、**インスタンス停止時に料金が発生します。**

うーむ。。。

２．プライベートIPアドレスで接続する。
画像右上の`172.31.4.57`に対して SSH でアクセスしサーバに接続する作戦。
プライベートIPアドレスは停止しても変更しないので、毎回同じアドレスで接続できる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/797483/7b43c3d8-26b9-65e9-aa00-cfbcd544c977.png)

ただ、もちろんローカルのPC上からは SSH できない。
同じ VPC 内に踏み台サーバ的なものを作る必要があります。

会社のAWSアカウントとかだったら色々やりようがありますが、
個人で利用する分にはあまりいい手はありませんね。。

ということで、結局 WLS2 を使用するのが一番楽かな。。

# さいごに

ここまでご覧いただきありがとうございました。

今回は EC2 に Terraform 環境を構築しましたが、なんだか微妙な終わり方になりすみません（笑）

上手く料金かけないでEC2立ててそこに接続する方法ありますかね？
何かいい方法をご存じの方は教えていただきたいです。

最近 Terraform にはまっているので、また何か記事を載せようと思います。