ChatOps with Mattermost and AWS Lambda
I’ve been working towards making things simpler when managing distributed resources at work. And since we spend most of our day in the chat room (was Slack, now Mattermost) I thought it’s best to get started with ChatOps
It’s just a fancy word for doing stuff right from the chat window. And there’s so much one can do, especially with simple Slash Commands.
Here’s a lambda function I setup yesterday for invalidating CloudFront distributions.
from time import time
import boto3
import json
import os
import re
EXPECTED_TOKEN = os.environ['mmToken']
ALLOWED_USERS = re.split('[, ]', os.environ['allowedUsers'])
DISTRIBUTIONS = {
'site-name': 'DISTRIBUTIONID',
'another.site': 'DISTRIBUTIONID05'
}
def parse_command_text(command_text):
pattern = r"({})\s+(.*)".format('|'.join(DISTRIBUTIONS.keys()))
m = re.match(pattern, command_text)
if m:
return { 'site': m.group(1), 'path': path}
else:
return False
def lambda_handler(event, context):
# Parse the request
try:
request_data = event["queryStringParameters"]
except:
return {
"statusCode": 400,
"headers": {"Content-Type": "application/json"},
"body": '{ "message": "Use GET for setting up mattermost slash command" }'
}
# Check the token matches.
if request_data.get("token", "") != EXPECTED_TOKEN:
print('Wrong Token!')
return {
"statusCode": 401,
"headers": {"Content-Type": "application/json"},
"body": '{ "message": "Mattermost token does not match" }'
}
# Check the user is allowed to run the command
if request_data.get("user_name", "") not in ALLOWED_USERS:
print('Wrong User! {} not in {}'.format(request_data['user_name'], ALLOWED_USERS))
return {
"statusCode": 401,
"headers": {"Content-Type": "application/json"},
"body": '{ "message": "User not allowed to perform action" }'
}
# parse the command
command_text = request_data.get("text", "")
if not command_text:
print('Nothing to do, bailing out')
return {
"statusCode": 404,
"headers": {"Content-Type": "application/json"},
"body": '{ "message": "No command text sent" }'
}
parts = parse_command_text(command_text)
if not parts:
print('Bad formatting - command: {}'.format(command_text))
return {
"statusCode": 402,
"headers": {"Content-Type": "application/json"},
"body": '{ "message": "Wrong pattern" }'
}
# Do the actual work
cf_client = boto3.client('cloudfront')
# Invalidate
boto_response = cf_client.create_invalidation(
DistributionId=DISTRIBUTIONS[parts['site']],
InvalidationBatch={
'Paths': {
'Quantity': len(parts['path']),
'Items': parts['path']
},
'CallerReference': str(time()).replace(".", "")
}
)['Invalidation']
# Build the response message text.
text = """##### Executing invalidation
| Key | Info |
| --- | ---- |
| Site | {} |
| Path | {} |
| ID | {} |
| Status | {} |""".format(
parts['site'],
parts['path'],
boto_response['Id'],
boto_response['Status']
)
# Build the response object.
response = {
"response_type": "in_channel",
"text": text,
}
# Return the response as JSON
return {
"body": json.dumps(response),
"headers": {"Content-Type": "application/json"},
"statusCode": 200,
}
Note that you need to hook that up with an API Gateway in AWS. Once that’s done, you will have a URL endpoint ready for deployment.
Next, I created the slash command in mattermost with the following:

slash command configuration
That’s pretty much it. Rinse and repeat for a different command, different usage.
On my list next is to have more interaction with the user in mattermost per https://docs.mattermost.com/developer/interactive-messages.html
Weekend Project, Yay!