Send an SMS via AWS SNS using boto3 in an AWS Lambda function? - python-2.7

I would like to send an SMS message from an AWS Lambda function using the boto3 publish method to notify the user of issues via SMS. My lambda function is written in Python and I am using the boto3 module. My lambda function has full rights to SNS. I have this code,
sns = boto3.client('sns')
sns.publish(
PhoneNumber = '+11234567890',
Message = 'Simple text message'
)
According to the boto3 documentation, the publish method accepts the following parameters,
response = client.publish(
TopicArn='string',
TargetArn='string',
PhoneNumber='string',
Message='string',
Subject='string',
MessageStructure='string',
MessageAttributes={
'string': {
'DataType': 'string',
'StringValue': 'string',
'BinaryValue': b'bytes'
}
}
)
It requires a "Message" parameter and one of the following three parameters as described in the docs:
TopicArn (string) -- The topic you want to publish to.
If you don't specify a value for the TopicArn parameter, you must
specify a value for the PhoneNumber or TargetArn parameters.
TargetArn (string) -- Either TopicArn or EndpointArn, but not both.
If you don't specify a value for the TargetArn parameter, you must
specify a value for the PhoneNumber or TopicArn parameters.
PhoneNumber (string) -- The phone number to which you want to deliver
an SMS message. Use E.164 format.
If you don't specify a value for the PhoneNumber parameter, you must
specify a value for the TargetArn or TopicArn parameters.
When my code is executed a parameter validation error is returned. It states,
Unknown parameter in input: "PhoneNumber", must be one of: TopicArn,
TargetArn, >Message, Subject, MessageStructure, MessageAttributes".
So the documentation seems to indicate that PhoneNumber is a valid parameter, but when used, an error occurs and the feedback from the error indicates that PhoneNumber is not a possible parameter. I suspect I am missing something obvious and simple, but could use some help.
I know there are other avenues to send SMS messages such as email gateways and other vendor supplied solutions like Twilio, but I would like to pursue the SNS based route and understand where I have gone wrong.

Actually your example looks right. Here is what I tried
import boto3
sns = boto3.client('sns')
number = '+17702233322'
sns.publish(PhoneNumber = number, Message='example text message' )
Worked like a charm. I recommend using awscli configured with your root account credentials first and take the code for a test drive. Once its working either create a new user with just the rights you need, or apply it to an Instance role.
You need to create a policy that allows SNS:Publish on resource:* (allow texting to everyone) or resource: '+17702233322' (allow text to a specific number).

Related

Filtering AWS SNS messages by attribute value but only if attribute is present

I would like to know how I can create a filtering policy for AWS SNS subscription that would check a message attribute value but only if that attribute is present. By default if I check an attribute value but attribute is not present message is ignored e.g:
"customer_interests": ["paintball"]
I also found this for attribute presence checking:
"customer_interests": [{"exists": true}]
But I'm not sure how to combine this two checks into a single policy.
I've tried the obvious thing:
{
"customer_interests": [{"exists": false}, "paintball"]
}
but it doesn't work.
i have tried the following thing ( your obvious thing ) and it worked for me, i didn't have to even wait for 15 minutes (clearly eventual consistency is not the issue )
Used the following policy
{
"customer_interests": [
{
"exists": false
},
"paintball"
]
}
Created a sns topic with email-based subscription
added filter policy to subscriber
tried with three use case
Case 1
message hello
attribute type string
atrribute name customer_interests
atrribute value paintball
status recieved
Case 3
message hello
attribute type string
atrribute name customer_interests
atrribute value xyz
status not recieved
Case 3
message hello
attribute type string
atrribute name jatin
atrribute value test
status recieved
You apply OR logic in SNS subscription filter policies, by assigning multiple values to an attribute name.
Your example:
{
"customer_interests": [{"exists": false}, "paintball"]
}
should result in filtering messages that:
do not have the customer_interests message attribute
OR have the customer_interests message attribute set to paintball
Please note that additions or changes to a subscription filter policy require up to 15 minutes to fully take effect, as Amazon SNS is eventually consistent.
Changes will not always take effect immediately.

How to subscibe to SNS topic with lambda ARN?

