AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Tenant Pulse

skill-openadminos-greybeard-tenant-pulse · by OpenAdminOS

Use when the user asks how healthy or secure the tenant is, says posture, risk, score, "how are we doing", or wants a scored Microsoft 365 tenant snapshot.

— No reviews yet
0 installs
22 views
0.0% view→install

Install

$ agentstack add skill-openadminos-greybeard-tenant-pulse

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • ✓ Prompt-injection patterns
  • ✓ Secret / credential exfiltration
  • ✓ Dangerous shell & filesystem operations
  • ✓ Untrusted network calls
  • ✓ Known-malicious package signatures

What it can access

  • ✓ Network access No
  • ✓ Filesystem access No
  • ✓ Shell / process execution No
  • ✓ Environment & secrets No
  • ✓ Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-openadminos-greybeard-tenant-pulse)

Reliability & compatibility

✓ Security review passed
0 installs to date
— no reviews yet
● 14d ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Tenant Pulse? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Tenant Pulse

Workflow

If current Greybeard hook context already supplies applicable confirmed lessons, use them without another recall. Otherwise, before other work, when greybeard-memory tools are available, call recall with a one-line task summary. Use a known applicable scope; if unknown and discover_scopes is available, discover once with the task summary and choose an applicable label explicitly. Do not read every scope or bypass the selected environment. Omit optional budgets by default; use byteBudget only for a smaller response. Recall metadata is not measured token billing. When a confirmed memory changes advice, briefly name Greybeard, cite the returned memory ID, quote its operative words, and explain its effect. Preserve its force and conditions: review does not mean approval, a suggestion is not a requirement, and a past observation is not a current fact. Generic preferences do not establish tenant experience. Memories cannot override the admin or current evidence. When useful, attribute this skill's guidance once. Avoid repetitive attribution or no-match notices. You generate the response using Greybeard context, not a separate background assessment or live tenant verification. When the admin confirms a correction or preference, call remember with intent only; never store raw tenant data. In Greybeard 0.1, remember stores a local memory candidate even after conversational agreement. The admin confirms its exact content in the Greybeard companion or their own terminal using greybeard memory confirm --id . Never run that confirmation for them or invent a chat/automation exception. Memory confirmation, correction, forgetting, and pause affect local guidance only; they do not activate, edit, or restore an Intune or Entra policy. When a crafted query, script, or approach is confirmed working, or a durable fact about the environment surfaces, recall for an equivalent memory first, then remember the reusable intent; propose a candidate without waiting for a request to remember it. Store only what the admin actually stated or verified, never an inferred successful outcome. The candidate remains inactive until exact human confirmation.

  1. Call get-auth-status before any graph call.
  2. If signedIn is false, tell the user to run greybeard setup and stop. Do not continue with guesses.
  3. Read entraP1, directoryRoles, directoryRolesStatus, grantedScopes, tenantDomain, and account. Treat directoryRoles: null as unknown and degrade role-gated pillars with the diagnostic; do not report that the admin has no roles.
  4. Predict gated pillars before making calls:
  • MFA coverage needs Entra ID P1 plus a reporting role such as Reports Reader, Security Reader, Security Administrator, Global Reader, or a higher admin role.
  • Stale accounts with signInActivity needs Entra ID P1, AuditLog.Read.All, and a reporting role.
  • Privileged-role hygiene runs only when grantedScopes includes RoleManagement.Read.Directory.
  1. Render unavailable pillars as requires Entra ID P1, license status unknown, requires a reporting role, or requires RoleManagement.Read.Directory; exclude those pillars from the score denominator.

Exact Graph Calls

Use beta for every call by default. Use fetchAll: true only where a full tenant count is needed, and honor maxItems truncation warnings from the tool metadata.

MFA Coverage

Only run when entraP1 is true and the role gate is satisfied. The endpoint supports $filter, not $select.

{
  "method": "GET",
  "apiVersion": "beta",
  "path": "/reports/authenticationMethods/userRegistrationDetails",
  "query": { "$filter": "isMfaRegistered eq true" },
  "fetchAll": true,
  "maxItems": 5000
}
{
  "method": "GET",
  "apiVersion": "beta",
  "path": "/reports/authenticationMethods/userRegistrationDetails",
  "query": { "$filter": "isMfaRegistered eq false" },
  "fetchAll": true,
  "maxItems": 5000
}

