Serverless Framework は npm で配布されている AWS Lambda のためのデプロイツールである.詳細は省くが,設定ファイル(serverless.yml
という YAML 形式のファイル)を書けば,後はコマンドで AWS Lambda 上に関数をデプロイしてくれるとても便利なツール.
本記事では,AWS Lambda にデプロイした関数のテストにおいて AWS リソースへのアクセス権限に関するエラーが出たのでその処理を行った過程を綴る.
AWS Lambda に登録した関数の権限エラー
boto3(AWS SDK for Python)を用いて,毎日 Slack に利用料金を通知する関数をテストした際に下記のようなエラーが発生した.重要なのは [ERROR]...
の部分.要約すると,現在のロールでは CloudWatch のメトリクス(利用料金を参照できるサービス)にアクセスする権限が無いとのこと.
エラー出力
START RequestId: ********** Version: $LATEST [ERROR] ClientError: An error occurred (AccessDenied) when calling the GetMetricStatistics operation: User: [my-ARN]/[my-app-name]-ap-northeast-1-lambdaRole/[my-app-name]-[my-funcname] is not authorized to perform: cloudwatch:GetMetricStatistics Traceback (most recent call last): File "/var/task/[my-script-name].py", line 19, in main detail, color = get_aws_cost() File "/var/task/[my-script-name].py", line 39, in get_aws_cost Statistics=['Maximum']) File "/var/runtime/botocore/client.py", line 320, in _api_call return self._make_api_call(operation_name, kwargs) File "/var/runtime/botocore/client.py", line 623, in _make_api_call raise error_class(parsed_response, operation_name) END RequestId: [RequestId] REPORT RequestId: [RequestId] Duration: 772.58 ms Billed Duration: 800 ms Memory Size: 1024 MB Max Memory Used: 429 MB
ちなみに AWS Lambda の各関数のページでテストに失敗するとこんな感じでログが出力される.
ローカル環境で実行した際には以下のようにちゃんと料金が取れてきているので恐らくデプロイ先の設定が原因だと推測した.
エラー原因の究明
自分が設定したテスト用の IAM ロールは AdministratorAccess
という AWS 上のほぼ全てのサービスにアクセスできるポリシーを付与していたため(良くない),なぜ上記のようなエラーが起こるのか不思議だったが,ログを見ると自分の想定した IAM ロールとは別の名前の IAM ロールが使われていることに気づいた.原因はローカル環境とデプロイ先の環境での IAM ロールの違いだった.
- ローカル環境:AWS CLI 上の設定を
AdministratorAccess
の権限を持った IAM ロールから発行したアクセスキーを用いていた. - デプロイ先の環境:Serverless Framework のデフォルトで作られる IAM ロール(上記エラー 4 行目の
[my-app-name]-ap-northeast-1-lambdaRole
がデフォルトで作られるロール)が使われていた.
公式ドキュメントに Serverless Framework におけるデフォルトの IAM ロールに関する記載があったので以下に示しておく.
Serverless Framework - AWS Lambda Guide - IAM より引用
以上を参照すると,デフォルトの IAM ロールには CloudWatch Logs への読み書き権限しか設定されていないようだ.これがエラーの根本原因である.IAM ロールのソース(JSON 形式)を見るとこんな感じ.
{ "Version": "2012-10-17", "Statement": [ { "Action": ["logs:CreateLogStream"], "Resource": ["arn:aws:logs:[my-resource]"], "Effect": "Allow" }, { "Action": ["logs:PutLogEvents"], "Resource": ["arn:aws:logs:[my-resource]"], "Effect": "Allow" } ] }
というわけで設定ファイル serverless.yml
に特定のポリシーの設定を加えれば(or 自分で作成した IAM ロールを設定すれば)OK なはず.今回のエラーは cloudwatch:GetMetricStatistics
へのアクセス権限が無いという内容だったため,CloudWatchReadOnlyAccess
というポリシーを参考に serverless.yml
を編集した.
CloudWatchReadOnlyAccess
設定前 serverless.yml
provider: name: aws # 書かなければデフォルトの IAM ロールになる # 以下略 ...
設定後 serverless.yml
provider: name: aws iamRoleStatements: - Effect: Allow Action: # default - logs:CreateLogStream # default - logs:PutLogEvents # default # - logs:CreateLogGroup # CloudWatchReacOnlyAccess - 'autoscaling:Describe*' - 'cloudwatch:Describe*' - 'cloudwatch:Get*' - 'cloudwatch:List*' - 'logs:Get*' - 'logs:List*' - 'logs:Describe*' - 'logs:TestMetricFilter' - 'logs:FilterLogEvents' - 'sns:Get*' - 'sns:List*' Resource: - '*' # 以下略 ...
そんでデプロイしてテストする.Python 側のエr... 無事成功.
ちゃんと Slack にも通知が来てた.これで毎日 AWS の料金を監視できるので安心して眠れそうです.
他にも,関数ごとのロールの設定ができるなど,AWS マネージメントコンソール上で行える設定は柔軟に設定ファイルで設定できるようになっている.以下のドキュメントに Serverless Framework の IAM の設定の仕方が載っているので参照.Serverless Framework でデプロイの設定をバージョン管理できるため,一度設定してしまえば再現が取れるの本当に便利.
補足:Sereverless Framework の仕組み
Serverless Framework によってローカルに CloudFormation のテンプレートファイルが生成されていたため,裏側で CloudFormation が動いているんだなあくらいしか考えていなかったが,この記事を書いている途中に Serverless Framework の中の仕組みが気になったので少しだけ調べてみた.具体的には,下記のリンク先の Deploy All > How It Works を読んだ.
該当箇所
Serverless Framework がどのようにデプロイを実現しているかが書いてある.大雑把に言うと,Serverless Framework は設定ファイル serverless.yml
の構文を一つの AWS CloudFormation のテンプレートファイルに変換し,スタックを生成してデプロイを実現している.
終わりに
IAM ロールの仕組みをちゃんと勉強した方が良さそう... 以下の記事読んで勉強する.
あと,大半の方は既に使っているかもしれないが,知らない方への布教の意味も込めて Serverless Framework の公式 HP を貼っておく.
メモ
Serverless Framework の serverless.yml
内で変数を扱う方法.secret key などを扱う際に必要になりそう?
CloudWatchLogs 関連