{"id":51264509,"url":"https://github.com/jsabo/teleport-msteams-webhook","last_synced_at":"2026-06-29T14:32:27.263Z","repository":{"id":360816693,"uuid":"1251835842","full_name":"jsabo/teleport-msteams-webhook","owner":"jsabo","description":"Teleport access request notifications for Microsoft Teams via incoming webhooks","archived":false,"fork":false,"pushed_at":"2026-05-28T02:02:11.000Z","size":98,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-28T02:22:14.929Z","etag":null,"topics":["access-requests","adaptive-cards","golang","microsoft-teams","msteams","notifications","tbot","teleport"],"latest_commit_sha":null,"homepage":"https://github.com/jsabo/teleport-msteams-webhook","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jsabo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-28T00:38:55.000Z","updated_at":"2026-05-28T02:01:40.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/jsabo/teleport-msteams-webhook","commit_stats":null,"previous_names":["jsabo/teleport-msteams-webhook"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/jsabo/teleport-msteams-webhook","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jsabo%2Fteleport-msteams-webhook","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jsabo%2Fteleport-msteams-webhook/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jsabo%2Fteleport-msteams-webhook/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jsabo%2Fteleport-msteams-webhook/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jsabo","download_url":"https://codeload.github.com/jsabo/teleport-msteams-webhook/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jsabo%2Fteleport-msteams-webhook/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34931587,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-29T02:00:05.398Z","response_time":58,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["access-requests","adaptive-cards","golang","microsoft-teams","msteams","notifications","tbot","teleport"],"created_at":"2026-06-29T14:32:23.348Z","updated_at":"2026-06-29T14:32:27.238Z","avatar_url":"https://github.com/jsabo.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# teleport-msteams-webhook\n\nPosts Microsoft Teams Adaptive Card notifications when Teleport access requests are created, approved, or denied.\n\nUses Teams **incoming webhooks** — a URL a channel owner creates in ~2 clicks via the Power Automate Workflows app. No Azure AD registration, no Global Admin approval, no custom app package.\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd align=\"center\"\u003e\u003cimg src=\"assets/card-pending.png\" width=\"220\" alt=\"Pending card\"/\u003e\u003cbr/\u003e\u003cb\u003ePending\u003c/b\u003e\u003c/td\u003e\n\u003ctd align=\"center\"\u003e\u003cimg src=\"assets/card-approved.png\" width=\"220\" alt=\"Approved card\"/\u003e\u003cbr/\u003e\u003cb\u003eApproved\u003c/b\u003e\u003c/td\u003e\n\u003ctd align=\"center\"\u003e\u003cimg src=\"assets/card-denied.png\" width=\"220\" alt=\"Denied card\"/\u003e\u003cbr/\u003e\u003cb\u003eDenied\u003c/b\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n## How it differs from the official `msteams` plugin\n\n| | teleport-msteams-webhook | Official msteams plugin |\n|---|---|---|\n| Teams setup | 2 clicks per channel | Azure AD app registration + Global Admin + Teams Administrator approval + custom app package |\n| Approve/deny in Teams | No — link to Teleport UI | Yes (interactive buttons) |\n| Credentials | tbot machine identity (auto-refresh) | Azure AD service principal |\n| Routing | `role_to_recipients` TOML map | Same |\n\nIf you need interactive approve/deny buttons in Teams, use the official plugin. If you want simple notifications with minimal setup, this tool is for you.\n\n---\n\n## Teams setup\n\n**Create an incoming webhook for each Teams channel you want to notify:**\n\n1. In the channel, click **`···`** (More options) next to the channel name and select **Workflows**. If you don't see it, click the **+** icon in the channel toolbar and search for **Workflows**.\n2. Search for **\"Post to a channel when a webhook request is received\"** and select it.\n3. Teams pre-fills a name like **\"Send webhook alerts to [channel-name]\"** — keep it or rename it, then click **Next** → **Add workflow**.\n4. When setup completes the workflow shows as **Active**. Click **Copy webhook link** to grab the URL.\n\nThe URL format varies by Teams plan:\n- Enterprise Microsoft 365: `https://prod2.westus2.logic.azure.com/workflows/...`\n- Personal / free Teams: `https://default[...].environment.api.powerplatform.com/...`\n\nBoth formats work identically with this plugin. Treat the URL as a secret — anyone with it can post to the channel.\n\nRepeat for each channel. Paste the URLs into `role_to_recipients` in your config.\n\n\u003e **Note:** Microsoft deprecated Office 365 Connectors in 2024. The Power Automate Workflows method above is the current replacement.\n\n### Storing webhook URLs securely\n\nInstead of hardcoding webhook URLs in the config file, you can reference environment variables or secret-mount files:\n\n```toml\n[role_to_recipients]\n# Read from an environment variable\n\"dev\" = [\"env:TEAMS_WEBHOOK_DEV\"]\n\n# Read from a file (Vault agent, Kubernetes Secret mount, AWS Secrets Manager sidecar, etc.)\n\"*\"   = [\"file:/run/secrets/teams-webhook-general\"]\n\n# Plain URL still works\n\"ops\" = [\"https://prod.westus2.logic.azure.com/.../ops\"]\n```\n\nThe value in the env var or file must be the full `https://` webhook URL. All URLs (plain or resolved) are validated to use HTTPS at startup — the plugin will not start if a URL is invalid or a referenced variable/file is missing.\n\n---\n\n## Teleport setup\n\n### Create the bot role and bot\n\nSave this as `msteams-webhook-role-and-bot.yaml` and apply with `tctl create -f msteams-webhook-role-and-bot.yaml`:\n\n```yaml\nkind: role\nversion: v7\nmetadata:\n  name: msteams-webhook\nspec:\n  allow:\n    rules:\n      - resources: [access_request]\n        verbs: [list, read]\n      - resources: [role]\n        verbs: [list, read]\n---\nkind: bot\nversion: v1\nmetadata:\n  name: msteams-webhook\nspec:\n  roles: [msteams-webhook]\n```\n\nThe `role` permission is optional — it enables the plugin to show allowed SSH logins alongside each role in the notification card. Without it, cards still post but the Login(s) field is omitted.\n\nSee [Machine \u0026 Workload Identity — Getting Started](https://goteleport.com/docs/machine-workload-identity/getting-started/) for full bot creation documentation.\n\n---\n\n## Kubernetes / Helm install\n\nThe Helm chart deploys two containers in one pod sharing an `emptyDir` identity volume:\n- **tbot** (init container + sidecar) — joins the cluster and keeps the identity file refreshed\n- **teleport-msteams-webhook** — reads the identity file and watches for access request events\n\nReady-to-use example files are in [`examples/kubernetes/`](examples/kubernetes/).\n\n### Step 1 — Apply Teleport resources\n\n```bash\ntctl create -f examples/kubernetes/bot-and-role.yaml\ntctl create -f examples/kubernetes/token.yaml\n```\n\nSee [Kubernetes join method](https://goteleport.com/docs/machine-workload-identity/deployment/kubernetes/) and [join methods overview](https://goteleport.com/docs/reference/deployment/join-methods/).\n\n### Step 2 — Create the namespace and webhook Secret\n\nFill in your Teams webhook URL in `examples/kubernetes/webhook-secret.yaml`, then:\n\n```bash\nkubectl create namespace teleport-plugins\nkubectl apply -f examples/kubernetes/webhook-secret.yaml\n```\n\n### Step 3 — Install the Helm chart\n\nEdit `examples/kubernetes/values.yaml` — replace `teleport.example.com` with your proxy address and set `tbot.image.tag` to match your Teleport version, then:\n\n```bash\nhelm install teleport-msteams-webhook \\\n  oci://ghcr.io/jsabo/charts/teleport-msteams-webhook \\\n  --namespace teleport-plugins \\\n  -f examples/kubernetes/values.yaml\n```\n\n### Step 4 — What the chart creates\n\n| Resource | Purpose |\n|---|---|\n| Deployment | tbot init container + sidecar + plugin container |\n| ConfigMap | Rendered `tbot.yaml` and `plugin.toml` |\n| ServiceAccount | Used by tbot for the Kubernetes TokenReview join |\n\n### Step 5 — Upgrade / uninstall\n\n```bash\n# Upgrade\nhelm upgrade teleport-msteams-webhook oci://ghcr.io/jsabo/charts/teleport-msteams-webhook \\\n  --namespace teleport-plugins -f examples/kubernetes/values.yaml\n\n# Uninstall\nhelm uninstall teleport-msteams-webhook --namespace teleport-plugins\n```\n\n---\n\n## Linux / systemd install\n\n### Step 1 — Install the plugin binary\n\nDownload the latest release tarball for your platform from the [releases page](https://github.com/jsabo/teleport-msteams-webhook/releases), extract, and install:\n\n```bash\ntar -xzf teleport-msteams-webhook_\u003cversion\u003e_linux_amd64.tar.gz\nsudo install -m 755 teleport-msteams-webhook /usr/local/bin/\n```\n\nOr build from source:\n\n```bash\ngo install github.com/jsabo/teleport-msteams-webhook/cmd/teleport-msteams-webhook@latest\nsudo mv $(go env GOPATH)/bin/teleport-msteams-webhook /usr/local/bin/\n```\n\n### Step 2 — Install tbot\n\ntbot is Teleport's machine identity agent. It joins the cluster, then continuously writes and refreshes a short-lived certificate bundle to disk. The plugin reads that bundle and reloads it automatically — no static credentials, no restarts when certs rotate.\n\nInstall tbot from the [Teleport Linux deployment guide](https://goteleport.com/docs/machine-workload-identity/deployment/linux/):\n\n```bash\n# Debian / Ubuntu\ncurl https://apt.releases.teleport.dev/gpg -o /usr/share/keyrings/teleport-archive-keyring.asc\necho \"deb [signed-by=/usr/share/keyrings/teleport-archive-keyring.asc] https://apt.releases.teleport.dev/ubuntu $(lsb_release -cs) stable\" \\\n  | sudo tee /etc/apt/sources.list.d/teleport.list\nsudo apt update \u0026\u0026 sudo apt install teleport\n```\n\n### Step 3 — Create a join token for tbot\n\nOn your Teleport cluster (via `tctl` or the Teleport UI):\n\n```bash\ntctl tokens add --type=bot --bot-name=msteams-webhook --ttl=1h\n```\n\nThis token is used only once for the initial join. tbot renews its own certificates continuously after that.\n\nFor production, consider a [platform join method](https://goteleport.com/docs/reference/deployment/join-methods/) (IAM on AWS, GCP, Azure) that requires no token at all.\n\n### Step 4 — Configure tbot (`/etc/tbot.yaml`)\n\n```yaml\nversion: v2\nproxy_server: teleport.example.com:443\n\nonboarding:\n  join_method: token\n  token: \u003cpaste-token-here\u003e\n\nstorage:\n  type: directory\n  path: /var/lib/tbot\n\noutputs:\n  - type: identity\n    destination:\n      type: directory\n      path: /var/lib/teleport/plugins/msteams-webhook\n```\n\nSee [tbot configuration reference](https://goteleport.com/docs/machine-workload-identity/getting-started/) for all options.\n\n### Step 5 — Create system user and directories\n\n```bash\nsudo useradd -r -s /sbin/nologin teleport-plugin\nsudo install -d -o teleport-plugin -g teleport-plugin /var/lib/teleport/plugins/msteams-webhook\nsudo install -d -o teleport-plugin -g teleport-plugin /var/lib/tbot\n```\n\n### Step 6 — systemd unit for tbot\n\nCreate `/etc/systemd/system/tbot.service`:\n\n```ini\n[Unit]\nDescription=Teleport Machine Identity Agent (tbot)\nAfter=network-online.target\nWants=network-online.target\n\n[Service]\nType=simple\nUser=teleport-plugin\nGroup=teleport-plugin\nExecStart=/usr/local/bin/tbot start -c /etc/tbot.yaml\nRestart=on-failure\nRestartSec=5s\nStandardOutput=journal\nStandardError=journal\n\n[Install]\nWantedBy=multi-user.target\n```\n\nSee the [tbot Linux deployment guide](https://goteleport.com/docs/machine-workload-identity/deployment/linux/) for hardening options and distro-packaged unit files.\n\n### Step 7 — Configure the plugin (`/etc/teleport-msteams-webhook.toml`)\n\n```toml\n[teleport]\naddr             = \"teleport.example.com:443\"\nidentity         = \"/var/lib/teleport/plugins/msteams-webhook/identity\"\nrefresh_identity = true   # reload cert automatically when tbot renews it\n\n[log]\noutput   = \"stderr\"\nseverity = \"info\"\n\n[msteams]\n# Optional. Defaults to the Teleport logo in this repo.\n# Set disable_logo = true to omit the image entirely.\n# logo_url    = \"https://raw.githubusercontent.com/jsabo/teleport-msteams-webhook/main/assets/teleport-logo.png\"\n# disable_logo = false\n\n[role_to_recipients]\n\"dev\"            = [\"https://prod.westus2.logic.azure.com/.../dev-requests\"]\n\"database-admin\" = [\"https://prod.westus2.logic.azure.com/.../dba-team\",\n                    \"https://prod.westus2.logic.azure.com/.../security\"]\n\"*\"              = [\"https://prod.westus2.logic.azure.com/.../access-requests-general\"]\n```\n\nThe `\"*\"` wildcard catches any role not explicitly listed. It is required.\n\nA copy of this example lives at `deploy/systemd/teleport-msteams-webhook.toml.example`.\n\n### Step 8 — systemd unit for the plugin\n\nInstall the unit file from this repo:\n\n```bash\nsudo cp deploy/systemd/teleport-msteams-webhook.service /etc/systemd/system/\nsudo systemctl daemon-reload\nsudo systemctl enable --now tbot teleport-msteams-webhook\n```\n\nFollow logs:\n\n```bash\njournalctl -u teleport-msteams-webhook -f\n```\n\n---\n\n## Verification\n\n### Dry run\n\nValidates credentials and POSTs a test card to every configured webhook URL, then exits:\n\n```bash\nteleport-msteams-webhook -config /etc/teleport-msteams-webhook.toml -dry-run\n```\n\nA sample Adaptive Card should appear in each configured Teams channel.\n\n### Live test\n\nCreate a test access request with `tsh`:\n\n```bash\ntsh request create --roles=dev --reason=\"testing msteams webhook\"\n```\n\nA card should appear in the channel mapped to the `dev` role (or the `*` wildcard channel).\n\n---\n\n## Troubleshooting\n\n**Card not appearing — check the webhook URL**\n\nTest the webhook URL directly:\n\n```bash\ncurl -s -o /dev/null -w \"%{http_code}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"type\":\"message\",\"attachments\":[{\"contentType\":\"application/vnd.microsoft.card.adaptive\",\"content\":{\"type\":\"AdaptiveCard\",\"version\":\"1.4\",\"body\":[{\"type\":\"TextBlock\",\"text\":\"test\"}]}}]}' \\\n  \"https://prod.westus2.logic.azure.com/...\"\n```\n\nA `202` response means the webhook accepted the card. A `4xx` means the URL is wrong or expired — create a new webhook URL in Teams.\n\n**Plugin starts but `no recipients found` in logs**\n\nEnsure your `role_to_recipients` has a `\"*\"` wildcard entry. The plugin logs the resolved roles and webhook URLs at `debug` severity (`severity = \"debug\"` in config).\n\n**`identity file not found` on startup**\n\ntbot may not have completed its first certificate write yet. The plugin retries the Teleport connection with exponential backoff, so it will recover once tbot finishes. In Kubernetes, the init container ensures tbot writes the identity before the plugin starts.\n\nCheck tbot status:\n\n```bash\n# systemd\njournalctl -u tbot -f\n\n# Kubernetes\nkubectl logs -n teleport-plugins deployment/teleport-msteams-webhook -c tbot\n```\n\n**`certificate has expired` errors**\n\nThe identity file is stale. Verify tbot is running and `refresh_identity = true` is set in the plugin config. If tbot was down for longer than the cert TTL (default: 1 hour), restart tbot — it will re-join and write a fresh identity.\n\n**`permission denied` reading access requests**\n\nThe bot role is missing the `access_request` list/read rule. Verify with:\n\n```bash\ntctl get role/msteams-webhook\n```\n\nRe-apply the role YAML from the [Teleport setup](#teleport-setup) section if needed.\n\n---\n\n## Logo customization\n\nThe default card includes the Teleport logo fetched from this repo's `assets/` directory. Teams caches card images aggressively on the client side — this is not a per-notification request.\n\nTo use your own logo:\n\n```toml\n[msteams]\nlogo_url = \"https://example.com/your-logo.png\"\n```\n\nTo disable the logo entirely:\n\n```toml\n[msteams]\ndisable_logo = true\n```\n\nCards work correctly either way — the logo is purely cosmetic.\n\n---\n\n## Building from source\n\n```bash\ngit clone https://github.com/jsabo/teleport-msteams-webhook.git\ncd teleport-msteams-webhook\nmake build   # produces ./teleport-msteams-webhook\nmake test    # run all unit tests\nmake vet     # go vet\n```\n\n## License\n\n[Apache 2.0](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjsabo%2Fteleport-msteams-webhook","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjsabo%2Fteleport-msteams-webhook","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjsabo%2Fteleport-msteams-webhook/lists"}