I'm trying to create a Lambda notification via CloudFormation but getting an error about the ARN format being incorrect.
Either my CloudFormation is wrong or it doesn't support the Lambda preview yet.
{
"AWSTemplateFormatVersion": "2010-09-09",
"Parameters": {
"LambdaArn": {
"Type": "String",
"Default": "arn:aws:lambda:{some-region}:{some-account-id}:function:{some-fn-name}"
}
},
"Resources": {
"EventArchive": {
"Type": "AWS::S3::Bucket",
"Properties": {
"NotificationConfiguration": {
"TopicConfigurations": [
{
"Event": "s3:ObjectCreated:Put",
"Topic": {
"Ref": "LambdaArn"
}
}
]
}
}
}
}
}
But when I push up this CloudFormation I get the message:
The ARN is not well formed
Does anyone have idea as to what this means? I know the example above has been modified so not to use my actual ARN, but in my actual code I've copied the ARN directly from the GUI.
Also, interestingly I was able to create the notification via the AWS console, and so I just assume that AWS CloudFormation doesn't yet support this feature (even though that's not quite clear I don't think when reading the documentation).
It looks like AWS has now released support for notifying lambda functions directly in CloudFormation.
The S3 NotificationConfiguration definition used to only include TopicConfigurations but has been updated to include LambdaConfigurations as well.
After adding the NoficationConfiguration, make sure you include a Lambda::Permission resource so that S3 is allowed to execute your lambda function. Here is an example permission that can be used as a template:
"PhotoBucketExecuteProcessorPermission": {
"Type" : "AWS::Lambda::Permission",
"Properties" : {
"Action":"lambda:invokeFunction",
"FunctionName": { "Fn::GetAtt": [ "PhotoProcessor", "Arn" ]},
"Principal": "s3.amazonaws.com",
"SourceAccount": {"Ref" : "AWS::AccountId" },
"SourceArn": {
"Fn::Join": [":", [
"arn","aws","s3","", ""
,{"Ref" : "PhotoBucketName"}]]
}
}
}
From the docs:
The Amazon SNS topic to which Amazon S3 reports the specified events.
It appears that although S3 supports sending events to Lambda, CloudFormation has not yet caught up. It expects an SNS ARN where you are providing a Lambda function ARN.
For now, it looks like you will have to hook up the event notification manually.
Related
I have the following structure:
A S3 bucket where I upload files
A Cloud formation template where I'm creating a lambda function to process the files those are uploaded in the s3 bucket.
Now I want to create a trigger(it's my first time) using Cloudformation to launch automatically the lambda function when a file is uploaded in the S3 bucket. The problem here is I have the lambda function in two different regions(us-east-1 and us-west-2) and only one S3 bucket shared. I want to select by default the us-west-2 to trigger the lambda function and in the case that us-west-2 is not available change the region and launch the lambda function using us-east-1.
You want something like this:
Your function will need to accept a CustomResource Request and respond appropriately.
You will need to process on either the Create request, the Update request, or both. You need to respond to the request with Success (or Fail).
Be aware you might-can't remember not get an update request unless the AWS::CloudFormation::CustomResource has some values updated. In that case, a sentinel property on the custom resource filled with a date or UUID will force it to recreate.
The Condition on the AWS::CloudFormation::CustomResource will conditionally create it (thus calling your function conditionally) only if the stack is in us-west-1. You could also attach this condition to the function if you desire.
You could remove the condition altogether and check the region the lambda function is located in during execution, this is probably cleaner anyway.
{
"Resources": {
"MyLambdaFunction": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Role": {
"Ref": "MyLambdaFunctionRole"
}
}
},
"CustomResource": {
"Condition": "IsUsWest",
"Type": "AWS::CloudFormation::CustomResource",
"Properties": {
"ServiceToken": {
"Fn::GetAtt": [
"MyLambdaFunction",
"Arn"
]
}
}
}
},
"Parameters": {
"MyLambdaFunctionRole": {
"Type": "String"
}
},
"Conditions": {
"IsUsWest": {
"Fn::Equals": [
{
"Ref": "AWS::Region"
},
"us-west-1"
]
}
}
}
What you want is not possible to achieve in plain CloudFormatin. You need to develop custom resource for such a logic.
I know I can create a Scheduled Cloud Watch event by means of AWS Console:
https://docs.aws.amazon.com/AmazonCloudWatch/latest/events/Create-CloudWatch-Events-Scheduled-Rule.html
Is there a way to declare the similar event in Cloud Formation template?
Below is the example to create a scheduled event in cloudwatch, It creates a rule that invokes the specified Lambda function every 10 minutes. The PermissionForEventsToInvokeLambda resource grants EventBridge permission to invoke the associated function.
"ScheduledRule": {
"Type": "AWS::Events::Rule",
"Properties": {
"Description": "ScheduledRule",
"ScheduleExpression": "rate(10 minutes)",
"State": "ENABLED",
"Targets": [{
"Arn": { "Fn::GetAtt": ["LambdaFunction", "Arn"] },
"Id": "TargetFunctionV1"
}]
}
},
"PermissionForEventsToInvokeLambda": {
"Type": "AWS::Lambda::Permission",
"Properties": {
"FunctionName": { "Ref": "LambdaFunction" },
"Action": "lambda:InvokeFunction",
"Principal": "events.amazonaws.com",
"SourceArn": { "Fn::GetAtt": ["ScheduledRule", "Arn"] }
}
}
The example is referenced from AWS official documentation.
Yes, it's possible.
The AWS::Events::Rule resource creates a rule that matches incoming Amazon CloudWatch Events (CloudWatch Events) events and routes them to one or more targets for processing.
Here's the sample CloudFormation Snippet:
Type: AWS::Events::Rule
Properties:
Description: String
EventPattern: JSON object
Name: String
ScheduleExpression: String
State: String
Targets:
- Target
Here's the official documentation, if you have more questions.
Yes, It's possible as share by #bhalothia. Please find an article which will give you a deep dive into it.
Practical Implementation:
http://marcelog.github.io/articles/serverless_cloudwatch_event_cloudformation_template.html
Detail dodcumentation:
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-events-rule-target.html
I hope this helps you.
I'm trying to learn working of CustomResources in CloudFormation Template. Created simple template to create s3 bucket. But on creating stack, it remains in Create in progress state for long time and no bucket is created.
Is there anything, I'm missing in below validated template:
{
"AWSTemplateFormatVersion" : "2010-09-09",
"Description" : "Building A bucket With customeResources in CloudFormation",
"Parameters" : {
"NewBucket": {
"Default": "",
"Description": "S3 bucket containing customer assets",
"Type": "String"
}
},
"Conditions": {
"NewBucket": {
"Fn::Not": [
{
"Fn::Equals": [
{
"Ref": "NewBucket"
},
""
]
}
]
}
},
"Resources" : {
"CustomResource": {
"Properties": {
"S3Bucket": {
"Ref": "NewBucket"
},
"ServiceToken": "SNS topic ARN"
},
"Type": "AWS::CloudFormation::CustomResource"
}
},
"Outputs": {
"BucketName": {
"Value": {
"Fn::GetAtt": [ "CustomResource", {"Ref": "NewBucket"} ]
}
}
}
}
It would appear that your SNS-backed custom resource is not sending a response back to cloud formation, and it is stuck waiting for that response.
From Amazon Simple Notification Service-backed Custom Resources:
The custom resource provider processes the data sent by the template
developer and determines whether the Create request was successful.
The resource provider then uses the S3 URL sent by AWS CloudFormation
to send a response of either SUCCESS or FAILED.
When the request is made to the SNS service provider, it include the following object:
{
"RequestType": "Create",
"ServiceToken": "arn:aws:sns:us-west-2:2342342342:Critical-Alerts-development",
"ResponseURL": "https:\/\/cloudformation-custom-resource-response-uswest2.s3-us-west-2.amazonaws.com\/arn%3Aaws%3Acloudformation%3Aus-west-2%3A497903502641%3Astack\/custom-resource\/6bf07a80-d44a-11e7-84df-503aca41a029%7CCustomResource%7C5a695f41-61d7-475b-9110-cdbaec04ee55?AWSAccessKeyId=AKIAI4KYMPPRGIACET5Q&Expires=1511887381&Signature=WmHQVqIDCBwQSfcBMpzTfiWHz9I%3D",
"StackId": "arn:aws:cloudformation:us-west-2:asdasdasd:stack\/custom-resource\/6bf07a80-d44a-11e7-84df-503aca41a029",
"RequestId": "5a695f41-61d7-475b-9110-cdbaec04ee55",
"LogicalResourceId": "CustomResource",
"ResourceType": "AWS::CloudFormation::CustomResource",
"ResourceProperties": {
"ServiceToken": "arn:aws:sns:us-west-2:234234234:Critical-Alerts-development",
"S3Bucket": "test-example-com"
}
}
You will need to send a success/fail response to the ResponseURL provided in the event for Cloud Formation to continue processing.
I would also note that the bucket will not be created unless your custom service provider creates it. The Custom Resource function is only sending the request to the provider.
I am trying to create a template so that when i call api/divide/inputvalue, The api sends back response from DynamoDB which corresponds to inputvalue mapping.
Its pretty straight forward since i am fetching value directly from db without any business logic hence I don't need any lambda. But all the examples that I google or all tutorials they are using lambdas and i am now lost that how can i make it working without lambda
This is what I have so far. There is bug in this template right now since I haven't provided Uri in ApiGateway::Method. Which is what I am currently stuck at.
{
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"Deployment": {
"Type": "AWS::ApiGateway::Deployment",
"Properties": {
"RestApiId": { "Ref": "restApiName" },
"Description": "First Deployment",
"StageName": "StagingStage"
},
"DependsOn" : ["restApiMethod"]
},
"restApiMethod": {
"Type": "AWS::ApiGateway::Method",
"Properties": {
"AuthorizationType": "NONE",
"HttpMethod": "GET",
"ResourceId": {"Ref": "apiRestResource"},
"RestApiId": {"Ref": "restApiName"},
"Integration": {
"Type": "AWS",
"IntegrationHttpMethod": "GET",
"IntegrationResponses": [{"StatusCode": 200}],
"Uri": { "Fn::Sub":"arn.aws.apigateway:${AWS::Region}:dynamodb:action/${restApiName.Arn}"}
},
"MethodResponses": [{"StatusCode": 200}]
},
"DependsOn": ["apiRestResource"]
},
"apiRestResource": {
"Type": "AWS::ApiGateway::Resource",
"Properties": {
"RestApiId": {"Ref": "restApiName"},
"ParentId": {
"Fn::GetAtt": ["restApiName","RootResourceId"]
},
"PathPart": "divide"
},
"DependsOn": ["restApiName"]
},
"restApiName": {
"Type": "AWS::ApiGateway::RestApi",
"Properties": {
"Name": "CalculationApi"
}
}
}
}
According to the documentation, the Uri property is structured as follows for AWS service-proxy integration types:
If you specify AWS for the Type property, specify an AWS service that follows the form: arn:aws:apigateway:region:subdomain.service|service:path|action/service_api. For example, a Lambda function URI follows the form: arn:aws:apigateway:region:lambda:path/path. The path is usually in the form /2015-03-31/functions/LambdaFunctionARN/invocations. For more information, see the uri property of the Integration resource in the Amazon API Gateway REST API Reference.
The uri API Gateway property reference provides more details:
For AWS integrations, the URI should be of the form arn:aws:apigateway:{region}:{subdomain.service|service}:{path|action}/{service_api}. Region, subdomain and service are used to determine the right endpoint. For AWS services that use the Action= query string parameter, service_api should be a valid action for the desired service. For RESTful AWS service APIs, path is used to indicate that the remaining substring in the URI should be treated as the path to the resource, including the initial /.
For an AWS service proxy to the dynamodb service calling the Query Action, the Uri should be something like this (using the YAML short-form of Fn::Sub to insert a Ref for the current AWS region):
!Sub "arn:aws:apigateway:${AWS::Region}:dynamodb:action/Query"
As for your broader use-case of using API Gateway to access DynamoDB without using Lambda functions, refer to Andrew Baird's tutorial blog post, "Using Amazon API Gateway as a Proxy for DynamoDB", and translate the specified Management Console steps to corresponding CloudFormation template resources.
I have a Lambda function deployed to several regions. I would like to publish a message to SNS that will invoke these functions.
Using aws-cli I've created the topics, given Lambda permission to talk to SNS, and create the subscriptions. The subscription appears to be created successfully, and I can see it in the AWS console. But, it doesn't work. The lambda function does not get invoked.
This is CloudFormation based example. You have to add invoke permission for SNS to the Lambda functions:
{
"Type" : "AWS::Lambda::Permission",
"Properties" : {
"FunctionName" : { "Fn::GetAtt" : [ "YourLambda", "Arn" ] },
"Action" : "lambda:InvokeFunction",
"Principal" : "sns.amazonaws.com",
"SourceArn" : { "Ref" : "YourSNSTopicArn" }
}
}
Then you need to subscribe your Lambdas to your SNS topic. This can be done via API call or through CloudFormation.
{
"Type" : "AWS::SNS::Topic",
"Properties" : {
"TopicName" : "YourTopicName",
"Subscription" : [ {
"Endpoint" : { "Fn::GetAtt" : [ "YourLambda", "Arn" ] },
"Protocol": "lambda"
} ]
}
}
If you're missing any of this, your Lambdas won't invoke. Source for the above information is the official blog article Invoking Lambda functions via SNS.