-
Notifications
You must be signed in to change notification settings - Fork 247
ASD Organization Services Configuration Check
Purpose: Assess Microsoft 365 (M365) Organization / Tenant service configuration against an ASD / baseline JSON, producing console + HTML reports and distinguishing API‑verifiable vs GUI‑only settings (marked
MANUAL_CHECK_REQUIRED).
asd-orgservices-get.ps1 loads a baseline definition (local file or GitHub URL) of required values for tenant services, retrieves what it can via Microsoft Graph REST (device code auth), compares results, and outputs:
- Colored console summary
- HTML compliance report (cards, table, manual verification badge)
- CSV/JSON (if extended output is later added – placeholders for future enhancement)
The script intentionally avoids the Microsoft Graph PowerShell module for reliability, using raw REST calls via an acquired OAuth access token.
- Device code authentication (clipboard & browser launch convenience)
- Minimal required delegated Graph scopes (recommended start:
openid profile offline_access Organization.Read.All Reports.Read.All) - Separation of verifiable vs manual-only checks to prevent false failure reporting
- Extensible baseline-driven architecture (add/remove services or settings without code change)
- HTML dashboard with compliance percentage excluding manual (GUI-only) checks
The retrieval layer (Get-ServiceSettings) currently implements partial / placeholder coverage:
| Service Name | API Retrieval Status | Notes |
|---|---|---|
| ModernAuthentication | Placeholder values only | True/False placeholders until org settings endpoint confirmed |
| Reports | Reads admin/reportSettings (display concealment) |
Requires Reports.Read.All; may expand |
| Others (baseline-defined but not coded) | Manual only | Marked MANUAL_CHECK_REQUIRED (no endpoint implemented yet) |
Any baseline service without a coded branch or whose endpoint returns no data is marked MANUAL_CHECK_REQUIRED.
A check is flagged MANUAL_CHECK_REQUIRED if either:
- There is no implemented Graph endpoint for the service/setting
- The endpoint does not return a value (null) / is inaccessible
- The setting exists only in a GUI blade (security/compliance portals, admin centers) with no public Graph surface
These manual items are excluded from the compliance percentage. The HTML report status logic:
- All checks manual → Overall:
MANUAL VERIFICATION REQUIRED - No failed verifiable checks + some manual →
COMPLIANT (API) - Manual Checks Pending - Some failures among verifiable →
NON-COMPLIANT - All verifiable pass and no manual →
COMPLIANT
| Function | Purpose |
|---|---|
Get-GraphAccessToken |
Device code auth – obtains and caches token |
Invoke-MSGraphAPI |
Generic REST GET wrapper with error handling |
Test-Setting |
Compares required vs current or sets manual status |
Get-ServiceSettings |
Service-specific retrieval (switch statement) |
Invoke-OrganizationServicesCheck |
Iterates baseline, calls compare logic |
New-HTMLReport |
Builds styled compliance dashboard |
Baseline loading & schema validation: Get-BaselineSettings + Test-BaselineSchema.
- PowerShell 7+ recommended (Core); script may run on Windows PowerShell but not guaranteed
- Internet access to authenticate and optionally load baseline from GitHub
- Ability for signed-in user to consent scopes or existing admin pre-consent
- Outbound HTTPS to
login.microsoftonline.comandgraph.microsoft.com
Recommended delegated scopes (minimum for current implementation):
openid profile offline_access Organization.Read.All Reports.Read.All
Additional scopes may be required as you implement new service endpoints (e.g. AuditLog.Read.All, SecurityEvents.Read.All, Policy.Read.All). Always add the least privilege necessary.
If encountering repeated admin consent dialogs:
- Ask a Global / Privileged Role Administrator to pre-consent scopes via the Entra admin center.
- Confirm the app registration (if using one) or device flow context is within the tenant (not cross-account).
- Validate the token contains requested scopes (decode at
https://jwt.ms).
Basic invocation (baseline from default GitHub URL):
pwsh ./asd-orgservices-get.ps1Specify custom baseline:
pwsh ./asd-orgservices-get.ps1 -BaselinePath "C:\Baselines\services.json"Enable debug verbose logging (if a flag is exposed – or set $env:DEBUG=1 before running if the script checks it):
$env:DEBUG = 1
pwsh ./asd-orgservices-get.ps1Expected shape:
{
"ModernAuthentication": {
"EnableModernAuth": true,
"EnableAuthenticatedSMTP": false
},
"Reports": {
"DisplayConcealedNames": true
}
// Additional services...
}Rules:
- Top-level properties = service names
- Each service property object = individual setting:value pairs
- Values can be bool, string, number, or array (current compare supports simple equality & array membership equality)
- Add baseline settings in your JSON.
- Implement retrieval logic inside
Get-ServiceSettingsswitch:
"NewService" {
$raw = Invoke-MSGraphAPI -Uri "security/someEndpoint" # example
if ($raw) { $settings["SomeSetting"] = [bool]$raw.propertyFlag }
}- Map raw response values to primitive comparable values (bool/string/int/array)
- Re-run script; verifiable checks will now contribute to compliance percentage.
Console summary example (simplified):
Total Checks : 25
Verifiable (API) : 8
Passed (API) : 7
Failed (API) : 1
Manual (GUI) : 17
API Compliance : 87.50%
Overall Status : COMPLIANT (API) - Manual Checks Pending
HTML report cards:
- Total Checks: all baseline items
- Passed API / Failed API: only verifiable items
- GUI Required: manual checks count
- API Compliance (%): Passed / Verifiable * 100 Status badge color: Green (compliant), Amber (all manual), Red (non-compliant).
Below are step-by-step paths for typical settings commonly absent from Graph today. Adapt names to match your baseline.
Goal: Confirm modern auth is enforced; legacy protocols disabled where required.
- Sign in to Microsoft 365 admin center: https://admin.microsoft.com
- Navigate: Settings → Org settings → Modern authentication.
- Verify: "Enable modern authentication" is checked (per baseline
EnableModernAuth=true). - If baseline requires SMTP Auth disabled, ensure "Authenticated SMTP" toggle is OFF (
EnableAuthenticatedSMTP=false). - Save any changes; record observed values manually if script shows
MANUAL_CHECK_REQUIRED.
Alternate deeper checks (Exchange-specific legacy protocols):
- Open Exchange Admin Center: https://admin.exchange.microsoft.com
- Go to Settings → Authentication (preview) or (classic) Organization → Settings → Modern authentication.
- Validate legacy protocol toggles (POP3/IMAP, Exchange ActiveSync) align with security baseline.
Goal: Ensure user privacy in usage reports.
- Microsoft 365 admin center: https://admin.microsoft.com
- Reports → Settings (gear icon) or Reports → Usage → choose any report → top banner "Report settings".
- Verify "Display concealed user, group, and site names" matches baseline (often
truefor privacy). - If mismatch, adjust and save; refresh script after API propagation (may take minutes).
- Entra admin center: https://entra.microsoft.com
- Navigate: Protection → Conditional Access → Policies.
- Manually correlate required policies (naming, enabled state, assignments). Many CA attributes not wholly accessible via simple baseline currently.
- For Security Defaults: Identity → Overview → Properties → Manage Security defaults.
- Record enabled/disabled status for each baseline policy item.
- Microsoft Purview compliance portal: https://purview.microsoft.com (or https://compliance.microsoft.com)
- Audit → Audit search.
- Confirm audit log is enabled (banner indicates state). If baseline expects
AuditLogEnabled=true, ensure active; enabling may be tenant-wide already.
- Defender portal: https://security.microsoft.com
- Policies & rules → Threat policies → Safe Attachments.
- Verify policy mode (e.g., Dynamic Delivery) per baseline.
- Safe Links: Policies & rules → Safe Links → Global settings & policies.
- Note values (e.g., Do not let users click through, Track click) where baseline enumerates.
- SharePoint admin center: https://admin.microsoft.com → Expand "Admin centers" → SharePoint.
- Policies → Sharing.
- Confirm external sharing level per baseline (e.g., "Existing guests only" or "New and existing guests").
- Teams admin center: https://admin.teams.microsoft.com
- Users → External access; confirm allowed domains settings.
- Users → Guest access; verify toggles (meetings, messaging) meet baseline.
- Entra admin center → Protection → Conditional Access → Authentication strengths.
- Confirm required authentication strength assigned to key policies.
- Baseline may define
RequirePhishingResistantMFA=true.
Some legacy flags may require Exchange Online PowerShell module:
Install-Module ExchangeOnlineManagement
Connect-ExchangeOnline
Get-OrganizationConfig | Select OAuth2ClientProfileEnabledIf baseline needs OAuth2ClientProfileEnabled true → ensures modern auth. Document results manually.
Use a tracking table (CSV or section in baseline) for manual verification notes:
| Service | Setting | Required | Observed | Verified By | Date |
|---|---|---|---|---|---|
| ModernAuthentication | EnableModernAuth | true | true | admin@example.com | 2025-11-13 |
| Reports | DisplayConcealedNames | true | true | admin@example.com | 2025-11-13 |
| Baseline Service (example) | Typical Portal | Navigation Path |
|---|---|---|
| ModernAuthentication | M365 Admin / Exchange | Settings → Org settings → Modern authentication |
| Reports | M365 Admin | Reports → Report settings |
| ConditionalAccessPolicies | Entra | Protection → Conditional Access → Policies |
| SecurityDefaults | Entra | Identity → Properties → Security defaults |
| SafeLinks | Defender | Policies & rules → Threat policies → Safe Links |
| SafeAttachments | Defender | Policies & rules → Threat policies → Safe Attachments |
| AuditLog | Purview | Audit → Audit search (status banner) |
| SharePointSharing | SharePoint Admin | Policies → Sharing |
| TeamsGuestAccess | Teams Admin | Users → Guest access |
| TeamsExternalAccess | Teams Admin | Users → External access |
Adjust names to match real baseline keys.
Consider enriching baseline JSON with helper metadata:
{
"ModernAuthentication": {
"EnableModernAuth": { "required": true, "portalPath": "Admin Center > Org settings > Modern authentication" },
"EnableAuthenticatedSMTP": { "required": false, "portalPath": "Admin Center > Org settings > Modern authentication" }
}
}Then extend script to parse objects with { "required": value, "portalPath": "..." } for automatic inclusion in HTML (future enhancement proposal).
| Symptom | Possible Cause | Action |
|---|---|---|
| Repeated consent prompts | Scope not admin-consented | Admin pre-consent via Entra → Enterprise applications → (App) → Permissions |
400 BadRequest on organization
|
Missing or insufficient scope / endpoint variant | Add Directory.Read.All or use GET https://graph.microsoft.com/v1.0/organization explicitly |
| Manual items inflate failure count | Using older script version | Update to latest (GUI items excluded from compliance) |
| HTML report shows 0 verifiable | All services lack implemented API retrieval | Implement endpoints in Get-ServiceSettings
|
| Token lacks scopes | Device code used on wrong account | Re-run auth ensuring correct tenant context |
- Store baseline file in version control; review changes via pull requests.
- Limit who can run assessment scripts (tokens expose read scopes).
- Avoid adding write/delete scopes unless strictly required.
- Rotate client secrets (if moving from device code to confidential client flow).
- Add setting in baseline file with required value.
- Run script → verify it appears as
MANUAL_CHECK_REQUIRED. - Implement retrieval logic + map to boolean/string.
- Test with debugging enabled.
- Validate HTML compliance now includes setting in API counts.
- Portal path metadata integration
- Export CSV / JSON structured output
- Parallel Graph calls for performance
- Authentication strengths & conditional access extraction (policy parsing)
- Support for multiple baselines (e.g., Essential Eight maturity levels)
Acquire updated token (if cached token expired):
pwsh ./asd-orgservices-get.ps1Decode token to verify scopes:
(Get-GraphAccessToken).AccessToken | Out-File token.txt
# Open https://jwt.ms and paste contents of token.txtRun with custom baseline:
pwsh ./asd-orgservices-get.ps1 -BaselinePath "https://raw.githubusercontent.com/yourorg/security-baselines/main/services.json"| Term | Definition |
|---|---|
| Verifiable Check | Baseline item retrieved & compared via API |
| Manual Check | Baseline item requiring GUI navigation or unsupported endpoint |
| Compliance % | (Passed Verifiable / Total Verifiable) * 100 |
| Baseline | JSON file defining required configuration values |
This framework does not guarantee complete coverage of all tenant security controls. Manual review remains essential for GUI-only settings until equivalent Graph surfaces are published or implemented.
Last Updated: 2025-11-13