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