boto3 publish message in SNS - amazon-web-services

I want to run a script to publish message to my sns topic. I followed the documentation and my code is this :
import boto3
client = boto3.client('sns')
response = client.publish(
TopicArn='my topic arn',
Message='ptt message '
)
print("Response: {}".format(response))
It prints the response json, but I don't see any message in my AWS SNS console. I have a lambda function which is pretty basic as a subscriber to this sns.
The lambda code :
import json
def lambda_handler(event, context):
# TODO implement
return {
'statusCode': 200,
'body': json.dumps('Hello from Lambda!')
}
How can I be sure that my message "ptt message" has been published successfully? How can I check it?

For testing of integration you can configure Delivery status logging for your SNS topic. There is a simple setting for this in GUI of SNS. This will export to CloudWatch the logs of your subscription triggers.
Secondly, your Lambda is not doing anything with the event, so your "ptt message stays in the "event" dictionary. You can for example log your event and see the structure where the actual message body comes.
import logging
def lambda_handler(event, context):
logging.info(event)
...
And simply to see that the Lambda was triggered by the subscription, you can open the CloudWatch Logs, find the LogGroup automatically created by AWS for your Lambda (has the name of the function in it) and search the Log Group. There are comfortable filters by "last X minutes".
In order to test this integration you can also publish test messages to SNS Topic from the web interface if this feels more convenient than running a script.

Related

How to send CloudFormation Outputs to an SNS topic? (Or any other endpoint, actually?)

I have a CloudFormation template with an Output section. I would like to have the output sent to an SNS topic, but all I found so far is either the notificationsARNs, which sends the stack events (and not the outputs)
You can use boto3 to get the outputs of cloudformation and send it to sns
cloudformation_client = session.client('cloudformation')
response = cloudformation_client.describe_stacks(
StackName=StackName,
)
outputs_list = response['Stacks'][0]['Outputs']
and then post it to sns
https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sns.html

"module initialization " in AWS lambada

I have created a SQS queue and now i want to read the data from my lambada function.I have configured the trigger from SQS to this lambada function.
When i put the message in SQS Queue it shows me 1 message in Flight.But when i click test in lambda function it give me
Error
{
"errorMessage": "module initialization error"
}
I am using below boto3
import boto3
sqs = boto3.client('sqs')
queue_url = 'https://sqs.us-east-1.amazonaws.com/944198216610/My-Queue'
response = sqs.receive_message(
QueueUrl='https://sqs.us-east-1.amazonaws.com/944198216610/My-Queue',
AttributeNames=[
'SentTimestamp'],
MaxNumberOfMessages=1,
MessageAttributeNames=[
'All'
],
VisibilityTimeout=0,
WaitTimeSeconds=0
)
message = response['Messages'][0]
receipt_handle = message['ReceiptHandle']
# Delete received message from queue
sqs.delete_message(
QueueUrl='https://sqs.us-east-1.amazonaws.com/944198216610/My-Queue',
ReceiptHandle=receipt_handle
)
print('Received and deleted message: %s' % message)
Your code calls sqs.receive_message, however to properly integrate Lambda with SQS you can use Lambda event triggers. This way, whenever an event goes into the queue, SQS will automatically invoke your Lambda function with that event (or multiple events based on specified batch size).
See here for setting up an SQS Lambda event trigger
Also see here for setting up a Lambda handler in python where the event parameter will be the event SQS triggers your lambda with (print it and see the format of the event so you can get an idea of how to use the event data).
This way, when you send a message to SQS, SQS will trigger your Lambda function, and if your Lambda returns successfully, SQS will remove the message from the queue. You don't need to do receive_message or delete_message manually.

CloudWatch alarm to SNS in different region

