TerraformでS3オブジェクトを作成する方法いろいろ
TerraformでS3オブジェクトを作成する方法がいくつかあったので まとめてみます。
以下3通りで aws_s3_object を作成してみました。
- 方法1: source を指定する
- 方法2: content を指定する
- 方法3: content_base64 を使う
方法1: source を指定する
source 引数を使う方法です。ローカルにあるファイルのパスを指定します。
以下サンプルになります。 ローカルにある "src/clanyan.json" を S3オブジェクトとしてアップロードしています。
resource "aws_s3_object" "clanyan_source" { # アップロード元(ローカル) source = "src/clanyan.json" # アップロード先(S3) bucket = "DOC-EXAMPLE-BUCKET" key = "clanyan-from-source.json" # エンティティタグ (ファイル更新のトリガーに必要) etag = filemd5("src/clanyan.json") }
ファイル更新を検知できるように etag もしくは source_hash 引数を指定しましょう。 (使い分けについては後述のTipsで説明してます)。
方法2: content を指定する
content 引数を使う方法です。直接文字列を打ち込みます。
以下サンプルになります。jsonencode を使ってテキスト(JSON)をアップロードします。 (heredoc あたりを使って直列文字列を指定しても良いでしょう)。
resource "aws_s3_object" "clanyan_content" { bucket = "DOC-EXAMPLE-BUCKET" key = "clanyan-from-content.json" content = jsonencode( { "name" : "くらにゃん", "type" : "三毛猫", "features" : [ "フレンドリー", "優秀なアシスタント", ], "hobbies" : [ "プログラミング", "翻訳", ], "favorite_food" : "さかな", } ) }
content の中身を変更すれば、それが更新トリガーになります。 以下に terraform plan のサンプルを記載します。
Terraform will perform the following actions: # aws_s3_object.clanyan_content will be updated in-place ~ resource "aws_s3_object" "clanyan_content" { ~ content = jsonencode( ~ { ~ hobbies = [ "プログラミング", - "翻訳", + "またたび", ] name = "くらにゃん" # (3 unchanged attributes hidden) } ) id = "clanyan-from-content.json" tags = {} + version_id = (known after apply) # (10 unchanged attributes hidden) } ...
ただし、terraform 外の変更を検知したい場合は、 やはり etag が必要です。 以下のようにすると良いでしょう。
locals { clanyan = { "name" : "くらにゃん", "type" : "三毛猫", "features" : [ "フレンドリー", "優秀なアシスタント", ], "hobbies" : [ "プログラミング", "翻訳", ], "favorite_food" : "さかな", } } resource "aws_s3_object" "clanyan_content" { bucket = "DOC-EXAMPLE-BUCKET" key = "clanyan-from-content.json" content = jsonencode(local.clanyan) etag = md5(jsonencode(local.clanyan)) }
方法3: content_base64 を使う
content_base64 引数を使う方法です。 Base64エンコードされた文字列を指定します。
以下サンプルになります。 base64encode を使っています。
resource "aws_s3_object" "clanyan_content_base64" { bucket = "DOC-EXAMPLE-BUCKET" key = "clanyan-from-content-base64.json" content_base64 = base64encode( jsonencode( { "name" : "くらにゃん", "type" : "三毛猫", "features" : [ "フレンドリー", "優秀なアシスタント" ], "hobbies" : [ "プログラミング", "翻訳" ], "favorite_food" : "さかな" } ) ) }
Tips
使い分け
基本的には source で良いのかなと思います。
ただし小規模の情報量で、 わざわざファイルを分ける必要性が無い場合は content が取り回しが良さそうです。 Terraform 内のリソースや変数を参照できるのもメリットです。
etag と source_hash の使い分け
etag と source_hash は、両方とも更新をトリガーするのに役立つ引数です。
違いは「AWS側で保持される値かどうか」です。 source_hash は AWS側では保持されないです。
Terraformステート | AWS側(Etag) | |
---|---|---|
etag | 保持される | 保持される |
source_hash | 保持される | ★保持されない |
source_hash は オブジェクトのEtag が「MD5ダイジェストでない」ときに役に立ちます。 具体的には以下のパターンのときです。
- オブジェクトが SSE-C もしくは SSE-KMS によって暗号化されている
- オブジェクトが Multipart Upload で作成される
- オブジェクトが 16MB より大きい ( ※ Multipart Upload になるため )
例えば 16MB 以上のファイルを etag 引数を指定してアップロードしたとします(以下コード)。
resource "aws_s3_object" "very_large_clanyan_pkg" { bucket = "DOC-EXAMPLE-BUCKET" key = "very-large-clanyan.pkg" source = "src/very-large-clanyan.pkg" etag = filemd5("src/very-large-clanyan.pkg") }
一応アップロードは成功します。 しかし、その後 何も更新がない状態でも、 terraform plan(apply) でずっと差分が出てきます。
Terraform will perform the following actions: # aws_s3_object.very_large_clanyan_pkg will be updated in-place ~ resource "aws_s3_object" "very_large_clanyan_pkg" { ~ etag = "16febd313367913f7e719a49664c7c1c" -> "408a517f9e35e9e927d9af97ae1aee00" id = "very-large-clanyan.pkg" tags = {} + version_id = (known after apply) # (10 unchanged attributes hidden) } Plan: 0 to add, 1 to change, 0 to destroy.
これは「Terraformステート側の etag は MD5ダイジェストの値である」が、 「AWS側の etag は MD5ダイジェストの値ではない」ために差分と判定されていることが原因です。
そこで source_hash の出番です。以下のように書きましょう。 source_hash はTerraformステート側にのみ保存される値なので、ローカルでの更新をちゃんと検知できるようになります。
resource "aws_s3_object" "very_large_clanyan_pkg" { bucket = "DOC-EXAMPLE-BUCKET" key = "very-large-clanyan.pkg" source = "src/very-large-clanyan.pkg" source_hash = filemd5("src/very-large-clanyan.pkg") }
aws_s3_bucket_object は非推奨
似たようなリソースとして aws_s3_bucket_object がありますが、こちらは廃止予定なので使わないようにしましょう。
NOTE:
The
aws_s3_bucket_object
resource is DEPRECATED and will be removed in a future version! Useaws_s3_object
instead, where new features and fixes will be added. When replacingaws_s3_bucket_object
withaws_s3_object
in your configuration, on the next apply, Terraform will recreate the object. If you prefer to not have Terraform recreate the object, import the object usingaws_s3_object
.
おわりに
以上、 aws_s3_object を色んな方法で作成してみました。 参考になれば幸いです。
参考
- aws_s3_object | Terraform Registry
- filemd5 - Functions - Configuration Language | Terraform | HashiCorp Developer
- jsonencode - Functions - Configuration Language | Terraform | HashiCorp Developer
- base64encode - Functions - Configuration Language | Terraform | HashiCorp Developer
- オブジェクトの整合性をチェックする - Amazon Simple Storage Service