Skip to content

Feature/multi tenant report#1558

Open
SebastianClaesson wants to merge 18 commits intomaester365:mainfrom
SebastianClaesson:feature/multi-tenant-report
Open

Feature/multi tenant report#1558
SebastianClaesson wants to merge 18 commits intomaester365:mainfrom
SebastianClaesson:feature/multi-tenant-report

Conversation

@SebastianClaesson
Copy link
Copy Markdown
Contributor

Description

Multi-Tenant Report Support

Adds the ability to run Maester tests across multiple tenants and view all results in a single HTML report with a tenant selector sidebar.

Fixes #1551

What's new

PowerShell:

  • Merge-MtMaesterResult (new exported function) — merges multiple Invoke-Maester -PassThru results into a single object with a Tenants array
  • Get-MtHtmlReport — now supports both single-tenant and multi-tenant result objects
  • Get-MtMaesterConfig — new -TenantId parameter enables tenant-specific config files (maester-config.{tenantId}.json) with automatic fallback to maester-config.json
  • Invoke-Maester — automatically resolves the connected tenant ID and passes it to config lookup

Report UI (React):

  • TenantContext — new React context that normalizes single/multi-tenant data and manages tenant selection
  • Sidebar tenant selector — only shown when multiple tenants are present
  • All pages read data from the selected tenant via useTenant() context
  • ConfigPage shows which config file was loaded (multi-tenant only) and resets state on tenant switch to prevent data leakage

