Technical takeaway for "Agent 365: If It's Not Built with Copilot Studio...Does It Still Work?"
Yes, within the boundaries demonstrated here. A normal n8n AI Agent remained responsible for reasoning, model calls, tool selection and responses. Microsoft Entra supplied its identity and Graph authorisation. Agent 365 supplied a registry entry and, after custom instrumentation, an Activity view containing real execution sessions.
Last verified: 3 October 2026. This is a conference proof of concept, not a production reference architecture or a claim of complete Agent 365 policy enforcement. The registry API used here is a /beta API. The observability adapter is a custom, version-specific integration. The optional AI Teammate investigation stopped at a Frontier/licensing gate.
- A genuine third-party agent can stay on n8n, including its normal AI Agent node.
- Its Graph tool can authenticate as a Microsoft Entra Agent Identity, using the blueprint's credential to obtain agent-scoped tokens.
- That existing identity can be registered in Agent 365 without creating a replacement identity.
- Actual n8n model and tool records can be exported to Agent 365. Five real sessions appeared in Microsoft 365 admin centre Activity.
- Registration, telemetry ingestion, UI reporting, policy enforcement and user distribution are separate capabilities.
- Teams-native AI Teammate functionality has additional identity, mailbox, licensing and Frontier prerequisites. It was not implemented here.
The business task is deliberately small: read at most five directory profiles whose display names begin with Demo. The tested tenant returned zero matching profiles. A successful, empty Graph response is valid evidence of the integration; it is not a populated contacts demonstration.
This repository is a sanitised public snapshot of the implemented workflow, infrastructure, guarded helpers and saved evidence. It is not a parameterised, one-command installer for an arbitrary tenant. Tenant, account, resource, workflow and telemetry identifiers have been replaced consistently with examples. The helpers must be retargeted to your own verified environment before use.
To reproduce the demo in a fresh tenant, follow the stages below and retarget the identified bindings in your own working copy. Do not remove the identity, permission or duplicate-detection guards merely to make a command proceed. Only the original tenant was tested end to end.
Reusable commands below use placeholders. Replace every <PLACEHOLDER> before running them. Commands that create resources, assign a role, configure n8n or deploy the adapter require an explicit review of their effects. None of the teardown procedures was exercised against the completed demo.
Example GUIDs use 00000000-0000-4000-8000-...; other identifiers use EXAMPLE_..., reserved .invalid domains or the documentation address 192.0.2.10. Trace/span identifiers are substituted too. Historical dates, measured durations, operation counts, statuses and routing outcomes are preserved; the example identifiers are not the original live correlation values. Public Microsoft application/role IDs, upstream source revisions and container image digests are retained where required to explain the integration.
No original Git history, protected local state or live deployment credentials are included. Review any values you add before committing them. The bundled Microsoft Agent 365 skills and Copilot instructions retain their upstream MIT licence; that notice does not assign a licence to the original demo code.
flowchart TD User["User in authenticated n8n chat"] --> Chat subgraph ACA["Azure Container Apps"] subgraph Runtime["n8n 2.41.6: the agent runtime"] Chat["Chat Trigger"] --> Agent["Normal n8n AI Agent"] Agent -->|selects tool| Tool["Fixed Graph HTTP Request Tool"] Tool -->|tool result| Agent Credential["Entra Agent ID credential"] -. authenticates .-> Tool Agent -. completed execution records .-> Hook["postExecute metadata hook"] end Hook -->|authenticated loopback handoff| Adapter["Separate observability collector"] end Agent <-->|inference only| Model["Azure OpenAI: gpt-4.1-mini"] Tool -->|GET users| Graph["Microsoft Graph"] Credential -->|FMI token exchange| Entra["Microsoft Entra token service"] Adapter -->|separate S2S token| Entra Blueprint["Agent Blueprint and Blueprint Principal"] -. parent relationship .-> Identity["Entra Agent Identity"] Identity -. Graph token actor .-> Graph Registry["Agent 365 registry"] -. links existing identity .-> Identity Identity -. telemetry attribution .-> Adapter Adapter -->|direct OTLP over HTTPS| Ingestion["Agent 365 observability ingestion"] Ingestion --> Activity["Microsoft 365 admin centre: Activity"]
Solid arrows show runtime communication or data flow. Dotted arrows describe identity relationships or captured execution metadata. The registry is not a proxy through which the Graph request passes.
PostgreSQL stores n8n configuration and encrypted credentials. Azure Files persists n8n data and community packages. The collector uses a dedicated subdirectory of that share. Neither the collector nor the execution hook calls a model, chooses a tool or generates an answer.
- n8n runtime: executes the workflow, owns the agent loop and returns the answer.
- Microsoft Entra Agent ID: the identity platform used to create and authenticate agent identities.
- Agent Blueprint: the parent application and credential boundary. Its client ID is not the runtime agent ID.
- Blueprint Principal: the tenant service principal corresponding to that blueprint.
- Agent Identity: the individual runtime principal, linked to the blueprint and an accountable sponsor. It receives the demo's Graph application role.
- Microsoft Graph access: the authorised directory read made by the n8n tool. It is not Work IQ or a Graph MCP call.
- Agent 365 registry: the management record linking the existing agent and blueprint. It does not execute the workflow or automatically instrument it.
- Agent 365 observability: separately authenticated telemetry attributed to the runtime Agent Identity.
- AI Teammate / Frontier: an optional experience with an Agent User, mailbox and Teams presence. These objects are absent from the baseline.
- Teams/Copilot distribution: packaging, channel integration, administrator policies and installation. A registry card alone supplies none of these.
The completed baseline used:
- n8n 2.41.6, upgraded from 2.25.7 without changing the four-node workflow. Version 2.41.6 was the stable release selected at the time, not a claim about today's latest version.
- Chat Trigger 1.4 -> AI Agent 3.1, with Azure OpenAI Chat Model 1 and HTTP Request Tool 4.4 attached as model and tool subnodes.
- Azure OpenAI
gpt-4.1-mini, model version2025-04-14, Standard deployment, capacity 10, credential API version2024-10-21. @astaykov/n8n-nodes-entraagentid@0.1.1, using itsentraAgentIDApicredential directly. No token-output Authentication Manager node.- One blueprint, its principal, one runtime Agent Identity and one verified Agent 365 registration, displayed as platform n8n.
- One runtime Graph application role:
User.Read.All. No delegated human token was used for the Graph tool. - Authenticated chat, presenter-IP-restricted ingress, an anonymous request rejected with HTTP 401, and successful-execution payload persistence disabled.
- Five real exported executions, 16 spans, and zero reported partial rejections.
The inspected Activity panel showed 5 successful sessions, 0 active users, 0 session exceptions and 0 hours. No individual model/tool run tree or tool-exception detail appeared. The five recorded turns total 11.573 seconds; the hours display is not precise timing evidence.
Sources: docs/runtime-validation.json, docs/observability-validation.json and the recorded UI observations in docs/demo-plan.md. The Activity screenshot was captured during the session, but is not included as an image file in this repository.
infra/ Bicep for the n8n host and inference-only model deployment
scripts/ Guarded Windows PowerShell deployment and integration helpers
workflows/ Native four-node n8n workflow export
observability/ Custom execution hook, OTLP mapper, collector and unit tests
docs/ Implementation decisions and saved validation evidence
.agents/skills/ Installed Agent 365 skill manifests and reference material
.github/ Copilot instructions, not a deployment pipeline
Important entry points:
- infra/main.bicep: Container Apps, networking, PostgreSQL, Azure Files and Log Analytics. It does not contain the removable observability sidecar.
- infra/model.bicep: Azure OpenAI account and model deployment only.
- scripts/Deploy-N8nHost.ps1:
Validate,WhatIf,Deploy; preserves stable hosting secrets using Windows DPAPI. - scripts/New-N8nAgentIdentity.ps1:
Plan,Create,Verify; creates or verifies the three intended Entra objects. - scripts/Initialize-N8nAccess.ps1:
Plan,Initialize; authenticates to an existing n8n owner account and creates or reuses one short-lived blueprint credential. - scripts/Configure-N8nWorkflow.ps1: package installation, token preflight, credential/workflow configuration, activation and native runtime tests.
- scripts/Register-N8nAgent.ps1:
Plan,Register,Verify,Reconcile; direct registry API integration with duplicate and permission-drift checks. - scripts/Deploy-N8nObservability.ps1:
Plan,Deploy,Reconcile,Evidence; additive collector deployment and evidence retrieval. - workflows/n8n-directory-agent.json: the real runtime definition, with placeholders for the two n8n credential IDs.
- observability/adapter.test.cjs: 12 focused tests using Node's built-in test runner and mocked remote services.
- docs/demo-plan.md: the detailed chronological implementation record, including earlier proposals and later decisions. A proposed control or fallback in that document is not evidence that it ran.
There is no root application package, azure.yaml, Docker Compose deployment, CI deployment workflow or installable Teams/Copilot package. The final host was deployed with the Bicep and PowerShell files above, not azd up. No CLI setup configuration, source-agent detection cache or Work IQ tooling manifest was fabricated for this workflow-based integration.
Protected operational state lives outside Git and outside the synchronised workspace, under a current-user-only directory below %LOCALAPPDATA%\n8n-agent365-demo\<SUBSCRIPTION_ID>. It contains DPAPI-encrypted secrets/session material and local identity, workflow, registration and deployment checkpoints. Do not commit, paste, distribute or import another person's copies. The ignore rules are defence in depth, not a substitute for reviewing files before publication.
These are tested versions, not invented minimum requirements:
| Tool | Version Used | Role in This Project |
|---|---|---|
| Git for Windows | 2.55.0.windows.3 | Source and skills checkout. |
| GitHub CLI | 2.102.0 | Local preparation; public Git cloning does not require it. |
| Azure CLI | 2.89.1 | Infrastructure deployment and explicitly scoped administrative operations. |
| Azure Developer CLI | 1.35.0 | Inspected for the upstream reference; not used to deploy the final host. |
| PowerShell | 7.6.6 | Windows helpers and Graph provisioning. |
| .NET SDK | 10.0.303, with a .NET 8 runtime available | Runs the Agent 365 CLI tooling. |
| Local Node.js / npm | 24.19.0 / 11.17.0 | Skills installer and collector tests. |
| Microsoft.Entra | 1.3.0 | Installed during preparation for the Entra n8n reference. |
| Microsoft.Graph.Authentication | 2.41.0 | Connect-MgGraph and Invoke-MgGraphRequest used by the helpers. |
| Agent 365 CLI | 1.1.226+9b233a7c52 | Inspected for registration, observability and publishing capabilities. |
The checked-in helpers require PowerShell 7 on Windows because they use Windows DPAPI, Windows ACLs and PowerShell 7 APIs. The installed skills describe .NET 8 or later for the CLI. The Entra n8n guide used during preparation specified Microsoft.Entra 1.2 or later; the actual helpers use Graph PowerShell instead. Use the tested Node 24 environment for local tests rather than assuming a lower version is supported.
VS Code with GitHub Copilot Agent Mode was used to inspect and implement the integration. Copilot and the skills are development aids, not dependencies of the deployed agent. Docker Desktop, AgentsPlayground, Python and a Microsoft Agent Framework runtime are not required by this implemented path.
Use an isolated Microsoft Entra workforce tenant in the global commercial cloud, with synthetic demonstration data and access to the relevant Agent 365 features. The registry /beta API is not a production-support commitment or evidence of sovereign-cloud availability.
Arrange separately:
- Identity provisioning: an accountable sponsor and an administrator with the supported Agent ID permissions. The documented blueprint-creation role is Agent ID Developer; lifecycle administration can require Agent ID Administrator. The demo used Global Administrator. That is a tested administrative context, not a requirement to give the runtime an administrator role.
- Consent: a Global Administrator or another role explicitly authorised for the requested operation. Do not assume Application Administrator can grant every Microsoft Graph application role.
- Azure deployment: permission to create the dedicated resource group and its resources. Contributor on an existing group can cover resource deployment; creating the group, registering providers or assigning RBAC roles can require additional subscription permissions. The demo used subscription Owner. No such role was assigned to the running agent.
- Model access: Azure OpenAI availability and sufficient
gpt-4.1-miniquota in Sweden Central. The helper also needs authorised access to retrieve the existing model account key without printing it. - Agent 365 observability: the quickstart used for this project required at least one tenant user assigned Microsoft 365 E7 or Microsoft Agent 365. A subscribed SKU without an assigned user was not treated as sufficient. This technical gate does not determine the commercial entitlement for every user or scenario.
- n8n: self-hosted operation with community packages permitted. Review n8n's current licence and edition terms; this demo does not depend on paid SSO or enterprise log streaming.
The tested tenant had Office 365 E5 and assigned Agent 365 licences. It did not have Microsoft 365 Copilot, E7 or an established Frontier AI Teammate entitlement. Those missing capabilities did not prevent the baseline directory read, registration or observed Activity sessions.
Keep MFA and Security Defaults enabled. Confirm that local client policy permits the intended interactive login, Graph consent and Agent 365 access. Advanced Defender/Purview capabilities have their own licensing, roles, onboarding and retention requirements; their availability was not proved by this demo.
Open a PowerShell 7 terminal and clone this repository into a development directory:
git clone https://github.com/AJ-Zafar/Agent-365-N8N-Demo.git n8n-agent365-poc
Set-Location .\n8n-agent365-poc
git status --short --branchThe original project started as a local repository with empty infrastructure, script and documentation folders. This public repository begins with a sanitised snapshot, not the original deployment repository's history. The saved source and dated evidence are the reference; the public commit is not a claim that another tenant was deployed or tested.
Inspect the plan, templates and scripts before connecting to a tenant. Keep a private record of the new tenant ID, subscription ID, administrator UPN, sponsor object ID and dedicated resource group. These PowerShell variables are used in the examples below:
$TenantId = '<TENANT_ID>'
$SubscriptionId = '<SUBSCRIPTION_ID>'
$ResourceGroup = '<RESOURCE_GROUP>'
$AdminUpn = '<ADMIN_UPN>'
$PresenterIpCidr = '<PRESENTER_PUBLIC_IPV4>/32'These variables do not override constants inside the helpers. Retarget in stages:
- Before hosting and identity creation, update the fixed tenant, subscription, group, administrator and sponsor bindings in the relevant helpers. Keep the ownership tag
project=n8n-agent365-democonsistent with the templates and deployment guard. - After deployment and identity creation, use the returned host/model names and independently read blueprint object/client IDs, principal ID and agent object/client IDs in the access and workflow helpers. Check nested account/resource-name assertions as well as the constants at the top.
- Change the workflow's
allowedOriginsto the new HTTPS n8n origin. Review its demonstration-specific prompt text and use an appropriate webhook ID for your instance. Keep the four-node architecture, query restrictions and credential-ID placeholders. - After the role grant and workflow creation, bind the registration helper to the returned workflow ID, Graph resource service-principal ID and actual assignment ID. Do not copy the original assignment ID. The helper expects one n8n agent and one runtime role and refuses an unexpectedly empty catalogue; review these assumptions for the new environment without treating a failed read as an empty inventory.
- Before observability deployment, update
TARGETin observability/telemetry.cjs and the fixed targets in scripts/Deploy-N8nObservability.ps1. The collector is intentionally scoped to exactly one workflow and identity.
Several original helper checks assume the returned blueprint object/client IDs are equal, and likewise for the agent. That was verified in the demo, not assumed by the platform model. If your returned IDs differ, those checks and their API/token uses need a reviewed adaptation. Do not substitute an object ID where a client ID is required.
Keep original evidence distinct from new results. Do not edit historical outcome flags to make a fresh environment appear provisioned. The repository has no central configuration generator or general-purpose retargeting command.
The verified method was a separate checkout of Microsoft's skills repository followed by its Node.js installer, invoked from the target project. From this repository's root, for a sibling checkout that does not already exist:
Push-Location ..
git clone https://github.com/microsoft/agent365-skills.git
Pop-Location
node ..\agent365-skills\scripts\install.jsReview the upstream installer before running it. It uses the current working directory as its target, copies skill manifests and references into the project's skills directory and configures VS Code discovery. It can also configure other detected development clients. Existing skill manifests are skipped rather than guaranteed to be upgraded; preserve customisations when reviewing a newer version.
The installed skills directory contains eight skills: a365-setup, make-a365-agent, make-ai-teammate, instrument-observability, add-workiq-tools, purview-dlp-integration, a365-code-validator and test-local.
The working VS Code workspace setting was:
{
"chat.agentSkillsLocations": {
".agents/skills": true
}
}The local VS Code settings directory is ignored by Git. A fresh clone therefore needs discovery configured locally, for example by running the installer. Reload VS Code with Developer: Reload Window, select Agent mode in Copilot Chat and confirm that the expected skills are offered. The original inspection found no Agent 365 skills despite another Microsoft agent-development skill being installed; product names are not interchangeable.
The CLI installation command recorded in local history was:
dotnet tool install -g Microsoft.Agents.A365.DevTools.Cli
a365 --version
a365 --helpIf already installed, inspect its version instead of attempting a second installation. The tested output was 1.1.226+9b233a7c52; installing an unpinned package later may produce different behaviour. The Windows .NET global tools directory must be on PATH; reopen the terminal after installation if necessary.
Microsoft.Entra was installed during preparation with:
Install-Module Microsoft.Entra -Scope CurrentUser -Force -AllowClobberThe helpers require the Graph authentication commands. Verify their availability separately:
Get-Module -ListAvailable Microsoft.Entra, Microsoft.Graph.Authentication |
Select-Object Name, Version
Get-Command Connect-MgGraph, Invoke-MgGraphRequest, Set-MgRequestContextIf Graph.Authentication is absent, use its official installation guidance in the references; do not assume the presence of Microsoft.Entra proves that every required Graph command is loaded.
Do not run the skills' complete onboarding flow blindly against this repository. They primarily scaffold editable .NET, Node.js and Python agent applications. n8n being implemented in Node.js does not make workflow JSON such an application. In this project the skills informed decisions; the guarded helpers and custom integration implemented the baseline. No false detection cache or CLI setup-completion state was created.
Start with a read-only view of the existing selection:
az account show --query '{tenantId:tenantId,subscriptionId:id,user:user.name}' --output jsonIf no valid session exists, or you intentionally need the selected demonstration tenant, the successful interactive login pattern was:
az login --tenant $TenantId --allow-no-subscriptionsComplete Windows/browser sign-in and MFA yourself. For a fresh environment, deliberately select the approved subscription if it was not selected by login:
az account set --subscription $SubscriptionId
az account show --query '{tenantId:tenantId,subscriptionId:id,user:user.name}' --output jsonThen verify all three values before each writing stage. The helpers do not log in or silently switch context. --allow-no-subscriptions supports tenant authentication, but this Azure-hosted demo still requires an Azure subscription. VS Code's Azure selection and the Azure CLI selection were not assumed to be identical.
Use the same PowerShell process for Graph sign-in and the subsequent helper invocation. The successful provisioning path used the default Microsoft Graph PowerShell client, by omitting -ClientId:
Set-MgRequestContext -MaxRetry 0
Connect-MgGraph -TenantId $TenantId -Environment Global -ContextScope Process `
-Scopes 'AgentIdentityBlueprint.Create', `
'AgentIdentityBlueprint.AddRemoveCreds.All', `
'AgentIdentityBlueprintPrincipal.Create', `
'AgentIdentity.Create.All', 'User.Read'
Get-MgContext | Select-Object Account, TenantId, ClientId, AuthType, ContextScope, ScopesThese are delegated provisioning permissions, not permissions granted to n8n. First consent can add the provisioning client's enterprise application and delegated grants. That client is not another runtime Agent Identity.
Setting MaxRetry before sign-in matters for the tested Graph.Authentication version. Changing it after authentication cleared the SDK's cached HTTP client during the failed registration attempt and triggered an authentication-construction error. The registration helper now checks the setting without resetting the working client.
- WAM can display a native sign-in dialog behind other windows. Complete the actual prompt rather than treating silence as a failed API operation.
- A cancelled or interrupted sign-in was recovered through a fresh explicit-tenant interactive login. Do not clear unrelated accounts or credentials as routine troubleshooting.
- Device-code fallback failed with
AADSTS530035under Security Defaults. Do not disable MFA or Security Defaults to make it work. - Reusing the Microsoft Agent 365 CLI application's client ID with
Connect-MgGraphfailed withAADSTS65002. Broader tenant consent is not a fix for first-party API preauthorisation. Use the documented Graph PowerShell client for these helpers. Set-MgGraphOption -DisableLoginByWAMexisted in the installed module, but did not disable WAM for its default client. It is not a verified browser-login workaround for this project.- A successful process-scoped login in your terminal is not available inside another PowerShell process. Do not export tokens into chat or repeat login solely to bridge processes.
No verified numerical Windows work/school-account limit or account-removal workaround was established. This guide does not recommend deleting Windows work accounts.
Microsoft's Entra n8n guide links to the community-maintained n8n Azure Container Apps reference. This project reused the hosting and Agent ID authentication approach, but did not execute its unmodified deployment hooks.
The inspected upstream automation also provisioned an Agent User and SPA, enabled Graph MCP, requested broader permissions and printed credentials. Those actions were outside this demo. An upstream azd up or azd provision can execute those hooks, even when the intention is only to deploy infrastructure.
The hosting template creates a dedicated North Europe environment with:
- A Consumption Container App, HTTPS ingress to n8n port 5678, one main container at 1 vCPU / 2 GiB, single-revision mode and 0-1 replicas.
- An ingress allowlist for exactly the presenter's public IPv4
/32. Both the n8n editor and chat also require authentication. - A virtual network, delegated Container Apps and PostgreSQL subnets, and private PostgreSQL DNS.
- PostgreSQL Flexible Server 16, Burstable
Standard_B1ms, 32 GB, seven-day backup retention, no high availability and no public database access. - A Standard LRS storage account and SMB Azure Files share, mounted persistently for n8n. Storage access is restricted to the Container Apps subnet.
- Log Analytics with 30-day retention and a 0.1 GB/day ingestion cap. These platform logs are not Agent 365 semantic telemetry.
The model template deploys Azure OpenAI in Sweden Central, with a public HTTPS endpoint and key authentication. It is inference only, not a Foundry project or hosted agent. North Europe lacked usable model quota during setup; a gpt-4o-mini attempt was rejected as retired, and gpt-4.1-mini was then selected and validated.
The planning estimate was US$20-30/month for light hosting, excluding tax and model usage, not a quote or spending cap. PostgreSQL accounted for about US$17.19 per 730-hour month at the observed rate. Database, storage and other charges continue when n8n scales to zero. The later collector adds active compute. No Azure budget or hard spending cap was configured.
After retargeting the host helper and confirming the selected account, inspect whether the dedicated resource group already exists. Do not reuse an unrelated group:
az group show --name $ResourceGroup --subscription $SubscriptionId --output jsonOnly if it is confirmed absent and creation is approved:
az group create --name $ResourceGroup --location northeurope `
--subscription $SubscriptionId `
--tags project=n8n-agent365-demo purpose=conference-demo --output jsonValidate and preview the host before deploying it:
.\scripts\Deploy-N8nHost.ps1 -Mode Validate -PresenterIpCidr $PresenterIpCidr
.\scripts\Deploy-N8nHost.ps1 -Mode WhatIf -PresenterIpCidr $PresenterIpCidrThese modes do not deploy resources, but the helper can initialise protected local hosting secrets and creates a temporary parameter file. It deletes that temporary file afterwards. Retain the protected stable n8n encryption key and database password; losing them is not permission to regenerate keys against an existing host.
Review the preview and then perform the approved deployment:
.\scripts\Deploy-N8nHost.ps1 -Mode Deploy -PresenterIpCidr $PresenterIpCidrThe model was deployed separately with these command forms:
az deployment group what-if --name n8n-model --resource-group $ResourceGroup `
--subscription $SubscriptionId --template-file .\infra\model.bicep `
--result-format ResourceIdOnly --no-pretty-print --query changes --output jsonAfter reviewing region, quota, model and cost:
az deployment group create --name n8n-model --resource-group $ResourceGroup `
--subscription $SubscriptionId --template-file .\infra\model.bicep `
--query '{state:properties.provisioningState,outputs:properties.outputs}' --output jsonRecord the deployment outputs privately and use them to retarget the next-stage helpers. Open the n8n URL from an allowed address, complete owner setup in the browser and verify readiness:
$N8nUrl = 'https://<N8N_HOST>'
Invoke-RestMethod -Method Get -Uri "$N8nUrl/healthz/readiness"Do not expose the owner-setup page by temporarily removing the IP restriction. If the presenter's IP changes, make an explicitly reviewed allowlist change.
After observability is installed, do not redeploy the base host template for routine ingress or scaling changes. The base template excludes the adapter and can remove its containers, settings or secret configuration. Preserve the complete live template and use a scoped change instead.
The intended topology is one blueprint, one blueprint principal and one Agent Identity for this workflow. The administrator was the blueprint owner/sponsor and Agent Identity sponsor. Live checks found no separate owners on the principal or Agent Identity. Do not generalise those observed relationships into universal platform defaults.
The creation helper inventories existing objects before writing, reuses exact name/parent matches and stops on ambiguity or conflicts with checkpointed identifiers. From the Graph-authenticated terminal, after retargeting its administrator and sponsor bindings:
.\scripts\New-N8nAgentIdentity.ps1 -Mode PlanAfter approving the three intended objects:
.\scripts\New-N8nAgentIdentity.ps1 -Mode Create
.\scripts\New-N8nAgentIdentity.ps1 -Mode VerifyCreation uses these Graph resource types: agentIdentityBlueprint, agentIdentityBlueprintPrincipal and agentIdentity. It checkpoints after each successful creation. It does not grant Graph access, generate a secret, create an Agent User or register the agent in Agent 365.
The first live Create invocation completed object creation but failed during final read-back. Verify recovered the existing objects without creating duplicates. An interrupted command is not evidence that no object exists.
The demo explicitly approved Microsoft Graph application User.Read.All on the Agent Identity service principal, not the blueprint or human provisioning client. This is the least application permission documented for listing users, but it is tenant-wide. $filter, $select and $top reduce the tool's output; they do not narrow the permission itself.
This grant was a separately reviewed, one-off Graph REST operation using the administrator's existing Azure CLI authentication. There is no runtime-role-grant mode in the checked-in helpers. Have an authorised administrator inspect the target identity, blueprint relationship, current assignments and the tenant's Microsoft Graph resource principal before approving the grant. Discover the Graph resource principal by its well-known application ID 00000003-0000-0000-c000-000000000000; select its enabled User.Read.All role with Application in allowedMemberTypes.
The request shape used was:
POST https://graph.microsoft.com/v1.0/servicePrincipals/<AGENT_OBJECT_ID>/appRoleAssignments
Content-Type: application/json{
"principalId": "<AGENT_OBJECT_ID>",
"resourceId": "<GRAPH_RESOURCE_SERVICE_PRINCIPAL_ID>",
"appRoleId": "<USER_READ_ALL_APPLICATION_ROLE_ID>"
}Authenticate through the approved administrative client without exposing its token. The Graph sign-in scopes in Step 3 create identities and manage blueprint credentials; they do not imply authority to assign application roles. Do not put an administrator token into an n8n credential, request broader runtime roles after a 403, or grant all permissions declared on a blueprint as a shortcut.
Read back the assignment collection and record the returned assignment ID for the registration helper's guard. The tested runtime had exactly one assignment. The later token preflight must show the Agent Identity as actor, a Graph audience, User.Read.All and no delegated scp claim.
Retarget the access helper with the verified IDs and host, and complete owner setup before invoking it:
.\scripts\Initialize-N8nAccess.ps1 -Mode Plan
.\scripts\Initialize-N8nAccess.ps1 -Mode Initialize -OwnerEmail '<N8N_OWNER_EMAIL>'Enter the n8n password only at the hidden terminal prompt. The helper never creates or resets the owner. It authenticates the owner, verifies package-management access and creates or reuses one 30-day blueprint credential. The credential and n8n session cookie are stored as DPAPI-protected SecureStrings outside the repository. The owner password is not persisted.
Missing, expired, ambiguous or incompletely saved credentials cause a stop, not silent rotation. Plan deliberate renewal before expiry and update every consumer consistently. The original credential was due to expire on 1 November 2026; that historical date is not a reusable credential.
After retargeting the workflow helper, run the stages in this order:
.\scripts\Configure-N8nWorkflow.ps1 -Mode Plan
.\scripts\Configure-N8nWorkflow.ps1 -Mode Inspect
.\scripts\Configure-N8nWorkflow.ps1 -Mode InstallPackage
.\scripts\Configure-N8nWorkflow.ps1 -Mode InspectNodes
.\scripts\Configure-N8nWorkflow.ps1 -Mode VerifyAccess
.\scripts\Configure-N8nWorkflow.ps1 -Mode ConfigureCredentials
.\scripts\Configure-N8nWorkflow.ps1 -Mode ValidateWorkflow
.\scripts\Configure-N8nWorkflow.ps1 -Mode CreateWorkflow
.\scripts\Configure-N8nWorkflow.ps1 -Mode ActivateWorkflowReview Plan before the writing modes. InstallPackage installs the pinned community package. VerifyAccess performs the app-only token exchange and a real fixed Graph read. ConfigureCredentials stores the Entra and Azure OpenAI credentials in n8n's encrypted store. CreateWorkflow creates an inactive workflow; ActivateWorkflow publishes its authenticated chat endpoint. The helper reuses recorded objects and refuses unrecorded conflicts.
ValidateWorkflow makes no network calls, but requires the earlier protected identity/workflow state and bound credential IDs. It is not a standalone fresh-clone validator. The helper uses n8n's version-tested editor REST endpoints and an owner session, not a public API-key integration; those endpoints can change across releases.
The Graph tool is fixed to:
GET https://graph.microsoft.com/v1.0/users
$select=id,displayName,mail
$top=5
$filter=startsWith(displayName,'Demo')
The model cannot choose another URL, method, body or filter. Redirects are disabled. The credential's authentication implementation obtains and attaches the Agent Identity token internally. The separate Authentication Manager node from the reference was excluded because it emits an access token in ordinary node output.
The workflow limits the agent to three iterations, model output to 512 tokens, model requests to 60 seconds with one retry, the Graph tool to 30 seconds and the workflow to 120 seconds. It disables file uploads, previous-session loading, highlighted-data auto-saving and execution-content persistence. public: true exposes the hosted chat route; n8nUserAuth still protects requests.
Sign in to n8n, open the editor/chat URLs returned by activation and send:
Check the live directory for Demo profiles. Use the directory tool and report how many matching profiles it returns. Do not infer anything about other users.
Then run the saved test procedure:
.\scripts\Configure-N8nWorkflow.ps1 -Mode TestWorkflowThis is a live, potentially billable test. It checks anonymous rejection, invokes a read turn and a write-request refusal, and updates the runtime evidence file. If anonymous access is unexpectedly accepted, the helper deactivates the workflow before continuing; it is not an entirely read-only test.
The evidence must contain an actual list_demo_profiles entry in the AI Agent's intermediateSteps and a Graph-shaped observation containing value. A convincing answer alone, or a separate successful HTTP request, is insufficient. The tested answer truthfully reported no matching profiles. A request to create an account was refused.
For a later repeat run, RenewSession can extend only the local cache after the existing server session is accepted as the same owner:
.\scripts\Configure-N8nWorkflow.ps1 -Mode RenewSessionIf server-side authentication has expired, use the access helper's private login prompt instead. Do not export cookies or create an API key as a shortcut.
An Entra Agent Identity can obtain authorised Graph tokens without having an Agent 365 registry record. Conversely, a registry record says nothing about successful Graph access or telemetry delivery.
The project already had a working blueprint, principal and Agent Identity. The direct Agent Registry API explicitly accepts agentIdentityBlueprintId and agentIdentityId, so it could register those objects without another provisioning flow.
The installed CLI's registration-only retry relied on prior CLI setup state that this project did not have. Rerunning full setup or fabricating generated configuration would have risked changing the identity/permission boundary. The chosen helper instead performed a guarded registry POST and verified that Entra inventories and runtime grants were unchanged.
Use an intentionally consented Graph PowerShell session with these four delegated administrative permissions:
Set-MgRequestContext -MaxRetry 0
Connect-MgGraph -TenantId $TenantId -Environment Global -ContextScope Process `
-Scopes 'User.Read', 'CopilotPackages.Read.All', `
'AgentRegistration.Read.All', 'AgentRegistration.ReadWrite.All'
Get-MgContext | Select-Object Account, TenantId, ClientId, AuthType, ContextScope, ScopesConfigure retries before this deliberate sign-in, not halfway through a working registration request. Complete required consent yourself. These scopes belong to the provisioning client; the agent retains its existing application User.Read.All role.
Retarget the registration helper's identity, workflow and assignment checks, then run:
.\scripts\Register-N8nAgent.ps1 -Mode PlanPlan verifies the Graph context and enumerates catalogue pages, checking names and identifiers for existing matches. It writes a local preflight report, not a registry record. In the original tenant it examined 379 catalogue packages and found no matching n8n entry. 379 is historical evidence, not a required catalogue size.
After reviewing the preflight and approving the registry-only write:
.\scripts\Register-N8nAgent.ps1 -Mode Register
.\scripts\Register-N8nAgent.ps1 -Mode VerifyThe helper submits one request to POST https://graph.microsoft.com/beta/copilot/agentRegistrations. Its metadata supplies the existing blueprint and Agent Identity, the agent as sourceAgentId, originatingStore: n8n, and the approved owner/creator. It adds no executable tool or alternate runtime.
The successful project registration returned HTTP 201 on 2 October 2026 at approximately 23:36 UTC. Verification then established:
- The returned registration ID maps to the intended blueprint, identity, source ID, name and owner.
- Catalogue enumeration contains exactly one matching entry, and package-detail read-back links it to the existing Agent Identity.
- Before/after blueprint, principal and Agent Identity inventories match.
- Sponsors, owners, blueprint configuration and the sole runtime Graph assignment are unchanged.
The catalogue list's blueprint field was null. Blueprint linkage was proved through registry read-back, not invented from that null field. Returned registry, object and client IDs must be recorded separately even where their values happen to coincide.
The API POST is not treated as inherently idempotent. Safety comes from complete preflight reads, a local lock, a durable attempt checkpoint, disabled automatic POST retries and read-back. An uncertain outcome blocks another POST.
For the failure encountered here, these existing modes recovered diagnostics without repeating the write:
.\scripts\Register-N8nAgent.ps1 -Mode Reconcile
.\scripts\Register-N8nAgent.ps1 -Mode Reconcile -ReadCatalogThe first mode is local-only and preserves sanitised diagnostics from the same terminal. The second adds authenticated catalogue reads. The one-off successful recovery used -ResumeAfterAuthFailure, but that switch requires the exact recorded pre-request UriFormatException, successful no-match reconciliation and unchanged inventories. It is not the normal registration command or a general timeout/HTTP-error retry mechanism.
Open Microsoft 365 admin centre, then Agents > All agents. Select the registry/inventory view offered by the tenant and search for the configured agent name. The original entry was named n8n-agent365-demo-agent.
Verify the platform n8n, the configured name, owner/creator and Entra Agent ID against your own returned identifiers. Open the details page rather than trusting a similarly named card. The observed status was Available, while Channel and Last published had no configured value.
This establishes administrative registry visibility, not publication to Agent Store or user installation. The entry's static description still said that observability was not configured, because it was written during registration and was not changed later. Treat that text as historical metadata, not a live exporter health check.
The saved runtime evidence's RegistrationAdded: false and ObservabilityAdded: false mean that the test helper did not add those capabilities. They do not contradict the later registration and adapter deployment.
Registration alone did not populate Activity. n8n's Microsoft Agent 365 Trigger has its own embedded agent/executor and telemetry path. It is not a transparent entry point to the existing separate AI Agent node. Using it as a replacement would have changed the architecture being demonstrated.
Instead, the project preserved the native workflow and used n8n 2.41.6's workflow.postExecute external hook. A metadata-only live probe established the actual execution shape, including the Chat Trigger session, two model-call records and a Graph tool record.
This is custom instrumentation for this workflow and tested n8n version. It is not built-in Agent 365 observability for arbitrary n8n AI Agent workflows. It does not imply coverage of other workflows, queue workers or future node versions.
- observability/n8n-hook.cjs reads completed engine records and sends a bounded envelope to
127.0.0.1:4319, authenticated with a dedicated handoff key. It has a 1.5-second timeout and fails open without changing the workflow result. - observability/telemetry.cjs maps one measured workflow turn to an
invoke_agentroot, each observed model call tochat, and each observed tool call toexecute_tool. Child spans share the trace and parent ID. Missing session/timing information is not replaced with fabricated data. - observability/collector.cjs runs in a separate Node sidecar. It authenticates handoffs, obtains an app-only export token and sends OTLP/HTTP+JSON to Microsoft's documented endpoint. It checks per-span routing and partial rejections, not just HTTP 200.
The adapter uses built-in Node.js APIs and Microsoft's direct OpenTelemetry integration contract. It does not install @microsoft/opentelemetry, manually instrument a second agent framework or rely on automatic SDK discovery of the n8n workflow. The Microsoft OpenTelemetry documentation and installed observability skill were references, not the deployed exporter implementation.
Export uses:
https://agent365.svc.cloud.microsoft/observabilityService/tenants/<TENANT_ID>/otlp/agents/<AGENT_CLIENT_ID>/traces?api-version=1
The collector uses the existing blueprint credential to obtain an FMI assertion for the Agent Identity, then an Agent Identity client_credentials token for api://9b975845-388f-4429-889e-eab1ef63949c/.default. Graph and observability tokens have different audiences and are not interchangeable.
For every span, gen_ai.agent.id must identify the authenticated runtime Agent Identity, not the blueprint, administrator, provisioning application or Agent User. The blueprint is recorded separately in microsoft.a365.agent.blueprint.id. The channel is truthfully web.
The live export token had the correct tenant, agent and audience, no delegated scp claim and an empty roles array. Under the documented S2S model, this registered blueprint agent required no additional Agent365.Observability.OtelWrite role. Do not add an observability permission or change the Graph grant just to imitate a different AI Teammate authentication path.
After updating the workflow/identity/host bindings, validate locally:
node --test .\observability\adapter.test.cjsThese tests use fixtures and mocked remote endpoints. They are not synthetic live Agent 365 telemetry. No root npm install is required for the adapter.
Preview and then, after approval, deploy:
.\scripts\Deploy-N8nObservability.ps1 -Mode Plan
.\scripts\Deploy-N8nObservability.ps1 -Mode DeployPlan reads the existing Container App and requires the protected, verified registration checkpoint. Deploy adds a collector at 0.25 vCPU / 0.5 GiB and a one-shot installer using a digest-pinned Node 24 Alpine image. The installer verifies artifact hashes and exits before n8n starts. The collector mounts only its own subdirectory and drops to UID/GID 1000.
Two Container App secret entries are added: a copy of the existing blueprint credential for the collector and a dedicated local handoff key. These are Azure secret copies, not new Entra credentials or grants. The n8n container receives the hook key; the collector receives the blueprint secret. No public collector ingress is added.
Deploy returns after submitting a revision, not after proving telemetry. Check the new revision and readiness, then reconcile the submitted deployment:
az containerapp show --name '<CONTAINER_APP_NAME>' --resource-group $ResourceGroup `
--subscription $SubscriptionId `
--query '{state:properties.provisioningState,latest:properties.latestRevisionName,ready:properties.latestReadyRevisionName}' `
--output json
Invoke-RestMethod -Method Get -Uri "$N8nUrl/healthz/readiness"
.\scripts\Deploy-N8nObservability.ps1 -Mode ReconcileReconcile verifies the expected ready revision, artifact hashes, unchanged n8n configuration, scale, volumes, ingress and secret names. It updates a local checkpoint; it does not redeploy. Do not use it as proof that an execution was exported.
The adapter captures execution/session IDs, timestamps, durations, actual operation types, statuses, available model token counts and sanitised errors. It does not capture prompts, responses, directory profiles, tool arguments/results, human user IDs, client IP addresses or hidden reasoning. It does not fabricate an output_messages event or identify the sponsor as the caller.
Some of those omitted fields appear in Microsoft's complete reporting requirements. Accepted metadata and session counts therefore do not establish complete schema compliance, store certification or every Activity feature.
The collector has a 16-run in-memory queue, bounded duplicate suppression and at most 50 persisted evidence summaries. It is not a durable delivery spool and provides no exactly-once guarantee. Queue overflow, export failures, container termination or a process dying before postExecute can lose telemetry. A failed collector handoff does not block a successful agent answer.
Run a real native workflow test and retrieve its export evidence:
.\scripts\Configure-N8nWorkflow.ps1 -Mode RenewSession
.\scripts\Configure-N8nWorkflow.ps1 -Mode TestWorkflow
.\scripts\Deploy-N8nObservability.ps1 -Mode EvidenceUse RenewSession only when the existing server session is still valid. TestWorkflow performs model inference and Graph reads. Evidence reads collector logs, or its persisted summaries after a restart, and updates docs/observability-validation.json. It never replays spans. In a fresh tenant, separate or replace the original historical evidence deliberately; do not merge it into a claim about your own runs.
| Execution | Measured Calls | Turn Duration | Result |
|---|---|---|---|
| 10 | 2 model, 1 tool | 3,977 ms | Successful Graph read; 4 spans. |
| 11 | 1 model, no tool | 684 ms | Read-only refusal; 2 spans. |
| 12 | 2 model, 1 failed tool | 3,098 ms | Genuine handled tool error; 4 spans. |
| 13 | 2 model, 1 tool | 3,176 ms | Graph access restored; 4 spans. |
| 14 | 1 model, no tool | 638 ms | Post-restoration refusal; 2 spans. |
All 16 spans were reported as sent to each of the response's flashpoint, sentinel and esp destinations, with partialSuccess.rejectedSpans: 0. These are service response labels. They are not proof of an independently queried Defender or Sentinel workspace, nor 48 separate agent operations.
The failure was produced by temporarily enabling n8n's built-in SSRF protection and blocking graph.microsoft.com with N8N_SSRF_BLOCKED_HOSTNAMES. The normal AI Agent genuinely attempted the same tool call. Its measured NodeApiError said:
The request was blocked because the destination hostname is restricted
This was a local n8n egress denial, not a fabricated Graph 403 and not an Agent 365 enforcement action. No runtime permission or identity was disabled. The prior Container App template was restored in a finally block, the temporary settings were removed, and execution 13 proved that Graph access worked again.
The tool span was Error, while the root remained Success because the agent handled the failure and completed its response. Changing the root to Error to obtain a UI exception count would misrepresent what happened.
There is no checked-in fault-injection command or helper mode. To repeat this experiment, first prepare a separately reviewed maintenance procedure that captures the full current template, applies only the temporary egress settings, runs the real tool, restores the template even on failure and proves Graph recovery. Do not copy a partial template over an instrumented deployment or revoke tenant-wide access for a stage demonstration.
At approximately 07:13-07:16 UTC on 3 October 2026, the existing agent's Activity tab with Past 30 days selected showed:
- Sessions: 5, with the chart reporting five successful sessions.
- Active users: 0, and no active-user data. The hook had no verified human Entra caller to attribute.
- Exceptions: 0. The tooltip defined these as sessions that encountered errors; the handled child-tool failure did not become a failed session.
- Agent run-time: 0 hrs, despite 11.573 seconds of measured turns.
- Last activity: 3 October 2026.
- No individual session IDs, model/tool drill-down, token counts or exception detail in the inspected panel.
Five displayed sessions matched the five distinct exported sessions by count, but the UI did not expose IDs for per-session correlation. No Defender hunting query or alternative UI was used to establish more detailed visibility.
Allow for indexing delay. The working guidance was approximately 15-90 minutes, not a measured SLA or promise. Check exporter routing first, then the right agent identity, time range and Activity detail. A stale inventory row showing zero or Never is not equivalent to the loaded Activity panel.
- No Copilot Studio agent or orchestration.
- No Microsoft Foundry project or hosted agent. Azure OpenAI supplied inference only.
- No Microsoft Agent Framework runtime or replacement LLM/tool loop.
- No replacement of the ordinary n8n AI Agent with the Microsoft Agent 365 Trigger.
- No AI Teammate, Agent User, mailbox or Teams-native agent account.
- No Work IQ, Graph MCP, mail, calendar or SharePoint tool.
- No new runtime identity or Entra permission for observability.
- No claim that registry blocking automatically stops n8n or invalidates cached tokens.
- No implemented Purview DLP gate, Defender incident demonstration or rehearsed Entra lifecycle-control test.
A separate comparison workflow using the Microsoft Agent 365 Trigger 1.1 was investigated after the baseline succeeded. Its implementation is still n8n-hosted, but connects model/tools directly to the trigger's embedded executor. It is not the unchanged four-node architecture above.
The proposal kept the baseline intact and would have used a separate workflow, callback, credential boundary and preferably a separate blueprint. The AI Teammate instance flow would provision its own Agent Identity and Agent User, with licensed mailbox and Teams presence. Such presence does not by itself grant mail-reading tools.
Preflight stopped because:
- The live Copilot Frontier setting was No access, with Save disabled.
- The tenant had E5 and Agent 365, but no Microsoft 365 Copilot, E7 or Frontier for AI Teammates SKU in the inspected inventory.
- Accepted Agent 365 Frontier terms and an available AI Teammate licence pool were not established.
- The inspected documentation required feature-specific Frontier enrolment and licensing. Potential TAP exceptions were not established and were not assumed.
The existing n8n ingress admitted only the presenter. Teams could not simply reach a new webhook through that restriction. A path-restricted relay or another appropriately authenticated public callback was proposed, not implemented. Opening the editor to the internet was not accepted as a workaround.
No additional workflow, blueprint, identity, Agent User, mailbox, package, grant, licence assignment or Azure setting was created for this investigation. No terms were accepted. The existing registry entry cannot simply be installed in Teams to bypass these prerequisites.
These concepts must remain separate:
- Registry visibility: administrators can inventory and inspect the registered identity.
- Sharing: a supported authoring/runtime experience gives a named audience access. It does not establish catalogue publication.
- Availability: policy determines who can discover or acquire an eligible app/agent.
- Installation: a supported package is installed for users in its host experience.
- Agent Store: discovery and acquisition through the supported catalogue route, not an automatic consequence of a registry API record.
- Teams/Copilot distribution: a supported manifest/package, channel endpoint, identity/authentication and tenant installation policies.
- AI Teammate: the separate Agent User/Frontier experience, not a synonym for every Teams bot or custom-engine agent.
The existing entry's Users panel exposed the tabs, but reported:
- Installed for: "Shared agents can't be automatically installed."
- Available to: "Shared agents don't have availability settings" and "Shared agents aren't available in the agent store or Microsoft 365 Copilot Chat."
- Shared with: "The owner hasn't shared this agent."
The detail page had no configured channel or publication date. Its Available status was not evidence of a usable Teams/Copilot installation. No sharing, availability or installation setting was changed.
The installed a365 publish --help described packaging, but its matching implementation was more specific:
- Blueprint-based non-AI-Teammate publishing returned "Nothing to publish for blueprint-based agents." Registration belonged to setup, not packaging.
- The ordinary app-based non-AI-Teammate packaging branch was marked not implemented.
- The AI Teammate branch generated a manifest package for subsequent manual upload.
Therefore CLI 1.1.226 could not package this existing blueprint-based n8n registration as an installable app. No publish or publish dry-run was executed. In that source revision, the AI Teammate dry-run path could extract templates before returning, so it was not used as a read-only capability probe.
Microsoft documents a standard route for bringing an existing agent to Copilot. A messaging-only adapter using Microsoft 365 Agents SDK could authenticate channel activities, forward requests to n8n and return its answer. This SDK is distinct from Microsoft Agent Framework; using its transport layer need not move reasoning out of n8n.
That extension would require an appropriate bot/channel registration, normally a channel app registration/service principal, a reachable authenticated HTTPS callback and a Microsoft 365/Teams ZIP package with the app manifest and icons. Copilot support adds copilotAgents.customEngineAgents linked to the personal-scope bot, plus the appropriate supported scopes. A separate declarative-agent manifest or an Agent 365-hosted runtime is not the requirement for that ordinary route.
Reuse of this specialised Entra Agent Identity as the channel's bot identity was not established. Preserve the existing identity for the baseline workload and telemetry rather than assuming its ID can be pasted into a bot manifest. The current hosted-chat webhook is not a Bot Framework activity endpoint.
An eligible standard package could be sideloaded for a test user or submitted through the organisation's supported distribution route, subject to policy and host entitlement. It need not create a Frontier Agent User or mailbox. No such adapter, package or end-to-end Teams/Copilot test exists in this repository.
The simpler test-user option is the existing n8n web chat after arranging appropriate n8n account/workflow access and allowed network access. It remains web chat, not a Teams/Copilot installation. No account provisioning or network change was performed for that option.
- Use a non-production tenant and synthetic directory records. A non-empty synthetic fixture was not created or tested here; do not broaden the query to real users just to populate the demo.
- Keep provisioning permissions separate from runtime permissions. The administrator's Graph scopes and Azure rights do not belong to the agent.
- Preserve the sole intended Graph application role. Fixed URL/method/query restrictions are defence in depth, not row-level authorisation or user-specific permission trimming.
- Inventory before creating identities or registrations. Keep checkpointed IDs and investigate ambiguous outcomes instead of rerunning setup.
- Keep passwords, keys, cookies, tokens and protected state out of Git, chat, model input, execution output, screenshots and logs. DPAPI protects local storage, not a compromised running process.
- Preserve the stable n8n encryption key. Plan secret renewal explicitly across n8n credentials and the collector; do not rotate only one consumer.
- Review the pinned community credential code as a supply-chain dependency. Avoid token-output nodes and expression-based bearer credentials visible in workflow data.
- Keep HTTPS, validated PostgreSQL TLS, restricted ingress, private database access and limited execution retention. The model endpoint remains public/key-authenticated in this demo; private model networking was not implemented.
- Avoid verbose authentication logging and raw request/response dumps. Even redacted exceptions and execution identifiers require an appropriate retention and publication policy.
- Do not treat telemetry as a preventative control. Direct Graph REST calls are not automatically intercepted by Work IQ or Purview policies because the agent is registered.
- Remember token caching and propagation. Removing a grant or disabling an identity is not guaranteed to stop an already-issued token, running workflow or model request immediately.
The first inspection found no Agent 365 skills. The working fix was the separate skills checkout, Node installer and object-shaped VS Code discovery setting in Step 2. Confirm the eight installed manifests and reload the window. An array in place of the settings object is not the verified configuration. The installed skill count is eight even where upstream summary text describes an older count.
The CLI was initially absent. The recorded global .NET tool installation resolved it. Verify a365 --version and the global tool path in a new terminal. Do not run setup merely to check whether the CLI executable exists.
Check for a hidden native prompt and select the intended tenant account. Confirm the actual Azure CLI and Graph contexts independently. Follow the explicit-tenant interactive route in Step 3. Do not default to device code in a tenant that blocks it, promise that the tested Graph WAM-disable flag works, or clear unrelated Windows work accounts.
An Agent Identity and working User.Read.All do not authorise an administrator's registry API session. The helper requires User.Read, CopilotPackages.Read.All, AgentRegistration.Read.All and AgentRegistration.ReadWrite.All on the Graph provisioning client. A 403 or missing-scope check is not evidence that the registry is empty. Stop, inspect the current tenant/client/scopes and obtain the specifically approved consent.
The recorded failure originated while Graph.Authentication constructed an interactive credential, not from a confirmed registry rejection. Retry configuration was moved before fresh sign-in. Reconcile captured diagnostics and checked the catalogue before the narrow, explicitly approved recovery. Do not use the recovery flag for an unrelated error or delete the attempted-write checkpoint.
The helpers are fixed-target tools. Review every binding described in Step 1 and the returned IDs. Do not pass nonexistent -TenantId or -SubscriptionId parameters to them, weaken duplicate checks, overwrite checkpoints or generate a second identity to get past the guard.
Check the presenter's IP against the allowlist, the app revision/readiness and the authenticated owner session. RenewSession validates the existing session before updating the local cache; it cannot revive an invalid server-side session. Use the private login prompt when necessary. Never recover owner access by deleting the database.
North Europe lacked usable quota, and a gpt-4o-mini deployment was rejected as retired. The approved model-only deployment moved to Sweden Central and used gpt-4.1-mini. Check current model version, region, deployment type and quota, then review a new what-if. Do not assume a historically valid model remains deployable indefinitely.
An Azure template PATCH was accepted with an empty response body. The helper was corrected to read live state; Reconcile verified the submitted revision without another deployment. Earlier chunked exec uploads hit throttling and were replaced with the hash-checked init installer. Do not repeat writes simply because response parsing failed or retry uploads aggressively through a throttle.
Check actual postExecute capture, correct workflow binding, token actor/audience, registration and licence assignment before waiting for indexing. Require per-span routing and zero rejected spans; HTTP 200 alone is insufficient. Open the correct Agent Identity's Activity detail and an appropriate date range. Registration metadata or cached inventory counters are not exporter health signals.
Zero active users is consistent with this adapter's omitted human attribution. The real handled tool error did not become a session exception. No per-model/tool drill-down appeared in the inspected UI. These are observed limits, not reasons to manufacture a caller or failed root span.
Read the actual Users tabs and the publishing findings above. Registry visibility, sharing, package publication and installation are different operations. Neither another registry POST nor a365 publish --use-blueprint supplies the missing channel integration in the tested version.
Destructive, separately authorised work. Not performed or end-to-end tested in this project. Preserve any required evidence and backups first. Use exact recorded identifiers, not name prefixes or a tenant-wide cleanup script. Deleting Azure resources does not delete Entra objects or unregister an agent.
Confirm the selected tenant/account and inspect the resource group before any deletion:
az account show --query '{tenantId:tenantId,subscriptionId:id,user:user.name}' --output json
az group show --name $ResourceGroup --subscription $SubscriptionId `
--query '{name:name,location:location,tags:tags}' --output json
az resource list --resource-group $ResourceGroup --subscription $SubscriptionId `
--query '[].{name:name,type:type,id:id}' --output tableVerify the expected ownership tag, exact resource IDs, dependants and retention obligations. Stop if anything is shared or unrecognised. In n8n, deactivate the demo workflow and verify its chat route is no longer usable. The configuration helper has no teardown/deactivation mode; use n8n's authenticated management UI or a separately reviewed exact-workflow API operation.
Use the clean 2.41.6 RollbackTemplate recorded by the observability deployment helper. Compare it to the current live template so later unrelated changes are not lost. Restore only properties.template, with an empty initContainers list and a unique new revision suffix. Verify the clean revision is ready before removing the two adapter-owned Azure secret entries, a365-hook-key and a365-blueprint.
Then remove the dedicated adapter artifacts/evidence only under the agreed retention policy. Do not remove the original Entra blueprint credential, Graph/model credentials, n8n encryption key, identity or registry entry just to remove telemetry.
No -Mode Remove is implemented. Do not use a blind base-template redeployment as an adapter-removal command. Removing the adapter is also not an n8n downgrade: the 2.41.6 database migrations ran. A downgrade would require separately planned database recovery, not pointing 2.25.7 at the migrated database. A point-in-time restore was not rehearsed.
The documented delete API requires authorised AgentRegistration.ReadWrite.All and is irreversible. In an appropriately authenticated Graph terminal, read the entry first:
$RegistryId = '<AGENT_REGISTRATION_ID>'
$RegistryUri = 'https://graph.microsoft.com/beta/copilot/agentRegistrations/{0}' -f $RegistryId
Invoke-MgGraphRequest -Method GET -Uri $RegistryUri -OutputType PSObject |
Select-Object id, displayName, agentIdentityId, agentIdentityBlueprintId, ownerIdsCompare the returned values with the privately recorded demo inventory. Only after explicit approval of that exact record:
if ((Read-Host 'Type DELETE DEMO REGISTRY to confirm the reviewed record') -cne 'DELETE DEMO REGISTRY') {
throw 'Deletion not confirmed.'
}
Invoke-MgGraphRequest -Method DELETE -Uri $RegistryUriRead back the registry/catalogue and Entra inventories afterwards. Do not assume registry deletion cascades into identity deletion, or that remaining inventory entries justify another broad deletion.
Only delete identities this demo intentionally created and that no other workflow uses. Inspect each exact object, its parent, owners/sponsors, children, credentials and grants first. Stop the runtime and deliberately revoke the demo's recorded blueprint credential and application-role assignment as appropriate. Already-issued tokens can remain usable until expiry.
The documented typed Graph delete paths are:
DELETE https://graph.microsoft.com/beta/servicePrincipals/<AGENT_OBJECT_ID>/microsoft.graph.agentIdentity
DELETE https://graph.microsoft.com/beta/applications/<BLUEPRINT_OBJECT_ID>/microsoft.graph.agentIdentityBlueprintThese are operation references, not an automatic cleanup sequence. Their documented delegated permissions are respectively AgentIdentity.DeleteRestore.All and AgentIdentityBlueprint.DeleteRestore.All, with the required ownership/administrative role. Obtain any cleanup permission through a separate administrator decision; do not grant it to the runtime agent.
Remove the dedicated Agent Identity first. Check the blueprint's remaining instances and the blueprint principal before deleting the blueprint; inspect any surviving principal and remove it only through the supported management operation once dependency and ownership checks pass. Verify actual cascade behaviour rather than assuming it. The referenced identity delete APIs describe a 30-day restore window, but that is not a tested recovery procedure for this demo.
Never delete shared Microsoft Graph, Graph PowerShell, Agent 365 CLI or other Microsoft service principals. Do not remove shared provisioning consent or subscriptions just because they were used during this setup. There is no Agent User, mailbox or SPA from the baseline to clean up.
After the ownership/dependency checks above, confirmation that backups/data can be removed and explicit approval:
az group delete --name $ResourceGroup --subscription $SubscriptionIdThis command intentionally retains the CLI confirmation prompt. Do not add --yes, force-deletion options or a tenant-wide loop. Resource-group deletion removes the n8n deployment and its dedicated database, files, logs, network and model resources; it does not revoke Entra credentials or remove the registry record. Review soft-deleted/retained resources and any remaining costs afterwards.
Only after cloud cleanup, credential revocation and any retention requirements are satisfied, remove this demo's protected local checkpoint/secret directory from the machine that owns it. Do not delete another subscription's state or shared Azure/Graph sign-in caches. Removing a local cookie file does not revoke a copied server-side session.
Keep source code and approved sanitised evidence. Do not run azd down or generic a365 cleanup against this project: neither was its deployment/identity-state owner, and no corresponding setup state was created. Verify that no dedicated running resources, registrations, identities or unintended grants remain.
Aim for eight minutes, with authentication, consent, deployment and at least one indexed real session prepared beforehand.
- 0:00-1:30 - Show n8n. Open the four-node workflow. Explain that the normal AI Agent owns the reasoning and tool loop; Azure OpenAI is just its model endpoint.
- 1:30-3:00 - Invoke Graph. Send the Step 6 prompt. Show the actual
list_demo_profilesinvocation and truthful empty result, without opening credential values or personal directory data. - 3:00-4:00 - Show the registered agent. Open Microsoft 365 admin centre > Agents > All agents and match the n8n entry to the Agent Identity. Explain blueprint versus runtime identity and registry versus identity creation.
- 4:00-5:30 - Refresh Activity. Show the previously indexed, dated sessions. If the new run has not appeared yet, show its accepted export evidence and label the existing Activity data as earlier runs.
- 5:30-6:45 - Explain the value and limits. Demonstrate attributable inventory and observed execution. State that zero active users, zero session exceptions and no tool drill-down are the actual UI results, not hidden successes.
- 6:45-8:00 - Explain the boundary. The handled error span was accepted, but this is not proof of preventative governance. Explain the blocked Frontier AI Teammate path and why registry visibility is not Teams installation.
Do not perform first-time setup, licence assignment, fault injection or an unrehearsed identity-disablement experiment on stage. Use a clearly labelled recording or saved real evidence if the host, model or indexing service is unavailable. Never substitute a mocked response and describe it as a live Graph call.
Sources inspected for the implementation and its boundaries:
- Microsoft Entra: secure an n8n agent with Agent ID and the community Azure reference. The implementation review used reference commit
ff49c867ace7d7f7ae7f05038588d6bac520eba0, not an unreviewed latest deployment hook. - Create an Agent Identity Blueprint and register existing agents; Microsoft Graph: list users.
- Agent 365 integration options, quickstart and licensing gates, and service description.
- Agent 365 skills documentation, skills repository and Node installer. The local installed manifests are available under .agents/skills.
- Microsoft Graph PowerShell installation and Security Defaults device-code restrictions.
- Create an Agent Registry record and list catalogue packages.
- Microsoft OpenTelemetry for Agent 365, direct OTLP integration, observability authentication and attribute requirements.
- n8n 2.41.6 release, execution lifecycle hook source and SSRF protection configuration.
- n8n Microsoft Agent 365 Trigger, credential documentation and Microsoft's n8n sample.
- Agent 365 Frontier and AI Teammate instance creation.
- CLI publish reference and its version-matched source; bringing existing agents to Copilot, enabling Copilot support in a Teams app and test-user sideloading.
- Teardown references checked for this guide, not executed: delete a registration, delete an Agent Identity and delete a blueprint.
| Capability | Result | Notes |
|---|---|---|
| Third-party runtime | Proven | n8n's normal AI Agent retained the model/tool loop. |
| Entra Agent ID | Proven | Blueprint, principal and runtime Agent Identity verified. No Agent User. |
| Agent 365 registry | Proven | Existing identity registered and read back without duplicates. |
| Microsoft Graph access | Proven | Real fixed directory read with application User.Read.All; zero matches. |
| Agent 365 observability | Proven, custom integration | Real model/tool records exported; 16 spans accepted with zero partial rejections. |
| Activity sessions | Proven, aggregate only | Five successful sessions displayed; zero active users and session exceptions. |
| Detailed tool telemetry in UI | Not demonstrated | Accepted tool/error spans did not produce an inspected UI drill-down. |
| Teams AI Teammate | Not implemented | Optional native-trigger path stopped at Frontier/licensing preflight. |
| User distribution | Not implemented | Registry entry was not installable; a standard channel adapter remains future work. |