Skip to main content

Microsoft Agent 365

Microsoft Agent 365 answers which agents exist, who owns them, and what they may access. Beak answers how they behave at runtime. This integration connects the two, in both directions:

  • Inbound — agents in your Agent 365 / Entra Agent ID estate are synced into Beak, where they can be onboarded into the Agent Registry and governed like any other agent.
  • Outbound — any Beak agent can add or update custom properties on an Agent 365 agent. Those properties are Entra custom security attributes on the agent's service principal, which a Conditional Access policy can filter on.

The second direction is the point of the integration: it lets a Conditional Access policy act on a behavioural signal that only Beak can produce.

Beak's own use of that channel is the Agent Risk Score — a consolidated 0-100 score per governed agent, written to each linked service principal so a Conditional Access policy can enforce on it. Configure the credential below first; the risk sync settings are stored alongside it.


Prerequisites​

Register an Entra ID application in your own tenant and grant it:

PermissionUsed for
Application.Read.Allreading the agent inventory
CustomSecAttributeAssignment.ReadWrite.Allwriting custom properties
CustomSecAttributeAssignment.Read.Allreading them back
IdentityRiskyServicePrincipal.Read.Alloverlaying Entra's own risk state (optional)

Grant admin consent for each. You also need a custom security attribute set defined in Entra to hold the properties Beak writes — the default name Beak uses is Beak.

Warning: The Graph permission alone is not enough. Writing custom security attributes also requires the application to hold the Attribute Assignment Administrator directory role. Without it every write fails with 403, even though the API permission is consented and shows as granted.


Configuration​

Go to Integrations → Microsoft Agent 365 (see Integrations).

FieldMeaning
Application (client) ID, Directory (tenant) ID, Client secretYour Entra app registration. The secret is encrypted at rest with your tenant's own key and is never returned by the API — leave it blank when editing to keep the stored one.
Agents filter / Agent tagNarrows the inventory to actual agents. Agents filter is pre-filled with a sensible default — see below before changing or clearing it.
Onboarding modeStaged (default) or Auto — see Onboarding.
Attribute set (advanced)The Entra custom security attribute set Beak writes into. Must already exist in Entra. Default Beak.
Property prefix (advanced)Every writable property name must start with this. Default beak.
Agents URL / Risky agents URL (advanced)Only for tenants whose Graph exposes a different collection. Leave blank otherwise.

Use Test connection before relying on it. The result reports how many service principals came back and — importantly — whether the query was narrowed. Sync now runs a single inventory pass immediately.

Note: Everything on this screen is also available over the API — PUT /aisecurity/agent365 to save the credential and options, POST /aisecurity/agent365/test and .../sync for the two actions. Useful for scripted onboarding across tenants.


Where to find everything​

Two places, and the split is deliberate.

Agents Beak already governs live in the Agent Registry — the single inventory for every governed agent, whatever estate it came from. An onboarded Agent 365 agent appears there beside your natively registered ones, carrying a Source of Agent 365 and the Entra service principal object ID it is known by there. Filter the registry by source to see only these; the Agent 365 page links straight to that filtered view.

Agents Beak has found but is not yet governing live on the Agent 365 page under Configure, which has three tabs:

TabWhat it holds
DiscoveryCandidates from the last sync. Promote one with Onboard, or Ignore to exclude it. Once governed it drops off this tab — the Agent Registry is where it lives.
Write grantsThe cross-agent write authorizations. Create and revoke them here.
Property writesThe audit trail — every attempt, including refusals.

Note: The Agent Registry shows Governed agents by default. Switch its Governance filter to Discovered or Excluded to see candidates there too — they are in the same list, just not adopted. The Agent 365 page's Discovery tab is the workflow surface for acting on them.

Warning: Governance state is not the same as Status. Status (Active / Inactive / Suspended) says whether an agent Beak governs may act right now. Governance state says whether Beak governs it at all. A Discovered agent is not a suspended agent — it is not yet one of yours, and it cannot authenticate regardless of what Status says.

