AWS SAMでLambdaのPolicyとRoleを両方設定すると、Roleが優先されてハマった話

AWS SAMでLambdaのPolicyとRoleを付与したとき、Roleが設定されてPolicyで付与した権限が無いという現象に遭遇しました。よくよく考えれば当たり前なのですが、地味にハマったのでご紹介します。
2019.12.12

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

AWS SAMでLambdaのPolicyとRoleを付与したとき、Roleが設定されてPolicyで付与した権限が無いという現象に遭遇しました。

よくよく考えれば当たり前なのですが、地味にハマったのでご紹介します。

なお、本記事はAWS LambdaとServerless #2の12日目です。

ハマったこと

最初はPolicyのみ付与していた

たとえば、AWS SAMで次のLambdaを定義し、DynamoDBのReadOnlyAccessポリシーを付与していました。

template.yaml

Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: hello_world/
      Handler: app.lambda_handler
      Runtime: python3.7
      Policies:
        - arn:aws:iam::aws:policy/AmazonDynamoDBReadOnlyAccess
      Events:
        HelloWorld:
          Type: Api
          Properties:
            Path: /hello
            Method: get

このとき、実際のLambda関数には期待通り付与されていました。

LambdaのIAMロールの様子

次にIAMロールも付与した

そのあと、マネージドポリシーで定義されてない権限も必要になったので、IAMロールを作成してLambdaに紐付けました。次の例では、マネージドポリシーで定義されてない権限としてCost Explorerを使っています。

template.yaml

Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      ...略...
      Policies:
        - arn:aws:iam::aws:policy/AmazonDynamoDBReadOnlyAccess
      Role: !GetAtt HelloWorldFunctionRole.Arn
      ...略...

  HelloWorldFunctionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Action: "sts:AssumeRole"
            Principal:
              Service: lambda.amazonaws.com
      Policies:
        - PolicyName: "hello-world-policy"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
                Action:
                  - "ce:GetCostAndUsage"
                Resource: "*"

そしてRoleが優先された

マネージドポリシーのDynamoDBReadOnlyAccessLambdaBasicExecutionRoleに加えて、Cost Explororに対する権限も期待していました。

しかし、このLambdaをデプロイすると、自作したIAMロール(+ポリシー)がLambdaに紐づきました。そのため、Cost Explorerに対する権限のみがあり、CloudWatch LogsやDynamoDBに対する権限はありません。

Cost Explorerのみ権限がある

結果としてこのLambdaの権限は次のようになっていました。無念。

  • CloudWatch Logsに対するLog Put:権限なし
  • DynamoDBに対するReadOnlyAccess:権限なし
  • Cost Explorerに対する利用取得:権限あり

どうしてこうなった……

冷静に考えれば当たり前でした。

  • Lambdaに割り当てられるIAMロールは1つのみ
  • Policiesのみ指定時は、裏側でイイカンジにIAMロールを作ってくれていた
  • Roleを指定すると、指定されたIAMロールを作成してLambdaに割り当てる

つまり、この2つが両方設定された場合は、IAMロールが優先されます。

あとで気づきましたが、ドキュメント(Policiesの説明箇所)にもバッチリ書いてありました。

Names of AWS managed IAM policies or IAM policy documents or SAM Policy Templates that this function needs, which should be appended to the default role for this function. If the Role property is set, this property has no meaning.

serverless-application-model/2016-10-31.md

回避する方法

付与したい権限すべてを持つIAMロールを1つ作成し、Lambdaに割り当てます。ActionResourceを細かく設定すれば、セキュリティ面でも安心ですね。

template.yaml

Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      ...略...
      Role: !GetAtt HelloWorldFunctionRole.Arn
      ...略...

  HelloWorldFunctionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Action: "sts:AssumeRole"
            Principal:
              Service: lambda.amazonaws.com
      Policies:
        - PolicyName: "hello-world-policy"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
                Action:
                  - "ce:GetCostAndUsage"
                Resource: "*"
              - Effect: "Allow"
                Action:
                  - "logs:CreateLogGroup"
                  - "logs:CreateLogStream"
                  - "logs:PutLogEvents"
                Resource: "*"
              - Effect: "Allow"
                Action:
                  # AmazonDynamoDBReadOnlyAccess同等ではない。あくまでも例。
                  - "dynamodb:BatchGetItem"
                  - "dynamodb:Describe*"
                  - "dynamodb:List*"
                  - "dynamodb:GetItem"
                  - "dynamodb:Query"
                  - "dynamodb:Scan"
                Resource: "*"

全部入りIAMロールを作成した

さいごに

地味なハマりどころでしたが、誰かの役に立てば幸いです。

参考