<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-5285970135510371565</id><updated>2026-09-07T09:55:23.658-04:00</updated><category term="Events"/><category term="Article"/><category term="Troubleshooting"/><category term="Functionality"/><category term="Around the Blogosphere"/><category term="Code"/><category term="Dexterity"/><category term="Installation"/><category term="SQL Scripting"/><category term="Convergence"/><category term="Web Client"/><category term="Best Practices"/><category term="GPUG"/><category term="SQL Server"/><category term="Training"/><category term="Technical Conference"/><category term="Dynamics GP 12"/><category term="Dynamics GP 2013"/><category term="Integration"/><category term="VBA"/><category term="Roadmap"/><category term="Sales Order Processing"/><category term="Architecture"/><category term="Dynamics GP 2010"/><category term="Support Debugging Tool"/><category term="Visual Studio Tools"/><category term="Integration Manager"/><category term="PowerApps"/><category term="Reporting"/><category term="Compliance"/><category term="Azure"/><category term="Dynamics GP 2015"/><category term="PowerAddicts"/><category term="Security"/><category term="Inventory"/><category term="Modifier"/><category term="Dynamics GP 2010 R2"/><category term="General Ledger"/><category term="Power Platform"/><category term="Purchasing"/><category term="MVPs"/><category term="Report Writer"/><category term="C#"/><category term="Dynamics GP 11"/><category term="Payables Management"/><category term="Power Automate"/><category term="Microsoft Office"/><category term="SmartList and SmartList Builder"/><category term="Amplify"/><category term="Dynamics GP 2016"/><category term="Flow"/><category term="Payroll"/><category term="Word Templates"/><category term="Dynamics GP 2018"/><category term="Receivables Management"/><category term="Upgrades"/><category term="DevOps"/><category term="SQL Reporting Services"/><category term="VBScript"/><category term="GPPC"/><category term="DEX.INI"/><category term="From the Newsgroups"/><category term="System Manager"/><category term="Business Central"/><category term="Extender"/><category term="PowerPlatform"/><category term="VB.NET"/><category term="Windows 8"/><category term="eConnect"/><category term="Home Page"/><category term="Field Services"/><category term="Season Greetings"/><category term="Service Architecture"/><category term="The Technology Corner"/><category term="Visual Studio 2008"/><category term="Visual Studio Team Services"/><category term="Community"/><category term="Human Resources"/><category term="Management Reporter"/><category term="Power Apps"/><category term="Workflow"/><category term="Business Analyzer"/><category term="COM"/><category term="Corporate Performance Management"/><category term="Decisions Conference"/><category term="Fixed Assets"/><category term="Maintenance"/><category term="OLE Container"/><category term="Power BI"/><category term="SharePoint"/><category term="Source Code Control"/><category term="reimagine"/><category term="ALM"/><category term="CRM"/><category term="Macros"/><category term="Navigation Pane"/><category term="PowerShell"/><category term="Product Feedback"/><category term="Tax Updates"/><category term="Web Services"/><category term="Intelligent Edge"/><category term="Internet Explorer"/><category term="Project Accounting"/><category term="Bank Reconciliation"/><category term="Best of Series"/><category term="Books"/><category term="Copilot Studio"/><category term="FRx"/><category term="Humor"/><category term="MVP Summit"/><category term="Named Printers"/><category term="Table Import"/><category term="Visual Studio"/><category term="Windows 10"/><category term="AI Builder"/><category term="Ad Campaigns"/><category term="Cognitive Services"/><category term="Dexterity Shared Components"/><category term="Dynamics 365"/><category term="Dynamics NAV"/><category term="EFT"/><category term="Electronic Banking"/><category term="Encumbrance"/><category term="Line Sequence Number"/><category term="MOSS"/><category term="Manufacturing"/><category term="Mixed Reality"/><category term="Office Web Components"/><category term="UI flows"/><category term="Windows 7"/><category term="Analysis Cubes"/><category term="Analytical Accounting"/><category term="Automated Solutions"/><category term="Business Alerts"/><category term="Database Maintenance Utility"/><category term="Docker"/><category term="Document Attach"/><category term="Fargo Floods"/><category term="Internet Information Services"/><category term="MCP"/><category term="Menus for Visual Studio Tools"/><category term="Multicurrency Management"/><category term="Office 365"/><category term="Online Services"/><category term="Power Virtual Agents"/><category term="Printer Compatibility"/><category term="Silverlight"/><category term="Software Development Kits"/><category term="XBox"/><category term="AL language"/><category term="Augmented Reality"/><category term="Bing Maps Enterprise"/><category term="Bots"/><category term="Chrome"/><category term="Connect"/><category term="Continuum"/><category term="DEXSQL"/><category term="Dataverse"/><category term="DocWatch"/><category term="Drill-Down Builder"/><category term="Flic"/><category term="GP Power Tools"/><category term="IMHO"/><category term="Kinnect"/><category term="Newsgroups"/><category term="Notes"/><category term="OpenXML"/><category term="Performance"/><category term="PowerPlatform PowerAddicts"/><category term="Professional Services Tool Library"/><category term="Robotic Process Automation"/><category term="SafePay"/><category term="Service Call Management"/><category term="Team Foundation Server"/><category term="Telemetry"/><category term="Translation"/><category term="VAT"/><category term="VS Code"/><category term="Windows Server"/><category term="ADO"/><category term="AI"/><category term="Accounts Rollup"/><category term="Adobe Acrobat"/><category term="Agents"/><category term="Application Insights"/><category term="Azure Application Insights"/><category term="Azure Key Vault"/><category term="Best of 2009"/><category term="Business Portal"/><category term="COVID19"/><category term="Certificates"/><category term="Check Links"/><category term="Claude Code"/><category term="Clear Data"/><category term="Common Data Services"/><category term="Computer Vision"/><category term="Coronavirus"/><category term="DEX_ROW_ID"/><category term="DUOS"/><category term="Deferrals"/><category term="Discussions"/><category term="Dynamics GP"/><category term="Dynamics GP   14"/><category term="Dynamics GP 10"/><category term="Dynamics GP Russian"/><category term="Flexicoder"/><category term="FlowFam"/><category term="Forecaster"/><category term="Form Processing"/><category term="GP PowerShell"/><category term="GitHub"/><category term="HTML 5"/><category term="Hololense"/><category term="IoT"/><category term="LINQ to SQL"/><category term="Mentor"/><category term="Microsoft Band"/><category term="Microsoft Edge"/><category term="Microsoft Gadgets"/><category term="Multitenancy"/><category term="Next Document Number"/><category term="ODBC"/><category term="Open Source"/><category term="PO Commitments"/><category term="Partner Connections"/><category term="Process Server"/><category term="Project Madeira"/><category term="RPA"/><category term="RemoteApp"/><category term="Requisitions"/><category term="SmartLists"/><category term="SmartLists Designer"/><category term="Splash Screen"/><category term="Surface"/><category term="Task Scheduler"/><category term="Text Analytics"/><category term="The Partner Event"/><category term="Twilio"/><category term="Use Tax"/><category term="Virtual Earth"/><category term="Virtualization"/><category term="Web API"/><category term="XBRL"/><category term="pac CLI"/><title type='text'>The Workbench</title><subtitle type='html'>Hands-on with AI, Copilot Studio, and the Power Platform</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://www.theworkbench.blog/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default?redirect=false'/><link rel='alternate' type='text/html' href='http://www.theworkbench.blog/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default?start-index=26&amp;max-results=25&amp;redirect=false'/><author><name>Mariano Gomez</name><uri>http://www.blogger.com/profile/13267738662239812289</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiE6oIsiofbLlE_uzUGLv64DYdGPmN9xurCqrbDNWs_axq3WbN9RRlG9IYNI50LMAK96HtSeFIVKWTjqw_URFhfBlHzutaAfUXMkUvW4RK9sO0z6Rt8YyNaETF0D4MdiQ/s113/Mariano+Gomez+-+Square+Headshot+%28website%29.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>1098</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-5285970135510371565.post-1666200584224273562</id><published>2026-09-07T07:00:00.000-04:00</published><updated>2026-09-07T09:55:23.658-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Copilot Studio"/><category scheme="http://www.blogger.com/atom/ns#" term="DocWatch"/><title type='text'>Copilot Studio | Migrate the Agent, Keep the Client ID</title><content type='html'>&lt;!--Cover image is hosted in the public theworkbench-assets repo. If you prefer Google-hosted
     images, re-upload the PNG through the Blogger editor before you publish.--&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
&lt;img alt=&quot;Migrate the agent, keep the client ID: Copilot Studio agents move to Microsoft Entra Agent ID&quot; src=&quot;https://raw.githubusercontent.com/dgpblogster/theworkbench-assets/main/covers/cover-16-entra-agent-id.png&quot; style=&quot;max-width: 100%;&quot; /&gt;
&lt;/div&gt;

&lt;p&gt;There is a question I have been asked in one form or another by every administrator who has ever inherited a tenant full of Copilot Studio agents, and it always arrives with the same slightly worried tone: &quot;what are all these app registrations in Entra, and which agent does each one belong to?&quot; It is a fair question. For years the answer was that Copilot Studio quietly provisioned an Azure app registration for every agent you created, those registrations piled up in Microsoft Entra ID looking exactly like every other application in the tenant, and telling them apart was an exercise in cross-referencing GUIDs. Not exactly a governance story.&lt;/p&gt;

&lt;p&gt;Well, in late August three articles landed in the Copilot Studio documentation that finally close that loop, and one of them is the migration runbook administrators have been waiting for.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Background&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;Two brand new pages and one substantially rewritten page, all last updated on August 27, 2026:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/microsoft-copilot-studio/govern-agents-identities-overview&quot;&gt;Agent identities and authentication for Copilot Studio&lt;/a&gt;, new, a plain-language explainer written as a set of questions.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/microsoft-copilot-studio/govern-migrate-api-entra-agent-identity&quot;&gt;Migrate Copilot Studio agents to Microsoft Entra Agent ID&lt;/a&gt;, new, and the reason for this post.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/microsoft-copilot-studio/admin-use-entra-agent-identities&quot;&gt;Microsoft Entra Agent IDs for Copilot Studio agents&lt;/a&gt;, rewritten with the migration guidance and the transition-period rules.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, the model itself, because the terminology is new and worth pinning down before anything else. A &lt;b&gt;Microsoft Entra Agent ID&lt;/b&gt; is a Microsoft Entra service principal with an &quot;Agent&quot; subtype. That is the whole definition. It is not a new protocol and not a new token type. The documentation is careful about this: the underlying OAuth-based authentication flow remains the same. What changes is that the agent is now a first-class object in Entra that an administrator can see, govern, and write policy against, rather than an anonymous app registration sitting in a list of four hundred others.&lt;/p&gt;

&lt;p&gt;Copilot Studio has been creating these automatically for every new agent since May 2026. Agents created before that date still carry the legacy app registration, and here is the sentence that turns this from trivia into a project: existing agents will be migrated by Microsoft automatically in a future update. So the migration is happening either way. The only decision in front of you is whether it happens on your schedule or on Microsoft&#39;s.&lt;/p&gt;

&lt;p&gt;NOTE: one other thing changed in the same rewrite and it is easy to miss. Previously you could opt out of Entra Agent ID at the environment level. That option is gone. All new agents get Microsoft Entra Agent IDs and you can no longer opt out of automatic agent identity creation. There is also now a tenant-level object called the &lt;b&gt;Microsoft Copilot Studio agent identity blueprint&lt;/b&gt;, added the first time an agent is created after the rollout, with a corresponding blueprint principal.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;The Solution: an in-place conversion with a rollback&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;Here is the design decision worth slowing down for, and it is the one that makes the whole thing usable in a real tenant.&lt;/p&gt;

&lt;p&gt;Migration converts an agent&#39;s existing app-registration identity &lt;i&gt;in place&lt;/i&gt;. The agent keeps its application (client) ID. Everything downstream that resolves against that identifier, channel registrations and connectors among them, continues to resolve to the same value. The agent additionally gains an Entra Agent ID that administrators can manage.&lt;/p&gt;

&lt;p&gt;You may be asking why that detail deserves its own paragraph. Frankly, the answer is simple: the alternative would have been catastrophic. An identity migration that issued a new client ID would break every channel registration, every connector configuration, and every downstream system that had that GUID written into a config file somewhere, all at once, across an entire estate. Keeping the client ID is what turns &quot;a migration nobody will ever run&quot; into &quot;a migration you can pilot on Tuesday.&quot;&lt;/p&gt;

&lt;p&gt;And it is reversible. There is a documented rollback that reverts an agent to its legacy app-registration identity, which means you can validate an agent after migrating it and put it back if it does not pass. A migration path with a documented way back is a rarer thing than it should be, and whoever fought for it deserves the credit.&lt;/p&gt;

&lt;p&gt;What you get in exchange for the trip: a first-class agent identity that administrators can view and govern in Microsoft Entra, Conditional Access and other access policies designed for agentic workloads and &lt;i&gt;scoped to agents&lt;/i&gt; rather than inherited from app registrations, and a consistent identity model across the services that work with your agents. The Conditional Access point is the substantive one. Writing a policy that targets agents specifically, rather than catching them incidentally because they happen to be applications, is a capability that did not exist before.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Why it matters&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;For &lt;b&gt;makers&lt;/b&gt;, the honest answer is that this is not your job, but it will land on your calendar anyway. The documented process explicitly requires the administrator to coordinate a validation window with the makers who own the agents being migrated, and to wait for those makers to test before the next batch goes. If you own an agent, expect to be asked to exercise it across every channel it publishes to, run its actions and connectors, and confirm its authentication still behaves. Say yes and block the time. The alternative is finding out during the automatic migration, with nobody watching.&lt;/p&gt;

&lt;p&gt;For &lt;b&gt;professional developers and administrators&lt;/b&gt;, there are two pieces of work here. The near one is the pilot: identify eligible agents, migrate a representative handful, validate, expand. The further one is Conditional Access, and it is the more interesting of the two. Once agents are first-class identities in Entra, &quot;which policies apply to our agents&quot; becomes a question with an actual answer, and the documentation tells you to review Microsoft Entra sign-in logs including Conditional Access results before you widen a batch. That is a sentence with real work behind it, and it is the part I would start reading up on now rather than during the migration.&lt;/p&gt;

&lt;p&gt;Two cautions, both from the documentation rather than invented by me. The manual migration process is currently a &lt;b&gt;preview&lt;/b&gt; feature. And the documentation states plainly that migrating agent identities affects live agents and can disrupt authentication, connectors, and integrations if you do not plan the migration carefully. This is not a maintenance-window-free operation dressed up as one.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;The demo concept&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;Not &quot;watch an agent get migrated,&quot; because the interesting part of a successful migration is that nothing visible happens. The demonstration worth building is the &lt;b&gt;evidence trail&lt;/b&gt;, and it has three parts.&lt;/p&gt;

&lt;p&gt;Take one noncritical agent. Before you touch it, capture its application (client) ID and its Entra sign-in log entries. Migrate it through the API so you get the response object rather than a screen full of green checkmarks. Then capture the same two things again and put them side by side: the client ID identical, the agent now present in Entra as a service principal with the Agent subtype, and a sign-in log you can point a Conditional Access policy at. That before-and-after pair is the artifact that gets a change advisory board to approve the rest of the estate, and it is far more persuasive than any amount of documentation quoting.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Steps to recreate&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;First, the prerequisites, because two of them will stop you cold. You must be a &lt;b&gt;Power Platform Administrator&lt;/b&gt;, &lt;b&gt;Dynamics 365 Administrator&lt;/b&gt;, or &lt;b&gt;Global Administrator&lt;/b&gt;. Power Platform inventory must be enabled for your tenant, because that is how Advisor identifies eligible agents. And you must coordinate a validation window with the makers who own the agents. That third one is not a technical prerequisite, but the documentation lists it as one, and I would treat it exactly that way.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;The administrator path, through the Power Platform admin center&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;1) Sign in to the Power Platform admin center.&lt;br /&gt;
2) In the left navigation pane, select &lt;b&gt;Actions&lt;/b&gt;.&lt;br /&gt;
3) Under &lt;b&gt;Actions&lt;/b&gt;, select &lt;b&gt;Recommendations&lt;/b&gt;.&lt;br /&gt;
4) On the &lt;b&gt;Recommendations&lt;/b&gt; tab, select &lt;b&gt;Active&lt;/b&gt;.&lt;br /&gt;
5) Search for and select &lt;b&gt;Migrate Copilot Studio agents to Microsoft Entra Agent ID for enhanced agent governance&lt;/b&gt;.&lt;br /&gt;
6) In the recommendation pane, expand &lt;b&gt;Why is this important?&lt;/b&gt; and read the migration guidance.&lt;br /&gt;
7) Review the eligible agents. Use the &lt;b&gt;Suggested migration order&lt;/b&gt; and &lt;b&gt;Migration notes&lt;/b&gt; columns to pick your pilot, and note that the table also gives you environment, environment type, owner, recent activity, and authentication method.&lt;br /&gt;
8) Select the checkbox next to each agent you want to migrate. The &lt;b&gt;Migrate&lt;/b&gt; button becomes available and the action bar shows how many agents you have selected.&lt;br /&gt;
9) Select &lt;b&gt;Migrate&lt;/b&gt;, review the confirmation, and confirm.&lt;br /&gt;
10) Review the &lt;b&gt;Action&lt;/b&gt;, &lt;b&gt;Action state&lt;/b&gt;, and &lt;b&gt;Action date&lt;/b&gt; columns for each agent. The &lt;b&gt;Action history&lt;/b&gt; tab shows actions across all recommendations.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEio6Hvw2FB0EwA2jYruOhlNK9QuFHi4gQ0lE9s0oYbX6zlKDgRCdni8179FjeyKlaH_3W6L8aUGgC0ef1DoDUGr4tGp4Gp4kKWYjuD-UDbTi0oOvrOJ6FtuM3MgKNPHmHdUZSpZhQtKq8tuxKU0oOSs_N_uVSYiCQ9yITgtOmn7IzQHYzBgwLrddkzd9jXa/s1908/Screenshot%202026-09-07%20094808.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1065&quot; data-original-width=&quot;1908&quot; height=&quot;365&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEio6Hvw2FB0EwA2jYruOhlNK9QuFHi4gQ0lE9s0oYbX6zlKDgRCdni8179FjeyKlaH_3W6L8aUGgC0ef1DoDUGr4tGp4Gp4kKWYjuD-UDbTi0oOvrOJ6FtuM3MgKNPHmHdUZSpZhQtKq8tuxKU0oOSs_N_uVSYiCQ9yITgtOmn7IzQHYzBgwLrddkzd9jXa/w653-h365/Screenshot%202026-09-07%20094808.png&quot; width=&quot;653&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Power Platform Admin Center&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;p&gt;Then stop. Do not queue the next batch. Validate first, and the documentation is specific about what validating means: confirm the agent responds correctly in every channel where it is published, that its actions, connectors, flows and integrations run, that it authenticates as expected including any custom authentication, and that it behaves correctly with applicable agent access policies and Conditional Access policies. Then go read the sign-in logs for those agents in the Microsoft Entra admin center. If an agent fails validation, stop the rollout and revert that agent before continuing.&lt;/p&gt;

&lt;p&gt;Keep in mind that Advisor recommendations can remain visible for up to a week after you act on them while the recommendation data refreshes. Seeing the recommendation still sitting there does not mean your migration did not take.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;The API path, for when you want your own automation&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;The admin center is the recommended manual method, but there are Power Platform API endpoints if you would rather script it, and the documentation supplies the samples. Everything needs an OAuth2 bearer token for the Power Platform API:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;$token = (Get-AzAccessToken -ResourceUrl &quot;https://api.powerplatform.com&quot;).Token&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A few things to note. This uses the Az PowerShell module, and the token has to be associated with an account holding one of the three admin roles listed earlier. There is no service-principal shortcut documented here, so if multifactor authentication or Conditional Access requires interactive sign-in, expect a browser window and complete the prompts. This is also the single most common failure in the troubleshooting table, listed as expired credentials or a token error.&lt;/p&gt;

&lt;p&gt;To migrate one agent:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;$token = (Get-AzAccessToken -ResourceUrl &quot;https://api.powerplatform.com&quot;).Token
$environmentId = &quot;&amp;lt;EnvironmentId&amp;gt;&quot;
$botId = &quot;&amp;lt;BotId&amp;gt;&quot;
$uri = &quot;https://api.powerplatform.com/copilotstudio/environments/$environmentId/bots/$botId/api/agentidentitymigration/migrate?api-version=2024-10-01&quot;
Invoke-RestMethod `
  -Method Post `
  -Uri $uri `
  -Headers @{
    Authorization = &quot;Bearer $token&quot;
  }&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A few things to note. The request takes &lt;b&gt;no body&lt;/b&gt;, which surprised me the first time I read it: the agent is fully identified by the two IDs in the path. You get those two values from the agent inventory in the Power Platform admin center under &lt;b&gt;Manage&lt;/b&gt; &amp;gt; &lt;b&gt;Copilot Studio&lt;/b&gt;. The API version is pinned at &lt;code&gt;2024-10-01&lt;/code&gt;, so keep it, and note that this endpoint lives on &lt;code&gt;api.powerplatform.com&lt;/code&gt; rather than any Copilot Studio host. The response is an &lt;code&gt;AgentIdentityMigrationResult&lt;/code&gt; with a status of either &lt;code&gt;Migrated&lt;/code&gt; or &lt;code&gt;AlreadyMigrated&lt;/code&gt;, which means the call is safely idempotent. Running it twice does not do the migration twice, and that matters a great deal if you are looping over a list of agents and something times out halfway through.&lt;/p&gt;

&lt;p&gt;A successful response looks like this:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;status&quot;: &quot;Migrated&quot;,
  &quot;cdsBotId&quot;: &quot;&amp;lt;bot-id&amp;gt;&quot;,
  &quot;environmentId&quot;: &quot;&amp;lt;environment-id&amp;gt;&quot;,
  &quot;tenantId&quot;: &quot;&amp;lt;tenant-id&amp;gt;&quot;,
  &quot;agentIdentityId&quot;: &quot;&amp;lt;agent-identity-id&amp;gt;&quot;,
  &quot;applicationId&quot;: &quot;&amp;lt;application-client-id&amp;gt;&quot;,
  &quot;servicePrincipalObjectId&quot;: &quot;&amp;lt;service-principal-object-id&amp;gt;&quot;,
  &quot;managedIdentityId&quot;: &quot;&amp;lt;managed-identity-id&amp;gt;&quot;,
  &quot;completedAtUtc&quot;: &quot;2026-08-21T12:00:00Z&quot;
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A few things to note, and this payload is the reason I would use the API even if I ran the migration from the admin center. Look at what comes back: &lt;code&gt;applicationId&lt;/code&gt;, the client ID you can compare against what you captured before, and &lt;code&gt;servicePrincipalObjectId&lt;/code&gt; and &lt;code&gt;agentIdentityId&lt;/code&gt;, the handles you need to find this agent in Entra afterward. Log the whole object. It is your evidence trail, produced for free, and reconstructing it later from the portal is tedious work you would rather not do.&lt;/p&gt;

&lt;p&gt;And to put one back:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;$uri = &quot;https://api.powerplatform.com/copilotstudio/environments/$environmentId/bots/$botId/api/agentidentitymigration/rollback?api-version=2024-10-01&quot;
Invoke-RestMethod `
  -Method Post `
  -Uri $uri `
  -Headers @{
    Authorization = &quot;Bearer $token&quot;
  }&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A few things to note. Same shape, same authentication, same absence of a body, and the same idempotent design: the response status is &lt;code&gt;RolledBack&lt;/code&gt; or &lt;code&gt;NotMigrated&lt;/code&gt;. Write this script at the same time you write the migrate script, not after something has gone wrong. A rollback you have already tested on a throwaway agent is a rollback you can actually reach for at four in the afternoon with a maker on the phone.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Companion repo sketch&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;A small runbook repository, honest about being a runbook. A &lt;code&gt;scripts/&lt;/code&gt; folder with three files and no more: &lt;code&gt;Get-AgentInventory.ps1&lt;/code&gt; to pull the agents and their environment and bot IDs, &lt;code&gt;Invoke-AgentMigration.ps1&lt;/code&gt; that takes a CSV of the batch and writes every response object to disk, and &lt;code&gt;Invoke-AgentRollback.ps1&lt;/code&gt; written first and tested first. A &lt;code&gt;batches/&lt;/code&gt; folder with the CSV per batch, so the pilot, the second wave and the long tail are separate committed files rather than a spreadsheet somebody is editing in place. An &lt;code&gt;evidence/&lt;/code&gt; folder holding the before-and-after client IDs and the raw JSON responses, because that is the artifact that survives the project. And a &lt;code&gt;VALIDATION.md&lt;/code&gt; with the checklist from the documentation turned into tick boxes per agent: channels, actions, connectors, flows, integrations, custom authentication, access policies, Conditional Access.&lt;/p&gt;

&lt;p&gt;Point the README at a nonproduction tenant for the first run and say so in bold. And put the troubleshooting table in there too, because the four failure modes Microsoft documents are the four you will hit: inventory returning no agents when Power Platform inventory is not enabled or your role is wrong, token and reauthentication prompts, agents skipped because they are already migrated or you left out an ID, and single-agent failures from eligibility, access, or throttling, where the answer is to wait and rerun.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Final Notes&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;What I keep coming back to is not the identity model, welcome as it is. It is that Microsoft shipped the migration runbook &lt;i&gt;and&lt;/i&gt; the rollback &lt;i&gt;and&lt;/i&gt; a troubleshooting table with four named failure modes, for a migration it is eventually going to run for you anyway. It did not have to do any of that. It could have flipped the switch on a Tuesday and let us all find out.&lt;/p&gt;

&lt;p&gt;So take the invitation. Enable Power Platform inventory, open the Advisor recommendation, pick three agents nobody will miss, and run the pilot while the stakes are zero and the makers are available. The version of this project where you go first is a much better project than the version where you go last.&lt;/p&gt;

&lt;p&gt;What I learned from this exercise: the most valuable sentence in a migration document is rarely the one describing the migration. It is the one telling you how to undo it.&lt;/p&gt;

&lt;p&gt;Until next post!&lt;/p&gt;

&lt;p&gt;MG.-&lt;br /&gt;
Mariano Gomez Bent&lt;br /&gt;
Former Microsoft BizApps MVP&lt;/p&gt;
</content><link rel='replies' type='application/atom+xml' href='http://www.theworkbench.blog/feeds/1666200584224273562/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5285970135510371565/1666200584224273562' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/1666200584224273562'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/1666200584224273562'/><link rel='alternate' type='text/html' href='http://www.theworkbench.blog/2026/09/copilot-studio-migrate-agent-keep.html' title='Copilot Studio | Migrate the Agent, Keep the Client ID'/><author><name>Mariano Gomez</name><uri>http://www.blogger.com/profile/13267738662239812289</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiE6oIsiofbLlE_uzUGLv64DYdGPmN9xurCqrbDNWs_axq3WbN9RRlG9IYNI50LMAK96HtSeFIVKWTjqw_URFhfBlHzutaAfUXMkUvW4RK9sO0z6Rt8YyNaETF0D4MdiQ/s113/Mariano+Gomez+-+Square+Headshot+%28website%29.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEio6Hvw2FB0EwA2jYruOhlNK9QuFHi4gQ0lE9s0oYbX6zlKDgRCdni8179FjeyKlaH_3W6L8aUGgC0ef1DoDUGr4tGp4Gp4kKWYjuD-UDbTi0oOvrOJ6FtuM3MgKNPHmHdUZSpZhQtKq8tuxKU0oOSs_N_uVSYiCQ9yITgtOmn7IzQHYzBgwLrddkzd9jXa/s72-w653-h365-c/Screenshot%202026-09-07%20094808.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5285970135510371565.post-8604335111466068845</id><published>2026-09-04T07:00:00.000-04:00</published><updated>2026-09-04T07:00:00.113-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="DocWatch"/><category scheme="http://www.blogger.com/atom/ns#" term="Power Apps"/><title type='text'>Power Apps | Describe the App, Approve the Plan</title><content type='html'>&lt;!-- Cover image is hosted in the public theworkbench-assets repo. If you prefer Google-hosted
     images, re-upload the PNG through the Blogger editor before you publish. --&gt;
&lt;div style=&quot;text-align:center;&quot;&gt;
&lt;img alt=&quot;Describe the app, approve the plan: the model app builder skill for Power Apps&quot; src=&quot;https://raw.githubusercontent.com/dgpblogster/theworkbench-assets/main/covers/cover-15-model-app-builder.png&quot; style=&quot;max-width:100%;&quot; /&gt;
&lt;/div&gt;

&lt;p&gt;Every one of us who has stood up a model-driven app from nothing has lived the same first afternoon. You know the business process cold, you could describe it to a colleague in ninety seconds, and yet the next four hours go to clicking: a table, then its columns, then the relationships, then a main form, then two views, then a chart nobody asked for, then the sitemap, then the security roles you will get wrong the first time and fix on Thursday. None of that is thinking. It is transcription, and it is the reason so many good internal apps never get built at all.&lt;/p&gt;

&lt;p&gt;Well, in late August a new article appeared in the Power Apps documentation that goes after that first afternoon directly, and it did not arrive alone.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Background&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;A brand new page landed in the &lt;code&gt;MicrosoftDocs/powerapps-docs&lt;/code&gt; repository, dated 08/27/2026, and it is live on Learn:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/power-apps/maker/model-driven-apps/model-driven-app-external-tools&quot;&gt;Build and edit model-driven apps with AI code generation tools (preview)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What tells you this is a real shipped surface rather than a stray draft is the company it keeps. In the same set of changes, &lt;a href=&quot;https://learn.microsoft.com/en-us/power-apps/maker/model-driven-apps/app-building-steps&quot;&gt;Steps to building a model-driven app&lt;/a&gt; and the &lt;a href=&quot;https://learn.microsoft.com/en-us/power-apps/maker/model-driven-apps/app-designer-overview&quot;&gt;Overview of the model-driven app designer&lt;/a&gt; both gained a TIP block pointing at the new article, the existing &lt;a href=&quot;https://learn.microsoft.com/en-us/power-apps/maker/model-driven-apps/generative-page-external-tools&quot;&gt;generative pages&lt;/a&gt; article gained a NOTE telling you which of the two skills to reach for, and the maker landing page and table of contents were amended to carry it. Microsoft does not wire four existing articles into a page it is not serious about.&lt;/p&gt;

&lt;p&gt;Now, the thing itself. It is called the &lt;b&gt;model app builder skill&lt;/b&gt;, and it runs inside an AI code generation tool such as GitHub Copilot CLI or Claude Code. A &lt;b&gt;skill&lt;/b&gt;, in this context, is a packaged set of instructions and commands that teaches one of those tools how to perform a specific job. It is not a connector, and it is not a service you call. It is guidance the tool loads, plus the command-line plumbing to act on it. The distinction matters, because it means everything the skill does, it does as &lt;i&gt;you&lt;/i&gt;, through tools you already have authenticated on your own machine.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;What the skill actually produces&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;This is the part that surprised me, so let me quote the capability list rather than paraphrase it into something vaguer. The documentation says the skill can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a model-driven app from a description of a business scenario.&lt;/li&gt;
&lt;li&gt;Plan the app around user personas and the jobs they need to accomplish.&lt;/li&gt;
&lt;li&gt;Create supporting tables, columns, relationships, forms, views, charts, and sample data.&lt;/li&gt;
&lt;li&gt;Create generative pages for experiences that go beyond standard forms and views.&lt;/li&gt;
&lt;li&gt;Create a sitemap with custom icons for each table.&lt;/li&gt;
&lt;li&gt;Add JavaScript validation rules to forms.&lt;/li&gt;
&lt;li&gt;Add security roles based on the planned personas and data access needs.&lt;/li&gt;
&lt;li&gt;Edit an existing model-driven app by describing the changes you want.&lt;/li&gt;
&lt;li&gt;Verify the deployed app against the approved specification.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Read that list twice. Security roles derived from personas. Sitemap icons and form validation created as &lt;b&gt;web resources&lt;/b&gt; and wired to the right components during implementation. That is not a scaffolder producing a table and a form and calling it an app. That is the boring 80 percent of the first afternoon, including the parts people skip.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;The Solution: the dry run is the whole feature&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;Here is where I would slow down, because the interesting design decision is not the generation. It is the gate in front of the generation.&lt;/p&gt;

&lt;p&gt;The skill runs an interactive, multistep process, and the documentation is explicit about the order. You describe the business scenario, and the skill asks follow-up questions about users, data, workflows and the experience you want. It then presents an &lt;b&gt;app spec&lt;/b&gt;: personas, jobs to be done, tables, relationships, forms, views, charts, JavaScript validation rules, generative page intents, and the sitemap with a proposed icon for each table. You review and refine that specification. Only then does it validate the spec and present a &lt;b&gt;dry-run build plan&lt;/b&gt;, grouped by build phase, in the conversation. And then the sentence that carries the whole design:&lt;/p&gt;

&lt;blockquote&gt;No app artifacts are created until you approve the plan.&lt;/blockquote&gt;

&lt;p&gt;You may be asking why a plan step deserves this much attention when every generator ever built has had a preview mode. Very simple: the failure mode of an AI that writes to Dataverse is not a bad form. It is thirty artifacts you did not ask for, scattered across a solution, with a publisher prefix on all of them and no obvious way to tell which ones matter. A plan you can read and argue with before anything is written is the difference between a tool you can use in a real environment and a tool you can only use in a sandbox you are willing to throw away.&lt;/p&gt;

&lt;p&gt;After the build, there is an optional verification step that compares the deployed app with the approved app spec. That is worth noticing too. &quot;The build reported success&quot; and &quot;the app matches what I approved&quot; are two different claims, and only one of them is worth anything on Monday morning.&lt;/p&gt;

&lt;p&gt;NOTE: the editing path is the same skill and it works in reverse. Point it at an existing app and it retrieves the deployed app, builds an &lt;i&gt;editable app spec&lt;/i&gt; from it, and then proposes changes against that spec. Which means the spec is not just an input format. It is a two-way representation of the app, and that is the part of this design most likely to still be here in three years.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Why it matters&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;For &lt;b&gt;makers&lt;/b&gt;, this is genuinely for you, and that is a change from most of what lands in the developer docs. The article is tagged for makers and developers both, and the entry point is a sentence about your business process rather than a table designer. But be honest with yourself about the toolchain: this needs Node.js, the Power Platform CLI, the Azure CLI, and a terminal-based AI coding tool on your machine. If you have never opened a command prompt on purpose, the skill is not the barrier. The prerequisites are. That is a fair trade to know about up front rather than discover forty minutes in.&lt;/p&gt;

&lt;p&gt;For &lt;b&gt;professional developers&lt;/b&gt;, the claim worth weighing is the app spec. We have spent years arguing about how to describe a model-driven app in a form that survives review, diffing and source control, and the answers have all been either solution XML, which nobody enjoys reading, or a wiki page that goes stale in a week. A reviewable specification that generates the app and can be regenerated from the app is a different proposition. Whether the schema holds up is an open question, and the documentation says plainly that it might change between releases, but the shape is right.&lt;/p&gt;

&lt;p&gt;Two cautions, both taken from the documentation rather than invented by me. This is &lt;b&gt;preview&lt;/b&gt;, and Microsoft states that the app spec schema and command-line options might change between releases. And the skill does not support every model-driven app artifact or concept, so some of your app will still be built in the designers. The related warning is the one I would put on a sticky note: changes you make outside the skill can affect later AI-assisted edits, so before you approve another build, read the plan to confirm it preserves your manual customizations. An agent that regenerates from a spec has no idea what you changed by hand unless the spec caught it.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;The demo concept&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;Not &quot;watch it build an app,&quot; because a video of artifacts appearing proves nothing about whether they were the right artifacts. The demonstration worth building is the &lt;b&gt;round trip&lt;/b&gt;, and it takes three moves.&lt;/p&gt;

&lt;p&gt;Build a small app from a prompt and keep the approved app spec. Then go into Power Apps and change something by hand that the spec never knew about, a business rule, a column on a form, a tweaked view. Then ask the skill for an unrelated change and read the new plan carefully to see whether your manual work survives. That single observation is more useful than any amount of generation footage, because it answers the only question a working developer actually has: can I use this on an app I care about, or only on one I am willing to lose?&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Steps to recreate&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;1) Get the prerequisites in place first. The documentation lists a current long-term support version of &lt;b&gt;Node.js&lt;/b&gt;, &lt;b&gt;Power Platform CLI (PAC CLI) 2.7.0 or later&lt;/b&gt;, the latest &lt;b&gt;Azure CLI&lt;/b&gt;, and &lt;b&gt;GitHub Copilot CLI&lt;/b&gt; or &lt;b&gt;Claude Code&lt;/b&gt;. You also need a Power Platform environment where you can create and modify Dataverse and model-driven app artifacts, an authenticated PAC CLI profile pointed at that environment, and an authenticated Azure CLI session.&lt;/p&gt;

&lt;p&gt;2) Make sure both authentications are the same person. This is the one prerequisite that reads like a footnote and behaves like a blocker:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;az login&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A few things to note. The documentation is specific that you run &lt;code&gt;az login&lt;/code&gt; with &lt;i&gt;the same identity used by the active PAC CLI profile&lt;/i&gt;. Two different accounts across the two CLIs is the classic way to get a confusing permission error much later in the run, long after you have forgotten which account each tool holds. Check the active PAC profile with &lt;code&gt;pac auth list&lt;/code&gt; before you start, and if the two do not match, fix it now rather than at step six.&lt;/p&gt;

