Terraformでサーバを建てよう。

これは手記だ。
誰のためのものでもない。
しかして、誰かのためになれば、それはそれで。

## 目次
1. 前置き
2. 環境
3. 手順
4. まとめ

## 1. 前置き

何つくろっかな〜〜
特にないしゲームするか〜〜〜

### と、こんな状態で３連休を過ごしました。

作りたいものがないと言い訳を重ね堕落してましたよえぇ。
そんなんでいいのか？いや良いわけがなかろうて....。

さて、じゃぁSNSサイト作ってみましょうねということで始まりました。
まずWEBサーバ、DBサーバが必要になりますよね。

いちいちGUIぽちぽちするのは面倒..。
そんなあんたに素敵なアイテム。

` Terraform ` のご紹介！！

こいつに作りたいものをペッペと書き出してあげると自動でサーバを建ててくれるんです。
複数台にも対応しているので、とっても便利！

`Aさん「このサーバ、あと３台作らないとな〜。あ〜面倒だな〜〜」(ﾁﾗｯﾁﾗｯ)`

`ぼっく「5000兆円よこせ」(やっておきますね。)`

なんてこと言っても怒られるでしょって？
いやいや、  `terraform apply` してあげれば数秒でできるから~~ノープログラム~~ノープロブレムですよ奥さん！

さて。前置きはここまでとして。
では作っていきましょう：)

## 2. 環境

Mac OS Mojave
AWS
Terraform (v0.11.13)

## 3. 手順

### terraformのinstall
以下のコマンドでインストールできます。
`brew install terraform`

**brewって何？という方は以下のURLを参考にしてください。**
`https://qiita.com/krtsato/items/ba567acacb93a7a02dd9`

インストールできたらバージョンの確認をしましょう
`brew --version`
ちなみに僕は以下のバージョンでした
`Homebrew 2.1.2`

### 作業用ディレクトリを作ろう

僕の場合、以下のような構造にしています。
カレントディレクトリ名はなんでも良いです。
え？なんでもいいが一番困るってお母さんがよく言ってた？

しょうがにゃいにゃぁ。
以下は一例ですにゃ。
カレントディレクトリ名はサーバ名と同じだと分かりやすいかも。
って思ったので同じ名前にしてます。

```
.
├── ansible
│   └── role
└── terraform
    ├── environments
    │   ├── main.tf
    │   ├── provider.tf
    │   ├── terraform.tfstate
    │   ├── terraform.tfstate.backup
    │   └── variable.tf
    └── modules
        └── compute
            ├── ec2.tf
            └── variable.tf
```
今回使うのは `terraform/`のファイルなので、それ以外のファイルはちょっと目を瞑ります。
`environments/` ---> 主に変数の宣言をするファイルが置かれます。
`modukes/compute/` ---> サーバ構築やALB、RDSなど、作成したいデータを置きます。今回はEC2サーバだけ作るので、 `ec2.tf`のみ置きます。

ファイルの中身をのぞいてみましょう。

```environments/main.tf
module "compute" {
  source         = "../modules/compute" # compute配下を読み込みます
  common         = "${var.common}"      # commonという変数を読み込みます
  ec2            = "${var.ec2}"         # ec2という変数を読み込みます
  iam            = "${var.iam}"         # iamという変数を読み込みます
}
```

```environments/provider.tf
provider "aws" {
  access_key  = "access_key" # IAMユーザのaccess_key
  secret_key  = "secret_key" # IAMユーザのsecret_key
  region      = "${lookup(var.common, "region")}" # 先ほどのmain.tfで宣言した common 変数の中にある "region"を取得
}
```

```environments/variable.tf
variable "common" {
  type = "map" # これを記述しないと、common内に複数の変数を宣言できない

  default = {
    region  = "お好きなregion" # regionの指定
    vpc_id  = "お好きなvpc"    # 作成したvpcの指定
    os      = "amzn2"        # ec2で用いるOS名の指定(今回はAmazon Linux 2)
  }
}

variable "ec2" {
  type = "map"

  default = {
    count               = 1               # 何台作成するかの指定
    ami                 = "お好きなami-id"  # amiの指定
    instance_type       = "t2.micro"      # インスタンスタイプの指定
    key_name            = "SSHするための鍵"  # 名前は何でもいいよ
    subnet_id           = "お好きなサブネット" # お好きなサブネットを指定 
    security_groups_ids = "お好きなSG,お好きなSG" # お好きなSGの指定(今回は複数)
    alloc_id            = "お好きなEIP"     # お好きなElastic IPを指定 
    STOP                = "default"       # サーバの止め忘れ防止

    device_name = "/dev/xvd?" # EBSの指定(?の箇所はお好きにどうぞ)
    volume_type = "gp2"       # お好きなtypeを選んでね
    volume_size = 8           # お好きなサイズを選んでね(defaultは8です)
  }
}

variable "iam" {
  type = "map"

  default = {
    role_name = "お好きなIAMロール名を選んでね。"
  }
}
```

