I'm trying to setup an S3 bucket which notifies a Lambda function when a new object is created.
The stack below works fine but I want to added the SourceArn
to the Lambda permission, following best practices.
There's some literature on this which suggests the way to do it is via a string rather than Fn::GetAtt/Arn
-
But if I uncomment the relevant SourceArn
line and redeploy, I get -
Unable to validate the following destination configurations (Service: Amazon S3; Status Code: 400; Error Code: InvalidArgument; Request ID: TBZDEVZ1HVD9EN0Q; S3 Extended Request ID: gL3CRz6UayvHup5i5oC4 /RMm0p1oRaRrPVtfZrykeAaJ1BVuhNSKkqxQ8TL5sy749d9PtbMOEQ=; Proxy: null)
Ho hum. Looking at the article again, I see that MyBucket
needs to depend on MyBucketFunctionPermission
- but if I uncomment that and redeploy I now get -
An error occurred (ValidationError) when calling the CreateChangeSet operation: Circular dependency between resources: [MyBucketFunctionPermission, MyBucket]
This is some fresh circle of hell. Am I missing something from the article or is there some other combination of SourceArn
format DependsOn
that would get this to work ?
TIA.
AWSTemplateFormatVersion: '2010-09-09'
Outputs: {}
Parameters: {}
Resources:
MyBucket:
# DependsOn:
# - MyBucketFunctionPermission
Properties:
NotificationConfiguration:
LambdaConfigurations:
- Event: s3:ObjectCreated:*
Function:
Fn::GetAtt:
- MyBucketFunction
- Arn
Type: AWS::S3::Bucket
MyBucketFunction:
Properties:
Code:
ZipFile: "def handler(event, context):\n print (event)"
Handler: index.handler
Role:
Fn::GetAtt:
- MyBucketRole
- Arn
Runtime: "python3.8"
Type: AWS::Lambda::Function
MyBucketFunctionPermission:
Properties:
Action: lambda:InvokeFunction
FunctionName:
Ref: MyBucketFunction
Principal: s3.amazonaws.com
# SourceArn:
# Fn::Sub: arn:aws:s3:::${MyBucket}
Type: AWS::Lambda::Permission
MyBucketRole:
Properties:
AssumeRolePolicyDocument:
Statement:
- Action: sts:AssumeRole
Effect: Allow
Principal:
Service: lambda.amazonaws.com
Version: '2012-10-17'
Policies:
- PolicyDocument:
Statement:
- Action: logs:*
Effect: Allow
Resource: '*'
Version: '2012-10-17'
PolicyName:
Fn::Sub: my-bucket-role-policy-1234567890
Type: AWS::IAM::Role
CodePudding user response:
There is nothing seemingly wrong with your template. It works fine, at least for me. But a race condition is possible between MyBucket
and MyBucketFunctionPermission
. Thus, protect against this, you have to use DependsOn
. But for that to work you have to explicitly set your bucket name. For example:
AWSTemplateFormatVersion: '2010-09-09'
Outputs: {}
Parameters: {}
Resources:
MyBucket:
DependsOn:
- MyBucketFunctionPermission
Properties:
BucketName: !Sub "my-bucket-323323-${AWS::StackName}-${AWS::Region}"
NotificationConfiguration:
LambdaConfigurations:
- Event: s3:ObjectCreated:*
Function:
Fn::GetAtt:
- MyBucketFunction
- Arn
Type: AWS::S3::Bucket
MyBucketFunction:
Properties:
Code:
ZipFile: "def handler(event, context):\n print (event)"
Handler: index.handler
Role:
Fn::GetAtt:
- MyBucketRole
- Arn
Runtime: "python3.8"
Type: AWS::Lambda::Function
MyBucketFunctionPermission:
Properties:
Action: lambda:InvokeFunction
FunctionName:
Ref: MyBucketFunction
Principal: s3.amazonaws.com
SourceArn: !Sub "arn:aws:s3:::my-bucket-323323-${AWS::StackName}-${AWS::Region}"
Type: AWS::Lambda::Permission
MyBucketRole:
Properties:
AssumeRolePolicyDocument:
Statement:
- Action: sts:AssumeRole
Effect: Allow
Principal:
Service: lambda.amazonaws.com
Version: '2012-10-17'
Policies:
- PolicyDocument:
Statement:
- Action: logs:*
Effect: Allow
Resource: '*'
Version: '2012-10-17'
PolicyName:
Fn::Sub: my-bucket-role-policy-1234567890
Type: AWS::IAM::Role