TerraformでS3を作成してみた

# 背景・目的
以前、[Terraformの特徴について整理してみた](https://qiita.com/zumax/items/379ce305c59fb4c3af94)では、Terraformの特徴や利点について整理してみました。
今回は、[詳解 Terraform 第3版](https://www.oreilly.co.jp/books/9784814400522/)の、「第2章 Terraformをはじめよう」を元にTerraformの基本的な使い方を学んでいきます。


# まとめ
- Terraform のインストールには`tfenv`でインストールします
- 初回にプロバイダーコードをダウンロードするために、`terraform init`を実行します
- 実行前に確認するには、`terraform plan`を実行します
- 実行するには、`terraform apply`を実行します


# 概要
## Terraformのコード
- コードは、拡張子.tfファイルに、HCL(HashiCorp Configuration Language)という言語で書く
- 実現したいインフラを記述することで、Terraformがどう作るか判断する
- Terraformは、AWS、Azureなどのプロバイダと呼ばれるプラットフォーム上にインフラを構築できる

### main.tf
最初の第一歩は、プロバイダの設定。からのフォルダを作成し、main.tfファイルに下記を作成する

```
    provider "aws" {
      region ="us-east-2"
    }
```

### 一般的な文法
各プロバイダタイプに応じて、サーバ、データベース、ロードバランサーなどの多くのリソースが存在している。
一般的な文法は下記の通り。

```
resource "<プロバイダ>_<タイプ>" "<名前>" {
  [設定..]
}
```
- プロバイダ：awsなど
- タイプ：プロバイダ無いに作成するリソースのタイプ（Instanceなど）
- 名前:識別子
- 設定：リソース特有の引数

例：EC2を作る場合、main.tfに下記のように記載する。
```
resource "aws_instance" "example" {
    ami           ="ami-XXXXXXXXX"
    instance_type ="t2.micro"
}
```

### バージョンを固定する方法
Terraform本体のバージョンを制限する場合はterraformセクションのrequired_versionを使用します。

```
terraform {
  required_version = "~> 1.7.5"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.44.0"
    }
  }
}
```

### terraform init
Terraformのバイナリには、Terraformの基本的な機能が含まれているが、プロバイダに関するコードは含まれていません。
そのため、始めて利用する際には、`terraform init`を実行し、
Terraformがコードをスキャンし、どのプロバイダを使うか判断し、そのプロバイダのコードをダウンロードする必要があるとのことです。

### terraform plan
実際に変更を加える前に、Terraformが何をするか予定を確認できます。

### terraform apply
planコマンドを本当に適用していいか確認します。yesを入力しEnterを押してデプロイします


# 実践
## 前提
AWS Cloud Shellで実行しています

## Terraformのインストール
[AWS CloudShellでTerraformを実行してみた](https://dev.classmethod.jp/articles/terraform-cloudshell/)を元に、インストールします。

1. AWSにサインインします
1. CloudShellを起動します
1. tfenvをcloneします

    ```
    $ cd
    $ git clone https://github.com/tfutils/tfenv.git ~/.tfenv
    Cloning into '/home/cloudshell-user/.tfenv'...
    remote: Enumerating objects: 2057, done.
    remote: Counting objects: 100% (662/662), done.
    remote: Compressing objects: 100% (204/204), done.
    remote: Total 2057 (delta 518), reused 523 (delta 449), pack-reused 1395
    Receiving objects: 100% (2057/2057), 437.12 KiB | 9.93 MiB/s, done.
    Resolving deltas: 100% (1322/1322), done.
    $ 
    ```

1. バージョンを選択しインストールします
    ```
    $ mkdir -p ~/.local/bin/
    $ sudo ln -s ~/.tfenv/bin/* ~/.local/bin/
    $ tfenv install 1.7.5
    Installing Terraform v1.7.5
    Downloading release tarball from https://releases.hashicorp.com/terraform/1.7.5/terraform_1.7.5_linux_amd64.zip
    ######################################################################################################################################################################################################################################################### 100.0%
    Downloading SHA hash file from https://releases.hashicorp.com/terraform/1.7.5/terraform_1.7.5_SHA256SUMS
    Not instructed to use Local PGP (/home/cloudshell-user/.tfenv/use-{gpgv,gnupg}) & No keybase install found, skipping OpenPGP signature verification
    terraform_1.7.5_linux_amd64.zip: OK
    Archive:  /tmp/tfenv_download.cxRq3U/terraform_1.7.5_linux_amd64.zip
      inflating: /home/cloudshell-user/.tfenv/versions/1.7.5/terraform  
    Installation of terraform v1.7.5 successful. To make this your default version, run 'tfenv use 1.7.5'
    $ 
    ```

1. バージョンを選択します
    ```
    $ tfenv use 1.7.5
    Switching default version to v1.7.5
    Default version (when not overridden by .terraform-version or TFENV_TERRAFORM_VERSION) is now: 1.7.5
    $ 
    ```
1. バージョンを確認します
    ```
    $ terraform -version
    Terraform v1.7.5
    on linux_amd64
    $ 
    ```

## S3バケットを作成
1. main.tfを作成します

    ```
    data "aws_caller_identity" "this" {}

    terraform {
      required_version = "~> 1.7.5"
      required_providers {
        aws = {
          source  = "hashicorp/aws"
          version = "~> 5.44.0"
        }
      }
    }
    
    
    provider "aws" {
      region = "ap-northeast-1"
    }
    
    resource "aws_s3_bucket" "terraform_cloudshell_demo" {
      bucket = "terraform-cloudshell-demo-${data.aws_caller_identity.this.account_id}"
    }
    ```

1. initします
    ```
    $ terraform init
    
    Initializing the backend...
    
    Initializing provider plugins...
    - Finding hashicorp/aws versions matching "~> 5.44.0"...
    - Installing hashicorp/aws v5.44.0...
    - Installed hashicorp/aws v5.44.0 (signed by HashiCorp)
    
    Terraform has created a lock file .terraform.lock.hcl to record the provider
    selections it made above. Include this file in your version control repository
    so that Terraform can guarantee to make the same selections by default when
    you run "terraform init" in the future.
    
    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.
    $ 
    ```
1. planを実行します
    ```
    $ terraform plan
    data.aws_caller_identity.this: Reading...
    data.aws_caller_identity.this: Read complete after 0s [id=XXXXXXX]
    
    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.terraform_cloudshell_demo will be created
      + resource "aws_s3_bucket" "terraform_cloudshell_demo" {
          + acceleration_status         = (known after apply)
          + acl                         = (known after apply)
          + arn                         = (known after apply)
          + bucket                      = "terraform-cloudshell-demo-XXXXXXX"
          + bucket_domain_name          = (known after apply)
          + bucket_prefix               = (known after apply)
          + bucket_regional_domain_name = (known after apply)
          + force_destroy               = false
          + hosted_zone_id              = (known after apply)
          + id                          = (known after apply)
          + object_lock_enabled         = (known after apply)
          + policy                      = (known after apply)
          + region                      = (known after apply)
          + request_payer               = (known after apply)
          + tags_all                    = (known after apply)
          + website_domain              = (known after apply)
          + website_endpoint            = (known after apply)
        }
    
    Plan: 1 to add, 0 to change, 0 to destroy.
    
    ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
    
    Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.
    $ 
    ```

1. applyします。（途中でyesを入力します)
    ```
    $ terraform apply
    data.aws_caller_identity.this: Reading...
    data.aws_caller_identity.this: Read complete after 0s [id=XXXX]
    
    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.terraform_cloudshell_demo will be created
      + resource "aws_s3_bucket" "terraform_cloudshell_demo" {
          + acceleration_status         = (known after apply)
          + acl                         = (known after apply)
          + arn                         = (known after apply)
          + bucket                      = "terraform-cloudshell-demo-XXXX"
          + bucket_domain_name          = (known after apply)
          + bucket_prefix               = (known after apply)
          + bucket_regional_domain_name = (known after apply)
          + force_destroy               = false
          + hosted_zone_id              = (known after apply)
          + id                          = (known after apply)
          + object_lock_enabled         = (known after apply)
          + policy                      = (known after apply)
          + region                      = (known after apply)
          + request_payer               = (known after apply)
          + tags_all                    = (known after apply)
          + website_domain              = (known after apply)
          + website_endpoint            = (known after apply)
        }
    
    Plan: 1 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: yes
    
    aws_s3_bucket.terraform_cloudshell_demo: Creating...
    aws_s3_bucket.terraform_cloudshell_demo: Creation complete after 2s [id=terraform-cloudshell-demo-XXXX]
    
    Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
    $ 
    ```

1. できました！
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/b7c05324-c837-74a6-478b-707023e52899.png)

# 考察
今回、Terraformのインストールから、S3の作成まで実行しました。次回以降も引き続き試してみます。

# 参考
https://www.oreilly.co.jp/books/9784814400522/

https://dev.classmethod.jp/articles/terraform-cloudshell/

https://github.com/tfutils/tfenv

https://registry.terraform.io/providers/hashicorp/aws/latest