ASK CLI を使って Alexa Custom Skills をデプロイしよう (2) buildspec作成 〜 デプロイ

# はじめに

[前回](https://qiita.com/daisukeArk/items/ecd9687b44969917ddd7)の投稿に引き続き、本投稿ではASK CLIを利用したデプロイ方法についてご紹介します。
本投稿では、buildspecの作成〜デプロイについてをご紹介します。

# 前回のおさらい

buildspecを作成するにあたり、[前回](https://qiita.com/daisukeArk/items/ecd9687b44969917ddd7)までに決めたことのおさらいをします。

[AWS CodeBuild のビルド仕様に関するリファレンス](https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/build-spec-ref.html)

## 各フェーズについて

| フェーズ | 概要 | やること |
| :-- | :-- | :-- |
| install | インストール | ask-cliインストール |
| pre-build | ビルド前処理 | AWSCLI設定、ASKCLI設定 |
| build | ビルド処理 | 依存解決、トランスパイル |
| post-build | ビルド後処理 | ごみファイル掃除、デプロイ |

## パラメータストアについて

| 名前 | 種類 | キーID |
|:--|:--:|:--|
| alexa-aws-access-key-id | SecureString | alias/AlexaEncryptionKey |
| alexa-aws-secret-access-key | SecureString | alias/AlexaEncryptionKey |
| alexa-lwa-access-token | SecureString | alias/AlexaEncryptionKey |
| alexa-lwa-refresh-token | SecureString | alias/AlexaEncryptionKey |

## 環境変数について

| 名前 | 値 | タイプ |
|:--|:--|:--:|
| ALEXA_VENDOR_ID | {使用しているベンダーID} | プレーンテキスト |
| ALEXA_SKILL_ID | {使用しているスキルID} | プレーンテキスト |
| AWS_LAMBDA_FUNCTION_NAME | custom-skill-sample-to-convert | プレーンテキスト |

# buildspec.ymlを作ろう

さっそくファイルを作ります。

```bash:移動
$ cd ~/custom-skill-sample-to-convert
```

```bash:buildspec.ymlの作成
$ touch ./buildspec.yml
```

これを参考に順番に書いていきます。

[AWS CodeBuild のビルド仕様に関するリファレンス](https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/build-spec-ref.html)


## 変数、パラメータストアの定義

前回作成したパラメータストアを読み込む為に以下を定義します。

### コマンド

```yml
env:
  # 変数宣言
  variables:
    # AWS CLI プロファイル名
    AWS_CLI_PROFILE_NAME: alexa-custom-skill-profile
  # パラメータストア宣言
  parameter-store:
    # AWS アクセスキーID
    AWS_ACCESS_KEY_ID: "alexa-aws-access-key-id"
    # AWS シークレットアクセスキー
    AWS_SECRET_ACCESS_KEY: "alexa-aws-secret-access-key"
    # LWA アクセストークン
    LWA_ACCESS_TOKEN: "alexa-lwa-access-token"
    # LWA リフレッシュトークン
    LWA_REFRESH_TOKEN: "alexa-lwa-refresh-token"
```

## 各フェーズの定義

### install

インストールでは、以下の作業を行います。

* aws-cliのアップデート
* ask-cliのインストール

ここはさくっといきます。

#### コマンド

```yml
# ビルド環境でのパッケージのインストール
  install:
    commands:
      # メッセージ
      - echo install phase...
      # 現在のディレクトリを確認
      - pwd && ls -la
      # AWS-CLI アップデート
      - pip install awscli --upgrade
      # ASK-CLI インストール
      - npm install -g ask-cli
      # 各種バージョン確認
      - npm --version && aws --version && ask -v
```

### pre_build

ビルド前処理では、以下の作業を行います。

* AWS CLI設定
* ASK CLI設定

#### AWS CLI設定

ASK CLIはAWSアカウントの資格情報を使用します。以下は公式ドキュメントからの引用です。

[CLIのインストールと使用方法](https://developer.amazon.com/ja/docs/smapi/ask-cli-command-reference.html)

> AWSアカウントの資格情報。ask-cliツールは、次の場所にある資格情報ファイルから、それらの資格情報にアクセスします。

```text
~/.aws/credentials 
```

> 資格情報をセットアップするには、AWS CLIツールをインストールするか、手動で構成します。同じファイル内に複数の資格情報のセットを保持できますが、ask-cliツールが使用するのはデフォルトの資格情報だけです。

AWSアカウントの資格情報のセットアップはウィザード形式ではなく`aws configure set`を使用し、必要な設定値を設定します。

[AWS CLI コマンドリファレンス - aws configure set](https://docs.aws.amazon.com/cli/latest/reference/configure/set.html)

#### ASK CLIの設定

次にASK CLIの設定です。
ASK CLIの設定ファイルは以下の２種類があります。

* ~/.ask/cli_config
* ~/custom-skill-sample-to-convert/skill/.ask/config

> Amazon開発者アカウントに関連付けられているスキルをCLIで作成および変更するための認証を設定するには、initコマンドを実行する必要があります。

これらのファイルは、前述した通り`init`コマンドを実行し事前にセットアップすることで作成されます。
本投稿では、既にセットアップ済みである前提で進めます。

CodeBuild環境では`init`コマンドによるセットアップではなく、これらのファイルを手動でセットアップしてあげます。
セットアップ方法はいくつか候補があるかと思いますが、本投稿では`envsubst`コマンドを利用します。

[参考 envsubstを使ってShellでテンプレートエンジン的なことをする、Dockerコンテナ起動時にファイルを生成する - Hatena Blog](http://blue1st-tech.hateblo.jp/entry/2016/07/25/001123)

`envsubst`コマンドを利用することで環境変数を利用したファイル出力が可能となります。

予め上記２ファイルのテンプレートファイルを準備し、`envsubst`コマンドを利用しセットアップを行います。

##### テンプレートファイルの作成

```bash:移動
$ cd ~/custom-skill-sample-to-convert/skill
```

```bash:ディレクトリ作成
$ mkdir -p ./templates
```

```bash:テンプレートファイルの作成
$ touch ./templates/cli_config.tmpl
$ touch ./templates/config.tmpl
```

```json:cli_config.tmpl
{
  "profiles": {
    "default": {
      "aws_profile": "${AWS_CLI_PROFILE_NAME}",
      "token": {
        "access_token": "${LWA_ACCESS_TOKEN}",
        "refresh_token": "${LWA_REFRESH_TOKEN}",
        "token_type": "bearer",
        "expires_in": 3600,
        "expires_at": "2018-03-19T00:00:00.000Z"
      },
      "vendor_id": "${ALEXA_VENDOR_ID}"
    }
  }
}
```

```json:config.tmpl
{
  "deploy_settings": {
    "default": {
      "skill_id": "${ALEXA_SKILL_ID}",
      "was_cloned": false,
      "merge": {
        "manifest": {
          "apis": {
            "custom": {
              "endpoint": {
                "uri": "${AWS_LAMBDA_FUNCTION_NAME}"
              }
            }
          }
        }
      }
    }
  }
}
```

endpoint -> uriでは、ファンクション名以外にARNも指定できます。

#### コマンド

```yml
# ビルド前処理
  pre_build:
    commands:
      # メッセージ
      - echo pre_build phase...
      # AWS-CLI 設定
      - aws configure --profile $AWS_CLI_PROFILE_NAME set aws_access_key_id $AWS_ACCESS_KEY_ID
      - aws configure --profile $AWS_CLI_PROFILE_NAME set aws_secret_access_key $AWS_SECRET_ACCESS_KEY
      - aws configure --profile $AWS_CLI_PROFILE_NAME set default.region $AWS_REGION_NAME
      # ディレクトリ移動
      - cd ./skill
      # ASK-CLI 設定
      - mkdir -p ~/.ask && cat ./templates/cli_config.tmpl | envsubst > ~/.ask/cli_config
      # ASK-CLI スキル設定
      - mkdir -p ./.ask && cat ./templates/config.tmpl | envsubst > ./.ask/config
```


### build

ビルドでは、以下の作業を行います。

* モジュールの依存解決(すべて)
* トランスパイル
* モジュールの削除
* モジュールの依存解決(dependenciesのみ)

ここもさくっといきます。

#### コマンド

```yml
# ビルド
  build:
    commands:
      # メッセージ
      - echo build phase...
      # ディレクトリ移動
      - ls -la && cd ./lambda/custom && ls -la
      # npmインストール
      - npm install --loglevel error
      # トランスパイル
      - $(npm bin)/tsc
      # node_modules削除
      - rm -rf ./node_modules
      # パッケージ復元
      - npm install --production --loglevel warn
```


### post_build

ビルド後処理では、以下の作業を行います。

* 不要ディレクトリ、ファイルの掃除（任意）
* デプロイ

ここもさくっといきます。

#### コマンド

```yml
# ビルド後処理
  post_build:
    commands:
      # メッセージ
      - echo Build completed on
      # 不要ディレクトリ、ファイル削除
      - rm -rf coverage mytypes src tsconfig.json tslint.json
      # ２つ上のディレクトリへ移動
      - cd ../.. && ls -la
      # デプロイ
      - ask deploy
```

# 動作確認

buildspecその他ファイルを`staging`ブランチへプッシュします。

…

失敗しました＼(^o^)／

```
Phase context status code: Variables Error Message: AccessDeniedException:
User: arn:aws:sts::{account_id}:assumed-role/code-build-typescript-de-tsukuro-service-role/AWSCodeBuild-xxxx
is not authorized to perform:　ssm:GetParameters on resource:
arn:aws:ssm:us-east-1:{account_id}:parameter/alexa-aws-secret-access-key
```

前回の投稿で作成した暗号化キー／パラメータストアですが、図ではus-east-1に作成としていました。
が、東京リージョンに作成していました。。。（修正しておきます。）
us-east-1に作成していないのと、ポリシー設定をしていないので異常終了しました。

改めてus-east-1に暗号化キー／パラメータストアを作成し、プッシュします。

…

成功しました＼(^o^)／

<img width="297" alt="スクリーンショット 2018-03-19 11.03.09.png" src="https://qiita-image-store.s3.amazonaws.com/0/228453/2b6f0d89-1aec-3b2d-53bd-82909866a510.png">

# まとめ

本投稿では、以下のことを行いました。

* buildspec.ymlの作成
* `staging`ブランチへ変更をプッシュ
* デプロイの確認

前回の投稿でお伝えした課題がありつつも、自動化することによって効率は間違いなくあがると思います。

現状はASK CLIで出来ることは限られていますが、AWS CLIや他の仕組みを組み合わせることによって
より柔軟なCI/CD環境が構築できるのではないでしょうか。

本投稿がより良いアイデアの参考になれば幸いです。