I created a lambda function using the aws_cdk.aws_lambda.Function constructor. Following that I aim to subscribe to a SNS topic I created with boto3. However, one of the arguments needed is the lambda function ARN which I try to get with dynamodb_lambda.function_arn but the problem is that the the attributes returns a unresolved token more specifically ${Token[TOKEN.240]}
Here is a portion of the code to further clarify what I'm doing.
dynamodb_lambda = lambda_.Function(
self, #scope
"foobar", #id
runtime = lambda_.Runtime.PYTHON_3_7,
handler = "lambda_handlers.dynamodb_lambda_handler", #filename.methodname at path
code = lambda_.Code.from_asset(path),
role = iam_.Role( #need this for cloudwatch access
self, #scope
"foobar", #id
assumed_by = iam_.ServicePrincipal('lambda.amazonaws.com'),
managed_policies = [
iam_.ManagedPolicy.from_aws_managed_policy_name('DynamoDBFullAccess')
]
)
)
client_sns = boto3.client("sns")
response = client_sns.create_topic(
Name = c.SNS_TOPIC_NAME,
Tags = [ # for easier filtering and searching
{
'Key': 'CohortStudent',
'Value': 'anon'
}
])
client_sns.subscribe(
TopicArn = response['TopicArn'],
Protocol = 'lambda', #usually "email" or "sms", see link above for possible values
Endpoint = dynamodb_lambda.function_arn
)
The last parameter is where I'm facing trouble. The lambda function isn't created yet so the ARN is a token, but the subscribe function doesn't accept that.
botocore.errorfactory.InvalidParameterException: An error occurred (InvalidParameter) when calling the Subscribe operation: Invalid parameter: Lambda endpoint ARN
Please help me understand how to figure this out. Any help is appreciated.
As luk2302 has commented, using boto3 calls to subscribe to a SNS Topic created using aws_cdk is wrong. A simple fix would be to use subscribe to the lambda using aws_cdk.

Can I create Slack subscriptions to an AWS SNS topic?

I'm trying to create a SNS topic in AWS and subscribe a lambda function to it that will send notifications to Slack apps/users.
I did read this article -
https://aws.amazon.com/premiumsupport/knowledge-center/sns-lambda-webhooks-chime-slack-teams/
that describes how to do it using this lambda code:
#!/usr/bin/python3.6
import urllib3
import json
http = urllib3.PoolManager()
def lambda_handler(event, context):
url = "https://hooks.slack.com/services/xxxxxxx"
msg = {
"channel": "#CHANNEL_NAME",
"username": "WEBHOOK_USERNAME",
"text": event['Records'][0]['Sns']['Message'],
"icon_emoji": ""
}
encoded_msg = json.dumps(msg).encode('utf-8')
resp = http.request('POST',url, body=encoded_msg)
print({
"message": event['Records'][0]['Sns']['Message'],
"status_code": resp.status,
"response": resp.data
})
but the problem is, that in that implementation I have to create a lambda function for every user.
I want to subscribe multiple Slack apps/users to one SNS topic.
Is there a way of doing that without creating a lambda function for each one?
You really DON'T need Lambda. Just SNS and SLACK are enough.
I found a way to integrate AWS SNS with slack WITHOUT AWS Lambda or AWS chatbot. With this approach you can confirm the subscription easily.
Follow the video which show all the step clearly.
https://www.youtube.com/watch?v=CszzQcPAqNM
Steps to follow:
Create slack channel or use existing channel
Create a work flow with selecting Webhook
Create a variable name as "SubscribeURL". The name
is very important
Add the above variable in the message body of the
workflow Publish the workflow and get the url
Add the above Url as subscription of the SNS You will see the subscription URL in the
slack channel
Follow the URl and complete the subscription
Come back to the work flow and change the "SubscribeURL" variable to "Message"
The publish the
message in SNS. you will see the message in the slack channel.
Hi i would say you should go for a for loop and make a list of all the users. Either manually state them in the lambda or get them with api call from slack e.g. this one here: https://api.slack.com/methods/users.list
#!/usr/bin/python3.6
import urllib3
import json
http = urllib3.PoolManager()
def lambda_handler(event, context):
userlist = ["name1", "name2"]
for user in userlist:
url = "https://hooks.slack.com/services/xxxxxxx"
msg = {
"channel": "#" + user, # not sure if the hash has to be here
"username": "WEBHOOK_USERNAME",
"text": event['Records'][0]['Sns']['Message'],
"icon_emoji": ""
}
encoded_msg = json.dumps(msg).encode('utf-8')
resp = http.request('POST',url, body=encoded_msg)
print({
"message": event['Records'][0]['Sns']['Message'],
"status_code": resp.status,
"response": resp.data
})
Another solution you can do is set up email for the slack users, see link:
https://slack.com/help/articles/206819278-Send-emails-to-Slack
When you can just add the emails as subscribers to the sns topic. You can fileter the msg that the receiver gets with Subscription filter policy.

aws - is it possible to use Cognito user pool custom field in lambda function?

I have created a custom field "permissions" in my user pool.
I wonder if it is possible to use this field in my lambda function, so that I can do some permission control for calling the corresponding lambda function.
For example
if((**custom.permissions**).includes("admin")){
// execute the lambda function
}
For example if your lambda function was written using python boto3 you can get the user Attributes like this:
import boto3
client = boto3.client('cognito-idp')
response = client.get_user(
AccessToken='string'
)
The response structure contains
UserAttributes (list) --
An array of name-value pairs representing user attributes.
For custom attributes, you must prepend the custom: prefix to the attribute name.

