AnsibleでAWS操作　Certificate Manager編

# AnsibleでAWS操作シリーズ

1. [aws-cliインストール編](http://qiita.com/bakira/items/d94fc79a032b117bf87f)
1. [EC2インスタンス編](http://qiita.com/bakira/items/ae56e8e03eed4dc75a01)
1. [S3バケット編](http://qiita.com/bakira/items/a1dfe79cfd0e3acaa1a6)
1. [CloudFrontディストリビューション編](http://qiita.com/bakira/items/611e3a51677b751f8366)
1. [Simple Email Service編](http://qiita.com/bakira/items/8147fcc7cb684beedc63)
1. Certificate Manager編
1. [Lambda編](https://qiita.com/bakira/items/d55c91328cd1f0ee9581)

# 関連記事

[aws-cli　コマンド一覧(随時追記)](http://qiita.com/bakira/items/8a9481c99ed56a367415)

# やりたかったこと

- `ACM`にて独自ドメインへの`SSL`証明書を発行
- `SSL`証明書を`CloudFront`に設定
- ~~GUIを使わずに黒い画面でコマンドを「**ッターーン!**」してかっこつけたい~~

# やったこと

## 前提

- CIサーバー(`ansible`実行サーバー)構築済み
- CLIサーバー(`aws-cli`実行サーバー)構築済み
- `Ansible`インストール済み
- `aws-cli`インストール済み
- 各サーバーへのSSH接続設定済み
- 独自ドメイン取得済み
- SESの設定済み
- CFとS3の設定済み

※ `${~}`は各環境に合わせて値を設定してください。

## 作業フロー

### 1.　SSL証明書発行リクエストの作成

```bash:command
ansible-playbook -i inventory/production create-aws-acm-request.yml
```
戻り値の値を控えます。

### 2.　SESに保存された認証確認メールから認証用URLを取得し、ブラウザからapproveする

```bash:command
ansible-playbook -i inventory/production view-aws-acm-notice-mail-url.yml
```
戻り値のURLにブラウザからアクセスする必要あり

### 3.　CloudFrontの設定を更新して、httpsでアクセス出来るようにする

```bash:command
ansible-playbook -i inventory/production update-aws-cf.yml


## ディレクトリ構成
```text

├── ansible.cfg
├── create-aws-acm-request.yml
├── templates
│   └── production
│       └── cf
│           └── update.j2
├── inventory
│   └── production
│       └── inventory
├── roles
│   ├── create-aws-acm-request
│   │   └── tasks
│   │       └── main.yml
│   ├── update-aws-cf
│   │   └── tasks
│   │       └── main.yml
│   └── view-aws-acm-notice-mail-url
│       └── tasks
│           └── main.yml
├── update-aws-cf.yml
├── view-aws-acm-notice-mail-url.yml
└── vars
    └── all.yml
```

## Ansible構成ファイル

### inventory

```ini:inventory/production/inventory
[ciservers]
${CIサーバーホスト}

[cliservers]
${CLIサーバーホスト}

[all:vars]
ENV=production

```

### vars

```yaml:vars/all.yml
AWS:
  ACM:
    ARN: ${ACMリクエストのARN}
    REGION: us-west-2
    VERIFICATION_MAIL_OBJECT: ${認証メールオブジェクト名}
  CF:
    DISTRIBUTION:
      ID: ${ディストリビューションID}
      ORIGIN_ID: ${オリジンID}
      E_TAG: ${Eタグ}
  S3:
    BUCKET:
      NAME: ${バケット名}
    END_POINT: ${S3バケットの静的ホスティングエンドポイント}
DOMAIN:
  MAIN:
    NAME: ${ドメイン名}
  SUB:
    NAME: ${サブドメイン名}
```

### templates

```json:production/cf/update.j2
{
  "DistributionConfig": {
    "Comment": "Https Bucket.",
    "CacheBehaviors": {
      "Quantity": 0
    },
    "IsIPV6Enabled": true,
    "Logging": {
      "Bucket": "",
      "Prefix": "",
      "Enabled": false,
      "IncludeCookies": false
    },
    "WebACLId": "",
    "Origins": {
      "Items": [
        {
          "OriginPath": "",
          "CustomOriginConfig": {
            "OriginSslProtocols": {
              "Items": [
                "TLSv1",
                "TLSv1.1",
                "TLSv1.2"
              ],
              "Quantity": 3
            },
            "OriginProtocolPolicy": "http-only",
            "OriginReadTimeout": 30,
            "HTTPPort": 80,
            "HTTPSPort": 443,
            "OriginKeepaliveTimeout": 5
          },
          "CustomHeaders": {
            "Quantity": 0
          },
          "Id": "{{ AWS.CF.ID }}",
          "DomainName": "{{ AWS.S3.END_POINT }}"
        }
      ],
      "Quantity": 1
    },
    "DefaultRootObject": "index.html",
    "PriceClass": "PriceClass_All",
    "Enabled": true,
    "DefaultCacheBehavior": {
      "TrustedSigners": {
        "Enabled": false,
        "Quantity": 0
      },
      "LambdaFunctionAssociations": {
        "Quantity": 0
      },
      "TargetOriginId": "{{ AWS.CF.ID }}",
      "ViewerProtocolPolicy": "redirect-to-https",
      "ForwardedValues": {
        "Headers": {
          "Quantity": 0
        },
        "Cookies": {
          "Forward": "all"
        },
        "QueryStringCacheKeys": {
          "Quantity": 0
        },
        "QueryString": true
      },
      "MaxTTL": 604800,
      "SmoothStreaming": false,
      "DefaultTTL": 604800,
      "AllowedMethods": {
        "Items": [
          "HEAD",
          "GET"
        ],
        "CachedMethods": {
          "Items": [
            "HEAD",
            "GET"
          ],
          "Quantity": 2
        },
        "Quantity": 2
      },
      "MinTTL": 604800,
      "Compress": false
    },
    "CallerReference": "2017-07-29_06-25-01",
    "ViewerCertificate": {
      "SSLSupportMethod": "sni-only",
      "ACMCertificateArn": "{{ AWS.ACM.ARN }}",
      "MinimumProtocolVersion": "TLSv1",
      "Certificate": "{{ AWS.ACM.ARN }}",
      "CertificateSource": "acm"
    },
    "CustomErrorResponses": {
      "Quantity": 0
    },
    "HttpVersion": "http2",
    "Restrictions": {
      "GeoRestriction": {
        "RestrictionType": "none",
        "Quantity": 0
      }
    },
    "Aliases": {
      "Items": [
        "{{ DOMAIN.MAIN.NAME }}"
      ],
      "Quantity": 1
    }
  },
  "Id": "{{ AWS.CF.DISTRIBUTION.ID }}",
  "IfMatch": "{{ AWS.CF.DISTRIBUTION.E_TAG }}"
}
```

### playbook

```yaml:create-aws-acm-request.yml
- hosts: cliservers
  roles:
    - create-aws-acm-request
  vars_files:
    - vars/all.yml
```

```yaml:update-aws-cf.yml
- hosts: cliservers
  roles:
    - update-aws-cf
  vars_files:
    - vars/all.yml
```

```yaml:view-aws-acm-notice-mail-url.yml
- hosts: cliservers
  roles:
    - view-aws-acm-notice-mail-url
  vars_files:
    - vars/all.yml
```

### tasks

```yaml:role/create-aws-acm-request/tasks/main.yml
- name: "Create Certificate"
  shell: |
    aws acm request-certificate \
    --domain-name {{ DOMAIN.MAIN.NAME }} \
    --subject-alternative-names {{ DOMAIN.SUB.NAME }} \
    --domain-validation-options DomainName={{ DOMAIN.SUB.NAME }},ValidationDomain={{ DOMAIN.MAIN.NAME }} \
    --region={{ AWS.ACM.REGION }} 
  register: result
  changed_when: False

- debug: var=result.stdout_lines
  when: result | success
  tags:
    - always
```

```yaml:role/view-aws-acm-notice-mail-url/tasks/main.yml
- name: Download Verification Mail Object
  shell: |
    aws s3 cp \
    s3://${AWS.S3.BUCKET.NAME}/{{ AWS.ACM.VERIFICATION_MAIL_OBJECT }} \
    {{ TEMP.DIRECTORY }}.{{ AWS.ACM.VERIFICATION_MAIL_OBJECT }}
  register: result
  changed_when: False

- debug: var=result.stdout_lines
  when: result | success
  tags:
    - always

- name: View URL
  shell: |
    grep https {{ TEMP.DIRECTORY }}.{{ AWS.ACM.VERIFICATION_MAIL_OBJECT }} | grep context
  register: result
  changed_when: False

- debug: var=result.stdout_lines
  when: result | success
  tags:
    - always
```

```yaml:role/update-aws-cf/tasks/main.yml
- name: Create Json
  template: 
    src={{ ENV }}/cf/update.j2
    dest={{ TEMP.DIRECTORY }}/update.json
  tags:
    - always

- name: Update Distribution
  shell: |
    aws cloudfront update-distribution \
    --cli-input-json {{ TEMP.DIRECTORY }}/update.json
  register: result
  changed_when: False

- debug: var=result.stdout_lines
  when: result | success
  tags:
    - always

```

## 終わりに

これで今まで`http`でアクセスしてた`S3`バケット(`CloudFront`経由)にアクセスすると、`https`にリダイレクトされた上で正常に表示されると思います。

`AWS`+`CloudFrount`+`ACM`+`S3`を使えばほぼ無料で`SSL`証明証の発行や紐付けが出来、自動更新の設定や複数ドメインの`SSL`証明書をまとめて管理することが出来ます。

自分は以下のようなWordpressプラグインとAWSサービスで静的サイトを運用していますが、今のところ問題なく運用出来ています。

`Wordpress`+`staticpress`+`staticpresss3`+`S3`+`CloudFront`+`ACM`

`Wordpress`を静的サイト化することで一部機能が制限されてしまいますが、セキュリティ観点がかなり向上します。

また、制限される部分も特に不要な機能だと思いますし、`JavaScript`等である程度保管することも可能なので動的サイトを切り捨てて静的サイトにするメリットは十分あると思いますので、興味のある方は是非お試し下さい♪


じゃあの。