[Tips]Serverless Frameworkで${aws:username}と書きたいときのお話
ごきげんよう!ハンズラボの小川です。
先日に引き続き、Serverless Frameworkに関する話題です。
serverless.ymlに${aws:username}と書いてデプロイしたらエラーになっちゃったので、そのときのお話をします!
環境
macOS Big Sur
Framework Core: 2.59.0
やりたいこと
serverless.ymlにIAMポリシー変数を記述したい!
(IAMポリシー変数についてはAWS公式ドキュメントをどうぞ)
以下はserverless.ymlです。
ここに、IAMポリシーのResource:が”arn:aws:iam::<アカウントID>:mfa/${aws:username}“と設定されるような定義を書きたいとします。
service: example
frameworkVersion: '>=1.53.0 <3.0.0'
variablesResolutionMode: 20210326
plugins:
provider:
name: aws
runtime: python3.7
region: ap-northeast-1
stage: ${opt:stage, 'dev'}
profile: ${env:AWS_PROFILE, 'sandbox'}
environment:
TZ: Asia/Tokyo
STAGE: ${self:provider.stage}
custom:
package:
functions:
resources:
Resources:
ExampleMemberGroup:
Type: AWS::IAM::Group
Properties:
GroupName: ${self:service}-ExampleMemberGroup
Path: /
Policies:
- PolicyName: ${self:service}-ExampleMemberPolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- iam:DeleteVirtualMFADevice
- iam:CreateVirtualMFADevice
Resource: <ここの部分を良い感じに書きたい!>
では早速、Resource: <ここの部分を良い感じに書きたい!> 部分を良い感じに書いていきましょう。
ダメだった書き方
タイトルからお察しかもしれませんが、以下の書き方ではデプロイ時にエラーが発生します。
Resource: arn:aws:iam::${aws:accountId}:mfa/${aws:username}
発生するエラーは以下の通り。
Serverless Error ----------------------------------------
Cannot resolve serverless.yml: Variables resolution errored with:
- Cannot resolve variable at "resources.Resources.ExampleMemberGroup.Properties.Policies.0.PolicyDocument.Statement.0.Resource": Unsupported "username" address argument in variable "aws" source
(訳)
"resources.Resources.ExampleMemberGroup.Properties.Policies.0.PolicyDocument.Statement.0.Resource "で変数を解決できませんでした。変数 "aws "のアドレス引数 "username "がサポートされていません。
無効な変数です、と怒られちゃっています。
Serverless FrameworkのVariablesのページにもある通り、${}で囲まれた部分はServerless Framework内で変数として扱われ、デプロイ時に値が代入されます。${aws:username}と書いてあるけれどaws:usernameなんて変数はServerless Frameworkは知りませんよ、というエラーなわけですね。
解決策
ではどうしましょう?
${aws:username}は、Serverless Frameworkに変数として扱ってほしいわけではありません。単なる文字列としてそのままデプロイしてくれればOKです。しかし、${}で囲んでいると変数扱いされてしまう…。ならば、$と{}を引き離してしまえば良いのでは?
というわけで、Fn::Joinの出番です!実際に書いてみましょう(例は短縮形で書いています)。
Resource:
- !Join
- ''
- - 'arn:aws:iam::${aws:accountId}:mfa/'
- '$'
- '{aws:username}'
嘘のような話ですが、上記の書き方ですと正常にデプロイされます。
まだ信じられない方もいらっしゃるかもしれないので、デプロイしてAWS CLIで設定を確認してみましょう。
% aws iam get-group-policy \
--group-name example-ExampleMemberGroup \
--policy-name example-ExampleMemberPolicy \
--query 'PolicyDocument' \
--profile sandbox
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"iam:DeleteVirtualMFADevice",
"iam:CreateVirtualMFADevice"
],
"Resource": [
"arn:aws:iam::************:mfa/${aws:username}"
],
"Effect": "Allow"
}
]
}
(注)************部分は、実際はAWSのアカウントIDが入ります
期待通りの設定になっていることが確認できました!
最後に、OKパターンのserverless.ymlの全景をおさらいしておきましょう。
service: example
frameworkVersion: '>=1.53.0 <3.0.0'
variablesResolutionMode: 20210326
plugins:
provider:
name: aws
runtime: python3.7
region: ap-northeast-1
stage: ${opt:stage, 'dev'}
profile: ${env:AWS_PROFILE, 'sandbox'}
environment:
TZ: Asia/Tokyo
STAGE: ${self:provider.stage}
custom:
package:
functions:
resources:
Resources:
ExampleMemberGroup:
Type: AWS::IAM::Group
Properties:
GroupName: ${self:service}-ExampleMemberGroup
Path: /
Policies:
- PolicyName: ${self:service}-ExampleMemberPolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- iam:DeleteVirtualMFADevice
- iam:CreateVirtualMFADevice
Resource:
- !Join
- ''
- - 'arn:aws:iam::${aws:accountId}:mfa/'
- '$'
- '{aws:username}'
今回のお話はここまで!
最後までお読みくださってありがとうございました。またお会いしましょう!