[Terraform]理解したい人必見、、！

# Terraformとは？
HashiCorpが手がける、インフラ構築や設定をコード(テンプレートファイル)を使って自動化するためのツールです。
HCL（HashiCorp Configuration Language）という⾔語で .tf 拡張子のファイルにインフラ構成を記述し、Terraformのコマンドでそのファイルを実行するとその構成通りのインフラが作成されます。
また、AWS以外にもAzureや、Docker、Google Cloudなどいろんなプロバイダー(※1)に対応しています。

##メリット
- 公式ドキュメントがわかりやすい。

- 少量の記述量で宣言が可能
  - Workspaceという機能を利用することで環境を切り替えられ、一度書いたことのあるリソースを再利用して使い回すことが可能

- ググラビリティ(※2)が高い
  - Terraformを利用している企業が非常に多いという理由で、ググラビリティが非常に高い

- 学習コストの低さ

- ポータビリティー(※3)に優れている

- 管理が簡素化できる

- 転職に有利
  - 現在、システム開発やシステム保守に関して、AWSやAzureを採用している企業は多いため、Terraformの知識がある人材は重宝される。
  - AWSやAzureなどを採用する企業が増える可能性が高い。
   

##デメリット
- 常に管理更新が必要

- Terraformのインフラ保守や管理に継続的に投資が必要

- 再利用がしづらい
 - 必要な属性すべてに対して同じコマンドを繰り返すことになり、手間になってしまう


##よく使うコマンド
Terraformで使われる主なコマンドは、Init、Plan、Applyです。他にもよく使われるコマンドがあるので書きにまとめます。

####Init
ワークスペースを初期化するコマンド。
Terraformを実行するために必要なワークスペースを捻出するため、Initによってワークスペースを初期化します。


```

$ terraform init
```

####Plan
プランを参照するコマンドです。

```

$ terraform plan
```

####Apply
リソースを作成するためのコマンドです。

.tfファイルの情報を読み込みリソースを作成するためにApplyコマンドを使用します。

```

$ terraform apply
```

####Show
リソースの状況をチェックするためのコマンドです。

Showは、よく使われるコマンドというわけではありませんが、リソース作成には必要なコマンドになります。

```

$ terraform show
```

####Destroy
生成されたリソースを削除するためのコマンドです。

Destoryも、よく使われるコマンドではありませんが、リソースの削除に必要になります。

```

$ terraform destroy
```

##記述する上でのコツ
記述するには3STEPを意識すると良いらしい。

#####STEP1 : 必要なリソースのTerraform化
必要なリソースを全て記載すると、かなりの記述量になる。その際は既存のファイルをコピーして、各リソース等の名前を変更すれば、大きな手間や労力を省ける。

#####STEP2 : workspace機能を使用する
Terraformにはworkspaceという機能があり、同一コードで環境別に値などを切り替えられます。

#####STEP3 : main.tfの共通部分をmodule化する
AWSの場合、リソースのTerraformのmain.tfは、変数指定項目があらかじめ決められています。そこでAWSのリソースの共通部分をmodule化します。そうすることによって、AWSのリソースを宣言する部分は、他のmain.tfでも再利用できるコードになります。

##Terraformの使用方法

1.以下のコマンドでインストールする。

```

$ brew cask install terraform

# インストールされているか、バージョンを確認する
$ terraform version
```

2.テンプレートファイルを作成する。
※拡張子は「*.tf」とします。(Terraformはこの拡張子のファイルを自動的にテンプレートとして認識してくれます)。

```

$ mkdir terraform-test

$ cd terraform-test

$ touch main.tf
```

3.先ほど作成したファイルにプロバイダーの設定をする。
※AWSを使う場合はproviderブロックでawsを指定し、ブロックの中にAWSのクレデンシャル情報(access_key, secret_key)とリージョン(region)の設定を記述します。

```main.tf

provider "aws" {
    access_key = "ACCESS_KEY_HERE"
    secret_key = "SECRET_KEY_HERE"
    region = "ap-northeast-1"
}
```

4.クレデンシャル情報を環境変数を使って切り出す。
※「AWS_ACCESS_KEY_ID」と「AWS_SECRET_ACCESS_KEY」を設定します。terraformコマンドを実行すると、クレデンシャル情報とリージョンは自動的に読み込まれるので、access_keyとsecret_keyの設定は不要となります。

```main.tf

provider "aws" {
    region = "ap-northeast-1"
}
```

