In fast-moving DevOps and FinOps environments, it’s easy to lose track of stopped or non-compliant cloud resources. While Lambda or cron jobs can detect these resources, what matters just as much is where those results go. I don’t want alerts buried in email or tucked away in an S3 bucket—I want actionable messages delivered straight to my team’s Microsoft Teams channel.
This post focuses specifically on using Python to craft and send structured messages to Microsoft Teams using an Incoming Webhook.
Why Use Microsoft Teams Webhooks?
When integrated properly, Microsoft Teams becomes a lightweight dashboard for DevOps alerts. By posting messages through Teams webhooks, I can:
-
Provide quick visibility into weekly scans
-
Include links to S3-hosted reports
-
Highlight potential cost savings
-
Enable real-time discussion before any action is taken
Constructing the Message Card in Python
To get Teams to accept our message, we need to follow the schema for MessageCards. The Python script constructs the card as a dictionary and encodes it as JSON.
card = {
"@type": "MessageCard",
"@context": "http://schema.org/extensions",
"summary": "AWS Resource Compliance Report",
"sections": [{
"activityTitle": "⚠️ AWS Resource Compliance Notification",
"activitySubtitle": "Resources requiring attention",
"text": "This report lists AWS resources that require review for potential compliance issues.",
"facts": [
{"name": "Account", "value": "AWS_ACCOUNT_ID"},
{"name": "Total Instances", "value": "NUMBER_OF_INSTANCES"},
{"name": "Estimated Monthly Savings", "value": "$MONTHLY_SAVINGS"},
{"name": "Report Location", "value": "S3_REPORT_PATH"}
],
"markdown": True
}]
}
Let’s break down the key components of this card:
@type
and@context
: These tell Microsoft Teams what kind of message this is and how to interpret it using the MessageCard schema.summary
: A short summary string used in notifications and previews.sections
: A list of structured data blocks. Each section supports text, images, facts, and headers.activityTitle
: Bold header that grabs the user’s attention (like a subject line).activitySubtitle
: Smaller subheading for additional context.text
: A brief description of the message, giving users clarity on what they’re looking at.facts
: A list of key-value pairs presented in a tabular format. These are ideal for showing metadata like the AWS account number, instance count, estimated costs, or links to more detailed reports.markdown
: A boolean flag that allows for bold, italic, and other rich-text elements inside thetext
andfacts
fields.
This formatting makes it easy for Teams users to quickly interpret what’s going on and where to go next.
Sending the Message
After formatting the card, we send it to Teams using a simple HTTP POST to the webhook URL.
import urllib3
import json
http = urllib3.PoolManager()
response = http.request(
"POST",
TEAMS_WEBHOOK_URL,
body=json.dumps(card).encode("utf-8"),
headers={"Content-Type": "application/json"}
)
if response.status == 200:
print("Teams notification sent successfully.")
else:
print(f"Failed to send Teams notification. Status: {response.status}")
First, a connection pool is created using
urllib3.PoolManager()
. This is essential for managing and reusing HTTP connections efficiently.Then, the
request()
method sends a POST request to the Teams webhook URL. The request includes:The HTTP method (
POST
), as Teams expects messages to be submitted this wayThe
TEAMS_WEBHOOK_URL
, which is the destination for our messageA request body containing our message card, JSON-encoded and UTF-8 encoded
HTTP headers declaring the payload as
application/json
After making the request, the script evaluates the response code. If it's 200 OK
, the message was successfully delivered to Teams. Otherwise, an error message is logged with the returned HTTP status.
This minimal setup works seamlessly in serverless environments like AWS Lambda and avoids the need for heavier dependencies like requests
, making it ideal for quick, automated reporting.
Getting Your Microsoft Teams Webhook URL
Before you can post messages to Teams, you need to set up an Incoming Webhook in your desired channel:
- In Microsoft Teams, navigate to the channel where you want the messages to appear.
- Click the ellipsis (⋯) next to the channel name, then select Connectors.
- Search for Incoming Webhook, then click Configure.
- Name your webhook (e.g.,
AWS Resource Alerts
) and optionally upload an icon.
- Click Create, and Teams will generate a Webhook URL.
-
Copy that URL—you’ll need to paste it into your Python script.
Now, in your Python script, you can define it like this:
TEAMS_WEBHOOK_URL = "https://your.webhook.office.com/..."
Be sure to store your webhook URL securely. For production environments, consider injecting it as an environment variable or retrieving it securely through a tool like AWS Secrets Manager.
In my setup, this script runs every Thursday. It collects the week’s findings, uploads a CSV report to S3, and posts a summary to our Microsoft Teams channel. This gives everyone a chance to review the results and provide feedback before any cleanup tasks are scheduled.
The purpose of automation isn’t just speed—it’s alignment. By connecting Python scripts to Teams, we’re not just executing processes automatically, we’re keeping the right people informed. If you’re running regular audits or cost-optimization checks, make the results visible in the tools your team already uses. It’s a simple way to drive transparency and collaboration.