Aurora PostgreSQL permissions to access s3 - amazon-s3

Im trying to give my Aurora PostgreSQL permissions to access an s3 bucket. I'm using the serverless framework and have the following code.
RDSCluster:
Type: 'AWS::RDS::DBCluster'
Properties:
MasterUsername: AuserName
MasterUserPassword: Apassword
DBSubnetGroupName:
Ref: RDSClusterGroup
AvailabilityZones:
- eu-central-1a
- eu-central-1b
Engine: aurora-postgresql
EngineVersion: 11.9
EngineMode: provisioned
EnableHttpEndpoint: true
DatabaseName: initialbase
DBClusterParameterGroupName:
Ref: RDSDBParameterGroup
AssociatedRoles:
- RoleArn:
{ Fn::GetAtt: [ AuroraPolicy, Arn ] }
VpcSecurityGroupIds:
- Ref: RdsSecurityGroup
AuroraPolicy:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- rds.amazonaws.com
Action:
- sts:AssumeRole
Path: "/"
Policies:
- PolicyName: AuroraRolePolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- s3:AbortMultipartUpload
- s3:GetBucketLocation
- s3:GetObject
- s3:ListBucket
- s3:ListBucketMultipartUploads
- s3:PutObject
Resource:
- { Fn::GetAtt: [ S3BucketEgresbucket, Arn ] }
- Fn::Join:
- ""
- - { Fn::GetAtt: [ S3BucketEgresbucket, Arn ] }
- "/*"
This should grant the DB permission to execute query's using SELECT aws_commons.create_s3_ur
However when I try and deploy I get the error message:
The feature-name parameter must be provided with the current operation for the Aurora (PostgreSQL) engine.

The issue comes from the AssociatedRoles object, cloudformation states that the FeatureName field is not needed however if you are wishing for your cluster to access other AWS services it is required. In this case as I was wanting to have my cluster access an s3 bucket I had to change my AssociatedRoles object so it looked like this:
AssociatedRoles:
- RoleArn: { Fn::GetAtt: [ roleServiceIntegration, Arn ] }
FeatureName: s3Import

Related

Get SQS URL Name from within Serverless function?

I would like to grab the name of the serverless function. Here is my code.
What I am trying to achieve is, instead of arn https://sqs.us-east-1.amazonaws.com/xxxx/channels.fifo, I want to the env SQS_URL to be set to channels.fifo. I looked at Fn::Split fucntion of cloudformation, but was unable to properly use it to set it on env.
functions:
S3ToSqs:
handler: lambda_function.lambda_handler
role: S3ToSqsLambdaRole
memorySize: 128
timeout: 5
events:
- schedule:
name: 'S3ToSqsCronEvent'
rate: rate(1 minute)
enabled: true
environment:
SQS_URL:
Ref: sqsQueue
REGION: 'us-east-1'
resources:
Resources:
S3ToSqsLambdaRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
- events.amazonaws.com
Action:
- sts:AssumeRole
Policies:
- PolicyName: S3ToSqsRole
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
- sqs:DeleteMessage
- sqs:GetQueueUrl
- sqs:ChangeMessageVisibility
- sqs:SendMessageBatch
- sqs:ReceiveMessage
- sqs:SendMessage
- sqs:GetQueueAttributes
- sqs:ListQueueTags
- sqs:ListDeadLetterSourceQueues
- sqs:DeleteMessageBatch
- sqs:PurgeQueue
- sqs:DeleteQueue
- sqs:CreateQueue
- sqs:ChangeMessageVisibilityBatch
- sqs:SetQueueAttribute
- s3:GetObjectVersion
- s3:GetObject
- s3:ListBucket
Resource: "arn:aws:logs:*:*:*"
sqsQueue:
Type: "AWS::SQS::Queue"
Properties:
ContentBasedDeduplication: true
FifoQueue: true
QueueName: "channels.fifo"
#Package used from https://github.com/arabold/serverless-export-env
plugins:
- serverless-export-env
If you take a look at the SQS::Queue CloudFormation resource you can see that the Queue Name is exposed as an attribute.
As a result of that you can use: !GetAtt sqsQueue.QueueName or Fn::GetAtt [sqsQueue, QueueName], both of which may be a little easier to read than the solution you came up with (which still works).
I was able to achieve it by replacing
environment:
SQS_URL:
Ref: sqsQueue
REGION: 'us-east-1'
section with
environment:
SQS_URL: !Select [4, !Split ["/",!Ref sqsQueue ]]
which now gives me the output as channels.info instead of https://sqs.us-east-1.amazonaws.com/xxxx/channels.fifo