I'm trying to notify an SNS topic from a CloudWatch alarm that's in a different region. The reason is that I want SMS alerting, which isn't available in the region where my services are. If I enter the ARN of the subscription and save the changes in the console, I get "There was an error saving the alarm. Please try again." Trying again does not help. Using a topic in the local region does work, but that's not what I need.
Is there a way to notify a topic in a different region? If not, is there another easy way I can achieve my goal?
Didn't find any docs that explicitly say this can't be done but tried to set an SNS from us-east-1 as an action of an alarm in eu-west-1 using the CLI and I got this:
An error occurred (ValidationError) when calling the PutMetricAlarm operation: Invalid region us-east-1 specified. Only eu-west-1 is supported.
So I'll assume it's not supported.
To get the functionality you need you can use AWS Lambda. Lets say your service is in a region where SMS is not supported, I'll use eu-central-1 as an example.
Setup would go like this:
[us-east-1] Create your SNS topic that can send SMS messages, in the region where SMS is supported.
[eu-central-1 Create a lambda function that sends messages to the SNS topic from step 1 in the region where your service is.
[eu-central-1] Create an SNS topic in the region where your service is.
For the SNS topic configure subscription with AWS Lambda protocol and point it to lambda from step 2.
[eu-central-1] Create your alarm in the region where your service is and put the SNS topic from step 3 as an action.
To add to #Tartaglia's answer, here's the source of such a lambda function using Python 3, cobbled together from various sources because I don't have time to do it properly:
import json
import logging
import boto3
logger = logging.getLogger()
logger.setLevel(logging.INFO)
session = boto3.Session(region_name='eu-west-1') # EU (Ireland)
sns_client = session.client('sns')
def lambda_handler(event, context):
logger.info('Received event: %s', event)
for record in event['Records']:
sns_message = record['Sns']
response = sns_client.publish(
TopicArn='YOUR TOPIC ARN HERE',
Subject=sns_message.get('Subject', None),
Message=sns_message.get('Message', None))
logger.info('Publish response: %s', response)
return 'OK'

Lambda does not trigger SNS event. Chaining AWS lambdas with SNS

I am using AWS to chain lambdas together by having a second lambda subscribe to the SNS feed of the first lambda. The second lambda was not receiving anything from the first lambda and I also get no notification in CloudWatch (or my personal email that I subscribed with) that the SNS event has been triggered. So, I am wondering the question: How do I make sure my SNS receives an event from my lambda firing?
When I use the SNS web UI I get logs like these and my lambda logs show they have been touched, but only when I manually send a message.
{
"notification": {
"messageMD5Sum": "e9a72884afc5d9568f2748e34a4e50a4",
"messageId": "04f4a199-cd25-5a45-a877-f054df9b2adf",
"topicArn": "arn:aws:sns:us-east-1:xxxxxxxxxxxx:first-lambda",
"timestamp": "2017-06-28 02:12:14.098"
},
"delivery": {
"deliveryId": "173dca7a-7b31-55a2-8ee9-9bb7698f35f6",
"destination": "arn:aws:lambda:us-east-1:xxxxxxxxxx:function:second-labmda",
"providerResponse": "{\"lambdaRequestId\":\"33972992-5ba7-11e7-b363-09e0f6dc8594\"}",
"dwellTimeMs": 134,
"attempts": 1,
"statusCode": 202
},
"status": "SUCCESS"
}
I've given my lambda publish permissions in it's role, but that only shows up in IAM and when I view its policy inside of the lambda itself on the GUI I just see lambda firing permissions for the lambda itself. I also am sure that my SNS has permissions to write to the lambda because when you set up the lambda (as I did more than once yesterday) it asks for you to grant privileges to the SNS topic. However, even when testing the SNS topic the lambda is never reached, but I do get an email if I subscribe with my email account.
tl;dr
First lambda should be triggering an SNS event that a second lambda subscribes to. The SNS event is not receiving any data from my first lambda except for two logs in the middle of the night that never went through to my second lambda. How do I make sure my SNS receives an event from my lambda firing?
Solved this by adding a service to my lambda to send the message.
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.sns.AmazonSNSClient;
import com.amazonaws.services.sns.model.PublishRequest;
public class SNSPublishService {
private static final String ACCESS_KEY = "credential1";
private static final String SECRET_KEY = "credential2";
private static final String ARN = "arn:aws:sns:<region>:<id>:<topicname>";
public static void publish(String body) throws InterruptedException {
AmazonSNSClient service = new AmazonSNSClient(new BasicAWSCredentials(ACCESS_KEY, SECRET_KEY));
PublishRequest publishRequest = new PublishRequest()
.withTargetArn(ARN)
.withMessage(body);
service.publish(publishRequest);
}}

Trigger Amazon SNS message via Amazon Lambda

I have an Amazon Lambda instance and an Amazon SNS instance. The Lambda code watches for changes in our database and I would like it to make a call to Amazon SNS to send pushes to our users. For example:
When a user on one of our forums gets a new message, the Lambda code recognizes this change every time it is run (every 10 minutes) and should send a push to the user's smartphone via SNS.
I'm running into a wall when it comes to the documentation; Amazon's docs only talk about how to trigger Lambda code via SNS, but not the reverse. Does anyone have an example of how I can accomplish this?
There is nothing special about pushing SNS notifications in the context of Lambda. I would think of it as just another external service that you interact with.
What you could do is pull in the AWS SDK in your lambda code and after that use the code to make the SNS calls. You will need to inject the right credentials to be able to call the Amazon SNS API (but you probably do something similar for getting the database endpoint and credentials if you are talking to the database)
Yes, you can use AWS Lambda to achieve what you want. You also need to give proper IAM Permissions allowing your Lambda IAM Role to publish messages to you SNS Topic.
Example SNS Publish IAM Policy:
{
"Statement":[ {
"Effect":"Allow",
"Action":"sns:Publish",
"Resource":"arn:aws:sns:*:<your account id>:<your topic id>"
} ]
}
You can use the lambda below to push an SNS message to a user, but you must know what the endpoint ARN is for that user. For example, if in an Android app, when the user logs in you will have the app send a GCM (Google Cloud Messaging) token to your backend (via an API call that triggers a lambda, for example). Your backend, which is connected to GCM, can then use this token to lookup which endpoint ARN corresponds to such user and put that in the lambda below. Alternatively, you can have the app send the endpoint ARN directly to your backend, though I think it might be a bit less secure. Make sure you give IAM permissions to publish to your app via SNS. You can use the lambda below to push the message:
var AWS = require('aws-sdk');
var sns = new AWS.SNS({apiVersion: '2010-03-31'});
exports.handler = (event, context, callback) => {
console.log(JSON.stringify(event))
var payload = {
"default": "The message string.",
"GCM":"{"+
"\"notification\":{"+
"\"body\":\"PUT NOTIFICATION BODY HERE\","+
"\"title\":\"PUT NOTIFICATION TITLE HERE\""+
"}"+
"}"
};
payload = JSON.stringify(payload);
var params = {
TargetArn: 'PUT THE ENDPOINT ARN HERE',
Subject: 'foo2',
MessageStructure: 'json',
Message: payload
}
sns.publish(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});
};