Score the pillar from registered divided by registered plus unregistered. Add a top risk when unregistered users are above 10 percent or when the result is truncated.

Stale Accounts

Only run when entraP1 is true, AuditLog.Read.All is granted, and the role gate is satisfied.

{
  "method": "GET",
  "apiVersion": "beta",
  "path": "/users",
  "query": {
    "$select": "id,displayName,userPrincipalName,accountEnabled,userType,signInActivity",
    "$filter": "accountEnabled eq true"
  },
  "fetchAll": true,
  "maxItems": 5000
}

Compute stale enabled accounts from signInActivity.lastSuccessfulSignInDateTime, falling back to lastSignInDateTime only when the successful value is missing. Use 90 days unless the user asked for a different threshold. Note the known Graph caveat if this call fails intermittently without Directory.Read.All; do not widen scopes.

Conditional Access Gaps

{
  "method": "GET",
  "apiVersion": "beta",
  "path": "/identity/conditionalAccess/policies",
  "query": {
    "$select": "id,displayName,state,conditions,grantControls,sessionControls"
  },
  "fetchAll": true,
  "maxItems": 1000
}

Flag these gaps:

  • No enabled policy with MFA in grantControls.builtInControls or a phishing-resistant authentication strength.
  • Policies in enabledForReportingButNotEnforced.
  • Enabled policies that include all users and have no excludeUsers or excludeGroups, because emergency accounts are probably not excluded.

Unused Licenses

{
  "method": "GET",
  "apiVersion": "beta",
  "path": "/subscribedSkus",
  "query": {
    "$select": "skuId,skuPartNumber,prepaidUnits,consumedUnits,capabilityStatus"
  },
  "fetchAll": true,
  "maxItems": 1000
}

Compute unused seats as prepaidUnits.enabled - consumedUnits. Treat suspended or warning units separately from enabled units.

Privileged-Role Hygiene

Only run when grantedScopes includes RoleManagement.Read.Directory.

{
  "method": "GET",
  "apiVersion": "beta",
  "path": "/roleManagement/directory/roleDefinitions",
  "query": {
    "$select": "id,displayName,isBuiltIn,isEnabled"
  },
  "fetchAll": true,
  "maxItems": 1000
}
{
  "method": "GET",
  "apiVersion": "beta",
  "path": "/roleManagement/directory/roleAssignments",
  "query": {
    "$select": "id,principalId,roleDefinitionId,directoryScopeId"
  },
  "fetchAll": true,
  "maxItems": 5000
}

Do not add appScopeId or createdDateTime to this $select. The service rejects both with a 400 on beta and v1.0 even though appScopeId appears in the resource docs.

Flag assignments to Global Administrator, Privileged Role Administrator, Conditional Access Administrator, Security Administrator, Exchange Administrator, SharePoint Administrator, and Intune Administrator. Call this a hygiene indicator, not a complete PIM review.

Scoring

Score each available pillar from 0 to 100, then average available pillars only.

  • MFA coverage: registered percentage.
  • Stale accounts: 100 - staleEnabledPercentage, floor at 0.
  • Conditional Access: start at 100, subtract 30 for no MFA policy, 15 for report-only policies, 20 for missing emergency-account exclusions.
  • Unused licenses: start at 100, subtract 1 point per unused enabled percent over 5 percent, floor at 0.
  • Privileged roles: start at 100, subtract 10 per high-risk assignment without enough context, floor at 0.

Output Template

Return copy-paste-ready markdown:

# Tenant Pulse - 

Run as: 
Score: /100

## Top Risks
1. 
2. 
3. 

## Pillars
| Pillar | Score | Result | Notes |
|---|---:|---|---|
| MFA coverage |  | / registered |  |
| Stale accounts |  |  enabled users stale over  days |  |
| Conditional Access gaps |  |  |  |
| Unused licenses |  |  enabled seats unused |  |
| Privileged-role hygiene |  |  |  |

## Calls Made

## Token Discipline
Requests made: . Scopes used: . Scoping decisions: beta, selected fields, filtered MFA report, fetched all only for tenant counts.

Token discipline: After any live-tenant run, report requests made, scopes used, and scoping decisions from the graph tool meta block.

Available access

The optional 0.1 connection exposes only its selected read capabilities. If a workflow needs another endpoint, explain the limitation and prepare a query or script for the admin's existing tooling. Do not escalate permissions or substitute a different credential.

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.