見出し画像

#1 【Serverless】 serverless.ymlでの環境管理とAWSリソースの記述方法

 Queueソフトウェアチームの塩月です。弊社でも昨年からserverlessを触り始め、AppSync、Cognito、Lambda、APIGateway、DynamoDBなどのAWSリソースを実装に取り入れています。アプリケーション開発する際はほぼクライアントサイドだけで実装可能になってきたため、高速に実装を行えるようになりました。開発する中で知らないこともあるのでネットで調べるわけですが、リファレンスがしっかりまとまっていないことも多々あり調査の時間が嵩むこともしばしばです。

とりわけserverless.ymlでの各リソース+環境管理、serverless + モバイル(iOS, android)の記事はあまり投稿されていないようなので、

①serverless.ymlでの各リソース+環境管理

②serverless + モバイル(iOS, android)

の2回に分けて記事を投稿していきたいと思います。

ということで今回は①serverless.ymlでの各リソース+環境管理を紹介します。

AWSにサインインして各リソースの画面から作成や更新を行うこともできますが、環境を変更するたびにその作業を繰り返すのは面倒です。ほとんどのリソースはymlで管理することができるようになっているため、serverless.ymlに記述してデプロイすればCloud Formation経由で各リソースを環境ごとに管理することが可能です。

AppSync、Cognito、DynamoDB、Lambda、APIGatewayを一括で記述する方法を以下の流れで説明していきます。

1. 環境管理方法

2. AppSyncの記述方法

3. Cognitoの記述方法

4. DynamoDBの記述方法

5. IAMロールの記述方法

6. Lambda + APIGatewayの記述方法

各リソース毎に記述例を紹介していますが、サンプル全体はこちらで公開しています。

Queue-inc/base__serverless_yml
Contribute to Queue-inc/base__serverless_yml development by c
github.com


1. 環境管理方法

まずは環境ごとに分けるための記述方法ですが、以下のように書くと楽になります。

provider:
 stage: ${opt:stage, self:custom.defaultStage}
 profile: ${opt:profile, self:custom.defaultProfile}
 region: ${opt:region, self:custom.defaultRegion}
custom:
 defaultStage: dev
 defaultProfile: default
 defaultRegion: ap-northeast-1
 otherfile:
   environment:
     dev: ${file(./conf/dev.yml)}
     staging: ${file(./conf/staging.yml)}
     prod: ${file(./conf/prod.yml)}


defaultStageにdev, staging, prodなど環境を指定してあげれば、provider以下のstageで指定した環境を読み込むことができ、以降はself:provider.stageを呼ぶことで環境を記述する箇所で使うことができます。

sls deploy --stage [ステージ名]

この場合ymlでデフォルトはdevを指定しているので[ステージ名]に何も指定しなければdevが適用されます。他の環境名を指定するとその環境を読み込むことができます。

environmentには環境変数を記述した外部ファイルを用意して読みこむとコマンドラインだけで簡単に各環境にデプロイすることができます。

各リソース管理の際にはリソース名+${self:provider.stage}の記述にしておくことでどの環境のものか判別が容易になります。

次は各リソースの記述方法についてです。


2. AppSyncの記述方法

custom:
 appSync:
   name: # appsync名
   authenticationType: AMAZON_COGNITO_USER_POOLS # 認証先 AMAZON_COGNITO_USER_POOLS / API_KEY /* ここを埋める */
     userPoolConfig:
       awsRegion: # poolのregion /* ここを埋める */
       userPoolId: # userpoolのID /* ここを埋める */
       defaultAction: ALLOW # ALLOW / DENY /* ここを埋める */
     schema: # schema default → schema.graphql /* ここを埋める */
     dataSources: # データソース
       - type: AMAZON_DYNAMODB # AMAZON_DYNAMODB / AMAZON_ELASTICSEARCH / AWS_LAMBDA ... /* ここを埋める */
         name: # データソース名 /* ここを埋める */
         description: # 説明文 /* ここを埋める */
         config:
           tableName: # テーブル名 /* ここを埋める */
           serviceRoleArn: # ロールARN /* ここを埋める */
           region: # リージョン /* ここを埋める */
     mappingTemplates:
       - dataSource: # データソース名 /* ここを埋める */
         type: # Query / Mutation /* ここを埋める */
         field: # フィールド名 /* ここを埋める */
         request: # 'hogehoge-request.vtl' /* ここを埋める */
         response: # 'hogehoge-response.vtl' /* ここを埋める */


/* ここを埋める */の部分には任意の設定値を記述してください。複数のAppSyncを管理したい場合(各AppSyncに応じてデータソースが異なるなど)は[name]の先頭にハイフンをつけて[- name]とすることで複数記述することが可能です。

