Skip to content

Instantly share code, notes, and snippets.

@frankmalin
Last active January 12, 2026 20:16
Show Gist options
  • Select an option

  • Save frankmalin/cafbc135fff9eb08548ade658dfc90cd to your computer and use it in GitHub Desktop.

Select an option

Save frankmalin/cafbc135fff9eb08548ade658dfc90cd to your computer and use it in GitHub Desktop.
teams adaptive card posting
# This is an adaptive card
{
  "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
  "type": "AdaptiveCard",
  "version": "1.4",
  "body": [
    { "type": "TextBlock", "size": "Medium", "weight": "Bolder", "text": "ℹ️ License status" },
    { "type": "TextBlock", "wrap": true, "color": "good", "text": "__SUMMARY__" },
    { "type": "ActionSet", "actions": [
      { "type": "Action.ShowCard", "title": "Details", "card": {
        "type": "AdaptiveCard", "version": "1.4",
        "body": [
          { "type": "FactSet", "facts": [
            { "title": "Enterprise", "value": "__ENTERPRISE__" },
            { "title": "Purchased", "value": "__TOTAL_PURCHASED__" },
            { "title": "Consumed", "value": "__TOTAL_CONSUMED__" },
            { "title": "Available", "value": "__SEATS_AVAILABLE__" },
            { "title": "% Consumed", "value": "__PERCENT_CONSUMED__%" },
            { "title": "% Remaining", "value": "__PERCENT_REMAINING__%" },
            { "title": "Threshold (__METRIC_LABEL__)", "value": "__THRESHOLD__%" }
          ] }
        ]
      } }
    ] }
  ],
  "msteams": { "width": "Full" }
}

The secret which is needed to post : TEAMS_WORKFLOW This can be found by creating a workflow on the team, and it would be URL to post message to channel. The user generating must be an owner of the channel. The channel must be public to the group it is in.

#!/usr/bin/env bash
set -euo pipefail

# Expects env vars:
# TEAMS_WEBHOOK_URL, SUMMARY, ENTERPRISE, TOTAL_PURCHASED, TOTAL_CONSUMED,
# SEATS_AVAILABLE, PERCENT_CONSUMED, PERCENT_REMAINING, THRESHOLD, METRIC_LABEL
# Optional: TEMPLATE_PATH (defaults to .data/info_card.json)

BASE="https://defaultxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.05.environment.api.powerplatform.com:443/powerautomate/automations/direct"
URI="${BASE}/workflows/${TEAMS_WORKFLOW}/triggers/manual/paths/invoke?api-version=1&sp=%2Ftriggers%2Fmanual%2Frun&sv=1.0&sig=${TEAMS_SIG}"

TEMPLATE_PATH="${TEMPLATE_PATH:-data/license_infor.card}"
ADAPTED_PATH="${RUNNER_TEMP:-/tmp}/info_card.adapted.json"
PAYLOAD_PATH="${RUNNER_TEMP:-/tmp}/info_payload.json"

jq \
  --arg SUMMARY "${SUMMARY}" \
  --arg ENTERPRISE "${ENTERPRISE}" \
  --arg TOTAL_PURCHASED "${TOTAL_PURCHASED}" \
  --arg TOTAL_CONSUMED "${TOTAL_CONSUMED}" \
  --arg SEATS_AVAILABLE "${SEATS_AVAILABLE}" \
  --arg PERCENT_CONSUMED "${PERCENT_CONSUMED}" \
  --arg PERCENT_REMAINING "${PERCENT_REMAINING}" \
  --arg THRESHOLD "${THRESHOLD}" \
  --arg METRIC_LABEL "${METRIC_LABEL}" \
  '
  def walk(f):
    . as $in
    | if type == "object" then
        reduce keys[] as $key ({}; . + {($key): ($in[$key] | walk(f))})
      elif type == "array" then
        map(walk(f))
      else
        f
      end;

  walk(
    if type=="string" then
      . 
      | gsub("__SUMMARY__"; $SUMMARY)
      | gsub("__ENTERPRISE__"; $ENTERPRISE)
      | gsub("__TOTAL_PURCHASED__"; $TOTAL_PURCHASED)
      | gsub("__TOTAL_CONSUMED__"; $TOTAL_CONSUMED)
      | gsub("__SEATS_AVAILABLE__"; $SEATS_AVAILABLE)
      | gsub("__PERCENT_CONSUMED__"; $PERCENT_CONSUMED)
      | gsub("__PERCENT_REMAINING__"; $PERCENT_REMAINING)
      | gsub("__THRESHOLD__"; $THRESHOLD)
      | gsub("__METRIC_LABEL__"; $METRIC_LABEL)
    else . end
  )
  ' "${TEMPLATE_PATH}" > "${ADAPTED_PATH}"

jq -n --slurpfile card "${ADAPTED_PATH}" \
  '{type:"message",attachments:[{contentType:"application/vnd.microsoft.card.adaptive",content:$card[0]}]}' \
  > "${PAYLOAD_PATH}"

curl -sS -X POST -H 'Content-Type: application/json' \
  -d @"${PAYLOAD_PATH}" "${URI}" | cat
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment