AWS Lambda & SNS: Invoke Lambda cross-region - amazon-web-services

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.

Related

AWS Api Gateway "put-rest-api" doesn't create trigger in Lambda function

I am trying to put template in API Gate Way with next command:
aws apigateway put-rest-api --rest-api-id $APIGW --mode merge --body 'fileb://./api-gw-template.json'
Template is:
{
"swagger" : "2.0",
"basePath" : "/aws",
"schemes" : [ "https" ],
"paths" : {
"/{proxy+}" : {
"x-amazon-apigateway-any-method" : {
"parameters" : [ {
"name" : "proxy",
"in" : "path",
"required" : true,
"type" : "string"
} ],
"responses" : { },
"x-amazon-apigateway-integration" : {
"httpMethod" : "POST",
"uri" : "{{URI}}",
"responses" : {
"default" : {
"statusCode" : "200"
}
},
"passthroughBehavior" : "when_no_match",
"cacheNamespace" : "2wn7w2",
"cacheKeyParameters" : [ "method.request.path.proxy" ],
"contentHandling" : "CONVERT_TO_TEXT",
"type" : "aws_proxy"
}
}
}
},
"x-amazon-apigateway-binary-media-types" : [ "font/woff", "image/x-icon", "*/*" ]
}
This template creates a proxy resource with lambda integration, everything is fine here.
But in my lambda function, I don't see a new trigger for that resource.
But if I create the same resource manually, the trigger in the lambda is also created.
And if I export these two api gateway as swagger json template they are the same.
Also deploy api after import template doesn't help.
Here is output after import templat:
{
"id": "********",
"name": "Imported on 2022-09-14T12:09:17Z",
"createdDate": 1663149628,
"warnings": [
"Required \"info\" property is missing from the document root."
],
"binaryMediaTypes": [
"font/woff",
"image/x-icon",
"*/*"
],
"apiKeySource": "HEADER",
"endpointConfiguration": {
"types": [
"REGIONAL"
]
},
"policy": "{\\\"Version\\\":\\\"2012-10-17\\\",\\\"Statement\\\":[{\\\"Effect\\\":\\\"Allow\\\",\\\"Principal\\\":\\\"*\\\",\\\"Action\\\":\\\"execute-api:Invoke\\\",\\\"Resource\\\":\\\"arn:aws:execute-api:eu-central-1:*************:***********\\/*\\/*\\/*\\\"}]}",
"disableExecuteApiEndpoint": false
}
aws apigateway put-integration command also don't create trigger in lambda
Can someone help me? Where is my mistake?
I found the solution myself. The lambda function needs permission to be able to be called by the API gateway integration.
Solution in my case (if you are using a resource without a proxy - specify the resource name instead of the last asterisk in the "--source-arn" key):
aws lambda add-permission --function-name $lambda \
--statement-id apigateway-test-111111 --action lambda:InvokeFunction \
--principal apigateway.amazonaws.com \
--source-arn "arn:aws:execute-api:$REGION_NAME:$ACCOUNT:$APIGW/*/*/*"
When manually creating a resource, this permission is automatically created

How to include cloudWatch Log Group subscription in cloudFormation template?

Say I have a Log Group in cloudWatch, which I want to subscribe to a lambda with a filter (Subscriptions->Stream to AWS Lambda).
I want to achieve it with cloudFormation template, but from cloudFormation doc, it seems that the only two available cloudWatch resources are Alarm/Dashboard.
Questions is:
Is there any way to write cloudWatch Log Group subscription in
cloudFormation?
If not, any alternative way (say from lambda
resource configuration in cloudFormation template)?
Oh, that's a tricky one. I only figured it out by creating one in the console and reverse enginerring - ick. But you're lucky - I have it on hand :P This is the json I was using for subscribing a lambda to a vpc flow log.
Note that the 'VPCFlowLogsGroup' is the logical Id of the log group, the 'FlowLogsCollector' that of the lambda.
"FlowLogsCollectorEventPermission": {
"Type" : "AWS::Lambda::Permission",
"Properties" : {
"Principal" : { "Fn::Sub": "logs.${AWS::Region}.amazonaws.com" },
"Action" : "lambda:InvokeFunction",
"FunctionName" : { "Fn::GetAtt": [ "FlowLogsCollector", "Arn" ] },
"SourceAccount": { "Ref": "AWS::AccountId" },
"SourceArn" : { "Fn::GetAtt": [ "VPCFlowLogsGroup", "Arn" ] }
}
},
"FlowLogsCollectorSubscription": {
"Type" : "AWS::Logs::SubscriptionFilter",
"DependsOn": "FlowLogsCollectorEventPermission",
"Properties" : {
"LogGroupName" : { "Ref" : "VPCFlowLogsGroup" },
"FilterPattern" : "",
"DestinationArn" : { "Fn::GetAtt" : [ "FlowLogsCollector", "Arn" ] }
}
},

Email notification from AWS Cloudfomartion?

