Dynamo UpdateItem AccessDeniedException with default LambdaRole - go

I'm having difficulties getting the right role to execute a Dynamo UpdateItem in my golang lambda handler.
I've deployed the function using the serverless framework with the following config:
provider:
name: aws
runtime: go1.x
stage: ${opt:stage, 'dev'}
environment: ${file(./env/config.${self:provider.stage}.yml)}
iamRoleStatements: # TODO: create special roles and restrict access per lambda
- Effect: Allow
Action:
- dynamodb:DescribeTable
- dynamodb:Query
- dynamodb:Scan
- dynamodb:GetItem
- dynamodb:PutItem
- dynamodb:UpdateItem
- dynamodb:DeleteItem
Resource:
- "Fn::GetAtt": [ myTable, Arn ]
resources:
Resources:
myTable:
Type: 'AWS::DynamoDB::Table'
Properties:
TableName: myTable-${opt:stage, 'dev'}
AttributeDefinitions:
- AttributeName: UserID
AttributeType: S
KeySchema:
- AttributeName: UserID
KeyType: HASH
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1
functions:
myFunc:
handler: bin/myFunc
events:
- http:
path: myFunc
method: post
authorizer: app-auth
cors: true
The handler uses the golang aws-sdk to create a session and call UpdateItem on the table:
sess, err := session.NewSession()
svc := dynamodb.New(sess)
input := &dynamodb.UpdateItemInput{
...
}
_, err = svc.UpdateItem(input)
This throws the exception:
AccessDeniedException: User: arn:aws:sts::{acct}:assumed-role/myservice-stage-us-east-1-lambdaRole/myservice-stage-myfunc
The User: arn:aws:sts::{acct}:assumed-role/myservice-stage-us-east-1-lambdaRole is a role that has the correct permissions:
I'm not sure what the /myservice-stage-myfunc part of the User is in the exception as nothing of the sort exists in the IAM console.
Is there some kind of config step I'm missing. To my knowledge, the IAM permissions setup in the serverless.yaml should apply to all functions. However, the assumed role for when working with the go-aws-sdk seems wrong.

DynamoDB has sub resources that often need access. To ensure that you are also addressing those sub items I would recommend adding a wildcard * onto the end of the resource. To do this I prefer to use the serverless-pseudo-parameters plugin (you can install it quickly with serverless plugin install --name serverless-pseudo-parameters) and then use it to more cleanly describe the resource like:
Resource:
- arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/myTable-${opt:stage, 'dev'}*

Related

How to add desired policy to lambda with cloudformation & SAM

I want to make a lambda function that has access to dynamoDB and other lambda functions.
However, I don't know how to make an appropriate YAML template for SAM to give appropriate permissions.
Especially, I don't understand the confusing 3 entries including definitions
of the permissions (AWS::IAM::Role, AWS::Serverless::Function, AWS::Lambda::Permission). Which entry should I use to add the necessary permissions?
Here is my YAML below. But the created my_lambda_role does not have AWSLambdaRole policy and policy for dynamoDB. As a result, the lambda fails to access to dynamoDB. Please tell me how to fix it.
MyLambdaRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
- arn:aws:iam::aws:policy/service-role/AWSLambdaRole
RoleName: my_lambda_role
OnConnectFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: onconnect/
Handler: index.lambda_handler
MemorySize: 256
Role: !GetAtt MyLambdaRole.Arn
Runtime: python3.6
Environment:
Variables:
TABLE_NAME: !Ref TableName
Policies:
- DynamoDBCrudPolicy:
TableName: !Ref TableName
- Statement:
- Effect: Allow
Action:
- 'execute-api:ManageConnections'
Resource:
- 'arn:aws:execute-api:*:*:*/#connections/*'
OnConnectPermission:
Type: AWS::Lambda::Permission
DependsOn:
- MyAPI
Properties:
Action: lambda:InvokeFunction
FunctionName: !Ref OnConnectFunction
Principal: apigateway.amazonaws.com
You can add the missing policies. Depending on what you want, but if you want full access, then you can add AmazonDynamoDBFullAccess:
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
- arn:aws:iam::aws:policy/service-role/AWSLambdaRole
- arn:aws:iam::aws:policy/AmazonDynamoDBFullAccess

ARN for Query on DynamoDB index is not working

