TerraformでオブジェクトをS3へアップロードし、etagでファイル更新を管理する方法

# やりたいこと
・TerraformでAWS S3バケットとオブジェクトをデプロイしたい
・ローカルでs3オブジェクト（ファイル）を編集したらAWSへ連動できるか検証したい

# 前提
・AWSアカウント作成済み
・AWS IAMユーザーを作成し、access_keyとsecret_keyを発行済み
・Terraformインストール済み
・AWS CLIインストール済み
・VSCODEインストール済み
　（お好みのエディターで大丈夫）

# 環境
```
$ terraform -v 
Terraform v1.5.5
on windows_amd64
+ provider registry.terraform.io/hashicorp/aws v5.13.1
```
# 構築リソース
・AWS S3バケット
・S3バケットへアップロードするオブジェクト

# ディレクトリ構成
```
root
  ┣━ www
  ┃  ┗━ index.html
  ┣━ main.tf
  ┣━ variables.tf
  ┗━ s3.tf

```

# ソースの中身
### main.tf
main.tfには基本、全体共通で重要な設定、例えば「AWS プロバイダの設定」を書く
```ruby:main.tf
# AWS プロバイダの設定
provider "aws" {
  access_key = var.access_key
  secret_key = var.secret_key
  region     = var.region
}

```
### variables.tf

・variables.tfに全ソース共通の変数を書く
・リージョンは東京にしたが、適宜変更して大丈夫
・access_keyとsecret_keyは事前に発行したやつを使用
```ruby:variables.tf
variable "region" {
  default = "ap-northeast-1"
}

variable "access_key" {
  default = "自分のaccess_key"
}

variable "secret_key" {
  default = "自分のsecret_key"
}


```

### s3.tf

・一つS3バケットを構築し、指定したローカルパス配下のファイルをオブジェクトとしてS3へアップロード
・Terraformの**etag**でリソースを管理すると、ローカルにあるリソースを修正した場合、AWS側も連動で更新できる
・key（S3側、フォルダの階層は自由に指定できる）とsource（ファイルのローカルパス）を一緒じゃなくても大丈夫

```ruby:s3.tf
resource "aws_s3_bucket" "terraform-s3-20230829" {
  bucket = "terraform-s3-20230829"
}

resource "aws_s3_object" "index_page" {
  bucket = aws_s3_bucket.s3.id
  #S3へアップロードするときのkey値
  key = "www/src/index.html"
  #ファイルのローカルパス
  source = "www/index.html"
  content_type = "text/html"
  etag = filemd5("www/index.html")
}

```
:::note info
※補足
そもそも、S3にはフォルダという概念がなくて、**key-value** でオブジェクトを格納してる
S3へオブジェクトをアップロードするときは、"www/src/index.html"のようにkeyを指定し、フォルダを作成しファイルを格納するような動作に見えるが、実はそれはフォルダじゃなく、**プレフィックス** という概念である
:::

### index.html
テスト内容だけ
```ruby:index.html
test
```


# TerraformでオブジェクトをS3へアップロードする
早速用意したs3.tfを実行しよう

```
terraform plan 
```
S3バケット1つとオブジェクト1つをaddしてくれたそうです
```ruby:実行結果
Plan: 2 to add, 0 to change, 0 to destroy.
```
次に、AWSへデプロイしよう
```
terraform apply 
```
```ruby:実行結果
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
```
無事デプロイできたようで、AWSを確認しよう～

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/4cb4676e-914f-9109-315f-6569e5d1d971.png)

指定した"www/src/index.html"と同じプレフィックスとなってますね。

# ローカルでファイルを編集しTerraformで再アップする
tfファイルはもちろんTerraformで管理されてるが、オブジェクトの元のファイル（index.html）はちゃんとTerraformで管理されてるか不安なので、、、

少し修正をかけて、再デプロイしよう～

まず、s3.tfにaws_s3_objectのプレフィックスを変えよう～｜


```ruby:s3.tf
resource "aws_s3_bucket" "terraform-s3-20230829" {
  bucket = "terraform-s3-20230829"
}

resource "aws_s3_object" "index_page" {
  bucket = aws_s3_bucket.s3.id
  #key値を"www/src/index.html"から以下に修正
  key = "www/index.html"
  source = "www/index.html"
  content_type = "text/html"
  etag = filemd5("www/index.html")
}

```
そして、index.htmlの中身をちょっと修正

```ruby:index.html
test again
```

実行計画を変更する

```
terraform plan 
```
オブジェクト1つ（古いプレフィックス）をaddし、オブジェクト1つ（新しいプレフィックス）をdestroyしてくれるそうです。

```ruby:実行結果
Plan: 1 to add, 0 to change, 1 to destroy.
```

変更分をAWSへデプロイしよう
```
terraform apply 
```
```ruby:実行結果
Apply complete! Resources: 1 added, 0 changed, 1 destroyed.
```
ちゃんとプレフィックスを変えてくれたね～
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/a0440fb8-be1a-28b1-1bf1-2f768d53d126.png)

そして、S3からファイルをダウンロードし、中身を確認しよう

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/aa7fd19a-de86-ef3f-b2c8-1782eaaaf077.png)

s3.tfだけじゃなく、**etag**を使うことで、ちゃんとオブジェクトの元となるファイル（index.html）の修正分も検知し、S3へアップロードしてくれたね！Good！

# 終わりに
お疲れ様です！今回のやりたいことが無事終わった！
・TerraformでAWS S3バケットとオブジェクトをデプロイしたい
 ⇒aws_s3_bucketとaws_s3_objectでリソースを作れるよ～
・ローカルでs3オブジェクト（ファイル）を編集したらAWSへ連動できるか検証したい
 ⇒etagで管理すれば連動できるよ～