エンジニア

2021.10.07

[Tips]Serverless Frameworkで${aws:username}と書きたいときのお話

[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}'

今回のお話はここまで!
最後までお読みくださってありがとうございました。またお会いしましょう!

一覧に戻る