Skip to content

feat: add costing-entra-appid sample with Azure Workbook dashboard#145

Open
ncheruvu-MSFT wants to merge 46 commits intomainfrom
feature/costing-entra-appid
Open

feat: add costing-entra-appid sample with Azure Workbook dashboard#145
ncheruvu-MSFT wants to merge 46 commits intomainfrom
feature/costing-entra-appid

Conversation

@ncheruvu-MSFT
Copy link
Contributor

Summary

Adds a new sample costing-entra-appid that demonstrates APIM cost attribution by Entra ID caller (app registration).

What's included

  • caller-id-policy.xml - APIM policy that extracts \�ppid\ from JWT tokens and emits custom metrics with caller identity
  • main.bicep - Deploys Log Analytics, Application Insights, APIM diagnostic settings, and an Azure Workbook dashboard
  • create.ipynb - End-to-end notebook: deploy infrastructure, create Entra test apps, generate traffic, verify data, view reports
  • README.md - Full documentation with architecture, prerequisites, and workbook details

Azure Workbook features

  • 3 foldable sections: Usage by Caller, Cost Allocation, Request Trend
  • Name resolution: JSON parameter maps app IDs to friendly names
  • Accessibility: borders, readable labels, no-data messages, side-by-side layout
  • Parameters: TimeRange selector, BaseCost (USD), AppIdNames (JSON mapping)

Testing

  • Deployed and verified on Basicv2 APIM instance (index 4)
  • Simulated 4 callers + 2 real Entra app registrations
  • All 6 caller IDs visible in workbook queries
  • Infrastructure deleted after testing

ncheruvu-MSFT and others added 4 commits February 7, 2026 12:18
- Add costing sample with cost allocation workbook, cost exports, and budget alerts

- Add screenshots for cost reports and dashboard views

- Remove manual fallback steps (managed identity handles export auth)

- Update README with screenshot gallery

- Update shared azure_resources and template notebook
- APIM cost attribution by Entra ID caller (app registration)
- Custom metric emission via caller-id-policy.xml
- Azure Workbook with foldable sections, name resolution, accessibility
- Notebook: full end-to-end deploy, Entra test apps, traffic, verification
- KQL queries for usage, cost allocation, and request trends
@github-actions
Copy link

github-actions bot commented Feb 20, 2026

🐍 Python 3.14 Results

Metric Status Value
Ruff 0 issue(s)
Unit Tests success
Code Coverage 📊 100%

Full Workflow Logs

@github-actions
Copy link

github-actions bot commented Feb 20, 2026

Python 3.14 Detailed Test Results

1 719 tests  ±0   1 719 ✅ ±0   12s ⏱️ -6s
    1 suites ±0       0 💤 ±0 
    1 files   ±0       0 ❌ ±0 

Results for commit 3c6b413. ± Comparison against base commit db0b891.

♻️ This comment has been updated with latest results.

@github-actions
Copy link

github-actions bot commented Feb 20, 2026

🐍 Python 3.12 Results

Metric Status Value
Ruff 0 issue(s)
Unit Tests success
Code Coverage 📊 100%

Full Workflow Logs

@github-actions
Copy link

github-actions bot commented Feb 20, 2026

🐍 Python 3.13 Results

Metric Status Value
Ruff 0 issue(s)
Unit Tests success
Code Coverage 📊 100%

Full Workflow Logs

@github-actions
Copy link

github-actions bot commented Feb 20, 2026

Python 3.12 Detailed Test Results

1 719 tests  ±0   1 719 ✅ ±0   17s ⏱️ -1s
    1 suites ±0       0 💤 ±0 
    1 files   ±0       0 ❌ ±0 

Results for commit 3c6b413. ± Comparison against base commit db0b891.

♻️ This comment has been updated with latest results.

@github-actions
Copy link

github-actions bot commented Feb 20, 2026

Python 3.13 Detailed Test Results

1 719 tests  ±0   1 719 ✅ ±0   15s ⏱️ -3s
    1 suites ±0       0 💤 ±0 
    1 files   ±0       0 ❌ ±0 

Results for commit 3c6b413. ± Comparison against base commit db0b891.

♻️ This comment has been updated with latest results.

simonkurtz-MSFT and others added 12 commits February 20, 2026 11:11
…atterns

- Replace ARM template with Bicep using shared modules (diagnostics, api, appinsights, workspaces)
- Externalize KQL queries to .kql files (verify-metric-ingestion, budget-alert-threshold)
- Replace caller-id-policy.xml with emit_metric_caller_id.xml (emit-metric policy)
- Add Azure Monitor Workbook (workbook.json) with cost attribution dashboards
- Rewrite create.ipynb following _TEMPLATE pattern with NotebookHelper/deploy_sample()
- Rewrite README.md matching costing sample section order and formatting
- Remove old screenshots (will be re-added once sample is deployed)
- Fix emit_metric_caller_id.xml: add curly braces for APIM Razor parser
- Fix main.bicep: reference infrastructure's existing App Insights/Log Analytics
  instead of deploying separate instances (emit-metric sends to service-level AI)
- Fix create.ipynb: correct App Insights query endpoint and workbook portal link
- Fix budget-alert-threshold.kql: use 'timestamp' instead of 'TimeGenerated' for
  customMetrics table
