问题
It looks like AWS does not provide SMS as a protocol for SNS topic subscribers outside of US East. I wanted to hook up my CloudWatch alarms and receive text messages when something breaks but cannot send them to SMS.
回答1:
YES!
After some digging I was able to get this to work. It's a little more complicated than just selecting a topic or inputing an alarm but it works great!
The key to the solution was using AWS's lambda functions!
The flow of data is such:
> Alarm triggered > -> Push notification to SNS > -> SNS posts to lambda > -> lambda reposts to SNS on us-east-1 > -> subscriber receives message
Just the services:
> CloudWatch Alarm (us-west-2) > -> SNS (us-west-2) > -> Lambda (us-west-2) > -> SNS (us-east-1) > -> SMS subscriber (us-east-1)
I won't talk too much about setting up SNS or setting up lambda, you can go through the examples that already exist for that. However, I will share the lambda code that I wrote to perform this. You can test it by using the publish message to topic functionality.
console.log('Loading function');
var Aws = require('aws-sdk');
var usEastSns = new Aws.SNS({
accessKeyId: "ENTER YOUR ACCESS KEY HERE",
secretAccessKey: "ENTER YOUR SECRET KEY HERE",
region: "us-east-1",
logger: console
});
exports.snsProxy = function(event, context) {
var message = event.Records[0].Sns;
console.log("received message: ", message);
var newMessage = buildNewMessage(message);
console.log("publishing: ", newMessage);
usEastSns.publish(newMessage, function(err, data) {
if (err) {
console.log(err, err.stack); // an error occurred
} else {
// It's important that we succeed in the callback
// otherwise it seems to succeed without sending
console.log(data);
context.succeed(message);
}
})
};
function buildNewMessage(message) {
// For some reason the message that gets sent in the event
// does not contain the same interface as what the library
// expects so it needs to be created
return {
TargetArn: "ENTER YOUR ARN IN US EAST HERE",
Message: message.Message,
Subject: message.Subject,
MessageAttributes: collectAttr(message.MessageAttributes)
};
}
function collectAttr(attrs) {
var newAttrs = {};
for (var attr in attrs) {
newAttrs[attr] ={
DataType: attrs[attr].Type,
StringValue: attrs[attr].Value
}
}
return newAttrs;
}
This doesn't require any additional libraries other than the aws-sdk that is already in the lambda function. You can feel free to leave out the console logging if you don't want it but it was useful for debugging.
If you use there test functionality, it will likely fail. I believe this is because the callback never happens.
Remember that the lambda should not be on us-east-1 since it will be triggered on whatever region you are using.
Links to tutorials:
SNS Publish api:
http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/SNS.html#publish-property
How to node.js in lambda:
http://docs.aws.amazon.com/lambda/latest/dg/programming-model.html
A really great tutorial on using other aws functions:
http://docs.aws.amazon.com/lambda/latest/dg/with-s3-example.html
回答2:
Here's a more modern/concise similar solution leveraging the new Node 8.10 an environment variable:
const AWS = require('aws-sdk');
const TARGET_ARN = process.env.TARGET_ARN;
var sns = new AWS.SNS({
region: "us-east-1"
});
exports.handler = async (event) => {
const message = event.Records[0].Sns;
return await forwardMessage(message);
};
function forwardMessage(message) {
const messageToForward = {
TargetArn: TARGET_ARN,
Message: message.Message,
Subject: message.Subject
};
return sns.publish(messageToForward).promise();
}
回答3:
Outside of us-east-1 you will not find any protocol like SMS in the protocols dropdown
AWS is not providing a proper solution but you can do some hacks for this
To implement this Solution I am using AWS Lambda but if you are having some HTTP endpoint(any sort of API) then you can also use it, the basic steps are same.
- Create a Topic under the SNS section
- Create a Subscription and attach the subscription to the Topic created in point 1
- Go to Lambda service and create a Lambda like below
import json
import boto3
import os
ACCESS_KEY = os.environ.get("ACCESS_KEY", "<your key>")
ACCESS_SECRET = os.environ.get("ACCESS_SECRET", "<your-secret>")
AWS_REGION = os.environ.get("AWS_REGION_NAME", "<your-regoin>")
def execute_sms_service(event, context):
message = json.loads(event['Records'][0]['Sns']['Message'])
alarm_name = message['AlarmName']
alarm_description = message['AlarmDescription']
recipient_numbers = [<recipient number list>]
sender_id = <sender_id>
message = 'Alarm Name: {}\nAlarm Description:{}\n\n '.format(alarm_name, alarm_description)
sns = boto3.client('sns', aws_access_key_id=ACCESS_KEY,
aws_secret_access_key=ACCESS_SECRET,
region_name=AWS_REGION)
for number in recipient_numbers:
response = sns.publish(
PhoneNumber=number,
Message=message,
MessageAttributes={
'AWS.SNS.SMS.SenderID': {'DataType': 'String',
'StringValue': sender_id},
'AWS.SNS.SMS.SMSType': {'DataType': 'String',
'StringValue': 'Promotional'}
}
)
print(response)
- After creating the AWS Lambda check the lambda is working properly or not then attach this lambda in the Subscription protocol created in the second point.
- Create an Alarm and configure the action to send the notification to the Topic created in first point
Hope this helps, and please comment if there is some doubt on the above procedure.
来源:https://stackoverflow.com/questions/34645263/send-an-sms-for-a-cloudwatch-alarm-outside-of-us-east