&lt;p&gt;3) Install the plugin. There is a one-line installer that sets up all of the Power Platform plugins at once, in PowerShell:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;iwr https://raw.githubusercontent.com/microsoft/power-platform-skills/main/scripts/install.js -OutFile install.js; node install.js; del install.js&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A few things to note. This downloads a JavaScript file, runs it with Node, and deletes it, which is exactly the kind of command you should read before you run. The installer detects which supported AI code generation tools you have, registers the Power Platform Skills marketplace, installs the plugins, and turns on automatic updates. The source lives in the public &lt;a href=&quot;https://github.com/microsoft/power-platform-skills&quot;&gt;microsoft/power-platform-skills&lt;/a&gt; repository, so you can read &lt;code&gt;scripts/install.js&lt;/code&gt; first, and I would encourage you to do exactly that. There is an equivalent for a Windows command window:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;curl -fsSL https://raw.githubusercontent.com/microsoft/power-platform-skills/main/scripts/install.js | node&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;4) Or install just the one plugin, which is what I would do the first time. These are slash commands typed into the AI coding tool&#39;s own chat, not shell commands:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;/plugin marketplace add microsoft/power-platform-skills
/plugin install model-apps@power-platform-skills&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A few things to note. The first command registers the marketplace, the second installs the Power Apps plugin from it, and the part after the &lt;code&gt;@&lt;/code&gt; is the marketplace name. Restart the tool afterward if it does not pick up the plugin on its own. For Claude Code specifically, the documentation points out that plugins can be installed at global, local or user scope, and that depending on the scope you must be in the correct directory for the plugin to load. If the skill appears to be missing, check where you are standing before you reinstall anything.&lt;/p&gt;

&lt;p&gt;5) Build something. Start a conversation with the tool and invoke the skill:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;/app-builder

Build an equipment inspection app for field technicians and dispatchers.&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A few things to note. That example prompt is quoted from the documentation, along with &quot;Build an app to manage supplier onboarding and contract approvals&quot; and &quot;Create a model-driven app for tracking service requests across multiple teams.&quot; Notice the shape they all share: a business process and the people in it, not a list of tables. That is deliberate, because personas are a first-class part of the app spec and they are what the security roles get derived from. You can also skip &lt;code&gt;/app-builder&lt;/code&gt; and simply describe the app you want.&lt;/p&gt;

&lt;p&gt;6) Then work the review, which is the part that earns the whole exercise. Answer the skill&#39;s questions about the process, users, data and workflows. Read the app spec and check that the personas, jobs to be done, tables, relationships, forms, views, charts, validation rules, generative pages, navigation and sitemap icons actually support the business scenario, and ask for revisions until they do. Read the dry-run build plan and approve it only when its proposed changes match your intent. Let it build. Take the optional verification step. Then open the app at &lt;a href=&quot;https://make.powerapps.com&quot;&gt;make.powerapps.com&lt;/a&gt;, run its primary scenarios, and inspect the generated artifacts with your own eyes.&lt;/p&gt;

&lt;p&gt;7) To edit an app you already have, invoke &lt;code&gt;/app-builder&lt;/code&gt; again and name it:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;/app-builder

Add an Invoices table and related forms and views to the Supplier Management app.&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A few things to note. The skill asks you to confirm which existing app it should retrieve, then presents an editable app spec built from the deployed app, and then the same plan-and-approve cycle applies. This is the path where the caution about manual customizations bites, so read that plan properly rather than skimming for the word &quot;success.&quot;&lt;/p&gt;

&lt;p&gt;&lt;b&gt;One more thing that landed in the same set of changes&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;The generative pages article picked up a genuinely useful capability that has nothing to do with AI coding tools, and it would be a shame to let it get buried: you can now &lt;b&gt;embed a generative page inside a model-driven app form&lt;/b&gt;, in a section or a tab. Create and publish a generative page set up to accept the &lt;code&gt;recordId&lt;/code&gt; input parameter, then:&lt;/p&gt;

&lt;p&gt;1) Open the form in the form designer.&lt;br/&gt;
2) In the left pane, select &lt;b&gt;Components&lt;/b&gt;, and then expand &lt;b&gt;Display&lt;/b&gt;.&lt;br/&gt;
3) Select &lt;b&gt;Generative page&lt;/b&gt;.&lt;br/&gt;
4) Select the generative page you want to embed.&lt;br/&gt;
5) Optionally, provide values for any additional static inputs the page accepts.&lt;br/&gt;
6) Save and publish the form.&lt;/p&gt;

&lt;p&gt;When a user opens a record, the form passes the current record ID to the page as &lt;code&gt;recordId&lt;/code&gt; automatically. You do not configure a static value for it. That is a small paragraph in the docs and a fairly large door, because it turns generative pages from a separate destination into something that lives on the record the user is already looking at.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Companion repo sketch&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;A lab repository built around the round trip rather than the generation. A &lt;code&gt;prompts/&lt;/code&gt; folder with the exact prompts in order, so somebody else can reproduce the run instead of taking your word for it. A &lt;code&gt;specs/&lt;/code&gt; folder holding the approved app spec from the first build and the regenerated spec from after the manual edit, because the diff between those two files &lt;i&gt;is&lt;/i&gt; the finding. A &lt;code&gt;plans/&lt;/code&gt; folder with the dry-run build plans exactly as they appeared, including the one you rejected, since a lab that records only the approved plan is hiding the interesting half. A &lt;code&gt;solution/&lt;/code&gt; folder with the exported and unpacked solution so the generated artifacts show up as reviewable files. And a short &lt;code&gt;FINDINGS.md&lt;/code&gt; that says which artifacts the skill got right, which it missed, and whether the manual customization survived, in plain language.&lt;/p&gt;

&lt;p&gt;Point the README at a developer environment, say so loudly, and repeat it. This is preview software that writes schema. Nobody should be pointing it at production to find out what it does.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Final Notes&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;What I keep coming back to is that the headline feature of this skill is a document. Not the generation, which is impressive but is the part everybody expected. The app spec is the thing: a reviewable description of a model-driven app, produced from a conversation, editable by a human, capable of building the app and capable of being rebuilt from it. We have wanted that artifact for a very long time, and it is a little funny that it arrives as a side effect of teaching a coding agent to click the designers for us.&lt;/p&gt;

&lt;p&gt;It is preview, so treat it accordingly. Read the installer before you run it, keep your two CLI logins on the same identity, use a developer environment, and read every plan before you approve it. And if you take one habit from the whole exercise, make it the verification step, because the gap between &quot;it built&quot; and &quot;it matches what I approved&quot; is where every automation story eventually gets interesting.&lt;/p&gt;

&lt;p&gt;What I learned from this exercise: when a tool asks you to approve a plan, the plan is not a formality it added to be polite. It is the tool telling you exactly where the responsibility moves from the machine back to you.&lt;/p&gt;

&lt;p&gt;Until next post!&lt;/p&gt;

&lt;p&gt;MG.-&lt;br/&gt;
Mariano Gomez Bent&lt;br/&gt;
Former Microsoft BizApps MVP&lt;/p&gt;
</content><link rel='replies' type='application/atom+xml' href='http://www.theworkbench.blog/feeds/8604335111466068845/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5285970135510371565/8604335111466068845' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/8604335111466068845'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/8604335111466068845'/><link rel='alternate' type='text/html' href='http://www.theworkbench.blog/2026/09/power-apps-describe-app-approve-plan.html' title='Power Apps | Describe the App, Approve the Plan'/><author><name>Mariano Gomez</name><uri>http://www.blogger.com/profile/13267738662239812289</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiE6oIsiofbLlE_uzUGLv64DYdGPmN9xurCqrbDNWs_axq3WbN9RRlG9IYNI50LMAK96HtSeFIVKWTjqw_URFhfBlHzutaAfUXMkUvW4RK9sO0z6Rt8YyNaETF0D4MdiQ/s113/Mariano+Gomez+-+Square+Headshot+%28website%29.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5285970135510371565.post-5958214116179228133</id><published>2026-08-31T07:00:00.000-04:00</published><updated>2026-08-31T07:00:00.121-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ALM"/><category scheme="http://www.blogger.com/atom/ns#" term="Copilot Studio"/><category scheme="http://www.blogger.com/atom/ns#" term="MCP"/><category scheme="http://www.blogger.com/atom/ns#" term="Open Source"/><category scheme="http://www.blogger.com/atom/ns#" term="pac CLI"/><category scheme="http://www.blogger.com/atom/ns#" term="Power Platform"/><title type='text'>Copilot Studio | Turning War Stories into Guardrails: Introducing pac-copilot-kit</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;text-align: center;&quot;&gt;&lt;img alt=&quot;Turning war stories into guardrails: building pac-copilot-kit&quot; height=&quot;630&quot; src=&quot;https://raw.githubusercontent.com/dgpblogster/theworkbench-assets/main/covers/cover-10-pac-copilot-kit.png&quot; style=&quot;height: auto; max-width: 100%;&quot; width=&quot;1200&quot; /&gt;&lt;/div&gt;

&lt;p&gt;Let me start with the announcement, because some of you came for the tool and not the story, and that is perfectly fine.&lt;/p&gt;

&lt;p&gt;Today I am releasing &lt;strong&gt;pac-copilot-kit&lt;/strong&gt;, open source under MIT at &lt;a href=&quot;https://github.com/dgpblogster/pac-copilot-kit&quot;&gt;github.com/dgpblogster/pac-copilot-kit&lt;/a&gt;. It is a paved-road toolkit for the Copilot Studio agent lifecycle, and it does exactly two things. First, capability: it creates Dataverse knowledge sources from code, inside your solution, repeatably, which is the one thing &lt;code&gt;pac&lt;/code&gt; cannot do at all. Second, the paved road: it makes Microsoft&#39;s own ALM prescription executable, one guarded command from YAML on disk to a published agent, in a shell or in CI. The first is why you install it. The second is why you keep it.&lt;/p&gt;

&lt;p&gt;The PowerShell module is on the PowerShell Gallery as &lt;code&gt;PacCopilotKit&lt;/code&gt;, and an MCP server that exposes the same engine to Claude Code, GitHub Copilot, or Codex is on npm as &lt;code&gt;pac-copilot-kit-mcp&lt;/code&gt;. Two commands and you are running:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;Install-Module PacCopilotKit
npx -y pac-copilot-kit-mcp   # optional: the MCP door, for your AI agent&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Issues and pull requests are welcome, and the war-stories document in the repository is the living inventory of every gotcha the tool guards against. If that is all you needed, go build something and let me know how it goes!&lt;/p&gt;

&lt;p&gt;Now, if you brought popcorn, stay for the rest, because the story of &lt;em&gt;why&lt;/em&gt; this tool exists is the more interesting half, and it includes one moment where the build caught its own author in an overclaim.&lt;/p&gt;

&lt;h2&gt;Where it came from&lt;/h2&gt;

&lt;p&gt;My last two Copilot Studio articles, one on &lt;a href=&quot;https://www.theworkbench.blog/2026/08/copilot-studio-adding-dataverse.html&quot;&gt;wiring Dataverse knowledge sources with the Web API&lt;/a&gt; and one on &lt;a href=&quot;https://www.theworkbench.blog/2026/08/copilot-studio-real-alm-for-agents-and.html&quot;&gt;the ALM loop that keeps an agent rebuildable&lt;/a&gt;, both ended the same way: with a list of war stories and their workarounds. The runtime that silently ignores your knowledge source. The &lt;code&gt;pac&lt;/code&gt; auth profile that another workspace quietly re-points. The Quick Find view update that fails with an opaque error code. Each one cost me real hours, each one got written down, and I felt rather good about that.&lt;/p&gt;

&lt;p&gt;Then a thought started nagging at me. A war story you have to remember is a war story you will eventually forget. Six months from now, in a different tenant, I will hit one of these again and I will not remember the article, and neither will you. Writing it down was necessary. It was nowhere near sufficient. So, this time around, I turned the whole inventory into a tool.&lt;/p&gt;

&lt;h2&gt;From paragraphs to guardrails&lt;/h2&gt;

&lt;p&gt;The idea fits in one sentence: every war story becomes something that executes. Not documentation you consult, but a check that runs, refuses, and tells you why. I call them guardrails, and as it turns out, they come in exactly two flavors. &lt;strong&gt;Preflight checks&lt;/strong&gt; refuse before anything is touched: the runtime trap and the authentication requirement from the knowledge article, the profile drift from the ALM article, each now a refusal with the fix named in the message. &lt;strong&gt;Error translators&lt;/strong&gt; let a call happen and catch a known failure on the way back: when the infamous &lt;code&gt;0x80040216&lt;/code&gt; shows up on a savedquery update, you get the war story and the working alternatives, not the opaque code.&lt;/p&gt;

&lt;p&gt;Every guardrail lives under a contract from day one: one file, one test proving the failure it prevents is real, and one plain-language entry in a war-stories document that travels with the code. No entry, no guardrail. The document I would have written anyway now cannot drift from the code, because the code refuses to exist without it. And every refusal carries a typed exit code whose range means something: 10 through 19 says your environment is misconfigured, 20 says you asked for a route the platform is known to break. You may be asking why anyone should care about an exit code taxonomy. Very simple: CI cares. A pipeline that can tell &quot;the environment is wrong&quot; from &quot;the operation failed&quot; is one a human can diagnose from the log line alone.&lt;/p&gt;

&lt;p&gt;The natural home for all of this was a PowerShell module. But I work with an AI coding agent driving a lot of the platform interaction now, and an agent cannot benefit from a guardrail it cannot see, so the same engine got a second door: the MCP server, seven task-shaped verbs over the same guardrails. My favorite is the one with no cmdlet behind it, &lt;code&gt;explain-failure&lt;/code&gt;: when any operation is refused, the agent can ask what just happened and gets the war story, the fix, and a warning against working around the refusal, straight from the exit code registry, without another round trip.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; I did not build this alone, and pretending otherwise would be strange in 2026. I paired with an AI coding agent (Claude Code, for the record) for the entire build. Keep in mind the division of labor, though: every platform fact in the tool was verified against a live environment before it was allowed to ship, because an AI pair is exactly like every other tool in my shop. Trust, but verify.&lt;/p&gt;

&lt;h2&gt;How my own tool tried to lie to me&lt;/h2&gt;