Every one of these carries the standard dashboard controls: search and filters that cover the whole dataset rather than the page on screen, per-column filters and sorting, a column picker that remembers your selection, CSV and PDF export matching the filters you have applied, and an auto-refresh interval. The audit tab adds a time range.


Narrowing the inventory to agents​

Agent 365 agents are Entra service principals, and the directory they live in is mostly not agents — a typical tenant holds hundreds of enterprise applications and app registrations alongside a handful of agents.

Beak narrows the inventory for you by default. The Agents filter is pre-filled with:

servicePrincipalType eq 'ServiceIdentity'

Entra types an Agent 365 or Copilot Studio agent identity as ServiceIdentity, while ordinary applications and app registrations are Application. Because that is a Microsoft-defined value rather than a convention your tenant chose, it is safe to apply without knowing anything about your directory. On the first tenant this ran against it took the inventory from 353 service principals to the one real agent.

You can replace it with any OData filter, or clear it to get the unnarrowed directory back. Narrowing options:

  • Agents filter — an OData filter evaluated by Graph. Defaulted as above; replace it if your tenant identifies agents some other way.
  • Agent tag — a tag Beak matches client-side, for tenants where the server-side filter is unavailable. Not set by default: unlike servicePrincipalType, an agent tag genuinely varies by tenant and rollout stage, so guessing one would silently match nothing.

If the filter matches nothing, Test connection says so explicitly rather than reporting a bare zero — it distinguishes "narrowed and matched nothing" from "this tenant has no agents", and names the filter so you can widen it.

With the filter cleared, the sync returns every service principal in your tenant. Beak states that rather than hiding it: Test connection and every sync report whether the query was narrowed, and describe the count as service principals rather than agents. The Agent 365 page shows a banner while the inventory is unnarrowed.

Note: This matters most for Auto mode, which onboards everything the sync discovers. Against an unfiltered directory that would register every enterprise application in your tenant as a Beak-governed agent in a single tick. Beak therefore refuses to auto-onboard an unfiltered inventory and falls back to discovery only, logging why. Set a filter — or, if governing the entire directory is genuinely intended, opt in explicitly with allow_unfiltered_auto_onboard.


Onboarding: Staged vs Auto​

ModeBehaviour
Staged (default)The sync discovers agents into a queue. An administrator promotes each one deliberately.
AutoThe sync onboards every newly discovered agent itself — provided the inventory is narrowed.

Every synced agent gets an entry in the Agent Registry the moment it is found — so the registry really is one list — but the entry carries a governance state saying whether Beak has adopted it:

  • Discovered — present in Agent 365, not yet adopted. It cannot authenticate: Beak will refuse a request that claims this agent's identity, because nobody has decided to govern it yet.
  • Governed — adopted, governed at runtime, and able to authenticate. This is what the Agent Registry shows by default.
  • Excluded — deliberately kept out of adoption. Like a candidate, it cannot authenticate.

Excluded is honoured in both modes. Switching a tenant from Staged to Auto never resurrects an agent that was previously excluded.

An agent's Beak profile ID is derived from its Entra service principal object ID, so re-syncing never creates a duplicate. Adopting one moves its governance state to Governed and mirrors Microsoft's own enabled flag into its Status, rather than assuming it should be live.

The sync runs hourly; Sync now runs a single pass immediately. Promote a discovered agent with Onboard, or exclude it with Ignore.


Custom properties​

Who may write​

Because these properties feed Conditional Access, writing one changes what Microsoft will allow that agent to reach. Four conditions must hold before Beak calls Graph:

  1. the target is an agent Beak has actually synced from your inventory — not an arbitrary object ID,
  2. every property name sits inside your configured prefix,
  3. the caller is authorised for that target, and
  4. where a grant names specific properties, it covers every property in the request.

An agent may always write to its own linked service principal. Writing to a different agent's properties requires an explicit write grant.

That restriction is the point: without it, one agent could lower another agent's beakRiskScore and disarm the Conditional Access policy meant to contain it. A grant can be narrowed to named properties — for example allowing beakGuardrailStatus but not beakRiskScore.

Writing from an agent​