5.リソースを設定する。
リソースの種類は、プロバイダーがAWSの場合はaws_*という名前でTerraformで予め定義されています。
リソース名は任意の名前を設定可能です。各リソースの用途などを識別できるような分かりやすい名前を付けておきましょう。
ブロックの中には各AWSリソースの設定値を設定項目名 = 設定値の形式で記述します。
※各AWSリソースで設定が必要な項目については、以下より各リソースごとの[公式リファレンス](https://registry.terraform.io/providers/hashicorp/aws/latest/docs)を参照ください。

```main.tf

# VPCの設定
resource "aws_vpc" "myVPC" {
    cidr_block = "10.1.0.0/16"
    instance_tenancy = "default"
    enable_dns_support = "true"
    enable_dns_hostnames = "false"
    tags {
      Name = "myVPC"
    }
}
```

6.Internet Gatewayを設定する。

```main.tf

resource "aws_internet_gateway" "myGW" {
    vpc_id = "${aws_vpc.myVPC.id}"
    depends_on = "${aws_vpc.myVPC}" # myVPCに依存することを明示。
}
```

7.デフォルト値は単一の値ではなくMapを指定する。

```main.tf

variable "images" {
    default = {
        us-east-1 = "ami-1ecae776"
        us-west-2 = "ami-e7527ed7"
        us-west-1 = "ami-d114f295"
        eu-west-1 = "ami-a10897d6"
        eu-central-1 = "ami-a8221fb5"
        ap-southeast-1 = "ami-68d8e93a"
        ap-southeast-2 = "ami-fd9cecc7"
        ap-northeast-1 = "ami-cbf90ecb"
        sa-east-1 = "ami-b52890a8"
    }
}
```

8.outputを書いておくと、terraformコマンド実行時に指定した属性値がコンソール上に出力する。

```main.tf

output "<アウトプットする属性の説明>" {
  value = "<アウトプットする属性値>"
}

output "public ip of cm-test" {
  value = "${aws_instance.cm-test.public_ip}"
}
```

9.以下のコマンドで、AWS上に環境構築する。

```

$ terraform plan
```

10.以下のコマンドで、テンプレートを適用してAWS上にリソースを作成する。

```

$ terraform apply
```

11.完成形。


```main.tf

variable "images" {
    default = {
        us-east-1 = "ami-1ecae776"
        us-west-2 = "ami-e7527ed7"
        us-west-1 = "ami-d114f295"
        eu-west-1 = "ami-a10897d6"
        eu-central-1 = "ami-a8221fb5"
        ap-southeast-1 = "ami-68d8e93a"
        ap-southeast-2 = "ami-fd9cecc7"
        ap-northeast-1 = "ami-cbf90ecb"
        sa-east-1 = "ami-b52890a8"
    }
}

provider "aws" {
    region = "ap-northeast-1"
}

resource "aws_vpc" "myVPC" {
    cidr_block = "10.1.0.0/16"
    instance_tenancy = "default"
    enable_dns_support = "true"
    enable_dns_hostnames = "false"
    tags {
      Name = "myVPC"
    }
}

resource "aws_internet_gateway" "myGW" {
    vpc_id = "${aws_vpc.myVPC.id}"
}

resource "aws_subnet" "public-a" {
    vpc_id = "${aws_vpc.myVPC.id}"
    cidr_block = "10.1.1.0/24"
    availability_zone = "ap-northeast-1a"
}

resource "aws_route_table" "public-route" {
    vpc_id = "${aws_vpc.myVPC.id}"
    route {
        cidr_block = "0.0.0.0/0"
        gateway_id = "${aws_internet_gateway.myGW.id}"
    }
}

resource "aws_route_table_association" "puclic-a" {
    subnet_id = "${aws_subnet.public-a.id}"
    route_table_id = "${aws_route_table.public-route.id}"
}

resource "aws_security_group" "admin" {
    name = "admin"
    description = "Allow SSH inbound traffic"
    vpc_id = "${aws_vpc.myVPC.id}"
    ingress {
        from_port = 22
        to_port = 22
        protocol = "tcp"
        cidr_blocks = ["0.0.0.0/0"]
    }
    egress {
        from_port = 0
        to_port = 0
        protocol = "-1"
        cidr_blocks = ["0.0.0.0/0"]
    }
}

resource "aws_instance" "cm-test" {
    ami = "${var.images.ap-northeast-1}"
    instance_type = "t2.micro"
    key_name = "cm-yawata.yutaka"
    vpc_security_group_ids = [
      "${aws_security_group.admin.id}"
    ]
    subnet_id = "${aws_subnet.public-a.id}"
    associate_public_ip_address = "true"
    root_block_device = {
      volume_type = "gp2"
      volume_size = "20"
    }
    ebs_block_device = {
      device_name = "/dev/sdf"
      volume_type = "gp2"
      volume_size = "100"
    }
    tags {
        Name = "cm-test"
    }
}

output "public ip of cm-test" {
  value = "${aws_instance.cm-test.public_ip}"
}
```


##分からない単語一覧

※1 プロバイダー
回線をインターネットと繋げる役割を担う接続事業者のこと。

※2 ググラビリティ
Googleの検索結果にひっかかる件数の量。

※3 ポータビリティー
ソフトウエアなどが，別なシステムにも移植できること。

##参考
[10分で理解するTerraform](https://qiita.com/Chanmoro/items/55bf0da3aaf37dc26f73)

[AWSでTerraformに入門](https://dev.classmethod.jp/articles/terraform-getting-started-with-aws/)

[インフラの構成管理を自動化するTerraform入門](https://thinkit.co.jp/story/2015/07/14/6212)

[技術者でない人のためのTerraform入門](https://www.lac.co.jp/lacwatch/service/20200605_002206.html)

[Terraform初心者が実戦投入するまでにやったこと
](https://dev.classmethod.jp/articles/my-first-step-of-terraform/)

[Terraformとは?基本知識とTerraformのメリット4つを紹介
](https://www.fenet.jp/infla/column/technology/terraform%E3%81%A8%E3%81%AF%E5%9F%BA%E6%9C%AC%E7%9F%A5%E8%AD%98%E3%81%A8terraform%E3%81%AE%E3%83%A1%E3%83%AA%E3%83%83%E3%83%884%E3%81%A4%E3%82%92%E7%B4%B9%E4%BB%8B/)

[Terraformとは？Terraformの使い方とメリットを徹底解説！インストールの手順からAWSでの構築方法も伝授](https://agency-star.co.jp/column/terraform)