serviceRoleArnにはデータソースのDynamoDBに対するアクションを許可したポリシーを持つロールを指定します。ロールもserverless.ymlに記述することで作成可能なので一括で管理するのが良いと思います。

mapping-templateはvtlという拡張子のファイルを他のディレクトリから読み込むようにしています。

serverless.ymlでのAppSyncの管理はこちらのpluginを使用してデプロイが可能です。

plugins:
 - serverless-appsync-plugin


3. Cognitoの記述方法

ユーザープールとアイデンティティを含めると少し記述が長くなります。細かい設定でまだ指定できないものもあるようです。

custom:
UserPool:
Type: AWS::Cognito::UserPool                                                     
Properties:
  UserPoolName: # プール名 /* ここを埋める */
  AdminCreateUserConfig:
    AllowAdminCreateUserOnly: false # admin以外でユーザー作成の権限を与えるかの有無 true / false /* ここを埋める */
    UnusedAccountValidityDays: 7 # 使用されていないアカウントの有効日数 /* ここを埋める */
  AliasAttributes: # aliasの属性
    - email # /* ここを埋める */
  AutoVerifiedAttributes: # 属性の検証 email / phone_number 
    - email # /* ここを埋める */
  EmailVerificationMessage: "認証コードは {####} です。" # メール認証での文言 /* ここを埋める */
  EmailVerificationSubject: "認証コード発行" # メール認証のタイトル /* ここを埋める */
  MfaConfiguration: 'OFF' # 他要素認証 'ON' / 'OFF' / 'OPTIONAL' /* ここを埋める */
  Policies:
    PasswordPolicy: # パスワードポリシー
      MinimumLength: 8 # パスワードの長さ /* ここを埋める */
      RequireLowercase: false # 小文字を必要とするか true / false /* ここを埋める */
      RequireNumbers: false # 数字を必要とするか true / false /* ここを埋める */ 
      RequireSymbols: false # 特殊記号を必要とするか true / false /* ここを埋める */
      RequireUppercase: false # 大文字を必要とするか true / false /* ここを埋める */
  Schema: # 標準の属性に存在しないものを記述するとカスタム属性に追加される
    - AttributeDataType: String # データ型 /* ここを埋める */
      DeveloperOnlyAttribute: false # 属性タイプが開発者のみかの有無 true / false
      Mutable: true # 後からの変更を許可するか /* ここを埋める */
      Name: # カスタム属性名 /* ここを埋める */
      StringAttributeConstraints:
        MaxLength: 256 # 最長文字数 /* ここを埋める */
        MinLength: 0 # 最短文字数 /* ここを埋める */
     Required: false # 必須の有無 /* ここを埋める */                 
UserPoolClient: # 複数のuserpoolを作成する場合はここの名前を変更して追加する
Type: AWS::Cognito::UserPoolClient
Properties:
  ClientName: # アプリクライアント名 /* ここを埋める */
  UserPoolId:
    Ref: # UserPoolを指定するとUserPoolから反映される、または名前を指定する /* ここを埋める */
  ExplicitAuthFlows:
    - ADMIN_NO_SRP_AUTH # 認証フロー ADMIN_NO_SRP_AUTH / CUSTOM_AUTH_FLOW_ONLY / USER_PASSWORD_AUTH /* ここを埋める */
  RefreshTokenValidity: 30 # tokenの更新日数 /* ここを埋める */           
  ReadAttributes: # カスタム属性
    - custom:custom-attributes # カスタム属性がある場合は'custom-attributes'の箇所に追加する /* ここを埋める */
  WriteAttributes:          
    - custom:custom-attributes # カスタム属性がある場合は'custom-attributes'の箇所に追加する /* ここを埋める */
  GenerateSecret: false # クライアントシークレット作成の有無 true / false /* ここを埋める */
CognitoIdentityPool:
  Type: AWS::Cognito::IdentityPool        
  Properties:
    IdentityPoolName: # プール名
    AllowUnauthenticatedIdentities: true # 認証されていないユーザーの許可有無 true / false
CognitoIdentityPoolRoles:        
  Type: AWS::Cognito::IdentityPoolRoleAttachment
  Properties:
    IdentityPoolId:
      Ref: UserCognitoIdentityPool
    Roles:
      authenticated: 
        Fn::Join:
          - ''
          -
            - 'arn:aws:iam:'
            - ':'
            - Ref: AWS::AccountId
            - ':role/'
            - Ref: UserCognitoAuthRole
      unauthenticated: 
        Fn::Join:
          - ''
          -
            - 'arn:aws:iam:'
            - ':'
            - Ref: AWS::AccountId
            - ':role/'
            - Ref: UserCognitoUnAuthRole


