色々とハマったのでメモ。
参考文献
- cloudfront_distribution - create, update and delete aws cloudfront distributions. — Ansible Documentation
- s3_bucket - Manage S3 buckets in AWS, Ceph, Walrus and FakeS3 — Ansible Documentation
当初やろうとしたこと
- cloudfront_distribution モジュールで以下のパラメーターを指定, CloudFront Distribution を作成する。
- default_origin_domain_name
- default_root_object
- origins の domain_name と s3_origin_access_identity_enabled
- 返り値を変数に格納する。今回は distribution_result とする。
- distribution_result の origins の items リスト の s3_origin_config の origin_access_identity から
origin-access-identity/cloudfront
文字列を削除した末尾の ID を取り出す。 - s3_bucket モジュールで先ほどの ID を S3 のポリシーに設定する。
ハマったこと
1. S3 のポリシーって動的に設定できるの?
結論は Yes.
それまで, templates の j2 ファイルは vars や defaults で設定した変数しか扱えないと思い込んでいたけども, 実は register した変数も扱える。
2. え, リストってどうやって取り出すの?
こいつのせいで 2 日掛かったorz
C に慣れてるせいで, 当初は distribution_result.origins.items[0]
と書いてもうまく取り出せない。
一応 distribution_result.origins
までなら取り出せることは確認。他に試した内容は以下の通り。
変数 | 値 |
---|---|
distribution_result.origins.items |
<built-in method items of dict object at 0x7fb8b8f65280 > |
distribution_result.origins.items.0 | VARIABLE IS NOT DEFINED!: builtin_function_or_method object has no element 0 |
distribution_result.origins.items[0] | VARIABLE IS NOT DEFINED!: builtin_function_or_method object has no element 0 |
distribution_result.origins.items['0'] | VARIABLE IS NOT DEFINED!: 'builtin_function_or_method object' has no attribute '0' |
で, 調べてみると, リストなんだから with_items で取り出せばいいんじゃね? という記事が。(元記事は失念orz)
そこで, 一旦以下のデバッグ出力で試してみることに。
- debug:
var: "{{ item }}"
with_items: distribution_result.origins.items
結果。
{
"distribution_result.origins.items": "<built-in method items of dict object at 0x7f3cfbb75c58>",
"item": "distribution_result.origins.items"
}
…え? built-in method items of dict object? ですか?
仕方ないので built-in method items of dict object
で調べてみると, 同じような内容で悩んでいる方はおられるものです。
しかも丁寧に,
This is happening because the name items is clashing with the name of some built-in method.
とのこと。
じゃーどーするのよ? と読み進めると, 優しい方もおられるもので, ['items']
という書き方で試せよ, と。
なるほど。やってみましょう。
- debug:
var: "{{ item }}"
with_items: distribution_result.origins['items']
結果。
{
"distribution_result.origins['items']": [
{
"custom_headers": {
"quantity": 0
},
"domain_name": "test-s3-bucket991.s3.amazonaws.com",
"id": "2018-07-20T15:05:00.375176",
"origin_path": "",
"s3_origin_config": {
"origin_access_identity": "origin-access-identity/cloudfront/E3QU476GYR1RJB"
}
}
],
"item": "distribution_result.origins['items']"
}
お! だいぶ近い! けど要素が 2 つ出来てる! orz いいや, with_items 側も括ってしまえ。
- debug:
var: "{{ item }}"
with_items: "{{ distribution_result.origins['items'] }}"
結果。
{
"<type 'dict'>": "VARIABLE IS NOT DEFINED!: ",
"item": {
"custom_headers": {
"quantity": 0
},
"domain_name": "test-s3-bucket991.s3.amazonaws.com",
"id": "2018-07-20T15:05:00.375176",
"origin_path": "",
"s3_origin_config": {
"origin_access_identity": "origin-access-identity/cloudfront/E3QU476GYR1RJB"
}
}
}
お, item が辞書からリストになりましたな。変数を連結してみましょう。
- debug:
var: "{{ item.s3_origin_config.origin_access_identity }}"
with_items: "{{ distribution_result.origins['items'] }}"
結果。
{
"item": {
"custom_headers": {
"quantity": 0
},
"domain_name": "test-s3-bucket991.s3.amazonaws.com",
"id": "2018-07-20T15:05:00.375176",
"origin_path": "",
"s3_origin_config": {
"origin_access_identity": "origin-access-identity/cloudfront/E3QU476GYR1RJB"
}
},
"origin-access-identity/cloudfront/E3QU476GYR1RJB": "VARIABLE IS NOT DEFINED!: 'origin' is undefined"
}
あれ? 一番下がそれっぽくなってる。ってことは, これにフィルターを掛ければ良さそう?
- debug:
var: "{{ item.s3_origin_config.origin_access_identity | regex_replace('origin-access-identity/cloudfront/') }}"
with_items: "{{ distribution_result.origins['items'] }}"
結果。
{
"E3QU476GYR1RJB": "VARIABLE IS NOT DEFINED!: 'E3QU476GYR1RJB' is undefined",
"item": {
"custom_headers": {
"quantity": 0
},
"domain_name": "test-s3-bucket991.s3.amazonaws.com",
"id": "2018-07-20T15:05:00.375176",
"origin_path": "",
"s3_origin_config": {
"origin_access_identity": "origin-access-identity/cloudfront/E3QU476GYR1RJB"
}
}
}
おぉ! 一番上で取り出せてますな。
ということで, 現在のソースコードの通りになりましたとさ。
3. cloudfront_distribution して直ぐに s3_bucket を実行するとエラーになるんだけどorz
Invalid principal in policy と出力される。
調査すると, cloudfront_distribution モジュール実行直後は確かに S3 ポリシー向けの ID を取得できるんだけど, 実際にはまだ有効化されてないっぽい。
苦肉の策として, pause モジュールで 10 秒待機させることにしました。
サンプルソース
と言っても, 今回作ったファイルは以下の 3 つ。
ansible-playbook -vvv 実行時の出力 JSON 抜粋
具体的なコマンド: $ ansible-playbook -vvv s3-cloudfront.yml -i '127.0.0.1,'
※1 そういえば今月の 13 日 (金) は JSON の日でした。
※2 一応, アカウント ID は伏せています。
cloudfront_distribution
{
"active_trusted_signers": {
"enabled": false,
"quantity": 0
},
"aliases": {
"quantity": 0
},
"arn": "arn:aws:cloudfront::************:distribution/E3J3Z681FITN9S",
"cache_behaviors": {
"quantity": 0
},
"caller_reference": "2018-07-20T15:05:00.375176",
"changed": true,
"comment": "Distribution created by Ansible with datetime stamp 2018-07-20T15:05:00.375176",
"custom_error_responses": {
"quantity": 0
},
"default_cache_behavior": {
"allowed_methods": {
"cached_methods": {
"items": [
"HEAD",
"GET"
],
"quantity": 2
},
"items": [
"HEAD",
"GET"
],
"quantity": 2
},
"compress": false,
"default_ttl": 86400,
"forwarded_values": {
"cookies": {
"forward": "none"
},
"headers": {
"quantity": 0
},
"query_string": true,
"query_string_cache_keys": {
"quantity": 0
}
},
"lambda_function_associations": {
"quantity": 0
},
"max_ttl": 31536000,
"min_ttl": 0,
"smooth_streaming": false,
"target_origin_id": "2018-07-20T15:05:00.375176",
"trusted_signers": {
"enabled": false,
"quantity": 0
},
"viewer_protocol_policy": "allow-all"
},
"default_root_object": "index.html",
"domain_name": "d2q6fsy0du38oe.cloudfront.net",
"enabled": true,
"http_version": "http2",
"id": "E3J3Z681FITN9S",
"in_progress_invalidation_batches": 0,
"invocation": {
"module_args": {
"alias": null,
"aliases": [],
"aws_access_key": null,
"aws_secret_key": null,
"cache_behaviors": null,
"caller_reference": null,
"comment": null,
"custom_error_responses": null,
"default_cache_behavior": null,
"default_origin_domain_name": "test-s3-bucket991.s3-website-ap-northeast-1.amazonaws.com",
"default_origin_path": null,
"default_root_object": "index.html",
"distribution_id": null,
"e_tag": null,
"ec2_url": null,
"enabled": null,
"http_version": null,
"ipv6_enabled": null,
"logging": null,
"origins": [
{
"custom_headers": {
"quantity": 0
},
"domain_name": "test-s3-bucket991.s3.amazonaws.com",
"id": "2018-07-20T15:05:00.375176",
"origin_path": "",
"s3_origin_config": {
"origin_access_identity": "origin-access-identity/cloudfront/E3QU476GYR1RJB"
}
}
],
"price_class": null,
"profile": null,
"purge_aliases": false,
"purge_cache_behaviors": false,
"purge_custom_error_responses": false,
"purge_origins": false,
"purge_tags": false,
"region": null,
"restrictions": null,
"security_token": null,
"state": "present",
"tags": {},
"validate_certs": true,
"viewer_certificate": null,
"wait": false,
"wait_timeout": 1800,
"web_acl_id": null
}
},
"is_ipv6_enabled": false,
"last_modified_time": "2018-07-20T06:05:02.012000+00:00",
"logging": {
"bucket": "",
"enabled": false,
"include_cookies": false,
"prefix": ""
},
"origins": {
"items": [
{
"custom_headers": {
"quantity": 0
},
"domain_name": "test-s3-bucket991.s3.amazonaws.com",
"id": "2018-07-20T15:05:00.375176",
"origin_path": "",
"s3_origin_config": {
"origin_access_identity": "origin-access-identity/cloudfront/E3QU476GYR1RJB"
}
}
],
"quantity": 1
},
"price_class": "PriceClass_All",
"restrictions": {
"geo_restriction": {
"quantity": 0,
"restriction_type": "none"
}
},
"status": "InProgress",
"tags": {},
"viewer_certificate": {
"certificate_source": "cloudfront",
"cloud_front_default_certificate": true,
"minimum_protocol_version": "TLSv1"
},
"web_acl_id": ""
}
pause
{
"changed": false,
"delta": 10,
"echo": true,
"rc": 0,
"start": "2018-07-20 15:05:03.088325",
"stderr": "",
"stdout": "Paused for 10.0 seconds",
"stop": "2018-07-20 15:05:13.088542",
"user_input": ""
}
s3_bucket
{
"changed": true,
"invocation": {
"module_args": {
"aws_access_key": null,
"aws_secret_key": null,
"ceph": false,
"ec2_url": null,
"force": false,
"name": "test-s3-bucket991",
"policy": "{\"Version\": \"2008-10-17\", \"Id\": \"PolicyForCloudFrontPrivateContent\", \"Statement\": [{\"Action\": \"s3:GetObject\", \"Principal\": {\"AWS\": \"arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity E3QU476GYR1RJB\"}, \"Resource\": \"arn:aws:s3:::test-s3-bucket991/*\", \"Effect\": \"Allow\", \"Sid\": \"1\"}]}",
"profile": null,
"region": null,
"requester_pays": false,
"s3_url": null,
"security_token": null,
"state": "present",
"tags": null,
"validate_certs": true,
"versioning": null
}
},
"item": {
"custom_headers": {
"quantity": 0
},
"domain_name": "test-s3-bucket991.s3.amazonaws.com",
"id": "2018-07-20T15:05:00.375176",
"origin_path": "",
"s3_origin_config": {
"origin_access_identity": "origin-access-identity/cloudfront/E3QU476GYR1RJB"
}
},
"name": "test-s3-bucket991",
"policy": {
"Id": "PolicyForCloudFrontPrivateContent",
"Statement": [
{
"Action": "s3:GetObject",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity E3QU476GYR1RJB"
},
"Resource": "arn:aws:s3:::test-s3-bucket991/*",
"Sid": "1"
}
],
"Version": "2008-10-17"
},
"requester_pays": false,
"tags": {},
"versioning": {
"MfaDelete": "Disabled",
"Versioning": "Disabled"
}
}
どっとはらい。