公式チュートリアルから学ぶTerraform

# Infrastructure as code (IaC)とは？

>Infrastructure as code (IaC) ツールを使用すると、グラフィカルユーザーインターフェイスではなく、設定ファイルを使用してインフラストラクチャを管理することができます。
IaCは、バージョン管理、再利用、共有が可能なリソース構成を定義することで、安全で一貫性のある反復可能な方法でインフラストラクチャを構築、変更、管理することを可能にします。

# terraformとは？
Iacツールの一種であり、人間が読める宣言型の設定ファイルでリソースやインフラを定義し、インフラのライフサイクルを管理することができます。
その中でも2022年4月現在ではシェア率がNo.1であり、以下のような特徴があります。
> - Terraform は複数のクラウドプラットフォーム上のインフラストラクチャを管理することができます。
> - Terraform のステートにより、デプロイメント中のリソースの変更を追跡することができます。
> - 設定をバージョンコントロールにコミットすることで、安全にインフラストラクチャの共同作業を行うことができます。

コミュニティが充実していて、1,000以上のプロバイダと連携できるため、terraformであらゆるインフラ管理が可能であるということが利点であると解釈しました。

それ以外の特徴は以下

- Terraformは宣言型であり、タスクを実行するためにステップバイステップの指示を必要とする手続き型のプログラミング言語とは対照的に、インフラストラクチャの望ましい最終状態を記述する形を取ります。
- Terraformプロバイダーはリソース間の依存関係を自動的に計算し、正しい順番でリソースを作成したり破壊したりします。

## Terraformを使ってインフラストラクチャをデプロイするまでの流れ

大枠としては以下のような流れになります。

> - Scope プロジェクトのインフラを特定する。
> - Author インフラの設定を書きます。
> - Initialize インフラを管理するために必要な Terraform のプラグインをインストールします。
> - Plan 設定に合わせて Terraform が行う変更をプレビューします。
> - Apply 計画した変更を実行します。

## 公式チュートリアルを試す

terraformのインストールについては[こちら](https://learn.hashicorp.com/tutorials/terraform/install-cli?in=terraform/aws-get-started)を参照

Docker for Macがある前提で進めます。

1.作業ディレクトリを作成します。
```
mkdir learn-terraform-docker-container
cd learn-terraform-docker-container
```

2.main.tfという名称で以下のような構成のファイルを作成
* terraformの構成ファイルは*.tfという名称で作成する
```
terraform {
  required_providers {
    docker = {
      source  = "kreuzwerker/docker"
      version = "~> 2.13.0"
    }
  }
}

provider "docker" {}

resource "docker_image" "nginx" {
  name         = "nginx:latest"
  keep_locally = false
}

resource "docker_container" "nginx" {
  image = docker_image.nginx.latest
  name  = "tutorial"
  ports {
    internal = 80
    external = 8000
  }
}
```

3.initコマンドでプロジェクトを初期化します。
```
terraform init
```

4.applyコマンドでNGINXサーバコンテナをプロビショニングします。
```
terraform apply
```

5.localhost:8000にアクセスして、nginxのデフォルトページが表示されることを確認する。

6.destroyコマンドでterraformで起動したコンテナを停止
```
terraform destroy
```

7.docker psコマンドで、マニフェストで定義した名称(tutorial)のコンテナが停止していることを確認

無事Terraformを使用して簡単にNGINX Webサーバをプロビショニングして破棄できることが確認できました！

## TerraformでAWSにEC２インスタンスをプロビショニングする

> 前提条件
このチュートリアルに従うには、以下のものが必要です。
Terraform CLI (0.14.9+) がインストールされていること。
AWS CLIがインストールされていること。
AWS アカウントと、リソースを作成するための認証情報。

1.作業ディレクトリを作成する
```
mkdir learn-terraform-aws-instance
cd learn-terraform-aws-instance
```

2.インフラストラクチャを定義するファイルを作成する
```
touch main.tf
```

3.main.tfに以下の構成を記述する

```
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 3.27"
    }
  }

  required_version = ">= 0.14.9"
}

provider "aws" {
  profile = "default"
  region  = "ap-northeast-2"
}

resource "aws_instance" "app_server" {
  ami           = "ami-830c94e3"
  instance_type = "t2.micro"

  tags = {
    Name = "ExampleAppServerInstance"
  }
}
```

4.initコマンドでディレクトリを初期化
ディレクトリを初期化すると、設定で定義したプロバイダがダウンロードおよびインストールされます。
これらの情報は.terraformというファイルが作成され、そこに格納されます。
```terraform init```

5.fmtコマンドで設定ファイルをフォーマットする
fmtコマンドを使うとカレンとディレクトリにある設定ファイルを自動的に更新し、フォーマットしてくれます。
```terraform fmt```

6.validateコマンドで設定ファイルの構文が有効であるか確認
構文的に有効である場合はSuccessが返ります
```terraform validate```

7.applyコマンドで構成ファイルを適用します
``` terraform apply```

8.成功すれば、AWSコンソールからEC2にExampleAppServerInstanceという名称のインスタンスが起動しています

構成を適用すると、Terraformはterraform.tfstateというファイルを作成してデータを書き込みます。
Terraformは、管理するリソースのIDとプロパティをこのファイルに保存するため、これらのリソースを今後更新または破棄できます。

9.showコマンドで現在の状態を確認する
terraform showで管理しているリソースの現在の状態を確認できます。
```terraform show```

10.stateコマンド
Terraformには、高度な状態管理を行うためのterraform stateというコマンドが組み込まれています。list サブコマンドを使って、プロジェクトの状態にあるリソースをリストアップします。
```terraform state list```

11.destroyコマンドで最後にお掃除します。
```terraform destroy```

### トラブルシューティング
terraform validateが成功し、applyが失敗した場合、以下の一般的なエラーのいずれかに遭遇している可能性があります。

>AMI ID は地域ごとに異なるため、us-west-2 以外の地域を使用している場合は、ami を変更する必要があります。AMI ID は地域固有なので、以下の手順で地域固有の AMI ID を選択し、この ID で main.tf を変更してください。その後、terraform applyを再実行します。

>正しいリージョンのAWSアカウントにデフォルトVPCがない場合、Web UIのAWS VPC Dashboardに移動し、リージョンに新しいVPCを作成し、そのVPCにサブネットとセキュリティグループを関連付けます。次に、aws_instanceリソースにセキュリティグループID（vpc_security_group_ids）とサブネットID（subnet_id）の引数を追加し、その値を新しいセキュリティグループとサブネットの値に置き換えます。