AWS SAMでLambdaのPolicyとRoleを両方設定すると、Roleが優先されてハマった話
AWS SAMでLambdaのPolicyとRoleを付与したとき、Roleが設定されてPolicyで付与した権限が無いという現象に遭遇しました。
よくよく考えれば当たり前なのですが、地味にハマったのでご紹介します。
なお、本記事はAWS LambdaとServerless #2の12日目です。
ハマったこと
最初はPolicyのみ付与していた
たとえば、AWS SAMで次のLambdaを定義し、DynamoDBのReadOnlyAccessポリシーを付与していました。
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関数には期待通り付与されていました。
次にIAMロールも付与した
そのあと、マネージドポリシーで定義されてない権限も必要になったので、IAMロールを作成してLambdaに紐付けました。次の例では、マネージドポリシーで定義されてない権限としてCost Explorerを使っています。
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が優先された
マネージドポリシーのDynamoDBReadOnlyAccess
とLambdaBasicExecutionRole
に加えて、Cost Explororに対する権限も期待していました。
しかし、このLambdaをデプロイすると、自作したIAMロール(+ポリシー)がLambdaに紐づきました。そのため、Cost Explorerに対する権限のみがあり、CloudWatch LogsやDynamoDBに対する権限はありません。
結果としてこの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.
回避する方法
付与したい権限すべてを持つIAMロールを1つ作成し、Lambdaに割り当てます。Action
やResource
を細かく設定すれば、セキュリティ面でも安心ですね。
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: "*"
さいごに
地味なハマりどころでしたが、誰かの役に立てば幸いです。