Tools https://dynamics-chronicles.com/ en Power Apps Code Apps in Practice: Recreating Model-Driven App Patterns https://dynamics-chronicles.com/article/power-apps-code-apps-practice-recreating-model-driven-app-patterns <span property="schema:name" class="field field--name-title field--type-string field--label-hidden">Power Apps Code Apps in Practice: Recreating Model-Driven App Patterns</span> <span rel="schema:author" class="field field--name-uid field--type-entity-reference field--label-hidden"><a title="View user profile." href="/user/axel" lang="" about="/user/axel" typeof="schema:Person" property="schema:name" datatype="" class="username">Axel</a></span> <span property="schema:dateCreated" content="2026-03-11T14:56:40+00:00" class="field field--name-created field--type-created field--label-hidden">Wed, 03/11/2026 - 15:56</span> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-above"> <div class="field__label">Body</div> <div property="schema:text" class="field__item"><h2></h2> <p>Power Apps Code Apps went from preview to general availability in record time. With that, Microsoft has sent a clear signal that it is taking this technology very seriously.</p> <p>In fact, Code Apps are something of a small revolution. They allow pro-code developers to deliver custom web apps directly within a Dataverse environment without the need to implement authentication libraries.</p> <p>But that is not all: Low-code developers can benefit from them as well. With tools like GitHub Copilot and Claude Code, you no longer necessarily need to understand a programming language such as React in order to build full-blown web application. Instead, you can simply describe the features you want in a prompt and have them implemented.</p> <p>This will fundamentally change the way how apps will be implemented in the future. <a href="https://storybooks.fluentui.dev/react">React Fluent UI components</a> can be integrated into Code Apps and open up entirely new possibilities for designing user interfaces such as accordions, cards or TreeViews.</p> <h2>Motivation</h2> <p>There is, however, one important point to consider: At the time of writing, Power Apps Code Apps are stand-alone apps. That means they are not integrated into a model-driven app, but exist as independent applications, similar to canvas apps. There is also no custom page in a model-driven app to represent a code app.</p> <p>But what if you still need the typical built-in functionality of a model-driven app upfront, such as filtering records in a data grid? One option would be to add a button to the model-driven app or try to integrate the app through an iframe.</p> <p>This article explores another approach: recreating typical model-driven app functionality directly inside the custom app. The goal is to assess how much effort this requires and how quickly something like this can be implemented, <strong>not to reinvent the wheel, but to demonstrate the capabilities and the power of Code Apps. </strong>And of course also to show that, for many use cases, you no longer need a model-driven app as the entry point.</p> <h2>PoC: Table Browser App</h2> <p>The goal was to build an new code app "Table Browser" from scratch which demonstrates that a standalone Power Apps Code App can reproduce familiar Model-Driven-App-style functionality, such as filtered and searchable record lists with the same look and feel like Model-Driven-Apps. This can serve as an entry point and opens the door to building a fully fledged standalone app with a much richer user experience, including features such as tree views and other advanced interface patterns that are difficult to realize in a traditional Model-Driven App.</p> <p>It is also a <strong>proof of concept</strong>: each feature was chosen to answer a specific question about what Code Apps can do. A secondary goal was to evaluate these two tools in a real-world scenario and gain hands-on experience with AI-assisted development.</p> <p><strong>The entire app was built in a single Saturday</strong> — using both <strong>GitHub Copilot</strong> and <strong>Claude Code</strong> as AI coding assistants. Fun fact: Claude Code estimated the development effort for the entire app at 12 to 15 days if implemented by a senior developer without the help of AI.</p> <p><img alt="Screenshots Table Browser App" data-entity-type="file" data-entity-uuid="db68f02b-3641-4694-afb2-2c9bd8b91f85" src="/sites/default/files/inline-images/TableBrowser.png" /></p> <p> </p> <p></p> <h3>Proof Points</h3> <ul> <li><strong>Performance</strong>: Can a Code App render large Dataverse record sets noticeably faster than a Model-Driven grid? Yes.</li> <li><strong>Infinite scroll</strong>: How much code does it take to implement demand-driven pagination? Very little — a scroll event plus a <code>getAll</code> call with <code>top</code> and <code>skip</code>.</li> <li><strong>Metadata access</strong>: How easy is it to read column metadata at runtime? One SDK call to <code>retrieveMultipleRecordsAsync</code> on <code>EntityDefinition</code> gives you full attribute detail.</li> <li><strong>View definitions</strong>: Can a Code App read the default system view and use it to determine which columns to show? Yes — <code>savedquery</code> records expose the column layout as XML, which the app parses to build its default column set.</li> <li><strong>Form definitions</strong>: Can a Code App reconstruct the default form layout — tabs, sections, fields — without hardcoding it? Yes — <code>systemform</code> records contain the full form structure as JSON and XML, which the app uses to render the record detail dialog.</li> <li><strong>Column persistence</strong>: Can user preferences be stored in Dataverse instead of localStorage — cross-device, without a custom table? Yes, via personal views (<code>userquery</code>).</li> </ul> <p></p> <h3>Key capabilities</h3> <ul> <li>Browse records from multiple Dataverse tables with infinite scroll and client-side search</li> <li>Inspect column metadata and field properties (schema view)</li> <li>Customize visible columns per table — persisted as personal views (<code>userquery</code> records) in Dataverse</li> <li>View full record details in a form-based dialog with tabs and sections</li> <li>Switch between list (DataGrid) and card grid layouts</li> <li>Light/dark mode, display density, and accent color theming (persisted to localStorage)</li> </ul> <h3>UI Features</h3> <p>The goal was to implement the same look and feel that we know from Model-Driven-App. This can easily be achieved by using the  <a href="https://storybooks.fluentui.dev/react">React Fluent UI components</a> library. The following chapters show which features have been implemented. </p> <h4>List View</h4> <img alt="List View" data-entity-type="file" data-entity-uuid="597a8d02-4260-4b33-979d-5f83a837e491" src="/sites/default/files/inline-images/Accounts-List-800x600_0.png" class="align-center" /> <p> </p> <ul> <li>The app uses the metadata to look for the default view of the table and shows all columns defined there.</li> <li>Columns width can be changed by drag and drop.</li> <li>Columns can be sorted. If sorted, an api call is used for server side sorting</li> <li>Smooth paging: Scrolling to the end of the list reloads the next page of data (e.g. the next 50 records)</li> </ul> <p> </p> <h4>Search and highlighting</h4> <img alt="Search Highlighting" data-entity-type="file" data-entity-uuid="95b7edfa-b8c9-492c-b6c1-7c5586c995fe" src="/sites/default/files/inline-images/Search-Highlighting-800.png" class="align-center" /> <p> </p> <ul> <li>Search was implemented with a single field which searches in all text fields <ul> <li>This was easier to implement</li> <li>A filter below every column, as in model driven apps could be another option for the future.</li> </ul> </li> <li>Highlighting the matched text was very easy to implement, so this functionality was added as well, even though it does not exist in model-driven apps</li> </ul> <p> </p> <h4>Column selection and reordering<br />  </h4> <img alt="Select columns" data-entity-type="file" data-entity-uuid="60057dc1-b686-4bab-9768-013a98b68d35" src="/sites/default/files/inline-images/Select-columns%20-%20800.png" class="align-center" /> <p> </p> <ul> <li>A modal dialog window can be used to select the columns to display</li> <li>A drag and drop component can be used to reorder the columns. The <a href="https://dndkit.com/overview">@dnd-kit/sortable library </a>was used to implement this feature</li> <li>The adjusted view is updated in the Dataverse personal view</li> </ul> <p> </p> <h4>Card View</h4> <img alt="Card View" data-entity-type="file" data-entity-uuid="83ad4ccb-1adc-4366-a153-900f2c7a75d6" src="/sites/default/files/inline-images/Accounts-Cardview-800.png" class="align-center" /> <p> </p> <ul> <li>A Card View as an alternative to a List View was implemented</li> <li>This was quite easy because a card already exists as a UI component in the React UI Components</li> <li>This view is especially useful for data with images</li> </ul> <p> </p> <h4>Detail View Edit Dialog</h4> <img alt="Detail View Edit Dialog" data-entity-type="file" data-entity-uuid="9715deab-a704-462f-a6d6-e44f5fed7d02" src="/sites/default/files/inline-images/Editor-800_0.png" class="align-center" /> <p> </p> <ul> <li>A detailed review was implemented to show and edit the current record</li> <li>The code dynamically parses the system form record to create the layout dynamically from the defaults form definition (including tabs)</li> <li>Field types text, text area, option set, lookup, yes/no, date/time are supported and fully working</li> <li>Of course, the form doesn't support PCF or sub grids.</li> <li>An alternative approach approach for the future, could be to redirect to a detail view, like a Model-Driven-App does </li> </ul> <p> </p> <h4>Table Column Definitions</h4> <img alt="Column meta data list" data-entity-type="file" data-entity-uuid="b3926707-c64d-40ce-a2ba-1a91b65b3814" src="/sites/default/files/inline-images/Schema-Browser-800.png" class="align-center" /> <p> </p> <ul> <li>A column meta data list was implemented</li> <li>It automatically shows all columns of the table with display name, data type, required, and description</li> <li>It proves that metadata can easily be fetched by the @microsoft/power-apps/data lib (for code apps)</li> <li>The column metadata can also be filtered</li> <li>The screenshot shows the list in dark mode. Of course, dark mode is available for the whole application. </li> </ul> <h4> Settings dialog </h4> <img alt="Settings dialog" data-entity-type="file" data-entity-uuid="aa4c2d07-fcce-4ca5-a210-87e179d8f429" src="/sites/default/files/inline-images/settings-400.png" class="align-center" /> <p> </p> <ul> <li>A small settings dialog was implemented for the settings of dark mode, display density, and accent color</li> <li>This was very easy to implement since dark mode, accent color and density are just properties in the React Fluent UI library</li> <li>The settings are stored in the local storage of the browser for simplicity</li> </ul> <h2>Lessons learned with GitHub Copilot and Claude Code</h2> <p>The project was developed almost entirely in "interactive agent-driven mode".<br /> Both GitHub Copilot and Claude Code were used as active development assistants during the entire process.<br /> <br /> The agents were involved in:</p> <ul> <li>Concept discussions</li> <li>Implementation</li> <li>Refactoring</li> <li>Architectural decisions</li> <li>Documentation improvements</li> <li>Iterative improvements to the collaboration workflow</li> </ul> <p>In addition to generating code, the agents were also used to discuss best practices and improve the documentation and structure of the project itself. Both agents used the same model (Claude Sonnet 4.6), but produced different results due to their overall architectural differences.</p> <p><br /> <strong>Claude Code</strong></p> <p>Overall, Claude Code produced more consistent and reliable results.<br /> <br /> Strengths included:</p> <ul> <li>Better architectural reasoning</li> <li>More structured implementations</li> <li>Clearer explanations</li> <li>Higher-quality refactorings</li> </ul> <p>Claude Code was particularly strong when working across multiple files or when performing larger structural changes in the project. In the beginning it introduced reusable UI components even before having instructions to do so.<br /> When working with Claude Code, development sessions occasionally reached usage limits. This required temporarily switching back to GitHub Copilot until the session limit reset. As a result, the project naturally evolved into a mixed workflow where both tools contributed to the final implementation.</p> <p><strong>GitHub Copilot</strong></p> <p>GitHub Copilot performed well for smaller tasks but showed some weaknesses when acting as a full development agent.<br /> <br /> Some common issues included:<br /> - Guessing instead of reasoning<br /> - Repeating the same incorrect approach multiple times<br /> - Getting stuck in loops when a problem was not clearly defined<br /> <br /> However, some of these issues were likely influenced by better instructions given to the agent (e.g. links to the official documentation, instructions to always look there first) <br /> <br /> At the beginning of the project, important rules were not yet defined, for example:<br /> - Asking for confirmation before large refactorings<br /> - Explaining architectural changes before applying them<br /> - Avoiding speculative changes<br /> <br /> Once clearer rules were established, the results improved.</p> <h2>Architecture</h2> <p>The architecture naturally follows the architecture of Power Apps Code Apps. Services for accessing data and model classes were generated using the pac tool.</p> <p>The following architecture diagram was generated with Claude Code. This no longer has to be done manually by hand either...</p> <img alt="Architecture" data-entity-type="file" data-entity-uuid="8f7e12a6-d6cd-42d9-b7c4-01ede36e8977" src="/sites/default/files/inline-images/architecture.png" class="align-center" /> <h2>Conclusion</h2> <p>It is extremely impressive how quickly and easily a Power Apps Code App can be developed with GitHub Copilot or Claude Code. What makes development so fast and straightforward is the combination of coding agents with an intelligent framework for app development.</p> <p>Throughout the entire development process, there was never any sense of working with an unstable product or with something that had only recently reached general availability. There were no crashes at all, and deploying the app via solution to another environment also worked flawlessly.</p> <p>Only in a few places does the API appear to be not entirely complete yet. For example, <code>RetrieveRecordAsync</code> did not work, so a workaround had to be implemented using <code>GetAll</code> with a filter expression. However, the most important actions for loading, filtering, and saving data are already very stable.<br /> <br /> Code Apps will fundamentally change the way we build apps in Dataverse environments and across the Power Platform. Canvas Apps may soon become obsolete and be replaced by Code Apps. There may also be more options in the future to integrate Code Apps into Model-Driven Apps. Until then, however, both can coexist peacefully side by side.</p> <p><br />  </p></div> </div> <div class="field field--name-field-image field--type-image field--label-above"> <div class="field__label">Image</div> <div class="images-container clearfix"> <div class="image-preview clearfix"> <div class="image-wrapper clearfix"> <div class="field field--name-field-image field--type-image field--label-above field__item">/sites/default/files/2026-03/Power-Apps-Code-Apps-in-Practice_1.png</div> </div> </div> </div> </div> Wed, 11 Mar 2026 14:56:40 +0000 Axel 870 at https://dynamics-chronicles.com Row Summary in Model-Driven Apps https://dynamics-chronicles.com/article/row-summary-model-driven-apps <span property="schema:name" class="field field--name-title field--type-string field--label-hidden">Row Summary in Model-Driven Apps</span> <span rel="schema:author" class="field field--name-uid field--type-entity-reference field--label-hidden"><a title="View user profile." href="/user/stephane-pelhatre" lang="" about="/user/stephane-pelhatre" typeof="schema:Person" property="schema:name" datatype="" class="username">Stephane Pelhatre</a></span> <span property="schema:dateCreated" content="2025-10-14T08:04:03+00:00" class="field field--name-created field--type-created field--label-hidden">Tue, 10/14/2025 - 10:04</span> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-above"> <div class="field__label">Body</div> <div property="schema:text" class="field__item"><h3>Introduction</h3> <p>In model-driven applications, forms often contain many fields spread across multiple tabs and sections, which can make it difficult and time-consuming to find key information. Navigating between these tabs slows productivity and increases the risk of missing important information. Finding key insights quickly can be a challenge.</p> <p>The Row Summary feature solves this problem by displaying a clear and concise summary of the most important information. Makers can customize the columns to include and choose how the summary is presented (as bullet points or as a short paragraph). This allows users to get the necessary context at a glance, without having to scroll through the entire form.<br /> Furthermore, the summary can include clickable links to related records.</p> <p>At the time of writing this article, this feature is still in preview.<br /> Summaries are currently only supported in the English language.<br /> Preview features aren’t meant for production use and might have restricted functionality.</p> <h3>Enable Row Summary feature</h3> <p>Before you can configure a Row Summary, you need to enable the feature in your environment:</p> <ol> <li>Go to <a href="https://admin.powerplatform.microsoft.com">Power Platform Admin Center</a></li> <li>Select your environment</li> <li>Go to <strong>Settings &gt; Product &gt; Features</strong><br /> <img alt="enable0" data-entity-type="file" data-entity-uuid="02031ea5-0e19-4bd1-a664-fd4c5a7e848e" src="/sites/default/files/inline-images/enable%20feature1.png" /><br />  </li> <li>Enable the feature <strong>AI Insight Cards</strong><br /> <img alt="Enable1" data-entity-type="file" data-entity-uuid="cd7be403-59f5-40d9-8689-8efcdb06684f" src="/sites/default/files/inline-images/Enable1_0.png" /></li> </ol> <p>Once enabled, you’ll be able to access and configure the Row Summary feature in model-driven apps.</p> <h3>Configure Row Summary for a table</h3> <p>To configure a row summary for main forms for a specific table:</p> <ol> <li>Open <a href="https://make.powerapps.com/">Power Apps Portal</a> and select your environment</li> <li>Select the table where you want to apply the row summary (e.g., Contact) from the left navigation pane and click on <strong>Row summary</strong> in the section <strong>Customizations<br /> <img alt="Enable2" data-entity-type="file" data-entity-uuid="b9449246-8aee-46cd-a2d8-8558df313b72" src="/sites/default/files/inline-images/Enable2.png" /></strong><br /> <br /> Another way is to select your table from the left navigation pane and click on <strong>Forms </strong>in the section <strong>Data experience<br /> <img alt="Enable8" data-entity-type="file" data-entity-uuid="6f75bf72-246c-4780-bc0b-f2ad9375243e" src="/sites/default/files/inline-images/Enable8.png" /></strong><br /> <br /> Then click on <strong>Row summary</strong> on the command bar and from the dropdown select <strong>Edit Summary </strong>to begin customization<br /> <img alt="Enable3" data-entity-type="file" data-entity-uuid="c793e558-8c1f-4a6b-833e-f9e49427ff71" src="/sites/default/files/inline-images/Enable3.png" /><br />  </li> <li>Following window is displayed:<br /> <img alt="Enable4" data-entity-type="file" data-entity-uuid="58879e41-495b-46dc-a92e-e65860d37813" src="/sites/default/files/inline-images/Enable4.png" /><br />  </li> <li>Now you have to define columns to include in the summary and write a prompt<br /> - In the Prompt editor, click on <b>+Add data </b>or type "/" to choose the columns you want the AI to summarize (in my example: full name, job title, company name, marital status)<br />   If you click on <b>+Add data </b>you can add several columns in one step which is not the case if you type "/"<br /> - Write a custom instruction (prompt) describing how the summary should be generated<br />   In my example below : <em>Generate a concise summary using the selected fields. Use bullet points and return “No value” if a field is empty<br /> <img alt="Enable5" data-entity-type="file" data-entity-uuid="95317f6a-7933-44f3-a180-db57a3eb4c88" src="/sites/default/files/inline-images/Enable5.png" /></em><br />  </li> <li>Click on <strong>Test </strong>to preview the summary output (the most recently edited record is used to generate the test response)<br /> <img alt="Enable6" data-entity-type="file" data-entity-uuid="57e8d46b-b361-4f40-b55e-b680a06ca89b" src="/sites/default/files/inline-images/Enable6.png" /><br />  </li> <li>Once satisfied, click on <strong>Apply to main forms</strong> to enable the summary across all main forms for your table<br />  </li> </ol> <p>Display the list of forms : an AI icon is visible for forms where the summary is active (only main forms)<br /> <img alt="Enable7" data-entity-type="file" data-entity-uuid="133c10d2-1fe7-4f3c-9d1a-84a2acbc2c20" src="/sites/default/files/inline-images/Enable7.png" /></p> <p>To modify an existing row summary:</p> <ol> <li>In <a href="https://make.powerapps.com/">Power Apps Portal</a> select your table where you want to modify the row summary</li> <li>Click on <strong>Forms </strong>in the section <strong>Data experience</strong><br /> Then click on <strong>Row summary</strong> on the command bar and from the dropdown select <strong>Edit Summary <br /> <img alt="Modify1" data-entity-type="file" data-entity-uuid="1aaa7ca0-540a-4689-a304-c93c8a7eed58" src="/sites/default/files/inline-images/Modify1.png" /></strong><br /> <br /> An alternative to the previous method (point 2) is to click on <strong>Row summary (applied) </strong>in section <strong>Customizations<br /> <img alt="Modify2" data-entity-type="file" data-entity-uuid="5c91604b-7fb9-4bbf-8863-bee3a28ea558" src="/sites/default/files/inline-images/Modify2.png" /></strong><br />  </li> <li>Make your changes to the prompt or included columns and reapply</li> </ol> <p>To remove a summary from all forms:</p> <ol> <li>In <a href="https://make.powerapps.com/">Power Apps Portal</a> select your table where you want to remove the row summary</li> <li>Click on <strong>Forms </strong>in the section <strong>Data experience</strong>.<br /> Then click on <strong>Row summary</strong> on the command bar and from the dropdown select <strong>Hide on all main forms<br /> <img alt="Hide1" data-entity-type="file" data-entity-uuid="c077490f-7872-44dd-9507-082ea7e198de" src="/sites/default/files/inline-images/Hide1.png" /></strong></li> </ol> <h3><br /> View Row Summary in Dynamics 365</h3> <p>Open your app in Dynamics 365 and navigate to the entity (e.g., Contact) where the row summary is configured.<br /> Select a record in the view : an AI summary icon is displayed.<img alt="Use1" data-entity-type="file" data-entity-uuid="1ccfd515-4736-456a-a423-127321a7c6bb" src="/sites/default/files/inline-images/Use1_1.png" /><br /> <br /> Click on this icon : AI-generated summary is displayed in a modal popup.<img alt="Use2" data-entity-type="file" data-entity-uuid="f97e3e25-2fda-42f9-99a3-88f8916b2e2d" src="/sites/default/files/inline-images/Use2_0.png" /></p> <p>Note that you can copy the summary into the clipboard.</p> <p>If you open a record (e.g., Contact) the summary is displayed at the top of the form in a collapsible bar (called Insights bar).<br /> <img alt="Use3" data-entity-type="file" data-entity-uuid="0b67c080-7a67-4464-afb1-c6086f909571" src="/sites/default/files/inline-images/Use3_0.png" /></p> </div> </div> <div class="field field--name-field-image field--type-image field--label-above"> <div class="field__label">Image</div> <div class="images-container clearfix"> <div class="image-preview clearfix"> <div class="image-wrapper clearfix"> <div class="field field--name-field-image field--type-image field--label-above field__item">/sites/default/files/2025-10/ChatGPT%20Image%20Oct%2020%2C%202025%2C%2006_24_24%20PM.png</div> </div> </div> </div> </div> Tue, 14 Oct 2025 08:04:03 +0000 Stephane Pelhatre 861 at https://dynamics-chronicles.com Copilot Chat in Model-Driven Apps https://dynamics-chronicles.com/article/copilot-chat-model-driven-apps <span property="schema:name" class="field field--name-title field--type-string field--label-hidden">Copilot Chat in Model-Driven Apps</span> <span rel="schema:author" class="field field--name-uid field--type-entity-reference field--label-hidden"><a title="View user profile." href="/user/stephane-pelhatre" lang="" about="/user/stephane-pelhatre" typeof="schema:Person" property="schema:name" datatype="" class="username">Stephane Pelhatre</a></span> <span property="schema:dateCreated" content="2025-10-06T15:11:43+00:00" class="field field--name-created field--type-created field--label-hidden">Mon, 10/06/2025 - 17:11</span> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-above"> <div class="field__label">Body</div> <div property="schema:text" class="field__item"><h3>Introduction</h3> <p>Managing data in a model-driven app requires sometimes many clicks, manual navigation through views and dashboards.<br /> With Copilot in Dynamics 365 model-driven apps users can access information instantly through AI-powered chat.</p> <p>Copilot Chat is an AI assistant that helps the user to obtain information in natural language from the application data and allows an intuitive navigation. <span lang="en" xml:lang="en" xml:lang="en"><span><span>It also improves user productivity by easily accessing necessary information or menus within the application through direct access</span></span></span>.</p> <p>At the time of writing this article, this feature is still in preview.<br /> Preview features aren’t meant for production use and might have restricted functionality.</p> <h3>How to enable Copilot in your environment</h3> <ol> <li>Go to <a href="https://admin.powerplatform.microsoft.com">Power Platform Admin Center</a></li> <li>Select your environment</li> <li>Go to <strong>Settings &gt; Product &gt; Features<br /> <img alt="feature1" data-entity-type="file" data-entity-uuid="a54071ed-fb15-421e-a30a-0557abae0ebe" src="/sites/default/files/inline-images/enable%20feature1_1.png" /></strong><br />  </li> <li>Enable the feature <strong>Allow users to analyze data using an AI-powered chat experience</strong>.<br /> 3 values are available: </li> </ol> <ul> <li><strong>By Default</strong>: Copilot is turned on for Dynamics 365 licensed environments but turned off for Power Apps licensed environments</li> <li><strong>On</strong>: Copilot remains enabled, independent of the license type</li> <li><strong>Off</strong>: Copilot remains disabled, independent of the license type<br /> <br /> <img alt="Enable feature" data-entity-type="file" data-entity-uuid="6b99666e-dcf4-46fd-b068-4a30cdc4d410" src="/sites/default/files/inline-images/enable%20feature_0.png" /><br />  </li> </ul> <h3>How to disable Copilot Chat for a specific model-driven app</h3> <p>Even though Copilot is a great tool to increase productivity, sometimes you might want to disable it for a specific app.<br /> To turn off Copilot Chat for a particular model-driven app:</p> <ol> <li>Open the model-driven app in App Designer</li> <li>Choose <strong>Settings </strong>from the command bar</li> <li>Navigate to <strong>Upcoming</strong></li> <li>Select <strong>Off </strong>or <strong>Default </strong>for the Copilot control.<br /> <img alt="Disable app" data-entity-type="file" data-entity-uuid="a793d958-2f08-4bfd-a000-fe46126094e2" src="/sites/default/files/inline-images/disable%20app_1.png" /><br /> <br /> By choosing <strong>Reset to environment value</strong> in the same settings tab, you return Copilot control to the environment value.</li> </ol> <h3>How to use Copilot Chat</h3> <p>Once enabled, users can access Copilot directly via the Copilot icon in the right navigation pane.<br /> <img alt="Use1" data-entity-type="file" data-entity-uuid="f4cecd6a-1243-41ff-8ae7-d78133826d11" src="/sites/default/files/inline-images/Use1.png" /></p> <p>You can start asking questions related to the tables present in the App.<br /> For instance, I have asked a simple question to get the contacts created last week.<br /> You can see the result below:</p> <p><img alt="Use2" data-entity-type="file" data-entity-uuid="d456e7b6-a483-4e00-971e-33eb13700088" src="/sites/default/files/inline-images/Use2.png" /></p> <p>If you click on <strong>Show all on a page</strong> results are displayed on a specific page.<br /> <img alt="Use3" data-entity-type="file" data-entity-uuid="8582e5a8-2bab-42f5-8ebf-b044e6e5c9cd" src="/sites/default/files/inline-images/Use3.png" /></p> <p>Likewise, you can ask different types of questions related to the data present in your App and explore on Copilot.<br /> To navigate to a specific table page you can type "Navigate to..."<br /> In the example below I entered "Navigate to Leads". The default view is displayed on the Leads page.<img alt="Use4" data-entity-type="file" data-entity-uuid="192fb457-37f6-459c-bb17-19b54ff6e1ec" src="/sites/default/files/inline-images/Use4.png" /></p> <p> </p></div> </div> Mon, 06 Oct 2025 15:11:43 +0000 Stephane Pelhatre 858 at https://dynamics-chronicles.com Dataverse Functions Deep Dive https://dynamics-chronicles.com/article/dataverse-functions-deep-dive <span property="schema:name" class="field field--name-title field--type-string field--label-hidden">Dataverse Functions Deep Dive</span> <span rel="schema:author" class="field field--name-uid field--type-entity-reference field--label-hidden"><a title="View user profile." href="/user/stephane-pelhatre" lang="" about="/user/stephane-pelhatre" typeof="schema:Person" property="schema:name" datatype="" class="username">Stephane Pelhatre</a></span> <span property="schema:dateCreated" content="2025-08-28T12:38:15+00:00" class="field field--name-created field--type-created field--label-hidden">Thu, 08/28/2025 - 14:38</span> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-above"> <div class="field__label">Body</div> <div property="schema:text" class="field__item"><h1>Introduction</h1> <p>Microsoft released in 2024 Dataverse low-code plugins. The promise was great : writing plugins using low-code tools. No need to have development skills and use a development environment as Microsoft Visual Studio, write C# code and register plugin in your environment.</p> <p>There are two types of low-code plugins supported in Dataverse : instant and automated. The difference is the trigger. For instant low-code plugin the trigger is manual, the plugin must be explicitly called (for example from a Power Automate flow). Automated low-code plugins work like classic plugins : the trigger is a Dataverse table event. For example you can create an automated low-code plugin on Create event for Account table. Low-code plugin feature is still in preview.</p> <p>Then Microsoft has introduced Power Fx functions (eventually renamed Dataverse functions). Functions are a low-code/no-code way to write reusable logic directly within Dataverse. Functions use Power Fx to execute a set of commands within Dataverse that run on the server-side. The trigger is also manual, so a function must be called explicitly .<br /> The situation is therefore somewhat confusing as instant low-code plugins resemble Dataverse functions and both features are available in preview.<br /> Technically functions and low-code plugins are 2 different tools, with their own Microsoft Learn Docs having their own sections.</p> <p><br /> <img alt="learn1" data-entity-type="file" data-entity-uuid="44b88b89-6a30-4095-989b-760bbb9c8a46" src="/sites/default/files/inline-images/learn1.png" /><br /> <br /> Officially instant low-code plugins are deprioritized and are replaced with Dataverse functions. Automated low-code plugins are still available. You can find more information here : <a href="https://learn.microsoft.com/en-us/power-apps/maker/data-platform/low-code-plug-ins?tabs=instant">Use low-code plug-ins in Dataverse</a></p> <p><img alt="disclaimer" data-entity-type="file" data-entity-uuid="605f4e9b-b739-481f-9a8e-e47499ed96a8" src="/sites/default/files/inline-images/disclaimer.png" /></p> <p>In this article we will first briefly examine low-code plugins. We won't go into detail because, as we will see, they don't really work.<br /> Then we will examine Dataverse functions in detail.</p> <h1>Low-code plugins</h1> <p>Let's briefly talk about low-code plugins.<br /> To access low -code plugins interface you need to install a model-driven app called <strong>Dataverse Accelerator</strong>:</p> <ol> <li>Go to <a href="https://admin.powerplatform.microsoft.com">Power Platform Admin Center</a></li> <li>Click on <strong>Environments </strong>on the left pane.<br /> Then click on the ellipsis (...)  next to your environment, select <strong>Resources </strong>and then <strong>Dynamics 365 apps</strong><br /> <img alt="acc0" data-entity-type="file" data-entity-uuid="030c4df0-e33f-49c0-82b5-f23e20a023bb" src="/sites/default/files/inline-images/accelerator0.png" /><br />  </li> <li>Click on <strong>Install app</strong> on the command bar<br /> <img alt="acc1" data-entity-type="file" data-entity-uuid="fcff52a6-a59b-4706-84c7-68149c785334" src="/sites/default/files/inline-images/accelerator1.png" /><br />  </li> <li>Select <strong>Dataverse Accelerator </strong>in the list and click on <strong>Next</strong><br /> <img alt="acc2" data-entity-type="file" data-entity-uuid="c497a9b0-3e23-454e-abf4-bc433ea3ac87" src="/sites/default/files/inline-images/accelerator2.png" /><br />  </li> <li>Check<strong> I agree to the terms of service</strong> and click on <strong>Install</strong><br /> <img alt="acc3" data-entity-type="file" data-entity-uuid="4f8e8ec3-cbd7-49c0-a2b5-193d159f68ca" src="/sites/default/files/inline-images/accelerator3.png" /><br />  </li> <li>Once the installation is complete Dataverse Accelerator app is available in the list of apps<br /> <img alt="acc4" data-entity-type="file" data-entity-uuid="71c96b13-e889-4785-a26b-92c27d68e791" src="/sites/default/files/inline-images/accelerator4.png" /></li> </ol> <p>Now you can play the app and you will see the interface below:<br /> <img alt="acc5" data-entity-type="file" data-entity-uuid="6ba71fb7-e4c1-4b55-9a06-36a5ea3ae43d" src="/sites/default/files/inline-images/accelerator5.png" /></p> <p>As explained in the introduction there are two types of plugins : instant plugins that must be called explicitly and automated plugins that are triggered by an event on a table.<br /> There is a button to create an instant plugin and another to create an automated plugin. Note that it 's a bit strange to have the option to create an instant plugin as this feature is deprioritized and replaced by Dataverse functions.</p> <p>Then if you click on either of the two buttons ... nothing happens and the screen remains blank. I tried with several Dataverse environments located in different regions (US, Western Europe) with the same result : a blank screen.</p> <p>This problem is understandable for instant plugins as they are replaced by functions. But regarding automated plugin it's a problem : it's impossible to create an automated plugin.<br /> It was working a few months ago (but buggy as hell), but as of the date of writing this article (October 2025),  it no longer works.<br /> Maybe it will work again in the future or maybe Microsoft will remove this feature.</p> <h1>Dataverse Functions</h1> <h2>Introduction</h2> <p><span class="font-[700]">Functions</span><span> are a </span><span class="font-[700]">low-code/no-code way</span><span> to write </span><span class="font-[700]">reusable logic directly within Dataverse</span><span> (running server-side) without having to write C# code or deploying plugins.</span><br /> Functions can have parameters: parameters allow you to pass information between the function and the context that runs it, making it easier to design business logic that can be reused in varying situations. Input parameters are used to provide data to the function, and allow you to control how the function behaves by passing in different values you specify in the Power Fx formula. Output parameters allow you to retrieve the results of a function for further use in your program.</p> <h2>How to create a function</h2> <ol> <li>Open <a href="https://make.powerapps.com/">Power Apps Portal</a> and select your environment</li> <li>Open the solution <span> where you want to build a function</span></li> <li>Click on <strong>New </strong>--&gt; <strong>Automation </strong>--&gt; <strong>Function<br /> <br /> <img alt="funct1" data-entity-type="file" data-entity-uuid="e0c1dbf6-6b79-4ab9-beb1-c17f68779994" src="/sites/default/files/inline-images/function1.png" /></strong><br /> Provide the following details:<br /> <strong>- Display name</strong> : The human-readable name of the function. Can't be changed once created.<br /> <strong>- Description </strong>:<strong> </strong>provide additional context about the function, such as purpose, behavior, and so on. Can't be changed once created.<br /> - <strong>Input parameters(s)</strong> : add required input parameters. Input paramerts are optional.<br />   To add a parameter click on <strong>+ New input parameter<br />   </strong>Following types are supported: Boolean, Datetime, Decimal, Float, Integer, String<br /> <strong>- Output result(s) </strong>: add output results parameters. Output results are optional. To add an output result click on <strong>+ New output result<br /> - Table references </strong>:<strong> </strong>used to associate functions to specific tables. You can select up to five tables to read or write from in your function’s formula.<br /> - <strong>Formula</strong>: custom function that can be used to perform actions or calculations, defined using the Power Fx expression language<br /> <br /> Let's create a simple function to sum two numbers.<br /> I define my function <em>CustomSum </em>with two inputs parameters <em>x</em> and <em>y</em> and one output parameter <em>z</em>.The Power Fx formula to sum the 2 numbers is : <em>{z:x+y}<br /> <img alt="funct3" data-entity-type="file" data-entity-uuid="136c5ff0-ee42-4e87-8bb8-3193944a26d9" src="/sites/default/files/inline-images/function3.png" /></em><br /> <br /> Another example: let's implement a function that creates a contact from two input parameters <em>firstname </em>and <em>lastname</em> (both with string data type)<br /> This time we need to access a table to create a record, so I select 'Contacts' table in <strong>Table references</strong><br /> I also define an output result <em>message</em> to indicate that the contact was created successfully<br /> In the Power Fx formula I use the function<em> Collect() </em>to create the record <pre> <code>Collect(contact, { firstname: firstname, lastname: lastname } ); { message: "New contact " &amp; firstname &amp; "," &amp; lastname &amp; " created" }</code></pre> <br /> <img alt="funct5" data-entity-type="file" data-entity-uuid="80eca25a-afe7-4c8a-bd5f-f6881222e098" src="/sites/default/files/inline-images/function5.png" /><br />  </li> <li>Click on <strong>Save </strong>to create the function<br /> Various components are created and added to the solution:<br /> - a Custom API<br /> - a Function<br /> - a FxExpression<br /> - a custom API Request Parameter for each input parameter (2 in my example)<br /> - a Custom API Response Property for each ouput result  (1 in my example)<br /> <img alt="funct4" data-entity-type="file" data-entity-uuid="aa87857a-3cff-4987-a669-42b989c4a082" src="/sites/default/files/inline-images/function4.png" /></li> </ol> <h2>How to test a function</h2> <p>If you expect to find a button in the command bar when you select your function wihtin the solution, forget it. It is not possible to test a function inside a solution.<br /> To test a function:</p> <ol> <li>Come out of the solution</li> <li>Click on <strong>More </strong>on the left pane and then click on <strong>Discover all</strong><br /> <img alt="test1" data-entity-type="file" data-entity-uuid="dec92a15-b73b-4b91-bc6c-78e72e9883b3" src="/sites/default/files/inline-images/test1.png" /><br />  </li> <li>Click on <strong>Functions<br /> <img alt="test2" data-entity-type="file" data-entity-uuid="bc569094-54e2-4dde-91f8-b32c6c2e111d" src="/sites/default/files/inline-images/test2.png" /></strong><br />  </li> <li>Select your function and click on <strong>Test </strong>on the command bar<br /> <img alt="test3" data-entity-type="file" data-entity-uuid="b8f89e90-38a6-4ca1-91bb-af75b448d081" src="/sites/default/files/inline-images/test3.png" /><br /> <br /> Another way is to click on the ellipsis (...) and select <strong>Test </strong>in the menu<br /> <img alt="test4" data-entity-type="file" data-entity-uuid="5b04f55d-c6bf-4d27-aeb3-3deb9fe61873" src="/sites/default/files/inline-images/test4.png" /><br />  </li> <li>Enter a value for each input parameter and click on <strong>Play</strong><br /> Result is displayed in the text area at the bottom.<br /> In my example with function <em>CustomSum </em>I set x = 10 and y = 5 and we can see that value assigned to output result is 15<br /> Result is : <em><span style="color: rgb(50, 49, 48); font-family: &quot;Segoe UI&quot;, &quot;Segoe UI Web (West European)&quot;, -apple-system, BlinkMacSystemFont, Roboto, &quot;Helvetica Neue&quot;, sans-serif; font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none;">{"@odata.context":"https://orgxxxxx.crm.dynamics.com/api/data/v9.0/$metadata#Microsoft.Dynamics.CRM.cr0de_CustomSumResponse","z":15}</span></em><br /> <img alt="test5" data-entity-type="file" data-entity-uuid="3f7b97be-ada1-4da5-8a83-3e77fbdecb78" src="/sites/default/files/inline-images/0_test5.png" /><br /> <br /> For the second function <em>Create Contact</em> I set firstname = 'John' and lastname = 'Doe'. You can see the result below:<br /> <img alt="test6" data-entity-type="file" data-entity-uuid="1a7ed145-cd11-4aaf-ae7b-66f13f7784ce" src="/sites/default/files/inline-images/0_test6.png" /></li> </ol> <h2>How to delete a function</h2> <p>You must be careful if you want to delete a function. If you delete the function component in your solution only the function itself will be deleted, but related components (Custom API, FxExpression, custom API Request Parameters, Custom API Response Properties) will not be deleted. Therefore, you will end up with orphaned components.<br /> To delete the function and all related components you must follow the same steps as for testing a function (see above): come out of the solution, click on <strong>More </strong>then <strong>Discover all </strong>and select <strong>Functions</strong>.<br /> Then you can click on <strong>Delete </strong>on the command bar or click on the ellipsis (...) and select <strong>Delete</strong>.<br /> <img alt="delete" data-entity-type="file" data-entity-uuid="a866fca3-3a7b-4f01-b973-d675d5a6f857" src="/sites/default/files/inline-images/delete_1.png" /></p> <h2>Trace logs</h2> <p>You can access Dataverse functions trace logs in the same place as classic plugins.<br /> Open <strong>Power Platform Environment Settings</strong> app and click on <strong>Plug-in Trace Log</strong> on the left pane.<br /> <img alt="logs1" data-entity-type="file" data-entity-uuid="8bbdb569-991e-4d5a-9e5c-2ccb1c5baad7" src="/sites/default/files/inline-images/logs1.png" /><br />  </p> <p>You have access to execution information (duration, message block, exception, ...)</p> <p><img alt="logs2" data-entity-type="file" data-entity-uuid="f54b19a3-7cee-4282-aa46-d90e1f014021" src="/sites/default/files/inline-images/logs2.png" /></p> <h2>Invoke a function</h2> <p>We now know how to create and test a function, but it's time to use it.<br /> As explained previously, functions are only triggered manually, so you need to call them explicitly.<br /> You can invoke functions in Dataverse from:</p> <ul> <li>a canvas app</li> <li>a custom page in a model driven app</li> <li>a Power Automate flow</li> <li>from another function</li> <li>in your code (Web API)</li> </ul> <p>You can obtain a code snippet of your function that you will then use to call that function:</p> <ol> <li>Open <a href="https://make.powerapps.com/">Power Apps Portal</a> and select your environment</li> <li>Click on <strong>More </strong>on the left pane and then click on <strong>Discover all</strong></li> <li>Click on <strong>Functions</strong></li> <li>Select you function and click on <strong>Copy code snippet</strong> on the command bar<br /> <img alt="snip1" data-entity-type="file" data-entity-uuid="439b7a85-4f1b-408b-ab80-fdb76a8ce0b2" src="/sites/default/files/inline-images/snippet1.png" /><br /> <br /> Another way is to click on the ellipsis (...) and select <strong>Copy code snippet </strong>in the menu<br /> <img alt="snip2" data-entity-type="file" data-entity-uuid="b89ddb6b-6339-4b0a-a2e2-789f5839cf52" src="/sites/default/files/inline-images/snippet2.png" /></li> </ol> <p>Below the code snippet for my functions <em>CustomSum </em>and <em>Create contact</em></p> <pre> <code>Environment.cr0de_CustomSum({x:Value,y:Value}) Environment.cr0de_Createcontact({firstname:Value,lastname:Value})</code></pre> <h3>Invoke a function from the Dataverse Web API</h3> <p>You can invoke a Dataverse function from the Web API with a POST request<br /> Example with the function <em>CustomSum</em>:</p> <pre> <code class="language-http">POST [Organization URI]/api/data/v9.2/cr0de_CustomSum OData-MaxVersion: 4.0 OData-Version: 4.0 Content-Type: application/json; charset=utf-8 { "x":4, "y":5 }</code></pre> <p>Here the result I get with the plugin WebAPI Launcher in XrmtoolBox:<br /> <img alt="invoke10" data-entity-type="file" data-entity-uuid="44eb24cb-10c3-456e-ae83-0e83343dd8dd" src="/sites/default/files/inline-images/0_invoke1.png" /></p> <h3>Invoke a function from within another function</h3> <p>You can invoke a function from any other function.<br /> To invoke a function within another function, use the syntax: <code>Environment.MyFunction({inputParam1: value1, inputParam2: value2, ... inputParamN: valueN})</code>.<br /> Since the output is always a record, use the dot notation to access the output parameters.<br /> For example, if the function <em>MyFunction </em>has two output parameters defined as out1 and out2, you can access them in either of these two ways:</p> <ul> <li><code>Environment.ExistingFunction({inputParam1: value1, inputParam2: value2, ... inputParamN: valueN}).out1</code></li> <li><code>Environment.ExistingFunction({inputParam1: value1, inputParam2: value2, ... inputParamN: valueN}).out2</code></li> </ul> <p>To demonstrate this, I create a new function <em>CustomSum2 </em>to add 2 and 3. To perform this operation I use the first function <em>CustomSum.</em><br /> The new function <em>CustomSum2</em> has no input parameter and one output result <em>result </em>(Integer type).<br /> Power Fx formula of the new function:<br /> {<br /> <em>    result:Environment.cr0de_CustomSum({x:2,y:3}).z<br /> }</em></p> <p><img alt="invoke2" data-entity-type="file" data-entity-uuid="eaac6da7-5374-4492-a892-214c80c44ab3" src="/sites/default/files/inline-images/invoke2.png" /></p> <h3>Invoke a function from a Power Automate cloud flow</h3> <p>To use a function in a cloud flow:</p> <ol> <li>Add a step to your flow</li> <li>select <strong>Microsoft Dataverse</strong> connector</li> <li>select <strong>Perform an unbound action</strong> action</li> <li>In <strong>Action Name</strong>, select the function (<em>cr0de_CustomSum</em> in my example) and provide values for the input parameters (x = 6 and y = 3 in my example)<br /> <img alt="Invoke3" data-entity-type="file" data-entity-uuid="4d9ea59f-c1d5-4363-b9a0-7ec590538a01" src="/sites/default/files/inline-images/invoke3.png" /><br />  </li> <li>Add a <strong>compose </strong>action using the output of the unbound action as an input so that you can access the results of the function as needed</li> </ol> <p>Below the result:<br /> <img alt="invoke4" data-entity-type="file" data-entity-uuid="907196bc-9660-40ae-aac2-7ccc1a6eb670" src="/sites/default/files/inline-images/0_invoke4.png" /></p> <h2>Limitations with functions</h2> <p>Functions have some limitations:</p> <ul> <li>Max 5 tables per function</li> <li>Only manual trigger, cannot be automated (that's the goal of low-code plugins)</li> <li>Cannot call external API, limited to Dataverse</li> <li>Still in preview : <span>may have bugs. Not suitable for production</span></li> <li>The following Power Fx function aren't currently supported with functions in Dataverse<br /> <br /> <img alt="limit1" data-entity-type="file" data-entity-uuid="6b6110fe-9eb0-4820-8e80-340d6b89de84" src="/sites/default/files/inline-images/limit1_0.png" /></li> </ul> <p> </p> </div> </div> <div class="field field--name-field-image field--type-image field--label-above"> <div class="field__label">Image</div> <div class="images-container clearfix"> <div class="image-preview clearfix"> <div class="image-wrapper clearfix"> <div class="field field--name-field-image field--type-image field--label-above field__item">/sites/default/files/2026-01/dataver_functions2.png</div> </div> </div> </div> </div> Thu, 28 Aug 2025 12:38:15 +0000 Stephane Pelhatre 849 at https://dynamics-chronicles.com Dataverse Managed Environments Deep Dive https://dynamics-chronicles.com/article/dataverse-managed-environments-deep-dive <span property="schema:name" class="field field--name-title field--type-string field--label-hidden">Dataverse Managed Environments Deep Dive</span> <span rel="schema:author" class="field field--name-uid field--type-entity-reference field--label-hidden"><a title="View user profile." href="/user/julienbiedermann" lang="" about="/user/julienbiedermann" typeof="schema:Person" property="schema:name" datatype="" class="username">julien.biedermann</a></span> <span property="schema:dateCreated" content="2024-10-08T12:45:19+00:00" class="field field--name-created field--type-created field--label-hidden">Tue, 10/08/2024 - 14:45</span> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-above"> <div class="field__label">Body</div> <div property="schema:text" class="field__item"><p><em>Dataverse Managed Environments Deep Dive:</em></p> <h2>What is Managed Environments ?</h2> <p>Managed environments is a set of features that allows administrators to manage Power Platform at scale with more control, less effort, and more visibility.</p> <p>List of features available with Managed Environments for now (18/09/24) :</p> <ul> <li>Limit sharing</li> <li>Weekly usage insights</li> <li>Data policies</li> <li>Pipelines in Power Platform (preview)</li> <li>Maker welcome content (preview)</li> <li>Solution checker</li> <li>IP Firewall</li> <li>IP cookie binding</li> <li>Customer Managed Key (CMK)</li> <li>Lockbox</li> <li>Extended backup</li> <li>DLP for desktop flow</li> <li>Export data to Azure Application Insights</li> <li>Catalog in Power Platform</li> <li>Default environment routing</li> <li>Create an app description with Copilot</li> <li>Virtual Network support for Power Platform</li> </ul> <h2>Requirements to enable Managed Environments</h2> <p>You may ask why to not activate this option on every environments ? What are the requirements and implications of enabling Managed Environments.</p> <p>Everything is about <a href="https://learn.microsoft.com/en-us/power-platform/admin/managed-environment-licensing">licensing</a>, when activating Managed Environments, every user that want to access the resources (Power Apps, Power Automate, Power Virtual Agents, and Power Pages website) in this environment must have a standalone licenses, Dynamics 365 Enterprise license or capacity add-ons.</p> <p>Example of which licenses cannot run app in Managed Environments:</p> <ul> <li>Microsoft/Office 365 licenses (Business, E1, E3, E5, F1, F3, …) that are coming with access to some limited Power Apps, Power Automate capabilities.</li> </ul> <h2>How to enable/disable Managed Environments ?</h2> <h3>Permissions required</h3> <ul> <li>You must have the role of <strong>global admin</strong>, <strong>Power Platform service admin</strong>, or <strong>Dynamics 365 admin Microsoft Entra ID</strong>.</li> <li>Any user with permission to see environment details can consult the Managed Environments property for an environment (from the list).</li> </ul> <p>Users with the <u>Delegated Admin role</u> or the <u>Environment Admin security</u> role <u>aren't allowed to change the Managed Environments property</u> in an environment.</p> <h3 id="enable-or-edit-managed-environments-in-the-admin-center">Enable Managed Environments in the admin center</h3> <p>Open the <a href="https://admin.powerplatform.microsoft.com">Power Platform admin center</a>.</p> <p><img alt="Dataverse Managed Environments Deep Dive" data-entity-type="file" data-entity-uuid="42a65883-de3b-41ab-928c-6acef48071b9" src="/sites/default/files/inline-images/2024-03-20%2011_20_50-Ideas%20-%20OneNote.png" /></p> <p><img alt="Dataverse Managed Environments Deep Dive" data-entity-type="file" data-entity-uuid="9d9aab6e-a3eb-4138-94c2-9af66ef9405a" src="/sites/default/files/inline-images/2024-03-20%2011_24_31-Presentation1%20-%20PowerPoint.png" /></p> <h3>Enable Managed Environments using PowerShell</h3> <p>Below the script to enable Managed Environments (don't forget to replace &lt;EnvironmentID&gt;, it can be found here <a href="https://admin.powerplatform.microsoft.com">https://admin.powerplatform.microsoft.com</a>) :</p> <pre> <code>$GovernanceConfiguration = [pscustomobject] @{ protectionLevel = "Standard" settings = [pscustomobject]@{ extendedSettings = @{} } } Set-AdminPowerAppEnvironmentGovernanceConfiguration -EnvironmentName &lt;EnvironmentID&gt; -UpdatedGovernanceConfiguration $GovernanceConfiguration</code></pre> <h3>Copy Managed Environment settings using PowerShell</h3> <p>These commands can be useful for deploying Managed Environments settings from one environment to another one. If the target environment wasn't a Managed Environment, copy settings will also enable it as a Managed Environment.</p> <pre> <code>#Get settings from the source Managed Environment $sourceEnvironment = Get-AdminPowerAppEnvironment -EnvironmentName &lt;SourceEnvironmentId&gt; # Copy the settings from the source Managed Environment above to the target environment Set-AdminPowerAppEnvironmentGovernanceConfiguration -EnvironmentName &lt;TargetEnvironmentId&gt; -UpdatedGovernanceConfiguration $sourceEnvironment.Internal.properties.governanceConfiguration</code></pre> <h3 id="disable-managed-environments-using-powershell">Disable Managed Environments using PowerShell</h3> <p>Managed Environments can be disabled by administrators using PowerShell. Before disabling Managed Environments, the administrator must ensure that none of the Managed Environments capabilities are in use.</p> <p>Below the PowerShell script to disable Managed Environments :</p> <pre> <code>$UpdatedGovernanceConfiguration = [pscustomobject]@{ protectionLevel = "Basic" } Set-AdminPowerAppEnvironmentGovernanceConfiguration -EnvironmentName &lt;EnvironmentID&gt; -UpdatedGovernanceConfiguration $UpdatedGovernanceConfiguration</code></pre> <h2>Quick overview of the available features</h2> <h3>Limit sharing</h3> <p>Canvas app can be shared with users. With this feature, admins can limit how broadly users can share canvas apps. It is possible to restrict sharing with security groups and also set a limit on the number of individuals with whom the app can be shared.</p> <p><img alt="Limit sharing" data-entity-type="file" data-entity-uuid="b9791d77-0dad-4c9c-bbcf-16ca3f8ca6b8" src="/sites/default/files/inline-images/managed-environment-limit-sharing.png" /></p> <p>Here is what the user will see when trying to share an app that is not matching the limit sharing rule:</p> <p><img alt="Limit sharing rule" data-entity-type="file" data-entity-uuid="778b4a9c-df95-4f99-a7b3-24575b055462" src="/sites/default/files/inline-images/managed-environment-canvas-app-sharing-rule.png" /></p> <p> </p> <h3>Usage Insights</h3> <p>Keep updated on the status of your managed environments with Power Platform's weekly admin digest. Receive analytics detailing your top apps, most influential makers, and inactive resources that can be safely cleaned up, all conveniently delivered to your mailbox every week.</p> <p>Here is the setting to enable <a href="https://learn.microsoft.com/en-us/power-platform/admin/managed-environment-usage-insights">Usage Insights</a> (please note that also need to <a href="https://learn.microsoft.com/en-us/power-platform/admin/tenant-level-analytics#how-do-i-enable-tenant-level-analytics">enable tenant-level analytics</a> to get usage insights):</p> <p><img alt="Dataverse Managed Environments Deep Dive" data-entity-type="file" data-entity-uuid="256209f5-7ba7-4024-9f04-43ba2a28b569" src="/sites/default/files/inline-images/managed-environment-weekly-digest-include-environmenta.png" /></p> <h4>Who will receive the weekly digest ?</h4> <p>The weekly digest is sent to all users with the roles of <a href="https://learn.microsoft.com/en-us/power-platform/admin/use-service-admin-role-manage-tenant#power-platform-administrator">Power Platform administrator</a> and <a href="https://learn.microsoft.com/en-us/power-platform/admin/use-service-admin-role-manage-tenant#dynamics-365-administrator">Dynamics 365 service administrator</a>.</p> <p> </p> <p>To include more recipients, choose "Add additional recipients for the weekly email digest", then opt for "Weekly digest" and input email addresses into the "Additional recipients" box.</p> <h4>What information is presented in the weekly digest ?</h4> <p>The first part of the weekly digest shows the number of apps used and active users in your managed environments in the past month.</p> <p><img alt="Dataverse Managed Environments Deep Dive" data-entity-type="file" data-entity-uuid="331d9713-9ea9-4587-927c-b608eb05519c" src="/sites/default/files/inline-images/managed-environment-weekly-digest-first-section.png" /></p> <p>The second part of the weekly digest shows lists apps and flows that haven't been launched in a while. The last launch column shows the last date of use for the app or flow. If an app or flow isn't being used, we recommend that you work with its owner to update or remove it.</p> <p><img alt="Weekly digest 2" data-entity-type="file" data-entity-uuid="764a55f8-67fb-4ea0-aabd-f91f7e80bec1" src="/sites/default/files/inline-images/managed-environment-weekly-digest-second-section.png" /></p> <p>The third part of the weekly digest shows the most popular apps and flows in your managed environments in the past month, indicated by the number of sessions and runs. When a user launches and interacts with an application, that's considered a session.</p> <p><img alt="Dataverse Managed Environments Deep Dive" data-entity-type="file" data-entity-uuid="dc03c8a3-52b1-4660-b73c-2805c3400a52" src="/sites/default/files/inline-images/managed-environment-weekly-digest-third-section.png" /></p> <h3>Data Policies</h3> <p>With this feature, admins can easily identify all the data policies that are applied to an environment.</p> <p><img alt="Dataverse Managed Environments Deep Dive" data-entity-type="file" data-entity-uuid="982e94ba-bc71-490f-b556-47302bd37d67" src="/sites/default/files/inline-images/managed-environment-weekly-see-active-data-policies.png" /></p> <p>When clicking on "See active data policies for this environment", the data policies page opens in a new tab. The view is filtered to display only the data policies that are applied to the managed environment. The environment filter is exclusively available for managed environments.</p> <h3>Pipelines in Power Platform</h3> <p><a href="https://learn.microsoft.com/en-us/power-platform/alm/pipelines">Pipelines in Power Platform</a> aim to democratize application lifecycle management (ALM) for Power Platform and Dynamics 365 customers by bringing ALM automation and continuous integration and continuous delivery (CI/CD) capabilities into the service in a manner that's more approachable for all makers, admins, and developers. <u>All target environments used in a pipeline must be enabled as Managed Environments</u>.</p> <p><img alt="Pipelines in Power Platform" data-entity-type="file" data-entity-uuid="30f92476-c22b-4699-91e8-e36b744de8c9" src="/sites/default/files/inline-images/deployment-pipelines.png" /></p> <h3>Enable maker welcome content</h3> <p>Admins can provide customized <a href="https://learn.microsoft.com/en-us/power-platform/admin/welcome-content">welcome content</a> to help their makers get started with Power Apps. When you add your own help content, it replaces the default Power Apps first-time help experience for makers.</p> <p><img alt="Welcome maker" data-entity-type="file" data-entity-uuid="56512f08-08c0-4fd5-ae58-22652100d8da" src="/sites/default/files/inline-images/maker-welcome-2.png" /></p> <p>Here is an example:</p> <p><img alt="Dataverse Managed Environments Deep Dive" data-entity-type="file" data-entity-uuid="95173b23-51f9-4fe7-b1bf-ef466de46797" src="/sites/default/files/inline-images/welcome_0.png" /></p> <h3>Solution checker enforcement</h3> <p>With solution checker enforcement feature, you can perform a rich static analysis check on your solutions against a set of best practice rules and identify problematic patterns.</p> <p><img alt="Solution checker" data-entity-type="file" data-entity-uuid="8ff93285-1b14-4170-9bae-765522228726" src="/sites/default/files/inline-images/managed-environment-solution-checker_0.png" /></p> <table border="1" cellpadding="1" cellspacing="1" style="width: 500px;"> <thead> <tr> <th scope="col" style="width: 89px;">Setting</th> <th scope="col" style="width: 397px;">Description</th> </tr> </thead> <tbody> <tr> <td style="width: 89px;">None</td> <td style="width: 397px;">Turns off the automatic solution validations during solution import. There aren't any experience or behavioral changes to solution authoring, exports, or imports.</td> </tr> <tr> <td style="width: 89px;">Warn</td> <td style="width: 397px;">All custom solutions are automatically verified during solution import. When a solution with highly-critical issues is being imported, you are warned about the action but the import itself continues, and if everything else with the import is fine, the solution is imported into the environment. After a successful import, a message stating that the imported solution had validation issues is shown. Additionally, a summary email is sent with details of the solution validation.</td> </tr> <tr> <td style="width: 89px;">Block</td> <td style="width: 397px;">All custom solutions are automatically verified during solution import. When a solution has highly-critical issues, the import process is canceled, and a message stating that the imported solution had validation issues is shown. This happens before the actual import, so there aren't any changes to the environment due to the import failure. Additionally, a summary email is sent with details of the solution validation.</td> </tr> </tbody> </table> <h4>Who will receive the emails ?</h4> <p>When the validation mode is set to <strong>Warn</strong> or <strong>Block</strong>, a summary email is sent when a solution is imported or blocked. When the solution is imported into an environment, the summary email shows the count of issues by severity in the solution.</p> <p>The email is sent to all users with the roles of Power Platform administrator and Dynamics 365 service administrator. It is also sent to recipients of the weekly digest emails.</p> <p>By default, emails are sent for solutions containing medium or higher severities. When the checkbox "Send emails only when a solution is blocked [...]" is selected, emails won't be sent in warn mode. In block mode, emails aren't sent except for critical violations, which block solution import.</p> <h4>Rule exclusions</h4> <p>You have the option to opt out of enforcing certain solution checker rules. For instance, if fixing a specific rule requires considerable time and effort across the solution, but you still want other rules to be enforced, you can exclude that rule from enforcement. Utilize the "Excluded Rules" dropdown menu to choose which rules to exclude from enforcement. As a reminder, only critical severity rules block a solution from being imported.</p> <p><img alt="Dataverse Managed Environments Deep Dive" data-entity-type="file" data-entity-uuid="e39c4cea-9864-445f-a078-3489b93eb4ed" src="/sites/default/files/inline-images/managed-environment-solution-checker-rule-exclusions-list.png" /></p> <h3>IP firewall in Power Platform environments</h3> <p>The IP firewall helps to protect your organizational data by limiting user access to Microsoft Dataverse from only allowed IP locations. The IP firewall analyzes the IP address of each request in real time. For example, suppose the IP firewall is turned on in your production Dataverse environment, and allowed IP addresses are in the ranges associated with your office locations and not any external IP location like a coffee shop. If a user tries to access organizational resources from a coffee shop, Dataverse denies access in real time.</p> <p><img alt="IP Firewall diagram" data-entity-type="file" data-entity-uuid="21a43cf8-79dc-45da-954a-e201947b4c29" src="/sites/default/files/inline-images/ip-firewall-dataverse-diagram.png" /></p> <h3>Safeguarding Dataverse sessions with IP cookie binding</h3> <p>Prevent session hijacking exploits in Dataverse with IP address-based cookie binding. Let's say that a malicious user copies a valid session cookie from an authorized computer that has cookie IP binding enabled. The user then tries to use the cookie on a different computer to gain unauthorized access to Dataverse. In real time, Dataverse compares the IP address of the cookie's origin against the IP address of the computer making the request. If the two are different, the attempt is blocked, and an error message is shown.</p> <h3>Manage your customer-managed encryption key</h3> <p>Customers have data privacy and compliance requirements to secure their data by encrypting their data at-rest. This secures the data from exposure in an event where a copy of the database is stolen. With data encryption at-rest, the stolen database data is protected from being restored to a different server without the encryption key.</p> <p>All customer data stored in Power Platform is encrypted at-rest with strong Microsoft-managed encryption keys by default. Microsoft stores and manages the database encryption key for all your data so you don't have to. However, Power Platform provides this customer-managed encryption key (CMK) for your added data protection control where you can self-manage the database encryption key that is associated with your Microsoft Dataverse environment. This allows you to rotate or swap the encryption key on demand, and also allows you to prevent Microsoft's access to your customer data when you revoke the key access to our services at any time.</p> <h3>Securely access customer data using Customer Lockbox in Power Platform and Dynamics 365</h3> <p>Most operations, support, and troubleshooting performed by Microsoft personnel (including subprocessors) don't require access to customer data. With Power Platform Customer Lockbox, we provide an interface for the customers to review and approve (or reject) data access requests in the rare occasion when data access to customer data is needed. It's used in cases where a Microsoft engineer needs to access customer data, whether in response to a customer-initiated support ticket or a problem identified by Microsoft.</p> <p><em>Customer Lockbox is available in public clouds and US Government Community Cloud (GCC), GCC High, and Department of Defense (DoD) regions.</em></p> <h3>Back up and restore environments</h3> <p>For environments that don't have Dynamics 365 applications, the default backup retention period is seven days. Admins who run production Managed Environments of this type can use PowerShell to change the retention period to 7, 14, 21, or 28 days.</p> <p><em>A Managed Environment can be restored only to another Managed Environment. A non-Managed Environment can't be restored to a Managed Environment.</em></p> <h2>Is activating the Managed Environments a risky move?</h2> <p>Apart from the dependence on licenses, to me, the risk associated with activating the feature is minimal, here are the reasons:</p> <ul> <li>This is transparent for the users if no feature are activated</li> <li>This option is at the environment level (not at tenant level) <ul> <li>This gives more flexibility and it allows to test the features in dev environment</li> </ul> </li> <li>Activation of a feature does not have retroactive effect</li> <li>This option can be disabled at any time</li> </ul> <p>On the other hand, admins need to be careful before activating a feature because it can have an impact on users. For example, activating "limit sharing" can block user in their job. It is important to test feature before activating them in production.</p> <h4>When to activate Managed Environments?</h4> <ul> <li>A feature requires to have Managed Environments activated</li> <li>For large organizations with lots of environments, users, apps</li> </ul> <h2 class="title">Dataverse Managed Environments Deep Dive</h2> </div> </div> <div class="field field--name-field-image field--type-image field--label-above"> <div class="field__label">Image</div> <div class="images-container clearfix"> <div class="image-preview clearfix"> <div class="image-wrapper clearfix"> <div class="field field--name-field-image field--type-image field--label-above field__item">/sites/default/files/2024-09/article.png</div> </div> </div> </div> </div> Tue, 08 Oct 2024 12:45:19 +0000 julien.biedermann 766 at https://dynamics-chronicles.com Power Platform Dataflow Tutorial Deep Dive https://dynamics-chronicles.com/article/power-platform-dataflow-tutorial-deep-dive <span property="schema:name" class="field field--name-title field--type-string field--label-hidden">Power Platform Dataflow Tutorial Deep Dive</span> <span rel="schema:author" class="field field--name-uid field--type-entity-reference field--label-hidden"><a title="View user profile." href="/user/stephane-pelhatre" lang="" about="/user/stephane-pelhatre" typeof="schema:Person" property="schema:name" datatype="" class="username">Stephane Pelhatre</a></span> <span property="schema:dateCreated" content="2024-06-17T12:35:36+00:00" class="field field--name-created field--type-created field--label-hidden">Mon, 06/17/2024 - 14:35</span> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-above"> <div class="field__label">Body</div> <div property="schema:text" class="field__item"><p><em>Power Platform Dataflow Tutorial Deep Dive</em></p> <h2>Introduction</h2> <p>Dataflows are a self-service, cloud-based, data preparation technology. Dataflows enable customers to ingest, transform, and load data into Dataverse environments, Power BI workspaces, or an organization's Azure Data Lake Storage account.<br /> Dataflows are authored by using Power Query, a unified data connectivity and preparation experience already featured in many Microsoft products, including Excel and Power BI.<br /> Customers can trigger dataflows to run either on demand or automatically on a schedule.</p> <p>Dataflows are featured in multiple Microsoft products and don't require a dataflow-specific license to be created or run. They are available in Power Apps, Power BI, and Dynamics 365 Customer Insights.</p> <p><img alt="Power Platform Dataflow Tutorial Deep Dive" data-entity-type="file" data-entity-uuid="b015a862-560c-4d05-8ef8-59f2003813ee" src="/sites/default/files/inline-images/dataflow-function_0.png" /></p> <p>The previous image shows an overall view of how a dataflow is defined.<br /> A dataflow gets data from different data sources.<br /> Then, based on the transformations configured with the Power Query editor, the dataflow transforms the data by using the dataflow engine.<br /> Finally, data are loaded to the output destination, which can be a Power Platform environment, a Power BI workspace, or the organization's Azure Data Lake Storage account.</p> <p>Dataflows are cloud-based. A dataflow is stored and runs in the cloud. However, if a data source is on-premises, an on-premises data gateway can be used to extract the data to the cloud.</p> <p>Power Query is the data transformation engine used in dataflow. This engine supports many transformations. It also uses a graphical user interface called Power Query Editor.</p> <p>In this article we will study the use of dataflow in the Power Platform through a tutorial.</p> <h2>Dataflow in practice</h2> <p>In this tutorial we will import a CSV file into a Dataverse custom table.</p> <h3>Create a dataflow</h3> <p>To create a dataflow just go to make.powerapps.com on your Power Platform environment and select 'Dataflows' in the left pane.<br /> Then click on '+ New dataflow' on the top.</p> <p><img alt="Power Platform Dataflow Tutorial Deep Dive" data-entity-type="file" data-entity-uuid="f058575d-d04f-413b-baeb-ba011da864e2" src="/sites/default/files/inline-images/powerapps.png" /></p> <p>You will get the following screen:</p> <p><img alt="Power Platform Dataflow Tutorial Deep Dive" data-entity-type="file" data-entity-uuid="cecb17d4-5335-4e50-a572-f988c10b1094" src="/sites/default/files/inline-images/firsst%20screen_0.png" /></p> <p>I named my dataflow 'Import persons'.</p> <p>If you want to load data to an Azure data lake for analytical purposes check 'Analytical entities only'.<br /> In this tutorial I leave the checkbox unchecked as I want to load data into a Dataverse table.</p> <h3>Data sources</h3> <p>First step is to select a data source.<br /> Around 50 data sources are available as you can see below.</p> <p><img alt="Power Platform Dataflow Tutorial Deep Dive" data-entity-type="file" data-entity-uuid="6ba0eb96-fe18-4606-9edf-79aaeca798b4" src="/sites/default/files/inline-images/sources_0.png" /></p> <p>Main types of source:</p> <ul> <li><strong>Files </strong>: Excel, Text/CSV, XML/JSON, ...</li> <li><strong>Databases </strong>: SQL Server, Oracle, MySQL, PostgreSQL, ...</li> <li><strong>Azure </strong>: Azure SQL Database, Synapse Analytics, Blobs, Data Lake Storage Gen2, ...</li> <li><strong>Online services</strong> : Sharepoint, Exchange, Google Analytics, ...</li> </ul> <p>In this tutorial I will use a CSV file. You can directly drag&amp;drop your file in the interface.</p> <p><img alt="Excel_connect" data-entity-type="file" data-entity-uuid="e62f4ac0-7ba2-43f9-bdaf-20fa78f64f44" src="/sites/default/files/inline-images/Excel%20connection.png" /></p> <p>You get a preview of your file. Then click on 'Transform Data'.</p> <p><img alt="excel preview" data-entity-type="file" data-entity-uuid="24168957-2b84-4873-9627-c13313eec1b3" src="/sites/default/files/inline-images/excel%20preview_1.png" /></p> <h3>Data filtering</h3> <p>You get the screen of Power Query Editor</p> <p>At the top we have the formula bar (as in Excel or Power-fx). We will see in this article that we can display the current formula, the full script or hide this formula bar.<br /> Below you can see your data.<br /> And on the right side we have the 'Applied steps' pane with all steps/actions performed. It's a little empty because we've just started, but don't worry it will fill up:</p> <p><img alt="Power Platform Dataflow Tutorial Deep Dive" data-entity-type="file" data-entity-uuid="4c6a78c7-0022-4905-ba0a-cc3a84f41852" src="/sites/default/files/inline-images/screen%20home_0.png" /></p> <p>We have several tabs:</p> <ul> <li><strong>Home </strong>: functionalities to filter data and some features of other tabs</li> <li><strong>Transform</strong>: data transformations</li> <li><strong>Add column</strong>: add a new column to apply a transformation</li> <li><strong>View </strong>: display settings</li> </ul> <p>In this tutorial I first click on 'Use first column as headers' in the ribbon as the first line of my CSV file contains column names.</p> <p>The tab 'Home' contains several interesting features: for instance you can filter rows.<br /> To perform that action you first have to select a column and then click on the button 'Filter Rows'.<br /> Another interesting feature is the possibility to remove duplicates.<br /> As previously you first have to select a column and then click on the command 'Remove duplicates' in the menu 'Remove rows'.<br /> In my example I apply this command on the column <em>LastName </em>: we can see in the screenshot below that we have a duplicate in this column (raley - lines 1 and 4)</p> <p><img alt="remove dup" data-entity-type="file" data-entity-uuid="380cf750-ea01-41b0-92be-a2997f4b2b53" src="/sites/default/files/inline-images/remove%20duplicates_0.png" /></p> <p>And we obtain the expected result:</p> <p><img alt="Power Platform Dataflow Tutorial Deep Dive" data-entity-type="file" data-entity-uuid="fc94d39b-daf2-480d-a3ee-384c1480903e" src="/sites/default/files/inline-images/duplicates2_1.png" /></p> <p>You can also remove blank rows or lines with errors.</p> <h3>Transformations</h3> <p>When you have filtered your data you can now apply some transformations.<br /> Click on 'Transform' tab. You will get a ribbon dedicated to transformation features.</p> <p><br /> <img alt="Power Platform Dataflow Tutorial Deep Dive" data-entity-type="file" data-entity-uuid="ccb08364-224e-40fa-9768-30ac32d89849" src="/sites/default/files/inline-images/ribbon%20transformation.png" /></p> <p>You can perform many data transformations. This is a non-exhaustive list of transformation features:</p> <ul> <li>Transform any column: <ul> <li>Replace values</li> <li>Change type</li> <li>Detect data type</li> <li>Mark column as key</li> <li>Rename column</li> <li>Pivot/Unpivot columns</li> <li>Fill up/down</li> <li>Move column</li> </ul> </li> <li>Transform text column <ul> <li>Split (by delimiter, number of characters, lower/upper case and more)</li> <li>Format (lower/upper case capitalize words, trim, clean and more)</li> <li>Extract (length, first/last characters, range and more)</li> <li>Statistics (count/distinct values)</li> </ul> </li> </ul> <p>As a first example let's remove the characters 'xxx' in the column <em>Firstname</em> (in <em>rickiexxx </em>or <em>xxxryan</em>).<br /> Select column <em>Firstname </em>and then select the command 'Replace values...' in the menu 'Replace values'<br /> In my example I want to remove 'xxx' so I leave 'Replace with' empty.</p> <p><img alt="replace values" data-entity-type="file" data-entity-uuid="be4509a6-65d9-4878-a775-145e7dfb169e" src="/sites/default/files/inline-images/replace%20values.png" /></p> <p>And we obtain the expected result:</p> <p><img alt="Power Platform Dataflow Tutorial Deep Dive" data-entity-type="file" data-entity-uuid="7f6ae239-1fd6-47d7-bae0-a58e53cb276f" src="/sites/default/files/inline-images/result%20replace%202.png" /></p> <p>Note that at the top in the formula bar you can see the source code (Power Query M) of the last command :<br /> <em>Table.ReplaceValue(#"Removed duplicates", "xxx", "", Replacer.ReplaceText, {"Firstname"})</em></p> <p>Now let's change data type of column <em>Age. </em>It is currently a text column and I will change it to a whole number column.<br /> We can see that in line 3 the column <em>Age </em> contains 'abc' : let's say that it is an error as this column should contain only whole numbers.<br /> So I first select column <em>Age </em>and then in the menu 'Data type' I select the item 'Whole number'.<br /> This is the result:</p> <p><img alt="age1" data-entity-type="file" data-entity-uuid="90b74c27-8d2a-4a56-abbf-c3cf73bfd6a3" src="/sites/default/files/inline-images/age1_0.png" /></p> <p>We get an error in line 3. This makes sense as original data 'abc' is not a number.<br /> To solve this problem I select the column <em>Age </em>and select the command 'Replace errors...' in the menu 'Replace values'.<br /> In my example I set value to 99</p> <p><img alt="replace errors" data-entity-type="file" data-entity-uuid="2be1d771-af22-4e71-9b96-34f46be971e2" src="/sites/default/files/inline-images/replace%20errors_0.png" /></p> <p>And I get the expected result:</p> <p><img alt="age2" data-entity-type="file" data-entity-uuid="af77d668-356f-47e6-b7a7-37c67ec48948" src="/sites/default/files/inline-images/age2_0.png" /></p> <p>The menu 'Format' contains several interesting commands:</p> <p><img alt="format" data-entity-type="file" data-entity-uuid="a3dd1341-bf74-4b70-91b0-5ab8501420a0" src="/sites/default/files/inline-images/format.png" /></p> <p>We have the classic commands lowercase, uppercase, trim.<br /> But we also have the nice command 'Capitalize Each Word' (first letter of each word converted into an uppercase letter) and the command 'Clean' that removes all non-printable characters.</p> <p>Now I apply the command 'Capitalize Each Word'  on column <em>FirstName </em>and the command UPPERCASE on the column <em>Lastname</em>.<br /> This is the result:</p> <p><img alt="Power Platform Dataflow Tutorial Deep Dive" data-entity-type="file" data-entity-uuid="7ccc9ad2-5e76-436b-a904-f46a0f689b68" src="/sites/default/files/inline-images/uppercase.png" /></p> <h3>Add new column</h3> <p>Click on 'Add column' tab, you will get a ribbon dedicated to new columns.</p> <p><img alt="Power Platform Dataflow Tutorial Deep Dive" data-entity-type="file" data-entity-uuid="6d5cb8ca-3fb9-4068-acc5-1879e65439ad" src="/sites/default/files/inline-images/ribbon_column.png" /></p> <p>With commands in tab 'Transform' you apply the transformation to the original column (in-place) as we have seen previously.<br /> Commands in tab 'Add column' store the result of the transformation in a new column.<br /> The menu 'Format' we used previously in tab 'Transform' is also in tab 'Add column'.<br /> But if you use one the commands (for instance UPPERCASE) you will see that a new column is created.</p> <p>With the command 'Custom column' you add a new column but you cannot select a built-in command : you have to write your code (formula) in Power Query M language.</p> <p>As an example, let's create a column <em>Fullname </em>that concatenates columns <em>Firstname </em>and <em>Lastname </em>separated by a comma.<br /> Click on the command 'Custom column' and set the parameters:</p> <ul> <li>New column name : <em>Fullname</em></li> <li>Datatype : Text</li> <li>Custom column formula: I use the Power Query M function<em>Text.Combine()</em> with the 2 columns <em>Firstname </em>and <em>Lastname </em>and comma as separator<br /> You only have to type your function in the editor and click on fields in the section 'Available columns(s)' on the right to add the columns to the formula.</li> </ul> <p><img alt="combine" data-entity-type="file" data-entity-uuid="8c5abc3b-2ee0-4426-ac14-438c24265068" src="/sites/default/files/inline-images/combine_0.png" /></p> <p>And we obtain the expected result:</p> <p><img alt="combine2" data-entity-type="file" data-entity-uuid="9e20a444-41ce-4e78-bc06-466bceac4907" src="/sites/default/files/inline-images/combine2_1.png" /></p> <p>Another interesting feature is <strong>Conditional column</strong> : with this feature you can create new columns whose values are based on one or more conditions applied to other columns.</p> <p>Let's create a conditional column named 'Is too young' : the result will be set to TRUE is Age &lt; 18 otherwise result will be FALSE.</p> <p>You just have to click on the command 'Conditional column' and fill the data as below.</p> <p><img alt="condition_column" data-entity-type="file" data-entity-uuid="c87fed9d-e781-42db-896b-e358b9c8d9cc" src="/sites/default/files/inline-images/condtional_column.png" /></p> <p>In my example I use a static value (18) but it is possible to specify another column as value.</p> <p>And this is the result:</p> <p><img alt="Power Platform Dataflow Tutorial Deep Dive" data-entity-type="file" data-entity-uuid="b596d6c6-b146-4882-92a1-6c4fcd6152fd" src="/sites/default/files/inline-images/too%20young.png" /></p> <p>And now I change the data type of column 'Is too young'. I select the command 'Detect data type' in tab 'Transform': it automatically detects the best data type for the selected column.</p> <p><img alt="detect type" data-entity-type="file" data-entity-uuid="a90d0dd0-0c56-4912-a698-a9d814051c23" src="/sites/default/files/inline-images/detect%20date%20type_1.png" /></p> <p>As this column contains only strings 'TRUE' or 'FALSE' the best data type is logical true/false (boolean).<br /> And as you can see below it worked : the icon in the header of the column has changed to indicates that the column is now a logical column.</p> <p><img alt="Power Platform Dataflow Tutorial Deep Dive" data-entity-type="file" data-entity-uuid="517dc8b0-b942-4e29-9cc8-459f4b507bc1" src="/sites/default/files/inline-images/datatype2_1.png" /></p> <h3>'Applied steps' pane</h3> <p>And what about the 'Applied steps' pane on the right side?<br /> It displays all the steps executed until now.</p> <p>If you want to view the content of a specific step, simply click on the gear wheel on the right.<br /> To remove a step click on the cross on the left.</p> <p><img alt="view_step" data-entity-type="file" data-entity-uuid="3de1e405-dab0-4643-a5e1-13730e31fc38" src="/sites/default/files/inline-images/view_step_0.png" /></p> <p>If you right-click on a step you access a context menu : rename the step, insert a new step, move the step, ...</p> <p><img alt="context menu" data-entity-type="file" data-entity-uuid="c5878b5f-34e9-4671-a2e2-d818eb5ab74d" src="/sites/default/files/inline-images/context%20menu%20step_0.png" /></p> <p>At the bottom of the pane you have a menu 'Step'. If you click on it you will have 3 choices: Off, Query script, Step script (default).<br /> This defines what is displayed in the formula bar.</p> <p><img alt="viewstep2" data-entity-type="file" data-entity-uuid="152237f2-e61b-42c4-ab06-088f83430a34" src="/sites/default/files/inline-images/view_step2.png" /></p> <p>'Off' : hide the formula bar</p> <p>'Step script' : display in the formula bar the formula (code M) of the selected step.</p> <p><img alt="Power Platform Dataflow Tutorial Deep Dive" data-entity-type="file" data-entity-uuid="098112cb-34c6-42be-9c83-2fade00a9d53" src="/sites/default/files/inline-images/formula_2.png" /></p> <p>'Query script' : display in the formula bar the full script (all steps).</p> <p><img alt="Power Platform Dataflow Tutorial Deep Dive" data-entity-type="file" data-entity-uuid="a84d199b-0bde-4074-b90f-33c93c4180f2" src="/sites/default/files/inline-images/view_step3_0.png" /></p> <p>Next to the 'Step' button there is 'Diagram view' button. It displays in the formula bar the diagram of all steps.</p> <p><img alt="Power Platform Dataflow Tutorial Deep Dive" data-entity-type="file" data-entity-uuid="ce576561-2b0e-4964-9290-ca9db9d8003a" src="/sites/default/files/inline-images/diag1_1.png" /></p> <p>If you click on the '+' in the diagram a context menu is displayed : you can filter data, add a step, etc...</p> <p><img alt="diag2" data-entity-type="file" data-entity-uuid="e8f79485-3f06-4827-b9ce-eedf0b6461bd" src="/sites/default/files/inline-images/view_diagram2.png" /></p> <p>The 'View' tab contains several display settings including those we have just seen (step script, query script, diagram view),</p> <p><img alt="Power Platform Dataflow Tutorial Deep Dive" data-entity-type="file" data-entity-uuid="0678e527-04e8-4252-981b-aa4c3ceb7262" src="/sites/default/files/inline-images/view%20tab.png" /></p> <h3>Importing data into Dataverse</h3> <p>When you have finished applying transformations to you data you can click on button 'Next' at the bottom of the screen and move to next step to import transformed data into a Dataverse table.</p> <p>You can choose whether you want to load data into a new table or an existing one.<br /> In my example I create a new table <em>Person</em>.<br /> To do that simply name the table and choose which column is your unique primary column.<br /> An automatic mapping is performed.</p> <h3><img alt="export" data-entity-type="file" data-entity-uuid="c049226c-9d49-4b0f-95ff-1c1ad1bbf1ee" src="/sites/default/files/inline-images/export%20dataverse_0.png" /></h3> <p>Then click on 'Publish'.<br /> If the publish is successful you get the following screen:</p> <p><img alt="export2" data-entity-type="file" data-entity-uuid="19d957c0-ed6a-44d4-a1cb-c7b151614908" src="/sites/default/files/inline-images/export%202.png" /></p> <p>Finally you can schedule your dataflow : click on the 3 dots '...' and select 'Edit Refresh settings'</p> <p><img alt="Power Platform Dataflow Tutorial Deep Dive" data-entity-type="file" data-entity-uuid="cd2a7eb1-39dd-4a04-954c-c125c27af4e4" src="/sites/default/files/inline-images/schedule1.png" /></p> <p>You can run your dataflow manually or schedule it based on a specific frequency.</p> <p><img alt="Power Platform Dataflow Tutorial Deep Dive" data-entity-type="file" data-entity-uuid="78baab5e-ed75-420d-bd24-a7c0221f669b" src="/sites/default/files/inline-images/schedule2.png" /></p> <p>Note that you can get the following message if your refresh frequency exceeds the allowed limit of 48 refreshes per day.</p> <p><img alt="Power Platform Dataflow Tutorial Deep Dive" data-entity-type="file" data-entity-uuid="31088401-e59c-41a4-9ca0-0b8967b9cb88" src="/sites/default/files/inline-images/schedule3.png" /></p> <p>And the result in the Power Platform:</p> <p><br /> <img alt="result" data-entity-type="file" data-entity-uuid="5ac53ee8-d680-40f8-a584-e7f4f1bf6980" src="/sites/default/files/inline-images/result.png" /></p> <h2>Dataflow refresh</h2> <p>Each time you refresh a dataflow, it fetches records from the source and loads data into Dataverse.<br /> If you run the dataflow more than once you can:</p> <ul> <li>Create new records for each dataflow refresh, even if such records already exist in the destination table. In this case you will create duplicates.</li> <li>Create new records if they don't already exist in the table, or update existing records if they already exist in the table. This behavior is called <em>upsert</em>.</li> </ul> <p>Using an alternate key column indicates to the dataflow to upsert records into the destination table, while not selecting a key indicates to the dataflow to create new records in the destination table.</p> <p>For instance if your data source has a column that is different for all rows (a guid or any unique identifier) you can use this column has an alternate key in your Dataverse table.</p> <p>You can get details here: <a href="https://learn.microsoft.com/en-us/power-query/dataflows/get-best-of-standard-dataflows">https://learn.microsoft.com/en-us/power-query/dataflows/get-best-of-standard-dataflows</a></p> <h2>Power Query M language</h2> <p>As mentioned previously Dataflow is based on Power Query and its related language.<br /> The Power Query editor contains many built-in commands to perform basic operations and transformations.<br /> But you will probably have to use the language to write your own transformations. It is what we call 'writing code M'.<br /> The M stands for data Mash-up, as Power Query is all about connecting to various different data sources and “Mashing” them up.<br /> The Power Query M formula language is optimized for building flexible data mashup queries. It's a functional, case sensitive language similar to F#.<br /> Power Query Formula Language is used in a number of Microsoft products such as Power BI Desktop, Excel, and Analysis Services.</p> <p>Details about Power Query M language are out of the scope of this article.<br /> Specifications of the language can be found here:<br /> <a href="https://download.microsoft.com/download/8/1/A/81A62C9B-04D5-4B6D-B162-D28E4D848552/Power%20Query%20M%20Formula%20Language%20Specification%20(July%202019).pdf">https://download.microsoft.com/download/8/1/A/81A62C9B-04D5-4B6D-B162-D28E4D848552/Power%20Query%20M%20Formula%20Language%20Specification%20(July%202019).pdf</a></p> <p>The specification describes the values, expressions, environments and variables, identifiers, and the evaluation model that form the Power Query M language’s basic concepts.</p> <p>The official reference guide : <a href="https://learn.microsoft.com/en-gb/powerquery-m/power-query-m-function-reference">https://learn.microsoft.com/en-gb/powerquery-m/power-query-m-function-reference</a></p> <p>Many categories are available:</p> <ul> <li>    Accessing data functions</li> <li>    Binary functions</li> <li>    Combiner functions</li> <li>    Comparer functions</li> <li>    Date functions</li> <li>    DateTime functions</li> <li>    DateTimeZone functions</li> <li>    Duration functions</li> <li>    Error handling</li> <li>    Expression functions</li> <li>    Function values</li> <li>    List functions</li> <li>    Lines functions</li> <li>    Logical functions</li> <li>    Number functions</li> <li>    Record functions</li> <li>    Replacer functions</li> <li>    Splitter functions</li> <li>    Table functions</li> <li>    Text functions</li> <li>    Time functions</li> <li>    Type functions</li> <li>    Uri functions</li> <li>    Value functions</li> </ul> <p>Some basics about the language:</p> <p>A Power Query M formula language query is composed of formula expression steps that create a mashup query.<br /> A formula expression can be evaluated (computed), yielding a value.<br /> The <strong>let </strong>expression encapsulates a set of values to be computed, assigned names, and then used in a subsequent expression that follows the <strong>in </strong>statement.<br /> For example, a let expression could contain a <strong>Source </strong>variable that equals the value of <strong>Text.Proper</strong>() and yields a text value in proper case.</p> <pre> <code>let Source = Text.Proper("hello world") in Source</code></pre> <p>In this tutorial we wrote a formula to concatenate first name and last name:</p> <pre> <code>Text.Combine({[Firstname],[Lastname]}, " , ")</code></pre> <p>Note that function name (Combine) is prefixed by the category name (Text).<br /> For example : <em>Date.AddMonths()</em>, <em>Splitter.SplitTextByDelimiter()</em></p> <p>Below the full script of this tutorial:</p> <pre> <code>let Source = Csv.Document(Web.Contents("https://crm315857-my.sharepoint.com/personal/admin_crm315857_onmicrosoft_com/Documents/Apps/Microsoft Power Query/Uploaded Files/example1 8.csv"), [Delimiter = ";", Columns = 3, QuoteStyle = QuoteStyle.None]), #"Promoted headers" = Table.PromoteHeaders(Source, [PromoteAllScalars = true]), #"Removed duplicates" = Table.Distinct(#"Promoted headers", {"Lastname"}), #"Replaced value" = Table.ReplaceValue(#"Removed duplicates", "xxx", "", Replacer.ReplaceText, {"Firstname"}), #"Changed column type" = Table.TransformColumnTypes(#"Replaced value", {{"Age", Int64.Type}}), #"Replaced errors" = Table.ReplaceErrorValues(#"Changed column type", {{"Age", 99}}), #"Capitalized each word" = Table.TransformColumns(#"Replaced errors", {{"Firstname", each Text.Proper(_), type nullable text}}), #"Uppercased text" = Table.TransformColumns(#"Capitalized each word", {{"Lastname", each Text.Upper(_), type nullable text}}), #"Added custom" = Table.TransformColumnTypes(Table.AddColumn(#"Uppercased text", "Fullname", each Text.Combine({[Firstname], [Lastname]}, " , ")), {{"Fullname", type text}}), #"Inserted conditional column" = Table.AddColumn(#"Added custom", "Is too young", each if [Age] &lt; 18 then true else false), #"Changed column type 1" = Table.TransformColumnTypes(#"Inserted conditional column", {{"Is too young", type logical}}) in #"Changed column type 1"</code></pre> <p>A major drawback of Power Query editor :<br /> There is no intellisense/auto-completion when you type a formula (like in Microsoft Visual Studio or with Power-fx editor).<br /> This technology is very useful when you do not know the exact name of a function.<br /> And don't forget that code M language is case sensitive: so any case error in a function name and your code will not work.</p> <h2>Mass import</h2> <p>In order to evaluate the performance I have imported to Dataverse a CSV file with 100'000 rows.<br /> <br /> Format of each row:</p> <ul> <li><em>Id </em>: incremental number (unique value in order to perform updates)</li> <li><em>Firstname </em>: string</li> <li><em>Lastname</em>: string</li> <li><em>Age </em>: integer</li> </ul> <p>Transformations applied:</p> <ul> <li>Firstname : Capitalize Each Word</li> <li>Lastname : Uppercase</li> <li>Add new column :  Fullname = Firstname , Lastname</li> </ul> <p>The Dataverse custom table has a column <em>Id </em>as primary column and  alternate key to be able to perform updates.</p> <p>I performed the import first in creation mode : import of transformed data into an empty Dataverse table.<br /> Then I performed an import in update mode : I modified all data in the CSV file (firstname/lastname/age but not Id of course) and imported transformed data in the same Dataverse table.</p> <p>Creation mode : the 100'000 transformed rows have been imported in 58 minutes.<br /> Update mode : the 100'000 transformed rows have been imported in 52 minutes.</p> <h2>Licenses</h2> <p>To create dataflows in Power Apps a license is required (per-user or per-app).</p> <p>If you want to create analytical dataflows that store data in your organization's Azure Data Lake Storage Gen2 account, you or your administrator need access to an Azure subscription and an Azure Data Lake Storage Gen2 account.</p> <h2>Limitations</h2> <ul> <li>Maximum of 48 runs per day for a dataflow</li> <li>Only one owner is currently enabled. If another user wants to modify the dataflow you must change the owner</li> <li>Mapping to polymorphic lookup fields is currently not supported.</li> <li>Mapping to <em>Status </em>and <em>Status Reason</em> fields is currently not supported.</li> <li>Mapping data into multi-line text that includes line break characters isn't supported and the line breaks get removed. Instead, you could use the line break tag &lt;br&gt; to load and preserve multi-line text</li> <li>Mapping to <strong>Choice</strong> fields configured with the multiple select option enabled is only supported under certain conditions. The dataflow only loads data to <strong>Choice</strong> fields with the multiple select option enabled, and a comma-separated list of values (integers) of the labels are used.<br /> For example, if the labels are "Choice1, Choice2, Choice3" with corresponding integer values of "1, 2, 3", then the column values should be "1,3" to select the first and last choices.</li> </ul> <h2 class="title">Power Platform Dataflow Tutorial Deep Dive</h2> </div> </div> <div class="field field--name-field-image field--type-image field--label-above"> <div class="field__label">Image</div> <div class="images-container clearfix"> <div class="image-preview clearfix"> <div class="image-wrapper clearfix"> <div class="field field--name-field-image field--type-image field--label-above field__item">/sites/default/files/2024-06/Designer.jpeg</div> </div> </div> </div> </div> Mon, 17 Jun 2024 12:35:36 +0000 Stephane Pelhatre 785 at https://dynamics-chronicles.com Create a service principal with Power Platform CLI https://dynamics-chronicles.com/article/create-service-principal-power-platform-cli <span property="schema:name" class="field field--name-title field--type-string field--label-hidden">Create a service principal with Power Platform CLI</span> <span rel="schema:author" class="field field--name-uid field--type-entity-reference field--label-hidden"><a title="View user profile." href="/user/stephane-pelhatre" lang="" about="/user/stephane-pelhatre" typeof="schema:Person" property="schema:name" datatype="" class="username">Stephane Pelhatre</a></span> <span property="schema:dateCreated" content="2024-03-28T13:46:41+00:00" class="field field--name-created field--type-created field--label-hidden">Thu, 03/28/2024 - 14:46</span> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-above"> <div class="field__label">Body</div> <div property="schema:text" class="field__item"><p><em>Create a service principal with Power Platform CLI</em></p> <h2>Introduction</h2> <p>To perform an integration with Dynamics 365 you typically need to set up a service principal in Azure.<br /> To achieve this goal you must complete several steps:</p> <ul> <li>Create an Azure App Registration</li> <li>Create a secret for that App Registration</li> <li>Set up API permissions for Dynamics 365</li> <li>Create an Application User in the Power Platform Admin Center</li> <li>Assign permissions to the Application User</li> </ul> <p>A tutorial to perform those actions : <a href="https://dynamics-chronicles.com/article/step-step-connect-d365-clientsecret-use-apis">https://dynamics-chronicles.com/article/step-step-connect-d365-clientsecret-use-apis</a></p> <p><span lang="en" xml:lang="en" xml:lang="en"><span><span>All these manual steps take time</span></span></span>. But you can perform them with a Powershell command (<em>pac admin create-service-principal</em>).</p> <h2>Create your service principal</h2> <p>First you have to install <span>Power Platform CLI<span> </span></span>on your machine.<br /> <span lang="EN-US" xml:lang="EN-US" xml:lang="EN-US">Power Platform CLI </span>is a command-line interface <span lang="EN-US" xml:lang="EN-US" xml:lang="EN-US">used to perform various operations in the Power Platform related to environment lifecycle, authentication, and work with Dataverse environments, solution packages, portals, code components.</span></p> <p><span lang="EN-US" xml:lang="EN-US" xml:lang="EN-US"></span>You can download the MSI file here : <a href="https://aka.ms/PowerAppsCLI">https://aka.ms/PowerAppsCLI</a><br /> Power Platform CLI reference guide : <a href="https://learn.microsoft.com/en-us/power-platform/developer/cli/reference/tool">https://learn.microsoft.com/en-us/power-platform/developer/cli/reference/tool</a></p> <p><span lang="en" xml:lang="en" xml:lang="en"><span><span>After installing Power Platform CLI open a Powershell command prompt and type the command <em>pac</em> to check that the installation was successful.</span></span></span></p> <p><img alt="Create a service principal with Power Platform CLI" data-entity-type="file" data-entity-uuid="1c6b5cff-e105-467f-9ed3-5cb06dd22543" src="/sites/default/files/inline-images/01.%20pac_0.png" /></p> <p>Then type the following command to update to the latest version.</p> <pre> <code> pac install latest</code></pre> <p>Next step is to connect to your Dataverse environment with an authentication profile.<br /> To get a list of all authentication profiles type the following command :</p> <pre> <code class="language-bash">pac auth list</code></pre> <p><img alt="Create a service principal with Power Platform CLI" data-entity-type="file" data-entity-uuid="95804576-ff78-4647-8c07-4cfcf26a3e5b" src="/sites/default/files/inline-images/02.%20pac%20list.png" /></p> <p>As you can see I have no authentication profile installed.<br /> To create a profile type the following command:</p> <pre> <code class="language-bash">pac auth create -env &lt;env id&gt;</code></pre> <p>&lt;env Id&gt; is the environment id of your organisation, which you will find in the Power Platform Admin Center.</p> <p><img alt="Create a service principal with Power Platform CLI" data-entity-type="file" data-entity-uuid="11f4d83f-d12d-4d17-84a9-86a22e8e02b6" src="/sites/default/files/inline-images/04.%20env%20id_0.png" /></p> <p>You will be asked to provide your credentials. Below the result :</p> <p><img alt="Create a service principal with Power Platform CLI" data-entity-type="file" data-entity-uuid="f72d03d4-a289-474e-9a21-f92f5404f321" src="/sites/default/files/inline-images/05.%20pac%20create_0.png" /></p> <p>The last step is to use the command below :</p> <pre> <code class="language-bash">pac admin create-service-principal --environment &lt;env id&gt;</code></pre> <p><img alt="Create a service principal with Power Platform CLI" data-entity-type="file" data-entity-uuid="731dfcdd-c9c8-4228-85bf-237601e5f6ef" src="/sites/default/files/inline-images/06.%20create%20principal.png" /></p> <p>Now you can check the result in your Azure Portal.<br /> As you can see below a new App Registration has been created.</p> <p><br /> <img alt="07" data-entity-type="file" data-entity-uuid="a9f0095e-6444-437a-a77f-6a3c2574b737" src="/sites/default/files/inline-images/07%20app%20reg.png" /></p> <p>A secret with one year validity has been created for the App Registration.</p> <p><br /> <img alt="08" data-entity-type="file" data-entity-uuid="127c27b9-dd25-48df-a372-d09803677642" src="/sites/default/files/inline-images/08%20secret.png" /></p> <p>And correct permissions have also be added.</p> <p><br /> <img alt="09" data-entity-type="file" data-entity-uuid="c543918b-b7cf-4331-9eaf-babf90770415" src="/sites/default/files/inline-images/09%20permissions.png" /></p> <p>You can see your new application user in the Power Platform Admin Center.</p> <p><br /> <img alt="10" data-entity-type="file" data-entity-uuid="71bb7043-ad4a-44ca-ada0-8b57c43b2107" src="/sites/default/files/inline-images/10%20app%20user.png" /><br /> The application user has the security role 'System administrator'.<br /> It is the default security role when you use the command <em>pac admin create-service-principal</em>.<br /> If you want to associate another security role you have to use the option <em>--role</em>.</p> <pre> <code class="language-bash">pac admin create-service-principal --environment &lt;env id&gt; --role &lt;role&gt;</code></pre> <p>Where <em>&lt;role&gt;</em> is the name or ID of security role to be applied to the application user.<br /> Below the command to create a service principal with security role 'Sales Manager' assigned to the application user.</p> <p><br /> <img alt="Create a service principal with Power Platform CLI" data-entity-type="file" data-entity-uuid="f30bc1a4-de9c-4c2a-9124-ee1a9b541acc" src="/sites/default/files/inline-images/11%20role.png" /></p> <h2 class="title">Create a service principal with Power Platform CLI</h2> </div> </div> Thu, 28 Mar 2024 13:46:41 +0000 Stephane Pelhatre 779 at https://dynamics-chronicles.com Azure Programming Tools https://dynamics-chronicles.com/article/create-service-principal-power-platform-cli#comments Power Docu : Easy to document Dataverse Solution https://dynamics-chronicles.com/article/power-docu-easy-document-dataverse-solution <span property="schema:name" class="field field--name-title field--type-string field--label-hidden">Power Docu : Easy to document Dataverse Solution</span> <span rel="schema:author" class="field field--name-uid field--type-entity-reference field--label-hidden"><a title="View user profile." href="/user/joaoneto" lang="" about="/user/joaoneto" typeof="schema:Person" property="schema:name" datatype="" class="username">joao.neto</a></span> <span property="schema:dateCreated" content="2024-03-14T13:45:19+00:00" class="field field--name-created field--type-created field--label-hidden">Thu, 03/14/2024 - 14:45</span> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-above"> <div class="field__label">Body</div> <div property="schema:text" class="field__item"><h1>Intro</h1> <p>Want to document a Power Apps Solution?</p> <p>Power Docu provides a easy way of doing it by documenting Flows, Canvas Apps and other Power Apps solution components in the form of a Word Document or in a markdown format (GitHub or Azure Devops style) by using an self contained Windows Gui application.</p> <p>All info over this link here: <a href="https://github.com/modery/PowerDocu">https://github.com/modery/PowerDocu</a></p> <h1>How it Works</h1> <p>Power Docu support the generation of documentation for Power Apps based on a local solution .zip or on a .msapp file.</p> <p>The documentation contains information about :</p> <ul> <li>Power Automate Flows <ul> <li>General information</li> <li>Connectors</li> <li>Trigger</li> <li>Actions</li> <li><img alt="Flow Actions" data-entity-type="file" data-entity-uuid="99f325f8-b758-4918-89b2-959a117c0113" height="458" src="/sites/default/files/inline-images/PowerDocu.Flow_.Action.png" width="469" /></li> <li>Flow diagram (simple and detailed)</li> <li><img alt="Simple Flow Diagram" data-entity-type="file" data-entity-uuid="c8080098-b754-40f7-aaa6-c0f8a20697e6" height="441" src="/sites/default/files/inline-images/flow.png" width="242" /></li> <li><img alt="Detailed Flow Diagram" data-entity-type="file" data-entity-uuid="b34c84d5-a97e-42ef-9dd8-6d0d37da4be2" height="489" src="/sites/default/files/inline-images/flow-detailed.png" width="826" /></li> </ul> </li> <li>Canvas Apps <ul> <li>General information</li> <li>Global variables</li> <li><img alt="Canvas Variables" data-entity-type="file" data-entity-uuid="3b2f1b05-56cb-4966-ad32-489a09ca7c23" height="183" src="/sites/default/files/inline-images/PowerDocu.CanvasApps.Variables.png" width="471" /></li> <li>Data sources</li> <li>Resources</li> <li>Controls</li> <li><img alt="Canvas Controls" data-entity-type="file" data-entity-uuid="32003bed-d0b5-414f-b30b-71e927765643" height="295" src="/sites/default/files/inline-images/PowerDocu.CanvasApps.Controls_0.png" width="439" /></li> <li>Screens overview</li> </ul> </li> <li>Solution components <ul> <li>Dataverse Tables <ul> <li>Columns</li> <li>Relationships</li> </ul> </li> </ul> </li> </ul> <p><img alt="Tables" data-entity-type="file" data-entity-uuid="e95e59f8-76c3-41ee-bbde-542b1fa4eff8" height="386" src="/sites/default/files/inline-images/PowerDocu.DocTables.png" width="418" /></p> <ul> <li>Security Roles</li> </ul> <p><img alt="Security Roles" data-entity-type="file" data-entity-uuid="93a40987-7267-4ebe-bf85-a6470e564917" height="463" src="/sites/default/files/inline-images/PowerDocu.DocSecRoles.png" width="412" /></p> <ul> <li>Solution Dependencies</li> </ul> <p><img alt="Solution Dependencies" data-entity-type="file" data-entity-uuid="e67d122e-c0aa-4d87-93b3-1c5cbf288a37" height="392" src="/sites/default/files/inline-images/PowerDocu.DocDependencies.png" width="446" /></p> <p>By the way Model Driven-Apps aren't yet support in the current version 2.0 !</p> <h1>How to</h1> <ul> <li>Install Power Docu locally</li> <li>Run Power Docu</li> </ul> <p><img alt="App" data-entity-type="file" data-entity-uuid="d49b1193-6a66-4b0e-8173-1a4a57ca7de9" height="369" src="/sites/default/files/inline-images/PowerDocu.01.png" width="587" /></p> <ul> <li>Select the outputs</li> </ul> <p><img alt="Outputs" data-entity-type="file" data-entity-uuid="62378785-1446-47a9-bf5a-17bdaccae9fc" height="140" src="/sites/default/files/inline-images/PowerDocu.Output.png" width="284" /></p> <ul> <li>Select the Canvas App documentation level</li> </ul> <p><img alt="Canvas Options" data-entity-type="file" data-entity-uuid="0cebabe6-2840-4945-97c4-d98cdbbe4701" height="156" src="/sites/default/files/inline-images/PowerDocu.CanvasApps.png" width="398" /></p> <ul> <li>Click "Next" and select a solution export file on a .zip or on a .msapp file format</li> </ul> <p><img alt="Open Solution" data-entity-type="file" data-entity-uuid="bf95d75c-6ba7-432f-a646-21efc43f8d21" height="424" src="/sites/default/files/inline-images/PowerDocu.OpenFile.png" width="589" /></p> <ul> <li>Your Documentation is ready on the same folder as you solution file!</li> </ul> <h1>Final thoughts</h1> <p>Power Docu it's a very interesting App to easily generate standard Documentation for you Power Apps solution.</p> <p>In my perspective just missing the Model-Driven Apps module that I would really use and just noticed that it's on the project Roadmap !</p> <p>The code is open source so teams can fork and build their own generation model.</p> <p> </p> <p> </p> </div> </div> <div class="field field--name-field-image field--type-image field--label-above"> <div class="field__label">Image</div> <div class="images-container clearfix"> <div class="image-preview clearfix"> <div class="image-wrapper clearfix"> <div class="field field--name-field-image field--type-image field--label-above field__item">/sites/default/files/2024-07/PowerDocu%20-%20Info%20Header.png</div> </div> </div> </div> </div> Thu, 14 Mar 2024 13:45:19 +0000 joao.neto 768 at https://dynamics-chronicles.com Power Apps Power Automate Tools Tutorial https://dynamics-chronicles.com/article/power-docu-easy-document-dataverse-solution#comments PowerApps Model Driven UI Testing with Playwright https://dynamics-chronicles.com/article/powerapps-model-driven-ui-testing-playwright <span property="schema:name" class="field field--name-title field--type-string field--label-hidden">PowerApps Model Driven UI Testing with Playwright</span> <span rel="schema:author" class="field field--name-uid field--type-entity-reference field--label-hidden"><a title="View user profile." href="/user/zsoltzombik" lang="" about="/user/zsoltzombik" typeof="schema:Person" property="schema:name" datatype="" class="username">zsoltzombik</a></span> <span property="schema:dateCreated" content="2023-12-07T15:16:37+00:00" class="field field--name-created field--type-created field--label-hidden">Thu, 12/07/2023 - 16:16</span> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-above"> <div class="field__label">Body</div> <div property="schema:text" class="field__item"><p><em>PowerApps Model Driven UI Testing with Playwright</em></p> <h2>Introduction</h2> <p>In the ever-evolving landscape of software development, test automation has emerged as a pivotal trend, revolutionizing the way we build and deploy software. For businesses embracing Agile and DevOps methodologies, automated testing has become a cornerstone practice, enabling faster development and deployment cycles. In this article, we embark on a journey to explore the profound benefits of automated testing and why it holds such paramount importance in the context of software development, particularly within Dynamics 365 Model Driven Apps. We'll also touch upon the pivotal role of Playwright in automating UI testing within this environment. So, let’s discover the major benefits of automated testing and why automation in software testing matters:</p> <ul> <li><strong>Accelerated Development and Delivery:</strong> UI Test automation significantly reduces testing time, as automated tests can be executed rapidly and repeatedly. This results in shorter software development cycles, more frequent releases, quicker updates, and faster time-to-market delivery. The integration of automation, particularly with Playwright, expedites the testing process within Dynamics 365 Model Driven Apps, enhancing the development speed.</li> <li><strong>Enhanced Productivity:</strong> Automated tests operate without human intervention, enabling testing at any time, even during non-working hours. This autonomy minimizes the time software developers and quality assurance teams spend on testing, allowing them to focus on critical tasks. With automation, engineers can prioritize the creation of new features and improvements, ultimately boosting productivity.</li> <li><strong>Precision and Accuracy:</strong> Automated testing reduces the risk of human errors during the testing process. Unlike human testers who may make mistakes, automated tests execute precisely according to predefined criteria. This precision contributes to more reliable and error-free releases, minimizing the risk of failure.</li> <li><strong>Superior App Quality and Performance:</strong> Automated testing offers extensive test coverage, ensuring high-quality and high-performance applications. It allows for simultaneous testing of thousands of test cases across multiple platforms and devices, a feat difficult to achieve manually. With automation, complex and lengthy test cases can be created quickly, elevating the overall quality of applications.</li> <li><strong>Immediate Feedback:</strong> Automated testing provides instant feedback to developers, enabling quick response to failures. This immediate feedback accelerates the bug-fixing process and ensures a better user experience, leading to higher customer satisfaction. In contrast, manual testing can slow down the development and update process.</li> <li><strong>Enabling CI/CD and DevOps:</strong> Test automation is pivotal for implementing Continuous Integration (CI) and Continuous Delivery (CD) practices. In the CI/CD pipeline, every code commit needs to be tested swiftly and efficiently, a task perfectly suited for automation. Automation, including Playwright for UI testing, streamlines the transition to Continuous Testing and Delivery.</li> </ul> <p>Incorporating test automation, especially within the context of Dynamics 365 Model Driven Apps, is not just a trend; it's a necessity. The benefits of automated testing continue to expand as tools and testing frameworks evolve. Beyond traditional automated testing, new areas like model-based testing, predictive analysis, robotics automation, and API test-case automation are emerging. To stay at the forefront of technological evolution, implementing test automation is imperative. It accelerates development, enhances efficiency, accuracy, and productivity, all while maintaining the highest standards of app quality and performance. Partnering automation with a robust testing solution and integrating it with your CI/CD tools ensures that you harness its full potential efficiently on real devices, propelling your software development to new heights without compromising on quality or performance.</p> <ul> </ul> <h2>Setting Up Your Environment for Playwright in Dynamics 365</h2> <h3>Prerequisites for Playwright Setup</h3> <p>Before diving into using Playwright for UI testing, ensure you have the following prerequisites in place:</p> <h4><strong>Native Installation</strong></h4> <ul> <li>Playwright Test for Visual Studio Code: This is available on Visual Studio Marketplace. Make sure you have version 1.19 or newer to leverage the full capabilities of Playwright in Visual Studio Code.</li> </ul> <h4><strong>Playwright NPM Package</strong></h4> <ul> <li><strong>Cross Platform Support:</strong> Playwright is designed for versatility, supporting Windows, Linux, and macOS. It can be used for testing both locally and on Continuous Integration (CI) platforms such as Azure DevOps, GitHub. </li> <li><strong>Browser Testing Flexibility:</strong> It offers extensive testing capabilities, including both headless and headed modes. For mobile testing, Playwright provides native mobile emulation for Google Chrome on Android and Mobile Safari, ensuring comprehensive coverage across devices.</li> </ul> <h4><strong>Azure Subscription (Optional)</strong></h4> <ul> <li><strong>Enhanced Testing with Microsoft Azure:</strong> While not mandatory, an active Microsoft Azure subscription is recommended for a more robust testing framework. It becomes essential if you're planning to conduct automated tests within Azure or integrate load testing into your CI/CD pipelines. Utilizing Azure enhances your testing scope and efficiency, particularly for complex Dynamics 365 environments.</li> </ul> <p>Having these prerequisites in place sets a solid foundation for effective and efficient use of Playwright in your Dynamics 365 UI testing projects.</p> <h3>Installation and Configuration</h3> <p>Before you start, you will need install the following:</p> <ul> <li><strong>NodeJS </strong>- <a href="https://bit.ly/getnodejs">https://bit.ly/getnodejs</a></li> <li><strong>VSCode </strong>- <a href="https://bit.ly/downloadvscode">https://bit.ly/downloadvscode</a></li> </ul> <p>When installing Node, select the LTS version and accept the defaults.</p> <p>After installation, you should check you have Node installed correctly, at command line type:</p> <pre> <code class="language-javascript">npm</code></pre> <p><img alt="PowerApps Model Driven UI Testing with Playwright" data-entity-type="file" data-entity-uuid="1298b6e6-812f-451d-8d07-63fc3d4fc08c" src="/sites/default/files/inline-images/Playwright_extension0.png" /></p> <p>You should see the npm command help information displayed.</p> <p>To check you had the VSCode installed correctly, at the command line type:</p> <pre> <code class="language-javascript">Code .</code></pre> <p>This should open VSCode at the code folder location you are currently in.</p> <h3>UI Test Project folder setup</h3> <p>Playwright UI Test project do not have a project file like C# (.csproj)  so you can simply create a new folder with the name of your project. I will use UITest.</p> <p>At the command line, type:</p> <pre> <code class="language-javascript">Mkdir UITest Cd UITest</code></pre> <h3>Install Playwright Test for Visual Studio Code</h3> <p>Open Visual Studio Code.</p> <p>Select Extension, then search for Playwright Test for VSCode.</p> <p><img alt="Introduction to Dynamics 365 Model Driven Apps UI Testing with Playwright" data-entity-type="file" data-entity-uuid="6234db30-87a2-4e8e-ba5a-75b7bb7e60fd" src="/sites/default/files/inline-images/Playwright_extension1.png" /></p> <p>Then click on Install.</p> <p>Open the command panel (Ctrl + Shift + P), and then enter the following command: <strong>Install Playwright</strong>.</p> <p><img alt="PowerApps Model Driven UI Testing with Playwright" data-entity-type="file" data-entity-uuid="cd701b6a-de78-435f-92bd-4027e24a0191" src="/sites/default/files/inline-images/Playwright_extension2.png" /></p> <p>On the next screen, select the browser(s) you want to run your tests in. You can change the setting later in the <em>playwright.config</em> file.</p> <p>Finally you created your first Playwright test project.</p> <h3>Get a Test Environment</h3> <p>Next, you need a Dynamics 365 environment that you can run tests in. If you're finalizing an implementation project, you probably have a test environment already. If you don't, you can get a free 30-day trial (for this article, we provisioned a trial Dynamics 365 Customer Service Environment.</p> <h3>Setting up Test Environment</h3> <p>To optimize the test environment for automated testing, especially when using Playwright with a model driven app, follow these steps:</p> <h4>Disable Security Defaults</h4> <ul> <li><strong>Adjust Security Defaults:</strong> For the purpose of automated testing it's necessary to turn off security defaults. This ensures that the automated tests can run without unnecessary interruptions interruptions or blocks, which are typically in place for security reasons. Please follow this link: <a href="https://learn.microsoft.com/en-us/entra/fundamentals/security-defaults">https://learn.microsoft.com/en-us/entra/fundamentals/security-defaults</a></li> </ul> <h4>Create and Configure User Accounts</h4> <ul> <li><strong>Access Microsoft 365 Admin Center:</strong> Utilize the Microsoft 365 admin center to set up the required user account(s) for testing.</li> <li><strong>Assign Security Roles:</strong> Properly assign the necessary security roles to these accounts to ensure they have the appropriate access levels for testing.</li> </ul> <p>By carefully configuring the test environment as described, you create a more controlled and effective setting for conducting automated tests using Playwright in Dynamics 365.</p> <h3>First Playwright Test</h3> <p>Let's create our first test! Playwright provides convenient utilities for capturing your browser interactions and generating test scripts. To accomplish this, simply execute the Playwright Node command with the <em>codegen </em>argument. When you include the URL of your model-driven Power App as a parameter, it initiates a browser session that records the user's actions within the Playwright Inspector.</p> <p>To inititate the recording a new test, type this command on Visual Studio Code Terminal window:</p> <pre> <code class="language-javascript">npx playwright codegen https://&lt;you-crm-org&gt;.crm4.dynamics.com/main.aspx?appid=&lt;app guid&gt;</code></pre> <p>Playwright open a new browser instance and launch Playwright Inspector:</p> <p><img alt="PowerApps Model Driven UI Testing with Playwright" data-entity-type="file" data-entity-uuid="88710191-fcb5-4d9f-8fc3-5526f4f57302" src="/sites/default/files/inline-images/First%20Test.png" /></p> <p><img alt="Introduction to Dynamics 365 Model Driven Apps UI Testing with Playwright" data-entity-type="file" data-entity-uuid="61f95da0-e58e-4472-bb23-14bcd03eae3e" src="/sites/default/files/inline-images/First%20Test2.png" /></p> <p>Enter the credentials.</p> <p>Then select <strong>Service </strong>-&gt; <strong>Cases</strong>.</p> <p>Click on <strong>New Case</strong> button.</p> <p>Fill the form, then click on the <strong>Save</strong> button.</p> <p>Finally, take a look at the Playwright inspector window, it generated the code of our test.</p> <p>After the test script generated, we need to create a new test file under the tests folder such as and copy the code generated by Playwright Inspector.</p> <p> </p> <p><img alt="Introduction to Dynamics 365 Model Driven Apps UI Testing with Playwright" data-entity-type="file" data-entity-uuid="da431062-f6a9-447a-978f-9fa576746f59" src="/sites/default/files/inline-images/Runnimgtest_2.png" /></p> <p>Select the Test icon on Visual Studio Code.</p> <p>Find the test we created previously.</p> <p>Click in <strong>Debug Test</strong> button.</p> <p><img alt="PowerApps Model Driven UI Testing with Playwright" data-entity-type="file" data-entity-uuid="f0577589-e636-4f10-9a0e-e017f7137ff9" src="/sites/default/files/inline-images/Runnimgtest3.png" /></p> <p>It kicks off by running tests in debug mode, and eventually, you'll spot the test results right in the terminal window.</p> <p><img alt="Running Playwright Tests" data-entity-type="file" data-entity-uuid="afd50dc2-7238-4525-9e5f-d99facf81e61" src="/sites/default/files/inline-images/Runnimgfinal.png" /></p> <h2>Here's a small preview of the upcoming articles on Playwright.</h2> <ul> <li>The upcoming article, <strong>Writing Effective UI Tests with Playwright in Dynamics 365</strong> will focus on mastering Playwright's testing capabilities, including automated browser testing and managing dynamic content. It will provide a step-by-step guide to developing test scripts specifically tailored for Dynamics 365 UI, complete with example scripts and scenarios, and conclude with strategies for debugging and troubleshooting these tests.</li> <li>Third article <strong>Advanced Playwright Features for Dynamics 365 Testing</strong> will cover sophisticated techniques for working with multiple browsers and devices, including cross-browser and responsive testing. It will also delve into Playwright's advanced capabilities like network interception, mocking, and visual regression testing, along with their integration in CI/CD pipelines.</li> <li>Fourth article titled <strong>Best Practices and Optimizing Test Performance</strong> focuses on creating maintainable test code with well-organized structure and reusable components. It will discuss strategies to enhance test performance, such as parallel testing and methods to reduce test flakiness, and will conclude with approaches for monitoring and reporting test results.</li> </ul> <h2 class="title">PowerApps Model Driven UI Testing with Playwright</h2> </div> </div> <div class="field field--name-field-image field--type-image field--label-above"> <div class="field__label">Image</div> <div class="images-container clearfix"> <div class="image-preview clearfix"> <div class="image-wrapper clearfix"> <div class="field field--name-field-image field--type-image field--label-above field__item">/sites/default/files/2023-11/1970%27s%20sci%20robot%20testing%20software%2C%20studio%20light%2C%20on%20an%20emerald%20colored%20background.jpg</div> </div> </div> </div> </div> Thu, 07 Dec 2023 15:16:37 +0000 zsoltzombik 634 at https://dynamics-chronicles.com Azure CI/CD - Testing Deep Dive Programming Tools Tutorial https://dynamics-chronicles.com/article/powerapps-model-driven-ui-testing-playwright#comments Enhancing Dynamics 365 Client API Interaction with Xrm-Ex https://dynamics-chronicles.com/article/enhancing-dynamics-365-client-api-interaction-xrm-ex <span property="schema:name" class="field field--name-title field--type-string field--label-hidden">Enhancing Dynamics 365 Client API Interaction with Xrm-Ex</span> <span rel="schema:author" class="field field--name-uid field--type-entity-reference field--label-hidden"><a title="View user profile." href="/user/ahash" lang="" about="/user/ahash" typeof="schema:Person" property="schema:name" datatype="" class="username">Ahash</a></span> <span property="schema:dateCreated" content="2023-10-30T16:34:43+00:00" class="field field--name-created field--type-created field--label-hidden">Mon, 10/30/2023 - 17:34</span> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-above"> <div class="field__label">Body</div> <div property="schema:text" class="field__item"><p><em>Enhancing Dynamics 365 Client API Interaction with Xrm-Ex</em></p> <p>Stepping into software development for Dynamics 365, developers come across various tools and frameworks to make working with its Client API easier. Xrm-Ex stands out as a strong framework designed mainly for JavaScript, even though it has roots in TypeScript. The main goal of Xrm-Ex is to simplify how developers interact with the formContext and the Xrm Object. By doing this, it aims to reduce the amount of code developers have to write, making the codebase easier to manage and less likely to have errors.</p> <p>Xrm-Ex provides a set of wrappers that work well with the Dynamics 365 Client API, making the developer experience smoother. With Xrm-Ex, developers have a toolkit that can help boost productivity, allowing more focus on creating great applications. This article will explore the Xrm-Ex framework, covering its installation, core features, how it can be used in real-world scenarios, and how developers can contribute to its community. All these aspects make Xrm-Ex a reliable choice for developers working with the Dynamics 365 Client API landscape.</p> <h2>Licensing</h2> <p>Xrm-Ex is licensed under the <strong>MIT License</strong>, which is a permissive open-source license. This means that it is <strong>free to use</strong> and you can also modify or redistribute it. The open-source nature of Xrm-Ex encourages collaboration and sharing in the developer community. The full license text is available in the project repository on GitHub.</p> <h2>Understanding Xrm-Ex</h2> <p>Xrm-Ex is a solid framework tailored for Dynamics 365 Client API. Although it's built with TypeScript principles, it's mainly made for JavaScript usage. The key goal of Xrm-Ex is to make working with the formContext and the Xrm Object simpler. By doing this, it aims to cut down the amount of code you need to write, ensuring your codebase remains easy to manage and less likely to have errors.</p> <p>At its core, Xrm-Ex aims to streamline the interaction between developers and Dynamics 365. It does this by providing a clear structure and set of tools that help manage the different parts and events within Dynamics 365. This makes it easier to work with Dynamics 365's Client API, reducing the learning curve and making the development process more straightforward.</p> <p>By using Xrm-Ex, developers can focus more on building out their applications' functionality rather than getting bogged down with managing complex interactions with Dynamics 365. This way, Xrm-Ex helps in maintaining a clean and efficient codebase, making the development process smoother and more enjoyable.</p> <h2>Installation and Getting Started</h2> <p>Installing Xrm-Ex is a straightforward process done via npm. You can find the package on npm <a href="https://www.npmjs.com/package/xrm-ex">here</a>. To install, use the following command:</p> <div class="bg-black rounded-md"> <div class="p-4 overflow-y-auto"><code class="!whitespace-pre hljs language-bash">npm install xrm-ex </code></div> </div> <h3>Documentation</h3> <p>For a comprehensive guide to using XrmEx, please check out the full <a href="https://xrm-ex.ahash.dev/modules/src_XrmEx.XrmEx.html">documentation</a>.</p> <h3>Video: Set up Project</h3> <div style="max-width: 600px;"> <video controls="" width="100%"><source src="https://github.com/AhashSritharan/Xrm-Ex/assets/63707488/750cd578-e174-43b0-8783-149599db3da5" type="video/mp4"></source> Your browser does not support the video tag.</video> </div> <h3>Video: Deployment</h3> <div style="max-width: 600px;"> <video controls="" width="100%"><source src="https://github.com/AhashSritharan/Xrm-Ex/assets/63707488/34e2642d-0d73-4964-a5c5-6c035a474773" type="video/mp4"></source> Your browser does not support the video tag.</video> </div> <p>After installing Xrm-Ex, the next step is to set up your project. The setup involves adding <code>XrmEx.js</code> from your <code>node_modules</code> to your Dynamics 365 form as a library. Here’s a basic template to help you get started with your JavaScript setup:</p> <div style="color: #d4d4d4;background-color: #1e1e1e;font-family: Consolas, 'Courier New', monospace;font-weight: normal;font-size: 14px;line-height: 19px;white-space: pre;"> <div><span style="color: #6a9955;">/// </span><span style="color: #808080;">&lt;</span><span style="color: #569cd6;">reference</span><span style="color: #6a9955;"> </span><span style="color: #9cdcfe;">path</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"node_modules/xrm-ex/src/XrmEx.d.ts"</span><span style="color: #6a9955;"> </span><span style="color: #808080;">/&gt;</span><br /> <span style="color: #569cd6;">var</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">YourNamespace</span><span style="color: #d4d4d4;"> = </span><span style="color: #9cdcfe;">YourNamespace</span><span style="color: #d4d4d4;"> || {};</span><br /> <span style="color: #9cdcfe;">YourNamespace</span><span style="color: #d4d4d4;">.</span><span style="color: #9cdcfe;">Contact</span><span style="color: #d4d4d4;"> = </span><span style="color: #9cdcfe;">YourNamespace</span><span style="color: #d4d4d4;">.</span><span style="color: #9cdcfe;">Contact</span><span style="color: #d4d4d4;"> || {};</span><br /> <span style="color: #6a9955;">//Only properties assigned to the Self object will be exposed to the global scope</span><br /> <span style="color: #d4d4d4;">(</span><span style="color: #569cd6;">function</span><span style="color: #d4d4d4;"> (</span><span style="color: #9cdcfe;">Self</span><span style="color: #d4d4d4;">) {</span><br /> <span style="color: #d4d4d4;">    </span><span style="color: #569cd6;">class</span><span style="color: #d4d4d4;"> </span><span style="color: #4ec9b0;">Fields</span><span style="color: #d4d4d4;"> {</span><br /> <span style="color: #d4d4d4;">        </span><span style="color: #9cdcfe;">Firstname</span><span style="color: #d4d4d4;"> = </span><span style="color: #569cd6;">new</span><span style="color: #d4d4d4;"> </span><span style="color: #4ec9b0;">XrmEx</span><span style="color: #d4d4d4;">.</span><span style="color: #4ec9b0;">Class</span><span style="color: #d4d4d4;">.</span><span style="color: #4ec9b0;">TextField</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">"firstname"</span><span style="color: #d4d4d4;">);</span><br /> <span style="color: #d4d4d4;">        </span><span style="color: #9cdcfe;">Customer</span><span style="color: #d4d4d4;"> = </span><span style="color: #569cd6;">new</span><span style="color: #d4d4d4;"> </span><span style="color: #4ec9b0;">XrmEx</span><span style="color: #d4d4d4;">.</span><span style="color: #4ec9b0;">Class</span><span style="color: #d4d4d4;">.</span><span style="color: #4ec9b0;">LookupField</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">"parentcustomerid"</span><span style="color: #d4d4d4;">);</span><br /> <span style="color: #d4d4d4;">        </span><span style="color: #9cdcfe;">DoNotEmail</span><span style="color: #d4d4d4;"> = </span><span style="color: #569cd6;">new</span><span style="color: #d4d4d4;"> </span><span style="color: #4ec9b0;">XrmEx</span><span style="color: #d4d4d4;">.</span><span style="color: #4ec9b0;">Class</span><span style="color: #d4d4d4;">.</span><span style="color: #4ec9b0;">BooleanField</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">"donotemail"</span><span style="color: #d4d4d4;">);</span><br /> <span style="color: #d4d4d4;">        </span><span style="color: #9cdcfe;">Birthday</span><span style="color: #d4d4d4;"> = </span><span style="color: #569cd6;">new</span><span style="color: #d4d4d4;"> </span><span style="color: #4ec9b0;">XrmEx</span><span style="color: #d4d4d4;">.</span><span style="color: #4ec9b0;">Class</span><span style="color: #d4d4d4;">.</span><span style="color: #4ec9b0;">DateField</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">"birthdate"</span><span style="color: #d4d4d4;">);</span><br /> <span style="color: #d4d4d4;">        </span><span style="color: #9cdcfe;">PreferredContactMethod</span><span style="color: #d4d4d4;"> = </span><span style="color: #569cd6;">new</span><span style="color: #d4d4d4;"> </span><span style="color: #4ec9b0;">XrmEx</span><span style="color: #d4d4d4;">.</span><span style="color: #4ec9b0;">Class</span><span style="color: #d4d4d4;">.</span><span style="color: #4ec9b0;">OptionsetField</span><span style="color: #d4d4d4;">(</span><br /> <span style="color: #d4d4d4;">            </span><span style="color: #ce9178;">"preferredcontactmethodcode"</span><span style="color: #d4d4d4;">,</span><br /> <span style="color: #d4d4d4;">            {</span><br /> <span style="color: #d4d4d4;">                </span><span style="color: #9cdcfe;">Any</span><span style="color: #9cdcfe;">:</span><span style="color: #d4d4d4;"> </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">,</span><br /> <span style="color: #d4d4d4;">                </span><span style="color: #9cdcfe;">Email</span><span style="color: #9cdcfe;">:</span><span style="color: #d4d4d4;"> </span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">,</span><br /> <span style="color: #d4d4d4;">                </span><span style="color: #9cdcfe;">Phone</span><span style="color: #9cdcfe;">:</span><span style="color: #d4d4d4;"> </span><span style="color: #b5cea8;">3</span><span style="color: #d4d4d4;">,</span><br /> <span style="color: #d4d4d4;">                </span><span style="color: #9cdcfe;">Fax</span><span style="color: #9cdcfe;">:</span><span style="color: #d4d4d4;"> </span><span style="color: #b5cea8;">4</span><span style="color: #d4d4d4;">,</span><br /> <span style="color: #d4d4d4;">                </span><span style="color: #9cdcfe;">Mail</span><span style="color: #9cdcfe;">:</span><span style="color: #d4d4d4;"> </span><span style="color: #b5cea8;">5</span><span style="color: #d4d4d4;">,</span><br /> <span style="color: #d4d4d4;">            }</span><br /> <span style="color: #d4d4d4;">        );</span><br /> <span style="color: #d4d4d4;">    }</span><br /> <span style="color: #d4d4d4;">    </span><span style="color: #569cd6;">class</span><span style="color: #d4d4d4;"> </span><span style="color: #4ec9b0;">Tabs</span><span style="color: #d4d4d4;"> {</span><br /> <span style="color: #d4d4d4;">        </span><span style="color: #9cdcfe;">General</span><span style="color: #d4d4d4;"> = </span><span style="color: #569cd6;">new</span><span style="color: #d4d4d4;"> </span><span style="color: #4ec9b0;">XrmEx</span><span style="color: #d4d4d4;">.</span><span style="color: #4ec9b0;">Class</span><span style="color: #d4d4d4;">.</span><span style="color: #4ec9b0;">Tab</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">"tab1"</span><span style="color: #d4d4d4;">, {</span><br /> <span style="color: #d4d4d4;">            </span><span style="color: #9cdcfe;">Section1</span><span style="color: #9cdcfe;">:</span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">new</span><span style="color: #d4d4d4;"> </span><span style="color: #4ec9b0;">XrmEx</span><span style="color: #d4d4d4;">.</span><span style="color: #4ec9b0;">Class</span><span style="color: #d4d4d4;">.</span><span style="color: #4ec9b0;">Section</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">"section1"</span><span style="color: #d4d4d4;">),</span><br /> <span style="color: #d4d4d4;">            </span><span style="color: #9cdcfe;">Section2</span><span style="color: #9cdcfe;">:</span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">new</span><span style="color: #d4d4d4;"> </span><span style="color: #4ec9b0;">XrmEx</span><span style="color: #d4d4d4;">.</span><span style="color: #4ec9b0;">Class</span><span style="color: #d4d4d4;">.</span><span style="color: #4ec9b0;">Section</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">"section2"</span><span style="color: #d4d4d4;">),</span><br /> <span style="color: #d4d4d4;">        });</span><br /> <span style="color: #d4d4d4;">        </span><span style="color: #9cdcfe;">Details</span><span style="color: #d4d4d4;"> = </span><span style="color: #569cd6;">new</span><span style="color: #d4d4d4;"> </span><span style="color: #4ec9b0;">XrmEx</span><span style="color: #d4d4d4;">.</span><span style="color: #4ec9b0;">Class</span><span style="color: #d4d4d4;">.</span><span style="color: #4ec9b0;">Tab</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">"tab2"</span><span style="color: #d4d4d4;">, {</span><br /> <span style="color: #d4d4d4;">            </span><span style="color: #9cdcfe;">Section1</span><span style="color: #9cdcfe;">:</span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">new</span><span style="color: #d4d4d4;"> </span><span style="color: #4ec9b0;">XrmEx</span><span style="color: #d4d4d4;">.</span><span style="color: #4ec9b0;">Class</span><span style="color: #d4d4d4;">.</span><span style="color: #4ec9b0;">Section</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">"section1"</span><span style="color: #d4d4d4;">),</span><br /> <span style="color: #d4d4d4;">            </span><span style="color: #9cdcfe;">Section2</span><span style="color: #9cdcfe;">:</span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">new</span><span style="color: #d4d4d4;"> </span><span style="color: #4ec9b0;">XrmEx</span><span style="color: #d4d4d4;">.</span><span style="color: #4ec9b0;">Class</span><span style="color: #d4d4d4;">.</span><span style="color: #4ec9b0;">Section</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">"section2"</span><span style="color: #d4d4d4;">),</span><br /> <span style="color: #d4d4d4;">        });</span><br /> <span style="color: #d4d4d4;">    }</span><br /> <span style="color: #d4d4d4;">    </span><span style="color: #569cd6;">class</span><span style="color: #d4d4d4;"> </span><span style="color: #4ec9b0;">Grids</span><span style="color: #d4d4d4;"> {</span><br /> <span style="color: #d4d4d4;">        </span><span style="color: #9cdcfe;">ContactSubgrid</span><span style="color: #d4d4d4;"> = </span><span style="color: #569cd6;">new</span><span style="color: #d4d4d4;"> </span><span style="color: #4ec9b0;">XrmEx</span><span style="color: #d4d4d4;">.</span><span style="color: #4ec9b0;">Class</span><span style="color: #d4d4d4;">.</span><span style="color: #4ec9b0;">GridControl</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">"Test"</span><span style="color: #d4d4d4;">);</span><br /> <span style="color: #d4d4d4;">    }</span><br /> <span style="color: #d4d4d4;">    </span><span style="color: #6a9955;">/**</span><span style="color: #569cd6;">@type</span><span style="color: #6a9955;"> </span><span style="color: #4ec9b0;">{Fields}</span><span style="color: #6a9955;">*/</span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">var</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">fields</span><span style="color: #d4d4d4;">;</span><br /> <span style="color: #d4d4d4;">    </span><span style="color: #6a9955;">/**</span><span style="color: #569cd6;">@type</span><span style="color: #6a9955;"> </span><span style="color: #4ec9b0;">{Tabs}</span><span style="color: #6a9955;">*/</span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">var</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">tabs</span><span style="color: #d4d4d4;">;</span><br /> <span style="color: #d4d4d4;">    </span><span style="color: #6a9955;">/**</span><span style="color: #569cd6;">@type</span><span style="color: #6a9955;"> </span><span style="color: #4ec9b0;">{Grids}</span><span style="color: #6a9955;">*/</span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">var</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">grids</span><span style="color: #d4d4d4;">;</span><br /> <br /> <span style="color: #d4d4d4;">    </span><span style="color: #6a9955;">/**</span><br /> <span style="color: #6a9955;">     * </span><span style="color: #569cd6;">@param</span><span style="color: #6a9955;"> </span><span style="color: #4ec9b0;">{Xrm.FormContext | Xrm.Events.EventContext}</span><span style="color: #6a9955;"> </span><span style="color: #9cdcfe;">executionContext</span><span style="color: #6a9955;"> </span><br /> <span style="color: #6a9955;">     */</span><br /> <span style="color: #d4d4d4;">    </span><span style="color: #9cdcfe;">Self</span><span style="color: #d4d4d4;">.</span><span style="color: #dcdcaa;">OnLoad</span><span style="color: #d4d4d4;"> = </span><span style="color: #569cd6;">async</span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">function</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">OnLoad</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">executionContext</span><span style="color: #d4d4d4;">) {</span><br /> <span style="color: #d4d4d4;">        </span><span style="color: #c586c0;">await</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">Init</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">executionContext</span><span style="color: #d4d4d4;">); </span><span style="color: #6a9955;">//Ensures XrmEx is only accessed after the OnLoad Event</span><br /> <span style="color: #d4d4d4;">        </span><span style="color: #c586c0;">try</span><span style="color: #d4d4d4;"> {</span><br /> <span style="color: #d4d4d4;">            </span><span style="color: #9cdcfe;">fields</span><span style="color: #d4d4d4;">.</span><span style="color: #9cdcfe;">Firstname</span><span style="color: #d4d4d4;">.</span><span style="color: #9cdcfe;">Value</span><span style="color: #d4d4d4;"> = </span><span style="color: #ce9178;">"Joe"</span><span style="color: #d4d4d4;">;</span><br /> <span style="color: #d4d4d4;">            </span><span style="color: #9cdcfe;">fields</span><span style="color: #d4d4d4;">.</span><span style="color: #9cdcfe;">PreferredContactMethod</span><span style="color: #d4d4d4;">.</span><span style="color: #9cdcfe;">Value</span><span style="color: #d4d4d4;"> = </span><span style="color: #9cdcfe;">fields</span><span style="color: #d4d4d4;">.</span><span style="color: #9cdcfe;">PreferredContactMethod</span><span style="color: #d4d4d4;">.</span><span style="color: #9cdcfe;">Option</span><span style="color: #d4d4d4;">.</span><span style="color: #9cdcfe;">Phone</span><span style="color: #d4d4d4;">;</span><br /> <span style="color: #d4d4d4;">            </span><span style="color: #9cdcfe;">fields</span><span style="color: #d4d4d4;">.</span><span style="color: #9cdcfe;">Firstname</span><span style="color: #d4d4d4;">.</span><span style="color: #dcdcaa;">setVisible</span><span style="color: #d4d4d4;">(</span><span style="color: #569cd6;">true</span><span style="color: #d4d4d4;">).</span><span style="color: #dcdcaa;">setDisabled</span><span style="color: #d4d4d4;">(</span><span style="color: #569cd6;">true</span><span style="color: #d4d4d4;">).</span><span style="color: #dcdcaa;">setRequired</span><span style="color: #d4d4d4;">(</span><span style="color: #569cd6;">false</span><span style="color: #d4d4d4;">);</span><br /> <span style="color: #d4d4d4;">            </span><span style="color: #c586c0;">await</span><span style="color: #d4d4d4;"> </span><span style="color: #4ec9b0;">XrmEx</span><span style="color: #d4d4d4;">.</span><span style="color: #dcdcaa;">openAlertDialog</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">"Success"</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">"Xrm works."</span><span style="color: #d4d4d4;">);</span><br /> <span style="color: #d4d4d4;">        } </span><span style="color: #c586c0;">catch</span><span style="color: #d4d4d4;"> (</span><span style="color: #9cdcfe;">error</span><span style="color: #d4d4d4;">) {</span><br /> <span style="color: #d4d4d4;">            </span><span style="color: #9cdcfe;">console</span><span style="color: #d4d4d4;">.</span><span style="color: #dcdcaa;">error</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">error</span><span style="color: #d4d4d4;">);</span><br /> <span style="color: #d4d4d4;">            </span><span style="color: #c586c0;">await</span><span style="color: #d4d4d4;"> </span><span style="color: #4ec9b0;">XrmEx</span><span style="color: #d4d4d4;">.</span><span style="color: #dcdcaa;">openAlertDialog</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">"Error"</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">`Error in </span><span style="color: #569cd6;">${</span><span style="color: #4ec9b0;">XrmEx</span><span style="color: #d4d4d4;">.</span><span style="color: #dcdcaa;">getFunctionName</span><span style="color: #d4d4d4;">()</span><span style="color: #569cd6;">}</span><span style="color: #d7ba7d;">\n</span><span style="color: #ce9178;">`</span><span style="color: #d4d4d4;"> + </span><span style="color: #9cdcfe;">error</span><span style="color: #d4d4d4;">.</span><span style="color: #9cdcfe;">message</span><span style="color: #d4d4d4;">);</span><br /> <span style="color: #d4d4d4;">        }</span><br /> <span style="color: #d4d4d4;">    };</span><br /> <span style="color: #d4d4d4;">    </span><span style="color: #6a9955;">/**</span><br /> <span style="color: #6a9955;">     * </span><span style="color: #569cd6;">@param</span><span style="color: #6a9955;"> </span><span style="color: #4ec9b0;">{Xrm.FormContext | Xrm.Events.EventContext}</span><span style="color: #6a9955;"> </span><span style="color: #9cdcfe;">executionContext</span><span style="color: #6a9955;"> </span><br /> <span style="color: #6a9955;">     */</span><br /> <span style="color: #d4d4d4;">    </span><span style="color: #569cd6;">async</span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">function</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">Init</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">executionContext</span><span style="color: #d4d4d4;">) {</span><br /> <span style="color: #d4d4d4;">        </span><span style="color: #c586c0;">if</span><span style="color: #d4d4d4;"> (!</span><span style="color: #4ec9b0;">XrmEx</span><span style="color: #d4d4d4;">) {</span><br /> <span style="color: #d4d4d4;">            </span><span style="color: #569cd6;">let</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">errorMessage</span><span style="color: #d4d4d4;"> = </span><span style="color: #ce9178;">"XrmEx is not loaded. Please make sure you have XrmEx.js loaded in your form."</span><span style="color: #d4d4d4;">;</span><br /> <span style="color: #d4d4d4;">            </span><span style="color: #9cdcfe;">console</span><span style="color: #d4d4d4;">.</span><span style="color: #dcdcaa;">error</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">errorMessage</span><span style="color: #d4d4d4;">);</span><br /> <span style="color: #d4d4d4;">            </span><span style="color: #c586c0;">await</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">Xrm</span><span style="color: #d4d4d4;">.</span><span style="color: #9cdcfe;">Navigation</span><span style="color: #d4d4d4;">.</span><span style="color: #dcdcaa;">openAlertDialog</span><span style="color: #d4d4d4;">({ </span><span style="color: #9cdcfe;">title</span><span style="color: #9cdcfe;">:</span><span style="color: #d4d4d4;"> </span><span style="color: #ce9178;">"Error"</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">text</span><span style="color: #9cdcfe;">:</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">errorMessage</span><span style="color: #d4d4d4;">, });</span><br /> <span style="color: #d4d4d4;">            </span><span style="color: #c586c0;">return</span><span style="color: #d4d4d4;">;</span><br /> <span style="color: #d4d4d4;">        }</span><br /> <span style="color: #d4d4d4;">        </span><span style="color: #4ec9b0;">XrmEx</span><span style="color: #d4d4d4;">.</span><span style="color: #4ec9b0;">Form</span><span style="color: #d4d4d4;">.</span><span style="color: #9cdcfe;">formContext</span><span style="color: #d4d4d4;"> = </span><span style="color: #9cdcfe;">executionContext</span><span style="color: #d4d4d4;">;</span><br /> <span style="color: #d4d4d4;">        </span><span style="color: #9cdcfe;">fields</span><span style="color: #d4d4d4;"> = </span><span style="color: #569cd6;">new</span><span style="color: #d4d4d4;"> </span><span style="color: #4ec9b0;">Fields</span><span style="color: #d4d4d4;">();</span><br /> <span style="color: #d4d4d4;">        </span><span style="color: #9cdcfe;">tabs</span><span style="color: #d4d4d4;"> = </span><span style="color: #569cd6;">new</span><span style="color: #d4d4d4;"> </span><span style="color: #4ec9b0;">Tabs</span><span style="color: #d4d4d4;">();</span><br /> <span style="color: #d4d4d4;">        </span><span style="color: #9cdcfe;">grids</span><span style="color: #d4d4d4;"> = </span><span style="color: #569cd6;">new</span><span style="color: #d4d4d4;"> </span><span style="color: #4ec9b0;">Grids</span><span style="color: #d4d4d4;">();</span><br /> <span style="color: #d4d4d4;">        </span><span style="color: #9cdcfe;">parent</span><span style="color: #d4d4d4;">.</span><span style="color: #4fc1ff;">window</span><span style="color: #d4d4d4;">.</span><span style="color: #4ec9b0;">XrmEx</span><span style="color: #d4d4d4;"> = </span><span style="color: #4ec9b0;">XrmEx</span><span style="color: #d4d4d4;">;</span><br /> <span style="color: #d4d4d4;">    }</span><br /> <br /> <span style="color: #d4d4d4;">})(</span><span style="color: #9cdcfe;">YourNamespace</span><span style="color: #d4d4d4;">.</span><span style="color: #9cdcfe;">Contact</span><span style="color: #d4d4d4;">);</span></div> </div> <p><span style="white-space: normal">In the code above, you create a namespace for your code to live in and then wrap your code in a function to keep it organized and prevent global scope pollution.</span></p> <p>To deploy, follow the instructions provided in the video: <a href="https://github.com/AhashSritharan/Xrm-Ex#deployment">Xrm-Ex Deployment</a>. This is a detailed guide on how to deploy your project.</p> <p>Now, you'll want to add <code>XrmEx.js</code> from your <code>node_modules</code> to your Dynamics 365 form as a library. Then, execute the method <code>YourNamespace.ContactFunctions.OnLoad</code> in your form and pass the <code>executionContext</code> to that function</p> <p>And that's it! You've now installed Xrm-Ex, set up your project, and deployed it to Dynamics 365. You're ready to start coding and enjoying a simplified interaction with the formContext and the Xrm Object.</p> <div class="flex-col gap-1 md:gap-3"> <div class="flex flex-grow flex-col gap-3 max-w-full"> <div class="min-h-[20px] flex flex-col items-start gap-3 whitespace-pre-wrap break-words overflow-x-auto" data-message-author-role="assistant" data-message-id="32f50512-68cf-457d-9767-89777e8ea4e4"> <div class="result-streaming markdown prose w-full break-words dark:prose-invert dark"> <h2>Core Features</h2> <p>Xrm-Ex comes packed with features that make working with Dynamics 365 Client API a lot easier. Here are some of the core features:</p> <ul> <li> <p><strong>Event Handling</strong>:</p> <ul> <li>Xrm-Ex makes it simple to manage events in Dynamics 365.</li> <li>Without Xrm-Ex: You would need to write more code to handle events.</li> <li>With Xrm-Ex: Event handling becomes straightforward, reducing your code.</li> </ul> </li> <li> <p><strong>Field Changes and Events</strong>:</p> <ul> <li>Tracking and managing field changes and events is easier with Xrm-Ex.</li> <li>Without Xrm-Ex: It could be time-consuming to track field changes and events.</li> <li>With Xrm-Ex: The process is streamlined, saving you time.</li> </ul> </li> <li> <p><strong>Form Types and Field Requirements</strong>:</p> <ul> <li>Xrm-Ex helps manage form types and field requirements without hassle.</li> <li>Without Xrm-Ex: Managing form types and field requirements can be complex.</li> <li>With Xrm-Ex: It becomes a simpler task, making your work quicker.</li> </ul> </li> <li> <p><strong>Data Retrieval and Setting</strong>:</p> <ul> <li>Retrieving and setting data in Dynamics 365 is simplified with Xrm-Ex.</li> <li>Without Xrm-Ex: These tasks might require more code and time.</li> <li>With Xrm-Ex: The process is quicker and requires less code.</li> </ul> </li> <li> <p><strong>Alert Dialogs</strong>:</p> <ul> <li>Managing alert dialogs in Dynamics 365 is straightforward with Xrm-Ex.</li> <li>Without Xrm-Ex: It could be challenging to handle alert dialogs.</li> <li>With Xrm-Ex: It’s a straightforward task, improving your user interaction.</li> </ul> </li> <li> <p><strong>Advanced Features</strong>:</p> <ul> <li><strong>Lookup Filters</strong>: Easier management of lookup filters.</li> <li><strong>Advanced Lookup Filter</strong>: Supports entire FetchXml including Link-Entity, making it special.</li> <li><strong>Execute Bound Action</strong>: Simplified bound action execution.</li> <li><strong>Retrieve EnvironmentVariableValue</strong>: Easier retrieval of environment variable values.</li> </ul> </li> </ul> <p>Each of these features aims to reduce the amount of code you need to write, making your interactions with Dynamics 365 Client API more straightforward and less error-prone. For a deeper dive into these features, check out the <a href="https://xrm-ex.ahash.dev/modules/src_XrmEx.XrmEx.html">Xrm-Ex documentation</a>. By providing a clear and simple way to handle common tasks and actions within Dynamics 365, Xrm-Ex helps to speed up the development process and reduce the likelihood of errors, making your project more efficient and reliable.</p> </div> </div> </div> </div> <h2>Real-world Usage</h2> <p>Xrm-Ex proves to be a handy tool in real-world scenarios where making interactions with Dynamics 365 Client API simpler is crucial. Here are a few examples:</p> <ul> <li> <p><strong>Complex Projects</strong>:</p> <ul> <li>In projects with lots of moving parts, where many field changes and events need to be tracked, Xrm-Ex can cut down the code complexity and make things easier to manage.</li> </ul> </li> <li> <p><strong>Tight Deadlines</strong>:</p> <ul> <li>When working on tight deadlines, the ease of setting up and deploying projects with Xrm-Ex can be a big time-saver. It helps get the basics in place quickly so developers can focus on building the needed functionality.</li> </ul> </li> <li> <p><strong>Maintaining Clean Code</strong>:</p> <ul> <li>Xrm-Ex helps in keeping the codebase clean and well-organized. By reducing the amount of code needed for common tasks, it helps prevent bugs and makes the code easier to read and maintain.</li> </ul> </li> <li> <p><strong>Learning Curve</strong>:</p> <ul> <li>For developers new to Dynamics 365, Xrm-Ex can reduce the learning curve. Its straightforward features and easy setup can help newcomers get up to speed quicker.</li> </ul> </li> </ul> <p>These examples show how Xrm-Ex can be a practical choice in various real-world scenarios, making the development process smoother and helping to deliver projects on time and with fewer issues.</p> <h2>Community and Contribution</h2> <p>The Xrm-Ex community welcomes contributions from developers. You can find the project on GitHub <a href="https://github.com/AhashSritharan/Xrm-Ex">here</a> and check out the contribution guidelines to see how you can help improve Xrm-Ex:</p> <ul> <li> <p><strong>Contribution Guidelines</strong>:</p> <ul> <li>There are clear guidelines provided for those who want to contribute. Following these guidelines helps ensure that contributions align well with the project’s goals.</li> </ul> </li> <li> <p><strong>Learning and Collaboration</strong>:</p> <ul> <li>Being a part of the Xrm-Ex community gives developers a chance to learn from others, share knowledge, and work together on improving the framework.</li> </ul> </li> <li> <p><strong>Feedback and Improvement</strong>:</p> <ul> <li>Developers can provide feedback on any issues they encounter or suggest improvements. This feedback is valuable for the continuous improvement of Xrm-Ex.</li> </ul> </li> <li> <p><strong>Shared Resources</strong>:</p> <ul> <li>Community members often share resources, like documentation or solutions to common problems, which can be a big help to others working with Xrm-Ex.</li> </ul> </li> <li> <p><strong>Community Support</strong>:</p> <ul> <li>Having a community to turn to for support or questions can be a great benefit, especially when facing challenging issues in a project.</li> </ul> </li> </ul> <p>Being a part of the Xrm-Ex community not only allows developers to contribute to a useful framework but also provides a supportive environment for learning and improving one’s skills in working with Dynamics 365. The collective effort and shared knowledge within the community contribute to making Xrm-Ex a reliable and continuously evolving tool for developers.</p> <h2>Conclusion</h2> <p>Xrm-Ex is a solid framework that helps tackle common challenges developers face when working with Dynamics 365 Client API. It's easy to install, has a lot of useful features, and comes with the backing of a supportive community. These elements make it a good choice for developers looking to make their Dynamics 365 projects easier to manage and less prone to errors.</p> <p>By simplifying interactions with the formContext and the Xrm Object, Xrm-Ex helps reduce the amount of code needed, which in turn makes the codebase easier to handle. This ease of use, combined with the helpful features it offers, allows developers to focus more on building the functionality needed for their projects.</p> <p>For anyone looking to boost their productivity and the quality of their Dynamics 365 applications, diving into Xrm-Ex and exploring what it has to offer is a step in the right direction. The benefits it provides in streamlining development processes are clear, making it a worthy addition to a developer's toolkit for Dynamics 365 projects.</p> <h2 class="title">Enhancing Dynamics 365 Client API Interaction with Xrm-Ex</h2> </div> </div> <div class="field field--name-field-image field--type-image field--label-above"> <div class="field__label">Image</div> <div class="images-container clearfix"> <div class="image-preview clearfix"> <div class="image-wrapper clearfix"> <div class="field field--name-field-image field--type-image field--label-above field__item">/sites/default/files/2023-10/2023-10-29%2017_43_25-%E2%97%8F%20demo.js%20-%20XrmExDemo%20-%20Visual%20Studio%20Code%20-%20Insiders.png</div> </div> </div> </div> </div> Mon, 30 Oct 2023 16:34:43 +0000 Ahash 720 at https://dynamics-chronicles.com Power Apps Programming Tools Tutorial https://dynamics-chronicles.com/article/enhancing-dynamics-365-client-api-interaction-xrm-ex#comments