Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

AWS: S3 を origin とする CloudFront を設定する Ansible Role

More than 1 year has passed since last update.

色々とハマったのでメモ。

参考文献

当初やろうとしたこと

  1. cloudfront_distribution モジュールで以下のパラメーターを指定, CloudFront Distribution を作成する。
    • default_origin_domain_name
    • default_root_object
    • origins の domain_name と s3_origin_access_identity_enabled
  2. 返り値を変数に格納する。今回は distribution_result とする。
  3. distribution_result の origins の items リスト の s3_origin_config の origin_access_identity から origin-access-identity/cloudfront 文字列を削除した末尾の ID を取り出す。
  4. 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"
    }
}

どっとはらい。

ryo-sato
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away