&lt;p&gt;Once the first guardrails were in place, we ran a red team pass against our own code: thirteen tests written to attack the guardrails the way a careless caller would, written before any fixes, expected to fail. Seven landed. Seven! The worst were bypasses of the flagship guardrail, which watched for &lt;code&gt;savedqueries(&lt;/code&gt; at the start of a request path. The very same forbidden request sailed right past it spelled as an absolute URL, or with a leading slash, or with the payload as a raw JSON string. Four spellings of one request, and the guardrail caught exactly one. Now, you may be thinking the fix is three more string checks. It is not! The fix was structural: normalize every request into one canonical shape before any check sees it, so the whole class of bypass dies at once. Suffice to say, every guardrail now gets a bypass hunt, red first, before it ships.&lt;/p&gt;

&lt;h2&gt;What live verification taught me, including about my own articles&lt;/h2&gt;

&lt;p&gt;The unit suite proves the code does what we intended. The live runs proved something better: that one of my published claims was too broad, and that the platform had two more surprises waiting. Three findings, in ascending order of embarrassment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First, the drift guardrail caught me. Me!&lt;/strong&gt; The very first live pipeline run refused to deploy, because the active &lt;code&gt;pac&lt;/code&gt; profile on my machine pointed at a completely different environment than the one I had pinned. The founding war story of the whole effort, firing on its author on day one. I could not have scripted a better validation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Second, my savedquery claim did not survive a third environment.&lt;/strong&gt; I reported that the &lt;code&gt;fetchxml&lt;/code&gt; update fails on every route, reproduced in two orgs. The integration test then found views that accept the very same call. We spent an afternoon hunting the discriminator: custom versus system table? No. The search index flag? No. Standard versus virtual versus elastic? Also no. The honest current claim is that the failure reproduces on most Quick Find views while a minority accept the call, and the discriminator is not yet identified. The guardrail was redesigned that same day, from refuse-up-front to attempt-and-translate, because refusing a route that sometimes works is its own kind of bug. If you figure out what separates the two, the comments are open and I will credit you properly!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Third, and this one should worry every CI pipeline you own: &lt;code&gt;pac&lt;/code&gt; can print an error and hand your shell a success code.&lt;/strong&gt; Three times in one session, the CLI wrote a clear &lt;code&gt;Error:&lt;/code&gt; line and returned 0: an argument error twice, and an environment resolution failure. Any pipeline gating on the exit code treats all three as success and keeps right on going. Before publishing that sentence I wanted to know &lt;em&gt;which&lt;/em&gt; part of &lt;code&gt;pac&lt;/code&gt; was doing it, so I ran the same failing command two ways. Through the MSI-installed &lt;code&gt;pac&lt;/code&gt; on my &lt;code&gt;PATH&lt;/code&gt;, a two-line wrapper that calls &lt;code&gt;pac.launcher.exe&lt;/code&gt;: error printed, exit code 0. Through the versioned &lt;code&gt;pac.exe&lt;/code&gt; underneath it: same error, exit code 1. As it turns out, &lt;code&gt;pac&lt;/code&gt; itself tells the truth. It is the launcher that discards the exit code, on every command I tried, and that launcher is version 1.0.7 with a file date of September 2019, still shipping in the current installer. A seven-year-old shim, silently defeating a command Microsoft documents as safe to run in a build pipeline!&lt;/p&gt;

&lt;p&gt;This is not new. It sits on Microsoft&#39;s tracker as &lt;a href=&quot;https://github.com/microsoft/powerplatform-build-tools/issues/912&quot;&gt;issue #912&lt;/a&gt; (the launcher) and &lt;a href=&quot;https://github.com/microsoft/powerplatform-build-tools/issues/1027&quot;&gt;issue #1027&lt;/a&gt; (the symptom, with other users chiming in), both still open as I write this, and I have &lt;a href=&quot;https://github.com/microsoft/powerplatform-build-tools/issues/912#issuecomment-5309857001&quot;&gt;added the isolation above to the thread&lt;/a&gt;. In the meantime the tool refuses to believe a zero exit code when the output carries an &lt;code&gt;Error:&lt;/code&gt; line, and I would gently suggest your scripts do the same, or install &lt;code&gt;pac&lt;/code&gt; as a &lt;code&gt;dotnet tool&lt;/code&gt;, which skips the launcher entirely. The lesson generalizes: verify the exit codes of every CLI you automate, through the exact path your pipeline will call, because a wrapper on the &lt;code&gt;PATH&lt;/code&gt; can defeat a perfectly honest executable underneath it.&lt;/p&gt;

&lt;h2&gt;The bottom line&lt;/h2&gt;

&lt;p&gt;A war story is a liability until it is executable. The moment the savedquery story became a guardrail with a test, the test caught my own overclaim, and documentation cannot do that. That is the whole reason pac-copilot-kit exists, and the discriminator hunt from finding number two is genuinely open, so if you run it against your own environments, I would love to hear what you find.&lt;/p&gt;

&lt;p&gt;My next installment will be the one I promised last time: the walk from proof of concept to production, with the actual promotion done rather than theorized. Stay tuned!&lt;/p&gt;

&lt;p&gt;Until next post!&lt;/p&gt;

&lt;p&gt;MG.-&lt;br /&gt;Mariano Gomez Bent&lt;br /&gt;Former Microsoft BizApps MVP&lt;/p&gt;
</content><link rel='replies' type='application/atom+xml' href='http://www.theworkbench.blog/feeds/5958214116179228133/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5285970135510371565/5958214116179228133' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/5958214116179228133'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/5958214116179228133'/><link rel='alternate' type='text/html' href='http://www.theworkbench.blog/2026/08/copilot-studio-turning-war-stories-into.html' title='Copilot Studio | Turning War Stories into Guardrails: Introducing pac-copilot-kit'/><author><name>Mariano Gomez</name><uri>http://www.blogger.com/profile/13267738662239812289</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiE6oIsiofbLlE_uzUGLv64DYdGPmN9xurCqrbDNWs_axq3WbN9RRlG9IYNI50LMAK96HtSeFIVKWTjqw_URFhfBlHzutaAfUXMkUvW4RK9sO0z6Rt8YyNaETF0D4MdiQ/s113/Mariano+Gomez+-+Square+Headshot+%28website%29.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5285970135510371565.post-8320439968606332011</id><published>2026-08-28T07:00:00.000-04:00</published><updated>2026-08-28T07:00:00.120-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ALM"/><category scheme="http://www.blogger.com/atom/ns#" term="Copilot Studio"/><category scheme="http://www.blogger.com/atom/ns#" term="Dataverse"/><category scheme="http://www.blogger.com/atom/ns#" term="DevOps"/><category scheme="http://www.blogger.com/atom/ns#" term="Power Platform"/><title type='text'>Copilot Studio | Real ALM for Agents (and the One Click That Breaks Your Pipeline)</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;text-align: center;&quot;&gt;&lt;img alt=&quot;Real ALM for Copilot Studio agents: the repo is the agent&quot; height=&quot;630&quot; src=&quot;https://raw.githubusercontent.com/dgpblogster/theworkbench-assets/main/covers/cover-09-agent-alm.png&quot; style=&quot;height: auto; max-width: 100%;&quot; width=&quot;1200&quot; /&gt;&lt;/div&gt;
&lt;p&gt;In my &lt;a href=&quot;https://www.theworkbench.blog/2026/08/copilot-studio-adding-dataverse.html&quot;&gt;previous Copilot Studio article&lt;/a&gt;, I was working on a support agent grounded in Dataverse knowledge articles and a custom table of curated case resolutions, and I showed how to wire those knowledge sources up with the Web API rather than clicking them together in the maker portal. What I did not explain there is why I cared so much about scripting them in the first place.&lt;/p&gt;

&lt;p&gt;The reason is a question every consultant eventually asks about a sandbox: what happens to all of this when somebody refreshes the environment from production? The answer, of course, is that it all goes away. The agent, its topics, its knowledge sources, the custom table behind them, all of it. And the honest follow-up is the question that actually matters: how long would it take me to build it back?&lt;/p&gt;

&lt;p&gt;If the answer is &quot;about a week, and I would probably forget something,&quot; then what you have is not a project. It is a sandcastle! So, in this article I will walk through the lifecycle loop I settled on for a Copilot Studio agent, score it honestly against what Microsoft prescribes today, and show you the three places where the prescribed path quietly runs out of road. All three are documented by Microsoft, in public, and not one of them is where you would expect.&lt;/p&gt;

&lt;h2&gt;What Microsoft actually prescribes&lt;/h2&gt;

&lt;p&gt;Let me start by being fair to the guidance, because it is good guidance and I follow most of it. Microsoft&#39;s ALM story for Copilot Studio agents is &lt;strong&gt;solutions&lt;/strong&gt;, and it has not wavered: &lt;a href=&quot;https://learn.microsoft.com/microsoft-copilot-studio/authoring-solutions-overview&quot;&gt;the documentation&lt;/a&gt; is explicit that to move an agent between environments you need a custom solution rather than the default one. On top of that sits a set of &lt;a href=&quot;https://learn.microsoft.com/microsoft-copilot-studio/guidance/alm&quot;&gt;ALM golden rules&lt;/a&gt; familiar to anyone who has shipped a model-driven app: work in solutions, use a custom publisher and prefix, use environment variables for anything that changes between environments, deploy as managed downstream, and automate it all with source control. Microsoft also states a floor: at least three environments, development, test, and production.&lt;/p&gt;

&lt;p&gt;Alongside that, there is now a genuine authoring story for code-first people. The Visual Studio Code extension is generally available, the &lt;code&gt;pac copilot&lt;/code&gt; command group gives you a local workspace of YAML files, and the pitch includes Git and pull requests. Suffice to say, this is a real improvement over clicking everything in a portal and hoping.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; read the banner at the top of those pages. Every ALM and code-first article I could find says the features described are powered by the &lt;em&gt;standard harness&lt;/em&gt;. If you are building in the newer agent experience, hold that thought.&lt;/p&gt;

&lt;h2&gt;The loop I actually ran&lt;/h2&gt;

&lt;p&gt;Here is the cycle, start to finish. Nothing exotic, and that is rather the point:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;pac copilot init                          # scaffold a local agent workspace
# author agent.mcs.yml, settings.mcs.yml, topics/*.mcs.yml
# validate every file offline, before anything touches the tenant
pac copilot pack --publisher-prefix wrk --solution-name WorkbenchSupportAssistant
pac solution import --path .\WorkbenchSupportAssistant.zip --publish-changes
git add . &amp;amp;&amp;amp; git commit&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A few things to note here. The offline validation step is mine, not Microsoft&#39;s, and it is the single highest-value habit in the loop: a Power Fx string containing a colon followed by a space parses wrong and surfaces as strange runtime behavior rather than an error, and catching that locally costs a second where catching it after deployment costs an afternoon. &lt;code&gt;pac copilot pack&lt;/code&gt; is the interesting one: &lt;a href=&quot;https://learn.microsoft.com/power-platform/developer/cli/reference/copilot&quot;&gt;Microsoft documents it&lt;/a&gt; as a purely local operation that needs no authentication and no environment, which makes it, in their own words, safe to run in a build pipeline. Hold that thought too. And then, always, a commit. That repository is not a nicety. It &lt;em&gt;is&lt;/em&gt; the recovery plan.&lt;/p&gt;

&lt;h2&gt;Scoring myself against the golden rules&lt;/h2&gt;

&lt;table&gt;
&lt;tr&gt;&lt;th&gt;Golden rule&lt;/th&gt;&lt;th&gt;What I did&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Always work in the context of solutions&lt;/td&gt;&lt;td&gt;Followed. Everything packs and imports as one solution.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Use a custom publisher and prefix&lt;/td&gt;&lt;td&gt;Followed, from day one.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Use environment variables for what changes between environments&lt;/td&gt;&lt;td&gt;Followed to the letter. The escalation address lives in an environment variable, never in a topic node.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Do not customize outside a development environment&lt;/td&gt;&lt;td&gt;Followed, though see below, because I only had one.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Create separate solutions only when you need independent deployment&lt;/td&gt;&lt;td&gt;Followed. One solution.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Automate ALM with source control&lt;/td&gt;&lt;td&gt;Followed, and then some.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Deploy as managed downstream&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;Not followed.&lt;/strong&gt; Unmanaged only.&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;

&lt;p&gt;Six out of seven, and the miss is not laziness. It follows from a platform constraint that will hit you too: &lt;strong&gt;Copilot Studio&#39;s Dataverse knowledge sources only read the agent&#39;s own environment.&lt;/strong&gt; My agent had to be grounded on data that lived in one specific environment, so the agent had to live there as well. That collapses the three-environment model into one, and with nothing downstream to deploy to, managed solutions buy you nothing but ceremony. That is a limitation of my situation, not a flaw in the guidance. Keep in mind, though, that &quot;the data only exists in one place&quot; is an extremely common shape for a grounded agent, and the standard ALM advice does not really account for it.&lt;/p&gt;

&lt;h2&gt;Where the prescribed path runs out&lt;/h2&gt;

&lt;p&gt;Three gaps, and I found all three the hard way before finding them in the documentation afterward.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First: the command built for pipelines has no pipeline.&lt;/strong&gt; Microsoft explicitly documents &lt;code&gt;pac copilot pack&lt;/code&gt; as safe to run in a build pipeline, so I went looking for the pipeline that accepts it. Let me be precise, because this claim deserves precision: agents are solution components, so the generic solution tasks deploy one just fine, and Microsoft&#39;s Build Tools FAQ says as much. What the &lt;a href=&quot;https://learn.microsoft.com/power-platform/alm/devops-build-tool-tasks&quot;&gt;Power Platform Build Tools task list&lt;/a&gt; does not contain is anything that knows an agent from a canvas app: no task wraps &lt;code&gt;pac copilot&lt;/code&gt;, and the only &quot;agent&quot; in that reference is the build agent running the job. &lt;a href=&quot;https://learn.microsoft.com/power-platform/alm/devops-github-available-actions&quot;&gt;GitHub Actions for Power Platform&lt;/a&gt; is the same story. I could not find a single Microsoft sample pipeline that runs &lt;code&gt;pac copilot pack&lt;/code&gt; followed by &lt;code&gt;pac solution import&lt;/code&gt;. You can build it yourself, and it is not hard, but the CLI was designed for CI, documented for CI, and the CI tooling has not caught up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Second: the newer agent experience has no documented lifecycle at all.&lt;/strong&gt; Copilot Studio now ships more than one runtime, and I wrote last time about how that split silently breaks knowledge grounding. It splits the ALM story more sharply. Every solutions, import/export, and code-first article carries the standard-harness banner, and the newer experience&#39;s documented lifecycle is create, build, test, publish, monitor, all inside a single environment. No promotion, no solutions, no source control story. There is a related asymmetry worth knowing: in the newer experience each publish creates a new version, which &lt;a href=&quot;https://learn.microsoft.com/microsoft-copilot-studio/agents-experience/publication-fundamentals-publish-channels&quot;&gt;the documentation&lt;/a&gt; frames as a way to tell the live release from newer drafts. That is a counter, and I found no documented way to browse history or restore a previous version; rollback lives at the solution layer instead. So an agent can carry two unrelated version numbers, and only one can take you backward. I will say only what I can defend: I found no documentation of a restore capability, which is not the same as proving there is none.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Third, and this is the one that changes how you should build: there is a manual click in the middle of the automated path.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;The one click that breaks your pipeline&lt;/h2&gt;

&lt;p&gt;Microsoft publishes a &lt;a href=&quot;https://learn.microsoft.com/troubleshoot/power-platform/copilot-studio/lifecycle-management/agents-solution-mapping&quot;&gt;troubleshooting article&lt;/a&gt; for agents whose components go missing after a solution import. The symptoms it lists are topics, environment variables, tools, child agents, and, yes, knowledge sources, all present in the source environment and absent in the target. The cause it gives is that the imported solution does not automatically include components added to the agent&#39;s source solution after the fact. The prescribed fix is to open the agent&#39;s commands menu, choose &lt;strong&gt;Advanced&lt;/strong&gt;, select &lt;strong&gt;Add required objects&lt;/strong&gt;, and export again, and the instruction is explicit that you repeat this before every export.&lt;/p&gt;

&lt;p&gt;Read that as an operations person rather than a maker. The fidelity of your deployment depends on a human remembering to click something in a portal &lt;em&gt;before every export&lt;/em&gt;. There is no CLI equivalent I could find, and no pipeline step, so your automated deployment has a manual precondition that fails silently when skipped. Nothing errors. The solution imports cleanly. The agent simply answers as though a knowledge source it needs was never there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; the documentation is candid about the general shape of this. The &lt;a href=&quot;https://learn.microsoft.com/microsoft-copilot-studio/authoring-solutions-import-export&quot;&gt;import and export guidance&lt;/a&gt; says outright that some components, custom topics and knowledge sources among them, might not be included depending on how they were created or linked; there is a published list of things that are simply not solution-aware (Application Insights settings, manual authentication, Direct Line and web channel security, deployed channels, sharing); and after any import you must reconfigure authentication and publish before sharing. None of it is hidden. It is just easy not to go looking until something breaks.&lt;/p&gt;

&lt;h2&gt;Why scripting the components sidesteps all of it&lt;/h2&gt;

&lt;p&gt;In the previous article I showed that a Dataverse knowledge source is really four ordinary Dataverse records, and that one header on each create call, &lt;code&gt;MSCRM.SolutionUniqueName&lt;/code&gt;, lands them inside your solution. That was written as a way around a tooling limitation. It turns out to be the whole answer to the click.&lt;/p&gt;

&lt;p&gt;The knowledge wiring in this project is a PowerShell script. It takes a token from the signed-in Azure CLI, talks to the Dataverse Web API directly, and creates the custom table, its columns, the table search configurations, the knowledge-source components, and the associations between them, every call carrying that header. The shape is the part worth stealing:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;$org   = &quot;https://yourorg.crm.dynamics.com&quot;
$token = (az account get-access-token --resource $org | ConvertFrom-Json).accessToken

$headers = @{
  Authorization             = &quot;Bearer $token&quot;
  &quot;Content-Type&quot;            = &quot;application/json&quot;
  &quot;MSCRM.SolutionUniqueName&quot; = &quot;WorkbenchSupportAssistant&quot;   # every call. no exceptions.
}

$search = Invoke-RestMethod -Method Post -Headers $headers `
  -Uri &quot;$org/api/data/v9.2/dvtablesearchs&quot; `
  -Body (@{ name = &quot;wrk_case_resolutions_search&quot; } | ConvertTo-Json)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A few things to note here. The token comes from the Azure CLI, so there is no credential in the script and nothing to rotate in source control. The solution header sits in the shared &lt;code&gt;$headers&lt;/code&gt; block rather than being passed per call, which is the whole discipline in one line: you cannot forget it on the fourth create if it was never optional to begin with. And it deliberately depends on no &lt;code&gt;pac&lt;/code&gt; state whatsoever, because &lt;code&gt;pac&lt;/code&gt; authentication profiles are machine-global rather than per-project, and I have had a different workspace on the same laptop quietly re-point the profile my project was using. A recovery script that assumes the CLI is still aimed where you left it is a recovery script that fails on the one day you need it.&lt;/p&gt;

&lt;p&gt;Because the knowledge sources come from a script I can run again, I never depend on a solution remembering them. The failure mode Microsoft documents cannot happen to a component I rebuild from source in ninety seconds. The same goes for the custom table, its columns, and the flow behind the escalation topic, which had to be created with a direct call to the Dataverse workflow endpoint carrying that same header, because the tooling I first reached for created a flow that never appeared in the solution at all.&lt;/p&gt;

&lt;p&gt;And when something resists the API entirely, there is one more door. Last time I noted that updating a Quick Find view&#39;s find columns through the Web API fails with an opaque error, and that the maker portal was the fastest way through. Here is the part I promised to come back to: the same change can be made at the solution level, without clicking anything.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;pac solution export --name WorkbenchSupportAssistant --path .\out
# unzip, edit the savedquery block in customizations.xml, re-zip
pac solution import --path .\WorkbenchSupportAssistant.zip --publish-changes&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I have come to think of this as solution surgery, and the safety rule is the whole trick: a Quick Find view is an ordinary Dataverse view, not an agent component. Microsoft warns that changing an agent&#39;s components directly inside the solution breaks export and import, and that warning is correct and worth obeying. The rest of the solution is ordinary Power Platform metadata and behaves accordingly. Keep in mind the import round-trips everything else in the solution, so if you have a flow in there, verify afterward that it came back activated. Mine did, but I checked.&lt;/p&gt;

&lt;h2&gt;What this approach costs, honestly&lt;/h2&gt;

&lt;p&gt;The scripts are real code, and real code needs maintenance. They encode undocumented record shapes that can change in any release, so my recovery plan has a shelf life and needs re-verification rather than blind trust. I gave up managed solutions, and with them the layering protections that stop someone editing production by hand. Parts of what I built sit outside the supported path, which is a fine place for a proof of concept and a rather different proposition for production. And I gave up the three-environment model, so I have no test environment standing between an idea and the only environment that exists, and I want to be blunt that source control does nothing whatsoever about that.&lt;/p&gt;

&lt;p&gt;That last point deserves precision, because I have seen it oversold. What all of this bought me is that &lt;strong&gt;the environment became replaceable, not unnecessary.&lt;/strong&gt; Rebuildability protects you from losing work; a test environment protects you from breaking something in front of people, and those are two different problems with two different answers. Anyone who tells you a tidy repository removes the need for proper environments is selling you something. Here is what reconciles the two, though, and it is why I would do it all again: &lt;strong&gt;being able to rebuild the whole agent from source is precisely what makes a second environment cheap.&lt;/strong&gt; When everything comes back by running a script, standing up a test environment stops being a project and becomes an afternoon pointed at a different URL. The discipline that looks like a substitute for the three-environment model is actually what makes it affordable.&lt;/p&gt;

&lt;h2&gt;So what does production actually look like?&lt;/h2&gt;

&lt;p&gt;Which is the question you are probably asking from inside your own proof of concept, and it has three answers worth knowing now.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Placement is not a choice.&lt;/strong&gt; Dataverse knowledge reads only the agent&#39;s own environment, so the production agent lives in production, right alongside live customer data. Promoting a grounded agent is not like promoting a self-contained app into a tidy empty environment; you are placing an agent inside your live business system, and every decision about what it can see and say gets more serious the moment you do.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The solution carries less than you would hope.&lt;/strong&gt; The agent, its topics, your table&#39;s schema, the environment variable definitions, and your flows travel. Authentication, deployed channels, sharing, and Application Insights settings do not, so a production deployment ends with an afternoon of hand configuration, plus pointing your environment variables at real values instead of the test address you have used since day one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your curated knowledge is data, not schema.&lt;/strong&gt; The table definition rides along in the solution; the reviewed content in it does not. So you choose between regenerating that content against production data and migrating the rows you already reviewed, and as it turns out, regeneration is the tempting answer and probably the wrong one, because synthesis is not deterministic and re-running it discards the editorial judgement that was the expensive part. Migrate the reviewed rows, including the ones you deliberately rejected, so the audit trail stays intact.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; one sequencing trap. You cannot convert an unmanaged solution into a managed one in place. A development environment holding your work unmanaged is exactly right, and you export managed from it into production. But if you ever decide that environment should instead &lt;em&gt;receive&lt;/em&gt; managed solutions, the unmanaged solution has to come out first, and it takes your agent with it. Decide what each environment is for before the first managed export, not after.&lt;/p&gt;

&lt;p&gt;And then there is the part that is not really an ALM question at all: the guardrail work a sandbox lets you write down as caveats and a production rollout does not. That deserves its own article rather than a paragraph, so consider this the honest state of play. I know the shape production takes, I know which parts of my proof of concept were a loan rather than a discount, and the walk from one to the other is something I will be exploring in a future article, with the actual promotion done rather than theorized. Stay tuned!&lt;/p&gt;

&lt;h2&gt;The bottom line&lt;/h2&gt;

&lt;p&gt;Microsoft&#39;s ALM prescription for Copilot Studio agents is sound, and I followed six of its seven golden rules without argument. But the guidance assumes a shape of project that a grounded agent frequently cannot have, the tooling stops one step short of the automation it openly advertises, and the step that guarantees your components actually travel is a click somebody has to remember before every single export.&lt;/p&gt;

&lt;p&gt;So my rule ended up simple enough to fit on one line: &lt;strong&gt;the repository defines the agent, and an environment is a place I can put it.&lt;/strong&gt; One environment today, because the data gave me no choice. Two on the way to production, because promotion demands it. And the reason the second one will cost an afternoon rather than a fortnight is the same discipline that made the first one survivable.&lt;/p&gt;

&lt;p&gt;If you have automated a Copilot Studio agent deployment end to end, especially if you have found a way to handle the add-required-objects step without a human in the loop, please drop a note in the comments describing how. That is the piece I would most like to stop doing by hand.&lt;/p&gt;

&lt;p&gt;Until next post!&lt;/p&gt;

&lt;p&gt;MG.-&lt;br /&gt;Mariano Gomez Bent&lt;br /&gt;Former Microsoft BizApps MVP&lt;/p&gt;
</content><link rel='replies' type='application/atom+xml' href='http://www.theworkbench.blog/feeds/8320439968606332011/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5285970135510371565/8320439968606332011' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/8320439968606332011'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/8320439968606332011'/><link rel='alternate' type='text/html' href='http://www.theworkbench.blog/2026/08/copilot-studio-real-alm-for-agents-and.html' title='Copilot Studio | Real ALM for Agents (and the One Click That Breaks Your Pipeline)'/><author><name>Mariano Gomez</name><uri>http://www.blogger.com/profile/13267738662239812289</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiE6oIsiofbLlE_uzUGLv64DYdGPmN9xurCqrbDNWs_axq3WbN9RRlG9IYNI50LMAK96HtSeFIVKWTjqw_URFhfBlHzutaAfUXMkUvW4RK9sO0z6Rt8YyNaETF0D4MdiQ/s113/Mariano+Gomez+-+Square+Headshot+%28website%29.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5285970135510371565.post-3602864419858771009</id><published>2026-08-26T07:00:00.000-04:00</published><updated>2026-08-26T08:28:11.938-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ALM"/><category scheme="http://www.blogger.com/atom/ns#" term="Architecture"/><category scheme="http://www.blogger.com/atom/ns#" term="Copilot Studio"/><category scheme="http://www.blogger.com/atom/ns#" term="Dataverse"/><category scheme="http://www.blogger.com/atom/ns#" term="Web API"/><title type='text'>Copilot Studio | Adding Dataverse Knowledge Sources with the Web API (the &quot;UI-Only&quot; Rule That Isn&#39;t)</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;text-align: center;&quot;&gt;&lt;img alt=&quot;Adding Copilot Studio Dataverse knowledge sources with the Web API&quot; height=&quot;630&quot; src=&quot;https://raw.githubusercontent.com/dgpblogster/theworkbench-assets/main/covers/cover-08-dataverse-knowledge.png&quot; style=&quot;height: auto; max-width: 100%;&quot; width=&quot;1200&quot; /&gt;&lt;/div&gt;
&lt;p&gt;Not long ago, I set out to build a support agent in Microsoft Copilot Studio for a product in the Dynamics ecosystem: the kind of agent that answers customer questions grounded in &lt;em&gt;your&lt;/em&gt; content, meaning Dataverse knowledge articles plus a custom table of curated case resolutions. No web browsing, no model knowledge; the knowledge sources are the agent&#39;s entire world. And since I treat agents as code (YAML in source control, &lt;code&gt;pac copilot pack&lt;/code&gt;, solution import, repeatable deployments), I ran head-first into a wall you may have hit yourself:&lt;/p&gt;
&lt;!-- On publish: link &quot;treat agents as code&quot; to the git-vs-pac source control article --&gt;

&lt;p&gt;&lt;strong&gt;Dataverse knowledge sources can only be added through the Copilot Studio UI.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That is what the documentation implies, that is the line the community tooling holds, and &lt;code&gt;pac copilot pack&lt;/code&gt; enforces it: put a Dataverse source definition in your workspace&#39;s &lt;code&gt;knowledge/&lt;/code&gt; folder and pack rejects it outright. Public website sources? Fine in YAML. SharePoint? Fine. Dataverse tables, the one source type that lives &lt;em&gt;in the same platform as the agent itself&lt;/em&gt;, are clicks only.&lt;/p&gt;

&lt;p&gt;Except, as it turns out, that is not quite true. Copilot Studio is built &lt;em&gt;on&lt;/em&gt; Dataverse. Every knowledge source you click together in the maker portal materializes as plain Dataverse rows, and anything that is plain Dataverse rows can be created with the Web API: inside a solution, from a pipeline, repeatably. So, in this article I will walk through the recipe, battle-tested twice now: once in a production-grade Dynamics 365 CE org, and once in a freshly provisioned vanilla environment, where it surfaced several extra lessons you will want in hand before trying this yourself.&lt;/p&gt;

&lt;div style=&quot;background: rgba(245, 158, 11, 0.12); border-left: 4px solid #f59e0b; border-radius: 6px; padding: 0.9rem 1.2rem; margin: 1.6rem 0;&quot;&gt;&lt;strong&gt;&amp;#9888;&amp;#65039; NOT OFFICIALLY SUPPORTED:&lt;/strong&gt; none of this is documented or supported by Microsoft. These are the shapes Copilot Studio happens to write today, discovered by inspecting what the maker UI creates, and they can change in any release. Use this for dev and ALM automation with your eyes open, and always verify the result in the maker portal afterward.&lt;/div&gt;

&lt;h2&gt;First, which kind of agent? (This one matters)&lt;/h2&gt;

&lt;p&gt;Copilot Studio now ships &lt;strong&gt;two agent runtimes&lt;/strong&gt;, which Microsoft calls &lt;em&gt;harnesses&lt;/em&gt;. The &lt;strong&gt;standard harness&lt;/strong&gt; is the classic experience: topics, the full Knowledge pane, solution-first ALM. The &lt;strong&gt;GitHub Copilot harness&lt;/strong&gt; is the new streamlined agent builder, with its Build, Preview, and Evaluate tabs and a model picker.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dataverse knowledge is a standard-harness feature.&lt;/strong&gt; The new experience does not offer Dataverse in its Add knowledge dialog at all, and here is the trap that cost me an evening: if you create your knowledge component via the API against a new-experience agent, &lt;strong&gt;the Knowledge panel will happily display it&lt;/strong&gt; (the panel reads the same component table) &lt;strong&gt;while the runtime silently never queries it&lt;/strong&gt;. The agent will cheerfully answer from its own general knowledge and tell you it has no documents. No error anywhere!&lt;/p&gt;

&lt;div class=&quot;separator&quot; style=&quot;text-align: center;&quot;&gt;&lt;img alt=&quot;The new-experience agent builder showing the API-created Curated Case Resolutions source listed in the Knowledge panel, displayed but never used&quot; height=&quot;573&quot; src=&quot;https://raw.githubusercontent.com/dgpblogster/theworkbench-assets/main/images/post-08/fig1-new-experience-knowledge-panel.png&quot; style=&quot;height: auto; max-width: 100%;&quot; width=&quot;1837&quot; /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: center; font-size: 0.9em; margin: 0.4em 0 1.4em;&quot;&gt;&lt;em&gt;The trap in action: the new experience displays the API-created Dataverse source in its Knowledge panel...&lt;/em&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;text-align: center;&quot;&gt;&lt;img alt=&quot;The new-experience agent answering a scanner question from general model knowledge, stating it has no device docs in the workspace&quot; height=&quot;893&quot; src=&quot;https://raw.githubusercontent.com/dgpblogster/theworkbench-assets/main/images/post-08/fig2-new-experience-ungrounded.png&quot; style=&quot;height: auto; max-width: 100%;&quot; width=&quot;1832&quot; /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: center; font-size: 0.9em; margin: 0.4em 0 1.4em;&quot;&gt;&lt;em&gt;...but its runtime never queries it. Note the answer opens with &amp;quot;I don&amp;#39;t have any device docs or network data in this workspace&amp;quot; while the source sits right there in the panel.&lt;/em&gt;&lt;/div&gt;

&lt;p&gt;So check your agent&#39;s runtime before wiring anything, as follows:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;GET /api/data/v9.2/bots?$select=name,schemaname,template&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A &lt;code&gt;template&lt;/code&gt; of &lt;code&gt;default-2.1.0&lt;/code&gt; (or a similar &lt;code&gt;default-*&lt;/code&gt;) means the standard harness; proceed. A &lt;code&gt;template&lt;/code&gt; of &lt;code&gt;cliagent-1.0.0&lt;/code&gt; means the new experience, and this recipe will not ground there. To build standard-harness agents, flip the &lt;strong&gt;&quot;New experience&quot; toggle off&lt;/strong&gt; on the Copilot Studio homepage, or pick &lt;strong&gt;&quot;Other ways to build.&quot;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; a fun fact discovered along the way. In the new experience, the default &quot;Search all websites&quot; knowledge chip is not a knowledge source row at all; it is an &lt;code&gt;enableWebSearch&lt;/code&gt; flag inside the bot&#39;s configuration JSON. Different harness, different bookkeeping.&lt;/p&gt;

&lt;h2&gt;Why bother?&lt;/h2&gt;

&lt;p&gt;If you are happy clicking the UI once, close this tab with my blessing. But if any of the following apply, read on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your dev environment is a &lt;strong&gt;sandbox that gets refreshed&lt;/strong&gt; from production periodically, wiping everything you built. Mine was! With the agent rebuildable from a git repository, knowledge sources included, a refresh becomes a non-event instead of a lost week.&lt;/li&gt;
&lt;li&gt;You deploy the &lt;strong&gt;same agent to multiple environments&lt;/strong&gt; and want identical knowledge wiring in each without a click-checklist taped to the monitor.&lt;/li&gt;
&lt;li&gt;You want knowledge sources &lt;strong&gt;inside your solution&lt;/strong&gt;, versioned and transported like everything else you ship.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Finding the shapes: mirror a working example&lt;/h2&gt;

&lt;p&gt;The trick that unlocked everything is almost embarrassingly simple: find a working reference implementation &lt;em&gt;in the same environment&lt;/em&gt; and mirror its record shapes. If your org runs Dynamics 365 Customer Service, Microsoft&#39;s own first-party copilots ship as solution components, and their Dataverse knowledge sources are sitting right there in the very same tables yours will use; that is what I mirrored. In a clean environment with no first-party copilots, make your own reference: click one knowledge source together in the UI, then query what it wrote. Either way, the shapes reveal themselves:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;GET /api/data/v9.2/botcomponents?$filter=componenttype eq 16
  &amp;amp;$select=name,data,schemaname&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Four moving parts emerge from that inspection:&lt;/p&gt;

&lt;table&gt;
&lt;tr&gt;&lt;th&gt;Piece&lt;/th&gt;&lt;th&gt;What it is&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;dvtablesearch&lt;/code&gt;&lt;/td&gt;&lt;td&gt;A named &quot;table search&quot; configuration: the knowledge source&#39;s identity&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;dvtablesearchentity&lt;/code&gt;&lt;/td&gt;&lt;td&gt;A child row per table being searched, pointing at its parent via the &lt;code&gt;DVTableSearch&lt;/code&gt; navigation property and naming the table in &lt;code&gt;entitylogicalname&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;botcomponent&lt;/code&gt; (componenttype 16)&lt;/td&gt;&lt;td&gt;The agent-side knowledge component; its &lt;code&gt;data&lt;/code&gt; column holds KnowledgeSourceConfiguration YAML whose &lt;code&gt;skillConfiguration&lt;/code&gt; names the &lt;code&gt;dvtablesearch&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;botcomponent_dvtablesearch&lt;/code&gt;&lt;/td&gt;&lt;td&gt;The N:N association tying the component to the search config&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;

&lt;p&gt;A few things to note before we build. First, the component attaches to your agent through its &lt;strong&gt;&lt;code&gt;parentbotid&lt;/code&gt; lookup&lt;/strong&gt;, and not, as you might reasonably guess, through the &lt;code&gt;bot_botcomponent&lt;/code&gt; N:N relationship. That relationship exists in the schema, but for knowledge components it stays empty; the parent lookup is what the platform actually uses. Ask me how long that one took! Second, on naming, because it bit me: &lt;code&gt;dvtablesearch.name&lt;/code&gt; is a &lt;strong&gt;machine-style identifier&lt;/strong&gt; (the component YAML references it verbatim), while the friendly display name your makers see in the Knowledge tab lives on the &lt;code&gt;botcomponent.name&lt;/code&gt;. Keep them distinct on purpose.&lt;/p&gt;

&lt;h2&gt;Step 0: the custom table (if you are grounding on one)&lt;/h2&gt;

&lt;p&gt;My scenario grounds the agent on a custom curated-resolutions table. If you only need standard tables like &lt;code&gt;knowledgearticle&lt;/code&gt; (which, by the way, is a base Dataverse table, present even in environments without any Dynamics 365 apps), feel free to skip ahead to Step 1. Otherwise, three gotchas await, and the third one is the difference between an agent that grounds and one that shrugs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gotcha 1: Memo columns want an explicit format.&lt;/strong&gt; Declare &lt;code&gt;Format: TextArea&lt;/code&gt; on each &lt;code&gt;MemoAttributeMetadata&lt;/code&gt;. That is plain multi-line text, which is what the knowledge indexer expects, and a working column reads back with both &lt;code&gt;Format&lt;/code&gt; and &lt;code&gt;FormatName&lt;/code&gt; as &lt;code&gt;TextArea&lt;/code&gt;. Here is the whole table, one create:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;POST /api/data/v9.2/EntityDefinitions
MSCRM.SolutionUniqueName: WorkbenchSupportAssistant

{
  &quot;@odata.type&quot;: &quot;Microsoft.Dynamics.CRM.EntityMetadata&quot;,
  &quot;SchemaName&quot;: &quot;wrk_caseresolution&quot;,
  &quot;DisplayName&quot;: { &quot;LocalizedLabels&quot;: [ { &quot;Label&quot;: &quot;Case Resolution&quot;, &quot;LanguageCode&quot;: 1033 } ] },
  &quot;DisplayCollectionName&quot;: { &quot;LocalizedLabels&quot;: [ { &quot;Label&quot;: &quot;Case Resolutions&quot;, &quot;LanguageCode&quot;: 1033 } ] },
  &quot;OwnershipType&quot;: &quot;UserOwned&quot;,
  &quot;HasActivities&quot;: false,
  &quot;HasNotes&quot;: false,
  &quot;SyncToExternalSearchIndex&quot;: true,
  &quot;Attributes&quot;: [
    { &quot;@odata.type&quot;: &quot;Microsoft.Dynamics.CRM.StringAttributeMetadata&quot;,
      &quot;SchemaName&quot;: &quot;wrk_title&quot;, &quot;IsPrimaryName&quot;: true, &quot;MaxLength&quot;: 300,
      &quot;RequiredLevel&quot;: { &quot;Value&quot;: &quot;ApplicationRequired&quot; },
      &quot;DisplayName&quot;: { &quot;LocalizedLabels&quot;: [ { &quot;Label&quot;: &quot;Title&quot;, &quot;LanguageCode&quot;: 1033 } ] } },
    { &quot;@odata.type&quot;: &quot;Microsoft.Dynamics.CRM.MemoAttributeMetadata&quot;,
      &quot;SchemaName&quot;: &quot;wrk_symptom&quot;, &quot;Format&quot;: &quot;TextArea&quot;, &quot;MaxLength&quot;: 4000,
      &quot;DisplayName&quot;: { &quot;LocalizedLabels&quot;: [ { &quot;Label&quot;: &quot;Symptom&quot;, &quot;LanguageCode&quot;: 1033 } ] } },
    { &quot;@odata.type&quot;: &quot;Microsoft.Dynamics.CRM.MemoAttributeMetadata&quot;,
      &quot;SchemaName&quot;: &quot;wrk_resolution&quot;, &quot;Format&quot;: &quot;TextArea&quot;, &quot;MaxLength&quot;: 10000,
      &quot;DisplayName&quot;: { &quot;LocalizedLabels&quot;: [ { &quot;Label&quot;: &quot;Resolution&quot;, &quot;LanguageCode&quot;: 1033 } ] } }
  ]
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;One POST, one 204, done! And notice &lt;code&gt;SyncToExternalSearchIndex: true&lt;/code&gt; riding along in the create payload. Which brings us to...&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gotcha 2: Dataverse search must be on, and the org-level flag is just a column.&lt;/strong&gt; Copilot Studio&#39;s Dataverse knowledge rides Dataverse search. The admin-center setting (now &lt;em&gt;two&lt;/em&gt; checkboxes in the modern admin UI, one for &quot;AI and agent experiences&quot; and one for the global search bar) ultimately reflects a boolean on the organization record, and yes, you can flip it via API:&lt;/p&gt;

&lt;div class=&quot;separator&quot; style=&quot;text-align: center;&quot;&gt;&lt;img alt=&quot;The Power Platform admin center Dataverse search settings showing the two indexing checkboxes plus the Search behavior options&quot; height=&quot;718&quot; src=&quot;https://raw.githubusercontent.com/dgpblogster/theworkbench-assets/main/images/post-08/fig3-admin-dataverse-search.png&quot; style=&quot;height: auto; max-width: 100%;&quot; width=&quot;788&quot; /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: center; font-size: 0.9em; margin: 0.4em 0 1.4em;&quot;&gt;&lt;em&gt;The modern admin center splits search indexing into two scopes; the first checkbox (&amp;quot;AI and agent experiences&amp;quot;) is the one Copilot Studio knowledge rides on.&lt;/em&gt;&lt;/div&gt;

&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;PATCH /api/data/v9.2/organizations(&amp;lt;orgid&amp;gt;)
{ &quot;isexternalsearchindexenabled&quot;: true }&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;But budget real time for what happens next. On a freshly provisioned environment, &lt;strong&gt;initial index provisioning took about two hours&lt;/strong&gt; before my rows became searchable. And do not trust &lt;code&gt;GET /api/search/v1.0/status&lt;/code&gt; to tell you how it is going; it reported &quot;0 tables indexed&quot; &lt;em&gt;even while queries were returning hits&lt;/em&gt;. The only reliable readiness probe is an actual query:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;POST /api/search/v1.0/query
{ &quot;search&quot;: &quot;scanner&quot;, &quot;entities&quot;: [&quot;wrk_caseresolution&quot;], &quot;top&quot;: 5 }&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;For a table that already exists and needs the sync flag flipped, resist the urge to PATCH the entity definition, because &lt;strong&gt;metadata entities cannot be updated with PATCH&lt;/strong&gt;. Updates replace the whole definition: GET it, modify, PUT the &lt;em&gt;entire&lt;/em&gt; document back with an &lt;code&gt;MSCRM.MergeLabels: true&lt;/code&gt; header (or kiss your localized labels goodbye, since omitted properties reset to their defaults), then call the &lt;code&gt;PublishXml&lt;/code&gt; action. You may be asking whether all that ceremony is really necessary for one boolean. It is! Which is exactly why you set it at create time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gotcha 3: find columns ARE the search index.&lt;/strong&gt; This is the one that separates &quot;source attached&quot; from &quot;agent grounded.&quot; Dataverse search indexes the columns configured as &lt;strong&gt;find columns on the table&#39;s Quick Find view&lt;/strong&gt;, and a fresh custom table&#39;s Quick Find view has exactly one: the primary name. Your beautiful Symptom and Resolution text? Invisible to search, and therefore to your agent, until they are added.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; and here is the kicker. Updating the view&#39;s &lt;code&gt;fetchxml&lt;/code&gt; via the Web API fails with an opaque &lt;code&gt;0x80040216&lt;/code&gt; on every route I tried (record PATCH, single-property PUT), &lt;strong&gt;in two different orgs&lt;/strong&gt;, while &lt;code&gt;layoutxml&lt;/code&gt; on the very same record updates fine. Whatever message the maker UI sends, the Web API&#39;s &lt;code&gt;savedquery&lt;/code&gt; update path is not it. Budget one manual click-step: go to Power Apps, then &lt;strong&gt;Tables&lt;/strong&gt;, then your table, then &lt;strong&gt;Views&lt;/strong&gt;, open the Quick Find view, click &lt;strong&gt;Edit find table columns&lt;/strong&gt;, add your text columns, and &lt;strong&gt;Save and publish&lt;/strong&gt;. That is the fastest route, and it is the one I used here. Now, it is not the only route: the same change can be made at the solution level, by exporting the solution, editing the view definition inside it, and importing it back, which automates cleanly. That belongs to a bigger story about treating agents as source-controlled artifacts, and it is where I am headed next!&lt;/p&gt;

&lt;p&gt;Seed a few rows while you are here. An empty table grounds nothing!&lt;/p&gt;

&lt;h2&gt;Step 1: create the &lt;code&gt;dvtablesearch&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;First surprise: the entity-set name is &lt;code&gt;dvtablesearchs&lt;/code&gt;. Yes, really, that plural! Ask &lt;code&gt;$metadata&lt;/code&gt; if you do not believe me; I did, twice:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;POST /api/data/v9.2/dvtablesearchs
MSCRM.SolutionUniqueName: WorkbenchSupportAssistant

{ &quot;name&quot;: &quot;wrk_case_resolutions_search&quot; }&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That single property is genuinely the whole payload; verified. Machine-style name, remember: this exact string gets referenced by the component YAML in Step 3. And keep an eye on that &lt;code&gt;MSCRM.SolutionUniqueName&lt;/code&gt; header, because it is doing quiet but important work in every call in this article. It lands each row in &lt;strong&gt;your solution&lt;/strong&gt; instead of the default one, which is the whole ALM point of the exercise.&lt;/p&gt;

&lt;h2&gt;Step 2: add the table(s) to it&lt;/h2&gt;

&lt;p&gt;One &lt;code&gt;dvtablesearchentity&lt;/code&gt; per table the source searches, bound to its parent via the single-valued navigation property, as follows:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;POST /api/data/v9.2/dvtablesearchentities
MSCRM.SolutionUniqueName: WorkbenchSupportAssistant

{
  &quot;entitylogicalname&quot;: &quot;wrk_caseresolution&quot;,
  &quot;DVTableSearch@odata.bind&quot;: &quot;/dvtablesearchs(&amp;lt;dvtablesearch-guid&amp;gt;)&quot;
}&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Step 3: the &lt;code&gt;botcomponent&lt;/code&gt; that makes it a knowledge source&lt;/h2&gt;

&lt;p&gt;This is the piece that surprises people: the knowledge source your agent sees is a &lt;code&gt;botcomponent&lt;/code&gt; row of componenttype 16 whose &lt;code&gt;data&lt;/code&gt; column is... YAML. A &lt;code&gt;KnowledgeSourceConfiguration&lt;/code&gt; document whose &lt;code&gt;skillConfiguration&lt;/code&gt; property names the &lt;code&gt;dvtablesearch&lt;/code&gt; from Step 1, and whose &lt;code&gt;parentbotid&lt;/code&gt; lookup attaches it to your agent, all in one create:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;POST /api/data/v9.2/botcomponents
MSCRM.SolutionUniqueName: WorkbenchSupportAssistant

{
  &quot;name&quot;: &quot;Curated Case Resolutions&quot;,
  &quot;componenttype&quot;: 16,
  &quot;schemaname&quot;: &quot;wrk_WorkbenchSupportAssistant20.knowledge.wrk_case_resolutions&quot;,
  &quot;data&quot;: &quot;kind: KnowledgeSourceConfiguration\nsource:\n  kind: DataverseStructuredSearchSource\n  skillConfiguration: wrk_case_resolutions_search\n&quot;,
  &quot;parentbotid@odata.bind&quot;: &quot;/bots(&amp;lt;bot-guid&amp;gt;)&quot;
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That &lt;code&gt;data&lt;/code&gt; YAML, unfolded so you can actually read it:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;kind: KnowledgeSourceConfiguration
source:
  kind: DataverseStructuredSearchSource
  skillConfiguration: wrk_case_resolutions_search&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A few things to note here. The &lt;code&gt;schemaname&lt;/code&gt; follows the convention &lt;code&gt;&amp;lt;botSchemaName&amp;gt;.knowledge.&amp;lt;componentName&amp;gt;&lt;/code&gt;, and that is the &lt;strong&gt;bot&#39;s&lt;/strong&gt; schemaname, which you should read rather than guess: the same &lt;code&gt;GET /bots&lt;/code&gt; from the harness check gives it to you, and agents created in the new experience even carry a random suffix in theirs. And the friendly &lt;code&gt;name&lt;/code&gt; is what your makers will see in the Knowledge tab, so make it read like a knowledge source, not like a database object.&lt;/p&gt;

&lt;h2&gt;Step 4: tie the component to the search config&lt;/h2&gt;

&lt;p&gt;The N:N association that closes the loop:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;POST /api/data/v9.2/botcomponents(&amp;lt;component-guid&amp;gt;)/botcomponent_dvtablesearch/$ref

{ &quot;@odata.id&quot;: &quot;https://yourorg.crm.dynamics.com/api/data/v9.2/dvtablesearchs(&amp;lt;dvtablesearch-guid&amp;gt;)&quot; }&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Verify like the maker UI is watching&lt;/h2&gt;

&lt;p&gt;Open the agent in Copilot Studio and go to the &lt;strong&gt;Knowledge&lt;/strong&gt; page. Your source should be listed exactly as if you had clicked it together: name, table, status. Then comes the real test, and no publish is needed. Go straight into the test canvas and ask a question only your table can answer.&lt;/p&gt;

&lt;p&gt;When it works, it &lt;em&gt;really&lt;/em&gt; works! My test agent answered a scanner-connectivity question with the exact resolution from the seeded row, three citations pointing at &lt;code&gt;wrk_caseresolution&lt;/code&gt; records, and the canvas&#39;s activity map showing the knowledge searches with &quot;Curated Case Resolutions&quot; as the referenced source. Grounded, cited, and not one of those records touched by a mouse.&lt;/p&gt;

&lt;div class=&quot;separator&quot; style=&quot;text-align: center;&quot;&gt;&lt;img alt=&quot;The standard-harness test canvas showing the activity map with completed knowledge searches, the Referenced sources panel quoting the Curated Case Resolutions row, and the grounded answer with three wrk_caseresolution citations&quot; height=&quot;1069&quot; src=&quot;https://raw.githubusercontent.com/dgpblogster/theworkbench-assets/main/images/post-08/fig4-grounded-answer.png&quot; style=&quot;height: auto; max-width: 100%;&quot; width=&quot;1846&quot; /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: center; font-size: 0.9em; margin: 0.4em 0 1.4em;&quot;&gt;&lt;em&gt;The payoff: the activity map traces three knowledge searches, the inspection panel quotes the seeded resolution verbatim, and the answer carries three citations into wrk_caseresolution; every one of those records created by the Web API.&lt;/em&gt;&lt;/div&gt;

&lt;p&gt;Two behavioral notes for Dataverse sources generally, both documented but easy to miss:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Same environment only.&lt;/strong&gt; Copilot Studio&#39;s Dataverse knowledge reads the agent&#39;s own environment. If your data lives in environment A, the agent lives in environment A. Plan placement before you scaffold.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;End-user authentication must be &quot;Authenticate with Microsoft.&quot;&lt;/strong&gt; Dataverse sources ride the end user&#39;s identity; &quot;No authentication&quot; and &quot;Authenticate manually&quot; agents will not search them, and the docs are explicit about this one.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;The bigger picture&lt;/h2&gt;

&lt;p&gt;Everything Copilot Studio&#39;s maker portal does lands in Dataverse tables, and a surprising amount of the &quot;UI-only&quot; surface is only UI-only until you look at what the UI writes. The same technique has bailed me out more than once: find where the platform stores it, mirror the shape, create it in your solution. In turn, it converts &quot;click this checklist in every environment&quot; into &quot;run the deployment,&quot; and that is the difference between an ALM story with an asterisk and one without.&lt;/p&gt;

&lt;p&gt;One more time, because it matters: these are undocumented shapes, subject to change, so verify in the portal after every deployment. But with that caveat honored, a source-controlled agent is rebuildable end to end, knowledge sources included, and that is always a good thing.&lt;/p&gt;

&lt;p&gt;If you have automated your own way around a &quot;UI-only&quot; corner of Copilot Studio, or if these shapes have already drifted by the time you read this, please drop a note in the comments describing what you found; undocumented territory is exactly where comparing notes pays the most.&lt;/p&gt;

&lt;p&gt;Until next post!&lt;/p&gt;

&lt;p&gt;MG.-&lt;br /&gt;Mariano Gomez Bent&lt;br /&gt;Former Microsoft BizApps MVP&lt;/p&gt;
</content><link rel='replies' type='application/atom+xml' href='http://www.theworkbench.blog/feeds/3602864419858771009/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5285970135510371565/3602864419858771009' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/3602864419858771009'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/3602864419858771009'/><link rel='alternate' type='text/html' href='http://www.theworkbench.blog/2026/08/copilot-studio-adding-dataverse.html' title='Copilot Studio | Adding Dataverse Knowledge Sources with the Web API (the &quot;UI-Only&quot; Rule That Isn&#39;t)'/><author><name>Mariano Gomez</name><uri>http://www.blogger.com/profile/13267738662239812289</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiE6oIsiofbLlE_uzUGLv64DYdGPmN9xurCqrbDNWs_axq3WbN9RRlG9IYNI50LMAK96HtSeFIVKWTjqw_URFhfBlHzutaAfUXMkUvW4RK9sO0z6Rt8YyNaETF0D4MdiQ/s113/Mariano+Gomez+-+Square+Headshot+%28website%29.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5285970135510371565.post-5531898008819768436</id><published>2026-08-24T07:00:00.000-04:00</published><updated>2026-08-24T10:55:37.069-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Application Insights"/><category scheme="http://www.blogger.com/atom/ns#" term="Azure"/><category scheme="http://www.blogger.com/atom/ns#" term="Power Automate"/><category scheme="http://www.blogger.com/atom/ns#" term="Telemetry"/><title type='text'>Power Automate | Cloud Flow Telemetry with Azure Application Insights, Down to the Action Level</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;text-align: center;&quot;&gt;&lt;img alt=&quot;Power Automate cloud flow telemetry in Azure Application Insights, down to the action level&quot; height=&quot;630&quot; src=&quot;https://raw.githubusercontent.com/dgpblogster/theworkbench-assets/main/covers/cover-06-appinsights.png&quot; style=&quot;height: auto; max-width: 100%;&quot; width=&quot;1200&quot; /&gt;&lt;/div&gt;
&lt;p&gt;Longtime readers may remember that the very last article I published before this blog went quiet, back in August of 2021, covered monitoring &lt;a href=&quot;https://www.theworkbench.blog/2021/08/power-apps-application-monitoring-with.html&quot; target=&quot;_blank&quot;&gt;Power Apps with Azure Application Insights&lt;/a&gt;. So it is only fitting that I return to the subject from the other side of the platform: getting cloud flow telemetry out of Power Automate and into Application Insights, down to the action level.&lt;/p&gt;

&lt;p&gt;I will be honest about why I am writing this one. When I went looking for how this integration works, the picture I pieced together from searching around was incomplete, and once I configured it and read the actual tables, I understood why: the feature is admin-gated where makers cannot see it, and the telemetry schema is not what instinct suggests. I made the wrong assumption myself before the data corrected me! So, in this article I will walk through how the integration actually works, the schema surprise that changes how you query it, and the &lt;a href=&quot;https://learn.microsoft.com/en-us/kusto/?view=microsoft-fabric&quot; target=&quot;_blank&quot;&gt;Kusto queries&lt;/a&gt; that turn &quot;the flow failed&quot; into &quot;the flow failed at this action, with this error.&quot;&lt;/p&gt;

&lt;h2&gt;First things first: yes, the integration exists&lt;/h2&gt;

&lt;p&gt;Power Automate cloud flow telemetry can be exported to Azure Application Insights, and keep in mind, this is not a maker feature -- which is the source of a lot of the confusion. Canvas apps let a maker paste an instrumentation key into the app and start calling &lt;i&gt;&lt;b&gt;Trace()&lt;/b&gt;&lt;/i&gt;. Cloud flows offer nothing of the sort, and for a sound reason: flows have no client runtime to instrument. They execute server-side on shared infrastructure, so the integration was built as a platform-level export instead.&lt;/p&gt;

&lt;p&gt;The setup lives in the &lt;strong&gt;Power Platform admin center&lt;/strong&gt;, under &lt;strong&gt;Data export&lt;/strong&gt;. &lt;br /&gt;&lt;br /&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEii9WQxdgAykdSYgUhfp2ZM_5SH0AR4ddRhmq3AdVEnuxRxRlbp9oJVxUvKTVM1T4JTtfV_G2Zz6hjs0Xvki3N9MBzXBRDhWO0hF-h6UHy2mwad_USZ4lOHy5GxSmjzdEnAr6EpxjAs44_gt7Q_GcU-hBpmnLZ1qiAr5K4c6AG8UQJxu4UVqnKaaHawNPaE&quot;&gt;&lt;img style=&quot;width: 100%; height: auto;&quot; alt=&quot;&quot; data-original-height=&quot;818&quot; data-original-width=&quot;1622&quot; height=&quot;818&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEii9WQxdgAykdSYgUhfp2ZM_5SH0AR4ddRhmq3AdVEnuxRxRlbp9oJVxUvKTVM1T4JTtfV_G2Zz6hjs0Xvki3N9MBzXBRDhWO0hF-h6UHy2mwad_USZ4lOHy5GxSmjzdEnAr6EpxjAs44_gt7Q_GcU-hBpmnLZ1qiAr5K4c6AG8UQJxu4UVqnKaaHawNPaE=s1600&quot; width=&quot;1622&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: center; font-size: 0.9em; margin: 0.4em 0 1.4em;&quot;&gt;&lt;em&gt;Admin Center &amp;gt; Manage &amp;gt; Data Export&lt;/em&gt;&lt;/div&gt;&lt;br /&gt;An administrator creates an export package, selects Power Automate as the data type, chooses the environment, and points it at an Application Insights resource. Three requirements to check before you start, because each one can stop you cold:&lt;p&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The environment must be a Managed Environment.&lt;/strong&gt; The feature is gated behind this governance tier, which is exactly why a maker who goes looking for it at the flow level concludes it does not exist. It does; it just lives upstairs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;You need admin rights, not maker rights.&lt;/strong&gt; Power Platform administrator or Dynamics 365 administrator at the tenant level, plus environment or system administrator in the Dataverse environment.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;You need Contributor rights or better on the Application Insights resource&lt;/strong&gt; itself, over on the Azure side.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; once configured, expect up to 24 hours before telemetry starts appearing. Initial patience is required; ongoing latency is much lower. Do not spend that first afternoon rewriting your export package because the tables look empty!&lt;/p&gt;

&lt;h2&gt;The schema surprise: two tables, not one&lt;/h2&gt;

&lt;p&gt;Here is the assumption I walked in with: every action a flow executes, including the trigger, lands as an entry in the Application Insights &lt;code&gt;requests&lt;/code&gt; table. It seems reasonable. It is also not how the schema works, and the difference matters more than any other detail in this article. As it turns out, the export splits telemetry across two tables:&lt;/p&gt;

&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;Table&lt;/th&gt;&lt;th&gt;Contains&lt;/th&gt;&lt;th&gt;signalCategory value&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;requests&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Cloud flow runs&lt;/td&gt;&lt;td&gt;&quot;Cloud flow runs&quot;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;dependencies&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Cloud flow triggers and actions&lt;/td&gt;&lt;td&gt;&quot;Cloud flow triggers&quot; / &quot;Cloud flow actions&quot;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;

&lt;p&gt;In turn, if your monitoring queries only touch &lt;code&gt;requests&lt;/code&gt;, you are monitoring at the run level. You will know a flow failed. You will not know which action failed, how long each action took, or whether a specific connector call is degrading over time. All of that lives in &lt;code&gt;dependencies&lt;/code&gt;, waiting to be queried.&lt;/p&gt;

&lt;p&gt;There is a second, related detail that took me a moment to appreciate. When you configure the export package, you explicitly choose whether to export cloud flow runs, triggers, or actions. These are separate checkboxes! If you only selected runs during setup, action telemetry never leaves the platform, and no Kusto query will conjure it afterward. Granularity is decided at configuration time, not query time.&lt;/p&gt;

&lt;h2&gt;Run-level monitoring: the baseline&lt;/h2&gt;

&lt;p&gt;Run-level failure alerting is the right starting point, and the query looks as follows:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;requests
| where customDimensions.signalCategory == &quot;Cloud flow runs&quot;
| where success == false
| extend flowId = tostring(customDimensions[&quot;resourceId&quot;]),
         environmentId = tostring(customDimensions[&quot;environmentId&quot;])
| project timestamp, name, resultCode, duration, flowId, environmentId, operation_Id&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A few things to note here. Filtering on &lt;code&gt;signalCategory&lt;/code&gt; matters because your Application Insights instance may be collecting telemetry from many sources, and this keeps the flow signals separated from everything else. Filtering on &lt;code&gt;environmentId&lt;/code&gt; matters when multiple environments export to the same resource. Wire this query to an Azure Monitor alert rule, and you have real-time failure notifications by email, SMS, or webhook.&lt;/p&gt;

&lt;h2&gt;Action-level monitoring: where the real value is&lt;/h2&gt;

&lt;p&gt;Now, with the actions checkbox enabled in your export package, the &lt;code&gt;dependencies&lt;/code&gt; table gives you per-action telemetry:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;dependencies
| where customDimensions.signalCategory == &quot;Cloud flow actions&quot;
| where success == false
| extend flowId = tostring(customDimensions[&quot;resourceId&quot;])
| project timestamp, name, resultCode, duration, flowId, operation_Id&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;name&lt;/code&gt; field carries the action name exactly as it appears in the flow designer. This is the query that turns &quot;the invoice approval flow failed&quot; into &quot;the invoice approval flow failed at the vendor validation HTTP call with a 429.&quot;&lt;/p&gt;

&lt;p&gt;Correlation is what makes the two tables work together. The &lt;code&gt;operation_Id&lt;/code&gt; on an action&#39;s dependency row matches the &lt;code&gt;operation_Id&lt;/code&gt; on the parent run&#39;s request row, so you can join them:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;requests
| where customDimensions.signalCategory == &quot;Cloud flow runs&quot;
| where success == false
| join kind=inner (
    dependencies
    | where customDimensions.signalCategory == &quot;Cloud flow actions&quot;
    | where success == false
  ) on operation_Id
| project runTime = timestamp, flowRun = name,
          failedAction = name1, actionResult = resultCode1, actionDuration = duration1&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;What this join produces is a failure report at the granularity your support team actually needs: which run, which action, what error, and how long it ran before dying. The &lt;code&gt;name1&lt;/code&gt;, &lt;code&gt;resultCode1&lt;/code&gt;, and &lt;code&gt;duration1&lt;/code&gt; columns are Kusto&#39;s automatic renames for the joined table&#39;s fields; the projection gives them names a human can read.&lt;/p&gt;

&lt;p&gt;Beyond failures, the same table supports performance work. Percentile queries over &lt;code&gt;duration&lt;/code&gt; by action &lt;code&gt;name&lt;/code&gt; will surface connector calls that are slowly degrading long before they start timing out, which is exactly the kind of early signal run-level monitoring can never provide.&lt;/p&gt;

&lt;h2&gt;Three caveats before you turn everything on&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;NOTE 1:&lt;/strong&gt; ingestion cost scales with granularity. Application Insights bills per GB ingested, and action-level export on a busy flow with Apply to each loops generates a row per action, per iteration, per run. A flow processing a few thousand records nightly can produce telemetry volume that surprises you at invoice time. Scope the export deliberately: enable action-level export for the flows that justify it rather than blanketing the environment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE 2:&lt;/strong&gt; validate scope nesting behavior empirically. If your flows use the Try/Catch pattern built on Scope containers, test how nested actions surface before you write production alert queries. Build a flow with a deliberately failing action inside a scope, run it, and inspect what lands in &lt;code&gt;dependencies&lt;/code&gt;. Ten minutes with a test flow will teach you more than any amount of reading, and your failure queries should be written against observed behavior rather than assumptions. I know so as I&#39;ve done so myself!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE 3:&lt;/strong&gt; treat the export as telemetry, not as a system of record. Microsoft is explicit that small data losses can occur due to transient service issues. The flow run history inside the Power Automate portal remains the transactional, authoritative record. Application Insights is for alerting, dashboards, and trend analysis, not for audit-grade completeness.&lt;/p&gt;

&lt;h2&gt;What about custom telemetry from inside a flow?&lt;/h2&gt;

&lt;p&gt;The export covers runs, triggers, and actions, but you may be asking, &quot;where is the Trace() function for flows?&quot; Very simple: there is no native equivalent. If you need business-level events -- &quot;vendor matched,&quot; &quot;payment batch released&quot; -- the pattern is an HTTP action posting directly to the Application Insights ingestion endpoint at &lt;code&gt;https://dc.services.visualstudio.com/v2/track&lt;/code&gt;, or preferably the regional endpoint from your resource&#39;s connection string. No authentication header is required; the instrumentation key inside the payload identifies the target resource:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;name&quot;: &quot;Microsoft.ApplicationInsights.Event&quot;,
  &quot;time&quot;: &quot;@{utcNow()}&quot;,
  &quot;iKey&quot;: &quot;&amp;lt;your-instrumentation-key&amp;gt;&quot;,
  &quot;data&quot;: {
    &quot;baseType&quot;: &quot;EventData&quot;,
    &quot;baseData&quot;: {
      &quot;name&quot;: &quot;InvoiceApprovalFlow.Step3.VendorMatched&quot;,
      &quot;properties&quot;: {
        &quot;flowRunId&quot;: &quot;@{workflow()[&#39;run&#39;][&#39;name&#39;]}&quot;,
        &quot;flowName&quot;: &quot;@{workflow()[&#39;name&#39;]}&quot;,
        &quot;environment&quot;: &quot;@{workflow()[&#39;tags&#39;][&#39;environmentName&#39;]}&quot;,
        &quot;vendorId&quot;: &quot;@{variables(&#39;vendorId&#39;)}&quot;
      },
      &quot;measurements&quot;: {
        &quot;recordsProcessed&quot;: 42,
        &quot;durationMs&quot;: 1830
      }
    }
  }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A few things to note about this payload. Events posted this way land in the &lt;code&gt;customEvents&lt;/code&gt; table. Swap &lt;code&gt;baseType&lt;/code&gt; to &lt;code&gt;TraceData&lt;/code&gt; with a &lt;code&gt;message&lt;/code&gt; and &lt;code&gt;severityLevel&lt;/code&gt; for trace-style logging, or to &lt;code&gt;ExceptionData&lt;/code&gt; for structured errors. And carrying the flow run ID in the properties gives you a correlation key back to the platform-exported tables, so your business events join cleanly against the runs and actions they belong to.&lt;/p&gt;

&lt;h3&gt;Centralize it in a child flow&lt;/h3&gt;

&lt;p&gt;Do not scatter this HTTP action across every flow you own! Build it once as a child flow -- call it something like &quot;LogTelemetry&quot; -- and have every parent flow call it as a single action. The child flow takes three inputs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Event name&lt;/strong&gt; (text): the dotted event identifier, such as &lt;code&gt;InvoiceApprovalFlow.Step3.VendorMatched&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Properties&lt;/strong&gt; (text): a JSON string of custom dimensions, parsed and merged into the payload inside the child&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Severity&lt;/strong&gt; (text or number): drives whether the child posts EventData, TraceData, or ExceptionData&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In turn, the child flow owns the instrumentation key, the endpoint URL, and the payload envelope. When the ingestion endpoint changes, when you rotate the key, or when you decide to enrich every event with the environment name, you change one flow instead of forty. Parent flows stay clean: one &quot;Run a child flow&quot; action per telemetry point, and no HTTP plumbing in sight.&lt;/p&gt;

&lt;p&gt;The same child flow pairs naturally with Scope-based error handling. Wrap your business logic in a Try scope, follow it with a Catch scope configured to run after &quot;has failed&quot; and &quot;has timed out,&quot; and inside the Catch use &lt;code&gt;result(&#39;Try&#39;)&lt;/code&gt; to extract the failed action details and pass them to LogTelemetry as an ExceptionData payload. That gives you structured failure telemetry carrying the actual action error, not just a run that shows red in the portal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; two constraints to know before you commit to this pattern. The HTTP connector requires premium licensing, and the raw post does not work if your Application Insights resource enforces Entra ID-only ingestion. In that case, a small Azure Function relay running the real SDK is the better shape, since a flow cannot easily perform the token acquisition against Azure Monitor scopes.&lt;/p&gt;

&lt;p&gt;And to save you the scaffolding: I built LogTelemetry exactly as described and published it as an importable solution on GitHub, over at &lt;a href=&quot;https://github.com/dgpblogster/power-automate-logtelemetry&quot;&gt;github.com/dgpblogster/power-automate-logtelemetry&lt;/a&gt;. It uses no connectors that require authentication, so the import prompts for nothing; you replace two clearly-marked CONFIG values, turn it on, and start logging. The README walks the import click by click.&lt;/p&gt;

&lt;h2&gt;The bottom line&lt;/h2&gt;

&lt;p&gt;Power Automate telemetry in Application Insights is real, admin-configured, and gated behind Managed Environments. Runs live in &lt;code&gt;requests&lt;/code&gt;; triggers and actions live in &lt;code&gt;dependencies&lt;/code&gt;. If your monitoring stops at the &lt;code&gt;requests&lt;/code&gt; table, you have built a smoke detector that tells you the building is on fire without telling you which room. The &lt;code&gt;dependencies&lt;/code&gt; table, an export package with the actions checkbox enabled, and one join on &lt;code&gt;operation_Id&lt;/code&gt; are all it takes to do better.&lt;/p&gt;

&lt;p&gt;If you have wired up this export yourself, or found other signals hiding in these tables worth querying, please drop a note in the comments describing your experience; telemetry patterns get better every time somebody shares what they found in the data.&lt;/p&gt;

&lt;p&gt;Until next post!&lt;/p&gt;

&lt;p&gt;MG.-&lt;br /&gt;Mariano Gomez Bent&lt;br /&gt;Former Microsoft BizApps MVP&lt;/p&gt;
</content><link rel='replies' type='application/atom+xml' href='http://www.theworkbench.blog/feeds/5531898008819768436/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5285970135510371565/5531898008819768436' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/5531898008819768436'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/5531898008819768436'/><link rel='alternate' type='text/html' href='http://www.theworkbench.blog/2026/08/power-automate-cloud-flow-telemetry.html' title='Power Automate | Cloud Flow Telemetry with Azure Application Insights, Down to the Action Level'/><author><name>Mariano Gomez</name><uri>http://www.blogger.com/profile/13267738662239812289</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiE6oIsiofbLlE_uzUGLv64DYdGPmN9xurCqrbDNWs_axq3WbN9RRlG9IYNI50LMAK96HtSeFIVKWTjqw_URFhfBlHzutaAfUXMkUvW4RK9sO0z6Rt8YyNaETF0D4MdiQ/s113/Mariano+Gomez+-+Square+Headshot+%28website%29.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/a/AVvXsEii9WQxdgAykdSYgUhfp2ZM_5SH0AR4ddRhmq3AdVEnuxRxRlbp9oJVxUvKTVM1T4JTtfV_G2Zz6hjs0Xvki3N9MBzXBRDhWO0hF-h6UHy2mwad_USZ4lOHy5GxSmjzdEnAr6EpxjAs44_gt7Q_GcU-hBpmnLZ1qiAr5K4c6AG8UQJxu4UVqnKaaHawNPaE=s72-c" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5285970135510371565.post-8241138433724031971</id><published>2026-08-21T07:00:00.000-04:00</published><updated>2026-08-21T07:00:00.121-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Business Central"/><category scheme="http://www.blogger.com/atom/ns#" term="Integration"/><category scheme="http://www.blogger.com/atom/ns#" term="Power Automate"/><category scheme="http://www.blogger.com/atom/ns#" term="Power Platform"/><title type='text'>Power Automate | Design Every Flow So a Resubmit Is Always Safe</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;text-align: center;&quot;&gt;&lt;img alt=&quot;Design every Power Automate flow so a resubmit is always safe&quot; height=&quot;630&quot; src=&quot;https://raw.githubusercontent.com/dgpblogster/theworkbench-assets/main/covers/cover-05-resubmit.png&quot; style=&quot;height: auto; max-width: 100%;&quot; width=&quot;1200&quot; /&gt;&lt;/div&gt;
&lt;p&gt;If there is one question I have learned to ask of every single action in every Power Automate flow I build, it is this one: &lt;b&gt;what happens if this runs twice?&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;Throughout our Business Central to Dynamics 365 CE billing integration, the same project from my previous articles in this series, we made the answer to that question a design requirement. Every flow had to produce the same correct end state whether an event arrived once, twice, or five times, and whether the runs came from the webhook, a retry, or a human clicking resubmit weeks later. So, in this article I will walk through why that requirement pays for itself many times over, the patterns that satisfy it, and the two match-key mistakes we made on the way to getting it right. Yes, it took us three attempts!&lt;/p&gt;
&lt;!--On publish: link &quot;my previous articles&quot; to the FlowAgent post--&gt;

&lt;h2&gt;Why events arrive more than once&lt;/h2&gt;

&lt;p&gt;Keep in mind, if your integration is triggered by webhooks, replays are not an edge case. They are the operating environment!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The source system fires on modification, not creation.&lt;/strong&gt; Our flows trigger on Business Central&#39;s customer ledger, where a single business event can touch several entries, and each touch fires the trigger. Guards filter the noise, but the same document can legitimately come through the front door repeatedly.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Repairs happen by resubmission.&lt;/strong&gt; When a run fails -- a permissions gap, a defect, an outage -- the natural fix is: repair the cause, resubmit the stored run. The trigger payload replays against the flow as it exists now.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Testing happens by resubmission.&lt;/strong&gt; The fastest way to verify a flow change against real data is to resubmit a known run and inspect the result. If reprocessing old events corrupts data, you have lost your best testing tool.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Late events reprocess old ground.&lt;/strong&gt; A payment applied to an invoice months after posting triggers a reconcile over records your flow already created.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, you may be asking, &quot;&lt;b&gt;&lt;i&gt;can I not just guard against each of these individually?&lt;/i&gt;&lt;/b&gt;&quot; You certainly can, and you will be maintaining those guards forever. Or you can adopt the single stronger property that covers all of them at once: every run converges the target system to the source system&#39;s current truth, no matter how many times it executes.&lt;/p&gt;

&lt;h2&gt;Sync the state, not the event&lt;/h2&gt;

&lt;p&gt;The foundational pattern is a mental shift. An event-applying flow says &quot;an invoice was posted, so create an invoice.&quot; A state-converging flow says &quot;an invoice exists in the source; make the target match it.&quot; The first duplicates on replay. The second cannot, because its writes are defined relative to what is already there.&lt;/p&gt;

&lt;p&gt;Concretely, our invoice sync is a reconciling upsert. It looks up the target invoice by the source system&#39;s document number. If it does not exist, it creates it with its lines. If it does exist, it updates the header and then reconciles the lines: match each source line to a target line, update the matches in place, create the missing, and delete the orphans that no longer exist in the source. The payment sync works exactly the same way, reconciling the payment&#39;s application detail rows against what the source says is currently applied, deleting rows the source no longer has, right down to deleting the payment header itself when zero applications remain.&lt;/p&gt;

&lt;p&gt;In turn, a reconcile has a property an append never has: it is self-healing. When we later fixed a line-mapping defect, the reconcile did not just handle new invoices correctly; rerunning it against previously damaged invoices replaced the malformed lines with correct ones. The repair tool was the flow itself!&lt;/p&gt;

&lt;h2&gt;Let the source system name things&lt;/h2&gt;

&lt;p&gt;Idempotency needs a key, and the right key is the source system&#39;s own identity for the record, carried into the target where the flow can find it again. Our payment headers are named by the Business Central document number, which makes the name double as the idempotency key: any rerun finds the header it created before. Invoices are looked up by the posted invoice number. Nothing is keyed by anything the flow generates, because a generated key is different on every run, which is precisely the property you do not want.&lt;/p&gt;

&lt;p&gt;Getting this right at the line level took us three attempts, and the two failures are worth studying, because they will save you from repeating them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Attempt one: match lines by name.&lt;/strong&gt; As it turns out, this failed immediately, because the target system overwrites the line description with the product name on product-based lines. Our matching field was being silently rewritten by the platform after every create, so matches always missed, and every reconcile deleted and recreated every line. The lesson: never match on a field the target system considers its own to modify.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Attempt two: match lines by product ID.&lt;/strong&gt; This one worked for months. Then it failed structurally the day we met an invoice with two lines sharing one product -- a maintenance credit billed as a negative line on the same product code it credits. The reconcile saw the product already present and concluded the credit line was already synced. It was not a bug in the matching code; the key itself could not distinguish the rows. Worse, the failure mode was silent: the reconcile ran, reported success, and structurally could never repair the missing line. Suffice to say, a key that is merely usually unique is a defect with a delayed fuse.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Attempt three: match lines by the source system&#39;s line number,&lt;/strong&gt; stamped into a dedicated field on every line the flow writes. Business Central&#39;s line numbers are the source&#39;s own identity for the row: unique within the document, stable across time, and meaningless for the target platform to overwrite. Every line create and update stamps it; matching and orphan detection key on it. This is the version that survived every scenario we could throw at it.&lt;/p&gt;

&lt;p&gt;The general rule that falls out: &lt;strong&gt;the match key must be the source system&#39;s identity for the row, persisted in the target, and owned by nobody else.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A quick aside before moving on: both failures were diagnosed in minutes rather than an afternoon of clicking through run history, because we develop these flows with Claude Code reading run actions directly through FlowAgent. The agent walked the failed runs, found the exact action and input responsible each time, and in the second case also exposed that the product-keyed reconcile could never have repaired the damage. That workflow is a story of its own, and I told it in my FlowAgent article; here it is enough to say that resubmit-safe design and run forensics compose beautifully -- one gives you a safe repair tool, and the other tells you precisely when to use it.&lt;/p&gt;
&lt;!--On publish: link &quot;my FlowAgent article&quot; to the Building Power Automate Flows with Claude Code and FlowAgent post--&gt;

&lt;h2&gt;Gate the side effects on change&lt;/h2&gt;

&lt;p&gt;Data writes converge naturally under a reconcile, but side effects -- notifications, timeline notes, cascade triggers -- duplicate cheerfully unless you design them not to. Two patterns handled every case we had:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Attach side effects to transitions, not to runs.&lt;/strong&gt; Our settlement timeline notes are written only when a detail row is actually created, only when a synced row is actually deleted, and only on the first sighting of a cancellation, guarded by &quot;the invoice is not already cancelled.&quot; A resubmitted run that changes nothing writes no note, because no transition occurred. We proved this on a live rerun: identical resubmit, zero writes, zero duplicate notes. The note trail reads like a history precisely because it records transitions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Write only when the value differs.&lt;/strong&gt; Our bundle cascade flow updates a child record only if its expiration differs from the parent&#39;s or the row is inactive. That single guard makes the cascade convergent: reprocessing is free, and a chain of cascading updates terminates on its own -- five runs, converged, no runaway, in our live verification. As a bonus we did not initially design for, the flow can safely react to writes made by other flows sharing its connection identity, because reacting to an already-correct value is a no-op.&lt;/p&gt;

&lt;h2&gt;The payoff: resubmit becomes your universal tool&lt;/h2&gt;

&lt;p&gt;Once every flow held the property, resubmission quietly became the answer to almost everything:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Repairs.&lt;/strong&gt; When the credit-line defect left two invoices wrong in the target system, the repair was: deploy the fix, resubmit the two failed runs. Both invoices converged to the source&#39;s truth to the penny, replacing the malformed legacy lines on the way. No repair script, no manual edits!&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Environment fixes.&lt;/strong&gt; When a run failed on a missing privilege in a fresh environment, the fix was the role assignment plus one resubmit, which completed the scenario and re-proved reconcile idempotence in that environment for free.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Verification.&lt;/strong&gt; Every flow change was tested by resubmitting a stored run and reading the results, precisely because reprocessing was guaranteed harmless.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Locked-record handling.&lt;/strong&gt; Even where the target system makes records read-only -- paid invoices, in our staging environment -- the reconcile wraps its work in a capture-state, reopen, sync, restore sequence, so a resubmit against a paid invoice succeeds and leaves the record exactly as it found it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Two caveats from the field&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; a resubmit replays the trigger payload against the &lt;em&gt;current&lt;/em&gt; flow definition. That is exactly what makes fix-then-resubmit work, but it has a sharp edge: if a deployment has left the flow stopped, a resubmit silently replays against the old definition, and the result can masquerade as a test outcome. It happened to us exactly once, which was enough to add &quot;verify the flow is active and read the definition back&quot; to the deployment checklist.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; idempotency does not replace concurrency control. Convergent runs racing each other can still interleave reads and writes. We run our webhook-triggered flows at concurrency one, not because the design needs it to be correct in sequence, but as cheap insurance that runs actually execute in sequence. The two properties compose: idempotency makes replays safe, and serialization makes overlaps impossible.&lt;/p&gt;

&lt;h2&gt;The checklist&lt;/h2&gt;

&lt;p&gt;For every flow, before it ships, walk through the following five questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Can I resubmit any historical run right now with no damage? If not, why not? Fix that first.&lt;/li&gt;
&lt;li&gt;Is every record the flow writes findable by a key the source system owns?&lt;/li&gt;
&lt;li&gt;Does the flow reconcile to current source truth, including deleting what the source no longer has?&lt;/li&gt;
&lt;li&gt;Is every notification and note attached to a transition, not to a run?&lt;/li&gt;
&lt;li&gt;Does every cascading write fire only when the value actually differs?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When it&#39;s all said and done, the cost is a habit of mind during design, and the return is an integration where the scariest phrase in operations, &quot;just run it again,&quot; is not scary at all. It is the standard procedure, and it is always safe.&lt;/p&gt;

&lt;p&gt;If you have your own resubmit war story, or a match key that failed you in a way I did not list here, please drop a note in the comments describing your experience; idempotency lessons are expensive to learn firsthand and free to learn from each other.&lt;/p&gt;

&lt;p&gt;Until next post!&lt;/p&gt;

&lt;p&gt;MG.-&lt;br /&gt;Mariano Gomez Bent&lt;br /&gt;Former Microsoft BizApps MVP&lt;/p&gt;
</content><link rel='replies' type='application/atom+xml' href='http://www.theworkbench.blog/feeds/8241138433724031971/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5285970135510371565/8241138433724031971' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/8241138433724031971'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/8241138433724031971'/><link rel='alternate' type='text/html' href='http://www.theworkbench.blog/2026/08/power-automate-design-every-flow-so.html' title='Power Automate | Design Every Flow So a Resubmit Is Always Safe'/><author><name>Mariano Gomez</name><uri>http://www.blogger.com/profile/13267738662239812289</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiE6oIsiofbLlE_uzUGLv64DYdGPmN9xurCqrbDNWs_axq3WbN9RRlG9IYNI50LMAK96HtSeFIVKWTjqw_URFhfBlHzutaAfUXMkUvW4RK9sO0z6Rt8YyNaETF0D4MdiQ/s113/Mariano+Gomez+-+Square+Headshot+%28website%29.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5285970135510371565.post-7677422031119209399</id><published>2026-08-19T07:00:00.000-04:00</published><updated>2026-08-19T07:00:00.121-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Azure Key Vault"/><category scheme="http://www.blogger.com/atom/ns#" term="Power Automate"/><category scheme="http://www.blogger.com/atom/ns#" term="Power Platform"/><category scheme="http://www.blogger.com/atom/ns#" term="Security"/><title type='text'>Power Automate | Getting Secrets Out of Your Flows with Azure Key Vault</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;text-align: center;&quot;&gt;&lt;img alt=&quot;Getting secrets out of your Power Automate flows with Azure Key Vault&quot; height=&quot;630&quot; src=&quot;https://raw.githubusercontent.com/dgpblogster/theworkbench-assets/main/covers/cover-04-keyvault.png&quot; style=&quot;height: auto; max-width: 100%;&quot; width=&quot;1200&quot; /&gt;&lt;/div&gt;
&lt;p&gt;The flow we inherited at the start of our Business Central to Dynamics 365 CE billing integration, the same project from my FlowAgent article, had a problem so common it barely registers as a problem anymore: the OAuth client secret it used to call Business Central&#39;s OData services was sitting in plaintext, inside a String environment variable, referenced by the flow as an ordinary parameter. Everything worked. Nothing was on fire. And that secret was quietly everywhere!&lt;/p&gt;
&lt;!--On publish: link &quot;my FlowAgent article&quot; to the Building Power Automate Flows with Claude Code and FlowAgent post--&gt;

&lt;p&gt;So, in this article I will walk through where &quot;everywhere&quot; turns out to be, and the migration we ran to fix it: a secret-type environment variable backed by Azure Key Vault, runtime retrieval inside the flow, secured inputs and outputs, and a cleanup pass that chased the old secret out of every place it had settled. I will also cover the one gotcha that failed our first live run, because keep in mind, it will fail yours too if you do not know about it.&lt;/p&gt;

&lt;h2&gt;Where a plaintext secret actually lives&lt;/h2&gt;

&lt;p&gt;When a secret sits in a flow definition or a plaintext environment variable, it does not live in one place. It lives in every place the definition travels:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Every solution export.&lt;/strong&gt; The environment variable&#39;s value rides along in the zip. Email that zip to a colleague, attach it to a ticket, or drop it in a shared folder, and the secret goes with it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Run history.&lt;/strong&gt; If the secret flows through an ordinary action, every run&#39;s inputs and outputs display it to anyone with permission to read run history -- and that audience is usually much larger than the audience you would knowingly hand a credential to.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Source control.&lt;/strong&gt; The moment you bring the solution under version control, which you absolutely should, the plaintext secret is in a commit, and commits are forever. Rotating the secret later does not un-commit the old one.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Backups and working files.&lt;/strong&gt; Any tooling that snapshots flow definitions -- designer copies, local backups, agent working files -- multiplies the copies further.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these is hypothetical. We found our inherited secret in the solution export, in the flow definition, in run history via the token request&#39;s visible inputs, and, once we started working, in the local backup snapshots our own tooling captured. The secret had more copies than the flow had actions!&lt;/p&gt;

&lt;p&gt;The fix Microsoft provides for exactly this scenario is the &lt;strong&gt;secret-type environment variable&lt;/strong&gt;: instead of storing a value, it stores a pointer to a secret in Azure Key Vault, and the flow retrieves the value at runtime. In turn, the vault becomes the only place the secret exists. Everything that travels -- the solution, the source tree, the definition -- carries only the pointer.&lt;/p&gt;

&lt;h2&gt;The migration, in the order that worked&lt;/h2&gt;

&lt;p&gt;We did this as a single day&#39;s remediation, and as it turns out, the sequencing mattered as much as the steps. Here is the order, with the reasoning.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1) Set up Key Vault RBAC first.&lt;/strong&gt; Secret-type environment variables only support Azure Key Vault as the store, and two identities need roles on the vault before anything else works: the person doing the setup needs to write the secret and pass the environment variable&#39;s save-time validation (Key Vault Secrets Officer covers both), and the Dataverse service principal needs Key Vault Secrets User so the platform can retrieve the value at runtime. Also confirm the Power Platform resource provider is registered on the subscription that owns the vault; it is a one-time thing, and a confusing failure if it is missing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2) Rotate by appending, and never look at the new secret.&lt;/strong&gt; We did not migrate the old secret; we replaced it. The new client secret was created on the app registration with an append, leaving the old credential intact so the running flow never broke during the migration, and piped directly from the creation command into the vault in a single step. The value was never displayed on screen, never in a file, never in shell history. Keep in mind: if your new secret is ever visible, you have created another copy to worry about. The pipe means there is nothing to clean up afterward.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3) Create the secret-type environment variable, and get the reference format right.&lt;/strong&gt; The variable lives in the solution like any other environment variable, but its value record contains only a pointer to the secret, and the form of that pointer is the part that stopped us cold, because it is not what instinct suggests. It is not the secret&#39;s URL from the Azure portal, and it is not a vault name and secret name pair. It is the secret&#39;s full Azure &lt;strong&gt;resource ID path&lt;/strong&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;/subscriptions/{subscription-id}/resourceGroups/{resource-group}/providers/Microsoft.KeyVault/vaults/{vault-name}/secrets/{secret-name}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A few things to note here. Every segment is required, including the &lt;code&gt;providers/Microsoft.KeyVault&lt;/code&gt; in the middle that nobody types from memory. If you create the variable through the maker portal, the form collects the pieces separately and assembles the path for you; if you create it through the Web API, or find yourself editing the value record directly, you supply the whole path yourself, and anything else fails validation. The save is also more than a format check: Dataverse resolves the path against Azure with your permissions at save time, so a typo in any segment, or a missing role on the vault, surfaces right there rather than at the first flow run. That is a kindness once you understand it, and a mystifying save error until you do!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; export the solution afterward and inspect it; you should see the resource path and nothing else. We made this check part of the verification, not an assumption.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4) Prove retrieval with a throwaway flow before touching the real one.&lt;/strong&gt; You may be asking, &quot;can I just test the retrieval from the Web API?&quot; Categorically no! Secrets in secret-type environment variables are only resolvable from inside flows and custom connectors, and that is by design. So we built a disposable test flow whose only job was to call &lt;code&gt;RetrieveEnvironmentVariableSecretValue&lt;/code&gt; and succeed, ran it, confirmed the retrieval, and deleted it. Two minutes of scaffolding, and the real flow&#39;s rewire started from a known-good vault path instead of debugging two things at once.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5) Rewire the flow, and secure both ends.&lt;/strong&gt; The real flow gained one action: a Dataverse unbound action call to &lt;code&gt;RetrieveEnvironmentVariableSecretValue&lt;/code&gt; for the new variable, with &lt;strong&gt;secured inputs and outputs&lt;/strong&gt; turned on. The token request was repointed to consume that action&#39;s output, and it got secured inputs and outputs as well. That second part is easy to forget and matters just as much: the client secret passes through the token request, and the bearer token that comes back is itself a credential. With both actions secured, neither the secret nor the token appears in run history anymore.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6) Add a graceful failure path.&lt;/strong&gt; A vault outage or a permissions change now has a single, well-defined failure point, so we gave it a dedicated handler: an alert email carrying the run ID and the likely causes, then a Terminate with a named error code. Without this, a retrieval failure dies mid-run with secured -- and therefore blank -- diagnostics, which brings us to the gotcha in a moment.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhiwa9cvh8TajkcxBTT-T_F0b_6Udz46RdQ9RkijSVxKcSKn7-xFORQwlqMLJ1v08Jzx63RmHuqx3ATjZpj81pHV3r4774fxZ2_CRflcx6XqOCPcjUG5UT9JhirT9g92hDVvJHZRz1zft3GpmGkr_FuPjyGfFwMvYo4RrWGH93b5djY8CAdmnYLLJiyMmyb&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;1048&quot; data-original-width=&quot;762&quot; height=&quot;419&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhiwa9cvh8TajkcxBTT-T_F0b_6Udz46RdQ9RkijSVxKcSKn7-xFORQwlqMLJ1v08Jzx63RmHuqx3ATjZpj81pHV3r4774fxZ2_CRflcx6XqOCPcjUG5UT9JhirT9g92hDVvJHZRz1zft3GpmGkr_FuPjyGfFwMvYo4RrWGH93b5djY8CAdmnYLLJiyMmyb=w306-h419&quot; width=&quot;306&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: center; font-size: 0.9em; margin: 0.4em 0 1.4em;&quot;&gt;&lt;em&gt;Graceful failure path&lt;/em&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;strong&gt;7) Clean up until a search proves you are done.&lt;/strong&gt; The old plaintext environment variable and the flow parameter that referenced it were deleted; a forgotten draft copy of the flow held the last dependency and had to go first, which the dependency checker told us. Then we searched the entire synced source tree for any fragment of the old secret and did not stop until the search came back empty. Local backup snapshots that predated the fix still contained it, so they went into &lt;code&gt;.gitignore&lt;/code&gt;. Only after the tree was provably secret-free did the repository get its first commit. That ordering is worth stating plainly: &lt;strong&gt;git init comes after the secret hunt, not before.&lt;/strong&gt;&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8) Hand the old secret off for rotation.&lt;/strong&gt; The old credential was left working throughout the migration and then handed to the system administrator to rotate, after checking whether anything else used it. In our case, something else probably did, which is its own small lesson: plaintext secrets get shared around precisely because they are visible, and you cannot assume you are the only consumer.&lt;/p&gt;

&lt;h2&gt;The gotcha: runtime RBAC follows the connection owner&lt;/h2&gt;

&lt;p&gt;Our first live-fire test failed at the secret retrieval with a BadRequest and no details, because secured outputs hide details by design. The error handling worked beautifully -- alert sent, run terminated cleanly, zero partial writes -- but the diagnosis took some actual thought, so here it is for free.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; Dataverse validates the vault RBAC of the calling user, and the calling user is the owner of the Dataverse &lt;em&gt;connection&lt;/em&gt; the flow uses, not the person who built the flow. As it turns out, our earlier test flow had succeeded because it ran on the author&#39;s own connection, and the author had vault roles. The production flow ran on a shared connection owned by a service account that had none. Granting Key Vault Secrets User to that service account fixed it, and the resubmitted run passed end to end.&lt;/p&gt;

&lt;p&gt;The practical rule: enumerate every identity that will ever own a connection your flow runs on, in every environment, and grant each one Secrets User on the vault. And when the retrieval fails with hidden diagnostics, check connection ownership before anything else. It is the most likely cause and the least visible one.&lt;/p&gt;

&lt;h2&gt;The payoff shows up at deployment time&lt;/h2&gt;

&lt;p&gt;Weeks later, when we deployed the solution to a staging environment through a Power Platform pipeline, the secret story was the part that just worked. The secret-type environment variable&#39;s vault reference traveled with the solution; no secret value needed to be supplied at import, because there is no secret value in the solution. The only per-environment chore is the RBAC grant for that environment&#39;s connection-owning identity, and the first live run tells you immediately whether you missed it, through the graceful failure path built in step 6.&lt;/p&gt;

&lt;p&gt;Now, compare that with the plaintext alternative, where every deployment either carries the secret in the zip or prompts someone to paste it in. Both of those mint fresh copies. It just doesn&#39;t work!&lt;/p&gt;

&lt;h2&gt;The other half: everything else into environment variables too&lt;/h2&gt;

&lt;p&gt;The secret was the dangerous case, but the same remediation pass moved every per-environment value out of the flow definition and into ordinary environment variables: tenant ID, company ID, environment name, target system record IDs, alert recipients, and a set of sandbox-only test toggles. Two decisions there proved right in hindsight:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Ship no values in the solution for anything environment-specific.&lt;/strong&gt; An import that prompts is a feature; a value that silently travels to the wrong environment is a defect waiting to be discovered in production.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Make the flow&#39;s behavior mode explicit.&lt;/strong&gt; A Sandbox/Production mode variable, with test toggles honored only in Sandbox mode, means production can never accidentally run with a test configuration. The gate rule lives in the flow once, not scattered across conditions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When it&#39;s all said and done, you end up with a flow definition that has no secrets, no environment names, no GUIDs, and no email addresses in it. Everything that varies is a variable, and the one thing that must not leak is a pointer to a vault. That is the whole trick, and a day of careful sequencing is all it costs.&lt;/p&gt;

&lt;p&gt;If you run this migration yourself, or if you have chased a plaintext secret through more hiding places than the ones I listed here, please drop a note in the comments describing your experience; these war stories are how the community keeps each other&#39;s credentials out of run history.&lt;/p&gt;

&lt;p&gt;Until next post!&lt;/p&gt;

&lt;p&gt;MG.-&lt;br /&gt;Mariano Gomez Bent&lt;br /&gt;Former Microsoft BizApps MVP&lt;/p&gt;
</content><link rel='replies' type='application/atom+xml' href='http://www.theworkbench.blog/feeds/7677422031119209399/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5285970135510371565/7677422031119209399' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/7677422031119209399'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/7677422031119209399'/><link rel='alternate' type='text/html' href='http://www.theworkbench.blog/2026/08/power-automate-getting-secrets-out-of.html' title='Power Automate | Getting Secrets Out of Your Flows with Azure Key Vault'/><author><name>Mariano Gomez</name><uri>http://www.blogger.com/profile/13267738662239812289</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiE6oIsiofbLlE_uzUGLv64DYdGPmN9xurCqrbDNWs_axq3WbN9RRlG9IYNI50LMAK96HtSeFIVKWTjqw_URFhfBlHzutaAfUXMkUvW4RK9sO0z6Rt8YyNaETF0D4MdiQ/s113/Mariano+Gomez+-+Square+Headshot+%28website%29.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/a/AVvXsEhiwa9cvh8TajkcxBTT-T_F0b_6Udz46RdQ9RkijSVxKcSKn7-xFORQwlqMLJ1v08Jzx63RmHuqx3ATjZpj81pHV3r4774fxZ2_CRflcx6XqOCPcjUG5UT9JhirT9g92hDVvJHZRz1zft3GpmGkr_FuPjyGfFwMvYo4RrWGH93b5djY8CAdmnYLLJiyMmyb=s72-w306-h419-c" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5285970135510371565.post-8965672142025405958</id><published>2026-08-17T07:00:00.000-04:00</published><updated>2026-08-17T07:00:00.177-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ALM"/><category scheme="http://www.blogger.com/atom/ns#" term="DevOps"/><category scheme="http://www.blogger.com/atom/ns#" term="Power Automate"/><category scheme="http://www.blogger.com/atom/ns#" term="Power Platform"/><title type='text'>One Solution, Two Source Trees: The Maker Portal&#39;s Git Integration vs pac</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;text-align: center;&quot;&gt;&lt;img alt=&quot;One Solution, Two Source Trees: maker portal Git integration versus pac&quot; height=&quot;630&quot; src=&quot;https://raw.githubusercontent.com/dgpblogster/theworkbench-assets/main/covers/cover-03-git-pac.png&quot; style=&quot;height: auto; max-width: 100%;&quot; width=&quot;1200&quot; /&gt;&lt;/div&gt;
&lt;p&gt;Early in our Business Central to Dynamics 365 CE billing integration, the same project I wrote about in my &lt;a href=&quot;https://www.theworkbench.blog/2026/08/building-power-automate-flows-with.html&quot; target=&quot;_blank&quot;&gt;FlowAgent article&lt;/a&gt;, we ran into a lesson that Microsoft&amp;#39;s own documentation only caught up to this spring, and that I have not seen written up from the trenches: there are now two official ways to put a Power Platform solution under source control, and they do not mix. At all! We learned this the day we put the solution source pac had cloned next to the solution source the maker portal&#39;s Git integration had committed, and found two completely different structures staring back at us.&lt;/p&gt;


&lt;p&gt;So, in this article I will walk through the two formats, how the collision showed up in our project, and why we picked the CLI side, with a couple of smaller git-versus-pac frictions at the end that you will want to know about before you start.&lt;/p&gt;

&lt;h2&gt;Two formats, one solution&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The classic XML format&lt;/strong&gt; is what &lt;code&gt;pac solution clone&lt;/code&gt;, &lt;code&gt;pac solution sync&lt;/code&gt;, and the traditional &lt;a href=&quot;https://learn.microsoft.com/power-platform/alm/solution-packager-tool&quot;&gt;SolutionPackager&lt;/a&gt; workflow produce. The solution&#39;s identity lives in &lt;code&gt;Other\Solution.xml&lt;/code&gt; and &lt;code&gt;Other\Customizations.xml&lt;/code&gt;, and the component files (workflow JSON, environment variable definitions, connection references) sit in a folder hierarchy alongside them. Keep in mind this is the format that pairs with the round-trip commands: &lt;code&gt;pac solution pack&lt;/code&gt; builds an importable zip from the folder, &lt;code&gt;pac solution import&lt;/code&gt; pushes it to the environment, and &lt;code&gt;pac solution sync&lt;/code&gt; pulls the environment&#39;s current state back down.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjVJtSD3ss43jgqAPw8AeYT3fO0BYpiChiqAqLmYQ18yTOGKgiwmbu2q4drMnjWHunrOUZsLZ-svgOsW_mzy-tvQPG5F9UsUKhjkNCZaBitiaio2EIawsk5nPznaBIYVGOpXStoM7OVsKwOHYecT1tkGbijuftmW81Kzhn8jxCfNOxIi4VbV0_2KF0agVKb&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;693&quot; data-original-width=&quot;633&quot; height=&quot;570&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjVJtSD3ss43jgqAPw8AeYT3fO0BYpiChiqAqLmYQ18yTOGKgiwmbu2q4drMnjWHunrOUZsLZ-svgOsW_mzy-tvQPG5F9UsUKhjkNCZaBitiaio2EIawsk5nPznaBIYVGOpXStoM7OVsKwOHYecT1tkGbijuftmW81Kzhn8jxCfNOxIi4VbV0_2KF0agVKb=w521-h570&quot; width=&quot;521&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div style=&quot;font-size: 0.9em; margin: 0.4em 0px 1.4em; text-align: center;&quot;&gt;&lt;em&gt;Classic XML Format&lt;/em&gt;&lt;/div&gt;&lt;br /&gt;&lt;strong&gt;The &lt;a href=&quot;https://learn.microsoft.com/power-platform/alm/solution-source-control-yaml-format&quot;&gt;YAML source control format&lt;/a&gt;&lt;/strong&gt; is what the maker portal&#39;s native &lt;a href=&quot;https://learn.microsoft.com/power-platform/alm/git-integration/overview&quot;&gt;Git integration&lt;/a&gt; writes. When you connect an environment or a solution to Azure DevOps from within Power Apps, every commit lands as a &lt;code&gt;solutions\&amp;lt;name&amp;gt;\solution.yml&lt;/code&gt; manifest with component folders at the repository root. I will be the first to tell you it is a genuinely nicer format to read: compact YAML, small focused diffs, and support for canvas apps and modern flows that the XML layout never handled. It is also, and this is the entire point of this article, a different representation that the classic tooling chain does not interoperate with.&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjsbZTOjRDQMljVlwCsdza-yWxMUBTwiRt0v4V9beS7O6Z6SUwbpMRDoyFnIq5eGw6b-OVk7kRmtdbguUAIg1kdMpyEN3IbsXKiCYDoqwntqOcF2p1B9AnnWiHiQYZ3_Bblg1naYGx4R1vnFAazygpMwXkq7_oz86honTi7x8gqGlKFDwBuEsYGKHoKIv-F&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;859&quot; data-original-width=&quot;671&quot; height=&quot;674&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjsbZTOjRDQMljVlwCsdza-yWxMUBTwiRt0v4V9beS7O6Z6SUwbpMRDoyFnIq5eGw6b-OVk7kRmtdbguUAIg1kdMpyEN3IbsXKiCYDoqwntqOcF2p1B9AnnWiHiQYZ3_Bblg1naYGx4R1vnFAazygpMwXkq7_oz86honTi7x8gqGlKFDwBuEsYGKHoKIv-F=w527-h674&quot; width=&quot;527&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div style=&quot;font-size: 0.9em; margin: 0.4em 0px 1.4em; text-align: center;&quot;&gt;&lt;em&gt;YAML Format&lt;/em&gt;&lt;/div&gt;&lt;br /&gt;Now, here is the part that will save you an afternoon of head scratching. The packager tools decide which format they are looking at by folder shape. A &lt;code&gt;solutions\&lt;/code&gt; subdirectory containing &lt;code&gt;solution.yml&lt;/code&gt; files means YAML; no &lt;code&gt;solutions\&lt;/code&gt; subdirectory means classic XML, in which case the tool goes looking for &lt;code&gt;Other\Solution.xml&lt;/code&gt;. There is no flag that says &quot;this repo contains both, figure it out.&quot;&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; a folder that mixes the two layouts, or that has YAML files in the wrong place, produces the format war&#39;s signature error: a complaint about a missing &lt;code&gt;Customizations.xml&lt;/code&gt; from a tool that has silently fallen back to the XML code path while staring at YAML files. If you have ever seen that error and thought &quot;but this is not an XML solution,&quot; congratulations, you have met the auto-detection rules! And as of this spring, Microsoft&amp;#39;s SolutionPackager documentation carries a troubleshooting entry for exactly this case, describing the silent fallback and calling the error misleading in so many words. It is good to see it written down; it would have been better to read it before we found it.&lt;/p&gt;

&lt;h2&gt;What happened to us&lt;/h2&gt;

&lt;p&gt;Our source control story started deliberately. On the first day, we ran &lt;code&gt;pac solution clone&lt;/code&gt; against the inherited invoice flow&#39;s solution, dropped the XML source tree into a folder, and began a discipline we have kept ever since: the cloud flow is the working copy, the local tree is the versioned mirror, and after every applied change we run &lt;code&gt;pac solution sync&lt;/code&gt;, write a changelog entry, and commit. That loop was not an accident. Our whole development approach, an AI coding agent editing flow definitions, packing them, and importing them, depends on having the real definition JSON locally and being able to round-trip it through &lt;code&gt;pac solution pack&lt;/code&gt; and &lt;code&gt;pac solution import&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then the maker portal&#39;s Git integration entered the picture, the way it enters most pictures: it is right there in the portal, connecting a solution to a repo takes a few clicks, and it feels like the officially blessed path. And it is a blessed path! It is just a different one, and it wants to own the repository in its own format.&lt;/p&gt;

&lt;p&gt;Now, the moment the incompatibility became real for us was not an error message; it was a side-by-side comparison. We put the tree pac had cloned next to the repository the portal&#39;s Git integration had committed, expecting two views of the same solution, and found two structures with almost nothing in common: different file names, different nesting, different serialization, and no file on one side corresponding one-to-one with a file on the other. This was not two dialects that a diff tool could reconcile. There was nothing to diff. Nothing! Keep in mind, that comparison is worth doing deliberately if you are facing the same choice, because it settles the question faster than any documentation will.&lt;/p&gt;

&lt;p&gt;And once you have seen it, the operational hazard is obvious: had both stayed connected, every portal commit and every &lt;code&gt;pac solution sync&lt;/code&gt; would have written its own divergent truth into the same repo, with the packager&#39;s auto-detection deciding which one it believed based on which folders it noticed first. Nothing about that fails loudly at the moment you set it up. It fails later, confusingly, in tooling errors that point at the wrong cause. A repository is going to hold one format or the other, and somebody has to choose.&lt;/p&gt;

&lt;p&gt;So we chose, and wrote it down: this solution is versioned in the classic XML format maintained by pac, the maker portal&#39;s Git integration was deliberately disconnected, and the README carries a standing instruction, in bold, that says do not reconnect it. That last part matters more than it looks. The portal integration is a checkbox any admin can re-enable in a moment of tidiness, and the person who re-enables it will genuinely believe they are improving your source control posture. Suffice to say, that one bolded line in the README is the cheapest insurance we bought all week.&lt;/p&gt;
&lt;h2&gt;Why we picked the pac side&lt;/h2&gt;

&lt;p&gt;You may be asking, &quot;so is the YAML format bad?&quot; Not at all. On a fresh project with canvas apps in the mix, the YAML format and the portal integration are very likely the right choice, and Microsoft&#39;s direction of travel is clearly toward it. We picked the XML side for three reasons specific to how we work:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1) The round-trip commands are our development loop, not just our backup.&lt;/strong&gt; Large flow edits in our project go through the solution route: edit the definition JSON locally, then &lt;code&gt;pac solution pack&lt;/code&gt;, &lt;code&gt;pac solution import&lt;/code&gt;, and publish. The clone and sync commands our loop depends on produce the classic XML format, and as I write this they still do not emit the YAML layout, whatever the documentation implies (there is &lt;a href=&quot;https://github.com/microsoft/powerplatform-build-tools/issues/1385&quot;&gt;an open issue&lt;/a&gt; on exactly that). Choosing the portal&#39;s format would have meant choosing away the loop.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2) Commit cadence and commit meaning.&lt;/strong&gt; The portal integration commits when a maker clicks commit, at whatever granularity the portal batches. Our discipline is one commit per applied change, pushed immediately, with a changelog entry stating what changed, why, where it was applied, and how it was verified. In turn, that kind of history only happens when the same actor making the change also makes the commit, which in our case is the agent completing its edit-sync-log-commit ritual. The repository is not a backup of the solution; it is the audit trail of the project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3) One writer per repository.&lt;/strong&gt; Even if the formats had been compatible, two independent processes committing to the same branch on their own schedules is a merge conflict generator. A source control setup where you cannot predict who writes next is worse than either setup alone. It just doesn&#39;t work!&lt;/p&gt;

&lt;h2&gt;Two frictions that come with the pac side&lt;/h2&gt;

&lt;p&gt;Choosing pac did not end the git-versus-pac story. Two smaller frictions followed, both consequences of letting a CLI own the source tree, and both are worth knowing before you start.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Line endings.&lt;/strong&gt; Git on Windows helpfully converts line endings unless told otherwise, and pac-generated source trees do not want the help. Every &lt;code&gt;pac solution sync&lt;/code&gt; after a fresh clone produced walls of CRLF warnings and threatened noisy whitespace-only diffs. The fix was repo-local: set &lt;code&gt;core.autocrlf=input&lt;/code&gt; in the repository config, and add a &lt;code&gt;.gitattributes&lt;/code&gt; marking the pac-generated source trees as &lt;code&gt;-text&lt;/code&gt; so git stops converting them entirely. The solution source is machine-generated and machine-consumed; git should store it byte for byte and stay out of the way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Byte order marks.&lt;/strong&gt; A later sync quietly restored UTF-8 byte order marks on files where our editing had stripped them, which showed up as mysterious whole-file diffs on files nobody had meaningfully changed. We widened the &lt;code&gt;.gitattributes&lt;/code&gt; coverage and learned the larger lesson: treat the pac tree as pac&#39;s territory. Our edits happen there when the loop requires it, but the formatting conventions are the tool&#39;s, and fighting them costs more than accepting them. Fence the pac tree off from git&#39;s text handling with &lt;code&gt;.gitattributes&lt;/code&gt; the day you create it, not the day the warnings annoy you.&lt;/p&gt;

&lt;h2&gt;What I would tell you to do&lt;/h2&gt;

&lt;p&gt;Whichever side you land on, two practices carry over to any project:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Pick one format before the first commit, and never let both write to the same repository.&lt;/strong&gt; If your workflow lives in the CLI round-trip commands, take the classic XML format. If you are starting fresh, especially with canvas apps or a team of makers committing from the portal, take the YAML format and let the portal integration own the repo. Either way, if someone has already connected the other one, disconnect it before it writes a single commit. Divergence is silent, and cleanup is manual.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Write the decision down where the next admin will trip over it.&lt;/strong&gt; A bolded &quot;do not reconnect the Git integration for this solution&quot; in the README, with one sentence of why, will save a future colleague from an afternoon of &lt;code&gt;Customizations.xml&lt;/code&gt; errors on a repository full of YAML.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The two-format situation is a snapshot of a platform mid-transition, and it should resolve as the newer format&#39;s tooling support fills in. Until it does, keep in mind the failure mode is not that either path is broken. Both paths work, both look official, and nothing warns you that they cannot coexist in the same repository. Consider yourself warned!&lt;/p&gt;
&lt;p&gt;If you have hit the two-format collision yourself, or found a cleaner way through it, please drop a note in the comments describing your experience; this corner of Power Platform ALM has very little written about it, and the community learns fastest when we compare scars.&lt;/p&gt;

&lt;p&gt;Until next post!&lt;/p&gt;

&lt;p&gt;MG.-&lt;br /&gt;Mariano Gomez Bent&lt;br /&gt;Former Microsoft BizApps MVP&lt;/p&gt;
</content><link rel='replies' type='application/atom+xml' href='http://www.theworkbench.blog/feeds/8965672142025405958/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5285970135510371565/8965672142025405958' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/8965672142025405958'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/8965672142025405958'/><link rel='alternate' type='text/html' href='http://www.theworkbench.blog/2026/08/one-solution-two-source-trees-maker.html' title='One Solution, Two Source Trees: The Maker Portal&#39;s Git Integration vs pac'/><author><name>Mariano Gomez</name><uri>http://www.blogger.com/profile/13267738662239812289</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiE6oIsiofbLlE_uzUGLv64DYdGPmN9xurCqrbDNWs_axq3WbN9RRlG9IYNI50LMAK96HtSeFIVKWTjqw_URFhfBlHzutaAfUXMkUvW4RK9sO0z6Rt8YyNaETF0D4MdiQ/s113/Mariano+Gomez+-+Square+Headshot+%28website%29.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/a/AVvXsEjVJtSD3ss43jgqAPw8AeYT3fO0BYpiChiqAqLmYQ18yTOGKgiwmbu2q4drMnjWHunrOUZsLZ-svgOsW_mzy-tvQPG5F9UsUKhjkNCZaBitiaio2EIawsk5nPznaBIYVGOpXStoM7OVsKwOHYecT1tkGbijuftmW81Kzhn8jxCfNOxIi4VbV0_2KF0agVKb=s72-w521-h570-c" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5285970135510371565.post-7409906273057442616</id><published>2026-08-13T07:00:00.000-04:00</published><updated>2026-08-13T07:00:00.121-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="AI"/><category scheme="http://www.blogger.com/atom/ns#" term="Claude Code"/><category scheme="http://www.blogger.com/atom/ns#" term="MCP"/><category scheme="http://www.blogger.com/atom/ns#" term="Power Automate"/><title type='text'>Building Power Automate Flows with Claude Code and FlowAgent</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;text-align: center;&quot;&gt;&lt;img alt=&quot;Building Power Automate Flows with Claude Code and FlowAgent&quot; height=&quot;630&quot; src=&quot;https://raw.githubusercontent.com/dgpblogster/theworkbench-assets/main/covers/cover-02-flowagent.png&quot; style=&quot;height: auto; max-width: 100%;&quot; width=&quot;1200&quot; /&gt;&lt;/div&gt;
&lt;!--Cover hosted on GitHub; optionally re-upload via the Blogger editor for Google-hosted images--&gt;
&lt;p&gt;For the past week, my team and I have been heads-down building a Business Central to Dynamics 365 CE billing integration: posted sales invoices, applied cash receipts, subscription cancellations, and the product registry automation that ties them all together. Every one of those integrations is a Power Automate cloud flow, and every one of them was developed, debugged, and verified with &lt;b&gt;Claude Code&lt;/b&gt; driving Power Automate directly, through a tool called &lt;b&gt;FlowAgent&lt;/b&gt;. Suffice to say, this changed how I think about building flows, so I figured I would write up what we learned while it is still fresh.&lt;/p&gt;

&lt;p&gt;Now, keep in mind this is not a review written after a demo. This is written after shipping three production-bound flows through dozens of iterations, a full test campaign, and a healthy number of defects found and fixed along the way. The scars are real!&lt;/p&gt;

&lt;h2&gt;What FlowAgent is&lt;/h2&gt;

&lt;p&gt;FlowAgent is an MCP server that ships inside the &lt;strong&gt;power-automate&lt;/strong&gt; plugin from the good folks at Microsoft, published in the open source &lt;a href=&quot;https://github.com/microsoft/power-platform-skills&quot;&gt;power-platform-skills&lt;/a&gt; repository. MCP, the Model Context Protocol, is the standard that lets an AI coding assistant call real tools instead of just talking about them. Once the plugin is installed, Claude Code gains a set of Power Automate operations it can invoke on its own:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;List environments and flows, and set a working environment and current flow&lt;/li&gt;
&lt;li&gt;Read a flow&#39;s full definition -- this is, the JSON under the designer&lt;/li&gt;
&lt;li&gt;Edit, validate, and publish flows&lt;/li&gt;
&lt;li&gt;Read run history, run details, and per-action inputs and outputs from any run&lt;/li&gt;
&lt;li&gt;Inspect connections, connectors, and operation schemas&lt;/li&gt;
&lt;li&gt;Search connector operations and get expression help&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The plugin also bundles a Microsoft Learn MCP server, so the assistant can pull official documentation while it works, plus a set of skills -- guided workflows -- for creating, debugging, and diagnosing flows.&lt;/p&gt;

&lt;p&gt;You may be asking, &quot;why does this matter when the assistant can already tell me what to build?&quot; Very simple: without FlowAgent, the AI describes a flow and you go click it together in the designer. With FlowAgent, the assistant reads the actual flow, makes the actual edit, publishes it, watches the actual run, and reads the actual error. The feedback loop that used to run through your eyes and your mouse now runs at tool speed.&lt;/p&gt;

&lt;h2&gt;Setting it up&lt;/h2&gt;

&lt;p&gt;The prerequisites are modest: Claude Code, Node.js, the Azure CLI logged into your tenant -- FlowAgent uses it for authentication -- and ideally the Power Platform CLI (&lt;code&gt;pac&lt;/code&gt;) for solution work. Installation is two commands from within Claude Code:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;claude plugin marketplace add microsoft/power-platform-skills
claude plugin install power-automate@power-platform-skills&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A few things to note before moving on. The first command registers Microsoft&#39;s repository as a plugin marketplace; the second installs the power-automate plugin from it. After both commands complete, restart your Claude Code session, because plugins attach at session start. Then run the plugin&#39;s setup skill once: it verifies Node, the Azure CLI login, and the Power Automate token, and lets you pin a working environment so every later operation lands where you expect.&lt;/p&gt;

&lt;p&gt;Two lessons from our own installation that may save you an afternoon:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; check which Claude Code binary you are actually running. We had an old standalone CLI on the PATH that predated the plugin system entirely, and it silently swallowed the &lt;code&gt;plugin&lt;/code&gt; subcommands as if they were prompts. The VS Code extension ships a current CLI in its &lt;code&gt;resources\native-binary&lt;/code&gt; folder; that one worked immediately.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; do not hand-edit the plugin registration files. Our first attempt at manual registration produced a &quot;marketplace configuration corrupted&quot; state that even blocked removal. Resetting the marketplace file and using the real CLI commands fixed it in minutes.&lt;/p&gt;

&lt;h2&gt;Debugging: the single biggest win&lt;/h2&gt;

&lt;p&gt;When a flow fails, the assistant pulls the run, walks the failed action, reads its exact inputs and outputs, and correlates all of it with the flow definition in one pass. Early in the project, this alone paid for the whole setup. As it turns out, our invoice flow had been silently broken for about two weeks, because the web services it called no longer existed, and the failure was masked by trigger noise. Two weeks! Nobody noticed, and frankly, nobody was going to: reading run histories systematically across hundreds of runs is exactly the kind of work a human never quite gets around to. Claude Code found it, remapped both HTTP calls to the replacement services, and verified the fix against a live run.&lt;/p&gt;

&lt;p&gt;The same loop caught subtler things. A negative credit line on an invoice was failing because the CRM connector rejects negative discount amounts. The failed runs told the whole story: header created, positive line created, credit line refused. Reading the actual action outputs turned a vague &quot;totals are off in CRM&quot; report into a precise defect, a designed fix, and a verified repair, all inside one working session.&lt;/p&gt;

&lt;h2&gt;Edits go where edits belong&lt;/h2&gt;

&lt;p&gt;Our standing rule became: the cloud flow is the working copy, and the local solution source is the versioned mirror. Small, surgical changes go through FlowAgent&#39;s edit and publish tools. After every applied change, &lt;code&gt;pac solution sync&lt;/code&gt; pulls the new definition into a git repository and the change gets a changelog entry. The assistant does all of this in one motion, so the discipline actually holds. Every flow change in this project, and there have been many dozens, is in version control with a written rationale.&lt;/p&gt;

&lt;p&gt;For large edits, there is a practical workaround worth knowing: tool payloads truncate somewhere around 5 KB, so a big definition rewrite goes through the solution route instead. Edit the definition JSON locally, then &lt;code&gt;pac solution pack&lt;/code&gt;, &lt;code&gt;pac solution import&lt;/code&gt;, and publish. Same destination, different door. The assistant learned to choose the route based on the size of the change.&lt;/p&gt;

&lt;h2&gt;Verification stopped being optional&lt;/h2&gt;

&lt;p&gt;Because the assistant can resubmit a stored run and then read the new run&#39;s results, &quot;deploy and hope&quot; turned into &quot;deploy and prove.&quot; Our habit became: make the change, resubmit a known trigger event, read every action&#39;s output, compare the CRM records against Business Central truth, and only then call it done.&lt;/p&gt;

&lt;p&gt;During the test campaign, this cadence caught a deployment race where a managed export picked up a stale version and the target environment silently skipped the import as already installed. In turn, a resubmit against what turned out to be the old definition briefly looked like a test result. The lesson is now procedure: after every deployment, read the definition back from the target before trusting any run.&lt;/p&gt;

&lt;h2&gt;The rough edges, honestly&lt;/h2&gt;

&lt;p&gt;FlowAgent is genuinely useful and genuinely version one. Things we hit, and how we worked around them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Solution-aware flows do not appear in the flow list&lt;/strong&gt;, which only shows personal-scope flows. The workaround is to address your flow by its ID directly. Once pinned as the current flow, everything else works.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The resubmit and diagnose tools had bugs&lt;/strong&gt; in the version we used; both threw type errors. We resubmitted runs through the underlying Power Automate REST API instead, which the assistant handles just fine, and filed the issues upstream with the plugin&#39;s built-in report-issue skill.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Importing a solution leaves flows stopped&lt;/strong&gt;, and validation errors surface at activation time, not import time. Publishing or activating after import is a required step, not a nicety.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Connection authorization is per connection, not per flow.&lt;/strong&gt; A brand new flow referencing a shared connection needs one activation by the connection&#39;s owner; after that, other makers can deactivate, edit, and reactivate freely. Knowing this pattern saved us from repeatedly misdiagnosing authorization failures.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these were blockers. All of them are the kind of thing you want written down before you start, which is exactly why they are written down here.&lt;/p&gt;

&lt;h2&gt;What it added up to&lt;/h2&gt;

&lt;p&gt;In a bit under a week of working sessions, this setup carried us through:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Remediation of six pre-existing defects in an inherited flow, including moving a plaintext secret into Azure Key Vault and parameterizing every environment-specific value into environment variables&lt;/li&gt;
&lt;li&gt;A ground-up rebuild of the invoice flow onto a customer ledger trigger, eliminating an entire class of draft-noise and race-condition bugs&lt;/li&gt;
&lt;li&gt;A cash receipts flow built from a feasibility stub to feature complete in a single day, across four live-verified iterations&lt;/li&gt;
&lt;li&gt;A registry automation flow that replaced two hardcoded legacy flows with one table-driven design&lt;/li&gt;
&lt;li&gt;A full staging test campaign in which every planned scenario across three flows fired live, with every defect found along the way fixed and re-verified&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Could a person do all of that in the portal designer? Eventually. But the honest answer is that the verification depth is what a person would not do. Reading every action output of every test run, reconciling every synced record against the source system to the penny, and writing it all down as you go is precisely the tedious, high-value work that an agent with real tool access does without fatigue.&lt;/p&gt;

&lt;h2&gt;If you try it&lt;/h2&gt;

&lt;p&gt;Three practices made the difference for us, and none of them are about the tool itself:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Keep a changelog and a git mirror from day one.&lt;/strong&gt; The agent moves fast; the paper trail is what lets you trust the speed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Decide your edit routing up front.&lt;/strong&gt; Cloud as working copy, local as mirror, one commit per applied change. Ambiguity here is how definitions drift.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Insist on live verification.&lt;/strong&gt; The agent can prove its work against real runs. Make it do so every time, and &quot;it should work&quot; disappears from your vocabulary.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;One last thing, because it matters: FlowAgent will not design your integration for you, and it should not. The design conversations, the business rulings, and the judgment calls stayed firmly human in our project. What it collapses is the distance between a decision and a verified, versioned, running flow -- a distance that used to be measured in hours of clicking and is now measured in minutes of reading the agent&#39;s evidence.&lt;/p&gt;

&lt;p&gt;If you take FlowAgent for a spin, please drop a note in the comments describing your experience, including the rough edges you find; that feedback is exactly what makes open source tooling like this get better.&lt;/p&gt;

&lt;p&gt;Until next post!&lt;/p&gt;

&lt;p&gt;MG.-&lt;br /&gt;Mariano Gomez Bent&lt;br /&gt;Former Microsoft BizApps MVP&lt;/p&gt;
</content><link rel='replies' type='application/atom+xml' href='http://www.theworkbench.blog/feeds/7409906273057442616/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5285970135510371565/7409906273057442616' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/7409906273057442616'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/7409906273057442616'/><link rel='alternate' type='text/html' href='http://www.theworkbench.blog/2026/08/building-power-automate-flows-with.html' title='Building Power Automate Flows with Claude Code and FlowAgent'/><author><name>Mariano Gomez</name><uri>http://www.blogger.com/profile/13267738662239812289</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiE6oIsiofbLlE_uzUGLv64DYdGPmN9xurCqrbDNWs_axq3WbN9RRlG9IYNI50LMAK96HtSeFIVKWTjqw_URFhfBlHzutaAfUXMkUvW4RK9sO0z6Rt8YyNaETF0D4MdiQ/s113/Mariano+Gomez+-+Square+Headshot+%28website%29.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5285970135510371565.post-1641915574014671123</id><published>2026-08-11T09:35:27.912-04:00</published><updated>2026-08-11T09:35:27.912-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Agents"/><category scheme="http://www.blogger.com/atom/ns#" term="Copilot Studio"/><category scheme="http://www.blogger.com/atom/ns#" term="GitHub"/><category scheme="http://www.blogger.com/atom/ns#" term="MCP"/><title type='text'>Same Agent, Two Architectures: When MCP Wins (and When It Doesn&#39;t)</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;text-align: center;&quot;&gt;&lt;img alt=&quot;Same Agent, Two Architectures: connector agent versus MCP agent in Copilot Studio&quot; data-original-height=&quot;630&quot; data-original-width=&quot;1200&quot; height=&quot;630&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhkrl_litjnFdt-K1B-RXfDbnqeOFbPKUavvAqZ_s8W6v2QSwg2rdSYO-u3tGq2fkly3hmI53LiaSHhAR0bP54Ge2JZkylSDT82dIO7QbcTWSuuoy5o2aZMLBPViSW7-O38QTAfPS920iYhLkpxmKWlEgLNqXq_kT3QY-sg2HqZogzoEt-z_Mz0kAG5BP9j&quot; style=&quot;height: auto; max-width: 100%;&quot; width=&quot;1200&quot; /&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhkrl_litjnFdt-K1B-RXfDbnqeOFbPKUavvAqZ_s8W6v2QSwg2rdSYO-u3tGq2fkly3hmI53LiaSHhAR0bP54Ge2JZkylSDT82dIO7QbcTWSuuoy5o2aZMLBPViSW7-O38QTAfPS920iYhLkpxmKWlEgLNqXq_kT3QY-sg2HqZogzoEt-z_Mz0kAG5BP9j&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;I know, I know. It has been almost five years since my last post, and in that time the masthead changed, the name changed, and frankly, the entire technology landscape changed with them. But I did not want to relaunch this blog with a ceremonial hello-world post, so instead, this first post from &lt;b&gt;The Workbench&lt;/b&gt; starts out the way many of my posts have over the years: with a question.&lt;p&gt;&lt;/p&gt;

&lt;p&gt;The question comes up in just about every architecture review I sit in these days, whether in my day job as CTO or in conversations with folks in the community, and it goes something like this:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&quot;Should we be using MCP for our Copilot Studio agents?&quot;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I will be the first to tell you this is the wrong question. And rather than argue the point in the abstract, I built the same agent twice to prove it. Let&#39;s see how it&#39;s done!&lt;/p&gt;

&lt;h2&gt;Background&lt;/h2&gt;

&lt;p&gt;Model Context Protocol -- MCP for short, introduced by the folks at Anthropic -- has gone from curiosity to checklist item in record time, much like microservices did a few years back, and just like then, everyone wants to know where it fits before anyone has agreed on when it should. So, I put together a lab around a fictional but very realistic engineering program called &lt;strong&gt;Project Orion&lt;/strong&gt;: a customer-facing web application mid-way through a major release cycle, with its operational truth split across two systems, the way it always is in real life:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;GitHub Issues&lt;/strong&gt; holds the code-level activity: 35 issues covering bugs, feature work, blockers, and performance problems.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A SQL Server database&lt;/strong&gt; holds the delivery truth: five sprints of history, 70 work items, and 30 days of health metrics, including a release readiness score, critical bug counts, blocker counts, and velocity trend.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, the seeded data tells a story on purpose. Sprint 3 completed 38 of 45 points. Sprint 4 dropped to 33 of 44 with 11 points of rollover. The latest health snapshot shows a release readiness score of 52, three critical bugs, three blockers, and a velocity trend of Declining. Suffice to say, this project is quietly going sideways, and the signals are scattered across two systems that do not talk to each other.&lt;/p&gt;

&lt;p&gt;With the stage set, I built the same agent twice in Microsoft Copilot Studio and asked both versions the same questions.&lt;/p&gt;

&lt;h2&gt;Version 1: the connector agent&lt;/h2&gt;

&lt;p&gt;The first agent uses nothing but the GitHub connector. Ten minutes of work, most of which is authentication, and here is the part that surprises people every time I demo it: this agent is &lt;strong&gt;good&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Ask it &quot;What critical bugs are open in Project Orion?&quot; and it answers cleanly, with issue numbers, titles, and labels. Ask it &quot;What issues are labeled as blocked?&quot; and it nails that one too. For questions whose answer lives entirely inside GitHub, the connector agent is fast, correct, and required exactly zero infrastructure from me -- no server to provision, nothing to patch, and it sits comfortably inside the tenant&#39;s existing governance and data loss prevention story, which your IT department will certainly appreciate.&lt;/p&gt;

&lt;p&gt;Now, that&#39;s all cool, but then you ask it the ONE question your VP actually cares about: &lt;em&gt;&quot;What is blocking the Project Orion release?&quot;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And there it is -- the ceiling! The agent can see three critical bugs in GitHub, so it reports three critical bugs. It cannot see that the release readiness score sits at 52 and falling, it cannot see that velocity has declined two sprints in a row, and it cannot see that two of the blocked work items have been stalled for over a week. Keep in mind, its answer is not wrong. It is something worse than wrong: it is &lt;strong&gt;confidently incomplete&lt;/strong&gt;. A stakeholder reading that answer would walk away thinking the release has three bugs to fix, when the data says the release is structurally at risk.&lt;/p&gt;

&lt;p&gt;In the lab version of this agent I made the limitation explicit, and I recommend you do the same for any single-source agent you ship. Its instructions include the following line:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;&quot;I can only see GitHub Issues data. For full project health including sprint metrics and release readiness, you would need access to the project health database.&quot;&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;An agent that states its blind spots is an agent you can trust. An agent that answers everything with whatever fraction of the truth it can reach is a liability wearing a chat interface. It just doesn&#39;t work!&lt;/p&gt;

&lt;h2&gt;Version 2: the MCP agent&lt;/h2&gt;

&lt;p&gt;The second agent is the same Copilot Studio agent rebuilt on two MCP servers: the GitHub MCP Server for issue data, and a custom TypeScript MCP server I wrote that fronts the Project Orion SQL database.&lt;/p&gt;

&lt;p&gt;The custom server exposes seven tools, and I would encourage you to study the tool list closely, because this is where the real design work lives. These are not generic &quot;run a query&quot; endpoints; each one answers a question a human being would actually ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;get_current_sprint&lt;/code&gt;: the active sprint with planned versus completed points&lt;/li&gt;
&lt;li&gt;&lt;code&gt;get_sprint_history&lt;/code&gt;: velocity and completion history across completed sprints&lt;/li&gt;
&lt;li&gt;&lt;code&gt;get_critical_work_items&lt;/code&gt;: open Critical and High priority items, including blocked ones&lt;/li&gt;
&lt;li&gt;&lt;code&gt;get_work_items_by_sprint&lt;/code&gt;: work items for a sprint, optionally filtered by status&lt;/li&gt;
&lt;li&gt;&lt;code&gt;get_latest_health_metrics&lt;/code&gt;: the current health snapshot, readiness score included&lt;/li&gt;
&lt;li&gt;&lt;code&gt;get_health_metrics_trend&lt;/code&gt;: readiness score over the last N days&lt;/li&gt;
&lt;li&gt;&lt;code&gt;get_stalled_work_items&lt;/code&gt;: items sitting in Active or New with no movement&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two implementation details will save you a considerable amount of head scratching, so pay close attention. First, as it turns out, &lt;strong&gt;Copilot Studio speaks HTTP, not stdio&lt;/strong&gt;. Most MCP tutorials out there show the stdio transport, because that is what desktop clients use, but Copilot Studio requires the streamable HTTP transport running in stateless mode, with a fresh transport and server instance created for each incoming request. For brevity sake, I am only showing the shape of it:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;// Copilot Studio requires HTTP transport in stateless mode
const transport = new StreamableHTTPServerTransport({
  sessionIdGenerator: undefined, // stateless: critical for Copilot Studio
});
// a fresh server + transport pair is created per incoming request
app.post(&#39;/mcp&#39;, async (req, res) =&amp;gt; {
  const server = buildProjectOrionServer();
  await server.connect(transport);
  await transport.handleRequest(req, res, req.body);
});&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Second, validate your tool inputs. The model -- not you, not your code -- is choosing the parameter values at runtime, so every tool takes a schema, not a prayer:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;server.tool(
  &#39;get_health_metrics_trend&#39;,
  &#39;Returns the release readiness score trend over the last N days &#39; +
  &#39;to show whether project health is improving or declining.&#39;,
  { days: z.number().optional().default(14) },
  async ({ days }) =&amp;gt; {
    const rows = await getHealthMetricsTrend(days);
    return { content: [{ type: &#39;text&#39;, text: JSON.stringify(rows) }] };
  }
);&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A few things to note before moving on. That tool description string is not documentation for humans; it is the interface the model reasons over when deciding which tools to call and in what combination. Write your tool descriptions the way you would write requirements for a sharp new team member, because for all practical purposes, that is exactly what they are.&lt;/p&gt;

&lt;h2&gt;The moment it earns its keep&lt;/h2&gt;

&lt;p&gt;Now ask the rebuilt agent the same question: &lt;em&gt;&quot;What is blocking the Project Orion release?&quot;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This time the agent calls &lt;code&gt;get_critical_work_items&lt;/code&gt; and &lt;code&gt;get_latest_health_metrics&lt;/code&gt; on the SQL side, pulls the open critical issues from the GitHub side, and then does the thing no single-source agent can do: it correlates. The three critical GitHub bugs map to blocked work items in the sprint; the blockers explain the 11 points of rollover in Sprint 4; the rollover explains the declining velocity trend; and all of it together explains a readiness score of 52. And voila! The answer reads like something a good release manager would write, complete with a one-line risk summary at the end, because the agent finally has the same field of vision the release manager has.&lt;/p&gt;

&lt;p&gt;Ask it &quot;Are there any patterns between our open GitHub issues and our sprint health?&quot; and it produces an answer that did not have a data path to exist in Version 1. That is the difference, and it is worth being precise about it: not better answers to the old questions, but answers to questions that were previously UNANSWERABLE.&lt;/p&gt;

&lt;h2&gt;What MCP costs you&lt;/h2&gt;

&lt;p&gt;Here is the part the hype conveniently skips, and the part I lean on hardest whenever I present this material: everything above has a bill attached.&lt;/p&gt;

&lt;p&gt;With the connector agent, Microsoft runs the integration. With the MCP agent, I run the integration. In the lab, the server is a TypeScript process on my laptop reached through an Azure Dev Tunnel, which is perfectly fine for a demo and perfectly irresponsible for production.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; the moment this architecture goes to production, you own hosting, authentication, TLS, availability, logging, patching the SDK, and the security review for a brand-new surface area that reaches directly into a database. The connector came pre-governed; your MCP server is governed by whatever you remembered to build.&lt;/p&gt;

&lt;p&gt;So the honest comparison is not &quot;MCP versus connector.&quot; It is &quot;cross-source reasoning versus operational simplicity.&quot; Sometimes that trade is spectacular, and sometimes it amounts to buying yourself a server bill to answer questions a connector already answered. It is your job as an architect to know which of the two situations you are standing in.&lt;/p&gt;

&lt;h2&gt;The decision signal&lt;/h2&gt;

&lt;p&gt;After building both versions side by side, the rule I trust fits in one sentence:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;When your agent needs to reason across sources it did not know about at design time, that is your MCP signal.&lt;/strong&gt;&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;In practice, I walk through the following four questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Does the answer live in one system?&lt;/strong&gt; Use the connector. You are done, and you inherited Microsoft&#39;s operations team for free.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Does the answer require correlating two or more systems in a single reasoning pass?&lt;/strong&gt; That is your MCP signal. No amount of prompt engineering will give a single-source agent data it cannot reach.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Will the source list grow?&lt;/strong&gt; MCP servers compose. Adding a third source to the Orion agent is another server registration, not a rebuild.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Can you operate a service?&lt;/strong&gt; If the answer is no, that is not a character flaw, but keep in mind the connector ceiling is your ceiling until that answer changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Build it yourself&lt;/h2&gt;

&lt;p&gt;Everything in this post is reproducible from the lab I published for my Community Summit NA 2026 session in Nashville, &lt;em&gt;&quot;Same Agent, Two Architectures: When MCP Wins (And When It Doesn&#39;t)&quot;&lt;/em&gt;. The repository contains the SQL schema and seed data, a script that creates all 35 GitHub issues, scaffold prompts for the MCP server and a Next.js health dashboard, and the Copilot Studio prompts for both agents -- including the intentionally limited one:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/dgpblogster/project-orion-lab&quot;&gt;github.com/dgpblogster/project-orion-lab&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You need &lt;a href=&quot;https://code.visualstudio.com/&quot;&gt;VS Code&lt;/a&gt;, &lt;a href=&quot;https://nodejs.org/&quot;&gt;Node 18+&lt;/a&gt;, &lt;a href=&quot;https://www.microsoft.com/en-us/sql-server/sql-server-downloads&quot;&gt;SQL Server Express&lt;/a&gt;, the &lt;a href=&quot;https://cli.github.com/&quot;&gt;GitHub CLI&lt;/a&gt;, &lt;a href=&quot;https://learn.microsoft.com/en-us/azure/developer/dev-tunnels/get-started&quot;&gt;Azure Dev Tunnels&lt;/a&gt;, and a &lt;a href=&quot;https://copilotstudio.microsoft.com&quot;&gt;Copilot Studio license&lt;/a&gt; -- a trial works just fine. Budget an afternoon, and make sure you build toward the moment where you ask both agents the release-blocker question back to back, because watching one agent hit the ceiling and the other reason straight through it will teach you more about agent architecture than any slide deck, including mine. The effort is well worth it!&lt;/p&gt;

&lt;p&gt;If there is interest, my next installment will walk through hardening this MCP server for real production use -- authentication, hosting, and telemetry -- so please drop a note in the comments if you would like to see that. It is good to be back at the bench.&lt;/p&gt;

&lt;p&gt;Until next post!&lt;/p&gt;

&lt;p&gt;MG.-&lt;br /&gt;Mariano Gomez Bent&lt;br /&gt;Former Microsoft BizApps MVP&amp;nbsp;&amp;nbsp;&lt;/p&gt;
</content><link rel='replies' type='application/atom+xml' href='http://www.theworkbench.blog/feeds/1641915574014671123/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5285970135510371565/1641915574014671123' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/1641915574014671123'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/1641915574014671123'/><link rel='alternate' type='text/html' href='http://www.theworkbench.blog/2026/08/same-agent-two-architectures-when-mcp.html' title='Same Agent, Two Architectures: When MCP Wins (and When It Doesn&#39;t)'/><author><name>Mariano Gomez</name><uri>http://www.blogger.com/profile/13267738662239812289</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiE6oIsiofbLlE_uzUGLv64DYdGPmN9xurCqrbDNWs_axq3WbN9RRlG9IYNI50LMAK96HtSeFIVKWTjqw_URFhfBlHzutaAfUXMkUvW4RK9sO0z6Rt8YyNaETF0D4MdiQ/s113/Mariano+Gomez+-+Square+Headshot+%28website%29.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/a/AVvXsEhkrl_litjnFdt-K1B-RXfDbnqeOFbPKUavvAqZ_s8W6v2QSwg2rdSYO-u3tGq2fkly3hmI53LiaSHhAR0bP54Ge2JZkylSDT82dIO7QbcTWSuuoy5o2aZMLBPViSW7-O38QTAfPS920iYhLkpxmKWlEgLNqXq_kT3QY-sg2HqZogzoEt-z_Mz0kAG5BP9j=s72-c" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5285970135510371565.post-8900068900384765487</id><published>2021-08-12T15:09:00.000-04:00</published><updated>2026-08-19T10:59:34.591-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ALM"/><category scheme="http://www.blogger.com/atom/ns#" term="Azure Application Insights"/><category scheme="http://www.blogger.com/atom/ns#" term="Power Apps"/><category scheme="http://www.blogger.com/atom/ns#" term="Telemetry"/><title type='text'>Power Apps - Application Monitoring with Azure Application Insights</title><content type='html'>In this video, learn to enable Power Apps Application Monitoring with Azure Application Insights.&amp;nbsp;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;iframe allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot; frameborder=&quot;0&quot; height=&quot;480&quot; src=&quot;https://www.youtube.com/embed/nISv1JHNRQY&quot; title=&quot;YouTube video player&quot; width=&quot;640&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The following topics are covered in this video:&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;(&lt;a href=&quot;https://www.youtube.com/watch?v=nISv1JHNRQY&amp;amp;t=0s&quot;&gt;0:00&lt;/a&gt;) - Intro&amp;nbsp;&lt;/div&gt;&lt;div&gt;(&lt;a href=&quot;https://www.youtube.com/watch?v=nISv1JHNRQY&amp;amp;t=36s&quot;&gt;0:36&lt;/a&gt;) - What is Azure Application Insights&amp;nbsp;&lt;/div&gt;&lt;div&gt;(&lt;a href=&quot;https://www.youtube.com/watch?v=nISv1JHNRQY&amp;amp;t=92s&quot;&gt;1:32&lt;/a&gt;) - The timesheet app&amp;nbsp;&lt;/div&gt;&lt;div&gt;(&lt;a href=&quot;https://www.youtube.com/watch?v=nISv1JHNRQY&amp;amp;t=132s&quot;&gt;2:12&lt;/a&gt;) - Creating an Application Insight for the timesheet application&amp;nbsp;&lt;/div&gt;&lt;div&gt;(&lt;a href=&quot;https://www.youtube.com/watch?v=nISv1JHNRQY&amp;amp;t=235s&quot;&gt;3:55&lt;/a&gt;) - Identify and copy the application instrumentation key&amp;nbsp;&lt;/div&gt;&lt;div&gt;(&lt;a href=&quot;https://www.youtube.com/watch?v=nISv1JHNRQY&amp;amp;t=249s&quot;&gt;4:09&lt;/a&gt;) - Enabling the timesheet application with the instrumentation key&amp;nbsp;&lt;/div&gt;&lt;div&gt;(&lt;a href=&quot;https://www.youtube.com/watch?v=nISv1JHNRQY&amp;amp;t=332s&quot;&gt;5:32&lt;/a&gt;) - Working with Application Insights&amp;nbsp;&lt;/div&gt;&lt;div&gt;(&lt;a href=&quot;https://www.youtube.com/watch?v=nISv1JHNRQY&amp;amp;t=486s&quot;&gt;8:06&lt;/a&gt;) - Outro&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;For additional information on Azure Application Insights, please follow these resources:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Microsoft Docs - What is Azure Application Insights? &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/azure-monitor/app/app-insights-overview&quot;&gt;here&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;Adam Marczak - Azure for Everyone: Azure Application Insights Tutorial, &lt;a href=&quot;https://youtu.be/A0jAeGf2zUQ&quot;&gt;here&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;To check out my YouTube channel visit:
&lt;a href=&quot;https://www.youtube.com/@workbench-blog&quot;&gt;https://www.youtube.com/@workbench-blog&lt;/a&gt;
&lt;br /&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;div&gt;&lt;b&gt;LIKE AND SUBSCRIBE!!!&lt;br /&gt;&lt;/b&gt;
&lt;br /&gt;
Until next post!&lt;br /&gt;
&lt;br /&gt;
MG.-&lt;br /&gt;
Mariano Gomez, MVP
&lt;br /&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.theworkbench.blog/feeds/8900068900384765487/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5285970135510371565/8900068900384765487' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/8900068900384765487'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/8900068900384765487'/><link rel='alternate' type='text/html' href='http://www.theworkbench.blog/2021/08/power-apps-application-monitoring-with.html' title='Power Apps - Application Monitoring with Azure Application Insights'/><author><name>Mariano Gomez</name><uri>http://www.blogger.com/profile/13267738662239812289</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiE6oIsiofbLlE_uzUGLv64DYdGPmN9xurCqrbDNWs_axq3WbN9RRlG9IYNI50LMAK96HtSeFIVKWTjqw_URFhfBlHzutaAfUXMkUvW4RK9sO0z6Rt8YyNaETF0D4MdiQ/s113/Mariano+Gomez+-+Square+Headshot+%28website%29.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img.youtube.com/vi/nISv1JHNRQY/default.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5285970135510371565.post-1316020312829870925</id><published>2021-08-03T15:46:00.000-04:00</published><updated>2026-08-19T10:59:35.642-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ALM"/><category scheme="http://www.blogger.com/atom/ns#" term="Power Apps"/><category scheme="http://www.blogger.com/atom/ns#" term="Visual Studio"/><title type='text'>Power Platform ALM - Visual Studio Code Extensions</title><content type='html'>&lt;p&gt;Learn about Power Platform extension for Visual Studio Code and how this toolset is improving both the code first developer experience and strengthening the ALM story.&amp;nbsp;&lt;/p&gt;&lt;br /&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;iframe allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot; frameborder=&quot;0&quot; height=&quot;480&quot; src=&quot;https://www.youtube.com/embed/vfwYY6Aexeg&quot; title=&quot;YouTube video player&quot; width=&quot;640&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;The following topics are covered:&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;(&lt;a href=&quot;https://www.youtube.com/watch?v=vfwYY6Aexeg&amp;amp;t=0s&quot;&gt;0:00&lt;/a&gt;) - Intro&amp;nbsp;&lt;/li&gt;&lt;li&gt;(&lt;a href=&quot;https://www.youtube.com/watch?v=vfwYY6Aexeg&amp;amp;t=76s&quot;&gt;1:16&lt;/a&gt;) - Installing the Power Platform extensions for Visual Studio Code&amp;nbsp;&lt;/li&gt;&lt;li&gt;(&lt;a href=&quot;https://www.youtube.com/watch?v=vfwYY6Aexeg&amp;amp;t=133s&quot;&gt;2:13&lt;/a&gt;) - Working with the Power Platform CLI inside of Visual Studio Code&amp;nbsp;&lt;/li&gt;&lt;li&gt;(&lt;a href=&quot;https://www.youtube.com/watch?v=vfwYY6Aexeg&amp;amp;t=222s&quot;&gt;3:42&lt;/a&gt;) - Authenticating to your environment from Power Platform CLI&amp;nbsp;&lt;/li&gt;&lt;li&gt;(&lt;a href=&quot;https://www.youtube.com/watch?v=vfwYY6Aexeg&amp;amp;t=438s&quot;&gt;7:18&lt;/a&gt;) - Saving your canvas app to your local machine&amp;nbsp;&lt;/li&gt;&lt;li&gt;(&lt;a href=&quot;https://www.youtube.com/watch?v=vfwYY6Aexeg&amp;amp;t=535s&quot;&gt;8:55&lt;/a&gt;) - Unpacking your canvas app&amp;nbsp;&lt;/li&gt;&lt;li&gt;(&lt;a href=&quot;https://www.youtube.com/watch?v=vfwYY6Aexeg&amp;amp;t=619s&quot;&gt;10:19&lt;/a&gt;) - Using Visual Studio Code to make changes to your application&amp;nbsp;&lt;/li&gt;&lt;li&gt;(&lt;a href=&quot;https://www.youtube.com/watch?v=vfwYY6Aexeg&amp;amp;t=710s&quot;&gt;11:50&lt;/a&gt;) - Packing and uploading your application changes&amp;nbsp;&lt;/li&gt;&lt;li&gt;(&lt;a href=&quot;https://www.youtube.com/watch?v=vfwYY6Aexeg&amp;amp;t=835s&quot;&gt;13:55&lt;/a&gt;) - Outro&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;/p&gt;
To check out my YouTube channel visit:
&lt;a href=&quot;https://www.youtube.com/@workbench-blog&quot;&gt;https://www.youtube.com/@workbench-blog&lt;/a&gt;
&lt;br /&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;div&gt;&lt;b&gt;LIKE AND SUBSCRIBE!!!&lt;br /&gt;&lt;/b&gt;
&lt;br /&gt;
Until next post!&lt;br /&gt;
&lt;br /&gt;
MG.-&lt;br /&gt;
Mariano Gomez, MVP
&lt;br /&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.theworkbench.blog/feeds/1316020312829870925/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5285970135510371565/1316020312829870925' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/1316020312829870925'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/1316020312829870925'/><link rel='alternate' type='text/html' href='http://www.theworkbench.blog/2021/08/power-platform-alm-visual-studio-code.html' title='Power Platform ALM - Visual Studio Code Extensions'/><author><name>Mariano Gomez</name><uri>http://www.blogger.com/profile/13267738662239812289</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiE6oIsiofbLlE_uzUGLv64DYdGPmN9xurCqrbDNWs_axq3WbN9RRlG9IYNI50LMAK96HtSeFIVKWTjqw_URFhfBlHzutaAfUXMkUvW4RK9sO0z6Rt8YyNaETF0D4MdiQ/s113/Mariano+Gomez+-+Square+Headshot+%28website%29.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img.youtube.com/vi/vfwYY6Aexeg/default.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5285970135510371565.post-2012731729105666213</id><published>2021-08-02T17:31:00.000-04:00</published><updated>2026-08-19T10:59:36.588-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Power Apps"/><category scheme="http://www.blogger.com/atom/ns#" term="Power Automate"/><title type='text'>Power Automate | Passing Collections from Power Automate to Power Apps (Revisited)</title><content type='html'>I revisit an old video I did couple years ago showing how to pass collections from Power Automate to Power Apps.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;iframe allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot; frameborder=&quot;0&quot; height=&quot;480&quot; src=&quot;https://www.youtube.com/embed/qUmi3JwjLfI&quot; title=&quot;YouTube video player&quot; width=&quot;640&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;This time around, I show how to leverage arrays to accomplish the same. This time around you will learn about:&lt;div&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/powerapps/maker/canvas-apps/using-logic-flows&quot; target=&quot;_blank&quot;&gt;Power Apps trigger in Power Automate&lt;/a&gt;&amp;nbsp;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/connectors/sql/&quot; target=&quot;_blank&quot;&gt;Get Rows SQL action&lt;/a&gt;&amp;nbsp;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/power-automate/create-variable-store-values&quot; target=&quot;_blank&quot;&gt;Variables action&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/power-automate/data-operations&quot; target=&quot;_blank&quot;&gt;Select action and Arrays&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Response action&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/powerapps/maker/canvas-apps/create-update-collection&quot; target=&quot;_blank&quot;&gt;Power Apps Collections&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;To check out my YouTube channel visit:
&lt;a href=&quot;https://www.youtube.com/@workbench-blog&quot;&gt;https://www.youtube.com/@workbench-blog&lt;/a&gt;
&lt;br /&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;div&gt;&lt;b&gt;LIKE AND SUBSCRIBE!!!&lt;br /&gt;&lt;/b&gt;
&lt;br /&gt;
Until next post!&lt;br /&gt;
&lt;br /&gt;
MG.-&lt;br /&gt;
Mariano Gomez, MVP</content><link rel='replies' type='application/atom+xml' href='http://www.theworkbench.blog/feeds/2012731729105666213/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5285970135510371565/2012731729105666213' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/2012731729105666213'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/2012731729105666213'/><link rel='alternate' type='text/html' href='http://www.theworkbench.blog/2021/08/power-automate-passing-collections-from.html' title='Power Automate | Passing Collections from Power Automate to Power Apps (Revisited)'/><author><name>Mariano Gomez</name><uri>http://www.blogger.com/profile/13267738662239812289</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiE6oIsiofbLlE_uzUGLv64DYdGPmN9xurCqrbDNWs_axq3WbN9RRlG9IYNI50LMAK96HtSeFIVKWTjqw_URFhfBlHzutaAfUXMkUvW4RK9sO0z6Rt8YyNaETF0D4MdiQ/s113/Mariano+Gomez+-+Square+Headshot+%28website%29.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img.youtube.com/vi/qUmi3JwjLfI/default.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5285970135510371565.post-2212227777417175774</id><published>2020-11-10T14:00:00.000-05:00</published><updated>2026-08-19T10:59:37.573-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="AI Builder"/><category scheme="http://www.blogger.com/atom/ns#" term="FlowFam"/><category scheme="http://www.blogger.com/atom/ns#" term="Form Processing"/><category scheme="http://www.blogger.com/atom/ns#" term="Power Platform"/><category scheme="http://www.blogger.com/atom/ns#" term="PowerAddicts"/><title type='text'>AI Builder Form Processing: Multi-layout Form Processing</title><content type='html'>AI Builder Form Processing Multi-layout Form Processing has been one of the most requested features and is now available in preview for you to test. This feature allows you to process forms with a variety of different layouts (but similar in content) with a single model, which brings this feature one step closer to a scalable enterprise solution.&lt;br /&gt;&lt;br /&gt;&lt;iframe allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot; frameborder=&quot;0&quot; height=&quot;480&quot; src=&quot;https://www.youtube.com/embed/WL_eEOUK0fw&quot; width=&quot;640&quot;&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;In this video I cover the following:&lt;br /&gt;&lt;br /&gt;- Creating a model&lt;br /&gt;- Form collections&lt;br /&gt;- Training the form processing model&lt;br /&gt;- Publishing the form processing model&lt;br /&gt;- Testing the model&lt;br /&gt;&lt;br /&gt;For additional information, please refer to:&lt;br /&gt;&lt;br /&gt;Create a form processing model - click &lt;a href=&quot;https://docs.microsoft.com/en-us/ai-builder/create-form-processing-model&quot;&gt;here&lt;/a&gt;&lt;br /&gt;AI Builder form processing announcement - click &lt;a href=&quot;https://flow.microsoft.com/en-us/blog/ai-builder-form-processing-models-can-now-train-using-documents-that-have-different-layouts/&quot;&gt;here&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;To check out my YouTube channel visit:
&lt;a href=&quot;https://www.youtube.com/@workbench-blog&quot;&gt;https://www.youtube.com/@workbench-blog&lt;/a&gt;
&lt;br /&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;div&gt;&lt;b&gt;LIKE AND SUBSCRIBE!!!&lt;br /&gt;&lt;/b&gt;
&lt;br /&gt;
Until next post!&lt;br /&gt;
&lt;br /&gt;
MG.-&lt;br /&gt;
Mariano Gomez, MVP
&lt;br /&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.theworkbench.blog/feeds/2212227777417175774/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5285970135510371565/2212227777417175774' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/2212227777417175774'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/2212227777417175774'/><link rel='alternate' type='text/html' href='http://www.theworkbench.blog/2020/11/ai-builder-form-processing-multi-layout.html' title='AI Builder Form Processing: Multi-layout Form Processing'/><author><name>Mariano Gomez</name><uri>http://www.blogger.com/profile/13267738662239812289</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiE6oIsiofbLlE_uzUGLv64DYdGPmN9xurCqrbDNWs_axq3WbN9RRlG9IYNI50LMAK96HtSeFIVKWTjqw_URFhfBlHzutaAfUXMkUvW4RK9sO0z6Rt8YyNaETF0D4MdiQ/s113/Mariano+Gomez+-+Square+Headshot+%28website%29.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img.youtube.com/vi/WL_eEOUK0fw/default.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5285970135510371565.post-6690667304469326507</id><published>2020-10-20T16:15:00.000-04:00</published><updated>2026-08-19T10:59:38.992-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ALM"/><category scheme="http://www.blogger.com/atom/ns#" term="Flow"/><category scheme="http://www.blogger.com/atom/ns#" term="Power Apps"/><category scheme="http://www.blogger.com/atom/ns#" term="Power Automate"/><category scheme="http://www.blogger.com/atom/ns#" term="Power Platform"/><category scheme="http://www.blogger.com/atom/ns#" term="PowerAddicts"/><title type='text'>Power Platform: Application Lifecycle Management (Part 3/3) - Build and Release Pipelines</title><content type='html'>Welcome to the final episode of Power Platform Application Lifecycle Management. In parts 1 and 2 you saw how to prepare your environments, set up an Azure DevOps project, and define Service Connections to your Power Platform environments.&amp;nbsp;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In this, the final installment of this 3-part series, you will learn how to create the Build and Release pipelines using the Power Platform Build Tools to take your Power Platform solution from an environment, through source code control, through staging and production deployments.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;iframe allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot; frameborder=&quot;0&quot; height=&quot;480&quot; src=&quot;https://www.youtube.com/embed/NRLQeT0XP2M&quot; width=&quot;640&quot;&gt;&lt;/iframe&gt;
&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;
Topics covered:&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;
- Power Platform source code control with Azure Repos&lt;br /&gt;
- Build and Release pipelines&lt;br /&gt;
- Sharing your solution&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
References:&lt;br /&gt;&lt;br /&gt;&lt;div&gt;
Power Platform Build Tools webinar - &lt;a href=&quot;https://youtu.be/Qwue8fwetJA&quot;&gt;https://youtu.be/Qwue8fwetJA&lt;/a&gt;&amp;nbsp;&lt;div&gt;Solutions Overview and Limitations - click &lt;a href=&quot;https://www.youtube.com/redirect?event=video_description&amp;amp;v=q2j3QO0iBSs&amp;amp;redir_token=QUFFLUhqbnVxcjViRmR5LWlsRDY4bGY2SGw1dzIwdmMtUXxBQ3Jtc0trRU5LNlA0U0VLRVJoSDRJckhFTnhKNFlZbElyVzJscEtWVy0tRndHTWF3V1pMdTR4cDNGR09SWnA0YnhHa3dEbFdjNlU5dVhBTURwaE9UT3hRdTl0YzM5ZmpQbmpuaFNNWDRucEh4ak5wY1c2MWtyNA%3D%3D&amp;amp;q=https%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fpowerapps%2Fmaker%2Fcommon-data-service%2Fsolutions-overview&quot;&gt;here&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;To check out my YouTube channel visit:
&lt;a href=&quot;https://www.youtube.com/@workbench-blog&quot;&gt;https://www.youtube.com/@workbench-blog&lt;/a&gt;
&lt;br /&gt;
&lt;br /&gt;
LIKE AND SUBSCRIBE!!!&lt;br /&gt;
&lt;br /&gt;
Until next post!&lt;br /&gt;
&lt;br /&gt;
MG.-&lt;br /&gt;
Mariano Gomez, MVP
&lt;br /&gt;
&lt;/div&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.theworkbench.blog/feeds/6690667304469326507/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5285970135510371565/6690667304469326507' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/6690667304469326507'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/6690667304469326507'/><link rel='alternate' type='text/html' href='http://www.theworkbench.blog/2020/10/power-platform-application-lifecycle.html' title='Power Platform: Application Lifecycle Management (Part 3/3) - Build and Release Pipelines'/><author><name>Mariano Gomez</name><uri>http://www.blogger.com/profile/13267738662239812289</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiE6oIsiofbLlE_uzUGLv64DYdGPmN9xurCqrbDNWs_axq3WbN9RRlG9IYNI50LMAK96HtSeFIVKWTjqw_URFhfBlHzutaAfUXMkUvW4RK9sO0z6Rt8YyNaETF0D4MdiQ/s113/Mariano+Gomez+-+Square+Headshot+%28website%29.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img.youtube.com/vi/NRLQeT0XP2M/default.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5285970135510371565.post-5600519802411814652</id><published>2020-09-22T17:00:00.000-04:00</published><updated>2026-08-19T10:59:39.958-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Power Apps"/><category scheme="http://www.blogger.com/atom/ns#" term="Power Automate"/><category scheme="http://www.blogger.com/atom/ns#" term="Power BI"/><category scheme="http://www.blogger.com/atom/ns#" term="Power Platform"/><category scheme="http://www.blogger.com/atom/ns#" term="Power Virtual Agents"/><category scheme="http://www.blogger.com/atom/ns#" term="PowerAddicts"/><category scheme="http://www.blogger.com/atom/ns#" term="Source Code Control"/><title type='text'>Power Platform: Application Lifecycle Management (Part 2/3) - Solutions and DevOps</title><content type='html'>In the previous installment, you saw how to work through a set of prerequisites to get you going in your Power Platform Application Lifecycle Management journey. In this video, I will work through building an app and adding it to a solution and show you some additional Azure DevOps configuration options to link your repository to your Power Platform development environment.&lt;br /&gt;&lt;br /&gt;
&lt;iframe allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot; frameborder=&quot;0&quot; height=&quot;480&quot; src=&quot;https://www.youtube.com/embed/q2j3QO0iBSs&quot; width=&quot;640&quot;&gt;&lt;/iframe&gt;&lt;br /&gt;
&lt;br /&gt;
Topics Covered:&lt;br /&gt;&lt;br /&gt;
1. Building app based on template&lt;br /&gt;
2. Adding your app to a solution&lt;br /&gt;
3. Linking your DevOps project to your Power Platform environment&lt;br /&gt;&lt;br /&gt;
References:&lt;br /&gt;
Power Platform Build Tools webinar - &lt;a href=&quot;https://youtu.be/Qwue8fwetJA&quot;&gt;https://youtu.be/Qwue8fwetJA&lt;/a&gt;&amp;nbsp;&lt;div&gt;Solutions Overview and Limitations - click &lt;a href=&quot;https://www.youtube.com/redirect?event=video_description&amp;amp;v=q2j3QO0iBSs&amp;amp;redir_token=QUFFLUhqbnVxcjViRmR5LWlsRDY4bGY2SGw1dzIwdmMtUXxBQ3Jtc0trRU5LNlA0U0VLRVJoSDRJckhFTnhKNFlZbElyVzJscEtWVy0tRndHTWF3V1pMdTR4cDNGR09SWnA0YnhHa3dEbFdjNlU5dVhBTURwaE9UT3hRdTl0YzM5ZmpQbmpuaFNNWDRucEh4ak5wY1c2MWtyNA%3D%3D&amp;amp;q=https%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fpowerapps%2Fmaker%2Fcommon-data-service%2Fsolutions-overview&quot;&gt;here&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;To check out my YouTube channel visit:
&lt;a href=&quot;https://www.youtube.com/@workbench-blog&quot;&gt;https://www.youtube.com/@workbench-blog&lt;/a&gt;
&lt;br /&gt;
&lt;br /&gt;
LIKE AND SUBSCRIBE!!!&lt;br /&gt;
&lt;br /&gt;
Until next post!&lt;br /&gt;
&lt;br /&gt;
MG.-&lt;br /&gt;
Mariano Gomez, MVP
&lt;br /&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.theworkbench.blog/feeds/5600519802411814652/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5285970135510371565/5600519802411814652' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/5600519802411814652'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/5600519802411814652'/><link rel='alternate' type='text/html' href='http://www.theworkbench.blog/2020/09/power-platform-application-lifecycle_22.html' title='Power Platform: Application Lifecycle Management (Part 2/3) - Solutions and DevOps'/><author><name>Mariano Gomez</name><uri>http://www.blogger.com/profile/13267738662239812289</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiE6oIsiofbLlE_uzUGLv64DYdGPmN9xurCqrbDNWs_axq3WbN9RRlG9IYNI50LMAK96HtSeFIVKWTjqw_URFhfBlHzutaAfUXMkUvW4RK9sO0z6Rt8YyNaETF0D4MdiQ/s113/Mariano+Gomez+-+Square+Headshot+%28website%29.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img.youtube.com/vi/q2j3QO0iBSs/default.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5285970135510371565.post-7996061075346226070</id><published>2020-09-14T15:42:00.000-04:00</published><updated>2026-08-19T10:59:40.860-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Power Apps"/><category scheme="http://www.blogger.com/atom/ns#" term="Power Automate"/><category scheme="http://www.blogger.com/atom/ns#" term="Power BI"/><category scheme="http://www.blogger.com/atom/ns#" term="Power Platform"/><category scheme="http://www.blogger.com/atom/ns#" term="PowerAddicts"/><category scheme="http://www.blogger.com/atom/ns#" term="Source Code Control"/><title type='text'>Power Platform: Application Lifecycle Management (Part 1/3) - Prerequisites</title><content type='html'>This series is in response to multiple requests by my viewers to address the topic of Power Apps Application Lifecycle Management (ALM). This series is intended to breakdown the entire process in as much detail as possible, without lengthy explanations on how to get it done or, as often happens, little to no explanations on how you arrived at a particular step or result.&lt;br /&gt; 
&lt;br /&gt;My first installment will focus on Prerequisites, this is, what you need to have in place to get started. I will walk you through provisioning your Power Platform Dev, QA, and Production environments, along with installing the Power Platform Build Tools and setting up your Azure DevOps project. I also explain the concept of solutions in Power Platform and why they are important to your ALM strategy.&lt;br /&gt;&lt;br /&gt;
&lt;iframe allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot; frameborder=&quot;0&quot; height=&quot;480&quot; src=&quot;https://www.youtube.com/embed/6uQPDa4AxVA&quot; width=&quot;640&quot;&gt;&lt;/iframe&gt;
&lt;br /&gt;
&lt;br /&gt;
Topics covered:
&lt;br /&gt;
&lt;br /&gt;
1. Creating Power Platform Environments&lt;br /&gt;
2. Azure DevOps and Installing Power Platform Build Tools&lt;br /&gt;
3. Creating an Azure DevOps Project&lt;br /&gt;
4. Solutions&lt;br /&gt;
&lt;br /&gt;References:&lt;br /&gt;
Power Platform Build Tools webinar - &lt;a href=&quot;https://youtu.be/Qwue8fwetJA&quot;&gt;https://youtu.be/Qwue8fwetJA&lt;/a&gt;
&lt;br /&gt;
&lt;br /&gt;
Credits:&lt;br /&gt;
Summer - Bensound - Royalty free music at &lt;a href=&quot;https://www.bensound.com&quot;&gt;https://www.bensound.com&lt;/a&gt;
&lt;br /&gt;
&lt;br /&gt;
To check out my YouTube channel visit:
&lt;a href=&quot;https://www.youtube.com/@workbench-blog&quot;&gt;https://www.youtube.com/@workbench-blog&lt;/a&gt;
&lt;br /&gt;
&lt;br /&gt;
LIKE AND SUBSCRIBE!!!&lt;br /&gt;
&lt;br /&gt;
Until next post!&lt;br /&gt;
&lt;br /&gt;
MG.-&lt;br /&gt;
Mariano Gomez, MVP</content><link rel='replies' type='application/atom+xml' href='http://www.theworkbench.blog/feeds/7996061075346226070/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5285970135510371565/7996061075346226070' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/7996061075346226070'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/7996061075346226070'/><link rel='alternate' type='text/html' href='http://www.theworkbench.blog/2020/09/power-platform-application-lifecycle.html' title='Power Platform: Application Lifecycle Management (Part 1/3) - Prerequisites'/><author><name>Mariano Gomez</name><uri>http://www.blogger.com/profile/13267738662239812289</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiE6oIsiofbLlE_uzUGLv64DYdGPmN9xurCqrbDNWs_axq3WbN9RRlG9IYNI50LMAK96HtSeFIVKWTjqw_URFhfBlHzutaAfUXMkUvW4RK9sO0z6Rt8YyNaETF0D4MdiQ/s113/Mariano+Gomez+-+Square+Headshot+%28website%29.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img.youtube.com/vi/6uQPDa4AxVA/default.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5285970135510371565.post-3958098468401789063</id><published>2020-09-03T11:29:00.000-04:00</published><updated>2026-08-19T10:59:42.005-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Azure"/><category scheme="http://www.blogger.com/atom/ns#" term="Power Apps"/><category scheme="http://www.blogger.com/atom/ns#" term="Power Automate"/><category scheme="http://www.blogger.com/atom/ns#" term="SharePoint"/><category scheme="http://www.blogger.com/atom/ns#" term="SQL Server"/><title type='text'>Power Automate: Transfer records from SharePoint list to Azure SQL database</title><content type='html'>Majority of times, when we are working with various data sources, we want information to go from one source to another, conditioned to the occurrence of an event. Today, I will show you how to transfer records from a SharePoint list to Azure SQL database tables in response to a timesheet approval event. In the process, you will learn how I leveraged the existing Power Apps Expense template app and converted it into a Timesheet app for the purpose  of recording time for various activities&amp;nbsp;&lt;div&gt;&lt;br /&gt;
&lt;iframe allow=&quot;accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot; frameborder=&quot;0&quot; height=&quot;480&quot; src=&quot;https://www.youtube.com/embed/Xw4h7KECgNI&quot; width=&quot;640&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Other topics covered:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;Working with SharePoint lists and Azure SQL databases&lt;/li&gt;&lt;li&gt;&quot;When an item is created or modified&quot; SharePoint connector trigger&lt;/li&gt;&lt;li&gt;&quot;Delete Item&quot; SharePoint connector action&lt;/li&gt;&lt;li&gt;&quot;Get Items&quot; SharePoint connector action&lt;/li&gt;&lt;li&gt;Conditional actions&lt;/li&gt;&lt;li&gt;Get Rows and Insert Row SQL connector actions&lt;/li&gt;&lt;li&gt;Apply to each action&lt;/li&gt;&lt;li&gt;substring(), formatDateTime()&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Please leave your comments below and let me know what you think. Also, please feel free to suggest topics you would like me to cover.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;References:
Reza Dorrani - Expense Tracking Sample Power App with Receipt Scanning AI - &lt;a href=&quot;https://youtu.be/1X8ihV_EGbE &quot; target=&quot;_blank&quot;&gt;Click here&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Credits:
Creative Minds - Bensound - Royalty free music at &lt;a href=&quot;https://www.bensound.com&quot;&gt;https://www.bensound.com
&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;To check out my YouTube channel visit:
&lt;a href=&quot;https://www.youtube.com/@workbench-blog&quot;&gt;https://www.youtube.com/@workbench-blog&lt;/a&gt;
&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
LIKE AND SUBSCRIBE!!!&lt;br /&gt;
&lt;br /&gt;
Until next post!&lt;br /&gt;
&lt;br /&gt;
MG.-&lt;br /&gt;
Mariano Gomez, MVP</content><link rel='replies' type='application/atom+xml' href='http://www.theworkbench.blog/feeds/3958098468401789063/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5285970135510371565/3958098468401789063' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/3958098468401789063'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/3958098468401789063'/><link rel='alternate' type='text/html' href='http://www.theworkbench.blog/2020/09/power-automate-transfer-records-from.html' title='Power Automate: Transfer records from SharePoint list to Azure SQL database'/><author><name>Mariano Gomez</name><uri>http://www.blogger.com/profile/13267738662239812289</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiE6oIsiofbLlE_uzUGLv64DYdGPmN9xurCqrbDNWs_axq3WbN9RRlG9IYNI50LMAK96HtSeFIVKWTjqw_URFhfBlHzutaAfUXMkUvW4RK9sO0z6Rt8YyNaETF0D4MdiQ/s113/Mariano+Gomez+-+Square+Headshot+%28website%29.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img.youtube.com/vi/Xw4h7KECgNI/default.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5285970135510371565.post-35492654567424500</id><published>2020-08-10T14:57:00.000-04:00</published><updated>2026-08-19T10:59:42.991-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Mixed Reality"/><category scheme="http://www.blogger.com/atom/ns#" term="Power Apps"/><category scheme="http://www.blogger.com/atom/ns#" term="Power Automate"/><category scheme="http://www.blogger.com/atom/ns#" term="Power Platform"/><category scheme="http://www.blogger.com/atom/ns#" term="PowerAddicts"/><category scheme="http://www.blogger.com/atom/ns#" term="PowerApps"/><category scheme="http://www.blogger.com/atom/ns#" term="PowerPlatform"/><category scheme="http://www.blogger.com/atom/ns#" term="Workflow"/><title type='text'>Power Apps: Save Mixed Reality pictures to SharePoint</title><content type='html'>In this video you will learn how to save pictures taken in Power Apps Mixed Reality or Augmented Reality viewer to a SharePoint document library.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; 
&lt;iframe allow=&quot;accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot; frameborder=&quot;0&quot; height=&quot;480&quot; src=&quot;https://www.youtube.com/embed/QDlN6CEHydk&quot; width=&quot;640&quot;&gt;&lt;/iframe&gt;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Some topics covered include: Power Apps ForAll(), With(), Mid(), and Len() functions, the Self operator. Learn to use the Power Automate SharePoint connector and the Create File action, and the base64toBinary() function in an expression. Finally, you will see how you can call a flow from Power Apps to make it all work like a single application.&lt;/div&gt;
&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;To check out my YouTube channel visit:
&lt;a href=&quot;https://www.youtube.com/@workbench-blog&quot;&gt;https://www.youtube.com/@workbench-blog&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
LIKE AND SUBSCRIBE!!!&lt;br /&gt;
&lt;br /&gt;
Until next post!&lt;br /&gt;
&lt;br /&gt;
MG.-&lt;br /&gt;
Mariano Gomez, MVP</content><link rel='replies' type='application/atom+xml' href='http://www.theworkbench.blog/feeds/35492654567424500/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5285970135510371565/35492654567424500' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/35492654567424500'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/35492654567424500'/><link rel='alternate' type='text/html' href='http://www.theworkbench.blog/2020/08/power-apps-save-mixed-reality-pictures.html' title='Power Apps: Save Mixed Reality pictures to SharePoint'/><author><name>Mariano Gomez</name><uri>http://www.blogger.com/profile/13267738662239812289</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiE6oIsiofbLlE_uzUGLv64DYdGPmN9xurCqrbDNWs_axq3WbN9RRlG9IYNI50LMAK96HtSeFIVKWTjqw_URFhfBlHzutaAfUXMkUvW4RK9sO0z6Rt8YyNaETF0D4MdiQ/s113/Mariano+Gomez+-+Square+Headshot+%28website%29.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img.youtube.com/vi/QDlN6CEHydk/default.jpg" height="72" width="72"/><thr:total>0</thr:total><georss:featurename>Roswell, GA, USA</georss:featurename><georss:point>34.0232431 -84.3615555</georss:point><georss:box>5.7130092638211565 -119.5178055 62.333476936178847 -49.205305499999994</georss:box></entry><entry><id>tag:blogger.com,1999:blog-5285970135510371565.post-911519613313195805</id><published>2020-07-28T21:33:00.000-04:00</published><updated>2026-08-19T10:59:44.013-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Flow"/><category scheme="http://www.blogger.com/atom/ns#" term="Power Automate"/><category scheme="http://www.blogger.com/atom/ns#" term="Power Platform"/><title type='text'> Power Automate: Working with Microsoft Graph API and Teams Connector</title><content type='html'>In this video you will learn the basics of the Microsoft Graph API, how to use Graph Explorer, register an application to use Microsoft Graph in Azure Active Directory, and build a simple flow that will use the Teams Connector  and Graph API to list a set of tabs in a Teams channel.&amp;nbsp;&lt;div&gt;&amp;nbsp;
&lt;br /&gt;Other topics covered are: compose and HTTP actions, using the compose action as an alternative to variables, using the filter array action to filter the results in a JSON payload.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;iframe allow=&quot;accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot; frameborder=&quot;0&quot; height=&quot;480&quot; src=&quot;https://www.youtube.com/embed/E4Ve5frEO6s&quot; width=&quot;640&quot;&gt;&lt;/iframe&gt; 
&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;  
References: &lt;br /&gt;
Microsoft Graph: &lt;a href=&quot;https://graph.microsoft.com&quot;&gt;https://graph.microsoft.com&lt;/a&gt;&amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Credits:&amp;nbsp;&lt;/div&gt;&lt;div&gt;Creative Minds - Bensound - Royalty free music at &lt;a href=&quot;https://www.bensound.com&quot;&gt;https://www.bensound.com
&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;To check out my YouTube channel visit:
&lt;a href=&quot;https://www.youtube.com/@workbench-blog&quot;&gt;https://www.youtube.com/@workbench-blog&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
LIKE AND SUBSCRIBE!!!&lt;br /&gt;
&lt;br /&gt;
Until next post!&lt;br /&gt;
&lt;br /&gt;
MG.-&lt;br /&gt;
Mariano Gomez, MVP</content><link rel='replies' type='application/atom+xml' href='http://www.theworkbench.blog/feeds/911519613313195805/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5285970135510371565/911519613313195805' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/911519613313195805'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/911519613313195805'/><link rel='alternate' type='text/html' href='http://www.theworkbench.blog/2020/07/power-automate-working-with-microsoft.html' title=' Power Automate: Working with Microsoft Graph API and Teams Connector'/><author><name>Mariano Gomez</name><uri>http://www.blogger.com/profile/13267738662239812289</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiE6oIsiofbLlE_uzUGLv64DYdGPmN9xurCqrbDNWs_axq3WbN9RRlG9IYNI50LMAK96HtSeFIVKWTjqw_URFhfBlHzutaAfUXMkUvW4RK9sO0z6Rt8YyNaETF0D4MdiQ/s113/Mariano+Gomez+-+Square+Headshot+%28website%29.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img.youtube.com/vi/E4Ve5frEO6s/default.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5285970135510371565.post-4021578936803571859</id><published>2020-07-07T09:25:00.000-04:00</published><updated>2026-08-19T10:59:45.020-04:00</updated><title type='text'>Power Automate: Calculate Soundex of a word with a flow</title><content type='html'>In this video, I will be showing you how to use Power Automate to calculate the
Soundex value of a word using a flow. This calculation uses the American
standard algorithm, but can be adapted to support other algorithms developed for
the English language.&lt;div&gt;&lt;br /&gt;&amp;nbsp;

  &lt;iframe allow=&quot;accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot; frameborder=&quot;0&quot; height=&quot;380&quot; src=&quot;https://www.youtube.com/embed/AL92FNn5rtA&quot; width=&quot;640&quot;&gt;&lt;/iframe&gt;
  &lt;br /&gt;&lt;br /&gt;

In building the workflow you will learn how to use the
  &lt;b&gt;Scope&lt;/b&gt;,&amp;nbsp;&lt;b&gt;Compose&lt;/b&gt;, and&amp;nbsp;&lt;b&gt;Append To String&lt;/b&gt;&amp;nbsp;actions, the Do Until and If Then control structures, along with the
  &lt;b&gt;substring()&lt;/b&gt;, &lt;b&gt;replace()&lt;/b&gt;, &lt;b&gt;add()&lt;/b&gt;, and
  &lt;b&gt;mult()&lt;/b&gt; functions. Please leave your comments below.
  &lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;
  References:
  &lt;br /&gt;Wikipedia Soundex entry -
    &lt;a href=&quot;https://en.wikipedia.org/wiki/Soundex&quot;&gt;https://en.wikipedia.org/wiki/Soundex&lt;/a&gt;
&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;
  Downloads:
  &lt;br /&gt;Power Automate Cookbook: Click
    &lt;a href=&quot;https://powerusers.microsoft.com/t5/Power-Automate-Cookbook/Calculate-a-word-s-soundex-value-with-Power-Automate/td-p/616842&quot;&gt;here&lt;/a&gt;
&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;
To check out my YouTube channel visit:
&lt;a href=&quot;https://www.youtube.com/@workbench-blog&quot;&gt;https://www.youtube.com/@workbench-blog&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
LIKE AND SUBSCRIBE!!!&lt;br /&gt;
&lt;br /&gt;
Until next post!&lt;br /&gt;
&lt;br /&gt;
MG.-&lt;br /&gt;
Mariano Gomez, MVP


&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.theworkbench.blog/feeds/4021578936803571859/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5285970135510371565/4021578936803571859' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/4021578936803571859'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/4021578936803571859'/><link rel='alternate' type='text/html' href='http://www.theworkbench.blog/2020/07/power-automate-calculate-soundex-of.html' title='Power Automate: Calculate Soundex of a word with a flow'/><author><name>Mariano Gomez</name><uri>http://www.blogger.com/profile/13267738662239812289</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiE6oIsiofbLlE_uzUGLv64DYdGPmN9xurCqrbDNWs_axq3WbN9RRlG9IYNI50LMAK96HtSeFIVKWTjqw_URFhfBlHzutaAfUXMkUvW4RK9sO0z6Rt8YyNaETF0D4MdiQ/s113/Mariano+Gomez+-+Square+Headshot+%28website%29.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img.youtube.com/vi/AL92FNn5rtA/default.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5285970135510371565.post-180869434870450809</id><published>2020-06-19T18:41:00.000-04:00</published><updated>2026-08-19T10:59:45.877-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Flow"/><category scheme="http://www.blogger.com/atom/ns#" term="Power Automate"/><category scheme="http://www.blogger.com/atom/ns#" term="Web Services"/><category scheme="http://www.blogger.com/atom/ns#" term="Workflow"/><title type='text'>Power Automate: Building a REST endpoint with the HTTP Request connector</title><content type='html'>In this video I show you how to build a REST endpoint with the Power Automate HTTP Request connector to be used as a web service to transfer data to a on-premises SQL database. In the process, I will be using Postman API development platform to test the endpoint and submit data to a Microsoft Dynamics GP company database via its eConnect API stored procedures.&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp;

&lt;iframe allow=&quot;accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot; frameborder=&quot;0&quot; height=&quot;480&quot; src=&quot;https://www.youtube.com/embed/H9J68gCMbMs&quot; width=&quot;640&quot;&gt;&lt;/iframe&gt;&lt;br /&gt;
&lt;br /&gt;
To check out my YouTube channel visit: &lt;a href=&quot;https://www.youtube.com/@workbench-blog&quot;&gt;https://www.youtube.com/@workbench-blog&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
LIKE AND SUBSCRIBE!!!&lt;br /&gt;
&lt;br /&gt;
Until next post!&lt;br /&gt;
&lt;br /&gt;
MG.-&lt;br /&gt;
Mariano Gomez, MVP


</content><link rel='replies' type='application/atom+xml' href='http://www.theworkbench.blog/feeds/180869434870450809/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5285970135510371565/180869434870450809' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/180869434870450809'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/180869434870450809'/><link rel='alternate' type='text/html' href='http://www.theworkbench.blog/2020/06/power-automate-building-rest-endpoint.html' title='Power Automate: Building a REST endpoint with the HTTP Request connector'/><author><name>Mariano Gomez</name><uri>http://www.blogger.com/profile/13267738662239812289</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiE6oIsiofbLlE_uzUGLv64DYdGPmN9xurCqrbDNWs_axq3WbN9RRlG9IYNI50LMAK96HtSeFIVKWTjqw_URFhfBlHzutaAfUXMkUvW4RK9sO0z6Rt8YyNaETF0D4MdiQ/s113/Mariano+Gomez+-+Square+Headshot+%28website%29.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img.youtube.com/vi/H9J68gCMbMs/default.jpg" height="72" width="72"/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5285970135510371565.post-5321772096270111062</id><published>2020-06-10T16:58:00.000-04:00</published><updated>2026-08-19T10:59:46.713-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="PowerApps"/><title type='text'>Power Apps: Calculating various dates with date functions</title><content type='html'>In this article, I will show you how to leverage the &lt;b&gt;DateDiff()&lt;/b&gt;, &lt;b&gt;DateAdd()&lt;/b&gt;, &lt;b&gt;Weekday()&lt;/b&gt;, &lt;b&gt;DateValue()&lt;/b&gt;, and &lt;b&gt;Day(), &lt;/b&gt;and &lt;b&gt;Today()&lt;/b&gt;&amp;nbsp;functions to calculate all sort of dates that are typical to business applications: first and last day of previous month, first and last day of previous quarter, first and last day of previous week, first and last day of current month, first and last day of current quarter, first and last day of current week, first and last day of next month, first and last day of next quarter, first and last day of next week, etc.&lt;br /&gt;
&lt;br /&gt;
&lt;iframe allow=&quot;accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot; frameborder=&quot;0&quot; height=&quot;480&quot; src=&quot;https://www.youtube.com/embed/sOmZP6twlyM&quot; width=&quot;640&quot;&gt;&lt;/iframe&gt;&lt;br /&gt;
&lt;br /&gt;
Let&#39;s begin:&lt;br /&gt;
&lt;br /&gt;
Each calculation is self-contained for convenience sake, which means the only dependency is on the &lt;b&gt;cdt&lt;/b&gt;&amp;nbsp;context variable.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;u&gt;Current Date&lt;/u&gt;&lt;/b&gt;&lt;br /&gt;
&lt;div style=&quot;background-color: #f4f4f4; border-bottom: gray 1px solid; border-left: gray 1px solid; border-right: gray 1px solid; border-top: gray 1px solid; color: #3333ff; font: 12px / 12px &amp;quot;georgia&amp;quot; , &amp;quot;garamond&amp;quot; , serif; height: 40px; overflow: scroll; width: 640px;&quot;&gt;
&lt;pre&gt;UpdateContext({cdt:Today()});
&lt;/pre&gt;
&lt;/div&gt;
Note: you could also use the Now() function, but be aware that this function also includes the timestamp.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;u&gt;First Day of Previous Month&lt;/u&gt;&lt;/b&gt;&lt;br /&gt;
&lt;div style=&quot;background-color: #f4f4f4; border-bottom: gray 1px solid; border-left: gray 1px solid; border-right: gray 1px solid; border-top: gray 1px solid; color: #3333ff; font: 12px / 12px &amp;quot;georgia&amp;quot; , &amp;quot;garamond&amp;quot; , serif; height: 227px; overflow: scroll; width: 640px;&quot;&gt;
&lt;pre&gt;With(
    {rootdate: DateValue(&quot;01/01/1900&quot;)},
    With(
        {
            mts: DateDiff(
                rootdate,
                cdt,
                Months
            ) - 1
        },
        DateAdd(
            rootdate,
            mts,
            Months
        )
    )
)
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;b&gt;&lt;u&gt;Last Day of Previous Month&lt;/u&gt;&lt;/b&gt;&lt;br /&gt;
&lt;div style=&quot;background-color: #f4f4f4; border-bottom: gray 1px solid; border-left: gray 1px solid; border-right: gray 1px solid; border-top: gray 1px solid; color: #3333ff; font: 12px / 12px &amp;quot;georgia&amp;quot; , &amp;quot;garamond&amp;quot; , serif; height: 40px; overflow: scroll; width: 640px;&quot;&gt;
&lt;pre&gt;DateAdd(cdt, -Day(cdt), Days)
&lt;/pre&gt;
&lt;/div&gt;
&lt;b&gt;&lt;u&gt;&lt;br /&gt;&lt;/u&gt;&lt;/b&gt;
&lt;b&gt;&lt;u&gt;First Day of Current Month&lt;/u&gt;&lt;/b&gt;
&lt;br /&gt;
&lt;div style=&quot;background-color: #f4f4f4; border-bottom: gray 1px solid; border-left: gray 1px solid; border-right: gray 1px solid; border-top: gray 1px solid; color: #3333ff; font: 12px / 12px &amp;quot;georgia&amp;quot; , &amp;quot;garamond&amp;quot; , serif; height: 227px; overflow: scroll; width: 640px;&quot;&gt;
&lt;pre&gt;With(
    {rootdate: DateValue(&quot;01/01/1900&quot;)},
    With(
        {
            mts: DateDiff(
                rootdate,
                cdt,
                Months
            )
        },
        DateAdd(
            rootdate,
            mts,
            Months
        )
    )
)
&lt;/pre&gt;
&lt;/div&gt;
&lt;b&gt;&lt;u&gt;&lt;br /&gt;&lt;/u&gt;&lt;/b&gt;
&lt;b&gt;&lt;u&gt;Last Day of Current Month&lt;/u&gt;&lt;/b&gt;
&lt;br /&gt;
&lt;div style=&quot;background-color: #f4f4f4; border-bottom: gray 1px solid; border-left: gray 1px solid; border-right: gray 1px solid; border-top: gray 1px solid; color: #3333ff; font: 12px / 12px &amp;quot;georgia&amp;quot; , &amp;quot;garamond&amp;quot; , serif; height: 227px; overflow: scroll; width: 640px;&quot;&gt;
&lt;pre&gt;With(
    {rootdate: DateValue(&quot;01/01/1900&quot;)},
    With(
        {
            mts: DateDiff(
                rootdate,
                cdt,
                Months
            ) + 1
        },
        With(
            {
                FirstNxtMonth: DateAdd(
                    rootdate,
                    mts,
                    Months
                )
            },
            DateAdd(
                FirstNxtMonth,
                -1,
                Days
            )
        )
    )
)
&lt;/pre&gt;
&lt;/div&gt;
&lt;b&gt;&lt;u&gt;&lt;br /&gt;&lt;/u&gt;&lt;/b&gt;
&lt;b&gt;&lt;u&gt;First Day of Next Month&lt;/u&gt;&lt;/b&gt;

&lt;br /&gt;
&lt;div style=&quot;background-color: #f4f4f4; border-bottom: gray 1px solid; border-left: gray 1px solid; border-right: gray 1px solid; border-top: gray 1px solid; color: #3333ff; font: 12px / 12px &amp;quot;georgia&amp;quot; , &amp;quot;garamond&amp;quot; , serif; height: 227px; overflow: scroll; width: 640px;&quot;&gt;
&lt;pre&gt;With(
    {rootdate: DateValue(&quot;01/01/1900&quot;)},
    With(
        {
            mts: DateDiff(
                rootdate,
                cdt,
                Months
            ) + 1
        },
        DateAdd(
            rootdate,
            mts,
            Months
        )
    )
)
&lt;/pre&gt;
&lt;/div&gt;
&lt;u&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/u&gt;
&lt;u&gt;&lt;b&gt;Last Day of Next Month&lt;/b&gt;&lt;/u&gt;
&lt;br /&gt;
&lt;div style=&quot;background-color: #f4f4f4; border-bottom: gray 1px solid; border-left: gray 1px solid; border-right: gray 1px solid; border-top: gray 1px solid; color: #3333ff; font: 12px / 12px &amp;quot;georgia&amp;quot; , &amp;quot;garamond&amp;quot; , serif; height: 227px; overflow: scroll; width: 640px;&quot;&gt;
&lt;pre&gt;With(
    {rootdate: DateValue(&quot;01/01/1900&quot;)},
    With(
        {
            mts: DateDiff(
                rootdate,
                cdt,
                Months
            ) + 2
        },
        With(
            {
                FirstNxtMonth: DateAdd(
                    rootdate,
                    mts,
                    Months
                )
            },
            DateAdd(
                FirstNxtMonth,
                -1,
                Days
            )
        )
    )
)
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;b&gt;&lt;u&gt;First Day of Previous Quarter&lt;/u&gt;&lt;/b&gt;

&lt;br /&gt;
&lt;div style=&quot;background-color: #f4f4f4; border-bottom: gray 1px solid; border-left: gray 1px solid; border-right: gray 1px solid; border-top: gray 1px solid; color: #3333ff; font: 12px / 12px &amp;quot;georgia&amp;quot; , &amp;quot;garamond&amp;quot; , serif; height: 227px; overflow: scroll; width: 640px;&quot;&gt;
&lt;pre&gt;With(
    {rootdate: DateValue(&quot;01/01/1900&quot;)},
    With(
        {
            qtrs: DateDiff(
                rootdate,
                cdt,
                Quarters
            )
        },
        DateAdd(
            rootdate,
            qtrs - 1,
            Quarters
        )
    )
)
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;b&gt;&lt;u&gt;Last Day of Previous Quarter&lt;/u&gt;&lt;/b&gt;
&lt;br /&gt;
&lt;div style=&quot;background-color: #f4f4f4; border-bottom: gray 1px solid; border-left: gray 1px solid; border-right: gray 1px solid; border-top: gray 1px solid; color: #3333ff; font: 12px / 12px &amp;quot;georgia&amp;quot; , &amp;quot;garamond&amp;quot; , serif; height: 227px; overflow: scroll; width: 640px;&quot;&gt;
&lt;pre&gt;With(
    {rootdate: DateValue(&quot;01/01/1900&quot;)},
    With(
        {
            qrtr: DateDiff(
                rootdate,
                cdt,
                Quarters
            )
        },
        With(
            {
                NxtQrtr: DateAdd(
                    rootdate,
                    qrtr,
                    Quarters
                )
            },
            DateAdd(
                NxtQrtr,
                -1,
                Days
            )
        )
    )
)
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;b&gt;&lt;u&gt;
&lt;/u&gt;&lt;/b&gt;
&lt;b&gt;&lt;u&gt;First Day of Current Quarter&lt;/u&gt;&lt;/b&gt;
&lt;br /&gt;
&lt;div style=&quot;background-color: #f4f4f4; border-bottom: gray 1px solid; border-left: gray 1px solid; border-right: gray 1px solid; border-top: gray 1px solid; color: #3333ff; font: 12px / 12px &amp;quot;georgia&amp;quot; , &amp;quot;garamond&amp;quot; , serif; height: 227px; overflow: scroll; width: 640px;&quot;&gt;
&lt;pre&gt;With(
    {rootdate: DateValue(&quot;01/01/1900&quot;)},
    With(
        {
            qtrs: DateDiff(
                rootdate,
                cdt,
                Quarters
            )
        },
        DateAdd(
            rootdate,
            qtrs,
            Quarters
        )
    )
)
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;b&gt;&lt;u&gt;Last Day of Current Quarter&lt;/u&gt;&lt;/b&gt;
&lt;br /&gt;
&lt;div style=&quot;background-color: #f4f4f4; border-bottom: gray 1px solid; border-left: gray 1px solid; border-right: gray 1px solid; border-top: gray 1px solid; color: #3333ff; font: 12px / 12px &amp;quot;georgia&amp;quot; , &amp;quot;garamond&amp;quot; , serif; height: 227px; overflow: scroll; width: 640px;&quot;&gt;
&lt;pre&gt;With(
    {rootdate: DateValue(&quot;01/01/1900&quot;)},
    With(
        {
            qrtr: DateDiff(
                rootdate,
                cdt,
                Quarters
            ) + 1
        },
        With(
            {
                NxtQrtr: DateAdd(
                    rootdate,
                    qrtr,
                    Quarters
                )
            },
            DateAdd(
                NxtQrtr,
                -1,
                Days
            )
        )
    )
)
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;b&gt;&lt;u&gt;First Day of Next Quarter&lt;/u&gt;&lt;/b&gt;
&lt;br /&gt;
&lt;div style=&quot;background-color: #f4f4f4; border-bottom: gray 1px solid; border-left: gray 1px solid; border-right: gray 1px solid; border-top: gray 1px solid; color: #3333ff; font: 12px / 12px &amp;quot;georgia&amp;quot; , &amp;quot;garamond&amp;quot; , serif; height: 227px; overflow: scroll; width: 640px;&quot;&gt;
&lt;pre&gt;With(
    {rootdate: DateValue(&quot;01/01/1900&quot;)},
    With(
        {
            qrtr: DateDiff(
                rootdate,
                cdt,
                Quarters
            ) + 1
        },
        With(
            {
                NxtQrtr: DateAdd(
                    rootdate,
                    qrtr,
                    Quarters
                )
            },
            DateAdd(
                NxtQrtr,
                0,
                Days
            )
        )
    )
)
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;b&gt;&lt;u&gt;Last Day of Next Quarter&lt;/u&gt;&lt;/b&gt;
&lt;br /&gt;
&lt;div style=&quot;background-color: #f4f4f4; border-bottom: gray 1px solid; border-left: gray 1px solid; border-right: gray 1px solid; border-top: gray 1px solid; color: #3333ff; font: 12px / 12px &amp;quot;georgia&amp;quot; , &amp;quot;garamond&amp;quot; , serif; height: 227px; overflow: scroll; width: 640px;&quot;&gt;
&lt;pre&gt;With(
    {rootdate: DateValue(&quot;01/01/1900&quot;)},
    With(
        {
            qrtr: DateDiff(
                rootdate,
                cdt,
                Quarters
            ) + 2
        },
        With(
            {
                NxtQrtr: DateAdd(
                    rootdate,
                    qrtr,
                    Quarters
                )
            },
            DateAdd(
                NxtQrtr,
                -1,
                Days
            )
        )
    )
)
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;b&gt;&lt;u&gt;First Day of Previous Week&lt;/u&gt;&lt;/b&gt;
&lt;br /&gt;
&lt;div style=&quot;background-color: #f4f4f4; border-bottom: gray 1px solid; border-left: gray 1px solid; border-right: gray 1px solid; border-top: gray 1px solid; color: #3333ff; font: 12px / 12px &amp;quot;georgia&amp;quot; , &amp;quot;garamond&amp;quot; , serif; height: 227px; overflow: scroll; width: 640px;&quot;&gt;
&lt;pre&gt;With(
    {rootdate: DateValue(&quot;01/01/1900&quot;)},
    With(
        {
            diffval: DateDiff(
                rootdate,
                cdt,
                Days
            ),
            Offset: 1 - Weekday(cdt)
        },
        DateAdd(
            cdt,
            Offset - 7,
            Days
        )
    )
)
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;b&gt;&lt;u&gt;Last Day of Previous Week&lt;/u&gt;&lt;/b&gt;
&lt;br /&gt;
&lt;div style=&quot;background-color: #f4f4f4; border-bottom: gray 1px solid; border-left: gray 1px solid; border-right: gray 1px solid; border-top: gray 1px solid; color: #3333ff; font: 12px / 12px &amp;quot;georgia&amp;quot; , &amp;quot;garamond&amp;quot; , serif; height: 227px; overflow: scroll; width: 640px;&quot;&gt;
&lt;pre&gt;With(
    {rootdate: DateValue(&quot;01/01/1900&quot;)},
    With(
        {
            diffval: DateDiff(
                rootdate,
                cdt,
                Days
            ),
            Offset: 1 - Weekday(cdt)
        },
        DateAdd(
            cdt,
            Offset - 1,
            Days
        )
    )
)
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;b&gt;&lt;u&gt;First Day of Current Week&lt;/u&gt;&lt;/b&gt;
&lt;br /&gt;
&lt;div style=&quot;background-color: #f4f4f4; border-bottom: gray 1px solid; border-left: gray 1px solid; border-right: gray 1px solid; border-top: gray 1px solid; color: #3333ff; font: 12px / 12px &amp;quot;georgia&amp;quot; , &amp;quot;garamond&amp;quot; , serif; height: 227px; overflow: scroll; width: 640px;&quot;&gt;
&lt;pre&gt;With(
    {rootdate: DateValue(&quot;01/01/1900&quot;)},
    With(
        {
            diffval: DateDiff(
                rootdate,
                cdt,
                Days
            ),
            Offset: 1 - Weekday(cdt)
        },
        DateAdd(
            cdt,
            Offset,
            Days
        )
    )
)
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;b&gt;&lt;u&gt;Last Day of Current Week&lt;/u&gt;&lt;/b&gt;
&lt;br /&gt;
&lt;div style=&quot;background-color: #f4f4f4; border-bottom: gray 1px solid; border-left: gray 1px solid; border-right: gray 1px solid; border-top: gray 1px solid; color: #3333ff; font: 12px / 12px &amp;quot;georgia&amp;quot; , &amp;quot;garamond&amp;quot; , serif; height: 227px; overflow: scroll; width: 640px;&quot;&gt;
&lt;pre&gt;With(
    {rootdate: DateValue(&quot;01/01/1900&quot;)},
    With(
        {
            diffval: DateDiff(
                rootdate,
                cdt,
                Days
            ),
            Offset: 1 - Weekday(cdt)
        },
        DateAdd(
            cdt,
            Offset + 6,
            Days
        )
    )
)
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;b&gt;&lt;u&gt;First Day of Next Week&lt;/u&gt;&lt;/b&gt;
&lt;br /&gt;
&lt;div style=&quot;background-color: #f4f4f4; border-bottom: gray 1px solid; border-left: gray 1px solid; border-right: gray 1px solid; border-top: gray 1px solid; color: #3333ff; font: 12px / 12px &amp;quot;georgia&amp;quot; , &amp;quot;garamond&amp;quot; , serif; height: 227px; overflow: scroll; width: 640px;&quot;&gt;
&lt;pre&gt;With(
    {rootdate: DateValue(&quot;01/01/1900&quot;)},
    With(
        {
            diffval: DateDiff(
                rootdate,
                cdt,
                Days
            ),
            Offset: 1 - Weekday(cdt)
        },
        DateAdd(
            cdt,
            Offset + 7,
            Days
        )
    )
)
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;b&gt;&lt;u&gt;Last Day of Next Week&lt;/u&gt;&lt;/b&gt;
&lt;br /&gt;
&lt;div style=&quot;background-color: #f4f4f4; border-bottom: gray 1px solid; border-left: gray 1px solid; border-right: gray 1px solid; border-top: gray 1px solid; color: #3333ff; font: 12px / 12px &amp;quot;georgia&amp;quot; , &amp;quot;garamond&amp;quot; , serif; height: 227px; overflow: scroll; width: 640px;&quot;&gt;
&lt;pre&gt;With(
    {rootdate: DateValue(&quot;01/01/1900&quot;)},
    With(
        {
            diffval: DateDiff(
                rootdate,
                cdt,
                Days
            ),
            Offset: 1 - Weekday(cdt)
        },
        DateAdd(
            cdt,
            Offset + 13,
            Days
        )
    )
)
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
You can download the application on the &lt;a href=&quot;https://powerusers.microsoft.com/t5/Community-App-Samples/Power-Apps-Calculating-various-dates-with-date-functions/m-p/592175#M543&quot;&gt;Power Apps Community apps gallery&lt;/a&gt;
&lt;br /&gt;
&lt;br /&gt;
To check out my YouTube channel visit: &lt;a href=&quot;https://www.youtube.com/@workbench-blog&quot;&gt;https://www.youtube.com/@workbench-blog&lt;/a&gt;
&lt;br /&gt;
LIKE AND SUBSCRIBE!!!&lt;br /&gt;
&lt;br /&gt;
Until next post!&lt;br /&gt;
&lt;br /&gt;
MG.-&lt;br /&gt;
Mariano Gomez, MVP



</content><link rel='replies' type='application/atom+xml' href='http://www.theworkbench.blog/feeds/5321772096270111062/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5285970135510371565/5321772096270111062' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/5321772096270111062'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/5321772096270111062'/><link rel='alternate' type='text/html' href='http://www.theworkbench.blog/2020/06/powerapps-dates.html' title='Power Apps: Calculating various dates with date functions'/><author><name>Mariano Gomez</name><uri>http://www.blogger.com/profile/13267738662239812289</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiE6oIsiofbLlE_uzUGLv64DYdGPmN9xurCqrbDNWs_axq3WbN9RRlG9IYNI50LMAK96HtSeFIVKWTjqw_URFhfBlHzutaAfUXMkUvW4RK9sO0z6Rt8YyNaETF0D4MdiQ/s113/Mariano+Gomez+-+Square+Headshot+%28website%29.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img.youtube.com/vi/sOmZP6twlyM/default.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5285970135510371565.post-1255344704300884218</id><published>2020-06-08T12:35:00.000-04:00</published><updated>2026-08-19T10:59:47.747-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Augmented Reality"/><category scheme="http://www.blogger.com/atom/ns#" term="Mixed Reality"/><category scheme="http://www.blogger.com/atom/ns#" term="PowerApps"/><title type='text'>Power Apps: Mixed Reality spatial controls</title><content type='html'>For the final installment of the Power Apps Mixed Reality series, I&#39;m going to show you how to use the MR spatial controls, &lt;b&gt;Shape in MR&lt;/b&gt; and &lt;b&gt;Measure in MR&lt;/b&gt;. You will get to understand a bit more about the available properties for these controls and how they interact with collections and galleries to produce an app.&lt;br /&gt;
&lt;br /&gt;
&lt;iframe allow=&quot;accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot; frameborder=&quot;0&quot; height=&quot;480&quot; src=&quot;https://www.youtube.com/embed/OZQCWUEqjbg&quot; width=&quot;640&quot;&gt;&lt;/iframe&gt;

&lt;div&gt;
&lt;br /&gt;
To check out my YouTube channel visit: &lt;a href=&quot;https://www.youtube.com/@workbench-blog&quot;&gt;https://www.youtube.com/@workbench-blog&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
LIKE AND SUBSCRIBE!!!&lt;br /&gt;
&lt;br /&gt;
Until next post!&lt;br /&gt;
&lt;br /&gt;
MG.-&lt;br /&gt;
Mariano Gomez, MVP&lt;br /&gt;
&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://www.theworkbench.blog/feeds/1255344704300884218/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5285970135510371565/1255344704300884218' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/1255344704300884218'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5285970135510371565/posts/default/1255344704300884218'/><link rel='alternate' type='text/html' href='http://www.theworkbench.blog/2020/06/power-apps-mixed-reality-spatial.html' title='Power Apps: Mixed Reality spatial controls'/><author><name>Mariano Gomez</name><uri>http://www.blogger.com/profile/13267738662239812289</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiE6oIsiofbLlE_uzUGLv64DYdGPmN9xurCqrbDNWs_axq3WbN9RRlG9IYNI50LMAK96HtSeFIVKWTjqw_URFhfBlHzutaAfUXMkUvW4RK9sO0z6Rt8YyNaETF0D4MdiQ/s113/Mariano+Gomez+-+Square+Headshot+%28website%29.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img.youtube.com/vi/OZQCWUEqjbg/default.jpg" height="72" width="72"/><thr:total>0</thr:total></entry></feed>