管理警报通知 - Amazon IoT Events
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

管理警报通知

Amazon IoT Events使用 Lambda 函数来管理警报通知。您可以使用 Amazon IoT Events 提供的 Lambda 函数或创建新的函数。

注意

警报通知功能在中国 (北京) 区域中不可用。

创建 Lambda 函数

Amazon IoT Events 提供了 Lambda 函数,使警报能够发送和接收电子邮件和短信通知。

要求

为警报创建 Lambda 函数时应遵循以下要求:

  • 如果您的警报发送电子邮件或短信通知,则您必须具有允许 Amazon Lambda 与 Amazon SES 和 Amazon SNS 配合使用的 IAM 角色。

    示例策略:

    { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "ses:GetIdentityVerificationAttributes", "ses:SendEmail", "ses:VerifyEmailIdentity" ], "Resource": "*" }, { "Effect": "Allow", "Action": [ "sns:Publish", "sns:OptInPhoneNumber", "sns:CheckIfPhoneNumberIsOptedOut" ], "Resource": "*" }, { "Effect": "Deny", "Action": [ "sns:Publish" ], "Resource": "arn:aws:sns:*:*:*" } ] }
  • 必须为 Amazon IoT Events 和 Amazon Lambda 选择相同的 Amazon 区域。有关支持的区域列表,请参阅 Amazon Web Services 一般参考 中的Amazon IoT Events端点和配额以及Amazon Lambda端点和配额

部署 Lambda 函数

本教程使用 Amazon CloudFormation 模板来部署 Lambda 函数。此模板会自动创建一个 IAM 角色,该角色允许 Lambda 函数与 Amazon SES 和亚马逊 SNS 配合使用。

以下内容向您展示了如何使用 Amazon Command Line Interface (Amazon CLI) 来创建 CloudFormation 堆栈。

  1. 在设备的终端中,运行 aws --version 以检查是否安装了 Amazon CLI。有关更多信息,请参阅《Amazon Command Line Interface 用户指南》中的安装 Amazon CLI

  2. 运行 aws configure list 以检查您是否在具有本教程所有 Amazon 资源的 Amazon 区域中配置了 Amazon CLI。有关更多信息,请参阅《Amazon Command Line Interface 用户指南》中的配置 Amazon CLI

  3. 下载 CloudFormation 模板,网址notificationLambda.template.yaml.zip

    注意

    如果您在下载文件时遇到困难,也可以在 CloudFormation 模板 中找到该模板。

  4. 解压缩内容并将其作为 notificationLambda.template.yaml 保存在本地。

  5. 在您的设备上打开终端,导航到下载了 notificationLambda.template.yaml 文件的目录。

  6. 要创建 CloudFormation 堆栈,请运行以下命令:

    aws cloudformation create-stack --stack-name notificationLambda-stack --template-body file://notificationLambda.template.yaml --capabilities CAPABILITY_IAM

您可以修改此 CloudFormation 模板来自定义 Lambda 函数及其行为。

注意

Amazon Lambda 针对函数错误重试两次。如果该函数没有足够的容量来处理所有传入请求,则事件可能会在队列中等待数小时或数天才能发送到该函数。您可以在函数上配置未送达消息队列 (DLQ) 以捕获未成功处理的事件。有关更多信息,请参阅 Amazon Lambda 开发人员指南中的异步调用

您也可以在 CloudFormation 控制台中创建或配置堆栈。有关更多信息,请参阅 Amazon CloudFormation 用户指南中的使用堆栈

创建自定义 Lambda 函数

您可以创建 Lambda 函数或修改 Amazon IoT Events 提供的函数。

创建自定义 Lambda 函数时应遵循以下要求。

  • 添加允许您的 Lambda 函数执行指定操作和访问 Amazon 资源的权限。

  • 如果您使用 Amazon IoT Events 提供的 Lambda 函数,请务必选择 Python 3.7 运行时系统。

Lambda 函数示例:

import boto3 import json import logging import datetime logger = logging.getLogger() logger.setLevel(logging.INFO) ses = boto3.client('ses') sns = boto3.client('sns') def check_value(target): if target: return True return False # Check whether email is verified. Only verified emails are allowed to send emails to or from. def check_email(email): if not check_value(email): return False result = ses.get_identity_verification_attributes(Identities=[email]) attr = result['VerificationAttributes'] if (email not in attr or attr[email]['VerificationStatus'] != 'Success'): logging.info('Verification email for {} sent. You must have all the emails verified before sending email.'.format(email)) ses.verify_email_identity(EmailAddress=email) return False return True # Check whether the phone holder has opted out of receiving SMS messages from your account def check_phone_number(phone_number): try: result = sns.check_if_phone_number_is_opted_out(phoneNumber=phone_number) if (result['isOptedOut']): logger.info('phoneNumber {} is not opt in of receiving SMS messages. Phone number must be opt in first.'.format(phone_number)) return False return True except Exception as e: logging.error('Your phone number {} must be in E.164 format in SSO. Exception thrown: {}'.format(phone_number, e)) return False def check_emails(emails): result = True for email in emails: if not check_email(email): result = False return result def lambda_handler(event, context): logging.info('Received event: ' + json.dumps(event)) nep = json.loads(event.get('notificationEventPayload')) alarm_state = nep['alarmState'] default_msg = 'Alarm ' + alarm_state['stateName'] + '\n' timestamp = datetime.datetime.utcfromtimestamp(float(nep['stateUpdateTime'])/1000).strftime('%Y-%m-%d %H:%M:%S') alarm_msg = "{} {} {} at {} UTC ".format(nep['alarmModelName'], nep.get('keyValue', 'Singleton'), alarm_state['stateName'], timestamp) default_msg += 'Sev: ' + str(nep['severity']) + '\n' if (alarm_state['ruleEvaluation']): property = alarm_state['ruleEvaluation']['simpleRule']['inputProperty'] default_msg += 'Current Value: ' + str(property) + '\n' operator = alarm_state['ruleEvaluation']['simpleRule']['operator'] threshold = alarm_state['ruleEvaluation']['simpleRule']['threshold'] alarm_msg += '({} {} {})'.format(str(property), operator, str(threshold)) default_msg += alarm_msg + '\n' emails = event.get('emailConfigurations', []) logger.info('Start Sending Emails') for email in emails: from_adr = email.get('from') to_adrs = email.get('to', []) cc_adrs = email.get('cc', []) bcc_adrs = email.get('bcc', []) msg = default_msg + '\n' + email.get('additionalMessage', '') subject = email.get('subject', alarm_msg) fa_ver = check_email(from_adr) tas_ver = check_emails(to_adrs) ccas_ver = check_emails(cc_adrs) bccas_ver = check_emails(bcc_adrs) if (fa_ver and tas_ver and ccas_ver and bccas_ver): ses.send_email(Source=from_adr, Destination={'ToAddresses': to_adrs, 'CcAddresses': cc_adrs, 'BccAddresses': bcc_adrs}, Message={'Subject': {'Data': subject}, 'Body': {'Text': {'Data': msg}}}) logger.info('Emails have been sent') logger.info('Start Sending SNS message to SMS') sns_configs = event.get('smsConfigurations', []) for sns_config in sns_configs: sns_msg = default_msg + '\n' + sns_config.get('additionalMessage', '') phone_numbers = sns_config.get('phoneNumbers', []) sender_id = sns_config.get('senderId') for phone_number in phone_numbers: if check_phone_number(phone_number): if check_value(sender_id): sns.publish(PhoneNumber=phone_number, Message=sns_msg, MessageAttributes={'AWS.SNS.SMS.SenderID':{'DataType': 'String','StringValue': sender_id}}) else: sns.publish(PhoneNumber=phone_number, Message=sns_msg) logger.info('SNS messages have been sent')

有关更多信息,请参阅 Amazon Lambda 开发人员指南中的什么是 Amazon Lambda?

CloudFormation 模板

使用以下 CloudFormation 模板来创建您的 Lambda 函数。

AWSTemplateFormatVersion: '2010-09-09' Description: 'Notification Lambda for Alarm Model' Resources: NotificationLambdaRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Statement: - Effect: Allow Principal: Service: lambda.amazonaws.com Action: sts:AssumeRole Path: "/" ManagedPolicyArns: - 'arn:aws:iam::aws:policy/AWSLambdaExecute' Policies: - PolicyName: "NotificationLambda" PolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Action: - "ses:GetIdentityVerificationAttributes" - "ses:SendEmail" - "ses:VerifyEmailIdentity" Resource: "*" - Effect: "Allow" Action: - "sns:Publish" - "sns:OptInPhoneNumber" - "sns:CheckIfPhoneNumberIsOptedOut" Resource: "*" - Effect: "Deny" Action: - "sns:Publish" Resource: "arn:aws:sns:*:*:*" NotificationLambdaFunction: Type: AWS::Lambda::Function Properties: Role: !GetAtt NotificationLambdaRole.Arn Runtime: python3.7 Handler: index.lambda_handler Timeout: 300 MemorySize: 3008 Code: ZipFile: | import boto3 import json import logging import datetime logger = logging.getLogger() logger.setLevel(logging.INFO) ses = boto3.client('ses') sns = boto3.client('sns') def check_value(target): if target: return True return False # Check whether email is verified. Only verified emails are allowed to send emails to or from. def check_email(email): if not check_value(email): return False result = ses.get_identity_verification_attributes(Identities=[email]) attr = result['VerificationAttributes'] if (email not in attr or attr[email]['VerificationStatus'] != 'Success'): logging.info('Verification email for {} sent. You must have all the emails verified before sending email.'.format(email)) ses.verify_email_identity(EmailAddress=email) return False return True # Check whether the phone holder has opted out of receiving SMS messages from your account def check_phone_number(phone_number): try: result = sns.check_if_phone_number_is_opted_out(phoneNumber=phone_number) if (result['isOptedOut']): logger.info('phoneNumber {} is not opt in of receiving SMS messages. Phone number must be opt in first.'.format(phone_number)) return False return True except Exception as e: logging.error('Your phone number {} must be in E.164 format in SSO. Exception thrown: {}'.format(phone_number, e)) return False def check_emails(emails): result = True for email in emails: if not check_email(email): result = False return result def lambda_handler(event, context): logging.info('Received event: ' + json.dumps(event)) nep = json.loads(event.get('notificationEventPayload')) alarm_state = nep['alarmState'] default_msg = 'Alarm ' + alarm_state['stateName'] + '\n' timestamp = datetime.datetime.utcfromtimestamp(float(nep['stateUpdateTime'])/1000).strftime('%Y-%m-%d %H:%M:%S') alarm_msg = "{} {} {} at {} UTC ".format(nep['alarmModelName'], nep.get('keyValue', 'Singleton'), alarm_state['stateName'], timestamp) default_msg += 'Sev: ' + str(nep['severity']) + '\n' if (alarm_state['ruleEvaluation']): property = alarm_state['ruleEvaluation']['simpleRule']['inputProperty'] default_msg += 'Current Value: ' + str(property) + '\n' operator = alarm_state['ruleEvaluation']['simpleRule']['operator'] threshold = alarm_state['ruleEvaluation']['simpleRule']['threshold'] alarm_msg += '({} {} {})'.format(str(property), operator, str(threshold)) default_msg += alarm_msg + '\n' emails = event.get('emailConfigurations', []) logger.info('Start Sending Emails') for email in emails: from_adr = email.get('from') to_adrs = email.get('to', []) cc_adrs = email.get('cc', []) bcc_adrs = email.get('bcc', []) msg = default_msg + '\n' + email.get('additionalMessage', '') subject = email.get('subject', alarm_msg) fa_ver = check_email(from_adr) tas_ver = check_emails(to_adrs) ccas_ver = check_emails(cc_adrs) bccas_ver = check_emails(bcc_adrs) if (fa_ver and tas_ver and ccas_ver and bccas_ver): ses.send_email(Source=from_adr, Destination={'ToAddresses': to_adrs, 'CcAddresses': cc_adrs, 'BccAddresses': bcc_adrs}, Message={'Subject': {'Data': subject}, 'Body': {'Text': {'Data': msg}}}) logger.info('Emails have been sent') logger.info('Start Sending SNS message to SMS') sns_configs = event.get('smsConfigurations', []) for sns_config in sns_configs: sns_msg = default_msg + '\n' + sns_config.get('additionalMessage', '') phone_numbers = sns_config.get('phoneNumbers', []) sender_id = sns_config.get('senderId') for phone_number in phone_numbers: if check_phone_number(phone_number): if check_value(sender_id): sns.publish(PhoneNumber=phone_number, Message=sns_msg, MessageAttributes={'AWS.SNS.SMS.SenderID':{'DataType': 'String','StringValue': sender_id}}) else: sns.publish(PhoneNumber=phone_number, Message=sns_msg) logger.info('SNS messages have been sent')