serverless.ymlのちょっと便利な書き方 with serverless-offline on docker


# やりたかったこと

- serverless.ymlの`provider.environment`にはローカル環境とデプロイする環境で別の値を入れたい
    - デプロイする環境では同yaml内で作成したリソースのID等を`Ref:`して定義したい
        - `resources`はcloudformationなので`Ref:`等が使えるのは知ってる
        - `provider.environment`とかでも`Ref:`したいんだけどできるのかな？
    - ローカル環境ではコンテナの環境変数を参照して定義したい
        - ローカルは [serverless-offline on docker](https://qiita.com/sugo/items/f8ef40aa51a85eac157d) な環境 
        - そこではリソースの作成は行われないので、コンテナの環境変数を読む必要があった

# 結果

- `Ref:`できた
    - `custom`にて、作成したリソースの情報を取得している
    - 全てを試してはいないが、同yaml内であればどこでも使えそうな感じ
    - [ただし、`!Ref`といった記述はできないっぽい](https://forum.serverless.com/t/sqs-ref-in-serverless-yml/2739)
- 環境変数を読む / Ref:するに関して
    - ステージごとに`provider.environment`を制御することで実現できた
    - デプロイ時には`Ref:`の値を利用し、コンテナのserverless-offlineでは環境変数を読み込む

# serverless.yml

ちょこちょこ情報は削ってますが、こんな感じのyamlになります
cognitoのリソースを作成している

```yaml:serverless.yml
provider:
  name: aws
  runtime: nodejs8.10
  region: ${env:AWS_REGION}
  stage: ${opt:stage, self:custom.defaultStage}
  environment:
    COGNITO_DOMAIN: ${env:COGNITO_DOMAIN}
    COGNITO_CLIENT_ID: ${self:custom.cognito.clientId.${self:provider.stage}}
    COGNITO_USER_POOL_ID: ${self:custom.cognito.userPoolId.${self:provider.stage}}
  vpc:
    securityGroupIds:
      - ${env:SECURITY_GROUP_ID}
    subnetIds:
      - ${env:SUBNET_ID_0}
      - ${env:SUBNET_ID_1}
  iamRoleStatements:
    - Effect: "Allow"
      Action:
        - "ec2:CreateNetworkInterface"
        - "ec2:DescribeNetworkInterfaces"
        - "ec2:DeleteNetworkInterface"
        - "cognito-idp:*"
      Resource:
        - "*"

custom:
  defaultStage: dev
  cognito:
    clientId:
      dev:
        Ref: UserPoolClient
      local: ${env:COGNITO_CLIENT_ID}
    userPoolId:
      dev:
        Ref: UserPool
      local: ${env:COGNITO_USER_POOL_ID}

functions:
  # 〜略〜

resources:
  Resources:
    UserPool:
      Type: AWS::Cognito::UserPool
      Properties:
        # 〜略〜
    UserPoolClient:
      Type: AWS::Cognito::UserPoolClient
      Properties:
        # 〜略〜
        UserPoolId:
          Ref: UserPool
```

## 解説

コンテナで叩くコマンド

```
$ serverless offline --stage local
```


```yaml:serverless.ymlの状態
COGNITO_CLIENT_ID: ${env:COGNITO_CLIENT_ID}
COGNITO_USER_POOL_ID: ${COGNITO_USER_POOL_ID}
```


デプロイする時のコマンド

```
$ serverless deploy
```


```yaml:serverless.ymlの状態
COGNITO_CLIENT_ID:
  Ref: UserPoolClient
COGNITO_USER_POOL_ID:
  Ref: UserPool
```



- **opt:stage**
    - コマンドラインオプションの `--stage` で指定した値
- **self:custom.defaultStage**
    - コマンドラインオプション `--stage` の指定がなかった場合、`custom.defaultStage`がデフォルト値となる


- **\${self:custom.cognito.clientId.${self:provider.stage}}**
    - ${self:provider.stage} = `dev` または `--stageの引数`
    - つまり今回の場合、`custom.cognito.clientId.dev` または `custom.cognito.clientId.local`


ちなみに

- **self**
    - このserverless.yml自身を指す
- **custom**
    - 独自定義した値


# docker-compose.yml

```yaml:docker-compose.yml
version: '3'
services:
  slsdb:
    image: mariadb:10.2
    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_general_ci --skip-character-set-client-handshake
    ports:
    - "3306:3306"
    environment:
    - MYSQL_ROOT_PASSWORD
    - MYSQL_DATABASE
  slsapp:
    build: .
    command: npx serverless offline --stage local --host 0.0.0.0 --port 3000
    volumes:
    - .:/application
    - /application/node_modules
    ports:
    - "3000:3000"
    depends_on:
    - slsdb
    environment:
    - SECURITY_GROUP_ID
    - SUBNET_ID_0
    - SUBNET_ID_1
    - MYSQL_ROOT_PASSWORD
    - MYSQL_DATABASE
    - MYSQL_USER
    - MYSQL_HOST
    - MYSQL_PORT
    - COGNITO_CLIENT_ID
    - COGNITO_USER_POOL_ID
    - COGNITO_DOMAIN
```

## 解説

コンテナを立ち上げ時、コマンド `npx serverless offline --stage local --host 0.0.0.0 --port 3000` を実行する

`--stage local`オプションを指定しているため、[serverless.yml](#serverlessyml)で記載した通り、serverless.ymlでは`local`の値を使用してserverless-offlineが起動する


また、AWSリソースの作成は行われないため、一度デプロイしたあと、`COGNITO_CLIENT_ID`等の値をコンテナの環境変数に設定しないといけないところは注意。