I want to query an index in a DynamoDB table. When doing so I get the following error:
User: arn:aws:sts::XXX:assumed-role/bifr-dev-us-east-1-lambdaRole/bifr-dev-login is not authorized to perform: dynamodb:Query on resource: arn:aws:dynamodb:us-east-1:XXX:table/customers/index/email_index
After trying to fix it with the proposed configs in AccessDenied on DynamoDB GSI Index and AWS and DynamoDB permissions: "User is not authorized to access this resource" I came to the follwoing config, that still does not work. The same error persist. Maybe someone could help me with solving this problem.
Table config:
resources:
Resources:
customers:
Type: AWS::DynamoDB::Table
Properties:
TableName: customers
AttributeDefinitions:
- AttributeName: "id"
AttributeType: S
- AttributeName: "email"
AttributeType: S
KeySchema:
- AttributeName: "id"
KeyType: HASH
BillingMode: PAY_PER_REQUEST
GlobalSecondaryIndexes:
- IndexName: 'email_index'
KeySchema:
- AttributeName: 'email'
KeyType: 'HASH'
Projection:
ProjectionType: 'ALL'
IAM Role config:
iamRoleStatements:
- Effect: Allow
Action:
- dynamodb:GetItem
- dynamodb:UpdateItem
- dynamodb:PutItem
- dynamodb:Scan
Resource: "arn:aws:dynamodb:${opt:region, self:provider.region}:*:table/*"
- Effect: Allow
Action:
- dynamodb:Query
Resource: "arn:aws:dynamodb:us-east-1:250781267785:table/customers/index/email_index"
NodeJS code for the query:
var res = await aws.QueryItems({
TableName: tableName,
IndexName: 'email_index',
KeyConditionExpression: '#email = :email',
ExpressionAttributeNames: {
"#email": "email"
},
ExpressionAttributeValues: {
":email": email
}
});
According to https://docs.aws.amazon.com/IAM/latest/UserGuide/list_amazondynamodb.html#amazondynamodb-table
you need to add the ARN for the table to the dynamodb:Query IAM statement.
The problem was the indentation. The iamRoleStatements must be a child of the provider.
Here my working config:
provider:
name: aws
runtime: nodejs12.x
region: us-east-1
iamRoleStatements:
- Effect: Allow
Action:
- dynamodb:GetItem
- dynamodb:UpdateItem
- dynamodb:PutItem
- dynamodb:Scan
Resource: "arn:aws:dynamodb:${opt:region, self:provider.region}:*:table/*"
- Effect: Allow
Action:
- dynamodb:Query
Resource: "arn:aws:dynamodb:${opt:region, self:provider.region}:*:table/customers/index/*"

Serverless Framework - Setting up resource permissions for dynamodb

I've the following serverless.yml file.
I'm trying to assign read write permissions to the generated dynamodb..
So far it generates my lambda and the dynamodb table but the lambda isn't assigned permissions to access it.
I get no errors and it doesn't seem to add the permission to the dynamodb table.
Can anyone shed any light please?
service:
name: catcam
custom:
stage: ${opt:stage, self:provider.stage}
tableName: ${self:custom.stage}-notes
environment:
tableName: ${self:custom.tableName}
plugins:
- '#hewmen/serverless-plugin-typescript'
- serverless-plugin-optimize
- serverless-offline
provider:
name: aws
runtime: nodejs12.x
iamRoleStatements:
- Effect: Allow
Action:
- dynamodb:Query
- dynamodb:Scan
- dynamodb:GetItem
- dynamodb:PutItem
- dynamodb:UpdateItem
- dynamodb:DeleteItem
Resource:
- { "Fn::GetAtt": ["NotesTable", "Arn" ] }
# - { !GetAtt NotesTable.Arn }
functions:
main: # The name of the lambda function
# The module 'handler' is exported in the file 'src/lambda'
handler: src/lambda.handler
events:
- http:
method: any
path: /{any+}
resources:
Resources:
NotesTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: ${self:custom.tableName}
AttributeDefinitions:
- AttributeName: userId
AttributeType: S
- AttributeName: noteId
AttributeType: S
KeySchema:
- AttributeName: userId
KeyType: HASH
- AttributeName: noteId
KeyType: RANGE
# Set the capacity to auto-scale
BillingMode: PAY_PER_REQUEST
Turns out there was nothing wrong with the above, it's correct!!.. it's was me being a banana and not matching the full name of the table with the environment in the application.. i.e. notes table becomes dev-notes for instance.. maybe the above will help someone.

