Deep Dive https://dynamics-chronicles.com/ en Dataverse: Duplicate Detection Rules vs Alternate Keys https://dynamics-chronicles.com/article/dataverse-duplicate-detection-rules-vs-alternate-keys <span property="schema:name" class="field field--name-title field--type-string field--label-hidden">Dataverse: Duplicate Detection Rules vs Alternate Keys</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-08T15:57:17+00:00" class="field field--name-created field--type-created field--label-hidden">Wed, 10/08/2025 - 17:57</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>Having clean data in Dataverse can be tricky. One of the challenges is detecting and managing duplicate records.<br /> With out-of-the-box tools you can implement duplicate detection rules or create alternate keys.<br /> If you are a developer, you can implement a custom validation in a plugin.<br /> In this article we will focus on out-of-the-box features (duplicate detection rules and alternate keys) and compare them.</p> <h3>Duplicate detection rules</h3> <p>First step is to check if the feature <em>Duplicate Detection</em> is enabled in your environment (it is enabled by default):</p> <ol> <li>Connect to Dynamics 365 and open <strong>Power Platform Environment Settings </strong>app</li> <li>Click on <strong>Data Management</strong> on the left pane and select <strong>Duplicate detection settings<br /> <img alt="Enable1" data-entity-type="file" data-entity-uuid="82de77bd-935c-4901-8e51-323c559ac4e7" src="/sites/default/files/inline-images/enable%201_2.png" /></strong><br />  </li> <li>Check that the option <strong>Enable duplicate detection</strong> is enabled<br /> <img alt="Enable2" data-entity-type="file" data-entity-uuid="29cd6636-f3cb-4b3a-8a4b-113f4c280994" src="/sites/default/files/inline-images/enable%202_1.png" /><br /> <br /> There are 3 triggers that you can activate:</li> </ol> <ul> <li>When a record is created or updated</li> <li>When Microsoft Dynamics 365 for Outlook goes from offline to online</li> <li>During data import</li> </ul> <p>Second step is to make sure the feature is enabled for your table:</p> <ol> <li>Open <a href="https://make.powerapps.com/">Power Apps Portal</a> and select your environment</li> <li>Select your table on the left pane and click on <strong>Properties<br /> <img alt="Enable3" data-entity-type="file" data-entity-uuid="b063d228-a864-4888-984c-3c98f81a79f1" src="/sites/default/files/inline-images/enable%203.png" /></strong><br />  </li> <li>Expand <strong>Advanced options</strong> pane and ensure that <strong>Apply duplicate detection rules</strong> is checked<br /> <img alt="Enable4" data-entity-type="file" data-entity-uuid="3a0e8ef4-f0d5-488f-b539-5a63673485d7" src="/sites/default/files/inline-images/enable%204_0.png" /></li> </ol> <p>Then you can create duplicate detection rules:</p> <ol> <li>Connect to Dynamics 365 and open<strong> Power Platform Environment Settings </strong>app</li> <li>Click on <strong>Data Management</strong> on the left pane and select <strong>Duplicate detection rules<br /> <img alt="rule1" data-entity-type="file" data-entity-uuid="39c290ff-cd34-416d-8e94-472057d321ed" src="/sites/default/files/inline-images/rule1_0.png" /></strong><br />  </li> <li>All out-of-the-box duplicate detection rules are displayed<br /> <img alt="rule2" data-entity-type="file" data-entity-uuid="ebc76ed5-f1b1-4038-9fdf-ef7aef026f24" src="/sites/default/files/inline-images/rule2_1.png" /><br />  </li> <li>Click on <strong>New </strong>on the command bar<br /> Below an example with the out-of-the-box rule <em>Contacts with the same first name and last name </em>for Contact table<br /> <img alt="rule3" data-entity-type="file" data-entity-uuid="99d8e059-d2be-45da-b97b-3e4c57da25f4" src="/sites/default/files/inline-images/rule3_0.png" /><br /> <br /> Provide a name and description for the rule and define the criteria for identifying duplicates:</li> </ol> <ul> <li><strong>Base Record Type</strong>: select the type of record the rule applies to (e.g., Contacts).</li> <li><strong>Matching Record Type</strong>: select the type of record to compare. Typically, this is the same as the base record type, but you can compare different types (e.g., Contacts and Leads).</li> <li><strong>Exclude Inactive Records</strong>: check this box if you want to exclude inactive records from the duplicate detection process.</li> <li><strong>Case-Sensitive</strong>: check this box if the rule should be case-sensitive.</li> <li><strong>Field Selection</strong>: for each criterion, select the fields to compare and the operator (e.g., Exact Match).</li> <li>I<strong>gnore Blank Values</strong>: check this box if you do not want the rule to consider blank fields as equal.<br type="_moz" /> <br /> You can add several criterions to your rule criteria:<br /> For text fields you have 3 choices:<br /> - <em>Exact Match</em><br /> - <em>Same First Characters</em><br /> - <em>Same Last Characters</em><br /> For Datetime fields you have 2 choices:<br /> - <em>Same date</em><br /> - <em>Same date and time</em><br /> For whole number/decimal and lookup fields only <em>Exact Match</em> is available</li> </ul> <p>      5. After configuring the criteria, click on <strong>Save</strong>.<br />           To make the rule active, select it and click on <strong>Publish</strong>.<br />           Note that you can only publish up to five rules for the same base record type at a time.</p> <p>Now let's try creating a duplicate record using Dynamics 365 UI. For example a contact with existing first name and last name.<br /> You should see a pop-up dialog with the warning message <em>Duplicate records found</em>.<br /> <img alt="rule4" data-entity-type="file" data-entity-uuid="2b2bc889-8f1c-4e1a-b9eb-c9475edd7e12" src="/sites/default/files/inline-images/rule4_0.png" /></p> <p>You have 3 possibilities:</p> <ul> <li><strong>Ignore and save</strong> : if you are certain the record is unique or requires a new entry despite the match, click "Ignore and Save" to create the duplicate</li> <li><strong>Merge </strong>: if you want to combine the information from both records into a single, primary record, select "Merge." You will typically be prompted to choose which record will be the primary one, and then you can select which fields to keep from each record</li> <li><strong>Cancel </strong>: do not create the duplicate record</li> </ul> <h3>Alternate keys</h3> <p>An alternative to duplicate detection rule is to create alternate keys.<br /> This method is the same as creating unique-index(es) from the database perspective. Hence this method will be more strict compared to duplicate detection rules.<br /> An alternate key can combine one or several columns.<br /> It can contain multiple rows with NULL values. NULL values are not indexed.</p> <p>Some limitations:</p> <ul> <li>There can be a maximum of 10 alternate keys for a table in a Dataverse instance</li> <li>Columns used in the key must not have field-level security applied</li> <li>Alternate keys aren't supported in virtual tables</li> <li>When a key is created, the system validates the key, including that the total key size doesn't violate SQL-based index constraints like 900 bytes per key and 16 columns per key. If the key size doesn't meet the constraints, an error message is displayed</li> <li>Unicode characters in key value. If the data within a column that is used in an alternate key contains one of the following characters /,&lt;,&gt;,*,%,&amp;,:,\\,?,+ then retrieve (GET), update or upsert (PATCH) actions won't work. If you only need uniqueness, then this approach works, but if you need to use these keys as part of data integration then it's best to create the key on columns that won't have data with those characters</li> </ul> <p>To create an alternate key 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 into which you want to create an alternate key</li> <li>Click on <strong>Keys</strong></li> <li>Provide a display name and select the columns to include in your key<br /> In the example below I create an alternate key in table <em>Contact </em>for columns birthday and last name<img alt="key2" data-entity-type="file" data-entity-uuid="420b5f90-3b7b-492f-b551-2310c5edbee1" src="/sites/default/files/inline-images/key2_4.png" /> <p>Only columns of the following types can be included in alternate key table definitions:<br />     - Decimal Number<br />     - Whole Number<br />     - Single line of text<br />     - Date Time<br />     - Lookup<br />     - Option Set</p> </li> <li> <p>Click on <strong>Save</strong>. The system will automatically create the index in the database.<br /> If the system detects duplicate data in the table, the creation will fail and you need to delete data first.<br /> If there are lots of existing records in your table, index creation can be a lengthy process.<br /> During creation of the key status will be <em>Pending</em>. Once the creation is complete status changes to <em>Active</em>.<br /> <img alt="key3" data-entity-type="file" data-entity-uuid="0febdf8e-6580-49ef-ad78-428a12defaf2" src="/sites/default/files/inline-images/key3_2.png" /></p> </li> </ol> <p>Now let's try creating a duplicate record using Dynamics 365 UI. To be consistent with the alternate key created previously, a contact with existing birthday and last name.<br /> You should see a pop-up error with the message <em>Duplicate record</em>.</p> <p><img alt="Key 4" data-entity-type="file" data-entity-uuid="efbb6b10-e151-47cf-801c-7fb645d4e9f4" src="/sites/default/files/inline-images/key4_0.png" /></p> <p>Therefore, it is impossible to create a duplicate record with the user interface. This makes sense as uniqueness is ensured at the database level.</p> <h3>Creating/Updating records programmatically</h3> <p>We have seen that behavior is different when we create a duplicate record with the UI using duplicate detection rules or alternate keys.<br /> But what happens if we create duplicate records programmatically with Dynamics 365 SDK or Web API?</p> <h4>Duplicate detection rules</h4> <p>By default duplicate detection rules are not applied when you are creating or updating records using Web API or Dynamics 365 API. Therefore duplicate records will be created.<br /> If you want Dynamics 365 to raise an error you must utilize an optional parameter.<br /> With Web API use parameter <code>MSCRM.SuppressDuplicateDetection</code> in the header of your HTTP request (<em>POST </em>to create a record or <em>PATCH </em>to update a record)  to avoid creation of a duplicate record of an existing record.<br /> The value assigned to <code>MSCRM.SuppressDuplicateDetection</code> determines whether the Create or Update operation can be completed:</p> <ul> <li><em>true</em>: Create or update the record, if a duplicate is found.</li> <li><em>false</em>:  Do not create or update the record, if a duplicate is found.</li> </ul> <p>So ensure that <code>MSCRM.SuppressDuplicateDetection</code> is set to <em>false </em>in your Web API request to detect duplicates.<br /> You will get the following error message if you try to create a duplicate record:</p> <p><em>{<br />   "error": {<br />     "code": "0x80040333",<br />     "message": "A record was not created or updated because a duplicate of the current record already exists."<br />   }<br /> }</em></p> <p>Note that if you do not use <code>MSCRM.SuppressDuplicateDetection </code>duplicates will be created.<br /> Below an example of POST request to create a contact named "John Doe" with activation of duplicate detection rules.</p> <pre> <code class="language-http">POST [Organization URI]/org1/api/data/v9.2/contacts If-None-Match: null OData-Version: 4.0 OData-MaxVersion: 4.0 Content-Type: application/json Accept: application/json MSCRM.SuppressDuplicateDetection: false { "firstname":"John", "lastname":"Doe" }</code></pre> <p>And below an example performed with XrmToolbox plugin <em>WebAPI Launcher.</em><br /> Note parameter <code>MSCRM.SuppressDuplicateDetection </code>in the header.</p> <p><img alt="Prog 1" data-entity-type="file" data-entity-uuid="a80f712c-01c1-45fe-9ce2-42ea21edc56f" src="/sites/default/files/inline-images/Prog1.png" /></p> <p>And the result of this Web API request if we try to create a duplicate:<br /> <img alt="Prog 2" data-entity-type="file" data-entity-uuid="49f87afd-9a4c-408b-9717-5cea346adfb2" src="/sites/default/files/inline-images/Prog2.png" /></p> <p>With Dynamics 365 SDK you have to use <code>SuppressDuplicateDetection</code> optional parameter in your request (<em>CreateRequest </em>or <em>UpdateRequest</em>) if you want the configured duplicate detection rules to run and throw an exception while creating or updating a record.<br /> So set its value to <em>false </em>in your code to trigger duplicate detection rules.<br /> You will get the following error if you try to create a duplicate record:<br /> <em>A record was not created or updated because a duplicate of the current record already exists</em></p> <p>Below a C# example to create with the SDK a contact named "John Doe" with activation of duplicate detection rules.</p> <pre> <code class="language-cs">namespace CrmDuplicateDetection { class Program { static void Main(string[] args) { CrmServiceClient.MaxConnectionTimeout = TimeSpan.FromHours(3); var connectionString = WebConfigurationManager.AppSettings["connectionString"]; var client = new CrmServiceClient(connectionString); var contact = new Entity("contact") { ["firstname"] = "John", ["lastname"] = "Doe" }; var createRequest = new CreateRequest { Target = contact }; // Pass parameter 'SuppressDuplicateDetection' to trigger Duplicate Detection Rules createRequest["SuppressDuplicateDetection"] = false; var response = (CreateResponse)client.Execute(createRequest); } } }</code></pre> <h4>Alternate keys</h4> <p>If you try to create a duplicate record programmatically with Dynamics 365 SDK you will get an error even if you set parameter <code>SuppressDuplicateDetection</code> to <em>false </em>in your <em>CreateRequest</em>/<em>UpdateRequest </em>request (and same if you set <code>MSCRM.SuppressDuplicateDetection </code>to <em>false </em>in the header of your Web API request).</p> <p>Below an example of Web API POST request to create a contact with last name and birthdate that already exist in Dataverse.</p> <p><img alt="Prog3" data-entity-type="file" data-entity-uuid="4e4b2b72-300c-4482-aeec-429c11f598ca" src="/sites/default/files/inline-images/Prog3.png" /></p> <h3>Conclusion</h3> <p>As we have seen alternate keys are a strict way to avoid duplicates creation and ensure data correctness.<br /> It is not possible to create duplicate records using Dynamics 365 user interface or Web API/Dynamics 365 SDK.<br /> But alternate keys have some limitations (10 alternate keys max per table, not supported in virtual tables, cannot include columns with field level security enabled, no operator Same First/Last characters in the criteria).<br /> Duplicate detection rules are more flexible when creating duplicate records through the UI (ability to merge records or force the creation of duplicates).<br /> And with Web API/Dynamics 365 SDK you can choose whether you want to apply duplicate detection rules when creating/updating records.<br /> So the choice between duplicate detection rules and alternate keys will depend on whether you want to allow duplicates creation under certain circumstances.<br /> If you never want to create duplicate records (either through the user interface or programmatically) alternate keys are a good solution because the check is done at database level.<br /> But if you want to warn users when they try to create a duplicate record in the UI and allow them to confirm or not the duplicate creation (and use <em>Merge </em>feature) duplicate detection rules are a good choice.<br /> The same is true if you have interfaces between a third-party system and Dynamics 365 (an Azure webjob or Azure function for instance) and want to allow or not creation of duplicates.<br /> But if you have an existing interface that allows the creation of duplicate records and want to change this behavior, you will have to modify the source code of the interface to use <code>SuppressDuplicateDetection</code> parameter to trigger duplicate detection rules.</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_26_53%20PM.png</div> </div> </div> </div> </div> Wed, 08 Oct 2025 15:57:17 +0000 Stephane Pelhatre 860 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 / Dynamics 365 : Azure Managed Identities from Plugins. Step by Step. https://dynamics-chronicles.com/article/dataverse-dynamics-365-azure-managed-identities-plugins-step-step <span property="schema:name" class="field field--name-title field--type-string field--label-hidden">Dataverse / Dynamics 365 : Azure Managed Identities from Plugins. Step by Step. </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="2025-07-17T12:47:18+00:00" class="field field--name-created field--type-created field--label-hidden">Thu, 07/17/2025 - 14:47</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>Introduction</h2> <p>The days of storing Client ID and secret of your Key Vault in Dynamics are coming to an end. With <a href="https://learn.microsoft.com/en-us/power-platform/admin/managed-identity-overview">Power Platform Managed Identity</a>, Dataverse plug-ins can now <strong>connect to Azure resources that support managed identity without the need for credentials</strong>. Please note that at the time of writing this article, this feature is still in preview and is not intended for production use.</p> <p>This marks a significant improvement in security and access management, eliminating the risks associated with storing credentials while simplifying integration between Dataverse and Azure. In this article, we’ll explore how this feature works and the process for implementing it.</p> <h2>How to implement it</h2> <p>Microsoft’s documentation is missing some details that can make the configuration process challenging. In this article, I’ll provide a clear, step-by-step guide with practical examples to help you set everything up quickly and efficiently. My goal is to simplify the process and ensure you have a smooth experience getting it to work. </p> <h3>Step 1 : Create a new app registration or user-assigned managed identity</h3> <ol> <li>Create new "User Assigned Managed Identity". You can follow this <a href="https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/how-manage-user-assigned-managed-identities?pivots=identity-mi-methods-azp#create-a-user-assigned-managed-identity">documentation</a> to create it.</li> </ol> <p><img alt="Create User Assigned Managed Identity" data-entity-type="file" data-entity-uuid="225ecf33-42a1-4fb3-bece-9574ec494fc5" src="/sites/default/files/inline-images/Create%20User%20Assigned%20Managed%20Identity.png" /></p> <ol start="2"> <li>Copy and save the "Client ID" value of your managed identity to be used in step 6</li> </ol> <p><img alt="Client ID managed identity" data-entity-type="file" data-entity-uuid="c17834d3-195e-4ae9-91ba-e1b4a05252f2" src="/sites/default/files/inline-images/managed_identity_2.png" /></p> <h3>Step 2 : Create Key Vault</h3> <ol> <li>Create a key vault</li> <li>Create a secret with name "DynamicsChroniclesSecret" and with secret value = "Managed identity for Power Platform is working"</li> <li>In "Access control (IAM)" of your key vault, add the role assignment "Key Vault Secrets User" to the User Assigned Managed Identity created in step 1</li> </ol> <h3>Step 3 : Create a certificate</h3> <ol> <li>Open Powershell to create a certificate using this script</li> </ol> <pre> <code># Generate a self-signed certificate $cert = New-SelfSignedCertificate -Subject "CN=dynamicschronicles, O=corp, C=dynamicschronicles.ch" -DnsName "www.dynamicschronicles.ch" -Type CodeSigning -KeyUsage DigitalSignature -CertStoreLocation Cert:\CurrentUser\My -FriendlyName "dynamicschronicles" # Set a password for the private key $pw = ConvertTo-SecureString -String "complexpwd12345" -Force -AsPlainText # Export the certificate as a PFX file Export-PfxCertificate -Cert $cert -FilePath 'C:\temp\dynamicschronicles_certificate.pfx' -Password $pw Write-Host "Self-signed certificate exported as C:\temp\dynamicschronicles_certificate.pfx." # Display thumbprint $cert.Thumbprint </code></pre> <ol start="2"> <li>Copy and save the "Thumbprint" value to be used in step 5</li> </ol> <p><img alt="Thumbprint" data-entity-type="file" data-entity-uuid="e5b3e636-8b63-47f9-9b6b-0a40dc76586c" src="/sites/default/files/inline-images/thumbprint1.png" /></p> <h3>Step 4 : Create a plug-in assembly, sign it and register it</h3> <ol> <li>Create a <a href="https://learn.microsoft.com/en-us/power-apps/developer/data-platform/tutorial-write-plug-in">new plug-in using Visual Studio</a>. Here is an example of code :</li> </ol> <pre> <code class="language-cs">using Microsoft.Xrm.Sdk; using System; using System.Collections.Generic; using System.Net.Http; using System.Net.Http.Headers; namespace ManagedPlugin { public class ManagedPlugin : IPlugin { public void Execute(IServiceProvider serviceProvider) { // Obtain the tracing service ITracingService tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService)); // Obtain the execution context from the service provider. IManagedIdentityService managedIdentityService = (IManagedIdentityService) serviceProvider.GetService(typeof(IManagedIdentityService)); // Obtain the execution context from the service provider. IPluginExecutionContext context = (IPluginExecutionContext) serviceProvider.GetService(typeof(IPluginExecutionContext)); // Initialize empty token string token = string.Empty; string[] scopes = new string[] { "https://YOUR-DYNAMICS-ORG-URL.crm4.dynamics.com/.default" }; try { tracingService.Trace("Scopes in GetAccessTokenManagedIdentity: " + string.Join(", ", scopes)); token = managedIdentityService.AcquireToken(scopes); tracingService.Trace($"MANAGED IDENTITY TOKEN : {token}"); } catch (Exception ex) { tracingService.Trace($"Failed to acquire token {ex.Message}"); } var keyvaultname = "YOUR-KEYVAULT-NAME"; var secretname = "DynamicsChroniclesSecret"; // Get Token var kv_scopes = new List&lt;string&gt; { "https://vault.azure.net/.default" }; var kv_token = managedIdentityService.AcquireToken(kv_scopes); tracingService.Trace($"KV TOKEN : {kv_token}"); var keyvaultsecretUrl = $"https://{keyvaultname}.vault.azure.net/secrets/{secretname}?api-version=7.4"; try { using (HttpClient client = new HttpClient()) { client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", kv_token); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); var request = new HttpRequestMessage(HttpMethod.Get, new Uri(keyvaultsecretUrl)); var response = client.SendAsync(request).Result; string json = response.Content.ReadAsStringAsync().Result; var keyVaultResponse = System.Text.Json.JsonSerializer.Deserialize&lt;KeyVaultResponse&gt;(json); tracingService.Trace($"KV SECRET VALUE : {keyVaultResponse.value}"); } } catch (Exception ex) { tracingService.Trace($"Failed to acquire token {ex.Message}"); } } } } public class KeyVaultResponse { public string value { get; set; } public string id { get; set; } } </code></pre> <ol start="2"> <li>Replace the following values with yours <ol> <li>Line 27 =&gt; "https://YOUR-DYNAMICS-ORG-URL.crm4.dynamics.com/.default"</li> <li>Line 40 =&gt; "YOUR-KEYVAULT-NAME" (name of the key vault created in step 2)</li> </ol> </li> <li>Build the plugin</li> <li>Sign the plugin using <a href="https://learn.microsoft.com/en-us/dotnet/framework/tools/signtool-exe">signtool</a> and the following Powershell command</li> </ol> <pre> <code>.\signtool sign /f "C:\temp\dynamicschronicles_certificate.pfx" /p "complexpwd12345" "C:\Path_to_your_plugin\bin\Debug\ManagedPlugin.dll"</code></pre> <ol> <li><a href="https://learn.microsoft.com/en-us/power-apps/developer/data-platform/tutorial-write-plug-in#register-plug-in">Register the plug-in</a> and register a new step (i.e. create of account)</li> </ol> <h3>Step 5 : Configure federated identity credentials</h3> <ol> <li>Open the User Assigned Managed Identity created in step 1</li> <li>Navigate to "Settings/Federated credentials" and click on "Add Credential"</li> </ol> <p><img alt="Federated Credentials" data-entity-type="file" data-entity-uuid="fb29314e-d517-45ab-b09c-9c7d8183cd89" src="/sites/default/files/inline-images/FederatedCredentials_0.png" /></p> <ol start="3"> <li>Select "Other" for "Federated credential scenario"</li> <li>Set "Issuer" = https://[Dynamics environment ID prefix].[Dynamics environment ID suffix].environment.api.powerplatform.com/sts <ol> <li>Replace "[Dynamics Environment ID prefix]" with your environment ID <span style="color:#c0392b;">(except the last two characters and without dashes)</span></li> <li>Replace "[Dynamics Environment ID suffix]" with the two last characters of the environment ID</li> </ol> </li> <li>Set "Subject identifier" = component:pluginassembly,thumbprint:[Thumbprint],environment:[Dynamics environment ID] <ol> <li>Replace "[Thumbprint]" with Thumbprint value copied in step 2 </li> <li>Replace "[Dynamics environment ID]" with your environment ID (with dashes)</li> </ol> </li> <li>Set "Name" to whatever you want</li> <li>For "Audience", you can keep the default value</li> </ol> <p><img alt="Environment ID" data-entity-type="file" data-entity-uuid="8e2ed5e8-d8d5-45c7-a3c6-e6e2cdb66053" src="/sites/default/files/inline-images/EnvironmentID.png" /></p> <p><img alt="Federated Credentials" data-entity-type="file" data-entity-uuid="0331c265-af66-4a9d-b593-c229f522bff8" src="/sites/default/files/inline-images/FederatedCredentials1_0.png" /></p> <ol start="8"> <li>Click on "Add" to create the Federated Credential</li> </ol> <h3>Step 6 : Create managed identity record in Dataverse</h3> <ol> <li>Open <a href="https://www.xrmtoolbox.com/">"XRMToolbox"</a> and open the tool <a href="https://www.xrmtoolbox.com/plugins/Driv.XTB.PluginIdentityManager/">"Plugin Identity Manager"</a> (you can also create it manually if you want, see documentation <a href="https://learn.microsoft.com/en-us/power-platform/admin/set-up-managed-identity#create-managed-identity-record-in-dataverse">here</a>)</li> <li>Select your plugin assembly (1)</li> <li>Click on "Link to New Identity"</li> </ol> <p><img alt="Plugin Managed Identity" data-entity-type="file" data-entity-uuid="ac3a181c-b8e4-45c7-b318-2c925e6e13e6" src="/sites/default/files/inline-images/plugin_identity1.png" /></p> <ol start="3"> <li>Fill in the following information and click on "Create and Link" <ol> <li>ApplicationId = "Client ID" value of your managed identity copied in step 1</li> <li>TenantId = Your tenant Id (Directory ID in Azure)</li> </ol> </li> </ol> <p><img alt="Plugin Managed Identity" data-entity-type="file" data-entity-uuid="abecf5db-9e86-4cb9-a3c2-67fee74f336f" src="/sites/default/files/inline-images/plugin_identity2_0.png" /></p> <h2>Verification</h2> <p>Good news ! You're ready to go and check if everything is working well. To do so:</p> <ol> <li>Trigger your plugin. In our case, it will be the creation of an account</li> <li>Open Plugin Trace Log and check the plugin execution and if the secret is correctly retrieved</li> </ol> <p><img alt="Plugin Trace Logs" data-entity-type="file" data-entity-uuid="6d0ec0ce-be69-4d48-b97a-e4747971b3e8" src="/sites/default/files/inline-images/plugintrace1.png" /></p> <p> </p> <p>Everything is working well — we are now able to connect and retrieve secrets from Key Vault without having to store or manage any credentials in Dataverse.</p> <p>I hope this step-by-step guide was clear and helped you configure everything easily on your end. If that’s not the case or if any steps need further clarification, please let me know in the comments.</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-07/article.png</div> </div> </div> </div> </div> Thu, 17 Jul 2025 12:47:18 +0000 julien.biedermann 810 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 Dataverse Elastic Tables Deep Dive Tutorial https://dynamics-chronicles.com/article/dataverse-elastic-tables-deep-dive-tutorial <span property="schema:name" class="field field--name-title field--type-string field--label-hidden">Dataverse Elastic Tables Deep Dive Tutorial</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-06-27T12:46:41+00:00" class="field field--name-created field--type-created field--label-hidden">Thu, 06/27/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>Dataverse Elastic Tables Deep Dive Tutorial</em></p> <h2>Intro</h2> <p>Said to be the "Super Heros" of the information world, that they're super fast and can store lots of information and that they're a game changer for storing massive loads of data.</p> <p>I went to undercover what's behind the scenes of this Elastic Tables feature and check if the rumours are fake or not!</p> <h2>Principles and Architecture</h2> <p><span><span>Microsoft use Cosmos DB instead of azure SQL to store data, meaning they have a special column called Partition ID that organizes information in a "folders" like structure.</span></span></p> <img alt="Dataverse Elastic Tables Deep Dive Tutorial" data-entity-type="file" data-entity-uuid="91e65d2a-0e56-4924-a2c7-301931f858bd" height="173" src="/sites/default/files/inline-images/CosmosDBPartitions.jpg" width="351" class="align-center" /> <p><strong><span><span><span>Partitioning and Horizontal Scaling:</span></span></span></strong></p> <ul type="circle"> <li><span><span>Elastic tables use Azure Cosmos DB partitioning to scale individual tables</span></span></li> <li><span><span>Each elastic table contains a system-defined Partition Id column</span></span></li> <li><span><span>Rows are divided into distinct subsets (logical partitions) based on the value of the partitionid column, like storing rows per in folder by its partitionId</span></span></li> <li><span><span>Choosing a consistent partitioning strategy is crucial for optimal performance</span></span></li> </ul> <p><strong><span><span><span>Unique References and Partitioning:</span></span></span></strong></p> <ul type="circle"> <li><span><span>Unique references for elastic tables combine the primary key and the partitionid value</span></span></li> <li><span><span>There is no limit on the number of logical partitions</span></span></li> <li><span><span>If you don’t set a partitionid value, it remains null</span></span></li> <li><span><span>Custom partitionid values allow multiple records with the same primary key but different partition IDs</span></span></li> <li><span><span>PartitionId cannot be changed after creating a row</span></span></li> </ul> <p><strong><span><span><span>Consistency level</span></span></span></strong></p> <p><span><span>Elastic table supports strong consistency during a logical session. A logical session is a connection between a client and Dataverse. When a client performs a write operation on an elastic table, it receives a session token that uniquely identifies the logical session. To have strong consistency, you must maintain the logical session context by including the session token with all subsequent requests.</span></span></p> <p><span><span>Session tokens ensure that all the read operations that are performed during the same logical session context return the most recent write that was made during that logical session. In other words, session tokens ensure that reads always honor the read-your-writes and write-follows-reads guarantees during a logical session. If a different logical session performs a write operation, other logical sessions might not immediately detect those changes.</span></span></p> <p><strong><span><span><span>Transactional behavior</span></span></span></strong></p> <p><span><span>For example, you have a synchronous plug-in step that is registered on the PostOperation stage of the Create message on an elastic table. In this case, an error that occurs in the plug-in does not roll back the record that is created in Dataverse. You should always avoid intentionally canceling any operation by throwing InvalidPluginExecutionException in the PreOperation or PostOperation synchronous stage. If the error is thrown after the Main operation, the request returns an error, but the data operation succeeds. Any write operations that are started in the PreOperation stage succeed.</span></span></p> <p><span><span>However, you should always apply validation rules in a plug-in that is registered for the PreValidation synchronous stage. Validation is the purpose of this stage. Even when you use elastic tables, the request returns an error, and the data operation won't begin.</span></span></p> <p><strong><span><span><span>Expire data by using Time to live</span></span></span></strong></p> <p><span><span>Dataverse automatically includes a Time to live integer column with elastic tables. This column has the schema name TTLInSeconds and the logical name ttlinseconds.</span></span></p> <p><span><span>When a value is set in this column, it defines the amount of time, in seconds, before the row expires and is automatically deleted from database. If no value is set, the record persists indefinitely, just like standard tables.</span></span></p> <p><strong><span><span><span>Flexible schema with JSON columns</span></span></span></strong></p> <p><span><span>Elastic tables enable you to store and query data with varying structures, without the need for predefined schemas or migrations. There's no need to write custom code to map the imported data into a fixed schema. More information: Developer guide: Query JSON columns in elastic tables Elastic tables enable you to store and query data with varying structures, without the need for predefined schemas or migrations. There's no need to write custom code to map the imported data into a fixed schema. More information: Developer guide: Query JSON columns in elastic tables.</span></span></p> <h2><span><span><span>Limitations</span></span></span></h2> <p><span><span><span>Table features currently not supported with elastic tables:</span></span></span></p> <ul type="disc"> <li><span><span>Business rules</span></span></li> <li><span><span>Charts</span></span></li> <li><span><span>Business process flows</span></span></li> <li><span><span>One Dataverse connector for Power BI</span></span></li> <li><span><span>Many-to-many (N:N) relationships to standard tables</span></span></li> <li><span><span>Alternate key</span></span></li> <li><span><span>Duplicate detection</span></span></li> <li><span><span>Calculated and rollup columns</span></span></li> <li><span><span>Currency columns</span></span></li> <li><span><span>Column comparison in queries</span></span></li> <li><span><span>Table sharing</span></span></li> <li><span><span>Composite indexes</span></span></li> <li><span><span>Cascade operations: Delete, Reparent, Assign, Share, Unshare</span></span></li> <li><span><span>Ordering on lookup columns</span></span></li> <li><span><span>Aggregate queries:</span></span> <ul type="circle"> <li><span><span>Distinct value of attribute1 while orderby on attribute2 value</span></span></li> <li><span><span>Pagination when having multiple distincts</span></span></li> <li><span><span>Distinct with multiple order by</span></span></li> <li><span><span>Order by and group by together</span></span></li> <li><span><span>Group by on link entity (left outer join)</span></span></li> <li><span><span>Distinct on user owned tables </span></span></li> </ul> </li> <li><span><span>Table connections</span></span></li> <li><span><span>Access teams</span></span></li> <li><span><span>Queues</span></span></li> <li><span><span>Attachment</span></span></li> </ul> <p><span><span><span>Currently, you can't add the following types of columns:</span></span></span></p> <ul type="disc"> <li><span><span>Money (MoneyAttributeMetadata)</span></span></li> <li><span><span>MultiSelectPicklist (MultiSelectPicklistAttributeMetadata)</span></span></li> <li><span><span>State (StateAttributeMetadata)</span></span></li> <li><span><span>Status (StatusAttributeMetadata)</span></span></li> <li><span><span>Image (ImageAttributeMetadata)</span></span></li> <li><span><span>Calculated, Rollup, or Formula Columns</span></span></li> <li><span><span>Currency</span></span></li> <li><span><span>Lookup based on the Customer option</span></span></li> </ul> <p><strong><span><span>Relationships with elastic tables:</span></span></strong></p> <ul type="disc"> <li><span><span>Doesn't support creating many-to-many relationships </span></span></li> <li><span><span>One-to-many relationships are supported for elastic tables with the following limitations:</span></span> <ul> <li><span><span>Cascading isn't supported. Cascading behavior must be set to Cascade.None when the relationship is created</span></span></li> <li><span><span>Formatted values for lookup columns aren't returned when the following conditions are true</span></span></li> <li><span><span>The table that is retrieved is a standard table, and the lookup refers to an elastic table</span></span></li> </ul> </li> <li><span><span>You're using a custom elastic table partitionid value. In other words, the partitionid value is set to something other than the default value (null).</span></span></li> <li><span><span>Elastic tables support one-to-many relationships, and related rows can be retrieved when a record is retrieved. Related records can't be included in a query</span></span></li> </ul> <p><strong><span><span>Also: </span></span></strong></p> <ul> <li><span><span>Duplicate detection or adding activities aren't supported</span></span></li> <li><span><span><span>Return related rows in a query</span></span></span></li> <li><span><span>Elastic tables don't currently support returning related rows when a query is run. If you try to return related rows</span></span></li> <li><span><span>Deep insert is not supported with elastic tables. Each related record needs to be created independently</span></span><span><span> </span></span></li> <li><span><span>Elastic tables don't support multi-record transactions</span></span></li> <li><span><span>Elastic tables also don't support grouping requests in a single database transaction that uses the SDK ExecuteTransactionRequest class or in a Web API $batch operation changeset. Currently, these operations succeed but aren't atomic</span></span></li> </ul> <h2>Hands-on</h2> <p>After the theoretical side of the elastic tables let's demystify them by putting our hands-on creating and playing with some of the standard features.</p> <h3>Creating an elastic table</h3> <p><span><span>On the Power Apps maker portal inside your solution create a new table and then select the type elastic.</span></span></p> <p><img alt="Dataverse Elastic Tables Deep Dive Tutorial" data-entity-type="file" data-entity-uuid="a0496f2a-fd21-4e1a-9f21-1ac3e88615b2" height="529" src="/sites/default/files/inline-images/table01.png" width="383" /></p> <p>In the table properties as shown in the image below we can check that some of the default table features aren't available for the elastic tables (creating activities, duplicate detection rules, connection, SharePoint document management, ...)</p> <p><img alt="Dataverse Elastic Tables Deep Dive Tutorial" data-entity-type="file" data-entity-uuid="072a9abe-0636-40d3-9bea-c7e06cbc1e02" height="526" src="/sites/default/files/inline-images/table02.png" width="391" /></p> <h3>Exploring the elastic table Columns</h3> <p>Creating a new column on the elastic table has pretty much the same look &amp; feel except for the missing options in the data type choice list (Currency, Formula, Customer Lookup).</p> <p>A new option arises with the elastic tables within the data type single line of text we can now chose a new format, the JSON.</p> <p><img alt="Dataverse Elastic Tables Deep Dive Tutorial" data-entity-type="file" data-entity-uuid="bb73c162-47c9-460c-b570-e77ae0ee8095" height="432" src="/sites/default/files/inline-images/JsonColumn01.png" width="215" /></p> <p>We now have our table created and let's see the aspect of some of the default columns present in it with our JSON Column in the image below.</p> <p>Two new columns are present:</p> <ul> <li>Partition ID: as explained above it defines the logical compartiment to store the record, it cannot be changed after the record creation</li> <li>Time to Live: measured in seconds and determine the time the record will remain in the table</li> </ul> <p><img alt="Elastic Table View" data-entity-type="file" data-entity-uuid="a99ed98e-8e02-4c52-81ea-9c7abff673e7" height="148" src="/sites/default/files/inline-images/ElasticTableView01.png" width="1191" /></p> <p><span><span><span><span>Considerations around the partitionid column that should meet these criteria:</span></span></span></span></p> <ul> <li><span><span>The values in it don't change. After a row is created that has a partitionid value, you can't change it</span></span></li> <li><span><span>It has a high cardinality value. In other words, the property should have a wide range of possible values. Each logical partition can store 20 gigabytes (GB) of data. Therefore, by choosing a partitionid value that has a wide range of possible values, you ensure that the table can scale without reaching limits for any specific logical partition</span></span></li> <li><span><span>It spreads data as evenly as possible among all logical partitions</span></span></li> <li><span><span>No values are larger than 1,024 bytes</span></span></li> <li><span><span>No values contain slashes (/), angle brackets (&lt;, &gt;), asterisks (*), percent signs (%), ampersands (&amp;), colons (:), backslashes (\), question marks (?), or plus signs (+). These characters aren't supported for alternate keys</span></span></li> </ul> <p><span><span>If a partitionid value isn't specified for a row, Dataverse uses the primary key value as the default partitionid value. For write-heavy tables of any size, or for cases where rows are mostly retrieved by using the primary key, the primary key is a great choice for the partitionid column.</span></span></p> <h3>When querying the JSON column:</h3> <p>Using the the default filter in the table view only uses standard text filtering options so here we will only be able to check for the presence of strings inside the JSON but no more than this.</p> <p><img alt="Dataverse Elastic Tables Deep Dive Tutorial" data-entity-type="file" data-entity-uuid="055f4a4b-b39d-45ed-9cb6-36db0d72831a" height="351" src="/sites/default/files/inline-images/JsonColumnFiltering01.png" width="284" /></p> <p>However great news comes with You can use the <code>ExecuteCosmosSQLQuery</code> message that enables to run any Cosmos SQL query directly against your elastic table and filter rows based on properties inside JSON.</p> <p>So let's try it in our created table and column !</p> <p>Using the WebAPI we create the following http request, also available using the Dataverse SDK for .NET:</p> <pre> <code class="language-http">/api/data/v9.2/ExecuteCosmosSqlQuery( QueryText=@p1, EntityLogicalName=@p2, QueryParameters=@p3)? @p1='select c.props.sc_name as name, c.props.sc_jsoncolumn as json, c.props.sc_jsoncolumn.age as age from c where c.props.sc_jsoncolumn.car=@car and c.props.sc_jsoncolumn.age &gt;= @age' &amp;@p2='sc_elasticpoc' &amp;@p3={'Keys':['@car', '@age'],'Values':[{'Type':'System.String','Value':'Mustang'} , {'Type':'System.Int32','Value': '20'}]}</code></pre> <p>where "sc_elasticpoc" is our table, the "sc_jsoncolumn" is our JSON column and c.props is the prefix on the schema name of the columns. In this prefix, <code>c</code> is an alias or shorthand notation for the elastic table that is being queried.</p> <p>And the result is:</p> <pre> <code class="language-http">{ "@odata.context":"https://orgf9a090a4.crm17.dynamics.com/api/data/v9.2/$metadata#expando/$entity" ,"@odata.type":"#Microsoft.Dynamics.CRM.expando","PagingCookie":"","HasMore":false,"Result@odata.type":"#Collection(Microsoft.Dynamics.CRM.expando)" ,"Result": [{"@odata.type":"#Microsoft.Dynamics.CRM.expando","name":"elastic 2","age":20 ,"json": {"@odata.type":"#Microsoft.Dynamics.CRM.expando","name":"Paul","age":20,"car":"Mustang"}}] }</code></pre> <h3>Final considerations between Elastic and Standard tables:</h3> <p><strong>Performance:</strong></p> <p>As some announcements might let us think that elastic tables offer super performance over standard tables there are some studies that shows that's not quite the reality like in the Piotr Gaszewski blog (<a href="https://piotrgaszewski.hashnode.dev/dataverse-elastic-tables">https://piotrgaszewski.hashnode.dev/dataverse-elastic-tables</a>) that states:</p> <ul> <li> <p><span><span>Standard Create, Update, Delete operations: takes the same or a little bit more time to execute in elastic tables than in standard ones</span></span></p> </li> <li> <p><span><span>Multiple Create, Update, Delete operations: <span><span>takes significantly less time to execute in elastic tables than in standard ones (1/2 to 1/10)</span></span></span></span></p> </li> <li> <p><span><span>Retrieve data operations: takes 2 to 3 times longer <span><span><span><span>in elastic tables than in standard ones </span></span></span></span></span></span></p> </li> </ul> <p><span><span>So elastic tables offers significantly less performance execution times when using single atomic operation, however when working with multiple records on the same request it offers some advantages. </span></span></p> <p><strong><span><span>Service API Limits:</span></span></strong></p> <p><span><span>As per MS, Elastic Tables are still exposed to the Dataverse API throttling limits and bulk operation messages should be used to increase the throughput for high volume operations.</span></span></p> <p><strong><span><span>Is security model user compatible:</span></span></strong></p> <p><span><span>MS says yes that Elastic tables adhere to the Dataverse security model using</span></span></p> <ul type="disc"> <li><span><span>an User or Organisation level ownership</span></span></li> <li><span><span>field level security</span></span></li> </ul> <p><strong><span><span>And yes audit is also available for elastic tables!</span></span></strong></p> <h2>Conclusion:</h2> <p>Elastic tables enlarges the table types offer in dataverse, but it's to take with caution in my opinion because these pretty tables aren't the way to go to standard development in a relation database context, since table lookups aren't available ootb and since many other table features aren't also available which reduces a lot the scope of action of these tables.</p> <p>Regarding performance finally they're also not the way to go if you search for increasing CRUD performances, except using multiple CRUD operations.</p> <p>So my final take on the elastic tables would be for storing large datasets of data and mainly for IoT data that would managed by a service layer capable of using multiple CRUD operations and then consuming the data on a business intelligence scheme (as an example only).</p> <h2 class="title">Dataverse Elastic Tables Deep Dive Tutorial</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/output%20%281%29.jpg</div> </div> </div> </div> </div> Thu, 27 Jun 2024 12:46:41 +0000 joao.neto 778 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 Azure Peering Service for Dataverse https://dynamics-chronicles.com/article/azure-peering-service-dataverse <span property="schema:name" class="field field--name-title field--type-string field--label-hidden">Azure Peering Service for Dataverse</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-04-03T12:43:58+00:00" class="field field--name-created field--type-created field--label-hidden">Wed, 04/03/2024 - 14:43</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>Azure Peering Service for Dataverse:</em></p> <h2><strong>Introduction</strong></h2> <p><span><span>Requirements for Enterprise cloud services access are constantly evolving in terms of number of connections/users, data payload, multi-region/multi-continent access, .... </span></span></p> <p><span><span>To overcome this different types of Enterprise architectures are often used : Geo-Partitioned/Hybrid-Cloud/Global load balancing/CDN architectures.</span></span></p> <p><span><span>Nonetheless one question remains, how to enhance and improve network performance between Enterprise office locations and Microsoft cloud services? </span></span></p> <img alt="Azure Peering Service for Dataverse" data-entity-type="file" data-entity-uuid="e3ad4c17-26b0-4cf7-8feb-193bb5223f1c" height="193" src="/sites/default/files/inline-images/MAPS_Problem.png" width="431" class="align-center" /> <p><span><span>Microsoft says that MAPS, Microsoft Azure Peering Service</span></span>, is one of the answers.</p> <h2>What is</h2> <blockquote>A straightforward and easy way to establish direct peering with Microsoft</blockquote> <p>MAPS is a service that enhances the connectivity to Microsoft cloud services such as Microsoft 365, Dynamics 365, software as a service (SaaS) services, Azure, or any Microsoft services accessible via the public internet.</p> <p>It uses a partnership between ISP's, IXP's and SDCI providers worldwide to provide optimal routing and high-performing connectivity between Enterprise office locations and Microsoft cloud services.</p> <img alt="Azure Peering Service for Dataverse" data-entity-type="file" data-entity-uuid="d79122bc-0b42-430e-81ad-ab537f127965" src="/sites/default/files/inline-images/MAPS.arch1_.png" class="align-center" /> <h4>How it works</h4> <p>Microsoft 365, Dynamics 365, and any other Microsoft SaaS services are hosted in multiple Microsoft datacenters and can be accessed from any geographic location.</p> <p>The Microsoft global network has Microsoft Edge point-of-presence (PoP) locations around the world where it can connect to an end user via their service providers.</p> <img alt="Azure Peering Service for Dataverse" data-entity-type="file" data-entity-uuid="7e3814d2-503b-49ff-a229-48ddd028b649" height="349" src="/sites/default/files/inline-images/peering-service-background-final.png" width="537" class="align-center" /> <p>Microsoft and partner service providers ensure that the traffic for the prefixes registered with a Peering Service connection enters and exits the nearest Microsoft Edge PoP locations on the Microsoft global network.</p> <p>Microsoft ensures that the networking traffic egressing from the prefixes registered with Peering Service connections takes the nearest Microsoft Edge PoP locations on the Microsoft global network (Cold-Potato routing).</p> <h2><span style="font-weight:bold">Why use it</span></h2> <ul> <li> <p>If Microsoft services performance are business critical to an Enterprise</p> </li> <li> <p>To provide the shortest path (optimal routing) to Microsoft SaaS products, including Dynamics 365, Microsoft 365, Azure and all Microsoft services accessible from the public Internet</p> </li> <li> <p>Avoid the enormous hops between Enterprise office locations and Microsoft</p> </li> <li> <p>Geo-Redundancy: Microsoft’s interconnected service providers across multiple metro locations allow traffic to reroute via alternate sites if an Edge node experiences performance degradation</p> </li> <li> <p>Monitoring Platform: Service monitoring analyzes user traffic and routing using Microsoft Telemetry</p> </li> <li> <p>Redundancy: With primary and secondary connections, redundancy ensures reliability</p> </li> <li> <p>Route Analytics: Events related to BGP route anomalies (leak or hijack detection) and suboptimal routing are tracked</p> </li> <li> <p>Often in the Peering service partners offer but not in the Microsoft Peering service:</p> <ul> <li> <p>Integrated distributed denial-of-service (DDos) protection</p> </li> <li> <p>Traffic isolated from the public Internet</p> </li> <li> <p>SLA's to ensure service availability</p> </li> </ul> </li> </ul> <h2><span style="font-weight:bold">How to use it</span></h2> <p>In what concerns the technical management of the Peering services Microsoft has services available on the following 3 platforms:</p> <ul> <li>Azure Portal</li> <li>Azure PowerShell</li> <li>Azure CLI</li> </ul> <p>Before getting started let's state the Requirements for creating a new Peering service connection using the Azure Portal:</p> <ul style="unicode-bidi:embed" type="disc"> <li style="vertical-align:middle"> <p>An Azure account with an active subscription</p> </li> <li style="vertical-align:middle">A connectivity provider from the Peering Service partners <ul> <li style="vertical-align:middle"> <p>The Peering provider partner should be the closest to each office location</p> </li> <li style="vertical-align:middle"> <p>A complete list of the Peering Service partners can be found here in this <a href="https://learn.microsoft.com/en-us/azure/peering-service/location-partners?tabs=partners">link</a></p> </li> </ul> </li> </ul> <img alt="Azure Peering Service for Dataverse" data-entity-type="file" data-entity-uuid="bb72764a-314e-4aeb-933b-bfbf8a958e1c" height="396" src="/sites/default/files/inline-images/ps_service_partners.png" width="456" class="align-center" /> <ol style="unicode-bidi:embed; font-weight:normal; font-style:normal" type="1"> <li style="vertical-align:middle" value="1"> <p><span style=""><span style="font-weight:normal"><span style="font-style:normal">In the Azure portal search box select Peering Services in the search results and then create</span></span></span></p> </li> <li style="vertical-align:middle"><span style="">On the Basics of Create a peering service connection, enter or select the following information:</span> <ul> <li style="vertical-align:middle"> <p><img alt="Azure Peering Service for Dataverse" data-entity-type="file" data-entity-uuid="71092adf-65ed-4dc7-8007-5fa4e6bd6ef2" height="315" src="/sites/default/files/inline-images/HowTo1_0.png" width="508" /></p> </li> </ul> </li> <li style="vertical-align:middle"><span style=""><span style="font-weight:normal"><span style="font-style:normal">Next, let's configure the Service connection:</span></span></span> <ol style="unicode-bidi:embed; font-weight:normal; font-style:normal" type="a"> <li style="vertical-align:middle" value="1"><span style=""><span style="font-weight:normal"><span style="font-style:normal">Will need to provide the country, State/Province the Provider (from the available Peering service partners)</span></span></span></li> <li style="vertical-align:middle"><span style="">Select the</span><span style=""> provider primary peering location</span><span style=""> the and ideally the closest one to the target Office network location to enhance</span></li> <li style="vertical-align:middle"><span style="">Select the provider backup peering location as the next closest to your network location. A peering service will be active via the backup peering location only in the event of failure of primary peering service location for disaster recovery. If None is selected, internet is the default failover route in the event of primary peering service location failure</span></li> <li style="vertical-align:middle"><span style="">Under the Prefixes section, select Create new prefix. In Name, enter a name for the prefix resource. Enter the prefixes that are associated with the service provider in Prefix. In Prefix key, enter the prefix key that was given to you by your provider (ISP or IXP). This key allows Microsoft to validate the prefix and provider who have allocated your IP prefix. If your provider is a Route Server partner, you can create all of your prefixes with the same Peering Service prefix key</span> <ul> <li style="vertical-align:middle"><img alt="Azure Peering Service for Dataverse" data-entity-type="file" data-entity-uuid="55f0e2dd-8466-4a2e-8f26-6579affe9ec5" height="306" src="/sites/default/files/inline-images/HowTo2.png" width="492" /></li> </ul> </li> <li style="vertical-align:middle"><span style="">Select Review + Create and after creating a Peering Service connection, additional validation is performed on the included prefixes. You can review the validation status under the Prefixes section of your Peering Service</span></li> </ol> </li> </ol> <p style="vertical-align:middle">Note: For multi-region Enterprise scenarios a unique Peering service connection should be created per office region</p> <h2 style="vertical-align: middle;"><span style="font-weight:bold">Conclusion</span></h2> <blockquote> <p>another piece of the puzzle</p> </blockquote> <p>With this offer Microsoft releases another piece of the puzzle regarding network latency performance optimization over cloud services (SaaS), however Peering services isn't the so wished silver bullet while architecturing new PowerApps/Dataverse solutions for complex multi-region Enterprise scenarios, and here's why:</p> <ul> <li>Peering services aren't accountable for establishing a private secure network to Microsoft services, though it uses the public infrastructure to route Microsoft services traffic</li> <li>Geographic Power Apps environment distribution will still affect network performance for end-users accessing Apps hover a far distant office location, despite of the overall network performance optimization</li> <li>Regarding custom Power Apps solutions and Apps, Peering services won't improve design patterns with performance and calculation problems</li> <li>Azure Peering Services aren't available as trial</li> </ul> <p>As a final word MAPS is a crucial service for Enterprises having business critical services over Microsoft with a multi-region office distribution and looking for network performance improvement and in solution architectural design.</p> <h2 class="title">Azure Peering Service for Dataverse</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-03/MAPS_logo.png</div> </div> </div> </div> </div> Wed, 03 Apr 2024 12:43:58 +0000 joao.neto 745 at https://dynamics-chronicles.com Azure Deep Dive Integration https://dynamics-chronicles.com/article/azure-peering-service-dataverse#comments Dataverse Dynamics 365 Load testing for Model-driven app https://dynamics-chronicles.com/article/dataverse-dynamics-365-load-testing-model-driven-app <span property="schema:name" class="field field--name-title field--type-string field--label-hidden">Dataverse Dynamics 365 Load testing for Model-driven app</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-01-19T15:00:24+00:00" class="field field--name-created field--type-created field--label-hidden">Fri, 01/19/2024 - 16:00</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>Dataverse Dynamics 365 Load testing for Model-driven app</p> <h2>Introduction</h2> <p>In today's digital landscape, where user expectations for seamless, high-performing applications are at an all-time high, ensuring the reliability and scalability of software systems is paramount. Performance testing emerges as a critical practice in the software development lifecycle, aimed at evaluating the responsiveness, stability, and scalability of applications under various conditions.</p> <p>Performance testing encompasses various types, each serving specific objectives in evaluating the performance characteristics of a system. Here are some common performance test types:</p> <ul> <li><strong>Load Testing:</strong> Determines how a system behaves under expected load conditions by subjecting it to a specific number of users or transactions, assessing its response times and resource utilization.</li> <li><strong>Stress Testing:</strong> Pushes the system beyond its normal operational capacity to identify its breaking point and understand how it behaves under extreme conditions, helping determine its resilience and stability.</li> <li><strong>Soak Testing (Endurance Testing):</strong> Evaluates the system's performance over an extended period under a consistent load to detect memory leaks, resource depletion, or degradation of performance over time.</li> <li><strong>Spike Testing:</strong> Assesses the system's ability to handle sudden increases or spikes in load, such as traffic surges, to ensure it can scale appropriately and maintain performance without crashing or slowing down.</li> <li><strong>Scalability Testing:</strong> Measures how well the system can scale up or down to accommodate changes in load or user demand, helping determine its capacity to handle growth or fluctuations in traffic.</li> </ul> <p>In this article, we will focus how to conduct a load testing of a Dynamics 365 Model-driven App.</p> <h2>What is Load Testing ?</h2> <p>Load testing is a crucial aspect of performance testing focused on evaluating how a system behaves under specific levels of concurrent user activity or workload. The primary objective of load testing is to assess the system's performance, reliability, and scalability by subjecting it to simulated loads that represent expected usage patterns in real-world scenarios.</p> <p>During load testing, the system is subjected to various levels of load, typically represented by a specific number of concurrent users, transactions, or requests per unit of time. By gradually increasing the load, testers can observe how the system responds and identify any performance issues, such as slow response times, errors, or system crashes.</p> <p>When performing a load test, several important factors should be considered to ensure its effectiveness and relevance to the application's performance goals. Here are some key considerations:</p> <ul> <li><strong>Define Clear Objectives:</strong> Clearly define the objectives and goals of the load test. Understand what aspects of the application's performance you are testing, such as response times, throughput, scalability, or resource utilization.</li> <li><strong>Identify Key Scenarios:</strong> Identify the critical user scenarios or workflows that are most representative of real-world usage patterns. Focus the load test on these scenarios to simulate realistic user behavior.</li> <li><strong>Set Realistic Load Levels:</strong> Determine the appropriate load levels based on expected traffic volumes, peak usage periods, and scalability requirements. Ensure that the load levels accurately reflect the anticipated production environment.</li> <li><strong>Simulate Production Environment:</strong> Mimic the production environment as closely as possible during the load test. Consider factors such as network latency, hardware configurations, and software dependencies to create a realistic testing environment.</li> <li><strong>Analyze Results:</strong> Analyze the results of the load test thoroughly to identify performance issues, bottlenecks, and areas for improvement. Look for patterns in system behavior and correlate performance metrics to identify root causes of performance degradation.</li> </ul> <h2>How to load test a model-driven app ?</h2> <p>Microsoft is providing the following documentation : <a href="https://learn.microsoft.com/en-us/dynamics365/guidance/resources/test-scale-dynamics-365-solution">https://learn.microsoft.com/en-us/dynamics365/guidance/resources/test-scale-dynamics-365-solution</a></p> <p>In this article, we're gonna walk through using those test samples from Microsoft, step by step.</p> <h2>Requirements</h2> <ul> <li>Microsoft Dynamics 365 Customer Service environment with Customer Service-Hub model-driven application</li> <li>At least one user account with multi-factor authentication disabled</li> <li>Sample data in Dataverse <ul> <li>Min. 10 active accounts</li> <li>Min. 10 active products</li> <li>Min. 10 active subjects</li> </ul> </li> <li>Azure subscription (optional): It's a requirement if you plan to load test in Azure Load Testing (<a href="https://azure.microsoft.com/en-us/products/load-testing">https://azure.microsoft.com/en-us/products/load-testing</a>).</li> </ul> <h3>1. Download samples files</h3> <p>You will find all the samples files here : <a href="https://github.com/microsoft/Dynamics-365-FastTrack-Implementation-Assets/tree/master/Customer%20Service/Testing/At%20Scale/Samples">https://github.com/microsoft/Dynamics-365-FastTrack-Implementation-Assets/tree/master/Customer%20Service/Testing/At%20Scale/Samples</a></p> <p>In this walkthrough, we will use the CreateCase sample. To start, you can download the following files :</p> <ul> <li>CaseCreate.csv</li> <li>CreateCase.docx</li> <li>CreateCase.jmx</li> </ul> <h3>2. Install Apache JMeter</h3> <p>First, you need to install Apache JMeter <a href="https://jmeter.apache.org/usermanual/get-started.html#lets_start">https://jmeter.apache.org/usermanual/get-started.html#lets_start</a>.</p> <p>Apache JMeter is an open-source, Java-based software designed for load testing, performance testing, and functional testing of web applications.</p> <div class="alert alert-primary" role="alert">JMeter is not a browser, it works at protocol level. As far as web-services and remote services are concerned, JMeter looks like a browser (or rather, multiple browsers); however JMeter does not perform all the actions supported by browsers. In particular, JMeter does not execute the Javascript found in HTML pages. Nor does it render the HTML pages as a browser does (it's possible to view the response as HTML etc., but the timings are not included in any samples, and only one sample in one thread is ever displayed at a time).</div> <p>Apache JMeter has two modes, GUI mode and CLI mode.</p> <ul> <li>Use GUI mode for test creation, editing, and debugging.</li> <li>Use CLI mode to run a load test.</li> </ul> <h3>3. Populate CSV file</h3> <p>The file CaseCreate.csv contains the test user's information (username and password). Open the file and add your test user's credentials.</p> <p>Be careful, if the passwords contain special characters like <strong>% or #,</strong> it can be misinterpreted during test execution and generate errors.</p> <p><img alt="Dataverse Dynamics 365 Load testing for Model-driven app" data-entity-type="file" data-entity-uuid="2031403a-9172-4e58-8f99-1d7e215286a7" src="/sites/default/files/inline-images/casecreate_csv.png" /></p> <h3>4. Edit load test script</h3> <p>Run Apache JMeter in GUI mode by opening jmeter.bat in the bin folder.</p> <p>Open the <strong>CreateCase.jmx</strong> file.</p> <ol> <li>In Apache JMeter, click File – Open</li> <li>Use the explorer window to navigate to file or repository containing the CreateCase.jmx file.</li> <li>Select the CreateCase.jmx file and click Open.</li> </ol> <p><img alt="Dataverse Dynamics 365 Load testing for Model-driven app" data-entity-type="file" data-entity-uuid="515fb8ae-e691-4f51-81f2-d000c3ac5f89" src="/sites/default/files/inline-images/jmeter-1.png" /></p> <p>Update <strong>User Defined Variables</strong></p> <ol> <li>Click on the User Defined Variables element</li> <li>Update the following values <ol> <li>host – your Microsoft Dataverse environment url (e.g., yourorg.crm.dynamics.com)</li> <li>tenantId - your Microsoft 365 tenantid.</li> <li>cshAppId - the GUID of your Customer Service Hub model-driven application</li> </ol> </li> </ol> <p>Update the <strong>Thread Group Properties</strong></p> <ol> <li>Click on the CreateCase element</li> <li>Update the following values <ol> <li>Number of Threads (users) – total concurrent users to simulate.</li> <li>Ramp up period (seconds) – duration to increase the threads to the total number of threads.</li> <li>Loop Count – number of times to execute a single thread.</li> </ol> </li> </ol> <p>Configure the <strong>CSV Data Source</strong></p> <ol> <li>Click on the CSV Data Set Config</li> <li>Update the filename value with your updated csv file</li> </ol> <h3>5. Execute in GUI mode</h3> <p>Test can be started in GUI mode using one of the following methods:</p> <ol> <li>In the Run menu, click Start</li> <li>Click the Start button in the toolbar</li> <li>Right-click on CreateCase element and click Start</li> </ol> <div class="alert alert-warning" role="alert">Before running the test in GUI mode, ensure to reduce the number of threads (users). GUI mode should only be used for test creation, editing and debugging.</div> <p>You can use the View Results Tree to monitor the progress of the test and to debug, checking the request and response of the calls.</p> <p><img alt="Dataverse Dynamics 365 Load testing for Model-driven app" data-entity-type="file" data-entity-uuid="faf64260-d9b8-4792-a205-fa1310476b8e" src="/sites/default/files/inline-images/jmeter-2.png" /></p> <h3>6. Execute in CLI mode</h3> <p>GUI mode has limitations that might impact the performance of a load test. Use CLI mode when executing a load test at full scale.</p> <p>Test can be started in CLI mode following the steps below:</p> <ol> <li>Open CMD and change the directory to the jmeter bin folder</li> <li>Enter the following command</li> </ol> <pre> <code>jmeter -n -t path\CreateCase.jmx -l path\testresults.csv -e -o path\Results</code></pre> <table border="1" cellpadding="1" cellspacing="1" style="width: 500px;"> <thead> <tr> <th scope="col">Option</th> <th scope="col">Definition</th> </tr> </thead> <tbody> <tr> <td>-n</td> <td>This specifies JMeter is to run in cli mode.</td> </tr> <tr> <td>-t</td> <td>Name of JMX file that contains the Test Plan</td> </tr> <tr> <td>-l</td> <td>Name of JTL or CSV file to log sample results to</td> </tr> <tr> <td>-e</td> <td>Generate report dashboard after load test</td> </tr> <tr> <td>-o</td> <td>Output folder where to generate the report dashboard after load test. The folder must not exist or be empty.</td> </tr> </tbody> </table> <p>Sometimes, JMeter throws a heap dump along with an ‘out of memory’ error while executing the load test. In this case, you need to increase the heap size by editing the jmeter.bat file. The size has been updated to 8 GB in the example below:</p> <pre> <code>if not defined HEAP ( rem See the unix startup file for the rationale of the following parameters, rem including some tuning recommendations set HEAP=-Xms1g -Xmx8g -XX:MaxMetaspaceSize=256m )</code></pre> <h4>6.1 Report</h4> <p>After running the load test using CLI mode with the command above, a HTML report is generated in the folder defined.</p> <p><img alt="Dataverse Dynamics 365 Load testing for Model-driven app" data-entity-type="file" data-entity-uuid="ae0cf0c7-fc50-439e-9244-943ce72a9997" src="/sites/default/files/inline-images/report.png" /></p> <p>This report offers valuable insights into all requests conducted during the testing phase, presenting a comprehensive overview of the test execution. Additionally, it includes a detailed table illustrating the APDEX.</p> <p>APDEX (Application Performance Index) is a metric used to evaluate the user satisfaction of an application based on its response times. APDEX is a standardized method for measuring and reporting the satisfaction level of users with the responsiveness of web applications or services.</p> <p>APDEX categorizes response times into three zones:</p> <ul> <li>Satisfied: Response times that meet or exceed the user's expectations.</li> <li>Tolerating: Response times that are acceptable but not ideal.</li> <li>Frustrated: Response times that are considered unacceptable by users.</li> </ul> <p>The APDEX score is calculated based on the following formula:</p> <p><img alt="Dataverse Dynamics 365 Load testing for Model-driven app" data-entity-type="file" data-entity-uuid="38b1c812-45ed-48ee-93ca-8c2312d30a44" src="/sites/default/files/inline-images/apdex.png" /></p> <p>In JMeter, APDEX can be calculated using the following steps:</p> <ol> <li>Define the thresholds for response times that determine whether a sample is categorized as satisfied, tolerating, or frustrated. These thresholds are typically defined based on the application's performance goals and user expectations.</li> <li>Configure JMeter to track the response times of requests during the test execution.</li> <li>After the test execution, JMeter calculates the APDEX score based on the collected response times and the defined thresholds.</li> <li>The APDEX score provides a standardized measure of user satisfaction with the application's performance, helping testers and developers assess whether the application meets its performance goals and user expectations.</li> </ol> <p>By using APDEX in JMeter, testers can quantitatively measure and report the user satisfaction level of web applications or services, allowing them to identify performance issues and prioritize improvements to enhance the overall user experience.</p> <h3>7. Execute in Azure Load Testing</h3> <p>First, you need to deploy Azure Load Testing using your Azure subscription.</p> <p>Now, before diving into creating a load test in Azure Load Testing with a JMeter script, there are a few configurations we need to tweak:</p> <h4>7.1 Update CSV Data Set Config</h4> <p>In Azure Load Testing the JMX file and all related files are uploaded in a single folder. When you reference an external file in your JMeter script, verify that you only use the file name and remove any file path references.</p> <ol> <li>Remove any file path and reference the filename only in the filename field</li> <li>Set "Ignore first line" to False</li> <li>Remove the header row (userName,password) from CreateCase.csv</li> </ol> <p><img alt="CSV Configuration Azure Load Testing" data-entity-type="file" data-entity-uuid="be1d0bce-e2ed-4eda-8e24-37960957cd83" src="/sites/default/files/inline-images/alt1_0.png" /></p> <h4>7.2 Update User Defined Variables</h4> <ol> <li>Replace User Defined Variables with the following values :</li> </ol> <pre> <code>${__BeanShell(System.getenv("host"))} ${__BeanShell(System.getenv("tenantId"))} ${__BeanShell(System.getenv("cshAppId"))}</code></pre> <p><img alt="Dataverse Dynamics 365 Load testing for Model-driven app" data-entity-type="file" data-entity-uuid="800d329d-28c1-4b5e-a28f-41ea50af0325" src="/sites/default/files/inline-images/alt2.png" /></p> <h4>7.3 Create and run the test in Azure Load Testing</h4> <ol> <li>In Microsoft Azure, navigate to and open your Azure Load Testing resource.</li> <li>From "Get started" dashboard, select "Create" with the option "Upload a JMeter script" or create it from Tests menu.</li> <li>On the Basic tab, enter a test name and description</li> <li>On the Test plan tab, upload both CreateCase.jmx (the one adapted for Azure Load Testing) and CreateCase.csv (without header row) <ol> <li>Uncheck "Run test after create" if you don't want to run it directly after creation</li> </ol> </li> <li>On the Parameters tab <ol> <li> Add the correct values for the variables <strong>host</strong>, <strong>tenantId </strong>and <strong>cshAppId</strong></li> </ol> </li> <li>On the Load tab <ol> <li>Configure the test engine instances to a meet the target load for the test. The number of threads specified in the jmx file represents the number of thread (virtual users) executed by one test engine instance.</li> <li>Network traffic to Power Platform applications will be routed through Public networks.</li> </ol> </li> <li>On the Test criteria tab <ol> <li>(Optional) Define the criteria to specify the performance expectations of the system under test. Use the defined criteria to determine the failure conditions for the test when the criteria evaluate to true.</li> </ol> </li> <li>On the Monitoring tab <ol> <li>(Optional) Configure application components to monitor server-side metrics during the test run.</li> </ol> </li> <li>Once all tabs completed, you can review and create your test.</li> <li>Then, if you have unchecked the box at point 4.1, you need to open and run your test manually</li> <li>Once the test is executed, you can open it to see the load test results</li> </ol> <p><img alt="Dataverse Dynamics 365 Load testing for Model-driven app" data-entity-type="file" data-entity-uuid="676179a3-f2cd-4b85-883a-46e6b063b861" src="/sites/default/files/inline-images/alt_setup1.png" /></p> <p><img alt="Upload files" data-entity-type="file" data-entity-uuid="7d47373a-d0e8-4c07-bf5b-34dd9c2c0101" src="/sites/default/files/inline-images/alt_setup2.png" /></p> <p><img alt="Azure Load Testing Parameters" data-entity-type="file" data-entity-uuid="27143bc3-8bbb-4258-be8d-38eafce28f00" src="/sites/default/files/inline-images/alt_setup3.png" /></p> <p><img alt="Dataverse Dynamics 365 Load testing for Model-driven app" data-entity-type="file" data-entity-uuid="104141ef-e259-4064-9d92-74af6cbc66d8" src="/sites/default/files/inline-images/alt_results.png" /></p> <div class="alert alert-warning" role="alert">I'd like to note that I conducted the load test within a trial environment. This could account for the observed performance not meeting expected standards.</div> <h2>Conclusion</h2> <p>I hope you enjoyed this tutorial on running a load test sample in a model-driven app. This article provided an overview of load testing using JMeter and Azure Load Testing, and perhaps it will inspire other bloggers to delve into topics such as:</p> <ul> <li>How to build his own test case in JMeter ?</li> <li>How to analyze the results ?</li> </ul> <p>In conclusion, it's crucial to recognize that JMeter has limitations when it comes to measuring user experience elements occurring at the client side, such as load time or page rendering. JMeter functions as a performance testing tool but does not emulate a real browser. If we aim to test the user interface (UI) using JMeter, we'd need to incorporate tools like the WebDriver Sampler. This component facilitates integration with the Selenium browser automation framework, enabling more comprehensive UI testing capabilities within the performance testing context. Essentially, JMeter, while proficient at server-side performance assessments, requires additional tools like WebDriver Sampler for thorough evaluation of user interface aspects.</p> <h2 class="title">Dataverse Dynamics 365 Load testing for Model-driven app</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-02/article1.png</div> </div> </div> </div> </div> Fri, 19 Jan 2024 15:00:24 +0000 julien.biedermann 742 at https://dynamics-chronicles.com 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 Scale Power Automate Desktop (PAD) using multiple Machines https://dynamics-chronicles.com/article/scale-power-automate-desktop-pad-using-multiple-machines <span property="schema:name" class="field field--name-title field--type-string field--label-hidden">Scale Power Automate Desktop (PAD) using multiple Machines</span> <span rel="schema:author" class="field field--name-uid field--type-entity-reference field--label-hidden"><a title="View user profile." href="/user/david-uhlmann" lang="" about="/user/david-uhlmann" typeof="schema:Person" property="schema:name" datatype="" class="username">David Uhlmann</a></span> <span property="schema:dateCreated" content="2023-01-22T13:25:31+00:00" class="field field--name-created field--type-created field--label-hidden">Sun, 01/22/2023 - 14:25</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>Scale Power Automate Desktop (PAD) using multiple Machines</em></p> <p>Whether you should use Power Automate Desktop or not has been described by my colleague in the community Reza Dorrani in his excellent video about this topic (<a href="https://www.youtube.com/watch?v=z19CAWq1tK8">https://www.youtube.com/watch?v=z19CAWq1tK8</a>).<br /> However, since the practical application of this topic is relatively rare we cannot cover all uses cases. So we will show how to scale a small solution to multiple machines. One thing in advance: The solution shown here should only be used as a last "resort" if you can't or don't want to replace the corresponding solution with a newer application that has an API (e.g. due to budget restrictions).</p> <p><strong>Power Automate Desktop (PAD)</strong>, at least in the context of Power Automate cloud flows, is a <strong>bridging technology</strong> for applications without API. It does this very well, without question. However, it should by no means be seen as a long-term solution. A clearly better solution is to scrap the old software and replace it with a modern solution, for example based on the Power Platform. But anyways, lets go now. Keep in mind the following scenario is very much simplified to keep this article within reading span of most people.</p> <p>In the following scenario we have a VM with Power Automate Desktop and a corresponding flow. Next to it we have a second VM on which nothing else is currently happening:</p> <img alt="Scale Power Automate Desktop (PAD) using multiple Machines" data-entity-type="file" data-entity-uuid="2735cdca-38cd-4e24-bc14-e6e7c577d529" height="463" src="/sites/default/files/inline-images/VMs%20before%20expansion.png" width="1446" class="align-center" /> <p>In our use case, to keep the example simple, we have an Excel file that stores contact information. Two flows exist for this purpose: <strong>1x a Power Automate Desktop flow </strong>that fetches the data from the Excel file and <strong>1x a cloudflow</strong> that checks if there is a duplicate when creating a contact. So far so good:</p> <img alt="Scale Power Automate Desktop (PAD) using multiple Machines" data-entity-type="file" data-entity-uuid="7e097f34-5b6b-4c3e-99a0-ce99ddec0322" height="767" src="/sites/default/files/inline-images/process%20sample.png" width="1430" class="align-center" /> <p>When the users enters a name which is in the Excelfile s/he gets flag telling her/him that this user might be a duplicate. However, this solution is not so good for two main reasons:</p> <ul> <li>If the mentioned VM has unplanned downtime, the described mechanism is not available and the cloudflow would time out.</li> <li>This approach also has disadvantages with a high workload, i.e. many users creating contacts. There may be long waiting times.</li> </ul> <p>To prevent such problems, Microsoft offers the possibility to combine several VMs in a machine group. How this works is shown in the following steps. What we need to do is:</p> <ol> <li><strong>Start a new VM (this we have already done)</strong></li> <li><strong>Install PAD, launch the portal and add the new VM to the machine group</strong></li> <li><strong>Update cloud flow connection if needed</strong></li> </ol> <p>To go one, first thing is create a new machine group:</p> <img alt="Scale Power Automate Desktop (PAD) using multiple Machines" data-entity-type="file" data-entity-uuid="2ff617d6-eedb-460b-89bb-27f4d22313a5" height="806" src="/sites/default/files/inline-images/create%20a%20new%20machine%20group.png" width="1449" class="align-center" /> <p>Once you have created a machine group, or you already had one, you can go to <strong>step 2 (installing PAD and launching the portal)</strong>. How to get PAD is described by an in detail MS Learn article: <a href="https://learn.microsoft.com/en-us/power-automate/desktop-flows/install">https://learn.microsoft.com/en-us/power-automate/desktop-flows/install</a> . After installation is done you can log in with your account to the Power Automate Desktop Portal on the second VM. Here you should see something like this:</p> <img alt="Scale Power Automate Desktop (PAD) using multiple Machines" data-entity-type="file" data-entity-uuid="b2d8e0b8-88cc-42c3-a3b5-e2e75b368d12" height="841" src="/sites/default/files/inline-images/adding%20second%20VM%20to%20machine%20group.png" width="1434" class="align-center" /> <p>At this point the <strong>password of the Machine Group</strong> has to be entered, i.e. <strong>not the password of the admin account</strong> of the Power Platform or the local admin. The password of the Machine Group can be set as often as you like. So last we need to change the connection of the Cloud Flow to the connection of the Machine Group. This is done so that the Cloud Flow can also use the full power of the, now two, machines. For starters I would advise you to go to <a href="make.powerautomate.com ">make.powerautomate.com </a>which is the cloud portal for all power automate related activities. There you have more options than in the classical power platform portal:</p> <img alt="Scale Power Automate Desktop (PAD) using multiple Machines" data-entity-type="file" data-entity-uuid="19b31123-72dc-42d8-9011-a23eb34d58fb" height="774" src="/sites/default/files/inline-images/power%20automate%20portal_0.png" width="1444" class="align-center" /> <p>So now you need to open your cloud flow and update the connection to the machine group instead of the single machine. You click on the three dots of the action and select "<em>New connection reference</em>", then you should see something like this:</p> <img alt="Scale Power Automate Desktop (PAD) using multiple Machines" data-entity-type="file" data-entity-uuid="4f3debcd-68f6-441f-bc37-2427d3a55760" height="778" src="/sites/default/files/inline-images/before%20new%20connection.png" width="1441" class="align-center" /> <p>It is recommended to login at every machine inside your machine group with the same user. Most solutions that are displayed in the internet recommend this. This makes it easier giving the connection one username and one password. For simplicity I just use my personal user, but in practice you would create an AAD Admin user that has admin rights on all the machines.</p> <p>Once you are done, you should see something like this:</p> <img alt="Scale Power Automate Desktop (PAD) using multiple Machines" data-entity-type="file" data-entity-uuid="bdef6620-1ebc-4a23-bfc7-667c736d6924" height="782" src="/sites/default/files/inline-images/after%20new%20connection.png" width="1455" class="align-center" /> <p>Once all this is done you should make sure to log in to all the machines you want to us, or if you run your flows in unattended mode you have to log out. Now its time to test the machine group. To do that I will create multiple contacts shortly after another, knowing that the flow will take a while to start PAD, read the excel file, give results back and loop over it to check the results:</p> <img alt="flow runs after machine group was installed" data-entity-type="file" data-entity-uuid="2d1d382f-46af-4262-8c16-e7ed17d6a22c" height="736" src="/sites/default/files/inline-images/flow%20runs%20test.png" width="1437" class="align-center" /> <p>Now there is only one thing left to do. <strong>We want to check if the flows are really executed on different VMs.</strong> To check this we first need the "hostnames" of the two machines. This can be displayed on every Windows with the command "hostname". In my case the first hostname ends with: (...) <strong>VMPA</strong> and the second one ends on (...) <strong>U0Q4</strong>. <br /> Now we have to check where the flow run was executed. For this we open a flow run and click on the details of the connector:</p> <img alt="how to get to flow runs details of group" data-entity-type="file" data-entity-uuid="b63e2228-cb71-4984-a5a7-3f10c3be3740" height="825" src="/sites/default/files/inline-images/flow%20details.png" width="1426" class="align-center" /> <p>After this you should see a screen like the following:</p> <img alt="Scale Power Automate Desktop (PAD) using multiple Machines" data-entity-type="file" data-entity-uuid="cb7d3cad-a6eb-46bc-b905-811724f60a1a" height="955" src="/sites/default/files/inline-images/machine1%20run.png" width="1445" class="align-center" /> <p>So lets check out another run now:</p> <img alt="Scale Power Automate Desktop (PAD) using multiple Machines" data-entity-type="file" data-entity-uuid="2c8a7dbc-05a8-451a-8c93-f30f4a747590" height="945" src="/sites/default/files/inline-images/machine2%20run.png" width="1437" class="align-center" /> <p><strong>If you made it that far &amp; could reproduce: congratulations</strong>! Now you are ready to scale as many VMs as you want using Power Automate Desktop using machine groups. This piece was only a glimpse of things you can/should do. Stuff like automatic checks if your machines are running, and respective actions if not, would have been too much for such a piece. If you are interested in such content comment below and we might publish it in the future (using Power Automate of course :-)</p> <h2 class="title">Scale Power Automate Desktop (PAD) using multiple Machines</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-01/turn-on-gf61b49ce2_1920.jpg</div> </div> </div> </div> </div> Sun, 22 Jan 2023 13:25:31 +0000 David Uhlmann 614 at https://dynamics-chronicles.com Deep Dive Integration Power Automate Tutorial https://dynamics-chronicles.com/article/scale-power-automate-desktop-pad-using-multiple-machines#comments