<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" >

<channel><title><![CDATA[Ben Walker - Blog]]></title><link><![CDATA[http://www.bwmodular.org/blog]]></link><description><![CDATA[Blog]]></description><pubDate>Mon, 13 Apr 2026 15:16:59 +0000</pubDate><generator>Weebly</generator><item><title><![CDATA[Plugin Pipeline Simulation in FakeXrmEasy v2/3]]></title><link><![CDATA[http://www.bwmodular.org/blog/pipeline-simulation-in-fakexrmeasy-v23]]></link><comments><![CDATA[http://www.bwmodular.org/blog/pipeline-simulation-in-fakexrmeasy-v23#comments]]></comments><pubDate>Sun, 05 Mar 2023 00:00:00 GMT</pubDate><category><![CDATA[Uncategorized]]></category><guid isPermaLink="false">http://www.bwmodular.org/blog/pipeline-simulation-in-fakexrmeasy-v23</guid><description><![CDATA[Pipeline simulation has been a feature of FakeXrmEasy for several years. There have been some significant improvements in recent updates to versions 2 &amp; 3 which make the feature even more useful. In this article I'll go through the new features and discuss the use of Pipeline Simulation in general.      As well as some minor improvements, Versions 2 &amp; 3 of FakeXrmEasy include three very useful new features.Automatic Plugin Step RegistrationThe first new feature is the addition of Automat [...] ]]></description><content:encoded><![CDATA[<div class="paragraph">Pipeline simulation has been a feature of FakeXrmEasy for several years. There have been some significant improvements in recent updates to versions 2 &amp; 3 which make the feature even more useful. In this article I'll go through the new features and discuss the use of Pipeline Simulation in general.<br /></div>  <div>  <!--BLOG_SUMMARY_END--></div>  <div class="paragraph">As well as some minor improvements, Versions 2 &amp; 3 of FakeXrmEasy include three very useful new features.<br /><br /><strong>Automatic Plugin Step Registration</strong><br />The first new feature is the addition of Automatic Plugin Step Registration. Previously, when using Pipeline Simulation, you had to explicitly register the Plugin Steps as part of the setup of your tests, like this:<br /></div>  <div id="110674202611421961"><div><style type="text/css">	#element-7415f14f-af7d-435e-a852-a983ea382479 .code-editor--light {  padding: 20px 0px;}#element-7415f14f-af7d-435e-a852-a983ea382479 .code-editor--light .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #C9CDCF;  border-right: 1px solid #C9CDCF;  border-top: 1px solid #C9CDCF;  background-color: #F8F8F8;  color: #363B3E;}#element-7415f14f-af7d-435e-a852-a983ea382479 .code-editor--light .header .paragraph {  margin: 0;}#element-7415f14f-af7d-435e-a852-a983ea382479 .code-editor--light .body-code {  margin: 0;  border: 1px solid #C9CDCF;  background-color: #FFFFFF;  color: #666C70;}#element-7415f14f-af7d-435e-a852-a983ea382479 .code-editor--dark {  padding: 20px 0px;}#element-7415f14f-af7d-435e-a852-a983ea382479 .code-editor--dark .ace-tomorrow-night-eighties {  background-color: #363B3E;}#element-7415f14f-af7d-435e-a852-a983ea382479 .code-editor--dark .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #E0E1E2;  border-right: 1px solid #E0E1E2;  border-top: 1px solid #E0E1E2;  background-color: #666C70;  color: #FFFFFF;}#element-7415f14f-af7d-435e-a852-a983ea382479 .code-editor--dark .header .paragraph {  margin: 0;}#element-7415f14f-af7d-435e-a852-a983ea382479 .code-editor--dark .body-code {  margin: 0;  border: 1px solid #E0E1E2;  background-color: #363B3E;  color: #F8F8F8;}</style><div id="element-7415f14f-af7d-435e-a852-a983ea382479" data-platform-element-id="270170748587580171-1.3.3" class="platform-element-contents">	<div class="code-editor--dark">    <div class="header">        <div class="paragraph"></div>    </div>    <div class="body-code">        <pre class="editor"></pre>    </div></div></div><div style="clear:both;"></div></div></div>  <div class="paragraph">Not only was this time-consuming, but it was also error-prone. Tests might end up failing becaue of invalid plugin registration, rather than because of genuine errors. With Automatic Plugin Step Registration, if you use a declarative method of registering your plugins steps with a tool like <a href="https://github.com/scottdurow/SparkleXrm/wiki/spkl" target="_blank">spkl</a>, your plugin steps will automatically be registered in your tests. This is a great time-saver and will help you avoid making mistakes in registering plugin steps in your tests.<br />There's a sample solution showing how to get this working with spkl <a href="https://github.com/DynamicsValue/fake-xrm-easy-samples" target="_blank">here</a> and more documentation <a href="https://dynamicsvalue.github.io/fake-xrm-easy-docs/quickstart/plugins/pipeline/automatic-registration/#automatic-registration-of-plugin-steps" target="_blank">here</a>.<br /><br /><strong>Plugin Step Audit</strong><br />The second new feature is called Plugin Step Audit. With this enabled (by using the UsePluginStepAudit pipeline option in the AddPipelineSimulation directive), FakeXrmEasy will keep a record of the Plugin steps which fire during your tests, and this can be used to verify that you have set your plugin registration up correctly, and that the pipeline is working as expected. This could be used, for example, to test that you have configured your filtering attributes correctly in your step registration. The advantage of using this feature is that you can test that your steps registration is configured correctly without having to set up test data in order to test it - if you only need to know that a plugin fires under certain circumstances, without needing to know what the result of the plugin firing is, then you can test this with plugin step auditing. I'll talk later about the principle that Unit Tests should only test one thing and this feature allows you to do exactly that.<br /><br /><strong>Plugin Registration Validation</strong><br />Additionally, the Automated Plugin Registration feature validates your plugin registration and prevents you from defining registrations which would not be valid when registering message processing steps against a real instance of CRM.<br />For example, if your plugin registration config contains two steps with the same GUID, you will get an error, or if you try and register a plugin using a configuration which you would not be able to do against a real Dynamics instance, such as registering a pre-image on a Create step.<br /><br />Having explored the new features available in V2/3, I'd like to discuss the use of Pipeline Simulation in general, what its pros and cons are, and when I think you should use it.<br /><br /><strong>The advantages of using Pipeline Simulation</strong><br />I think there are three main advantages to using Pipeline Simulation when testing your Dynamics Code.<br /><br /><strong>Allows for end-to-end testing of code </strong><br />Plugin pipeline simulation allows you to perform full end-to-end testing of your transactions. So, if you have a plugin which creates or updates an entity, and this entity also has a plugin registered against it, you will be able to test the full end-to-end process. This is particularly useful when you have some code which is not called from the context of a plugin transaction, but, for example, an Azure Function. If your function creates a record and a plugin populates some attributes on that record which your Azure Function relies on, then pipeline simulation will allow you to test this. With pipeline simulation, the faked context behaves just as the real CRM instance would behave.<br /><br /><strong>Troubleshoot the whole pipeline</strong><br />Related to the above feature is the fact that with pipeline simulation its possible to troubleshoot and debug the entire transaction pipeline. Imagine for example that an Azure function which creates a record in CRM and then does something with that record, and that Azure function is not working as expected. With pipeline simulation, you can trouble shoot the entire function from start to finish and step into the plugin code even if it is called from the Azure Function. There is a good video demonstrating this <a href="https://www.youtube.com/watch?v=Pzd0La4TTLU&amp;t=1s" target="_blank">here</a>.<br /><br /><strong>Testing plugin registration</strong><br />This is another real bonus - if you use a declarative method such as spkl to register your plugins, you can test that your have registered your message processing steps correctly before you even deploy them to the database. You can check filtering attributes and images are defined correctly and, as mentioned above, the framework will even test whether your registration does anything 'illegal' like registering two steps with the same GUID.<br /><br /><strong>Ease of use</strong><br />Introducing newcomers to the practice of Unit Testing in general, and specifically when talking about the creation of fake plugin execution contexts, can be confusing if the developers are new to the concepts of Unit Testing and Fakes. Being able to demonstrate a Unit test which just consists of setting up some test data and then issuing a 'Create' message and testing the outcome is simpler than having to demonstrate the use of a fake Plugin Execution Context, registering input parameters, etc. Using Pipeline Simulation can be a good first step in the learning curve which comes with writing dataverse Unit Tests.<br /><br /><strong>Why use anything else?</strong><br />If using Pipeline Simulation in our Unit Tests is so good, should we always use it? Should we stop writing tests which invoke the plugin directly and simply call the CRUD or other messages which will cause the pipeline to fire? Why go through the complexity of writing Unit Tests which explicitly test our plugins when we could 'just' test our plugins by executing a service request and letting the plugin pipeline simulation execute the plugin(s) for us?<br /><br />Well, the truth is, that when we use pipeline simulation, we are no longer truly Unit Testing our code - testing the entire execution pipeline is really a kind of integration testing, albeit with tests that use a fake execution context and with many significant advantages (speed, repeatability, etc) over integration testing with a real instance of CRM. Strictly speaking a Unit Test should only test one thing, and if our tests invoke a whole plugin pipeline, we can no longer say that we are just testing a single function.<br /><br />Does this matter? Does the fact that we can no longer call these tests Unit Tests make any difference? Well, yes, in many cases it does. By simulating the entire plugin execution pipeline we are introducting fragility and dependencies in our tests. If a test fails, we can't be sure whether this is because the code under test is failing or because some other part of the pipeline is failing because it depends on certain data or certain conditions being true. Testing a pipeline plugin may well require more setup before each test and as the plugin pipeline grows in complexity so will the dependency on data which is not relevant to the test in question, but still required to allow the pipeline to run to completion without error. Tests which rely on the plugin pipeline execution are more likely to start failing some time after they have been written because changes to any of the individual plugins called within the plugin pipeline introduce further dependencies.<br /><br />This is not to say that we shouldn't use pipeline simulation, just that we should think about the reasons why we are using it and should only use it when our tests require the entire pipeline to run, such as when testing calls from Azure Functions which require the plugin pipeline to execute. We can consider there to be three levels of plugin testing which can be carried out using FakeXrmEasy:<ul><li>Method</li><li>Plugin</li><li>Pipeline<br /><br /></li></ul> Ideally, the code in your plugins should be broken into discrete methods for each function within the plugin. True Unit Testing will be done at this level - a method is called with certain parameters and a single outcome of that method call can be tested.<br /><br />Sometimes we want to be able to test the execution of an entire plugin, not just the individual methods within it. We might want to test how the plugin reacts when called with different parameters, such as different messages, targets or images. Or what happens when a pluginn which contains multiple methods is called.<br /><br />Finally, we may sometimes need to invoke the entire plugin pipeline. This should not so much be because we want to <em>test </em>the entire pipeline, although we may sometimes want to do this, but more because the code we are testing relies on the plugin pipeline executing because (for example) it expects a certain record to have been created or updated during the pipeline execution which is required later in the code being tested. It's also really useful to be able to test that your plugin step registration has been configured correctly.<br /><br />As a developer, it's really up to you to make a judgement call as to which level of testing you will use and you will most likely find that you will use a mixture of all three methods to acheive a good level of code coverage and robustness in your tests.<br /><br />I'd recommend watching the excellent videos explaining Pipeline Simulation on the <a href="https://www.youtube.com/@fakexrmeasy" target="_blank">FakeXrmEasy youtube channel</a>.<br /><br />In conclusion, I think that pipeline simulation, especially when used in conjunction with automatic plugin registration using a tool like spkl, is a <em>really</em> useful feature of FakeXrmEasy. I don't see it as a replacement for the other methods of testing plugin code, but an invaluable tool when you need to be able to test more than just the code in a specific plugin. It brings us ever closer to the ideal of being able to fully test our Dataverse code locally before we deploy it to a real Dynamics instance.<br /></div>]]></content:encoded></item><item><title><![CDATA[Adding Sitemap icons for Out Of the Box tables which have no SVG icons]]></title><link><![CDATA[http://www.bwmodular.org/blog/adding-sitemap-icons-for-out-of-the-box-tables-which-have-no-svg-icons]]></link><comments><![CDATA[http://www.bwmodular.org/blog/adding-sitemap-icons-for-out-of-the-box-tables-which-have-no-svg-icons#comments]]></comments><pubDate>Sun, 03 Oct 2021 00:00:00 GMT</pubDate><category><![CDATA[Uncategorized]]></category><guid isPermaLink="false">http://www.bwmodular.org/blog/adding-sitemap-icons-for-out-of-the-box-tables-which-have-no-svg-icons</guid><description><![CDATA[There are a number of Out Of the Box (OOB) tables in Dynamics 365 which have no SVG icons. Some examples include Order Products, Invoice Products and Services. This doesn't really present a problem until you decide to add one of these icons to your Sitemap.      As the table has no SVG icon, adding the table to the Sitemap results in the table being rendered like this (in Chrome):         or this (in Firefox):         So, how do we add an icon to these tables so they display as we would want the [...] ]]></description><content:encoded><![CDATA[<div class="paragraph">There are a number of Out Of the Box (OOB) tables in Dynamics 365 which have no SVG icons. Some examples include Order Products, Invoice Products and Services. This doesn't really present a problem until you decide to add one of these icons to your Sitemap.<br /></div>  <div>  <!--BLOG_SUMMARY_END--></div>  <div class="paragraph">As the table has no SVG icon, adding the table to the Sitemap results in the table being rendered like this (in Chrome):<br /></div>  <div><div class="wsite-image wsite-image-border-thin " style="padding-top:10px;padding-bottom:10px;margin-left:0px;margin-right:0px;text-align:center"> <a> <img src="http://www.bwmodular.org/uploads/1/3/0/4/130443290/published/missingiconschrome.png?1633206987" alt="Picture" style="width:auto;max-width:100%" /> </a> <div style="display:block;font-size:90%"></div> </div></div>  <div class="paragraph">or this (in Firefox):<br /></div>  <div><div class="wsite-image wsite-image-border-thin " style="padding-top:10px;padding-bottom:10px;margin-left:0px;margin-right:0px;text-align:center"> <a> <img src="http://www.bwmodular.org/uploads/1/3/0/4/130443290/missingiconsfirefox_orig.png" alt="Picture" style="width:auto;max-width:100%" /> </a> <div style="display:block;font-size:90%"></div> </div></div>  <div class="paragraph">So, how do we add an icon to these tables so they display as we would want them to in the Sitemap? If we open our solution in the old customiser interface we can see that the 'Update Icons' button that exists for custom entities does not appear, and if we look in the PowerApps portal, again, there is no option to add a web resource as an icon as there is for custom entities.<br />The entity does not even show up in the list of tables in the XRM Toolbox 'Iconator' tool (<a href="https://www.xrmtoolbox.com/plugins/MscrmTools.Iconator" target="_blank">https://www.xrmtoolbox.com/plugins/MscrmTools.Iconator</a>), so we can't use that to give the table an icon.<br />Finally, if we look in the App SiteMap editor, the table has the option 'Use Default Image' displayed but this is greyed out with no option to change it.<br /></div>  <div><div class="wsite-image wsite-image-border-none " style="padding-top:10px;padding-bottom:10px;margin-left:0;margin-right:0;text-align:center"> <a> <img src="http://www.bwmodular.org/uploads/1/3/0/4/130443290/usedefaultimage_orig.png" alt="Picture" style="width:auto;max-width:100%" /> </a> <div style="display:block;font-size:90%"></div> </div></div>  <div class="paragraph">But you <em>can</em> add an icon for the table to use in the sitemap, using the following very counter-intuitiuve approach:<br />In Advanced Settings, select the Settings -&gt; Customizations option and then click 'Customize the System' to open the Default solution:<br /></div>  <div><div class="wsite-image wsite-image-border-none " style="padding-top:10px;padding-bottom:10px;margin-left:0;margin-right:0;text-align:center"> <a> <img src="http://www.bwmodular.org/uploads/1/3/0/4/130443290/customisethesystem_orig.png" alt="Picture" style="width:auto;max-width:100%" /> </a> <div style="display:block;font-size:90%"></div> </div></div>  <div class="paragraph">Now navigate down to Model Driven Apps and from here open the App whose Sitemap you want to fix:<br /></div>  <div><div class="wsite-image wsite-image-border-none " style="padding-top:10px;padding-bottom:10px;margin-left:0;margin-right:0;text-align:center"> <a> <img src="http://www.bwmodular.org/uploads/1/3/0/4/130443290/modeldrivenapps_orig.png" alt="Picture" style="width:auto;max-width:100%" /> </a> <div style="display:block;font-size:90%"></div> </div></div>  <div class="paragraph">This will open the App Designer just as if you had opened it from the Power Apps portal, but this time if you go to the Sitemap Editor via this route, the option to choose an icon has now become enabled and you can select from any of the out of the box or custom icons available in your environment.<br /></div>  <div><div class="wsite-image wsite-image-border-none " style="padding-top:10px;padding-bottom:10px;margin-left:0;margin-right:0;text-align:center"> <a> <img src="http://www.bwmodular.org/uploads/1/3/0/4/130443290/chooseaniconinthesitemapeditor_orig.png" alt="Picture" style="width:auto;max-width:100%" /> </a> <div style="display:block;font-size:90%"></div> </div></div>  <div class="paragraph">Choosing an icon and saving the Sitemap results in the icon being displayed as you'd expect. I don't know if this behaviour is by design, as it doesn't really make sense that the only way to do this is to edit the Model-Driven App through the Default Solution, but it works!<br /><br />On a final&nbsp; note, the solution above works when you want to add an entity with no SVG icon to the Sitemap, but it does not help when one of these entities appears as a lookup on a form. Here's the OOB Unit Of Measure entity as it appears on one of the OOB forms - if you know how to get rid of that 'Jigsaw puzzle piece' icon, do let me know!<br /></div>  <div><div class="wsite-image wsite-image-border-none " style="padding-top:10px;padding-bottom:10px;margin-left:0;margin-right:0;text-align:center"> <a> <img src="http://www.bwmodular.org/uploads/1/3/0/4/130443290/jigsawicononunitofmeasure_orig.png" alt="Picture" style="width:auto;max-width:100%" /> </a> <div style="display:block;font-size:90%"></div> </div></div>  <div class="paragraph">With many thanks to Mishel Achkov from Microsoft for pointing out this solution to me.<br /></div>]]></content:encoded></item><item><title><![CDATA[Mocking unimplemented organisation requests in FakeXrmEasy]]></title><link><![CDATA[http://www.bwmodular.org/blog/mocking-unimplemented-organisation-requests-in-fakexrmeasy]]></link><comments><![CDATA[http://www.bwmodular.org/blog/mocking-unimplemented-organisation-requests-in-fakexrmeasy#comments]]></comments><pubDate>Sat, 10 Jul 2021 00:00:00 GMT</pubDate><category><![CDATA[Dynamics 365]]></category><category><![CDATA[Dynamics CRM]]></category><category><![CDATA[fakexrmeasy]]></category><category><![CDATA[Testing]]></category><guid isPermaLink="false">http://www.bwmodular.org/blog/mocking-unimplemented-organisation-requests-in-fakexrmeasy</guid><description><![CDATA[FakeXrmEasy is an invaluable tool for development of Dynamics CRM/365/Dataverse software and using it will make your life as a developer much easier and will undoubtedly help you write more robust, maintainable code. FakeXrmEasy implements an in-memory version of the XRM database and provides mocked version of most of the main methods provided by the IOrganisation service. You may, however, occasionally come across a method which has not been implemented in the library. When you test code which  [...] ]]></description><content:encoded><![CDATA[<div class="paragraph"><a href="https://github.com/jordimontana82/fake-xrm-easy" target="_blank">FakeXrmEasy</a> is an invaluable tool for development of Dynamics CRM/365/Dataverse software and using it will make your life as a developer much easier and will undoubtedly help you write more robust, maintainable code. FakeXrmEasy implements an in-memory version of the XRM database and provides mocked version of most of the main methods provided by the IOrganisation service. You may, however, occasionally come across a method which has not been implemented in the library. When you test code which calls one of these methods, your test will fail with a message like this: <em>The organization request type 'Microsoft.Crm.Sdk.Messages.NameOfTheUnimplementedRequest' is not yet supported... but we DO love pull requests so please feel free to submit one! :). This functionality is not available yet. Please consider contributing to the following Git project https://github.com/jordimontana82/fake-xrm-easy by cloning the repository and issuing a pull request."</em><br /></div>  <div>  <!--BLOG_SUMMARY_END--></div>  <div class="paragraph" style="text-align:left;">My first reaction when I came across this test was dismay as I thought that I would not be able to continue testing without first having to implement the unimplemented method, but in fact, this is far from the case. The library provides a number of ways to very easily to invoke these unimplemented methods without having to contribute to the FakeXrmEasy project.<br /><br />Whilst I would encourage everyone to contribute to this or any other Open Source project, there is actually good reason why some of these requests are unimplemented - in fact, there is no need for all organisation requests to be implemented in the FakeXRMEasy library.<br /><br />Clearly, the most obvious reason for a method not being implemented in FakeXrmEasy is that the XRM SDK is growing all of the time, and the FakeXrmEasy contributors may never have the time to implement the ever growing number of requests.<br /><br />Another reason, however, is that not every method needs to be implemented as Microsoft themselves have implemented it, because this is not, or should not, be relevant to your testing. Take, as an example, the OrganisationRequest which converts a Word Template into a PDF document as discussed <a href="http://www.bwmodular.org/blog/real-time-pdf-generation-from-word-templates-in-dynamics-365" target="_blank">here</a>. For the contributors to the FakeXrmEasy project to try and implement this request would be a collosal waste of time, and there is a very high chance that their implementation would be nothing like Microsoft's. But why would you need a faked implementation of this method to produce the same output as Microsoft's? Your tests should really not be interested in the contents of the PDF that gets produced - it's Microsoft's job to test that their implementation works correctly, and your unit tests should not really care about the actual contents of the PDF.<br /><br />Be that as it may, if you have code which at some point calls this particular Organisation Request, you don't want your tests to fail with the "not yet supported..." error message, and I will show you a couple of ways in which you can very easily implement fakes of these methods so that you can continue testing what's important - ie, your own code.<br /><br /><u><strong><span><span>AddExecutionMock</span></span></strong></u><br /><span><span>This is the easiest way to add a mocked execution </span></span>of an Organisation request to your tests and acording to Jordi Monta&ntilde;a, "should be used when you just want to override something ocasionally. <a href="https://github.com/jordimontana82/fake-xrm-easy/blob/5531248d7e9dc2a112b17735e0bf5939e974d342/FakeXrmEasy.Shared/XrmFakedContext.cs#L184" target="_blank">AddExecutionMock</a> is the easiest since you can override one fake message easily inline with your unit test." As a simple example, let's look at faking a method which does nothing but return a simple Success or Failure response. An example of this is the <em>CalculateRollupFieldRequest</em> - If we have a plugin which calls this method, we most likely don't really care how Microsoft implements this in the background, we just want to be able to call it from our plugin and for the request not to fail. We can do this using the <em>AddExecutionMock</em> method.<br /><br />To do this, we need to add a call to <em>AddExecutionMock</em> when we are initialising our test, after we have instantiated our faked context, so our initialisation might look like this:<br /></div>  <div id="248251333206535935"><div><style type="text/css">	#element-cc16305e-4340-4260-82aa-d6d0f2a223cf .code-editor--light {  padding: 20px 0px;}#element-cc16305e-4340-4260-82aa-d6d0f2a223cf .code-editor--light .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #C9CDCF;  border-right: 1px solid #C9CDCF;  border-top: 1px solid #C9CDCF;  background-color: #F8F8F8;  color: #363B3E;}#element-cc16305e-4340-4260-82aa-d6d0f2a223cf .code-editor--light .header .paragraph {  margin: 0;}#element-cc16305e-4340-4260-82aa-d6d0f2a223cf .code-editor--light .body-code {  margin: 0;  border: 1px solid #C9CDCF;  background-color: #FFFFFF;  color: #666C70;}#element-cc16305e-4340-4260-82aa-d6d0f2a223cf .code-editor--dark {  padding: 20px 0px;}#element-cc16305e-4340-4260-82aa-d6d0f2a223cf .code-editor--dark .ace-tomorrow-night-eighties {  background-color: #363B3E;}#element-cc16305e-4340-4260-82aa-d6d0f2a223cf .code-editor--dark .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #E0E1E2;  border-right: 1px solid #E0E1E2;  border-top: 1px solid #E0E1E2;  background-color: #666C70;  color: #FFFFFF;}#element-cc16305e-4340-4260-82aa-d6d0f2a223cf .code-editor--dark .header .paragraph {  margin: 0;}#element-cc16305e-4340-4260-82aa-d6d0f2a223cf .code-editor--dark .body-code {  margin: 0;  border: 1px solid #E0E1E2;  background-color: #363B3E;  color: #F8F8F8;}</style><div id="element-cc16305e-4340-4260-82aa-d6d0f2a223cf" data-platform-element-id="270170748587580171-1.3.3" class="platform-element-contents">	<div class="code-editor--light">    <div class="header">        <div class="paragraph"></div>    </div>    <div class="body-code">        <pre class="editor"></pre>    </div></div></div><div style="clear:both;"></div></div></div>  <div class="paragraph">We now need to add our own mocked implementation of the <em>CalculateRollUpFieldRequest</em> method. As the <em>CalculateRollUpFieldRequest </em>returns a <em>CalculateRollupFieldResponse, </em>our implementation just needs to return a <em>CalculateRollupFieldResponse</em>.<br /></div>  <div id="848363102127523609"><div><style type="text/css">	#element-a8bebfce-ec8f-4aaf-a5d3-8d3b1ac2669c .code-editor--light {  padding: 20px 0px;}#element-a8bebfce-ec8f-4aaf-a5d3-8d3b1ac2669c .code-editor--light .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #C9CDCF;  border-right: 1px solid #C9CDCF;  border-top: 1px solid #C9CDCF;  background-color: #F8F8F8;  color: #363B3E;}#element-a8bebfce-ec8f-4aaf-a5d3-8d3b1ac2669c .code-editor--light .header .paragraph {  margin: 0;}#element-a8bebfce-ec8f-4aaf-a5d3-8d3b1ac2669c .code-editor--light .body-code {  margin: 0;  border: 1px solid #C9CDCF;  background-color: #FFFFFF;  color: #666C70;}#element-a8bebfce-ec8f-4aaf-a5d3-8d3b1ac2669c .code-editor--dark {  padding: 20px 0px;}#element-a8bebfce-ec8f-4aaf-a5d3-8d3b1ac2669c .code-editor--dark .ace-tomorrow-night-eighties {  background-color: #363B3E;}#element-a8bebfce-ec8f-4aaf-a5d3-8d3b1ac2669c .code-editor--dark .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #E0E1E2;  border-right: 1px solid #E0E1E2;  border-top: 1px solid #E0E1E2;  background-color: #666C70;  color: #FFFFFF;}#element-a8bebfce-ec8f-4aaf-a5d3-8d3b1ac2669c .code-editor--dark .header .paragraph {  margin: 0;}#element-a8bebfce-ec8f-4aaf-a5d3-8d3b1ac2669c .code-editor--dark .body-code {  margin: 0;  border: 1px solid #E0E1E2;  background-color: #363B3E;  color: #F8F8F8;}</style><div id="element-a8bebfce-ec8f-4aaf-a5d3-8d3b1ac2669c" data-platform-element-id="270170748587580171-1.3.3" class="platform-element-contents">	<div class="code-editor--light">    <div class="header">        <div class="paragraph">Code Editor</div>    </div>    <div class="body-code">        <pre class="editor"></pre>    </div></div></div><div style="clear:both;"></div></div></div>  <div class="paragraph">If our code needs to check the outcome of the request for some reason, then it can set the properties of the response like this:</div>  <div id="402066816579042325"><div><style type="text/css">	#element-4ed181ce-5866-4da2-860d-edb5c5879110 .code-editor--light {  padding: 20px 0px;}#element-4ed181ce-5866-4da2-860d-edb5c5879110 .code-editor--light .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #C9CDCF;  border-right: 1px solid #C9CDCF;  border-top: 1px solid #C9CDCF;  background-color: #F8F8F8;  color: #363B3E;}#element-4ed181ce-5866-4da2-860d-edb5c5879110 .code-editor--light .header .paragraph {  margin: 0;}#element-4ed181ce-5866-4da2-860d-edb5c5879110 .code-editor--light .body-code {  margin: 0;  border: 1px solid #C9CDCF;  background-color: #FFFFFF;  color: #666C70;}#element-4ed181ce-5866-4da2-860d-edb5c5879110 .code-editor--dark {  padding: 20px 0px;}#element-4ed181ce-5866-4da2-860d-edb5c5879110 .code-editor--dark .ace-tomorrow-night-eighties {  background-color: #363B3E;}#element-4ed181ce-5866-4da2-860d-edb5c5879110 .code-editor--dark .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #E0E1E2;  border-right: 1px solid #E0E1E2;  border-top: 1px solid #E0E1E2;  background-color: #666C70;  color: #FFFFFF;}#element-4ed181ce-5866-4da2-860d-edb5c5879110 .code-editor--dark .header .paragraph {  margin: 0;}#element-4ed181ce-5866-4da2-860d-edb5c5879110 .code-editor--dark .body-code {  margin: 0;  border: 1px solid #E0E1E2;  background-color: #363B3E;  color: #F8F8F8;}</style><div id="element-4ed181ce-5866-4da2-860d-edb5c5879110" data-platform-element-id="270170748587580171-1.3.3" class="platform-element-contents">	<div class="code-editor--light">    <div class="header">        <div class="paragraph"></div>    </div>    <div class="body-code">        <pre class="editor"></pre>    </div></div></div><div style="clear:both;"></div></div></div>  <div class="paragraph">That's all it takes to Mock this request. Your unit tests should now continue to work when they hit the <em>call to CalculateRollUpFieldRequest.</em> There is also a RemoveExecutionMock method which you can call if you want to remove this mocked execution, which might be useful if you wanted to write two different implementations (if, for example, you wanted to be able to test what happens when your response returns failure rather than success). This can just be called like this:<br /></div>  <div id="744229155734876970"><div><style type="text/css">	#element-5bc5792a-7ff4-4427-ad0a-b7a9cbe575df .code-editor--light {  padding: 20px 0px;}#element-5bc5792a-7ff4-4427-ad0a-b7a9cbe575df .code-editor--light .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #C9CDCF;  border-right: 1px solid #C9CDCF;  border-top: 1px solid #C9CDCF;  background-color: #F8F8F8;  color: #363B3E;}#element-5bc5792a-7ff4-4427-ad0a-b7a9cbe575df .code-editor--light .header .paragraph {  margin: 0;}#element-5bc5792a-7ff4-4427-ad0a-b7a9cbe575df .code-editor--light .body-code {  margin: 0;  border: 1px solid #C9CDCF;  background-color: #FFFFFF;  color: #666C70;}#element-5bc5792a-7ff4-4427-ad0a-b7a9cbe575df .code-editor--dark {  padding: 20px 0px;}#element-5bc5792a-7ff4-4427-ad0a-b7a9cbe575df .code-editor--dark .ace-tomorrow-night-eighties {  background-color: #363B3E;}#element-5bc5792a-7ff4-4427-ad0a-b7a9cbe575df .code-editor--dark .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #E0E1E2;  border-right: 1px solid #E0E1E2;  border-top: 1px solid #E0E1E2;  background-color: #666C70;  color: #FFFFFF;}#element-5bc5792a-7ff4-4427-ad0a-b7a9cbe575df .code-editor--dark .header .paragraph {  margin: 0;}#element-5bc5792a-7ff4-4427-ad0a-b7a9cbe575df .code-editor--dark .body-code {  margin: 0;  border: 1px solid #E0E1E2;  background-color: #363B3E;  color: #F8F8F8;}</style><div id="element-5bc5792a-7ff4-4427-ad0a-b7a9cbe575df" data-platform-element-id="270170748587580171-1.3.3" class="platform-element-contents">	<div class="code-editor--light">    <div class="header">        <div class="paragraph"></div>    </div>    <div class="body-code">        <pre class="editor"></pre>    </div></div></div><div style="clear:both;"></div></div></div>  <div class="paragraph"><strong>Another example - mocking the generation of a PDF from a Word Tempalate</strong><br />Another example of how easily an OrganisationRequest can be mocked is the OrganisationRequest I mentioned earlier which <a href="http://www.bwmodular.org/blog/real-time-pdf-generation-from-word-templates-in-dynamics-365" target="_blank">converts a Word Template to a PDF</a>. Our tests should not really care about the content of the PDF generated, but if called from our code, the method does need to return <em>something</em>, so how do we go about adding a faked implementation of this request?<br /><br />As before we need to add a faked implementation to our faked context when we initialise our tests:<br /></div>  <div id="232498814546614101"><div><style type="text/css">	#element-8bf1b181-590c-48bc-9f5d-bdab7871b624 .code-editor--light {  padding: 20px 0px;}#element-8bf1b181-590c-48bc-9f5d-bdab7871b624 .code-editor--light .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #C9CDCF;  border-right: 1px solid #C9CDCF;  border-top: 1px solid #C9CDCF;  background-color: #F8F8F8;  color: #363B3E;}#element-8bf1b181-590c-48bc-9f5d-bdab7871b624 .code-editor--light .header .paragraph {  margin: 0;}#element-8bf1b181-590c-48bc-9f5d-bdab7871b624 .code-editor--light .body-code {  margin: 0;  border: 1px solid #C9CDCF;  background-color: #FFFFFF;  color: #666C70;}#element-8bf1b181-590c-48bc-9f5d-bdab7871b624 .code-editor--dark {  padding: 20px 0px;}#element-8bf1b181-590c-48bc-9f5d-bdab7871b624 .code-editor--dark .ace-tomorrow-night-eighties {  background-color: #363B3E;}#element-8bf1b181-590c-48bc-9f5d-bdab7871b624 .code-editor--dark .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #E0E1E2;  border-right: 1px solid #E0E1E2;  border-top: 1px solid #E0E1E2;  background-color: #666C70;  color: #FFFFFF;}#element-8bf1b181-590c-48bc-9f5d-bdab7871b624 .code-editor--dark .header .paragraph {  margin: 0;}#element-8bf1b181-590c-48bc-9f5d-bdab7871b624 .code-editor--dark .body-code {  margin: 0;  border: 1px solid #E0E1E2;  background-color: #363B3E;  color: #F8F8F8;}</style><div id="element-8bf1b181-590c-48bc-9f5d-bdab7871b624" data-platform-element-id="270170748587580171-1.3.3" class="platform-element-contents">	<div class="code-editor--light">    <div class="header">        <div class="paragraph"></div>    </div>    <div class="body-code">        <pre class="editor"></pre>    </div></div></div><div style="clear:both;"></div></div></div>  <div class="paragraph">We then need to add a faked version of the request - the OrganisationRequest we are faking has a property called "PdfFile" and returns the PDF as a byte stream, but we really don't need to return a fully formed PDF, so we can just fake the request and its response like this:<br /></div>  <div id="291083428157585504"><div><style type="text/css">	#element-db8fe834-5017-4898-9f4a-f32bd851786e .code-editor--light {  padding: 20px 0px;}#element-db8fe834-5017-4898-9f4a-f32bd851786e .code-editor--light .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #C9CDCF;  border-right: 1px solid #C9CDCF;  border-top: 1px solid #C9CDCF;  background-color: #F8F8F8;  color: #363B3E;}#element-db8fe834-5017-4898-9f4a-f32bd851786e .code-editor--light .header .paragraph {  margin: 0;}#element-db8fe834-5017-4898-9f4a-f32bd851786e .code-editor--light .body-code {  margin: 0;  border: 1px solid #C9CDCF;  background-color: #FFFFFF;  color: #666C70;}#element-db8fe834-5017-4898-9f4a-f32bd851786e .code-editor--dark {  padding: 20px 0px;}#element-db8fe834-5017-4898-9f4a-f32bd851786e .code-editor--dark .ace-tomorrow-night-eighties {  background-color: #363B3E;}#element-db8fe834-5017-4898-9f4a-f32bd851786e .code-editor--dark .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #E0E1E2;  border-right: 1px solid #E0E1E2;  border-top: 1px solid #E0E1E2;  background-color: #666C70;  color: #FFFFFF;}#element-db8fe834-5017-4898-9f4a-f32bd851786e .code-editor--dark .header .paragraph {  margin: 0;}#element-db8fe834-5017-4898-9f4a-f32bd851786e .code-editor--dark .body-code {  margin: 0;  border: 1px solid #E0E1E2;  background-color: #363B3E;  color: #F8F8F8;}</style><div id="element-db8fe834-5017-4898-9f4a-f32bd851786e" data-platform-element-id="270170748587580171-1.3.3" class="platform-element-contents">	<div class="code-editor--light">    <div class="header">        <div class="paragraph"></div>    </div>    <div class="body-code">        <pre class="editor"></pre>    </div></div></div><div style="clear:both;"></div></div></div>  <div class="paragraph">As before, we have the option of calling the <em>RemoveExecutionMock()</em> method if we want to implement the method if a different way during this test execution.<br /><br /><strong>AddFakeMessageExecutor</strong><br />According to Jordi, "if you want to override a message that is meant to be more generic and re-used across tests, then the <a href="https://github.com/jordimontana82/fake-xrm-easy/blob/f294b9af7319f8e23d4032a9290ec75a628f2435/FakeXrmEasy.Shared/XrmFakedContext.cs#L197">AddFakeMessageExecutor</a> is meant to be used for that, since you can encapsulate the logic in a separate class and pass it your context in every test". The implementation of the FakeMessageExecutor is slightly more complex, but not significantly so. The most significant difference with this method is that it gets the test execution passed into it. This means that, as Jordi says, the implementation can be written into a seperate class which does not need to 'know' the context of the Unit Test, as the context will be passed in as a parameter when called.<br /><br />Let's take as an example this time a slightly more complex OrganisationRequest, the <em>CloseIncidentRequest</em>. If we do not care the outcome of this request, we could just implement this as a method which returns an empty <em>CloseIncidentResponse </em>in the same way as our first example above. But if we want the request to act in the same way as a real <em>CloseIncidentRequest</em> would by creating an IncidentResolution entity in the database and changing the Status and Status reason of the Incident, we can do this as well, using the context.<br />As before we start by adding a call to the AddFakeMessageExecutor method when we initialise our test.<br /></div>  <div id="972444816327731376"><div><style type="text/css">	#element-e5ed3a6b-b9e5-4e03-a12e-f867d74c89a2 .code-editor--light {  padding: 20px 0px;}#element-e5ed3a6b-b9e5-4e03-a12e-f867d74c89a2 .code-editor--light .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #C9CDCF;  border-right: 1px solid #C9CDCF;  border-top: 1px solid #C9CDCF;  background-color: #F8F8F8;  color: #363B3E;}#element-e5ed3a6b-b9e5-4e03-a12e-f867d74c89a2 .code-editor--light .header .paragraph {  margin: 0;}#element-e5ed3a6b-b9e5-4e03-a12e-f867d74c89a2 .code-editor--light .body-code {  margin: 0;  border: 1px solid #C9CDCF;  background-color: #FFFFFF;  color: #666C70;}#element-e5ed3a6b-b9e5-4e03-a12e-f867d74c89a2 .code-editor--dark {  padding: 20px 0px;}#element-e5ed3a6b-b9e5-4e03-a12e-f867d74c89a2 .code-editor--dark .ace-tomorrow-night-eighties {  background-color: #363B3E;}#element-e5ed3a6b-b9e5-4e03-a12e-f867d74c89a2 .code-editor--dark .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #E0E1E2;  border-right: 1px solid #E0E1E2;  border-top: 1px solid #E0E1E2;  background-color: #666C70;  color: #FFFFFF;}#element-e5ed3a6b-b9e5-4e03-a12e-f867d74c89a2 .code-editor--dark .header .paragraph {  margin: 0;}#element-e5ed3a6b-b9e5-4e03-a12e-f867d74c89a2 .code-editor--dark .body-code {  margin: 0;  border: 1px solid #E0E1E2;  background-color: #363B3E;  color: #F8F8F8;}</style><div id="element-e5ed3a6b-b9e5-4e03-a12e-f867d74c89a2" data-platform-element-id="270170748587580171-1.3.3" class="platform-element-contents">	<div class="code-editor--light">    <div class="header">        <div class="paragraph"></div>    </div>    <div class="body-code">        <pre class="editor"></pre>    </div></div></div><div style="clear:both;"></div></div></div>  <div class="paragraph">We then need to implement the faked version of the request by implementing an instance of the IFakeMessageExecutor interface:<br /></div>  <div id="130247015480145198"><div><style type="text/css">	#element-316208ba-a1d0-43c0-a00a-a3ce73f95afe .code-editor--light {  padding: 20px 0px;}#element-316208ba-a1d0-43c0-a00a-a3ce73f95afe .code-editor--light .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #C9CDCF;  border-right: 1px solid #C9CDCF;  border-top: 1px solid #C9CDCF;  background-color: #F8F8F8;  color: #363B3E;}#element-316208ba-a1d0-43c0-a00a-a3ce73f95afe .code-editor--light .header .paragraph {  margin: 0;}#element-316208ba-a1d0-43c0-a00a-a3ce73f95afe .code-editor--light .body-code {  margin: 0;  border: 1px solid #C9CDCF;  background-color: #FFFFFF;  color: #666C70;}#element-316208ba-a1d0-43c0-a00a-a3ce73f95afe .code-editor--dark {  padding: 20px 0px;}#element-316208ba-a1d0-43c0-a00a-a3ce73f95afe .code-editor--dark .ace-tomorrow-night-eighties {  background-color: #363B3E;}#element-316208ba-a1d0-43c0-a00a-a3ce73f95afe .code-editor--dark .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #E0E1E2;  border-right: 1px solid #E0E1E2;  border-top: 1px solid #E0E1E2;  background-color: #666C70;  color: #FFFFFF;}#element-316208ba-a1d0-43c0-a00a-a3ce73f95afe .code-editor--dark .header .paragraph {  margin: 0;}#element-316208ba-a1d0-43c0-a00a-a3ce73f95afe .code-editor--dark .body-code {  margin: 0;  border: 1px solid #E0E1E2;  background-color: #363B3E;  color: #F8F8F8;}</style><div id="element-316208ba-a1d0-43c0-a00a-a3ce73f95afe" data-platform-element-id="270170748587580171-1.3.3" class="platform-element-contents">	<div class="code-editor--light">    <div class="header">        <div class="paragraph"></div>    </div>    <div class="body-code">        <pre class="editor"></pre>    </div></div></div><div style="clear:both;"></div></div></div>  <div class="paragraph">That's all it takes to add a Fake implementation of the <em>CloseIncidentRequest</em>. As before, there is a <em>RemoveFakeMessageExecutor </em>method which can be used to remove this faked implementation if you need to use more than one faked implementation in your test class.<br /><br /><strong>AddGenericFakeMessageExecutor</strong><br />Finally, the FakeXrmEasy library contains a method <a href="https://github.com/jordimontana82/fake-xrm-easy/blob/f294b9af7319f8e23d4032a9290ec75a628f2435/FakeXrmEasy.Shared/XrmFakedContext.cs#L210-L216">AddGenericFakeMessageExecutor</a> which "was added to be able to execute late bound custom actions and out of the box requests which didn't have an OrganizationRequest class generated in the SDK but could still be invoked by creating an OrganizationRequest manually. "<br /><br />Jordi continues: "I think some devs wanted it to test calling some out of the box messages that were accessible in the API, but that really, shouldn't be called as it was supposed to be unsupported doing so.... :D&nbsp; I kept it because it's still useful for users who still use late bound actions...." <br /><br />I've never had to use this, but it works very similarly to the AddFakeMessageExecutor, and I'll make sure that I update this blog post if I do find myself using it. As with pretty much everything else in FakeXrmEasy, there are Unit Tests in the source code which demonstrate how to use this. Here's an example: <a href="https://github.com/jordimontana82/fake-xrm-easy/blob/f294b9af7319f8e23d4032a9290ec75a628f2435/FakeXrmEasy.Tests.Shared/FakeContextTests/TestGenericMessageExecutors.cs" target="_blank">https://github.com/jordimontana82/fake-xrm-easy/blob/f294b9af7319f8e23d4032a9290ec75a628f2435/FakeXrmEasy.Tests.Shared/FakeContextTests/TestGenericMessageExecutors.cs</a><br /><br />I hope this blog has helped explain how easy it is to write faked implementations of Organisation Requests which have not been implemented in FakeXrmEasy. Happy Testing!<br /></div>]]></content:encoded></item><item><title><![CDATA[Real-time PDF generation from Word Templates in Dynamics 365]]></title><link><![CDATA[http://www.bwmodular.org/blog/real-time-pdf-generation-from-word-templates-in-dynamics-365]]></link><comments><![CDATA[http://www.bwmodular.org/blog/real-time-pdf-generation-from-word-templates-in-dynamics-365#comments]]></comments><pubDate>Mon, 14 Jun 2021 00:00:00 GMT</pubDate><category><![CDATA[Dynamics 365]]></category><guid isPermaLink="false">http://www.bwmodular.org/blog/real-time-pdf-generation-from-word-templates-in-dynamics-365</guid><description><![CDATA[Update 12 October 2021: Microsoft have stated that neither the organisation request described below, nor the publicly documented 'exportpdf' API are intended for public use, and that this public document (docs.microsoft.com/en-us/dynamics365/sales-enterprise/developer/exportpdf-api) is soon to be removed.In October 2021, customers using the Organisation request from within a Plugin or Workflow started to see this error message:&ldquo;The private ExportPdfDocument request cannot be called from th [...] ]]></description><content:encoded><![CDATA[<div class="paragraph"><font size="5">Update 12 October 2021: Microsoft have stated that neither the organisation request described below, nor the publicly documented 'exportpdf' API are intended for public use, and that this public document (<a href="https://docs.microsoft.com/en-us/dynamics365/sales-enterprise/developer/exportpdf-api" target="_blank">docs.microsoft.com/en-us/dynamics365/sales-enterprise/developer/exportpdf-api</a>) is soon to be removed.</font><br />In October 2021, customers using the Organisation request from within a Plugin or Workflow started to see this error message:<br />&ldquo;<strong>The private ExportPdfDocument request cannot be called from this plug-in because it has a different solution publisher."</strong><br />This change was then reverted by Microsoft the following weekend in order to give customers time to find an alternative solution to this very common requirement.<br />If you think that |Microsoft should provide a publicly supported way of generating PDF's from Word Templates without having to first create a Word Document and save it in CRM, consider upvoting this idea:<br /><a href="https://experience.dynamics.com/ideas/idea/?ideaid=0c7f12cb-9a2a-ec11-b76a-0003ff45b7ab">Microsoft Idea &nbsp;&middot; Make 'ExportPdfDocument' organisation request and 'exportPdf ' API action public (dynamics.com)</a><br /><br /></div>  <div>  <!--BLOG_SUMMARY_END--></div>  <div class="paragraph" style="text-align:left;">A common requirement for users of Dynamics 365 is to generate PDF documents based on Word Templates. A common solution to this is to use Power Automate to generate the PDF and there are plenty of examples online on how to do this, such as <a href="https://community.dynamics.com/crm/b/bruce365usingdynamics/posts/generate-quote-pdf-attachments-in-dynamics-through-flow" target="_blank">this</a>. A disadvantage of this approach, however, is that the documents are created asynchronously and this means that users may have to wait an indeterminate amount of time, without feedback, before the Flow completes.<br />This is fine when the document is created as part of an automated process, such as overnight invoice generation, but not ideal when the requirement is to produce the document on demand.<br />Fortunately, since version 9.0.1905.2010 of Dynamics 365, an out-of-the-box method of generating PDF's from Word Templates has been available for the platform. Orginally announced for the Sales Hub, and only available for Account, Contact, Lead, Opportunity, Order, Quote, and Invoice tables, as of 2020 release wave 2 this has now been extended to all custom entities. (<a href="http://docs.microsoft.com/en-us/dynamics365/sales-enterprise/create-quote-pdf" target="_blank">docs.microsoft.com/en-us/dynamics365/sales-enterprise/create-quote-pdf</a>). At the moment, the ability to generate a PDF from an out-of-the-box form button is still a Sales Hub only feature, but there is, however, an OrganizationRequest (ExportPdfDocument) which can be used to generate a PDF from a Template.<br />The ExportPdfDocument request requires three parameters:<ul><li>the GUID of the Word template</li><li>the GUID of the record the PDF should be generated for</li><li>the ObjectTypeCode of the record the PDF should be generated for</li></ul><br />Thus we can create a method such as the following which takes the GUID of the relevant record and either the name or the GUID of the Word Template, and either the name or the ObjectType code of the relevant entity (with a couple of helper methods to retrieve the Guid of the Document and the ObjectTypeCode if not known):<br /><br /></div>  <div id="448079044392947644"><div><style type="text/css">	#element-23d12f0a-92af-4451-b777-839f23be721a .code-editor--light {  padding: 20px 0px;}#element-23d12f0a-92af-4451-b777-839f23be721a .code-editor--light .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #C9CDCF;  border-right: 1px solid #C9CDCF;  border-top: 1px solid #C9CDCF;  background-color: #F8F8F8;  color: #363B3E;}#element-23d12f0a-92af-4451-b777-839f23be721a .code-editor--light .header .paragraph {  margin: 0;}#element-23d12f0a-92af-4451-b777-839f23be721a .code-editor--light .body-code {  margin: 0;  border: 1px solid #C9CDCF;  background-color: #FFFFFF;  color: #666C70;}#element-23d12f0a-92af-4451-b777-839f23be721a .code-editor--dark {  padding: 20px 0px;}#element-23d12f0a-92af-4451-b777-839f23be721a .code-editor--dark .ace-tomorrow-night-eighties {  background-color: #363B3E;}#element-23d12f0a-92af-4451-b777-839f23be721a .code-editor--dark .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #E0E1E2;  border-right: 1px solid #E0E1E2;  border-top: 1px solid #E0E1E2;  background-color: #666C70;  color: #FFFFFF;}#element-23d12f0a-92af-4451-b777-839f23be721a .code-editor--dark .header .paragraph {  margin: 0;}#element-23d12f0a-92af-4451-b777-839f23be721a .code-editor--dark .body-code {  margin: 0;  border: 1px solid #E0E1E2;  background-color: #363B3E;  color: #F8F8F8;}</style><div id="element-23d12f0a-92af-4451-b777-839f23be721a" data-platform-element-id="270170748587580171-1.3.3" class="platform-element-contents">	<div class="code-editor--light">    <div class="header">        <div class="paragraph"></div>    </div>    <div class="body-code">        <pre class="editor"></pre>    </div></div></div><div style="clear:both;"></div></div></div>  <div class="paragraph" style="text-align:left;">The function returns the PDF as a ByteArray. What we do with this depends on our requirements. One such requirement might be to save the document as an annotation against the record the PDF was generated againts. This is easily done:<br /></div>  <div id="207790195837814707"><div><style type="text/css">	#element-662920be-88b8-4ebc-8f1c-7ba2b38738cd .code-editor--light {  padding: 20px 0px;}#element-662920be-88b8-4ebc-8f1c-7ba2b38738cd .code-editor--light .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #C9CDCF;  border-right: 1px solid #C9CDCF;  border-top: 1px solid #C9CDCF;  background-color: #F8F8F8;  color: #363B3E;}#element-662920be-88b8-4ebc-8f1c-7ba2b38738cd .code-editor--light .header .paragraph {  margin: 0;}#element-662920be-88b8-4ebc-8f1c-7ba2b38738cd .code-editor--light .body-code {  margin: 0;  border: 1px solid #C9CDCF;  background-color: #FFFFFF;  color: #666C70;}#element-662920be-88b8-4ebc-8f1c-7ba2b38738cd .code-editor--dark {  padding: 20px 0px;}#element-662920be-88b8-4ebc-8f1c-7ba2b38738cd .code-editor--dark .ace-tomorrow-night-eighties {  background-color: #363B3E;}#element-662920be-88b8-4ebc-8f1c-7ba2b38738cd .code-editor--dark .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #E0E1E2;  border-right: 1px solid #E0E1E2;  border-top: 1px solid #E0E1E2;  background-color: #666C70;  color: #FFFFFF;}#element-662920be-88b8-4ebc-8f1c-7ba2b38738cd .code-editor--dark .header .paragraph {  margin: 0;}#element-662920be-88b8-4ebc-8f1c-7ba2b38738cd .code-editor--dark .body-code {  margin: 0;  border: 1px solid #E0E1E2;  background-color: #363B3E;  color: #F8F8F8;}</style><div id="element-662920be-88b8-4ebc-8f1c-7ba2b38738cd" data-platform-element-id="270170748587580171-1.3.3" class="platform-element-contents">	<div class="code-editor--light">    <div class="header">        <div class="paragraph"></div>    </div>    <div class="body-code">        <pre class="editor"></pre>    </div></div></div><div style="clear:both;"></div></div></div>  <div class="paragraph">The exportPdf method is also available as a Web API Action. More details here:<br /><a href="https://docs.microsoft.com/en-us/dynamics365/sales-enterprise/developer/exportpdf-api" target="_blank">https://docs.microsoft.com/en-us/dynamics365/sales-enterprise/developer/exportpdf-api</a><br /></div>]]></content:encoded></item><item><title><![CDATA[Updating D365 Solution version numbers using LINQPad]]></title><link><![CDATA[http://www.bwmodular.org/blog/updating-d365-solution-version-numbers-using-linqpad]]></link><comments><![CDATA[http://www.bwmodular.org/blog/updating-d365-solution-version-numbers-using-linqpad#comments]]></comments><pubDate>Fri, 01 May 2020 00:00:00 GMT</pubDate><category><![CDATA[Dynamics 365]]></category><category><![CDATA[Dynamics CRM]]></category><category><![CDATA[linqpad]]></category><guid isPermaLink="false">http://www.bwmodular.org/blog/updating-d365-solution-version-numbers-using-linqpad</guid><description><![CDATA[In this article I will demonstrate how you can use a LINQPad script to very quickly update Dynamics Solution Numbers in a consistent way across all of your solutions.      &nbsp;Updating your solutions with a meaningful description and version number with every significant change is clearly good practice. It allows us to compare which versions have been deployed to which environments, helps us to see when versions were developed and what has been changed with each release.Additionally, it might  [...] ]]></description><content:encoded><![CDATA[<div class="paragraph">In this article I will demonstrate how you can use a LINQPad script to very quickly update Dynamics Solution Numbers in a consistent way across all of your solutions.<br /></div>  <div>  <!--BLOG_SUMMARY_END--></div>  <div class="paragraph">&nbsp;Updating your solutions with a meaningful description and version number with every significant change is clearly good practice. It allows us to compare which versions have been deployed to which environments, helps us to see when versions were developed and what has been changed with each release.<br />Additionally, it might be that you want to take advantage of the deployment option (available for example in  <a href="https://marketplace.visualstudio.com/items?itemName=WaelHamze.xrm-ci-framework-build-tasks" target="_blank">Wael Hamze's Azure Devops Tasks</a>) to only deploy the the CRM solution if the version number is different from that which exists in the target environment. (This might be, for example, because your check-in only contains new tests, or some other component which does not need a Dynamics solution deployment.<br /></div>  <div><div class="wsite-image wsite-image-border-none " style="padding-top:10px;padding-bottom:10px;margin-left:0;margin-right:0;text-align:center"> <a> <img src="http://www.bwmodular.org/uploads/1/3/0/4/130443290/overridesolution_orig.jpg" alt="Picture" style="width:auto;max-width:100%" /> </a> <div style="display:block;font-size:90%"></div> </div></div>  <div class="paragraph">&nbsp;So, updating Solution Version Numbers is can clearly be useful and good practice, but it's a little cumbersome to do this through the Dynamics front end - we have to log into Dynamics, navigate to the Solutions area, find the solution we've updated, open it, update the version number with whatever the appropriate version number is (and add a comment detailing the change we've made, of course)! Wouldn't it be great if we could simplify this process?<br />Well, with a little help from <a href="https://www.linqpad.net/" target="_blank">LINQPad</a> we can make this a whole lot easier and add some standardisation to the way we version our solutions at the same time. A solution is just an entity in Dynamics like any other, so we can easily retrieve and update the details of a solution using the Dynamics SDK. Here's a method we can use to retrieve a Solution given its name:<br /></div>  <div id="166461824215380531"><div><style type="text/css">	#element-dedb2d5c-e271-41aa-ac23-562fdf3bc766 .code-editor--light {  padding: 20px 0px;}#element-dedb2d5c-e271-41aa-ac23-562fdf3bc766 .code-editor--light .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #C9CDCF;  border-right: 1px solid #C9CDCF;  border-top: 1px solid #C9CDCF;  background-color: #F8F8F8;  color: #363B3E;}#element-dedb2d5c-e271-41aa-ac23-562fdf3bc766 .code-editor--light .header .paragraph {  margin: 0;}#element-dedb2d5c-e271-41aa-ac23-562fdf3bc766 .code-editor--light .body-code {  margin: 0;  border: 1px solid #C9CDCF;  background-color: #FFFFFF;  color: #666C70;}#element-dedb2d5c-e271-41aa-ac23-562fdf3bc766 .code-editor--dark {  padding: 20px 0px;}#element-dedb2d5c-e271-41aa-ac23-562fdf3bc766 .code-editor--dark .ace-tomorrow-night-eighties {  background-color: #363B3E;}#element-dedb2d5c-e271-41aa-ac23-562fdf3bc766 .code-editor--dark .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #E0E1E2;  border-right: 1px solid #E0E1E2;  border-top: 1px solid #E0E1E2;  background-color: #666C70;  color: #FFFFFF;}#element-dedb2d5c-e271-41aa-ac23-562fdf3bc766 .code-editor--dark .header .paragraph {  margin: 0;}#element-dedb2d5c-e271-41aa-ac23-562fdf3bc766 .code-editor--dark .body-code {  margin: 0;  border: 1px solid #E0E1E2;  background-color: #363B3E;  color: #F8F8F8;}</style><div id="element-dedb2d5c-e271-41aa-ac23-562fdf3bc766" data-platform-element-id="270170748587580171-1.3.3" class="platform-element-contents">	<div class="code-editor--light">    <div class="header">        <div class="paragraph"></div>    </div>    <div class="body-code">        <pre class="editor"></pre>    </div></div></div><div style="clear:both;"></div></div></div>  <div class="paragraph">Now that we have the details of the solution, we can use LINQPad's TextBox and Button controls to create a simple UI that allows the user to input a new description and save it, as you can see below.</div>  <div><div class="wsite-image wsite-image-border-none " style="padding-top:10px;padding-bottom:10px;margin-left:0;margin-right:0;text-align:center"> <a> <img src="http://www.bwmodular.org/uploads/1/3/0/4/130443290/autoversionsolutionnumber_orig.gif" alt="Picture" style="width:auto;max-width:100%" /> </a> <div style="display:block;font-size:90%"></div> </div></div>  <div class="paragraph">Here is the full text of the LINQpad script:<br /></div>  <div id="341489456800474227"><div><style type="text/css">	#element-c977af9d-1af2-47a1-a730-befa7855004b .code-editor--light {  padding: 20px 0px;}#element-c977af9d-1af2-47a1-a730-befa7855004b .code-editor--light .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #C9CDCF;  border-right: 1px solid #C9CDCF;  border-top: 1px solid #C9CDCF;  background-color: #F8F8F8;  color: #363B3E;}#element-c977af9d-1af2-47a1-a730-befa7855004b .code-editor--light .header .paragraph {  margin: 0;}#element-c977af9d-1af2-47a1-a730-befa7855004b .code-editor--light .body-code {  margin: 0;  border: 1px solid #C9CDCF;  background-color: #FFFFFF;  color: #666C70;}#element-c977af9d-1af2-47a1-a730-befa7855004b .code-editor--dark {  padding: 20px 0px;}#element-c977af9d-1af2-47a1-a730-befa7855004b .code-editor--dark .ace-tomorrow-night-eighties {  background-color: #363B3E;}#element-c977af9d-1af2-47a1-a730-befa7855004b .code-editor--dark .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #E0E1E2;  border-right: 1px solid #E0E1E2;  border-top: 1px solid #E0E1E2;  background-color: #666C70;  color: #FFFFFF;}#element-c977af9d-1af2-47a1-a730-befa7855004b .code-editor--dark .header .paragraph {  margin: 0;}#element-c977af9d-1af2-47a1-a730-befa7855004b .code-editor--dark .body-code {  margin: 0;  border: 1px solid #E0E1E2;  background-color: #363B3E;  color: #F8F8F8;}</style><div id="element-c977af9d-1af2-47a1-a730-befa7855004b" data-platform-element-id="270170748587580171-1.3.3" class="platform-element-contents">	<div class="code-editor--light">    <div class="header">        <div class="paragraph"></div>    </div>    <div class="body-code">        <pre class="editor"></pre>    </div></div></div><div style="clear:both;"></div></div></div>  <div class="paragraph">The format of the version number is a simple Year.Month.Date.IncrementalNumber, but you can clearly amend this to suit your own Versioning strategy. I've uploaded a copy of the script to LINQPad's Instant Share here: <a href="http://share.linqpad.net/uvdbxx.linq" target="_blank">share.linqpad.net/uvdbxx.linq</a><br /></div>]]></content:encoded></item><item><title><![CDATA[Generating an MFA Secret Key for Dynamics 365 testing]]></title><link><![CDATA[http://www.bwmodular.org/blog/generating-an-mfa-secret-key-for-dynamics-365-testing]]></link><comments><![CDATA[http://www.bwmodular.org/blog/generating-an-mfa-secret-key-for-dynamics-365-testing#comments]]></comments><pubDate>Mon, 09 Mar 2020 17:05:48 GMT</pubDate><category><![CDATA[Dynamics 365]]></category><category><![CDATA[Testing]]></category><guid isPermaLink="false">http://www.bwmodular.org/blog/generating-an-mfa-secret-key-for-dynamics-365-testing</guid><description><![CDATA[If you are using EasyRepro to perform automated tests against your Dynamics 365 instance and the account you are using is protected with Multi-Factor Authentication (MFA) you will need to generate an MFA Secret Key in order for your tests to be able to log in.      User accounts which are MFA protected will be met by a screen like this in which they must enter a code which will be generated by Microsoft and either sent to the user by email or SMS, or through the Microsoft Authenticator phone app [...] ]]></description><content:encoded><![CDATA[<div class="paragraph">If you are using <a href="https://github.com/microsoft/EasyRepro" target="_blank">EasyRepro</a> to perform automated tests against your Dynamics 365 instance and the account you are using is protected with Multi-Factor Authentication (MFA) you will need to generate an MFA Secret Key in order for your tests to be able to log in.<br /></div>  <div>  <!--BLOG_SUMMARY_END--></div>  <div class="paragraph">User accounts which are MFA protected will be met by a screen like this in which they must enter a code which will be generated by Microsoft and either sent to the user by email or SMS, or through the Microsoft Authenticator phone app.<br /></div>  <div><div class="wsite-image wsite-image-border-none " style="padding-top:10px;padding-bottom:10px;margin-left:0;margin-right:0;text-align:center"> <a> <img src="http://www.bwmodular.org/uploads/1/3/0/4/130443290/published/mfacodeprompt.png?1584660129" alt="Picture" style="width:361;max-width:100%" /> </a> <div style="display:block;font-size:90%"></div> </div></div>  <div class="paragraph">Using the method described in <a href="https://dev.to/j_sakamoto/selenium-testing---how-to-sign-in-to-two-factor-authentication-2joi" target="_blank">this post</a> it is possible to programatically generate the OTP (One TIme Password) needed to sign in and for your tests to use this to connect to the Power Platform as part of your tests. You can use the <a href="https://www.nuget.org/packages/Otp.NET/" target="_blank">Otp.Net</a> library to generate the OTP you need to log in.<br />The sample code in the <em>EasyRepro </em>solution already references this library, so in this article I will just show you how to generate the MFA 'secret key' which you will need to use in order to generate the OTP used in your tests.<br /><br />You can obtain an MFA secret key for your account using these instructions - you will need Microsoft's Authenticator App to get this working:<br /><br />1. Go to your Office365 'My account' page. This is available at: <a href="https://portal.office.com/account/">https://portal.office.com/account/</a> or by clicking on the 'cog' icon in Dynamics 365 and selecting 'Privacy and Cookies':</div>  <div><div class="wsite-image wsite-image-border-hairline " style="padding-top:0px;padding-bottom:0px;margin-left:0px;margin-right:0px;text-align:center"> <a> <img src="http://www.bwmodular.org/uploads/1/3/0/4/130443290/editor/privacyandcookies.png?1584221394" alt="Picture" style="width:294;max-width:100%" /> </a> <div style="display:block;font-size:90%"></div> </div></div>  <div class="paragraph">or by clicking 'My Account' from the top right menu in Outlook.com.<br /></div>  <div><div class="wsite-image wsite-image-border-none " style="padding-top:0px;padding-bottom:0px;margin-left:0px;margin-right:0px;text-align:center"> <a> <img src="http://www.bwmodular.org/uploads/1/3/0/4/130443290/editor/myaccount.png?1584221212" alt="Picture" style="width:256;max-width:100%" /> </a> <div style="display:block;font-size:90%"></div> </div></div>  <div class="paragraph">2. Click on 'Manage security &amp; privacy'<span></span><br /></div>  <div><div class="wsite-image wsite-image-border-hairline " style="padding-top:0px;padding-bottom:0px;margin-left:0px;margin-right:0px;text-align:center"> <a> <img src="http://www.bwmodular.org/uploads/1/3/0/4/130443290/editor/managesecurityandprivacy.png?1584222042" alt="Picture" style="width:336;max-width:100%" /> </a> <div style="display:block;font-size:90%"></div> </div></div>  <div class="paragraph">3. Click on 'Create and manage app passwords'<br /></div>  <div><div class="wsite-image wsite-image-border-hairline " style="padding-top:0px;padding-bottom:0px;margin-left:0px;margin-right:0px;text-align:center"> <a> <img src="http://www.bwmodular.org/uploads/1/3/0/4/130443290/createandmanageapppasswords_orig.png" alt="Picture" style="width:auto;max-width:100%" /> </a> <div style="display:block;font-size:90%"></div> </div></div>  <div class="paragraph">4. On the page which opens, there are two tabs which don't look like tabs. Click on the first of these: 'Additional security verification'<br />5. Click on 'Set up Authenticator app'<br /></div>  <div><div class="wsite-image wsite-image-border-hairline " style="padding-top:0px;padding-bottom:0px;margin-left:0px;margin-right:0px;text-align:center"> <a> <img src="http://www.bwmodular.org/uploads/1/3/0/4/130443290/editor/setupauthenticatorapp.png?1584660746" alt="Picture" style="width:578;max-width:100%" /> </a> <div style="display:block;font-size:90%"></div> </div></div>  <div class="paragraph">6. Click on 'Configure app without notifications'<br /></div>  <div><div class="wsite-image wsite-image-border-none " style="padding-top:0px;padding-bottom:0px;margin-left:0px;margin-right:0px;text-align:center"> <a> <img src="http://www.bwmodular.org/uploads/1/3/0/4/130443290/editor/configuremobileapp.png?1584660704" alt="Picture" style="width:460;max-width:100%" /> </a> <div style="display:block;font-size:90%"></div> </div></div>  <div class="paragraph">8. Make a note of the 'Secret Key' which is displayed<span></span><br /></div>  <div><div class="wsite-image wsite-image-border-hairline " style="padding-top:0px;padding-bottom:0px;margin-left:0px;margin-right:0px;text-align:center"> <a> <img src="http://www.bwmodular.org/uploads/1/3/0/4/130443290/secretkey_orig.png" alt="Picture" style="width:auto;max-width:100%" /> </a> <div style="display:block;font-size:90%"></div> </div></div>  <div class="paragraph">9. In the authenticator app on you phone, Click the '+' to add an account and select 'work or school account'<br />10. Scan the QR code displayed on the web page<br />11. If the App displays a six-digit code, click 'Next' on the web page<br />12. Click Verify Now on the web page<br />13. Enter the six-digit verification code from the app into the web page<br />14. Add your MFA Secret key from step 8 <em>with the spaces removed</em> to your app.config as shown below, in the same section that you add your username and password:<br /><span>&lt;</span><span>add</span> <span>key</span><span>=</span><span><span>"</span><em>MfaSecretKey</em><span>"</span></span> <span>value</span><span>=</span><span><span>"</span><em>MfaSecretKeyWithSpacesRemoved</em><span>"</span></span> <span>/</span><span>&gt;<br />If you follow the examples in the EasyRepro repository your tests will now automatically generate and enter the one time password whenever the login dialogue box appears.</span><br /></div>]]></content:encoded></item><item><title><![CDATA[LINQPad connections to Dynamics 365 online - Part 3 - Using MyExtensions to encapsulate your connection code]]></title><link><![CDATA[http://www.bwmodular.org/blog/linqpad-connections-to-dynamics-365-online-part-3-using-myextensions-to-encapsulate-your-connection-code]]></link><comments><![CDATA[http://www.bwmodular.org/blog/linqpad-connections-to-dynamics-365-online-part-3-using-myextensions-to-encapsulate-your-connection-code#comments]]></comments><pubDate>Fri, 13 Dec 2019 00:00:00 GMT</pubDate><category><![CDATA[Dynamics 365]]></category><category><![CDATA[Dynamics CRM]]></category><category><![CDATA[linqpad]]></category><guid isPermaLink="false">http://www.bwmodular.org/blog/linqpad-connections-to-dynamics-365-online-part-3-using-myextensions-to-encapsulate-your-connection-code</guid><description><![CDATA[In the first part of this multi-part series of articles about creating connections in LINQPad to Dynamics 365 online, I went through the basic steps needed to create an OrganizationService connection in a LINQPad script.      The second article explained how to manage MFA protected accounts.In this third article, I will explain how we can improve things by encapsulating all of this logic into a couple of useful methods which allow us to connect to Dynamics 365 by simply typing a single line of c [...] ]]></description><content:encoded><![CDATA[<div class="paragraph">In the <a href="http://blogs.it.ox.ac.uk/benwalker/2019/12/13/linqpad-connections-to-dynamics-365-online-part-1-the-basics/" target="_blank">first part</a> of this multi-part series of articles about creating connections in LINQPad to Dynamics 365 online, I went through the basic steps needed to create an OrganizationService connection in a LINQPad script.</div>  <div>  <!--BLOG_SUMMARY_END--></div>  <div class="paragraph">The <a href="http://www.bwmodular.org/blog/linqpad-connections-to-dynamics-365-online-part-2-mfa" target="_blank">second article</a> explained how to manage MFA protected accounts.<br />In this third article, I will explain how we can improve things by encapsulating all of this logic into a couple of useful methods which allow us to connect to Dynamics 365 by simply typing a single line of code.<br /><br /><strong>Using &lsquo;My Extensions&rsquo; to encapsulate this into a single &lsquo;login&rsquo; command</strong><br />Clearly, we don&rsquo;t want to have to type all of the code from the previous articles into LINQPad every time we want to connect to Dynamics365. Using the MyExtensions class, we can very easily encapsulate all of the above logic into a single method, so that when you want to create a&nbsp; connection to one of your Dynamics 365 instances, all you need to type is something like this:<br /></div>  <div id="146103394823804537"><div><style type="text/css">	#element-e240ed6b-ba4d-45a5-84bb-adcf1c346261 .code-editor--light {  padding: 20px 0px;}#element-e240ed6b-ba4d-45a5-84bb-adcf1c346261 .code-editor--light .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #C9CDCF;  border-right: 1px solid #C9CDCF;  border-top: 1px solid #C9CDCF;  background-color: #F8F8F8;  color: #363B3E;}#element-e240ed6b-ba4d-45a5-84bb-adcf1c346261 .code-editor--light .header .paragraph {  margin: 0;}#element-e240ed6b-ba4d-45a5-84bb-adcf1c346261 .code-editor--light .body-code {  margin: 0;  border: 1px solid #C9CDCF;  background-color: #FFFFFF;  color: #666C70;}#element-e240ed6b-ba4d-45a5-84bb-adcf1c346261 .code-editor--dark {  padding: 20px 0px;}#element-e240ed6b-ba4d-45a5-84bb-adcf1c346261 .code-editor--dark .ace-tomorrow-night-eighties {  background-color: #363B3E;}#element-e240ed6b-ba4d-45a5-84bb-adcf1c346261 .code-editor--dark .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #E0E1E2;  border-right: 1px solid #E0E1E2;  border-top: 1px solid #E0E1E2;  background-color: #666C70;  color: #FFFFFF;}#element-e240ed6b-ba4d-45a5-84bb-adcf1c346261 .code-editor--dark .header .paragraph {  margin: 0;}#element-e240ed6b-ba4d-45a5-84bb-adcf1c346261 .code-editor--dark .body-code {  margin: 0;  border: 1px solid #E0E1E2;  background-color: #363B3E;  color: #F8F8F8;}</style><div id="element-e240ed6b-ba4d-45a5-84bb-adcf1c346261" data-platform-element-id="270170748587580171-1.3.3" class="platform-element-contents">	<div class="code-editor--light">    <div class="header">        <div class="paragraph"></div>    </div>    <div class="body-code">        <pre class="editor"></pre>    </div></div></div><div style="clear:both;"></div></div></div>  <div class="paragraph">The MyExtensions class allows us to create reusable methods and other reusable components. To access the file, you can find and double click on the file from the &lsquo;My Queries&rsquo; pane, or by using the shortcut Shift+Ctrl+Y:<br /></div>  <div><div class="wsite-image wsite-image-border-none " style="padding-top:10px;padding-bottom:10px;margin-left:0;margin-right:0;text-align:center"> <a> <img src="http://www.bwmodular.org/uploads/1/3/0/4/130443290/myextensions_orig.png" alt="Picture" style="width:auto;max-width:100%" /> </a> <div style="display:block;font-size:90%"></div> </div></div>  <div class="paragraph">When you first open the MyExtensions file, you will see something like this:</div>  <div><div class="wsite-image wsite-image-border-none " style="padding-top:10px;padding-bottom:10px;margin-left:0;margin-right:0;text-align:center"> <a> <img src="http://www.bwmodular.org/uploads/1/3/0/4/130443290/emptymyextensions_orig.png" alt="Picture" style="width:auto;max-width:100%" /> </a> <div style="display:block;font-size:90%"></div> </div></div>  <div class="paragraph">Let&rsquo;s start by defining an enum, listing the different environment we might want to connect to:<br /></div>  <div id="797407024816884365"><div><style type="text/css">	#element-2a1d4c0e-1bb2-4c6b-b80f-90ed256cd532 .code-editor--light {  padding: 20px 0px;}#element-2a1d4c0e-1bb2-4c6b-b80f-90ed256cd532 .code-editor--light .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #C9CDCF;  border-right: 1px solid #C9CDCF;  border-top: 1px solid #C9CDCF;  background-color: #F8F8F8;  color: #363B3E;}#element-2a1d4c0e-1bb2-4c6b-b80f-90ed256cd532 .code-editor--light .header .paragraph {  margin: 0;}#element-2a1d4c0e-1bb2-4c6b-b80f-90ed256cd532 .code-editor--light .body-code {  margin: 0;  border: 1px solid #C9CDCF;  background-color: #FFFFFF;  color: #666C70;}#element-2a1d4c0e-1bb2-4c6b-b80f-90ed256cd532 .code-editor--dark {  padding: 20px 0px;}#element-2a1d4c0e-1bb2-4c6b-b80f-90ed256cd532 .code-editor--dark .ace-tomorrow-night-eighties {  background-color: #363B3E;}#element-2a1d4c0e-1bb2-4c6b-b80f-90ed256cd532 .code-editor--dark .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #E0E1E2;  border-right: 1px solid #E0E1E2;  border-top: 1px solid #E0E1E2;  background-color: #666C70;  color: #FFFFFF;}#element-2a1d4c0e-1bb2-4c6b-b80f-90ed256cd532 .code-editor--dark .header .paragraph {  margin: 0;}#element-2a1d4c0e-1bb2-4c6b-b80f-90ed256cd532 .code-editor--dark .body-code {  margin: 0;  border: 1px solid #E0E1E2;  background-color: #363B3E;  color: #F8F8F8;}</style><div id="element-2a1d4c0e-1bb2-4c6b-b80f-90ed256cd532" data-platform-element-id="270170748587580171-1.3.3" class="platform-element-contents">	<div class="code-editor--light">    <div class="header">        <div class="paragraph"></div>    </div>    <div class="body-code">        <pre class="editor"></pre>    </div></div></div><div style="clear:both;"></div></div></div>  <div class="paragraph">Now you can add a &lsquo;ConnectToCrm&rsquo; method in MyExtensions which takes one of these &lsquo;environments&rsquo; as a parameter, and use a switch statement to build your connection string and connect to Dynamics 365:<br /></div>  <div id="604078732869486193"><div><style type="text/css">	#element-7927994d-fd13-4ccd-a7a0-bae14302e41c .code-editor--light {  padding: 20px 0px;}#element-7927994d-fd13-4ccd-a7a0-bae14302e41c .code-editor--light .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #C9CDCF;  border-right: 1px solid #C9CDCF;  border-top: 1px solid #C9CDCF;  background-color: #F8F8F8;  color: #363B3E;}#element-7927994d-fd13-4ccd-a7a0-bae14302e41c .code-editor--light .header .paragraph {  margin: 0;}#element-7927994d-fd13-4ccd-a7a0-bae14302e41c .code-editor--light .body-code {  margin: 0;  border: 1px solid #C9CDCF;  background-color: #FFFFFF;  color: #666C70;}#element-7927994d-fd13-4ccd-a7a0-bae14302e41c .code-editor--dark {  padding: 20px 0px;}#element-7927994d-fd13-4ccd-a7a0-bae14302e41c .code-editor--dark .ace-tomorrow-night-eighties {  background-color: #363B3E;}#element-7927994d-fd13-4ccd-a7a0-bae14302e41c .code-editor--dark .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #E0E1E2;  border-right: 1px solid #E0E1E2;  border-top: 1px solid #E0E1E2;  background-color: #666C70;  color: #FFFFFF;}#element-7927994d-fd13-4ccd-a7a0-bae14302e41c .code-editor--dark .header .paragraph {  margin: 0;}#element-7927994d-fd13-4ccd-a7a0-bae14302e41c .code-editor--dark .body-code {  margin: 0;  border: 1px solid #E0E1E2;  background-color: #363B3E;  color: #F8F8F8;}</style><div id="element-7927994d-fd13-4ccd-a7a0-bae14302e41c" data-platform-element-id="270170748587580171-1.3.3" class="platform-element-contents">	<div class="code-editor--light">    <div class="header">        <div class="paragraph"></div>    </div>    <div class="body-code">        <pre class="editor"></pre>    </div></div></div><div style="clear:both;"></div></div></div>  <div class="paragraph"><strong>NB: You must compile your MyExtensions file by hitting f5 before the code becomes available in your other scripts.</strong><br /><span></span>Now all you need to do to connect to the environment of your choice in any LINQPad script is to select one of your preconfigured environments:<br /></div>  <div><div class="wsite-image wsite-image-border-none " style="padding-top:10px;padding-bottom:10px;margin-left:0;margin-right:0;text-align:center"> <a> <img src="http://www.bwmodular.org/uploads/1/3/0/4/130443290/choosing-an-environment_orig.png" alt="Picture" style="width:auto;max-width:100%" /> </a> <div style="display:block;font-size:90%"></div> </div></div>  <div class="paragraph"><strong>Connecting to multiple environments at the same time</strong><br />One of the advantages in using this method of logging in, compared to a pre-configured LINQPad connector, is that it allows you to connect to more than one environment in a single script, to compare data or maybe to move data from one environment to another.<br /></div>  <div id="528443401695328520"><div><style type="text/css">	#element-08906f56-0af8-4ebf-9591-880ae7a1d695 .code-editor--light {  padding: 20px 0px;}#element-08906f56-0af8-4ebf-9591-880ae7a1d695 .code-editor--light .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #C9CDCF;  border-right: 1px solid #C9CDCF;  border-top: 1px solid #C9CDCF;  background-color: #F8F8F8;  color: #363B3E;}#element-08906f56-0af8-4ebf-9591-880ae7a1d695 .code-editor--light .header .paragraph {  margin: 0;}#element-08906f56-0af8-4ebf-9591-880ae7a1d695 .code-editor--light .body-code {  margin: 0;  border: 1px solid #C9CDCF;  background-color: #FFFFFF;  color: #666C70;}#element-08906f56-0af8-4ebf-9591-880ae7a1d695 .code-editor--dark {  padding: 20px 0px;}#element-08906f56-0af8-4ebf-9591-880ae7a1d695 .code-editor--dark .ace-tomorrow-night-eighties {  background-color: #363B3E;}#element-08906f56-0af8-4ebf-9591-880ae7a1d695 .code-editor--dark .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #E0E1E2;  border-right: 1px solid #E0E1E2;  border-top: 1px solid #E0E1E2;  background-color: #666C70;  color: #FFFFFF;}#element-08906f56-0af8-4ebf-9591-880ae7a1d695 .code-editor--dark .header .paragraph {  margin: 0;}#element-08906f56-0af8-4ebf-9591-880ae7a1d695 .code-editor--dark .body-code {  margin: 0;  border: 1px solid #E0E1E2;  background-color: #363B3E;  color: #F8F8F8;}</style><div id="element-08906f56-0af8-4ebf-9591-880ae7a1d695" data-platform-element-id="270170748587580171-1.3.3" class="platform-element-contents">	<div class="code-editor--light">    <div class="header">        <div class="paragraph"></div>    </div>    <div class="body-code">        <pre class="editor"></pre>    </div></div></div><div style="clear:both;"></div></div></div>  <div class="paragraph">which will give a result like this:</div>  <div><div class="wsite-image wsite-image-border-none " style="padding-top:10px;padding-bottom:10px;margin-left:0;margin-right:0;text-align:center"> <a> <img src="http://www.bwmodular.org/uploads/1/3/0/4/130443290/numberofusers-1_orig.png" alt="Picture" style="width:auto;max-width:100%" /> </a> <div style="display:block;font-size:90%"></div> </div></div>  <div class="paragraph"><strong>Do you really want to log into production?</strong><br />Another feature which you can add to your MyExtensions code is a warning when a user is about to log into production. This can be very useful in preventing someone from accidentally running a script against a production instance. We can easily do this by using LINQPad&rsquo;s <em>Util.ReadLine</em> method to prompt the user to confirm if they want to connect to Production:<br /></div>  <div id="751896187121222623"><div><style type="text/css">	#element-ec3a1c6f-9a9f-43b0-bdb4-b7952289c982 .code-editor--light {  padding: 20px 0px;}#element-ec3a1c6f-9a9f-43b0-bdb4-b7952289c982 .code-editor--light .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #C9CDCF;  border-right: 1px solid #C9CDCF;  border-top: 1px solid #C9CDCF;  background-color: #F8F8F8;  color: #363B3E;}#element-ec3a1c6f-9a9f-43b0-bdb4-b7952289c982 .code-editor--light .header .paragraph {  margin: 0;}#element-ec3a1c6f-9a9f-43b0-bdb4-b7952289c982 .code-editor--light .body-code {  margin: 0;  border: 1px solid #C9CDCF;  background-color: #FFFFFF;  color: #666C70;}#element-ec3a1c6f-9a9f-43b0-bdb4-b7952289c982 .code-editor--dark {  padding: 20px 0px;}#element-ec3a1c6f-9a9f-43b0-bdb4-b7952289c982 .code-editor--dark .ace-tomorrow-night-eighties {  background-color: #363B3E;}#element-ec3a1c6f-9a9f-43b0-bdb4-b7952289c982 .code-editor--dark .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #E0E1E2;  border-right: 1px solid #E0E1E2;  border-top: 1px solid #E0E1E2;  background-color: #666C70;  color: #FFFFFF;}#element-ec3a1c6f-9a9f-43b0-bdb4-b7952289c982 .code-editor--dark .header .paragraph {  margin: 0;}#element-ec3a1c6f-9a9f-43b0-bdb4-b7952289c982 .code-editor--dark .body-code {  margin: 0;  border: 1px solid #E0E1E2;  background-color: #363B3E;  color: #F8F8F8;}</style><div id="element-ec3a1c6f-9a9f-43b0-bdb4-b7952289c982" data-platform-element-id="270170748587580171-1.3.3" class="platform-element-contents">	<div class="code-editor--light">    <div class="header">        <div class="paragraph">Code Editor</div>    </div>    <div class="body-code">        <pre class="editor"></pre>    </div></div></div><div style="clear:both;"></div></div></div>  <div class="paragraph">A prompt will appear at the bottom of the page, asking for confirmation when a Production instance is chosen:<br /></div>  <div><div class="wsite-image wsite-image-border-none " style="padding-top:10px;padding-bottom:10px;margin-left:0;margin-right:0;text-align:center"> <a> <img src="http://www.bwmodular.org/uploads/1/3/0/4/130443290/warnifconnectingtoproduction_orig.png" alt="Picture" style="width:auto;max-width:100%" /> </a> <div style="display:block;font-size:90%"></div> </div></div>  <div class="paragraph"><strong>Adding Impersonation to our MyExtensions code</strong><br />In <a href="http://www.bwmodular.org/blog/using-impersonation-in-dynamics-365" target="_blank">this article</a>, I explained how impersonating users can be very useful in diagnosing problems, or testing what happens when code as different users with different security roles. Adding an Impersonate method to your MyExtensions method can make this very straightforward.</div>  <div><div class="wsite-image wsite-image-border-none " style="padding-top:10px;padding-bottom:10px;margin-left:0;margin-right:0;text-align:center"> <a> <img src="http://www.bwmodular.org/uploads/1/3/0/4/130443290/impersonatinganotheruser-1024x253_orig.png" alt="Picture" style="width:auto;max-width:100%" /> </a> <div style="display:block;font-size:90%"></div> </div></div>  <div class="paragraph" style="text-align:left;"><strong>Sharing the love</strong><br />Once you have created your methods in your MyExtensions file, you can share this with the rest of the team, using the method outlined in <a href="http://www.bwmodular.org/blog/integrating-linqpad-scripts-into-source-control" target="_blank">this article</a>. If you all set your&nbsp; &lsquo;Plugins and Extensions&rsquo; folder to the same folder in your Source Control system, you can all connect to Dynamics in exactly the same way without having to share any passwords.<br /></div>]]></content:encoded></item><item><title><![CDATA[LINQPad connections to Dynamics 365 online - Part 2 - MFA]]></title><link><![CDATA[http://www.bwmodular.org/blog/linqpad-connections-to-dynamics-365-online-part-2-mfa]]></link><comments><![CDATA[http://www.bwmodular.org/blog/linqpad-connections-to-dynamics-365-online-part-2-mfa#comments]]></comments><pubDate>Thu, 12 Dec 2019 00:00:00 GMT</pubDate><category><![CDATA[Dynamics 365]]></category><category><![CDATA[Dynamics CRM]]></category><category><![CDATA[linqpad]]></category><guid isPermaLink="false">http://www.bwmodular.org/blog/linqpad-connections-to-dynamics-365-online-part-2-mfa</guid><description><![CDATA[Handling MFA connections to Dynamics 365In the first part of this multi-part series of articles about creating connections in LINQPad to Dynamics 365 online, I went through the basic steps needed to create an OrganizationService connection in your LINQPad script.      In this second article, I will explain how to proceed if your account is additionally protected by Multi-Factor Authentication (MFA). When using MFA to authenticate against Dynamics 365, you are prompted to authenticate with an add [...] ]]></description><content:encoded><![CDATA[<div class="paragraph"><strong>Handling MFA connections to Dynamics 365</strong><br />In the <a href="http://www.bwmodular.org/blog/linqpad-connections-to-dynamics-365-online-part-1-the-basics" target="_blank">first part</a> of this multi-part series of articles about creating connections in LINQPad to Dynamics 365 online, I went through the basic steps needed to create an OrganizationService connection in your LINQPad script.<br /></div>  <div>  <!--BLOG_SUMMARY_END--></div>  <div class="paragraph">In this second article, I will explain how to proceed if your account is additionally protected by Multi-Factor Authentication (MFA). When using MFA to authenticate against Dynamics 365, you are prompted to authenticate with an additional token, such as a code sent via text message or an authenticator app. In order to avoid having to repeatedly authenticate this way you can use an <a href="https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-how-applications-are-added" target="_blank">Azure App Registration</a> so that connections made from LINQPad are pre-authenticated. First of all you have to create an App Registration in Azure which will allow you to register your &lsquo;App&rsquo; (in this case your connection from LINQPad with Azure Active Directory). Log into the Azure Portal and open the <a href="https://aka.ms/appregistrations" target="_blank">App Registrations blade</a>. You will see a screen like this:</div>  <div><div class="wsite-image wsite-image-border-hairline " style="padding-top:10px;padding-bottom:10px;margin-left:0px;margin-right:0px;text-align:center"> <a> <img src="http://www.bwmodular.org/uploads/1/3/0/4/130443290/appregistration-1024x608_orig.png" alt="Picture" style="width:auto;max-width:100%" /> </a> <div style="display:block;font-size:90%"></div> </div></div>  <div class="paragraph">Give your app registration a name, and provide a &lsquo;Redirect URI&rsquo;. For our purposes, this does not need to be a real URI, but it must be in the format of a real URI.<br />Click Register and you&rsquo;ll be taken to a page like this:<br /></div>  <div><div class="wsite-image wsite-image-border-none " style="padding-top:10px;padding-bottom:10px;margin-left:0;margin-right:0;text-align:center"> <a> <img src="http://www.bwmodular.org/uploads/1/3/0/4/130443290/appregistrationclientid-1024x360_orig.png" alt="Picture" style="width:auto;max-width:100%" /> </a> <div style="display:block;font-size:90%"></div> </div></div>  <div class="paragraph">You will use the Application (client) ID as part of your connection string.<br />Make the following amendments to your connection string in your LINQPad script:<ul><li>Change <em>AuthType</em> from <em>Office365</em> to <em>OAuth</em>;</li><li>Add the following additional parameters:</li><li><em>AppId</em>: This should be the token you copied from the Azure Portal.</li><li><em>RedirectUri</em>: This should be the Redirect URI you entered when setting up the App Registration.</li><li><em>LoginPrompt</em>=<em>Auto</em>;</li><li><em>TokenCacheStorePath</em>: The path to a location on your computer where a token will be stored after the first successful login, such as <em>C:\temp\LinqPadOauth\cache.txt </em>This will be used as the location in which the authentication token is stored so that you do not need to enter your credentials every time you log in.</li><li>Remove the <em>Password</em> parameter altogether.</li></ul>Your connection string should now look something like this:<br />AuthType=OAuth;Url=https://yourInstance.crm11.dynamics.com/;AppId=123449g6-45e9-489b-8eab-h20d45ea60e9;RedirectUri=https://anyoldurl.doesntmatter.com;LoginPrompt=Auto;Username=your.username@yourO365domain.onmicrosoft.com;RequireNewInstance=True;TokenCacheStorePath=C:\temp\LinqPadOauth\cache.txt<br />Running our script again, we should now get prompted <em>once</em> to enter our password and MFA token, but after that our credentials will be cached and we will not be prompted to enter our password again.<br />However, you will see that our connection generates an awful lot of diagnostic output, which can make it difficult to see what&rsquo;s going on&hellip;.<br /></div>  <div><div class="wsite-image wsite-image-border-none " style="padding-top:10px;padding-bottom:10px;margin-left:0;margin-right:0;text-align:center"> <a> <img src="http://www.bwmodular.org/uploads/1/3/0/4/130443290/verboselinqpadd365connectionoutput-1-1024x336_orig.png" alt="Picture" style="width:auto;max-width:100%" /> </a> <div style="display:block;font-size:90%"></div> </div></div>  <div class="paragraph">Luckily, there is a way to suppress all of this diagnostic information. Add this line to your code, and you will get a nice clean output again:</div>  <div id="910970571982571078"><div><style type="text/css">	#element-e3092771-faba-4745-90b5-232fb00a3749 .code-editor--light {  padding: 20px 0px;}#element-e3092771-faba-4745-90b5-232fb00a3749 .code-editor--light .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #C9CDCF;  border-right: 1px solid #C9CDCF;  border-top: 1px solid #C9CDCF;  background-color: #F8F8F8;  color: #363B3E;}#element-e3092771-faba-4745-90b5-232fb00a3749 .code-editor--light .header .paragraph {  margin: 0;}#element-e3092771-faba-4745-90b5-232fb00a3749 .code-editor--light .body-code {  margin: 0;  border: 1px solid #C9CDCF;  background-color: #FFFFFF;  color: #666C70;}#element-e3092771-faba-4745-90b5-232fb00a3749 .code-editor--dark {  padding: 20px 0px;}#element-e3092771-faba-4745-90b5-232fb00a3749 .code-editor--dark .ace-tomorrow-night-eighties {  background-color: #363B3E;}#element-e3092771-faba-4745-90b5-232fb00a3749 .code-editor--dark .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #E0E1E2;  border-right: 1px solid #E0E1E2;  border-top: 1px solid #E0E1E2;  background-color: #666C70;  color: #FFFFFF;}#element-e3092771-faba-4745-90b5-232fb00a3749 .code-editor--dark .header .paragraph {  margin: 0;}#element-e3092771-faba-4745-90b5-232fb00a3749 .code-editor--dark .body-code {  margin: 0;  border: 1px solid #E0E1E2;  background-color: #363B3E;  color: #F8F8F8;}</style><div id="element-e3092771-faba-4745-90b5-232fb00a3749" data-platform-element-id="270170748587580171-1.3.3" class="platform-element-contents">	<div class="code-editor--light">    <div class="header">        <div class="paragraph"></div>    </div>    <div class="body-code">        <pre class="editor"></pre>    </div></div></div><div style="clear:both;"></div></div></div>  <div class="paragraph">In the <a href="http://www.bwmodular.org/blog/linqpad-connections-to-dynamics-365-online-part-3-using-myextensions-to-encapsulate-your-connection-code" target="_blank">next article</a> in the series, I&rsquo;ll show you how you can encapsulate all of this code into a method so that you can easily create a connection in LINQPad by just typing a single line of code.<br /></div>]]></content:encoded></item><item><title><![CDATA[LINQPad connections to Dynamics 365 online - Part 1 - The Basics]]></title><link><![CDATA[http://www.bwmodular.org/blog/linqpad-connections-to-dynamics-365-online-part-1-the-basics]]></link><comments><![CDATA[http://www.bwmodular.org/blog/linqpad-connections-to-dynamics-365-online-part-1-the-basics#comments]]></comments><pubDate>Wed, 11 Dec 2019 00:00:00 GMT</pubDate><category><![CDATA[Dynamics 365]]></category><category><![CDATA[Dynamics CRM]]></category><category><![CDATA[linqpad]]></category><guid isPermaLink="false">http://www.bwmodular.org/blog/linqpad-connections-to-dynamics-365-online-part-1-the-basics</guid><description><![CDATA[LINQPad is an invaluable tool for .Net developers in general and as a Dynamics developer being able to test and execute code using the Dynamics365 API using LINQPad can be a real timesaver. In this multi-part series of articles, I will show you how to configure LINQPad so that creating connections to Dynamics365 can be done quickly and easily. Using the MyExtensions file, you&rsquo;ll be able to create a shared method for every member of your team to be able to connect to any of your Dynamics en [...] ]]></description><content:encoded><![CDATA[<div class="paragraph"><a href="https://www.linqpad.net/" target="_blank">LINQPad</a> is an invaluable tool for .Net developers in general and as a Dynamics developer being able to test and execute code using the Dynamics365 API using LINQPad can be a real timesaver. In this multi-part series of articles, I will show you how to configure LINQPad so that creating connections to Dynamics365 can be done quickly and easily. Using the MyExtensions file, you&rsquo;ll be able to create a shared method for every member of your team to be able to connect to any of your Dynamics environments by just typing a single line of code.<br /></div>  <div>  <!--BLOG_SUMMARY_END--></div>  <div class="paragraph">Whilst there <em>is</em> a LINQPad driver available for Dynamics <a href="https://github.com/kenakamu/CRMLinqPadDriverWebAPI" target="_blank">here</a>, it has not been updated for a while and I&rsquo;ve never been able to connect to Dynamics 365 using this driver.<br />Even if you do manage to get the driver to work, there will be times when you want to create a connection to Dynamics 365 manually &ndash; for example if you want to be able to connect to multiple instances in order to be able to compare two environments, or to copy data from one instance to another. <br /><strong>Creating a connection to D365 &ndash; the Basics</strong><br />Let&rsquo;s start with creating a basic connection to Dynamics 365, using a standard Microsoft Active Directory account.<br />Create a new LINQPad file of type C# Statement, and add references to <em>System.Net.dll</em> and <em>Microsoft.CrmSdk.XrmTooling.CoreAssembly</em>. (If you have the premium edition of LINQPad, you can download the latter using the integrated NuGet Manager, otherwise you&rsquo;ll have to download it outside of LINQPad and browse to the file.)<br />Here&rsquo;s the code you need to create an organisation service connection:<br /></div>  <div id="497377056859666092"><div><style type="text/css">	#element-b0989f70-640c-433e-85b0-7a7377e3e2ec .code-editor--light {  padding: 20px 0px;}#element-b0989f70-640c-433e-85b0-7a7377e3e2ec .code-editor--light .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #C9CDCF;  border-right: 1px solid #C9CDCF;  border-top: 1px solid #C9CDCF;  background-color: #F8F8F8;  color: #363B3E;}#element-b0989f70-640c-433e-85b0-7a7377e3e2ec .code-editor--light .header .paragraph {  margin: 0;}#element-b0989f70-640c-433e-85b0-7a7377e3e2ec .code-editor--light .body-code {  margin: 0;  border: 1px solid #C9CDCF;  background-color: #FFFFFF;  color: #666C70;}#element-b0989f70-640c-433e-85b0-7a7377e3e2ec .code-editor--dark {  padding: 20px 0px;}#element-b0989f70-640c-433e-85b0-7a7377e3e2ec .code-editor--dark .ace-tomorrow-night-eighties {  background-color: #363B3E;}#element-b0989f70-640c-433e-85b0-7a7377e3e2ec .code-editor--dark .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #E0E1E2;  border-right: 1px solid #E0E1E2;  border-top: 1px solid #E0E1E2;  background-color: #666C70;  color: #FFFFFF;}#element-b0989f70-640c-433e-85b0-7a7377e3e2ec .code-editor--dark .header .paragraph {  margin: 0;}#element-b0989f70-640c-433e-85b0-7a7377e3e2ec .code-editor--dark .body-code {  margin: 0;  border: 1px solid #E0E1E2;  background-color: #363B3E;  color: #F8F8F8;}</style><div id="element-b0989f70-640c-433e-85b0-7a7377e3e2ec" data-platform-element-id="270170748587580171-1.3.3" class="platform-element-contents">	<div class="code-editor--light">    <div class="header">        <div class="paragraph"></div>    </div>    <div class="body-code">        <pre class="editor"></pre>    </div></div></div><div style="clear:both;"></div></div></div>  <div class="paragraph">There are a couple of things to notice here.<br />Firstly, note the use of TLS 1.2. Microsoft discontinued the use of TLS 1.0 or 1.1 with the Dynamics 365 platform in 2018, so all connections must be made using TLS 1.2.<br />Secondly, notice the use of the &lsquo;<em>RequireNewInstance</em>&lsquo; option. By default, this is set to False, which in many circumstances is sensible as it allows for the caching of the connection, which makes for faster execution. However, the disadvantage of this is that, if you make a connection to one instance of Dynamics 365, and then immediately to another instance (let&rsquo;s say you are copying data from one instance to another in a script), you way well find that the second connection is to the same instance which you first connected to, which can be very confusing, if not downright dangerous!<br />So, now we can make a connection, but there are some further improvements which we might want to make.<br /><strong>Confirmation that we are logged in</strong><br />Strangely enough, if you use invalid credentials in this code, you will not receive any error. The connection will fail silently, which is not very helpful.<br />We can, however, trap any errors by inspecting the &lsquo;LastCrmError&rsquo; property of the connection:<br /></div>  <div id="234665173172099872"><div><style type="text/css">	#element-1ed80934-25ea-46a6-94bf-592c889741e2 .code-editor--light {  padding: 20px 0px;}#element-1ed80934-25ea-46a6-94bf-592c889741e2 .code-editor--light .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #C9CDCF;  border-right: 1px solid #C9CDCF;  border-top: 1px solid #C9CDCF;  background-color: #F8F8F8;  color: #363B3E;}#element-1ed80934-25ea-46a6-94bf-592c889741e2 .code-editor--light .header .paragraph {  margin: 0;}#element-1ed80934-25ea-46a6-94bf-592c889741e2 .code-editor--light .body-code {  margin: 0;  border: 1px solid #C9CDCF;  background-color: #FFFFFF;  color: #666C70;}#element-1ed80934-25ea-46a6-94bf-592c889741e2 .code-editor--dark {  padding: 20px 0px;}#element-1ed80934-25ea-46a6-94bf-592c889741e2 .code-editor--dark .ace-tomorrow-night-eighties {  background-color: #363B3E;}#element-1ed80934-25ea-46a6-94bf-592c889741e2 .code-editor--dark .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #E0E1E2;  border-right: 1px solid #E0E1E2;  border-top: 1px solid #E0E1E2;  background-color: #666C70;  color: #FFFFFF;}#element-1ed80934-25ea-46a6-94bf-592c889741e2 .code-editor--dark .header .paragraph {  margin: 0;}#element-1ed80934-25ea-46a6-94bf-592c889741e2 .code-editor--dark .body-code {  margin: 0;  border: 1px solid #E0E1E2;  background-color: #363B3E;  color: #F8F8F8;}</style><div id="element-1ed80934-25ea-46a6-94bf-592c889741e2" data-platform-element-id="270170748587580171-1.3.3" class="platform-element-contents">	<div class="code-editor--light">    <div class="header">        <div class="paragraph"></div>    </div>    <div class="body-code">        <pre class="editor"></pre>    </div></div></div><div style="clear:both;"></div></div></div>  <div class="paragraph">We can quickly validate that we are logged in to the expected instance by displaying the details of the instance URI as follows:</div>  <div id="565220718889751950"><div><style type="text/css">	#element-c3081012-efbe-4fdf-a8b5-1d42e379ef77 .code-editor--light {  padding: 20px 0px;}#element-c3081012-efbe-4fdf-a8b5-1d42e379ef77 .code-editor--light .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #C9CDCF;  border-right: 1px solid #C9CDCF;  border-top: 1px solid #C9CDCF;  background-color: #F8F8F8;  color: #363B3E;}#element-c3081012-efbe-4fdf-a8b5-1d42e379ef77 .code-editor--light .header .paragraph {  margin: 0;}#element-c3081012-efbe-4fdf-a8b5-1d42e379ef77 .code-editor--light .body-code {  margin: 0;  border: 1px solid #C9CDCF;  background-color: #FFFFFF;  color: #666C70;}#element-c3081012-efbe-4fdf-a8b5-1d42e379ef77 .code-editor--dark {  padding: 20px 0px;}#element-c3081012-efbe-4fdf-a8b5-1d42e379ef77 .code-editor--dark .ace-tomorrow-night-eighties {  background-color: #363B3E;}#element-c3081012-efbe-4fdf-a8b5-1d42e379ef77 .code-editor--dark .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #E0E1E2;  border-right: 1px solid #E0E1E2;  border-top: 1px solid #E0E1E2;  background-color: #666C70;  color: #FFFFFF;}#element-c3081012-efbe-4fdf-a8b5-1d42e379ef77 .code-editor--dark .header .paragraph {  margin: 0;}#element-c3081012-efbe-4fdf-a8b5-1d42e379ef77 .code-editor--dark .body-code {  margin: 0;  border: 1px solid #E0E1E2;  background-color: #363B3E;  color: #F8F8F8;}</style><div id="element-c3081012-efbe-4fdf-a8b5-1d42e379ef77" data-platform-element-id="270170748587580171-1.3.3" class="platform-element-contents">	<div class="code-editor--light">    <div class="header">        <div class="paragraph"></div>    </div>    <div class="body-code">        <pre class="editor"></pre>    </div></div></div><div style="clear:both;"></div></div></div>  <div class="paragraph">and we can display the username of the user we are logged in as by performing a WhoAmI request:<br /></div>  <div id="845850976852504438"><div><style type="text/css">	#element-dd965633-3813-48bb-b0f5-511167bc57da .code-editor--light {  padding: 20px 0px;}#element-dd965633-3813-48bb-b0f5-511167bc57da .code-editor--light .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #C9CDCF;  border-right: 1px solid #C9CDCF;  border-top: 1px solid #C9CDCF;  background-color: #F8F8F8;  color: #363B3E;}#element-dd965633-3813-48bb-b0f5-511167bc57da .code-editor--light .header .paragraph {  margin: 0;}#element-dd965633-3813-48bb-b0f5-511167bc57da .code-editor--light .body-code {  margin: 0;  border: 1px solid #C9CDCF;  background-color: #FFFFFF;  color: #666C70;}#element-dd965633-3813-48bb-b0f5-511167bc57da .code-editor--dark {  padding: 20px 0px;}#element-dd965633-3813-48bb-b0f5-511167bc57da .code-editor--dark .ace-tomorrow-night-eighties {  background-color: #363B3E;}#element-dd965633-3813-48bb-b0f5-511167bc57da .code-editor--dark .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #E0E1E2;  border-right: 1px solid #E0E1E2;  border-top: 1px solid #E0E1E2;  background-color: #666C70;  color: #FFFFFF;}#element-dd965633-3813-48bb-b0f5-511167bc57da .code-editor--dark .header .paragraph {  margin: 0;}#element-dd965633-3813-48bb-b0f5-511167bc57da .code-editor--dark .body-code {  margin: 0;  border: 1px solid #E0E1E2;  background-color: #363B3E;  color: #F8F8F8;}</style><div id="element-dd965633-3813-48bb-b0f5-511167bc57da" data-platform-element-id="270170748587580171-1.3.3" class="platform-element-contents">	<div class="code-editor--light">    <div class="header">        <div class="paragraph"></div>    </div>    <div class="body-code">        <pre class="editor"></pre>    </div></div></div><div style="clear:both;"></div></div></div>  <div><div class="wsite-image wsite-image-border-none " style="padding-top:10px;padding-bottom:10px;margin-left:0;margin-right:0;text-align:center"> <a> <img src="http://www.bwmodular.org/uploads/1/3/0/4/130443290/outputfromd365login_orig.png" alt="Picture" style="width:auto;max-width:100%" /> </a> <div style="display:block;font-size:90%"></div> </div></div>  <div class="paragraph"><strong>Can you keep a secret? Using LINQPad&rsquo;s Password Manager</strong><br />The next improvement to be made is to start using LINQPad&rsquo;s password manager so that we don&rsquo;t keep our passwords in our scripts. Replace the variable holding the password (Line 2 in the first snippet) with this:</div>  <div id="580360516169765385"><div><style type="text/css">	#element-ec32c8ea-d990-4fb2-84cb-a96d6e73f416 .code-editor--light {  padding: 20px 0px;}#element-ec32c8ea-d990-4fb2-84cb-a96d6e73f416 .code-editor--light .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #C9CDCF;  border-right: 1px solid #C9CDCF;  border-top: 1px solid #C9CDCF;  background-color: #F8F8F8;  color: #363B3E;}#element-ec32c8ea-d990-4fb2-84cb-a96d6e73f416 .code-editor--light .header .paragraph {  margin: 0;}#element-ec32c8ea-d990-4fb2-84cb-a96d6e73f416 .code-editor--light .body-code {  margin: 0;  border: 1px solid #C9CDCF;  background-color: #FFFFFF;  color: #666C70;}#element-ec32c8ea-d990-4fb2-84cb-a96d6e73f416 .code-editor--dark {  padding: 20px 0px;}#element-ec32c8ea-d990-4fb2-84cb-a96d6e73f416 .code-editor--dark .ace-tomorrow-night-eighties {  background-color: #363B3E;}#element-ec32c8ea-d990-4fb2-84cb-a96d6e73f416 .code-editor--dark .header {  padding: 10px 20px;  font-weight: bold;  border-left: 1px solid #E0E1E2;  border-right: 1px solid #E0E1E2;  border-top: 1px solid #E0E1E2;  background-color: #666C70;  color: #FFFFFF;}#element-ec32c8ea-d990-4fb2-84cb-a96d6e73f416 .code-editor--dark .header .paragraph {  margin: 0;}#element-ec32c8ea-d990-4fb2-84cb-a96d6e73f416 .code-editor--dark .body-code {  margin: 0;  border: 1px solid #E0E1E2;  background-color: #363B3E;  color: #F8F8F8;}</style><div id="element-ec32c8ea-d990-4fb2-84cb-a96d6e73f416" data-platform-element-id="270170748587580171-1.3.3" class="platform-element-contents">	<div class="code-editor--light">    <div class="header">        <div class="paragraph"></div>    </div>    <div class="body-code">        <pre class="editor"></pre>    </div></div></div><div style="clear:both;"></div></div></div>  <div class="paragraph">The first time that you run the script, you will be prompted to enter your password:</div>  <div><div class="wsite-image wsite-image-border-none " style="padding-top:10px;padding-bottom:10px;margin-left:0;margin-right:0;text-align:center"> <a> <img src="http://www.bwmodular.org/uploads/1/3/0/4/130443290/passwordmanagerprompt_orig.png" alt="Picture" style="width:auto;max-width:100%" /> </a> <div style="display:block;font-size:90%"></div> </div></div>  <div class="paragraph">Every time you run the script after that, LINQPad will retrieve the password from its secure password store, so you don&rsquo;t need to enter it every time.<br />This will also enable the sharing of scripts with other users without having to share login credentials &ndash; the passwords are not saved with the script, so each user will be prompted to enter their own credentials. You can save <em>any</em> information in the password manager, so this can also be useful to hold usernames so these are also not saved in scripts. Passwords can be managed (created/updated/deleted) directly in LINQPad, from the File -&gt; Password Manager menu item. In the <a href="http://www.bwmodular.org/blog/linqpad-connections-to-dynamics-365-online-part-2-mfa" target="_blank">next part</a> in this series, I will show you how to manage connections for accounts which use Multi-Factor Authentication.<br /><br /></div>]]></content:encoded></item><item><title><![CDATA[Using the Plugin Registration tool with MFA-enabled accounts in Dynamics 365]]></title><link><![CDATA[http://www.bwmodular.org/blog/using-the-plugin-registration-tool-with-mfa-enabled-accounts-in-dynamics-365]]></link><comments><![CDATA[http://www.bwmodular.org/blog/using-the-plugin-registration-tool-with-mfa-enabled-accounts-in-dynamics-365#comments]]></comments><pubDate>Sat, 19 Oct 2019 00:00:00 GMT</pubDate><category><![CDATA[Dynamics 365]]></category><guid isPermaLink="false">http://www.bwmodular.org/blog/using-the-plugin-registration-tool-with-mfa-enabled-accounts-in-dynamics-365</guid><description><![CDATA[I was finding it impossible to connect to Dynamics 365 using the Plugin Registration Tool with an account which has Multi-Factor Authentication enabled.      With older versions of the Toolkit, I was just getting stuck in a loop where I&rsquo;d successfully authenticate using MFA, be shown a list of instances, select one and then just get taken straight back to the initial login page.With the latest version of the tool (9.1.0.1), I was getting a different problem &ndash; it wasn&rsquo;t even sho [...] ]]></description><content:encoded><![CDATA[<div class="paragraph">I was finding it impossible to connect to Dynamics 365 using the Plugin Registration Tool with an account which has Multi-Factor Authentication enabled.</div>  <div>  <!--BLOG_SUMMARY_END--></div>  <div class="paragraph">With older versions of the Toolkit, I was just getting stuck in a loop where I&rsquo;d successfully authenticate using MFA, be shown a list of instances, select one and then just get taken straight back to the initial login page.<br />With the latest version of the tool (9.1.0.1), I was getting a different problem &ndash; it wasn&rsquo;t even showing me a list of instances, just giving me this error message after entering my username and password: <em>Error : You don&rsquo;t have permission to access any of the organizations in the Microsoft Common Data Service region that you specified. If you&rsquo;re not sure which region your organization resides in, choose &ldquo;Don&rsquo;t know&rdquo; for the CDS region and try again. Otherwise check with your CDS administrator.</em><br />Capturing the web traffic in Fiddler didn&rsquo;t help much &ndash; I could see that the call was returning this message: <em>&ldquo;Due to a configuration change made by your administrator, or because you moved to a new location, you must use multi-factor authentication to access&rdquo; </em>but this still didn&rsquo;t help me getting the problem solved.<br />It wasn&rsquo;t until I spoke to an engineer at Microsoft (Thanks, Trenton) that I was able to resolve the issue. When using MFA with the Plugin Registration Tool, it turns out that you <em>must not</em> enter your username and password in the login dialogue box!<br /></div>  <div><div class="wsite-image wsite-image-border-none " style="padding-top:10px;padding-bottom:10px;margin-left:0;margin-right:0;text-align:center"> <a> <img src="http://www.bwmodular.org/uploads/1/3/0/4/130443290/pluginregistrationlogin_orig.png" alt="Picture" style="width:auto;max-width:100%" /> </a> <div style="display:block;font-size:90%"></div> </div></div>  <div class="paragraph">If you leave these fields blank, when you click Login, you will get taken to the Microsoft Log-In screen as normal and this time your login will actually work!<br /><span></span>If you think this counter-intuitive UI could be improved, you could up-vote <a href="https://powerusers.microsoft.com/t5/PowerApps-Ideas/Add-Multi-Factor-Authentication-MFA-to-PluginRegistrationTool/idi-p/386289#M28130" target="_blank">this idea</a> posted on the PowerApps forum.<br /><span></span>NB If you want to get hold of the latest version of the Plugin Registration Tool, you will need to download it from the <a href="https://www.nuget.org/packages/Microsoft.CrmSdk.XrmTooling.PluginRegistrationTool" target="_blank">NuGet repository</a> as described <a href="https://docs.microsoft.com/en-us/dynamics365/customer-engagement/developer/download-tools-nuget" target="_blank">here</a>.<br /><br /><span></span></div>]]></content:encoded></item></channel></rss>