POST /aisecurity/agent365/properties
Content-Type: application/json
x-agent-signature: <base64 Ed25519 signature>
x-agent-timestamp: 2026-08-13T09:00:00Z
x-agent-nonce: <unique per request>

{
"agent_id": "my-agent",
"object_id": "11111111-2222-3333-4444-555555555555",
"properties": { "beakRiskScore": "72", "beakGuardrailStatus": "blocked" }
}

object_id is the agent's Entra service principal object ID. It is shown on the agent's row in both the Discovery tab and the Agent Registry's Source column, and is the same value Entra shows for that service principal.

Warning: A signed request is mandatory here. This endpoint requires a valid Ed25519 attestation even when the agent's profile does not otherwise require attestation. An agent with no active signing key is refused with 403. Generate a keypair from the Agent Registry first.

Two behaviours worth knowing:

  • Values are always stored as strings. Conditional Access application filters only match string-typed attributes, so a score written as a number would be saved successfully and then never matched by the policy meant to consume it. Beak converts for you.
  • Writes merge. Properties you do not name keep their current value, so add-or-update is a single call. Send null as a value to remove a property.

Writing as an administrator​

Open an agent from the Discovery tab, or from the Agent Registry once it is governed, and use Details to view and edit its properties. The editor refuses a name outside the allowlisted prefix before you can submit, and clearing a value removes the attribute rather than storing an empty string. Requires the agents:update permission. Grants do not apply to an administrator, but the inventory and prefix rules still do.

Audit​

Every attempt is recorded on the Property writes tab — including refused ones. A repeatedly denied write is a meaningful security signal in its own right, so the trail would be misleading if it held only successes.

Each record keeps the property's previous value, because Graph itself keeps no attribute history: what Beak does not capture at write time is unrecoverable.


Acting on the properties in Conditional Access​

  1. Confirm the attribute appears on the agent's service principal in Entra.
  2. Create a Conditional Access policy and scope it using filter for applications on your attribute, for example beakRiskScore.
  3. The policy now acts on Beak's runtime verdict.

The attribute must be string-typed and must live on the service principal. Conditional Access cannot filter on a user's custom security attributes, so writing to the wrong object type produces a policy that silently never matches.


Troubleshooting​

SymptomLikely cause
403 mentioning Attribute Assignment AdministratorThe application lacks the directory role. The API permission alone is not sufficient.
403 Write denied: … no write grantA cross-agent write without a grant. Add one, or write to the agent's own service principal.
403 … outside the allowlisted … namespaceThe property name does not start with your configured prefix.
403 … has no active signing keyGenerate a keypair for the calling agent.
401 Attestation failedMissing or invalid signature, a replayed nonce, or clock skew beyond five minutes.
403 … not in this tenant's synced Agent 365 inventoryRun a sync, or check the service principal object ID.
The sync returns far more agents than expectedNo filter is configured, so the query returns every service principal.
Auto mode onboards nothingThe inventory is unfiltered, so auto-onboarding was withheld by design. Set a filter.
The attribute is set but Conditional Access never firesThe value is not string-typed, or it is on the wrong object.

Best Practices​

  • Do not clear the filter before you set Auto. Auto against an unnarrowed inventory is the one combination that can flood your Agent Registry, which is why Beak refuses it. The default filter means this is already handled; if you replace or clear it, confirm the count with Test connection before switching modes.
  • Start in Staged. Even with a filter, run one sync in Staged and read the queue before promoting. It is the cheapest way to discover that your filter is wrong.
  • Grant to the agent that governs, not to every agent. An agent needs no grant to write to its own service principal. Reserve explicit grants for the one or two agents whose job is to score others, and narrow each grant to the properties it actually sets.
  • Name properties for what they mean, not what set them. beakRiskScore reads clearly in a Conditional Access filter; beakAgentXOutput does not, and the policy author is rarely the person who wrote the agent.
  • Treat denied writes as a signal. Filter the Property writes tab to denied — a rising count of refusals against one agent is worth reading — a correctly configured agent does not repeatedly attempt writes it has no grant for.
  • Re-check the attribute after the first write. Confirm in Entra that the value landed on the service principal and is string-typed, before building a Conditional Access policy that depends on it.