How do I get this DNSRecord as an Email notification from CloudFormation?
"DNSRecord" : {
"Type" : "AWS::Route53::RecordSet",
"Properties" : {
"HostedZoneName" : { "Fn::Join" : [ "", [{"Ref" : "HostedZoneName"}, "." ]]},
"Name" : { "Fn::Join" : [ "", [{"Ref" : "RecordSetName"}, ".", {"Ref" : "HostedZoneName"}, "."]]},
"Type" : "CNAME",
"ResourceRecords" :[ { "Fn::ImportValue" : "DNSName" } ],
"TTL" : "900"
}
}
AWS CloudFormation will not send an email, but your calling script could do so...
An AWS CloudFormation stack can produce Outputs, such as the name of a DNS record created as part of the stack.
If you have automated the creation of a CloudFormation stack (eg you have a script that calls CloudFormation to trigger the stack creation), it can receive back the content of these outputs. Your script could then extract the desired information and send an email.
Alternatively, you could create an AWS Lambda-backed Custom Resources that dependsOn the DNSRecord. You could then write a Lambda function that sends an email message.
Bottom line: No fully-automated method, but you could write your own.

AWS Lambda S3 Bucket Notification via CloudFormation

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.

Creating an ec2 instance along with IAM roles using cloud formation

I'm very new to Amazon cloudformation technique. I'm trying to launch an ec2 instance along with the IAM roles.
I have cloudformation script for this. But the problem I face is the IAM roles and Ec2 instances are created, but they aren't tied with each other.
I did create the IAM-roles using AWS::IAM::Role and AWS::IAM::InstanceProfile.
Is there any other command that I should use?
Thanks in advance.
Had to dig to get the final result, but here's an example of
Defining an access role (this will allow the EC2 instance to step into / assume to role),
Defining a policy for the role (i.e. when the EC2 assumes the role, what resources does it have access to),
Defining the instance profile (that is referenced by the EC2 instance, and has the access role mapped in)
"S3AccessRole" : {
"Type" : "AWS::IAM::Role",
"Properties" : {
"AssumeRolePolicyDocument" : {
"Statement" : [ {
"Effect" : "Allow",
"Principal" : {
"Service" : [ "ec2.amazonaws.com" ]
},
"Action" : [ "sts:AssumeRole" ]
} ]
},
"Path" : "/"
}
},
"S3RolePolicies" : {
"Type" : "AWS::IAM::Policy",
"Properties" : {
"PolicyName" : "s3access",
"PolicyDocument" : {
"Statement" : [ {
"Effect" : "Allow",
"Action" : "s3:*",
"Resource" : "*"
}]
},
"Roles" : [ { "Ref" : "S3AccessRole" } ]
}
},
"S3InstanceProfile" : {
"Type" : "AWS::IAM::InstanceProfile",
"Properties" : {
"Path" : "/",
"Roles" : [ { "Ref" : "S3AccessRole" } ]
}
}
The policy above allows all access to s3 resources. Adjust according to your needs. The IamInstanceProfile reference in the EC2 instance properties would refer be { "Ref" : "S3InstanceProfile" }
Note that as of May 2015, when you creating a stack that creates IAM roles, you need to check a box acknowledging such creation, otherwise you'll get a "Stack creation error: Requires capabilities : [CAPABILITY_IAM]" error.
The easiest way to solve such problems is to use CloudFormer. CloudFormer is a tool that creates a starting point template from the AWS resources you already have running in your environment.
The CloudFormer tool is packaged as a standalone application that you
can launch inside your AWS environment. The application is started on
a t1.micro Amazon EC2 instance via AWS CloudFormation.
Once you have launched Cloud Former, you will get a web interface (check the URL in the Output section of the launched stack), that will be able to describe all your resources in a specific region. It will lead you through which resources you wish in each category (DNS, Network, Compute...). At the end you can see the template and copy it, or save it in S3.
But if you wish to do it manually, you need to add the AWS::IAM::InstanceProfile you created to the Properties of AWS::EC2::Instance as IamInstanceProfile
{
"Type" : "AWS::EC2::Instance",
"Properties" : {
"AvailabilityZone" : String,
"BlockDeviceMappings" : [ EC2 Block Device Mapping, ... ],
"DisableApiTermination" : Boolean,
"EbsOptimized" : Boolean,
"IamInstanceProfile" : String,
"ImageId" : String,
"InstanceType" : String,
...
"UserData" : String,
"Volumes" : [ EC2 MountPoint, ... ]
}
}
See more details on AWS::EC2::Instance here
Suppose the AWS::IAM::InstanceProfile resource you create is called MyNewRole. To create an instance with that role (in the same CloudFormation template) set the EC2 resource's IamInstanceProfile property to a Ref to that resource. Here's an example (with lots of other details left out):
"Resources": {
"MyNewRole": {
"Type": "AWS::IAM::InstanceProfile",
... more stuff here
},
"MyNewEc2Instance": {
"Type": "AWS::EC2::Instance",
"Properties": {
"IamInstanceProfile": { "Ref": "MyNewRole" },
... more stuff here
}
}
}