How do I write a Cloud Function to receive, parse, and publish PubSub messages?

This can be considered a follow-up to this thread, but I need more help with moving things along. Hopefully someone can have a look over my attempts below and provide further guidance.
To summarize, I need a cloud function that
Is triggered by a PubSub message being published in topic A (this can be done in UI).
reads a messy object change notification message in "push" PubSub topic A.
"parse" it
publish a message in PubSub topic B, with the original message ID as data, and other metadata (e.g. file name, size, time) as attributes.
. 1:
Example of a messy object change notification:
\n "kind": "storage#object",\n "id": "bucketcfpubsub/test.txt/1544681756538155",\n "selfLink": "https://www.googleapis.com/storage/v1/b/bucketcfpubsub/o/test.txt",\n "name": "test.txt",\n "bucket": "bucketcfpubsub",\n "generation": "1544681756538155",\n "metageneration": "1",\n "contentType": "text/plain",\n "timeCreated": "2018-12-13T06:15:56.537Z",\n "updated": "2018-12-13T06:15:56.537Z",\n "storageClass": "STANDARD",\n "timeStorageClassUpdated": "2018-12-13T06:15:56.537Z",\n "size": "1938",\n "md5Hash": "sDSXIvkR/PBg4mHyIUIvww==",\n "mediaLink": "https://www.googleapis.com/download/storage/v1/b/bucketcfpubsub/o/test.txt?generation=1544681756538155&alt=media",\n "crc32c": "UDhyzw==",\n "etag": "CKvqjvuTnN8CEAE="\n}\n
To clarify, is this a message with blank "data" field, and all the information above are in attribute pairs (like "attribute name": "attribute data")? Or is it just a long string stuffed into the "data" field, with no "attributes"?
. 2:
In the above thread, a "pull" subscription is used. Is it better than using a "push" subscription? Push sample below:
def create_push_subscription(project_id,
topic_name,
subscription_name,
endpoint):
"""Create a new push subscription on the given topic."""
# [START pubsub_create_push_subscription]
from google.cloud import pubsub_v1
# TODO project_id = "Your Google Cloud Project ID"
# TODO topic_name = "Your Pub/Sub topic name"
# TODO subscription_name = "Your Pub/Sub subscription name"
# TODO endpoint = "https://my-test-project.appspot.com/push"
subscriber = pubsub_v1.SubscriberClient()
topic_path = subscriber.topic_path(project_id, topic_name)
subscription_path = subscriber.subscription_path(
project_id, subscription_name)
push_config = pubsub_v1.types.PushConfig(
push_endpoint=endpoint)
subscription = subscriber.create_subscription(
subscription_path, topic_path, push_config)
print('Push subscription created: {}'.format(subscription))
print('Endpoint for subscription is: {}'.format(endpoint))
# [END pubsub_create_push_subscription]
Or do I need further code after this to receive messages?
Also, doesn't this create a new subscriber every time the Cloud Function is triggered by a pubsub message being published? Should I add a subscription delete code at the end of the CF, or are there more efficient ways to do this?
. 3:
Next, to parse the code, this sample code doing a few attributes as follows:
def summarize(message):
# [START parse_message]
data = message.data
attributes = message.attributes
event_type = attributes['eventType']
bucket_id = attributes['bucketId']
object_id = attributes['objectId']
Will this work with my above notification in 1:?
. 4:
How do I separate the topic_name? Steps 1 and 2 use topic A, while this step is to publish into topic B. Is is as simple as re-writing the topic_name in the below code example?
# TODO topic_name = "Your Pub/Sub topic name"
publisher = pubsub_v1.PublisherClient()
topic_path = publisher.topic_path(project_id, topic_name)
for n in range(1, 10):
data = u'Message number {}'.format(n)
# Data must be a bytestring
data = data.encode('utf-8')
# Add two attributes, origin and username, to the message
publisher.publish(
topic_path, data, origin='python-sample', username='gcp')
print('Published messages with custom attributes.')
Source where I got most of the sample code from (besides the above thread):python-docs-samples. Will adapting and stringing the above code samples together produce useful code? Or will I still be missing stuff like "import ****"?
You should not attempt to manually create a Subscriber running in Cloud Functions. Instead, follow the documentation here for setting up a Cloud Function which will be called with all messages sent to a given topic by passing the --trigger-topic command line parameter.
To address some of your other concerns:
“Should I add a subscription delete code at the end of the CF”- Subscriptions are long-lived resources corresponding to a specific backlog of messages. If the subscription is created and deleted at the end of the cloud function, messages sent when it does not exist will not be received.
“How do I separate the topic_name”- The ‘topic_name’ in this example refers to the last part of the string formatted like this projects/project_id/topics/topic_name that will appear on this page in the cloud console for your topic after it has been created.