```compute/ec2.tf
## EC2
resource "aws_instance" "ec2" { # "ec2"の名称はなんでも良いです
  count                   = "${lookup(var.ec2, "count")}" # 変数ec2で宣言した count を読み込んでるよ。
  ami                     = "${lookup(var.ec2, "ami")}" 
  key_name                = "${lookup(var.ec2, "key_name")}"
  instance_type           = "${lookup(var.ec2, "instance_type")}"
  subnet_id               = "${lookup(var.ec2, "subnet_id")}"
  iam_instance_profile    = "${lookup(var.iam, "role_name")}" # # 変数iamで宣言した role_name を読み込んでるよ。
  vpc_security_group_ids  = "${split(",", "${lookup(var.ec2, "security_groups_ids")}")}" #  splitで , を削除してるよ。

  ebs_block_device = { # ebsの宣言をしているよ。
    device_name = "${lookup(var.ec2, "device_name")}"
    volume_type = "${lookup(var.ec2, "volume_type")}"
    volume_size = "${lookup(var.ec2, "volume_size")}"
  }

  tags { # EC2につけるタグを宣言してるよ。
    Name  = "EC2の名前をつけよう"
    os    = "${lookup(var.common, "os")}"
    STOP  = "${lookup(var.ec2, "STOP")}"
  }
}

## EIP
resource "aws_eip_association" "eip" { # "eip"の名称はなんでも良いです
  count         = "${lookup(var.ec2, "count")}"
  instance_id   = "${aws_instance.ec2.id}" # どのサーバにつけるか宣言しているよ ".id" で指定したサーバのinstanceIDを取得できるんだ！
  allocation_id = "${lookup(var.ec2, "alloc_id")}"
}

```

↓ environments/variable.tfで宣言した common, ec2, iamのdefoult内を取得します。

```compute/variable.tf
variable "common" {
  default = {}
}

variable "ec2" {
  default = {}
}

variable "iam" {
  default = {}
}

```

作成したファイルの中身を列挙しましたが、僕が躓いた箇所をピックアップしてみます

#### 躓いたところ 1

ぼっく「IAMロール名？うーん、適当な名前つけたら作ってくれるのかな？」

**？？？「そんなことしないぞ」**

はい、そんなことしません。
既存のIAMロールをのぞいて、どのロールを使用するかの宣言でした。
そのため、あらかじめIAMロールを作成する必要があります。

#### 躓いたところ 2

ぼっく「AMI？それはもう確実に動的生成するでしょ」

**???「エラーです」**

？？？？？？？？
既存のEC2サーバからAMIを取得してそのAMIを用いたらできました。
なんかこう、
「Ec2建てたからAMI取っておいてねterraformさん。」
ってのを想定していたのですが....。
まぁ調べたらやり方とか出てきそうですね(思考放棄)

**追記**
以下のやり方でAMIはうまくいきそうです。

[Terraformでもいつでも最新AMIからEC2を起動したい](https://dev.classmethod.jp/cloud/aws/launch-ec2-from-latest-ami-by-terraform/)


#### いざ実行

terraformは以下のコマンドの順で実行します。
`1. init`  <-- 初期化
`2. plan`  <-- テスト
`3. apply` <--　実行

environments/main.tfを実行するのでcdコマンドで移動しましょう。

```
My-Mac:environments sample$ terraform init
・・・・・ 前略 ・・・・・
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.
```

となっていればinit成功です！

```
My-Mac:environments sample$ terraform plan
・・・・・ 前略 ・・・・・

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

はいここ！注目するべきところです。
**2 to add** <--- 2つ作りますよ〜
**0 to change** <--- 0個(既存のものに)変更を加えますよ〜
**1 to destroy** <--- 1個(既存のものを)破壊しますよ〜

上記3点をよく確認ください。何を作ってどこのサーバにどんな変更がされ、どのサーバのどの機能を破壊するのか。影響範囲をしっかり確認しましょう。僕はそれを見落としてサーバを**ぶっ壊しました。**

```
My-Mac:environments sample$ terraform apply
・・・・・ 前略 ・・・・・

Plan: 2 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: # <--- applyするけど本当にいいの？って聞かれるので yes を入力

・・・・・ 前略 ・・・・・

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

```

はい無事に作成できました。
EC2のインスタンス項目を覗いて出来上がっているか確認しましょう。
確認するべき点は以下の通りです。
**1. 鍵は間違っていないか**
**2. SSHできるか**
**3. SGは付いているか**

以上3点です。

```

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

https://aws.amazon.com/amazon-linux-2/
7 package(s) needed for security, out of 10 available
Run "sudo yum update" to apply all updates.
[ec2-user@ip-XXX-XXX-XXX-XXX ~]$ 
```
と表示されたらSSH成功です。
あとは煮るなり焼くなり何なりとってやつです。

## 4. まとめ
今回はサーバを1台しか作りませんでしたし、RDSやALBなど作らず質素なものになってしまいました。
いまだにどのサービスがどのくらい料金かかるのか把握しておらず...。

貧民は勉強すら満足にできないので、早いところ宝くじで億とりたいです(’ ・ ・ `)

次回はAnsibleを用いてソフトウェアを導入したいですねぇ。