AWS SAM cloudformation: API Gateway can't invoke lambda (AWS::Serverless::Function )

I created a template.yaml file to declare a simple lambda function that is invoked by api gateway. When I try to invoke the function from the api gateway url the request fails with {"message": "Internal server error"} and in cloudwatch api gateway logs I see the error message Invalid permissions on Lambda function.
This is my template.yaml:
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Parameters:
AliasName:
Type: String
Default: dev
Resources:
DynamoDBTenantTable:
Type: AWS::DynamoDB::Table
Properties:
AttributeDefinitions:
- AttributeName: clientApiKey
AttributeType: S
KeySchema:
- AttributeName: clientApiKey
KeyType: HASH
ProvisionedThroughput:
ReadCapacityUnits: 5
WriteCapacityUnits: 5
TableName: !Sub "authtable-${AliasName}"
AmeAuthenticatorLambda:
Type: AWS::Serverless::Function
Properties:
Handler: authenticator.handler
Policies: AmazonDynamoDBFullAccess
Runtime: nodejs8.10
CodeUri: src/
Environment:
Variables:
TABLE_NAME: !Sub "authtable-${AliasName}"
Events:
AuthenticatorEvent:
Type: Api
Properties:
Path: /authentication/
Method: POST
The SAM Documentation says that the syntax above is able to create the necessary permissions and API declaration implicitly.
I also followed an example from AWS website.
If I add to the template.yaml file a lambda:InvokeFunction permission then the invocation works, but by reading the documentation doing that should not be necessary.
What can be going wrong?

Setting Access Role for Event Stream Created Via CloudFormation

I'm trying to add a dynamodb stream with the following template.yml
MyFunc:
Type: AWS::Serverless::Function
Properties:
CodeUri: ./myfunc
Handler: main
Runtime: go1.x
Events:
MyStream:
Type: DynamoDB
Properties:
Stream: !GetAtt MyTable.StreamArn
BatchSize: 1
StartingPosition: LATEST
Role:
Fn::ImportValue:
!Join ['-', [!Ref 'ProjectId', !Ref 'AWS::Region', 'LambdaTrustRole']]
However, I'm getting the following error during the deploy stage:
Please ensure the role can perform the GetRecords, GetShardIterator, DescribeStream, and ListStreams Actions on your stream in IAM.
Attempt 1
So I tried fixing the problem by adding the following policies to my IAM, CodeStarWorker-myproject-CloudFormation:
"dynamodb:GetRecords",
"dynamodb:GetShardIterator",
"dynamodb:DescribeStream",
"dynamodb:ListStreams",
That didn't work, still giving me the same error
Attempt 2
Tried using policies stead of role in template.yml
MyFunc:
Type: AWS::Serverless::Function
Properties:
CodeUri: ./myfunc
Handler: main
Runtime: go1.x
Events:
MyStream:
Type: DynamoDB
Properties:
Stream: !GetAtt MyTable.StreamArn
BatchSize: 1
StartingPosition: LATEST
Policies:
- IAMFullAccess
- AWSLambdaFullAccess
But it gave me the following error
API: iam:CreateRole User: arn:aws:sts::xxx:assumed-role/CodeStarWorker-xxx-CloudFormation/AWSCloudFormation is not authorized to perform: iam:CreateRole on resource: arn:aws:iam::xxx:role/awscodestar-xxx-lambda-MyFuncRole-1BO7G545IR5IC
Attempt 3
Specifying a role in template.yml
LambdaRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow #allow lambda to assume this role
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
Path: "/"
Policies:
- PolicyName: LambdaRolePolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow # allow to write logs to cloudwatch
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: arn:aws:logs:*:*:*
- Effect: Allow # allow lambda to read from the event stream
Action:
- dynamodb:DescribeStream
- dynamodb:GetRecords
- dynamodb:GetShardIterator
- dynamodb:ListStreams
Resource: "*"
And assign it to MyFunc
Role:
Fn::GetAtt: [ LambdaRole , Arn ]
However, it's also giving me the same error indicating that I'm not authorized to perform iam:CreateRole
Any help?
iam:CreateRole - you would need this action to create a role. The user that you use to run the Cloudformation template would need to include the "CreateRole" action.

Resources