Examples & Docs:

  • powershell/examples/multi-tenant-pipeline.yml — full Azure DevOps pipeline example with per-tenant service connections, supporting Global, China, USGov, USGovDoD, and Germany cloud environments
  • Blog post announcing the feature
  • Sample report generator and pre-built HTML reports for reviewer testing (Can be removed, just to give a easy way to review what I'm trying to achieve)

Backward compatibility

Single-tenant reports are unaffected. The TenantContext wraps legacy single-tenant data in a one-element array and all pages use a destructured alias ( const { selectedTenant: testResults } = useTenant() ) that preserves existing property access. The tenant selector is hidden for single-tenant reports. The -TenantId parameter in Get-MtMaesterConfig is optional and only activates when a valid GUID is provided.

Tenant-specific configuration

Each tenant can have its own config file named maester-config.{tenantId}.json. Resolution order:

  • maester-config.{tenantId}.json (if connected to Graph and file exists)
  • maester-config.json (default fallback)
  • Custom/maester-config.json (merged on top, existing behavior)

How to test

Open the sample reports in samples/ to verify:

  • sample-report-single-tenant.html — no tenant selector, no ConfigSource banner, everything works as before
  • sample-report-multi-tenant.html — tenant selector in sidebar, switch between 3 tenants, Config page shows "Loaded from" per tenant

Contribution Checklist

Before submitting this PR, please confirm you have completed the following:

  • 📖 Read the guidelines for contributing to this repository.
  • 🧪 Ensure the build and unit tests pass by running /powershell/tests/pester.ps1 on your local system. (All 4317 tests passed, zero failures. )

- New TenantContext provider for tenant selection state management
- Tenant selector in sidebar (visible when multiple tenants present)
- All pages use useTenant() hook instead of direct testResults import
- New Merge-MtMaesterResult function to combine tenant results
- Updated Get-MtHtmlReport with proper JSON depth and marker validation
- Full dark mode support across all pages
- Backwards compatible: single-tenant reports work unchanged
Demonstrates how to run Maester against multiple tenants using
separate service connections and merge results into a single
HTML report with tenant selector.
Example pipeline now supports Global, China (21Vianet) and other
cloud environments via an environment parameter per tenant.
Get-MtHtmlReport uses -Compress and LastIndexOf to fix blank reports.
Added blog post for multi-tenant reports feature.
ConfigPage state (emergency accounts, test settings) was not
resetting when switching tenants, leaking previous tenant config.
Added useEffect to reset state when tenant changes.

Get-MtHtmlReport now handles both Vite minifier formats (classic
testResults= and newer mangled variable names with backtick strings)
using LastIndexOf and pattern scanning. Also uses -Compress to
prevent newlines breaking minified JS.
Added Merge-MtMaesterResult.ps1 and New-MtMultiTenantHtmlReport.ps1
to powershell/examples so people can copy them into their repos.
Fixed missing -AsSecureString on Teams token request.
Updated blog post with prerequisites, parameter docs, and full
pipeline YAML with China cloud support.
The environment switch only handled China and Global. Added all
national cloud endpoints for Graph, Exchange and Compliance.
Since the blog post ships with the release, Merge-MtMaesterResult
and Get-MtHtmlReport are part of the installed module. Removed
standalone New-MtMultiTenantHtmlReport and dot-source references
from the example pipeline and blog post.
… diff

- Added 16 Pester tests covering Merge-MtMaesterResult and
  Get-MtHtmlReport for both single and multi-tenant scenarios
- Exported Merge-MtMaesterResult in module manifest
- Fixed operator precedence bug in validation (-not -contains)
- Reverted SettingsPage dark mode changes to keep diff minimal
  (pre-existing issue, not related to multi-tenant feature)
- Add 3+ tenant merge test and empty TenantName edge case
- Add ReportTemplate.html availability check with graceful skip
@SebastianClaesson SebastianClaesson requested review from a team as code owners March 31, 2026 07:52
- Get-MtMaesterConfig: add -TenantId parameter with GUID validation,
  look for maester-config.{tenantId}.json with fallback to default
- Invoke-Maester: resolve tenant ID from MgContext for config lookup
- ConfigPage: show loaded config filename for multi-tenant reports only
- Add Pester tests for tenant-specific config, fallback, and GUID validation
- Rebuild ReportTemplate.html with updated ConfigPage
- Add tenant-specific config section to blog post
- Add sample report generator and pre-built HTML reports
@merill
Copy link
Copy Markdown
Contributor

merill commented Apr 3, 2026

@SebastianClaesson this is super neat!! Wow. Love it.

Thank you so much for going into detail in the blog post.

If I can make a suggestion, it will be good to update the site navigation to include a new 'Multi-tenant' section and include most of the content from the blog post including the overview and how to combine with the cmdlet and pipeline.

We can then update the blog post to be just the announcement about the new feature and point the user to the docs page. This is because the blogs page will move down and it will be hard for folks to navigate and find out about this feature.

Thanks for samples folder it was handy, feel free to remove it though, it clutters the repo.

… samples

Move substantive multi-tenant content from the blog post into a proper
docs section (overview, merging results, configuration, Azure DevOps
pipeline) so users can find it via site navigation. Trim the blog post
to a short announcement linking to the docs. Delete the samples/ folder
as requested by the project owner.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@SebastianClaesson
Copy link
Copy Markdown
Contributor Author

@SebastianClaesson this is super neat!! Wow. Love it.

Thank you so much for going into detail in the blog post.

If I can make a suggestion, it will be good to update the site navigation to include a new 'Multi-tenant' section and include most of the content from the blog post including the overview and how to combine with the cmdlet and pipeline.

We can then update the blog post to be just the announcement about the new feature and point the user to the docs page. This is because the blogs page will move down and it will be hard for folks to navigate and find out about this feature.

Thanks for samples folder it was handy, feel free to remove it though, it clutters the repo.

I have now broken out the instructions into the docs - great call!
It's part of the new (preview) site.

Samples folders has also been removed :)

@codacy-production
Copy link
Copy Markdown

codacy-production bot commented Apr 5, 2026

Not up to standards ⛔

TIP This summary will be updated as you push new changes. Give us feedback

Keep upstream's function wrapper and indented comment block while
preserving our multi-tenant documentation additions and example.
@SebastianClaesson
Copy link
Copy Markdown
Contributor Author

The 4 test failures are pre-existing in main and unrelated to this PR. They come from PSScriptAnalyzer's PSUseSingularNouns rule flagging three AI agent test files that were recently added to main:

powershell/public/maester/aiagent/Test-MtAIAgentHardCodedCredentials.ps1
powershell/public/maester/aiagent/Test-MtAIAgentMcpTools.ps1
powershell/public/maester/aiagent/Test-MtAIAgentMissingInstructions.ps1
The function names use plural nouns (Credentials, Tools, Instructions) which triggers the rule. These files were pulled in when merging upstream main to resolve conflicts.

None of the multi-tenant changes in this PR are affected.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🙏 Support for multi-tenant reports

2 participants