Teams integration with AWS lambda + Postgres & Tulip

Microsoft teams has a pretty nifty way of opening access to posting messages. This can be configured like this

https://towardsdatascience.com/automate-teams-with-your-first-python-alerting-bot-bcc8f7c6ee5a

After generating a webhook url, let’s store it in SSM since it is credentials for writing to a channel. For this first go ahead I’m going to store it as json with multiple key pairs, it might be better to store them separately, but this will work for now.

The general look is

{
"team_test1": "https://sample.webhook.office.com/webhookb2/blahblah",
"team_test2": "https://sample.webhook.office.com/webhookb2/blahblah"
}

Let’s call the paramater

/dev/teamsintegration/channels

Next, creating the lambda

So it grabs the possible channels from SSM and loads into key pairs for use later (the channel_name paramater)

import sys
import logging
import os
import json
import urllib3
import boto3

logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
runtime_region = os.environ['AWS_REGION']

http = urllib3.PoolManager()
session = boto3.Session(region_name=runtime_region)
ssm = session.client('ssm')
channels = ssm.get_parameter(Name='/dev/teamsintegration/channels', WithDecryption=True)['Parameter']['Value']
channels = json.loads(channels)

def send_teams(webhook_url: str, content: str, title: str, color: str="000000") -> int:

    msg = {
            "@context": "https://schema.org/extensions",
            "@type": "MessageCard",
            "themeColor": color,
            "title": title,
            "text": content
    }
    encoded_msg = json.dumps(msg).encode('utf-8')
    http.request('POST', webhook_url, body=encoded_msg)
    
def lambda_handler(event, context):
    content = event["content"]
    title = event["title"]
    color = event["color"]
    channel_name = event["channel_name"]
    
    webhook_url = channels[channel_name]

    try:
        print(event)
        send_teams(webhook_url, content, title,color)
        
        return {
        'statusCode': 200,
        'body': json.dumps(title)
        }
        
    except Exception as error:
        raise

In order for the lambda to access the SSM parameter create a quick inline policy

{
    "Statement": [
        {
            "Action": "ssm:DescribeParameters",
            "Effect": "Allow",
            "Resource": "*",
            "Sid": "VisualEditor0"
        },
        {
            "Action": "ssm:GetParameter",
            "Effect": "Allow",
            "Resource": "arn:aws:ssm:us-east-1:accountid:parameter/dev/teamsintegration/channels",
            "Sid": "VisualEditor1"
        }
    ],
    "Version": "2012-10-17"
}

Ok, lets test this function out. Configure a test event like this

{
  "content": "some content",
  "title": "a title that is really cool",
  "color": "ff0400",
  "channel_name1": "team_test1"
}

Assuming that all worked you should get

To use this from Tulip my favorite method is https://michaelellerbeck.com/2021/07/06/my-new-favorite-way-to-trigger-lambdas-from-tulip/

Grant your rds-lambda-role permission to execute the lambda.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": "lambda:InvokeFunction",
            "Effect": "Allow",
            "Resource": "arn:aws:lambda:us-east-1:943667677541:function:your-function-name-here",
            "Sid": "VisualEditor2"
        }
    ]
}

Wrap it in a postgres function

CREATE OR REPLACE FUNCTION public.send_teams_msg(content text, title text, color text, channel_name text)
 RETURNS integer
 LANGUAGE plpgsql
AS $function$
declare

  rows_affected int;
  create_json TEXT;
  response TEXT;
  err_context text;
 
  
begin
RAISE INFO 'Started';	  
  
-- Create Json	  
select
	into
	create_json row_to_json(t)
from
	(
	select
	   content,
	   title,
	   color,
	   channel_name
	   ) t;
	  
SELECT into response payload FROM aws_lambda.invoke(aws_commons.create_lambda_function_arn('your-function-name-here', 'us-east-1'),
create_json::json,'RequestResponse'
);

RAISE INFO 'response:%',response;

-- parse the response
select into response response::json->'response';
-- remove qutoes
select into response replace(response,'"','');

if response = 'sent' then 
	return 1;
else
	return 0;
END IF;

end;
$function$
;

Call it like this

select public.send_teams_msg('some content','the title', 'ff0400','team_test1')

Then create a connector function

Then of course you can use it in an app like this

Of course, no solution is really complete until automated so let’s Terraform this up.

So this is a somewhat incomplete .tf

https://github.com/mellerbeck/tulip-send-teams-msg/blob/main/tulip_send_teams_msg.tf

Leave a comment