How to grant public read only access to S3 Bucket in AWS SAM template

I am creating an S3 bucket in a SAM template and would like to give it public read access. This is what I came up with so far:
ProductBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub ${ProductBucketName}${Stage}
ProductBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref ProductBucket
PolicyDocument:
Id: PublicReadPolicy
Version: 2012-10-17
Statement:
- Sid: PublicReadForGetBucketObjects
Effect: Allow
Principal: '*'
Action: 's3:GetObject'
Resource: !Join
- ''
- - 'arn:aws:s3:::'
- !Ref ProductBucket
This fails with a malformed exception.
Your template is fine except the line that says - - 'arn:aws:s3:::' where its malformed. Just a suggestion that you can use Fn::Sub over Fn::Join when delimiter is an empty string.
AWSTemplateFormatVersion: "2010-09-09"
Parameters:
ProductBucketName:
Type: String
Resources:
ProductBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub ${ProductBucketName}-${AWS::StackName}
ProductBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref ProductBucket
PolicyDocument:
Id: PublicReadPolicy
Version: 2012-10-17
Statement:
- Sid: PublicReadForGetBucketObjects
Effect: Allow
Principal: '*'
Action: 's3:GetObject'
Resource: !Sub arn:aws:s3:::${ProductBucket}/*
Hope this help you move forward.
I found the AWS documentation's yaml example to look a bit funny, but I followed it and gave my CF stack IAM s3:PutBucketPolicy permissions, and it worked. Below is my SAM.
ExampleBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub <bucket-name>
ExampleBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket:
Ref: "ExampleBucket"
PolicyDocument:
Statement:
- Action:
- "s3:GetObject"
Effect: "Allow"
Resource:
Fn::Join:
- ""
- - "arn:aws:s3:::"
- Ref: "ExampleBucket"
- "/*"
Principal: "*"
Here is the AWS documentation page you've probably already consulted: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3-policy.html.

Possible for one resource to reference another?

I have defined an S3 bucket in my serverless.yml file as follows:
resources:
Resources:
nameOfS3Bucket:
Type: AWS::S3::Bucket
Properties:
BucketName: ${self:custom.stage}-name-of-s3-bucket
AccessControl: Private
What I would like to do now is to reference the arn of the bucket in an IAM role which is also defined as a resource e.g.
resources:
Resources:
# s3 bucket definition removed for brevity
iamRoleName:
Type: AWS::IAM::Role
Properties:
RoleName: ${self:custom.stage}-iam-role-name
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: ${self:custom.stage}-iam-role-policy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action: "s3:PutObject"
Resource: *** Need the ARN of the s3 bucket here***
Is there a way to do this?
In my experience, the intrinsic functions in cloudformation work fine inside serverless.yml, but not the yaml short form.
i.e. use Fn::GetAtt over !GetAtt
As long as you use the full syntax for yaml, it should work.
Try this (untested)
resources:
Resources:
nameOfS3Bucket:
Type: AWS::S3::Bucket
Properties:
BucketName: ${self:custom.stage}-name-of-s3-bucket
AccessControl: Private
iamRoleName:
Type: AWS::IAM::Role
Properties:
RoleName: ${self:custom.stage}-iam-role-name
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: ${self:custom.stage}-iam-role-policy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action: "s3:PutObject"
Resource:
"Fn::GetAtt": [ nameOfS3Bucket, Arn ]

AccessDenied on DynamoDB GSI Index

I've wrote a serverless.yml to deploy some lambdas and I'm using GSI in a specific API.
If I run locally using serverless-offline, it's working but I'm facing an error when deploy the lambda:
AccessDeniedException: User: arn:aws:sts::408462944160:assumed-role/telecom-integration-dev-us-east-1-lambdaRole/integration-dev-dialerStatistics
is not authorized to perform: dynamodb:Query on resource: arn:aws:dynamodb:us-east-1:408462944160:table/integration-dialer-dev/index/other_dial_status-index
Here is how I've created serverless.yml
iamRoleStatements:
- Effect: Allow
Action:
- dynamodb:Query
- dynamodb:Scan
- dynamodb:GetItem
- dynamodb:PutItem
- dynamodb:UpdateItem
- dynamodb:DeleteItem
Resource:
- { "Fn::GetAtt": ["DialerDynamoDbTable", "Arn" ] }
dialerStatistics:
handler: integration/dialer.statistics
description: Import data on dialer.
memorySize: 256
timeout: 30
events:
- http:
path: dialer-statistics
method: get
cors: false
private: false
DialerDynamoDbTable:
Type: 'AWS::DynamoDB::Table'
DeletionPolicy: ${self:provider.environment.DELETION_POLICY}
# DeletionPolicy: Delete # Useful for recreating environment in dev
Properties:
AttributeDefinitions:
-
AttributeName: "id"
AttributeType: "S"
-
AttributeName: "dial_status"
AttributeType: "S"
KeySchema:
-
AttributeName: "id"
KeyType: "HASH"
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1
TableName: ${self:provider.environment.DIALER_TABLE}
GlobalSecondaryIndexes:
- IndexName: "other_dial_status-index"
KeySchema:
- AttributeName: "dial_status"
KeyType: HASH
Projection:
ProjectionType: "ALL"
ProvisionedThroughput:
ReadCapacityUnits: '20'
WriteCapacityUnits: '20'
Probably it's missing some permission on iAmRoleStatements but I'm not sure what else should I do.
Your IAM role does not cover the indexes. Try to add them in the role's ressources:
iamRoleStatements:
- Effect: Allow
Action:
- dynamodb:Query
- dynamodb:Scan
- dynamodb:GetItem
- dynamodb:PutItem
- dynamodb:UpdateItem
- dynamodb:DeleteItem
Resource:
- { "Fn::GetAtt": ["DialerDynamoDbTable", "Arn" ] }
- Fn::Join:
- "/"
-
- { "Fn::GetAtt": ["DialerDynamoDbTable", "Arn" ] }
- "index/*"
For reference, the Fn::Join will append /index/* to DialerDynamoDbTable's ARN.
It worked locally because Serverless uses the "admin" IAM user you configured it with.
Resource:
- arn:aws:dynamodb:*:*:table/${self:custom.myTable}
- arn:aws:dynamodb:*:*:table/${self:custom.myTable}/index/*
for those in search of cloud formation
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- dynamodb:GetItem
- dynamodb:PutItem
- dynamodb:DeleteItem
- dynamodb:UpdateItem
- dynamodb:Query
- dynamodb:Scan
- dynamodb:BatchGetItem
- dynamodb:BatchWriteItem
Resource: [!GetAtt DialerDynamoDbTable.Arn, !Join [ '/',[!GetAtt DialerDynamoDbTable.Arn,index/*]]]

AWS CloudFormation environmental conditional for ses role

I'm trying to make a reusable CloudFormation template and would like to do some kind of conditional where if the Environment parameter is "test" (or any other environment other than "prod"), then send SES emails to only gmail accounts (i.e., corporate accounts), but for "prod", send SES emails anywhere. Would I have to do two different roles and have conditions on each one? Or is there a way to do this inside of just the one role below? Thanks for any help!
Parameters:
Environment:
Description: Environment, which can be "test", "stage", "prod", etc.
Type: String
Resources:
Role:
Type: AWS::IAM::Role
Properties:
RoleName: myRole
Path: /
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: "Allow"
Principal:
Service:
- "ecs.amazonaws.com"
Action:
- "sts:AssumeRole"
Policies:
-
PolicyName: "ses-policy"
PolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: "Allow"
Action:
- "ses:SendEmail"
- "ses:SendRawEmail"
Resource: "*"
Condition:
"ForAllValues:StringLike":
"ses:Recipients":
- "*#gmail.com"
Conditions are perfectly suited for adding this sort of conditional logic to CloudFormation Resource Properties. In your example, you could use the Fn::If Intrinsic Function to include the existing Policy Condition (not to be confused with the CloudFormation Condition!) if the environment is not prod, and AWS::NoValue otherwise (removing the Policy Condition entirely when environment is prod):
Parameters:
Environment:
Description: Environment, which can be "test", "stage", "prod", etc.
Type: String
AllowedValues: [test, stage, prod]
Conditions:
IsProdEnvironment: !Equals [ !Ref Environment, prod ]
Resources:
Role:
Type: AWS::IAM::Role
Properties:
RoleName: myRole
Path: /
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: "Allow"
Principal:
Service:
- "ecs.amazonaws.com"
Action:
- "sts:AssumeRole"
Policies:
-
PolicyName: "ses-policy"
PolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: "Allow"
Action:
- "ses:SendEmail"
- "ses:SendRawEmail"
Resource: "*"
Condition: !If
- IsProdEnvironment
- !Ref AWS::NoValue
- "ForAllValues:StringLike":
"ses:Recipients":
- "*#gmail.com"