Infrastructure
Transport
| Aspect | Slack | Teams |
|---|---|---|
| Primary transport | Socket Mode (WebSocket) or HTTP | HTTPS only (inbound webhook) |
| Firewall-friendly | Socket Mode — outbound WebSocket, no inbound ports | Requires public HTTPS endpoint |
| Default endpoint | /slack/events | /api/messages |
| Local development | Socket Mode (no tunnel needed) | Dev Tunnels or ngrok required |
| Request verification | HMAC-SHA256 (signingSecret) | Bot Framework JWT (automatic) |
Rating: GREEN for HTTP-to-HTTPS, RED for Socket Mode → HTTPS (firewall environments).
Impact
Slack bots using Socket Mode run behind firewalls with zero inbound ports. Teams requires a public HTTPS endpoint — a fundamental architecture change for firewall-restricted environments.
Mitigation (Slack Socket Mode → Teams)
| Strategy | How | Effort |
|---|---|---|
| Deploy to Azure (Recommended) | Host in Azure App Service / Functions / Container Apps. Use Dev Tunnels for local dev. | 4–8 hrs |
| Azure Relay | Hybrid connection for strict on-premises firewalls. Adds latency. | 8–16 hrs |
Dual-Bot Transport
For bots targeting both platforms simultaneously:
| Pattern | How |
|---|---|
| Socket Mode + HTTP (Recommended) | Slack uses WebSocket (no HTTP needed), Teams uses Express on port 3978. No port conflicts. Simplest setup. |
| Shared Express | Both use HTTP. Slack ExpressReceiver at /slack/events, Teams adapter at /api/messages. Requires careful body-parsing middleware ordering. |
Compute (AWS ↔ Azure)
| AWS | Azure | Notes |
|---|---|---|
| Lambda + API Gateway | Azure Functions | Teams bots need 3-second response; Functions Consumption has 5–10s cold starts |
| ECS / Fargate | Container Apps | Best for long-running bots with streaming |
| EC2 | App Service | Always-on, predictable latency |
Cold Start Warning
Azure Functions Consumption plan has 5–10 second cold starts that violate the Teams 3-second response timeout. Mitigations:
| Strategy | Cost Impact |
|---|---|
| App Service with Always On (Recommended) | Fixed cost but no cold starts |
| Functions Premium with Always Ready | Higher cost, eliminates cold starts |
| Container Apps (min replicas ≥ 1) | Moderate cost, no scale-to-zero |
Storage (AWS ↔ Azure)
| AWS | Azure | Notes |
|---|---|---|
| S3 | Blob Storage | Hot/Cool/Archive tiers |
| DynamoDB | Cosmos DB | Table API (lowest effort) or Core SQL (richer querying) |
| RDS (MySQL) | Azure Database for MySQL | Managed migration service available |
| RDS (PostgreSQL) | Azure Database for PostgreSQL | Managed migration service available |
| RDS (SQL Server) | Azure SQL | Direct migration path |
Bot State Storage
| Aspect | Slack | Teams |
|---|---|---|
| SDK storage | No built-in state management | IStorage interface with pluggable backends |
| Default | Developer manages state | In-memory (lost on restart) |
| Production | External DB (Redis, PostgreSQL, etc.) | Cosmos DB, Azure SQL, or custom IStorage |
Mitigation: Implement the Teams IStorage interface with Cosmos DB for bot state. Use serverless pricing for development, provisioned RUs for production.
Secrets & Configuration
| AWS | Azure | Notes |
|---|---|---|
| Secrets Manager | Key Vault | Sensitive credentials |
| SSM Parameter Store | App Configuration | Non-secret configuration |
| IAM roles | Managed Identity | Zero-secret authentication |
| Environment variables | App Settings | Runtime configuration |
Bot Credentials
| Credential | Slack | Teams |
|---|---|---|
| Bot token | SLACK_BOT_TOKEN | Managed by SDK (CLIENT_ID + CLIENT_SECRET) |
| Signing/verification | SLACK_SIGNING_SECRET | Automatic JWT validation |
| Socket Mode | SLACK_APP_TOKEN | N/A |
| Tenant | N/A | TENANT_ID |
Production Secret Management
| Strategy | How |
|---|---|
| Key Vault references (Recommended) | @Microsoft.KeyVault(SecretUri=...) in App Settings. Zero-code secret injection. Requires managed identity. |
| Managed identity for bot auth | managedIdentityClientId: "system" in App constructor. Eliminates CLIENT_SECRET entirely. |
DefaultAzureCredential | Chains managed identity → environment → CLI → VS Code. Works everywhere. |
Observability (AWS ↔ Azure)
| AWS | Azure | Notes |
|---|---|---|
| CloudWatch Logs | Application Insights + Log Analytics | KQL query language (different from CloudWatch Insights) |
| CloudWatch Metrics | Azure Monitor Metrics | trackMetric() |
| CloudWatch Alarms | Azure Monitor Alerts | KQL-based alerting |
| X-Ray | Application Insights distributed tracing | Operation IDs, traceparent headers |
Bot Health Monitoring
Key metrics to track for both platforms:
| Metric | Why |
|---|---|
| Request rate | Volume baseline |
| Response time (P50/P95/P99) | Detect slowdowns before they cause timeouts |
| Failure rate | Catch errors before users report them |
| Active conversations | Usage trends |
| AI/external API latency | Dependency health |
Setup
// Application Insights — must be first import
import appInsights from "applicationinsights";
appInsights.setup(process.env.APPLICATIONINSIGHTS_CONNECTION_STRING).start();
// Then import everything else
import { App } from "@microsoft/teams.apps";Pitfall: Late instrumentation import. applicationinsights must run before http/https are loaded or distributed tracing won't work.
Rate Limiting & Resilience
| Aspect | Slack | Teams |
|---|---|---|
| Rate limit signal | HTTP 429 + Retry-After header | HTTP 429 + Retry-After header |
| Built-in retry | Bolt retryConfig option | No built-in retry |
| Conversation limits | ~1 msg/sec per method per token | ~1 msg/sec per conversation, ~30 msg/min per conversation |
| Graph API limits | N/A | Separate throttling (per-app per-tenant) |
| Invoke timeout | N/A | 3–10 seconds (varies by invoke type) |
Rating: GREEN for basic rate limiting, YELLOW for resilience patterns.
Mitigation (Slack → Teams)
Build a RetryPlugin with exponential backoff + jitter:
| Component | Purpose |
|---|---|
| Exponential backoff | Wait 1s, 2s, 4s, 8s between retries |
| Jitter | Add random delay to prevent thundering herd |
| Circuit breaker | Stop retrying after N consecutive failures |
p-queue | Concurrency control for proactive broadcast (avoid bursting) |
Effort: 12–16 hrs for a production-grade retry plugin.
Reverse Direction (Teams → Slack)
Use Bolt's built-in retryConfig option:
const app = new App({
token: process.env.SLACK_BOT_TOKEN!,
signingSecret: process.env.SLACK_SIGNING_SECRET!,
// Built-in retry with exponential backoff
});