前回の続きです。
Zoom会議をSlackをお知らせするアプリにてZoom APIに必要なJWTを生成する処理をSAMで作成しました。(下図の赤枠)
時間ができたらやりたいと思っていましたが、思ったよりすぐやっちゃいました。さすがStayHome週間(笑)
※本記事ではSAM,SAM-CLIなどの詳述は割愛します。ご了承ください。
SAMを始める
公式にもありますが、まず↓のような前提があるので、それぞれやっておきます。
SAMテンプレートの取得
まずは、SAMテンプレートをDLしてきます。
sam init
これを実行します。今回ランタイムはNode.js 12.Xを選択しました。
テンプレートを取得すると以下のような感じのフォルダ構成で展開されるので、適宜編集しながら、処理を実装します。
sam-app/
├── README.md
├── events/
│ └── event.json
├── hello_world/
│ └── app.js #Contains your AWS Lambda handler logic.
├── template.yaml #Contains the AWS SAM template defining your application's AWS resources.
└── tests/
└── unit/
└── test-handler.js
JWT生成処理
↓のようなコードになります。
const jwt = require('jsonwebtoken')
let response;
const apiKey = process.env.API_KEY;
const secretKey = process.env.SECRET_KEY;
/**
*
* Event doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format
* @param {Object} event - API Gateway Lambda Proxy Input Format
*
* Context doc: https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-context.html
* @param {Object} context
*
* Return doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html
* @returns {Object} object - API Gateway Lambda Proxy Output Format
*
*/
exports.lambdaHandler = async (event, context) => {
return this.generateJWT(apiKey, secretKey)
};
exports.generateJWT = function (apiKey, secretKey) {
try {
// const ret = await axios(url);
expire = Math.floor(Date.now()) + (60000 * 15);
const token = jwt.sign({
iss: apiKey,
exp: expire
}, secretKey, {
algorithm: 'HS256'
});
response = {
'statusCode': 200,
'jwt': token
}
} catch (err) {
console.log(err);
response = {
'statusCode': 500,
'jwt': null
}
}
return response
};
node-jsonwebtokenをいうAuth0のライブラリを用いてJWT生成処理を実装しています。
npm install jsonwebtoken
jwt.sign({iss: apiKey,exp: expire}, secretKey, {algorithm: 'HS256'});
がJWT生成部分になるワケですが、iss
にZoomのAPIキー、exp
にはトークンがエクスパイアするまでの時間を入れます。エクスパイアする時間はエポックミリ秒で指定します。今回は、15分後を指定しています。
そして、生成したJWTを返すワケです。
SAMのデプロイ
ローカル実行
ローカルテストにあたり擬似的に環境変数を与えてやる必要があります。
ざっくりとした理解ですが、ローカルにLambda環境を模したコンテナを建てるので、そこではローカルホストの環境変数は使えないワケですね。
そのため、以下のような環境変数用の設定ファイルを用意しました。
{
"GenerateJWT": {
"ApiKey": "XXX",
"SecretKey": "XXX"
}
}
そして、template.yamlに↓を追加しましょう。
Resources:
GenerateJWT:
Type: AWS::Serverless::Function
Properties:
CodeUri: generate-jwt/
Handler: app.lambdaHandler
Runtime: nodejs12.x
#ここから
Environment:
Variables:
API_KEY: !Ref ApiKey
SECRET_KEY: !Ref SecretKey
#ここまで
では、ローカルでテストしてみましょう!
まずは
sam build
ビルドして
sam local invoke --env-vars generate-jwt/env.json
generate-jwt/env.json
を環境変数をして渡しています。
このやり方は以下のブログを参考にさせていただきました。
AWS SAM Local と LocalStack を使って ローカルでAWS Lambdaのコードを動かす
デプロイ
デプロイは簡単です。
sam deploy --guided
これだけ。あとはガイドに従いながら、質問に答えていくだけです。SAM-CLI素晴らしい。
ガイド内容はStack名は?とかリージョンは?とか。↓のように回答していきます。
Deploying with following values
===============================
Stack name : generateJWT
Region : ap-northeast-1
Confirm changeset : True
Deployment s3 bucket :
Capabilities : ["CAPABILITY_IAM"]
Parameter overrides : {'ApiKey': '', 'SecretKey': ''}
このタイミングで今回のtemplate.yaml
を載せておきます。
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
GenerateJWT
# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
Function:
Timeout: 3
Resources:
GenerateJWT:
Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
Properties:
CodeUri: generate-jwt/
Handler: app.lambdaHandler
Runtime: nodejs12.x
Environment:
Variables:
API_KEY: !Ref ApiKey
SECRET_KEY: !Ref SecretKey
#Events:
# GetJWT:
# Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
# Properties:
# Path: /jwt
# Method: get
Parameters:
ApiKey:
Type: String
SecretKey:
Type: String
Outputs:
# ServerlessRestApi is an implicit API created out of Events key under Serverless::Function
# Find out more about other implicit resources you can reference within SAM
# https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api
#GenerateJWTdApi:
#Description: "API Gateway endpoint URL for Prod stage for GenerateJWT function"
#Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/jwt/"
GenerateJWT:
Description: "GenerateJWT Lambda Function ARN"
Value: !GetAtt GenerateJWT.Arn
GenerateJWTIamRole:
Description: "Implicit IAM Role created for GenerateJWT function"
Value: !GetAtt GenerateJWTRole.Arn
API Gatewayもデフォルトのテンプレートでは作成されるのですが、今回Step Functionsから呼び出すだけなこともあり、外しています。
JWTを返すAPIなんてデプロイしたくないし。
無事デプロイすると以下のようにターミナルに出ます。※更新の際のデプロイ結果です。
CloudFormation events from changeset
---------------------------------------------------------------------------------------------------------------------------------------------
ResourceStatus ResourceType LogicalResourceId ResourceStatusReason
---------------------------------------------------------------------------------------------------------------------------------------------
UPDATE_IN_PROGRESS AWS::Lambda::Function GenerateJWT -
UPDATE_COMPLETE AWS::Lambda::Function GenerateJWT -
UPDATE_COMPLETE_CLEANUP_IN_PROGRE AWS::CloudFormation::Stack generateJWT -
SS
UPDATE_COMPLETE AWS::CloudFormation::Stack generateJWT -
---------------------------------------------------------------------------------------------------------------------------------------------
CloudFormation outputs from deployed stack
----------------------------------------------------------------------------------------------------------------------------------------------
Outputs
----------------------------------------------------------------------------------------------------------------------------------------------
Key GenerateJWT
Description GenerateJWT Lambda Function ARN
Value
Key GenerateJWTIamRole
Description Implicit IAM Role created for GenerateJWT function
Value
----------------------------------------------------------------------------------------------------------------------------------------------
Successfully created/updated stack - generateJWT in ap-northeast-1
ところで、Parameters
を定義しているApiKey
とSecretKey
の値がガイドの中で聞かれるのですが、ここでローカルの環境変数としてキー情報を渡してやりたかったのに、上手くできませんでした。。。環境変数はこの中では使えないのかな。。。?
Step Functionsで呼び出す
Step Functionsも今回のLambdaに合わせて下図のように変更しています。JWT処理に失敗すればLambdaが500のステータスコードを返すので、そしたらエラー通知用のSNSトピックにプッシュする感じです。
CloudWatch Eventでサイクル実行
日次で毎朝9時に実行したければ、以下のようにcronを定義します。
0 0 * * ? *
GMTなので、9時間戻し毎日0時を指定します。
今回は朝9時まで待ちきれず12:55に実行させました。お昼休み開ける前にZoom会議を作る!みたいなイメージですかね(笑)
結果は?
無事成功しました。
ちなみに「密!」は自作です(笑)密を作らず、人と会うときはオンラインで。