複数のユーザープールとプールクライアントを管理する場合は[UserPool]と[UserPoolClient]の箇所を任意の名前に変更することで可能です。


4. DynamoDBの記述方法

DynamoDBの記述方法はネットに公開されていますがまとめとして一応紹介しておきます。

resources:
Resources:
  # DynamoDbの設定
  Table: # 名前 /* ここを埋める */
    Type: AWS::DynamoDB::Table
    Properties:
      TableName: # テーブル名 /* ここを埋める */
      AttributeDefinitions: # Hashkey, Rangekey, GSIのHashkey, Rangekeyに指定する場合はここに記述が必須
        - AttributeName: # 属性名 /* ここを埋める */
          AttributeType: # 属性の型 ex. S, N, B ...etc /* ここを埋める */
      KeySchema:
        - AttributeName: # AttributeDefinitionsで指定したものの名前 /* ここを埋める */
          KeyType: # HASH / RANGE /* ここを埋める */
      ProvisionedThroughput:
        ReadCapacityUnits: # 読み込みスループット ex. 1, 2, 3 ...etc /* ここを埋める */
        WriteCapacityUnits: # 書き込みスループット ex. 1, 2, 3 ...etc /* ここを埋める */
      GlobalSecondaryIndexes: # GSI 
        - IndexName: # インデックス名 /* ここを埋める */
          KeySchema:
            - AttributeName: # インデックスの属性 /* ここを埋める */
              KeyType: # HAAH / RANGE /* ここを埋める */
          ProvisionedThroughput:
            ReadCapacityUnits: # 読み込みスループット ex. 1, 2, 3 ...etc /* ここを埋める */
            WriteCapacityUnits: # 書き込みスループット ex. 1, 2, 3 ...etc /* ここを埋める */
          Projection:
            ProjectionType: # 射影される属性 ALL / INCLUDE / KEYS_ONLY /* ここを埋める */


複数テーブルは[Table]を任意の名前に変更して同様に記述します。ポイントはインデックスやHash key、Range keyに指定するものは全てAttributeDefinitionsのAttributeName、AttributeTypeに記述しておくことです。こうしておかないとデプロイで失敗します。


5. IAMロールの記述方法

AppSyncやCongnitoにIAMロールの記述が必要です。

下記はAppSyncにDynamoDBのアクセス権限を許可するIAMロールの記述例です。

AppSyncDynamoRole:
  Type: AWS::IAM::Role
  Properties:
    Path: /
    RoleName: Test-AppSyncRole-${self:provider.stage}
    AssumeRolePolicyDocument:
      Version: '2012-10-17'
      Statement:
        - Effect: Allow
          Principal:
            Service: 'lambda.amazonaws.com'
          Action:
            - 'sts:AssumeRole'
    Policies:                       
      - PolicyName: Test-AppSyncPolicy-${self:provider.stage}
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
            - Effect: 'Allow'
              Action:
                - 'dynamodb:DeleteItem'
                - 'dynamodb:GetItem'
                - 'dynamodb:PutItem'
                - 'dynamodb:Query'
                - 'dynamodb:Scan'
                - 'dynamodb:UpdateItem'
              Resource:
                - { "Fn::GetAtt": ["テーブル名", "Arn"] }

AppSyncで上記で作成したロールを設定する際はこのように記述することができます。

serviceRoleArn: { Fn::GetAtt: [AppSyncDynamoRole, Arn] }

[AppSyncDynamoRole]となっているところは作成した任意のロール名を指定することで適用することができます。Policiesの許可するActionは必要な権限を持たせるようにします。


6. Lambda + APIGatewayの記述方法

こちらも既に多くの記事が存在しますが載せておきます。

functions:
functionName: # 関数名 /* ここを埋める */
  handler: # handlerのpath /* ここを埋める */
  events:
    - http:
        path: # endpoint /* ここを埋める */
        method: # GET / POST / PUT / DELETE / OPTIONS ... /* ここを埋める */
        cors: true # corsの有無 true / false /* ここを埋める */

[functionName]は任意の関数名に変更します。corsをtrueにしておくと後々AWSの画面上で面倒な設定を一つずつ行う必要がなくなります。各functionはhandlerファイルを作成して記述するようにして下さい。

今回はserverless.ymlでの環境管理とAWSリソースの記述方法を紹介しました。リソース作成を何度も行う無駄が省け、環境管理も一括で行えるため、細かい設定値以外はymlを用いて行う方が効率がよいのではないでしょうか。次回はモバイル開発のバックエンドを上記のserverless.ymlを使いながら構築した例を紹介したいと思います。

Queueに興味を持ってくださった方は会社紹介の記事も書いてるのでこちらを御覧ください。


この記事が気に入ったらサポートをしてみませんか?