Teams integration with AWS lambda + Postgres & Tulip

From Teams integration with AWS lambda + Postgres & Tulip – Michael Ellerbeck

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

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": "","team_test2": ""}

Let’s call the paramater


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()


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" : "" ,

"@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:


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 My new favorite way to trigger lambdas from Tulip – Michael Ellerbeck

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 $


rows_affected int ;

create_json TEXT;

response TEXT;

err_context text;


RAISE INFO 'Started' ;

-- Create Json



create_json row_to_json(t)








) 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;


return 0;


end ;

$ function $


Call it like this

1 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

1 Like