diff --git a/sns-slack-teams-integration-cdk-python/README.md b/sns-slack-teams-integration-cdk-python/README.md new file mode 100644 index 000000000..b68ec2cc5 --- /dev/null +++ b/sns-slack-teams-integration-cdk-python/README.md @@ -0,0 +1,71 @@ +# Amazon SNS integration with slack/teams channels + +This pattern demonstrates how you can setup a fully serverless setup to integrate your SNS topic with a Slack/Teams channel. This provisions AWS resources for you and you just need to integrate your Slack/Teams webhooks to get it running. + +[Learn more about this pattern at Serverless Land Patterns](https://serverlessland.com/repos/sns-slack-teams-integration-cdk-python) + +[SNS & Slack/Teams integration diagram](https://github.com/ajayv-AWS/Img/blob/870961eb05a4732ea2ee6a36af8ad1fd6a7ef206/SNS-teams-slack.drawio.png?raw=true) + +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + +## Requirements + +* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +* [AWS CDK](https://docs.aws.amazon.com/cdk/latest/guide/cli.html) installed and configured + +## Deployment Instructions + +1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: + ```bash + git clone https://github.com/aws-samples/serverless-patterns + ``` +2. Change directory to the pattern directory: + ```bash + cd serverless-patterns/sns-slack-teams-integration-cdk-python + ``` +3. To manually create a virtualenv on MacOS and Linux: + ```bash + python3 -m venv .venv + ``` +4. After the init process completes and the virtualenv is created, you can use the following +step to activate your virtualenv. + ```bash + source .venv/bin/activate + ``` +5. If you are a Windows platform, you would activate the virtualenv like this: + ```bash + .venv\Scripts\activate.bat + ``` +6. Once the virtualenv is activated, you can install the required dependencies. + ```bash + pip install -r requirements.txt + ``` +7. To deploy the application: + ```bash + cdk deploy + ``` + +## How it works + +This template will create an SNS topic which you can use to publish notifications to your Slack/Teams channel, and a Lambda function which will process the SNS message and pass it to the Slack/Teams webhook. You can customise the code to apply additional formatting as per your usecase. + +## Testing + +Publish a message from AWS Console or by CLI. + +Example using CLI: + +aws sns publish --topic-arn ENTER_YOUR_SNS_TOPIC_ARN --subject testSubject --message testMessage + +## Cleanup + +* Delete the stack + ```bash + cdk destroy + ``` +---- +Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 diff --git a/sns-slack-teams-integration-cdk-python/app.py b/sns-slack-teams-integration-cdk-python/app.py new file mode 100644 index 000000000..74f53da54 --- /dev/null +++ b/sns-slack-teams-integration-cdk-python/app.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +import os + +import aws_cdk as cdk + +from sns_slack_teams_integration_cdk_python.sns_slack_teams_integration_cdk_python_stack import SnsSlackTeamsIntegrationCdkPythonStack + + +app = cdk.App() +SnsSlackTeamsIntegrationCdkPythonStack(app, "SnsSlackTeamsIntegrationCdkPythonStack", + # If you don't specify 'env', this stack will be environment-agnostic. + # Account/Region-dependent features and context lookups will not work, + # but a single synthesized template can be deployed anywhere. + + # Uncomment the next line to specialize this stack for the AWS Account + # and Region that are implied by the current CLI configuration. + + #env=cdk.Environment(account=os.getenv('CDK_DEFAULT_ACCOUNT'), region=os.getenv('CDK_DEFAULT_REGION')), + + # Uncomment the next line if you know exactly what Account and Region you + # want to deploy the stack to. */ + + #env=cdk.Environment(account='123456789012', region='us-east-1'), + + # For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html + ) + +app.synth() diff --git a/sns-slack-teams-integration-cdk-python/cdk.json b/sns-slack-teams-integration-cdk-python/cdk.json new file mode 100644 index 000000000..b5a313eaa --- /dev/null +++ b/sns-slack-teams-integration-cdk-python/cdk.json @@ -0,0 +1,59 @@ +{ + "app": "python3 app.py", + "watch": { + "include": [ + "**" + ], + "exclude": [ + "README.md", + "cdk*.json", + "requirements*.txt", + "source.bat", + "**/__init__.py", + "python/__pycache__", + "tests" + ] + }, + "context": { + "@aws-cdk/aws-lambda:recognizeLayerVersion": true, + "@aws-cdk/core:checkSecretUsage": true, + "@aws-cdk/core:target-partitions": [ + "aws", + "aws-cn" + ], + "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, + "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, + "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, + "@aws-cdk/aws-iam:minimizePolicies": true, + "@aws-cdk/core:validateSnapshotRemovalPolicy": true, + "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, + "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, + "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, + "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, + "@aws-cdk/core:enablePartitionLiterals": true, + "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, + "@aws-cdk/aws-iam:standardizedServicePrincipals": true, + "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, + "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, + "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true, + "@aws-cdk/aws-route53-patters:useCertificate": true, + "@aws-cdk/customresources:installLatestAwsSdkDefault": false, + "@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true, + "@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true, + "@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true, + "@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true, + "@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true, + "@aws-cdk/aws-redshift:columnId": true, + "@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true, + "@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true, + "@aws-cdk/aws-apigateway:requestValidatorUniqueId": true, + "@aws-cdk/aws-kms:aliasNameRef": true, + "@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true, + "@aws-cdk/core:includePrefixInUniqueNameGeneration": true, + "@aws-cdk/aws-efs:denyAnonymousAccess": true, + "@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true, + "@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true, + "@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true + } + } + \ No newline at end of file diff --git a/sns-slack-teams-integration-cdk-python/example-pattern.json b/sns-slack-teams-integration-cdk-python/example-pattern.json new file mode 100644 index 000000000..28da9d508 --- /dev/null +++ b/sns-slack-teams-integration-cdk-python/example-pattern.json @@ -0,0 +1,69 @@ +{ + "title": "Amazon SNS integration with Slack/Teams channels", + "description": "This sample project demonstrates the publishing of messages from an SNS topic to an AWS Lambda function, which in turn triggers notifications to a Slack/Teams channel.", + "language": "Python", + "level": "200", + "framework": "CDK", + "introBox": { + "headline": "How it works", + "text": [ + "This sample pattern demonstrates how to use AWS SNS and Lambda function to deliver notifications to your slack or teams channels.", + "This pattern uses AWS CDK to setup the AWS serverless services and provides you with a setup ready to be integrated with your Slack/Teams channel", + "This pattern deploys one SNS topic and one Lambda function" + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/sns-slack-teams-integration-cdk-python", + "templateURL": "serverless-patterns/sns-slack-teams-integration-cdk-python", + "projectFolder": "sns-slack-teams-integration-cdk-python", + "templateFile": "sns_slack_teams_integration_cdk_python/sns_slack_teams_integration_cdk_python_stack.py" + } + }, + "resources": { + "bullets": [ + { + "text": "Getting started with the AWS CDK", + "link": "https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html" + }, + { + "text": "Setup SNS topic", + "link": "https://docs.aws.amazon.com/sns/latest/dg/sns-create-topic.html" + }, + { + "text": "Create Lambda function", + "link": "https://docs.aws.amazon.com/lambda/latest/dg/getting-started.html" + },{ + "text": "Add lambda function as subscriber to a SNS topic", + "link": "https://docs.aws.amazon.com/sns/latest/dg/lambda-console.html" + }, + { + "text": "Add your slack/MS teams and channel configuration to the Lambda function", + "link": "https://repost.aws/knowledge-center/sns-lambda-webhooks-chime-slack-teams" + } + ] + }, + "deploy": { + "text": [ + "cdk deploy" + ] + }, + "testing": { + "text": [ + "See the GitHub repo for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "cdk destroy" + ] + }, + "authors": [ + { + "name": "Rick Xie", + "image": "https://drive.google.com/file/d/1GEM7I1PK3i0HT3pMMoI7d5zFXw4wAEFL/view?usp=sharing", + "bio": "Cloud Support Engineer @ AWS", + "linkedin": "https://www.linkedin.com/in/rick-xie" + } + ] +} \ No newline at end of file diff --git a/sns-slack-teams-integration-cdk-python/lambda/lambda_function.py b/sns-slack-teams-integration-cdk-python/lambda/lambda_function.py new file mode 100644 index 000000000..d5744287d --- /dev/null +++ b/sns-slack-teams-integration-cdk-python/lambda/lambda_function.py @@ -0,0 +1,26 @@ +import urllib3 +import json + +http = urllib3.PoolManager() + +def lambda_handler(event, context): + + #when using slack + url = 'https://hooks.slack.com/services/xxxxxxx' + msg = { + 'channel': '#CHANNEL_NAME', + 'text': event['Records'][0]['Sns']['Message'], + 'icon_emoji': '', + } + + #when using teams + #url = "https://outlook.office.com/webhook/xxxxxxx" + #msg = {"text": event["Records"][0]["Sns"]["Message"]} + + 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, + }) \ No newline at end of file diff --git a/sns-slack-teams-integration-cdk-python/requirements.txt b/sns-slack-teams-integration-cdk-python/requirements.txt new file mode 100644 index 000000000..0e96b367a --- /dev/null +++ b/sns-slack-teams-integration-cdk-python/requirements.txt @@ -0,0 +1,2 @@ +aws-cdk-lib==2.93.0 +constructs>=10.0.0,<11.0.0 \ No newline at end of file diff --git a/sns-slack-teams-integration-cdk-python/sns_slack_teams_integration_cdk_python/__init__.py b/sns-slack-teams-integration-cdk-python/sns_slack_teams_integration_cdk_python/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/sns-slack-teams-integration-cdk-python/sns_slack_teams_integration_cdk_python/sns_slack_teams_integration_cdk_python_stack.py b/sns-slack-teams-integration-cdk-python/sns_slack_teams_integration_cdk_python/sns_slack_teams_integration_cdk_python_stack.py new file mode 100644 index 000000000..a62157a5c --- /dev/null +++ b/sns-slack-teams-integration-cdk-python/sns_slack_teams_integration_cdk_python/sns_slack_teams_integration_cdk_python_stack.py @@ -0,0 +1,35 @@ +from aws_cdk import ( + Duration, + Stack, + CfnOutput, + aws_sns as sns, + aws_sns_subscriptions as subscriptions, + aws_lambda as _lambda +) +from constructs import Construct + +class SnsSlackTeamsIntegrationCdkPythonStack(Stack): + + def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None: + super().__init__(scope, construct_id, **kwargs) + + # Lambda function + lambdaFn = _lambda.Function(self, "SlackTeamsIntegrationLambda", + runtime=_lambda.Runtime.PYTHON_3_8, + code=_lambda.Code.from_asset('lambda'), + handler="lambda_function.lambda_handler", + timeout=Duration.seconds(10)) + + # SNS topic + topic = sns.Topic(self, "SlackTeamsIntegrationTopic", + display_name="SlackTeamsIntegrationTopic") + + # Subscribe Lambda to SNS topic + topic.add_subscription(subscriptions.LambdaSubscription(lambdaFn)) + + # Output information about the created resources + CfnOutput(self, 'snsTopicArn', + value=topic.topic_arn, + description='The ARN of the SNS topic') + + CfnOutput(self, 'functionName', value=lambdaFn.function_name, description="The name of the Lambda function") diff --git a/sns-slack-teams-integration-cdk-python/source.bat b/sns-slack-teams-integration-cdk-python/source.bat new file mode 100644 index 000000000..9e1a83442 --- /dev/null +++ b/sns-slack-teams-integration-cdk-python/source.bat @@ -0,0 +1,13 @@ +@echo off + +rem The sole purpose of this script is to make the command +rem +rem source .venv/bin/activate +rem +rem (which activates a Python virtualenv on Linux or Mac OS X) work on Windows. +rem On Windows, this command just runs this batch file (the argument is ignored). +rem +rem Now we don't need to document a Windows command for activating a virtualenv. + +echo Executing .venv\Scripts\activate.bat for you +.venv\Scripts\activate.bat