Slack Week 1: Basics + Webhooks
Goal
Master Slack Incoming Webhooks and basic message formatting for CI/CD notifications.
Topics
Day 1-2: Slack Workspace & Webhooks Setup
What you'll learn: - Slack workspace structure (channels, users, apps) - Creating a Slack App - Incoming Webhooks basics - Webhook URL security
Hands-on:
# 1. Create Slack App
https://api.slack.com/apps → "Create New App"
App Name: Platform Notifications
Workspace: Your workspace
# 2. Enable Incoming Webhooks
Features → Incoming Webhooks → Toggle ON
# 3. Add Webhook to Workspace
"Add New Webhook to Workspace" → Select channel
# 4. Save webhook URL (securely!)
export SLACK_WEBHOOK_URL="https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXX"
Test your webhook:
curl -X POST -H 'Content-type: application/json' \
--data '{"text":"Hello from Platform!"}' \
$SLACK_WEBHOOK_URL
Key concepts: - Webhook URL: Unique URL for posting to a specific channel - Incoming Webhooks: One-way, simple, no OAuth needed - Security: Treat webhook URLs like passwords!
Day 3-4: Message Formatting with Block Kit
What you'll learn: - Basic vs rich message formatting - Block Kit structure - Common block types (section, divider, actions) - Interactive Block Kit Builder
Simple message (plain text):
curl -X POST -H 'Content-type: application/json' \
--data '{
"text": "Pipeline failed: build-service"
}' \
$SLACK_WEBHOOK_URL
Rich message (Block Kit):
curl -X POST -H 'Content-type: application/json' \
--data '{
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": " Pipeline Failed",
"emoji": true
}
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*Pipeline:*\nbuild-service-123"
},
{
"type": "mrkdwn",
"text": "*Status:*\nFailed"
},
{
"type": "mrkdwn",
"text": "*Duration:*\n5m 32s"
},
{
"type": "mrkdwn",
"text": "*Author:*\ndemo_user"
}
]
},
{
"type": "divider"
},
{
"type": "context",
"elements": [
{
"type": "mrkdwn",
"text": "Commit: <https://github.com/platform/repo/commit/abc123|abc123> • <https://console.company.com/logs|View Logs>"
}
]
}
]
}' \
$SLACK_WEBHOOK_URL
Block Kit Builder:
Use this interactive tool to design messages:
https://app.slack.com/block-kit-builder/
Copy/paste the JSON output to your webhook call!
Common block types:
| Block Type | Purpose | Example |
|---|---|---|
| header | Bold title | Pipeline Failed |
| section | Text + fields | Status, Duration, Author |
| divider | Horizontal line | --- |
| context | Small text/links | Timestamps, metadata |
| actions | Buttons (requires bot) | [Rerun] [View Logs] |
Day 5-6: Channel Routing & Message Threading
What you'll learn: - Channel naming conventions - Routing messages to different channels - Using thread_ts for context - Ephemeral messages (requires bot)
Channel routing strategy:
# Different webhooks for different channels
export SLACK_WEBHOOK_CRITICAL="https://hooks.slack.com/services/.../critical-alerts"
export SLACK_WEBHOOK_INFO="https://hooks.slack.com/services/.../build-notifications"
export SLACK_WEBHOOK_DEPLOYS="https://hooks.slack.com/services/.../deployments"
# Route based on severity
if [ "$SEVERITY" = "critical" ]; then
WEBHOOK=$SLACK_WEBHOOK_CRITICAL
else
WEBHOOK=$SLACK_WEBHOOK_INFO
fi
Channel naming conventions:
#platform-ci-cd - General CI/CD updates
#platform-alerts-critical - P0/P1 alerts only
#platform-builds - Build pipeline status
#platform-deploys - Deployment notifications
#platform-incidents - Incident management
Message threading (note: requires Slack API, not webhooks):
Day 7: Slack Best Practices & Anti-Patterns
What you'll learn: - When to use webhooks vs bots - Avoiding alert fatigue - Message design principles - Security best practices
** DO:** - Use threads for related updates (requires bot, Week 2) - Include actionable links (logs, dashboards) - Use colors/emojis sparingly but effectively - Aggregate similar alerts - Different channels for different severities
** DON'T:** - Post every single event (too noisy!) - Use @channel / @here for non-critical alerts - Commit webhook URLs to git - Send sensitive data (passwords, tokens) - Use webhooks for two-way interactions (use bot instead)
Alert fatigue prevention:
Bad:
Pipeline succeeded: build-1
Pipeline succeeded: build-2
Pipeline succeeded: build-3
(Nobody reads these...)
Good:
Hourly Summary: 15/16 pipelines succeeded
1 failure: image-build-service (link)
Security checklist: - [ ] Webhook URLs stored in secrets/env vars - [ ] No sensitive data in messages - [ ] Rotate webhook URLs periodically - [ ] Use HTTPS only (always true for Slack) - [ ] Limit webhook access to specific channels
Exercises
Exercise 1: Create Your First Webhook
# 1. Create a test channel
Create: #test-webhooks
# 2. Set up incoming webhook
App → Incoming Webhooks → Add to #test-webhooks
# 3. Send test message
export SLACK_WEBHOOK_URL="your-url-here"
curl -X POST -H 'Content-type: application/json' \
--data '{"text":"My first webhook! "}' \
$SLACK_WEBHOOK_URL
# Expected: Message appears in #test-webhooks
Exercise 2: Build a Pipeline Failure Alert
Use Block Kit Builder to create:
Header: Pipeline Failed
Fields:
- Pipeline name
- Commit SHA
- Author
- Duration
Link: View Logs (to a fake URL)
Test it with curl:
# Copy JSON from Block Kit Builder
curl -X POST -H 'Content-type: application/json' \
--data '@pipeline-failure.json' \
$SLACK_WEBHOOK_URL
Exercise 3: Python Webhook Helper
Create a simple Python script to send formatted messages:
#!/usr/bin/env python3
import requests
import json
import os
SLACK_WEBHOOK = os.environ.get('SLACK_WEBHOOK_URL')
def send_pipeline_alert(pipeline_name, status, duration, author, commit_url):
emoji = "" if status == "success" else ""
color = "good" if status == "success" else "danger"
message = {
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": f"{emoji} Pipeline {status.title()}"
}
},
{
"type": "section",
"fields": [
{"type": "mrkdwn", "text": f"*Pipeline:*\n{pipeline_name}"},
{"type": "mrkdwn", "text": f"*Status:*\n{status}"},
{"type": "mrkdwn", "text": f"*Duration:*\n{duration}"},
{"type": "mrkdwn", "text": f"*Author:*\n{author}"}
]
},
{
"type": "context",
"elements": [
{
"type": "mrkdwn",
"text": f"<{commit_url}|View Commit>"
}
]
}
]
}
response = requests.post(SLACK_WEBHOOK, json=message)
return response.status_code == 200
# Test
if __name__ == "__main__":
send_pipeline_alert(
pipeline_name="build-service-123",
status="failed",
duration="5m 32s",
author="demo_user",
commit_url="https://github.com/platform/repo/commit/abc123"
)
Save as: slack_notifier.py
Test:
Exercise 4: Tekton Pipeline Integration (Basic)
Create a Tekton Task that sends Slack notifications:
apiVersion: tekton.dev/v1
kind: Task
metadata:
name: slack-notify
spec:
params:
- name: message
type: string
description: Message to send
- name: webhook-url
type: string
description: Slack webhook URL
steps:
- name: send-notification
image: curlimages/curl:latest
script: |
#!/bin/sh
curl -X POST -H 'Content-type: application/json' \
--data '{"text":"$(params.message)"}' \
$(params.webhook-url)
Use in a pipeline:
apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
name: build-with-slack
spec:
params:
- name: slack-webhook
tasks:
- name: build
taskRef:
name: buildah
# ... build params
- name: notify-success
runAfter:
- build
taskRef:
name: slack-notify
params:
- name: message
value: " Build succeeded!"
- name: webhook-url
value: $(params.slack-webhook)
Week 1 Checklist
- Created a Slack App
- Set up Incoming Webhook
- Sent test message with curl
- Built message with Block Kit Builder
- Created Python helper script
- Integrated webhook with Tekton (optional)
- Understand webhook security best practices
Resources
- Incoming Webhooks: https://api.slack.com/messaging/webhooks
- Block Kit Builder: https://app.slack.com/block-kit-builder/
- Block Kit Docs: https://api.slack.com/block-kit
- Message Formatting: https://api.slack.com/reference/surfaces/formatting
Real-World Example
Here's a complete example for Platform pipeline notifications:
#!/usr/bin/env python3
"""
Platform Pipeline → Slack Notifier
Trigger this from Tekton Pipeline on completion
"""
import requests
import os
import sys
WEBHOOK_URL = os.environ.get('SLACK_WEBHOOK_URL')
def notify_pipeline_result(pipeline_run_name, status, namespace, logs_url):
"""Send pipeline result to Slack"""
# Emoji and color based on status
if status == "Succeeded":
emoji = ""
status_text = "succeeded"
elif status == "Failed":
emoji = ""
status_text = "failed"
else:
emoji = "⏳"
status_text = status.lower()
# Build message
message = {
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": f"{emoji} Pipeline {status_text.title()}",
"emoji": True
}
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": f"*Pipeline:*\n{pipeline_run_name}"
},
{
"type": "mrkdwn",
"text": f"*Namespace:*\n{namespace}"
}
]
},
{
"type": "divider"
},
{
"type": "context",
"elements": [
{
"type": "mrkdwn",
"text": f"<{logs_url}|View Logs in Console>"
}
]
}
]
}
# Send to Slack
response = requests.post(WEBHOOK_URL, json=message)
if response.status_code != 200:
print(f"Failed to send Slack notification: {response.status_code}")
sys.exit(1)
print(f"Slack notification sent successfully!")
if __name__ == "__main__":
# Example usage (get these from Tekton params)
notify_pipeline_result(
pipeline_run_name=os.environ.get('PIPELINE_RUN_NAME', 'test-pipeline-run'),
status=os.environ.get('PIPELINE_STATUS', 'Succeeded'),
namespace=os.environ.get('NAMESPACE', 'platform-ci'),
logs_url=os.environ.get('LOGS_URL', 'https://console.company.com/...')
)