@ncheruvu-MSFT ncheruvu-MSFT requested a review from san360 February 23, 2026 16:03
@simonkurtz-MSFT simonkurtz-MSFT added enhancement New feature or request scenario An APIM Sample scenario labels Feb 23, 2026
@simonkurtz-MSFT
Copy link
Member

@ncheruvu-MSFT, please also address the three ruff issues. You can run the Developer CLI via .\start.ps1 and then select option 10 to do a full test scan.

@simonkurtz-MSFT
Copy link
Member

@ncheruvu-MSFT & @san360, I addressed the ruff items as well as scrubbing the notebook output. For the latter, please run the APIM Samples Developer CLI and select option 1 as it needs to act on your git config for the pre-commit hook.

san360
san360 previously approved these changes Mar 9, 2026
@simonkurtz-MSFT simonkurtz-MSFT self-requested a review March 9, 2026 14:08
- README.md: Fix heading 'Two' -> 'Three Tracking Approaches'
- main.bicep: Remove primaryKey from apiSubscriptionKeys output (security)
- create.ipynb: Replace api_subscription_keys Bicep output lookups with
  get_apim_subscription_key() (RBAC-controlled ARM listSecrets)
- verify-token-metric-ingestion.kql: Add tokenType filter, default to '*' (all)
@simonkurtz-MSFT
Copy link
Member

Hi @ncheruvu-MSFT, I should be able to review Tuesday or Wednesday. Apologies for taking so long!

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 20 changed files in this pull request and generated 8 comments.

Comment on lines +24 to +25
var payload = System.Text.Encoding.UTF8.GetString(
Convert.FromBase64String(parts[1].PadRight(parts[1].Length + (4 - parts[1].Length % 4) % 4, '=')));
Comment on lines +190 to +193
// KQL queries (Entra ID tab). Replace them with the actual App Insights name
// so the app() function resolves correctly at runtime.
var rawWorkbookJson = string(loadJsonContent('workbook.json'))
var workbookJsonWithAppInsights = replace(rawWorkbookJson, '__APP_INSIGHTS_NAME__', applicationInsightsName)
Comment on lines +855 to +889
"import json\n",
"import requests\n",
"\n",
"from apimrequests import ApimRequests\n",
"from azure_resources import get_apim_subscription_key\n",
"\n",
"if not use_real_jwt:\n",
" print_info('Real JWT testing is disabled (use_real_jwt = False)')\n",
" print_info('To enable, set use_real_jwt = True and provide client credentials in the init cell')\n",
"elif not all([real_jwt_tenant_id, real_jwt_client_id, real_jwt_client_secret]):\n",
" print_error('Missing credentials: set real_jwt_tenant_id, real_jwt_client_id, and real_jwt_client_secret in the init cell')\n",
"elif 'apim_gateway_url' not in locals():\n",
" print_error('Please run the deployment cell first')\n",
" raise SystemExit(1)\n",
"else:\n",
" # Acquire a token using the client credentials flow\n",
" token_url = f'https://login.microsoftonline.com/{real_jwt_tenant_id}/oauth2/v2.0/token'\n",
" token_payload = {\n",
" 'grant_type': 'client_credentials',\n",
" 'client_id': real_jwt_client_id,\n",
" 'client_secret': real_jwt_client_secret,\n",
" 'scope': f'{real_jwt_client_id}/.default'\n",
" }\n",
"\n",
" print_info('Acquiring token from Microsoft Identity Platform...')\n",
" token_response = requests.post(token_url, data=token_payload, timeout=30)\n",
"\n",
" if token_response.status_code != 200:\n",
" print_error(f'Token acquisition failed ({token_response.status_code}): {token_response.text[:300]}')\n",
" else:\n",
" access_token = token_response.json().get('access_token', '')\n",
" payload_part = access_token.split('.')[1]\n",
" payload_part += '=' * (4 - len(payload_part) % 4) # pad base64\n",
" claims = json.loads(base64.urlsafe_b64decode(payload_part))\n",
" real_appid = claims.get('appid', claims.get('azp', 'unknown'))\n",
Copy link
Member

@simonkurtz-MSFT simonkurtz-MSFT left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Naga,

Please take a look at my comments as well as Copilot's. Once that's done, please take a good, manual look over everything once more, please. I'll review any changes that you make Tuesday or Wednesday.

Thank you!

simonkurtz-MSFT and others added 3 commits March 18, 2026 11:28
…output cleanup

- Extract caller-id JWT parsing into reusable policy fragment (pf-extract-caller-id.xml)
- Fix orphaned raise SystemExit, duplicate alert_email, duplicate print_ok in init cell
- Fix undefined sample_requests_per_caller -> sample_requests_per_subscription
- Remove duplicate print block in Real JWT cell
- Clear all notebook cell outputs
- Remove temp REVIEW-FIXES.md
- Discard unrelated authX-pro whitespace change
@simonkurtz-MSFT
Copy link
Member

Hi @ncheruvu-MSFT, please add a graceful fallback in the event that AI tokens were not captured:

image

ncheruvu-MSFT and others added 5 commits March 18, 2026 15:19
When caller-tokens custom metrics have not been ingested yet, the
evaluate pivot() operator does not create prompt/completion/total
columns. Replace coalesce(prompt, 0.0) with column_ifexists('prompt', 0.0)
in all four affected queries so the AI Gateway Token/PTU tab renders
gracefully instead of showing KQL resolution errors.
@simonkurtz-MSFT
Copy link
Member

Much better!

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request scenario An APIM Sample scenario

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants