Question: How do I set up customized email notifications for Security Hub findings using EventBridge with an Amazon SNS topic?

I want to set up a customized email notification for Security Hub findings using Amazon EventBridge and an Amazon Simple Notification Service (Amazon SNS) topic.

Short descriptionThe following resolution shows you how to configure EventBridge with SNS to receive Security Hub notifications. Based on the EventBridge rule, Amazon SNS sends a notification to the email address or addresses subscribed to the topic when your event occurs. The message generated can be difficult to read because of its formatting. However, including an AWS Lambda function creates a customized alert message with improved formatting before sending it to the SNS subscribers.To create a customized email notification, do the following:1.    Create the following EventBridge rule targets:An SNS topic and email subscriptions.A Lambda function.2.    Create an EventBridge rule.3.    Receive a customized notification.Important: The following resolution uses AWS Security Hub events and a Lambda function for the customization. For more information on associated costs, see AWS Security Hub pricing and AWS Lambda Pricing.

ResolutionCreate an SNS topic and subscription1.    Open the Amazon SNS console.2.    On the navigation pane, choose Topics.3.    Choose Create topic.4.    In the Details section, for Type, choose Standard.5.    For Name, enter the name of your topic.6.    Choose Create topic.7.    From the created topic, choose Create subscription.8.    For Topic ARN, select the Amazon Resource Name (ARN) of the topic that you created if not automatically populated.9.    For Protocol, enter Email.10.    For Endpoint, enter the email address where you want to receive SNS notifications.11.    Choose Create subscription.Important: You must confirm the subscription in the confirmation email sent to the subscriber for the subscription to switch from PendingConfirmation status to Confirmed.Note: (Optional) You can also create an authenticated subscription that prevents the unsubscribe action on your topic.Create a Lambda functionCreate a Lambda function that extracts the information that you want from the JSON content and publish a customized message to Amazon SNS.1.    Open the Lambda console.2.    On the navigation pane, choose Functions.3.    Choose Create function.4.    Choose Author from scratch.5.    For Function name, enter a name for your function.6.    Choose Runtime, and then choose Python 3.9.7.    For Architecture, select x86_64.8.    Expand Change default execution role.9.    For Execution role, select Create a new role from AWS policy templates.10.    For Role name, enter a name for the role.11.    For Policy template, choose Amazon SNS publish policy.12.    Choose Create function.13.    After the function is created, paste the following code in the Code source section. import jsonimport boto3sns = boto3.client('sns')def lambda_handler(event, context):        #Extract details from JSON event    detailType= event["detail-type"]    region = event["region"]    accountId = event["account"]         #Security Hub Insight Results    if (detailType == "Security Hub Insight Results"):                action = event["detail"]["actionDescription"]                message = "Alert: %s in %s for account: %s\n Action description: %s" % (detailType, region,accountId,action)        elif  ("Security Hub Findings" in detailType):                finding = event["detail"]["findings"][0]         findingTime = finding["FirstObservedAt"]        findingType = finding["Types"][0]        findingDescription = finding["Description"]        remediation = finding["Remediation"]["Recommendation"]["Text"]                #Security Hub Findings - Custom finding        if(detailType == "Security Hub Findings - Custom"):            complianceStatus = finding["Compliance"]["Status"]            severity = finding["Severity"]["Label"]            remediationUrl = finding["Remediation"]["Recommendation"]["Url"]                        message = "Alert: %s in %s for account: %s\n\nFinding regarding: [%s] %s\n Severity: %s\nDescription: %s\nFirst observed at: %s\n%s: %s" % (detailType, region, accountId, complianceStatus, findingType,             severity, findingDescription, findingTime, remediation, remediationUrl)                #Security Hub Findings - Imported finding        else:            message = "Alert: %s in %s for account: %s\n\nFinding regarding: %s\nFirst observed at: %s\nRemediation recommendation: %s" % (detailType, region, accountId, findingDescription,findingTime, remediation)        #AWS API Call via CloudTrail finding    elif (detailType == "AWS API Call via CloudTrail"):                time = event["detail"]["eventTime"]        eventName = event["detail"]["eventName"]        requestParameters = event["detail"]["requestParameters"]                message = "Alert: %s in %s for account: %s at time: %s\n\n Event: %s \n Request parameters: %s" % (detailType, region, accountId, time, eventName, requestParameters)                    #If the event doesn't match any of the above, return the event        else:        message = str(event)        response = sns.publish(            TopicArn = "arn:aws:sns:eu-west-x:xxxxxxxxxxxx:test",            Message = message            )        return {      'statusCode': 200,      'body': json.dumps('Success!')}Note: The preceding code customizes and re-formats all Security Hub alert messages. Replace TopicArn("arn:aws:sns:REGION:ACCOUNT_ID:SecurityHubFindings") with your topic ARN. Replace eu-west-x:xxxxxxxxxxxx with your Account ID. 14.    To save the function code, choose Deploy.This function takes the default Security Hub events and reformats them into a more readable format. The following are example messages:Example 1: Security Hub Insight resultDefault:{"version": "0", "id": "ac844908-d14e-05b1-4b7b-836d85110e26", "detail-type": "Security Hub Insight Results", "source": "aws.securityhub", "account": "123456789012", "time": "2019-04-11T21:31:57Z", "region": "us-east-1", "resources": ["arn:aws:securityhub:us-east-1:123456789012:action/custom/slackMessaging"], "detail": {"actionName": "SendToSlack", "actionDescription": "Send Findings to Slack", "insightName": "5. AWS users with the most suspicious activity", "insightArn": "arn:aws:securityhub:::insight/securityhub/default/9", "resultType": "ResourceAwsIamAccessKeyUserName", "insightResults": [{"Admin": 7}, {"DenySlr_UI_User": 1}]}}Customized:Alert: Security Hub Insight Results in us-east-1 for account: 123456789012Action description: Send Findings to SlackExample 2: Security Hub Findings - Custom ActionDefault:{ "version": "0", "id": "e215f5c7-a866-e0cd-6d11-fc7ecf97e381", "detail-type": "Security Hub Findings - Custom Action", "source": "aws.securityhub", "account": "123456789012", "time": "2019-04-11T22:06:13Z", "region": "us-east-1", "resources": ["arn:aws:securityhub:us-east-1:123456789012:action/custom/slackMessaging"], "detail": { "actionName": "SendToSlack", "actionDescription": "Send Findings to Slack", "findings": [{ "SchemaVersion": "2018-10-08", "Id": "arn:aws:securityhub:us-east-1:123456789012:subscription/cis-aws-foundations-benchmark/v/1.2.0/1.12/finding/17932c44-6d58-4b3c", "ProductArn": "arn:aws:securityhub:us-east-1::product/aws/securityhub", "GeneratorId": "arn:aws:securityhub:::ruleset/cis-aws-foundations-benchmark/v/1.2.0/rule/1.12", "AwsAccountId": "123456789012", "Types": ["Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"], "FirstObservedAt": "2018-12-02T05:06:34.874Z", "LastObservedAt": "2019-04-11T18:07:10.995Z", "CreatedAt": "2018-12-02T05:06:34.874Z", "UpdatedAt": "2019-04-11T18:26:20.631Z", "Severity": { "Product": 0, "Normalized": 0 }, "Title": "1.12 Ensure no root account access key exists", "Description": "The root account is the most privileged user in an AWS account...", "Remediation": { "Recommendation": { "Text": "For directions on how to fix this issue, please consult the AWS Security Hub CIS documentation.", "Url": "https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-standards.html#securityhub-standards-checks-1.12" } }, "ProductFields": { "StandardsGuideArn": "arn:aws:securityhub:::ruleset/cis-aws-foundations-benchmark/v/1.2.0", "StandardsGuideSubscriptionArn": "arn:aws:securityhub:us-east-1:123456789012:subscription/cis-aws-foundations-benchmark/v/1.2.0", "RuleId": "1.12", "RecommendationUrl": "https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-standards.html#securityhub-standards-checks-1.12", "RecordState": "ACTIVE", "aws/securityhub/FindingId": "arn:aws:securityhub:us-east-1::product/aws/securityhub/arn:aws:securityhub:us-east-1:123456789012:subscription/cis-aws-foundations-benchmark/v/1.2.0/1.12/finding/17932c44-6d58-4b3c", "aws/securityhub/SeverityLabel": "INFORMATIONAL", "aws/securityhub/ProductName": "Security Hub", "aws/securityhub/CompanyName": "AWS" }, "Resources": [{ "Type": "AwsAccount", "Id": "AWS::::Account:123456789012", "Partition": "aws", "Region": "us-east-1" }], "Compliance": { "Status": "PASSED" }, "RecordState": "ACTIVE", "WorkflowState": "NEW" }, { "SchemaVersion": "2018-10-08", "Id": "arn:aws:securityhub:us-east-1:123456789012:subscription/cis-aws-foundations-benchmark/v/1.2.0/2.8/finding/5d6b42d8-122b-4cdf-8498-e045752e170c", "ProductArn": "arn:aws:securityhub:us-east-1::product/aws/securityhub", "GeneratorId": "arn:aws:securityhub:::ruleset/cis-aws-foundations-benchmark/v/1.2.0/rule/2.8", "AwsAccountId": "123456789012", "Types": ["Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"], "FirstObservedAt": "2019-01-05T05:21:44.990Z", "LastObservedAt": "2019-04-11T18:26:12.510Z", "CreatedAt": "2019-01-05T05:21:44.990Z", "UpdatedAt": "2019-04-11T18:26:12.510Z", "Severity": { "Product": 0, "Normalized": 0 }, "Title": "2.8 Ensure rotation for customer created CMKs is enabled", "Description": "AWS Key Management Service (KMS) allows customers to rotate the backing key...", "Remediation": { "Recommendation": { "Text": "For directions on how to fix this issue, please consult the AWS Security Hub CIS documentation.", "Url": "https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-standards.html#securityhub-standards-checks-2.8" } }, "ProductFields": { "StandardsGuideArn": "arn:aws:securityhub:::ruleset/cis-aws-foundations-benchmark/v/1.2.0", "StandardsGuideSubscriptionArn": "arn:aws:securityhub:us-east-1:123456789012:subscription/cis-aws-foundations-benchmark/v/1.2.0", "RuleId": "2.8", "RecommendationUrl": "https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-standards.html#securityhub-standards-checks-2.8", "RecordState": "ACTIVE", "aws/securityhub/FindingId": "arn:aws:securityhub:us-east-1::product/aws/securityhub/arn:aws:securityhub:us-east-1:123456789012:subscription/cis-aws-foundations-benchmark/v/1.2.0/2.8/finding/5d6b42d8-122b-4cdf-8498-e045752e170c", "aws/securityhub/SeverityLabel": "INFORMATIONAL", "aws/securityhub/ProductName": "Security Hub", "aws/securityhub/CompanyName": "AWS" }, "Resources": [{ "Type": "AwsAccount", "Id": "AWS::::Account:123456789012", "Partition": "aws", "Region": "us-east-1" }], "Compliance": { "Status": "PASSED" }, "RecordState": "ACTIVE", "WorkflowState": "NEW" }] } }Customized:Alert: Security Hub Findings - Custom Action in us-east-1 for account: 123456789012Finding regarding: The root account is the most privileged user in an AWS account...First observed at: 2018-12-02T05:06:34.874ZRemediation recommendation: For directions on how to fix this issue, please consult the AWS Security Hub CIS documentation.Example 3: Security Hub Findings - ImportedDefault:{ "version": "0", "id": "8e5622f9-d81c-4d81-612a-9319e7ee2506", "detail-type": "Security Hub Findings - Imported", "source": "aws.securityhub", "account": "123456789012", "time": "2019-04-11T21:52:17Z", "region": "us-west-2", "resources": ["arn:aws:securityhub:us-west-2::product/aws/macie/arn:aws:macie:us-west-2:123456789012:integtest/trigger/6294d71b927c41cbab915159a8f326a3/alert/f2893b211841"], "detail": { "findings": [{ "SchemaVersion": "2018-10-08", "Id": "arn:aws:macie:us-west-2:123456789012:integtest/trigger/6214d71b927c41cbab015159a8f316a3/alert/f2893b211841467198cc1201e9031ee4", "ProductArn": "arn:aws:securityhub:us-west-2::product/aws/macie", "GeneratorId": "arn:aws:macie:us-west-2:123456789012:integtest/trigger/6214d71b927c41cbab015159a8f316a3", "AwsAccountId": "123456789012", "Types": ["Sensitive Data Identifications/Passwords/Google Suite Two-factor backup codes in S3"], "FirstObservedAt": "2019-04-11T21:52:15.900Z", "LastObservedAt": "2019-04-11T21:52:15.900Z", "CreatedAt": "2019-04-11T21:52:15.900Z", "UpdatedAt": "2019-04-11T21:52:15.900Z", "Severity": { "Product": 6, "Normalized": 15 }, "Confidence": 5, "Title": "Google Suite Two-Factor Backup Codes uploaded to S3", "Description": "Google Suite two-factor backup codes uploaded to S3....", "Remediation": { "Recommendation": { "Text": "v2 Release" } }, "ProductFields": { "rule-arn": "arn:aws:macie:us-west-2:123456789012:trigger/6214d71b927c41cbab015159a8f316a3", "tags:0": "DATA_COMPLIANCE", "tags:1": "BASIC_ALERT", "themes:0/theme": "google_two_factor_backup", "themes:0/count": "1", "dlpRisk:0/risk": "8", "dlpRisk:0/count": "1", "owner:0/name": "vchin", "owner:0/count": "1", "aws/securityhub/FindingId": "arn:aws:securityhub:us-west-2::product/aws/macie/arn:aws:macie:us-west-2:123456789012:integtest/trigger/6214d71b927c41cbab015159a8f316a3/alert/f2893b211841467198cc1201e9031ee4", "aws/securityhub/SeverityLabel": "LOW", "aws/securityhub/ProductName": "Macie", "aws/securityhub/CompanyName": "Amazon" }, "Resources": [{ "Type": "AwsS3Bucket", "Id": "arn:aws:s3:::test-bucket-12", "Partition": "aws", "Region": "us-west-2" }], "RecordState": "ACTIVE", "WorkflowState": "NEW" }] } }Customized:Alert: Security Hub Findings - Imported in us-west-2 for account: 123456789012Finding regarding: Google Suite two-factor backup codes uploaded to S3....First observed at: 2019-04-11T21:52:15.900ZRemediation recommendation: v2 ReleaseExample 4 - AWS API Call via CloudTrailDefault:{"version": "0", "id": "b34c4525-95f0-8dd1-cd9e-9fc5be10039e", "detail-type": "AWS API Call via CloudTrail", "source": "aws.securityhub", "account": "123456789012", "time": "2021-12-10T10:47:54Z", "region": "eu-west-1", "resources": [], "detail": {"eventVersion": "1.08", "userIdentity": {"type": "AssumedRole", "principalId": "AROATGMYP4FKHTE5RKJC3", "arn": "arn:aws:sts::123456789012", "accountId": "123456789012", "accessKeyId": "ASIATGMYXXFKNHWOYQF7", "sessionContext": {"sessionIssuer": {"type": "Role", "principalId": "AROATGMYP4FKHX5RKJC3", "arn": "arn:aws:iam::123456789012:role/Admin", "accountId": "123456789012", "userName": "Admin"}, "webIdFederationData": {}, "attributes": {"creationDate": "2021-12-10T10:08:16Z", "mfaAuthenticated": "false"}}}, "eventTime": "2021-12-10T10:47:54Z", "eventSource": "securityhub.amazonaws.com", "eventName": "BatchUpdateFindings", "awsRegion": "eu-west-1", "sourceIPAddress": "54.240.197.20", "userAgent": "aws-internal/3 aws-sdk-java/1.12.112 Linux/5.4.156-94.273.amzn2int.x86_64 OpenJDK_64-Bit_Server_VM/25.312-b07 java/1.8.0_312 vendor/Oracle_Corporation cfg/retry-mode/standard", "requestParameters": {"Workflow": {"Status": "NEW"}, "FindingIdentifiers": [{"Id": "arn:aws:securityhub:eu-west-1:123456789012:subscription/cis-aws-foundations-benchmark/v/1.2.0/2.5/finding/2fd7f0dd-1088-44c5-bbe1-9c8a0ddce035", "ProductArn": "arn:aws:securityhub:eu-west-1::product/aws/securityhub"}]}, "responseElements": {"UnprocessedFindings": [], "ProcessedFindings": [{"Id": "arn:aws:securityhub:eu-west-1:123456789012:subscription/cis-aws-foundations-benchmark/v/1.2.0/2.5/finding/2fd7f0dd-1088-44c5-bbe1-9c8a0ddce035", "ProductArn": "arn:aws:securityhub:eu-west-1::product/aws/securityhub"}]}, "requestID": "fd52e76e-282f-47c7-a7bc-b9a1e1ca2cdd", "eventID": "433b8e9c-cf08-4909-adf7-ea0c548459ad", "readOnly": "False", "eventType": "AwsApiCall", "managementEvent": "True", "recipientAccountId": "123456789012", "eventCategory": "Management"}}Customized:Alert: AWS API Call via CloudTrail in eu-west-1 for account: 123456789012 at time: 2021-12-10T10:47:32ZEvent: BatchUpdateFindingsRequest parameters: {'Workflow': {'Status': 'NOTIFIED'}, 'FindingIdentifiers': [{'Id': 'arn:aws:securityhub:eu-west-x:xxxxxxxxxxxx:subscription/cis-aws-foundations-benchmark/v/1.2.0/2.5/finding/2fd7f0dd-1088-44c5-bbe1-9c8a0ddce035', 'ProductArn': 'arn:aws:securityhub:eu-west-1::product/aws/securityhub'}]}Note: You can edit the message for each Security finding type to make it more suitable for your use case.Create and configure the EventBridge rule1.    Open the EventBridge console.2.    Select Create rule.3.    Enter a Name for your rule. You can optionally enter a Description.4.    In Define pattern, select Event pattern.5.    For Event matching pattern, select Pre-defined pattern by service.6.    For Service provider, select AWS.7.    For Service name, select Security hub.8.    For Event type, select All Events.Note: You can also configure alerts for specific events rather than for all events. The Lambda function is configured to reformat all events into a more readable format.9.    In the Select event bus section, choose AWS default event bus.10.    In Select targets, choose Lambda target from the Target menu.11.    Select the previously created Lambda function.Receive a customized notificationWhen any one of the configured events occur, you receive a customized notification via email from no-reply@sns.amazonaws.com.

Related informationHow can I configure an EventBridge rule for GuardDuty to send custom SNS notifications if specific AWS service event types trigger?Tutorial: Use input transformer to customize what EventBridge passes to the event targetWhy wasn't my Lambda function triggered by my EventBridge rule?