<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Azure on LinkeD365 Blog</title><link>https://linked365.blog/tags/azure/</link><description>Recent content in Azure on LinkeD365 Blog</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Sat, 06 Feb 2021 00:00:00 +0000</lastBuildDate><atom:link href="https://linked365.blog/tags/azure/index.xml" rel="self" type="application/rss+xml"/><item><title>Logic Apps to Visio - XrmToolBox Tool</title><link>https://linked365.blog/2021/02/06/logic-apps-to-visio-xrmtoolbox-tool/</link><pubDate>Sat, 06 Feb 2021 00:00:00 +0000</pubDate><guid>https://linked365.blog/2021/02/06/logic-apps-to-visio-xrmtoolbox-tool/</guid><description>&lt;img src="https://linked365.blog/images/2021/02-Logic-App.gif" alt="Featured image of post Logic Apps to Visio - XrmToolBox Tool" />&lt;p>So, we all know Power Automate Cloud Flows are basically Logic Apps right? Which means I should be able to document them and convert to Visios in the same way as my &lt;a class="link" href="https://linked365.blog/2020/10/14/flow-to-visio-xrmtoolbox-addon/" target="_blank" rel="noopener"
>Flow To Visio Tool&lt;/a> right?&lt;/p>
&lt;p>Actually, it is true! After numerous requests, I finally got round to doing it.&lt;/p>
&lt;h2 id="tldr">TLDR;&lt;/h2>
&lt;p>I have included the ability to document Logic Apps in the Flow to Visio tool.&lt;/p>
&lt;h3 id="document-logic-apps-beta">Document Logic Apps (BETA)&lt;/h3>
&lt;p>First, let me stress this is VERY BETA. I do not have many (any) Logic Apps I can point this at. If you come across any issues with it, please let me know by raising issues on GitHub &lt;a class="link" href="https://github.com/LinkeD365/FlowToVisio/issues" target="_blank" rel="noopener"
>here&lt;/a>.&lt;/p>
&lt;p>To connect to the Azure Logic Apps, there is a new button available in the toolbar. But first, you need to amend or add an app registration to allow access to Logic Apps.&lt;/p>
&lt;p>Register or amend an App registration. API Permissions required are Azure Service Management/user impersonation&lt;/p>
&lt;p>&lt;a class="link" href="https://user-images.githubusercontent.com/43988771/107113837-75a41e00-6859-11eb-954c-aecbdc253857.png" target="_blank" rel="noopener"
>&lt;img src="https://linked365.blog/images/2021/02-107113837-75a41e00-6859-11eb-954c-aecbdc253857.png"
loading="lazy"
>&lt;/a>&lt;/p>
&lt;p>In the application, select the Connect to Logic Apps button&lt;/p>
&lt;p>&lt;a class="link" href="https://user-images.githubusercontent.com/43988771/107113888-c9af0280-6859-11eb-9bae-327743f74da2.png" target="_blank" rel="noopener"
>&lt;img src="https://linked365.blog/images/2021/02-107113888-c9af0280-6859-11eb-9bae-327743f74da2.png"
loading="lazy"
>&lt;/a>&lt;/p>
&lt;p>Populate the API connection pop up with the appropriate settings. &lt;/p>
&lt;p>Firstly, the Subscription Id can be found on your Logic App Overview, the purple box here&lt;/p>
&lt;p>&lt;a class="link" href="https://user-images.githubusercontent.com/43988771/107113966-4d68ef00-685a-11eb-850e-fbbfe0e9740d.png" target="_blank" rel="noopener"
>&lt;img src="https://linked365.blog/images/2021/02-107113966-4d68ef00-685a-11eb-850e-fbbfe0e9740d.png"
loading="lazy"
>&lt;/a>&lt;/p>
&lt;p>Next, the Application Id and Tenant Id both are available on the App Registration page, the red and yellow items here&lt;/p>
&lt;p>&lt;a class="link" href="https://user-images.githubusercontent.com/43988771/107114017-a5075a80-685a-11eb-9723-b4686c7e11c7.png" target="_blank" rel="noopener"
>&lt;img src="https://linked365.blog/images/2021/02-107114017-a5075a80-685a-11eb-9723-b4686c7e11c7.png"
loading="lazy"
>&lt;/a>&lt;/p>
&lt;h2 id="httpsgithubcomlinked365flowtovisiothe-return-url-needs-to-be-specified-as-a-mobile-and-desktop-application-1the-return-url-needs-to-be-specified-as-a-mobile-and-desktop-application">&lt;a class="link" href="https://github.com/LinkeD365/FlowToVisio#the-return-url-needs-to-be-specified-as-a-mobile-and-desktop-application-1" target="_blank" rel="noopener"
>&lt;/a>&lt;strong>The Return URL needs to be specified as a Mobile and Desktop application&lt;/strong>&lt;/h2>
&lt;h2 id="walkthrough">Walkthrough&lt;/h2>
&lt;p>&lt;img src="https://linked365.blog/images/2021/02-Logic-App.gif"
loading="lazy"
>&lt;/p></description></item><item><title>Alexa, Field Service and Me (Part 5) - Using Azure Service Bus</title><link>https://linked365.blog/2020/03/13/alexa-field-service-and-me-part-5-using-azure-service-bus/</link><pubDate>Fri, 13 Mar 2020 00:00:00 +0000</pubDate><guid>https://linked365.blog/2020/03/13/alexa-field-service-and-me-part-5-using-azure-service-bus/</guid><description>&lt;img src="https://linked365.blog/images/2020/03-image-5.png" alt="Featured image of post Alexa, Field Service and Me (Part 5) - Using Azure Service Bus" />&lt;p>In my previous &lt;a class="link" href="https://linked365.blog/2020/03/09/alexa-field-service-and-me-part-4-using-azure-funtions/" target="_blank" rel="noopener"
>post&lt;/a> I walked through swapping out Power Automate with Azure Functions for responding to Alexa in our &lt;a class="link" href="https://linked365.blog/2019/04/19/alexa-field-service-and-me-part-1/" target="_blank" rel="noopener"
>Field Service&lt;/a> scenario.&lt;/p>
&lt;p>This post is about finalising the code by using Service Bus function to create the Work Order.&lt;/p>
&lt;p>In the Power Automate version I used a child flow call to allow the response to happen quickly, without waiting for the numerous calls to establish data then creating the record. This is standard, good practice to improve the response time.&lt;/p>
&lt;h2 id="create-the-queue">Create the Queue&lt;/h2>
&lt;p>Firstly, over to portal.azure.com &amp;amp; create a service bus. The service bus is the messaging underpinning for our queue.&lt;/p>
&lt;p>&lt;img src="https://linked365.blog/images/2020/03-azurefunction-3.gif?w=1024"
loading="lazy"
>&lt;/p>
&lt;p>Next create a queue within the service bus. Queues are a list of things that your code needs to process. Functions can then be subscribed to the queue to be triggered when something enters the queue&lt;/p>
&lt;p>&lt;img src="https://linked365.blog/images/2020/03-azurefunction-4.gif?w=1024"
loading="lazy"
>&lt;/p>
&lt;h2 id="adding-to-the-queue">Adding to the Queue&lt;/h2>
&lt;p>Back in our first function, you only require a few lines to add an item to the queue. The first line gets an environment variable like previously, from the local json file or the application settings within the Azure function.&lt;/p>
&lt;p>The object I create here is to simply pass all the details I need to the queue.&lt;/p>
&lt;p>var queueClient = (IQueueClient)new QueueClient(Environment.GetEnvironmentVariable(&amp;ldquo;ServiceBusConString&amp;rdquo;), Environment.GetEnvironmentVariable(&amp;ldquo;queueName&amp;rdquo;), ReceiveMode.PeekLock, null);
var fsObject = new
{
email = emailAddress,
contactId = (Guid)jContactResponse[&amp;ldquo;value&amp;rdquo;][0][&amp;ldquo;contactid&amp;rdquo;],
intent,
date,
device
};
Task messageReturn = SendMessagesAsync(JsonConvert.SerializeObject(fsObject).ToString());&lt;/p>
&lt;p>I then call the function SendMessagesAsync, converting the object to a string as I do it. The SendMessageASync is below&lt;/p>
&lt;p>private static async Task SendMessagesAsync(string messageToSend)
{
try
{
Message message = new Message(Encoding.UTF8.GetBytes(messageToSend));
Console.WriteLine(&amp;ldquo;Sending message: &amp;quot; + messageToSend);
await queueClient.SendAsync(message);
message = null;
}
catch (Exception ex)
{
Console.WriteLine(string.Format(&amp;quot;{0} :: Exception: {1}&amp;rdquo;, DateTime.Now, ex.Message));
}
}&lt;/p>
&lt;p>Call the method uses an Asynchronous call which I don&amp;rsquo;t wait for the return. I just assume it works and get on with responding to the user.&lt;/p>
&lt;h2 id="reading-the-queue">Reading the Queue&lt;/h2>
&lt;p>To read the object from the queue, you need to register the function as a subscriber to the queue.&lt;/p>
&lt;p>To do this, the function trigger needs to have a certain format&lt;/p>
&lt;p>[FunctionName(&amp;ldquo;AddToFS&amp;rdquo;)]
public static async void AddToFS([ServiceBusTrigger(&amp;ldquo;ccalexa&amp;rdquo;, Connection = &amp;ldquo;ServiceBusConString&amp;rdquo;)] string myQueueItem, ILogger log, ExecutionContext context)
{&lt;/p>
&lt;p>The parameters to the function connect to a queue called ccalexa &amp;amp; a service bus indicated in the application variable &amp;ldquo;ServiceBusConString&amp;rdquo;. This signature shows that Microsoft is thinking about moving between environments from the start.&lt;/p>
&lt;p>The next part of the function defines the parameters for the call to D365. This leads to parsing the object that is being found on the queue.&lt;/p>
&lt;p>JObject woObject = JObject.Parse(myQueueItem);&lt;/p>
&lt;p>Guid contactId = (Guid)woObject[&amp;ldquo;contactId&amp;rdquo;];
var email = woObject[&amp;ldquo;email&amp;rdquo;];
var intent = woObject[&amp;ldquo;intent&amp;rdquo;];
string date = (string)woObject[&amp;ldquo;date&amp;rdquo;];
string device = woObject[&amp;ldquo;device&amp;rdquo;].ToString();&lt;/p>
&lt;p>Once we have the detail of the item sent in, we can go to D365 and retrieve some records we need to create the Work Order, firstly to the Contact, to retrieve the account associated with it.&lt;/p>
&lt;p>var contactResult = await d365Connect.GetAsync(&amp;ldquo;api/data/v9.1/contacts(&amp;rdquo; + contactId + &amp;ldquo;)?$select=_parentcustomerid_value&amp;rdquo;);
if (!contactResult.IsSuccessStatusCode)
{
return;
}&lt;/p>
&lt;p>If the return is not success, something went wrong. Forgive me for not doing some proper error trapping here. Next, we work to get the Default Pricelist, from the account &amp;amp; work order type from the intent passed in&lt;/p>
&lt;p>JObject contactObject = JObject.Parse(contactResult.Content.ReadAsStringAsync().Result);
var accountId = contactObject[&amp;quot;_parentcustomerid_value&amp;quot;];
HttpResponseMessage accountResponse = await d365Connect.GetAsync(&amp;ldquo;api/data/v9.1/accounts(&amp;rdquo; + accountId.ToString() + &amp;ldquo;)?$select=_defaultpricelevelid_value&amp;rdquo;);&lt;/p>
&lt;p>JObject jaccountResponse = JObject.Parse(accountResponse.Content.ReadAsStringAsync().Result);&lt;/p>
&lt;p>Guid priceListId = (Guid)jaccountResponse[&amp;quot;_defaultpricelevelid_value&amp;quot;];&lt;/p>
&lt;p>HttpResponseMessage woTypeResponse = await d365Connect.GetAsync(&amp;ldquo;api/data/v9.1/msdyn_workordertypes()?$select=msdyn_workordertypeid&amp;amp;$filter=cc_alexaintent eq &amp;lsquo;&amp;rdquo; + intent + &amp;ldquo;&amp;rsquo;&amp;rdquo;);&lt;/p>
&lt;p>JObject jwotResponse = JObject.Parse(woTypeResponse.Content.ReadAsStringAsync().Result);
Guid woTypeId = (Guid)jwotResponse[&amp;ldquo;value&amp;rdquo;][0][&amp;ldquo;msdyn_workordertypeid&amp;rdquo;];&lt;/p>
&lt;p>Next, we build up the object to add as a new work order. Line 2 shows binding to a pricelist record. This method is used for type &amp;amp; account too. I also generate a random number for the name to keep consistent with the Flow version.&lt;/p>
&lt;p>JObject workOrder = new JObject();
workOrder.Add(&amp;ldquo;msdyn_pricelist@odata.bind&amp;rdquo;, (&amp;quot;/pricelevels(&amp;quot; + priceListId + &amp;ldquo;)&amp;rdquo;));&lt;/p>
&lt;p>workOrder.Add(&amp;ldquo;msdyn_name&amp;rdquo;, (&amp;ldquo;AZ&amp;rdquo; + new Random().Next(4000, 500000)));
workOrder.Add(&amp;ldquo;msdyn_serviceaccount@odata.bind&amp;rdquo;, (&amp;quot;/accounts(&amp;quot; + accountId + &amp;ldquo;)&amp;rdquo;));
workOrder.Add(&amp;ldquo;msdyn_systemstatus&amp;rdquo;, 690970000);&lt;/p>
&lt;p>workOrder.Add(&amp;ldquo;msdyn_workordertype@odata.bind&amp;rdquo;, (&amp;quot;/msdyn_workordertypes(&amp;quot; + woTypeId + &amp;ldquo;)&amp;rdquo;));
workOrder.Add(&amp;ldquo;msdyn_taxable&amp;rdquo;, false);&lt;/p>
&lt;p>if (date != string.Empty) workOrder.Add(&amp;ldquo;msdyn_timefrompromised&amp;rdquo;, date);
if (device != string.Empty) workOrder.Add(&amp;ldquo;msdyn_instructions&amp;rdquo;, device);&lt;/p>
&lt;p>log.LogInformation(workOrder.ToString());&lt;/p>
&lt;p>HttpRequestMessage createWO = new HttpRequestMessage(HttpMethod.Post, d365Connect.BaseAddress.ToString() + &amp;ldquo;api/data/v9.1/msdyn_workorders&amp;rdquo;);&lt;/p>
&lt;p>createWO.Content = new StringContent(workOrder.ToString(), Encoding.UTF8, &amp;ldquo;application/json&amp;rdquo;);&lt;/p>
&lt;p>HttpResponseMessage createWOResp = d365Connect.SendAsync(createWO, HttpCompletionOption.ResponseContentRead).Result;&lt;/p>
&lt;p>Finally a post method to the msdyn_workorders entity pushes this as a new work order into the system.&lt;/p>
&lt;h2 id="connecting-alexa-to-our-function">Connecting Alexa to our Function&lt;/h2>
&lt;p>This is the simplest bit. In the original set of &lt;a class="link" href="https://linked365.blog/2019/04/20/alexa-field-service-and-me-part-2-linking-to-flow/" target="_blank" rel="noopener"
>posts&lt;/a>, I talked about endpoints. The endpoint needs swapping to the function call.&lt;/p>
&lt;p>The URL is retrieved the same way as I did in my demo, from the Azure Function properties in the Azure portal.&lt;/p>
&lt;p>Update the Service Endpoint &amp;amp; you are good to go.&lt;/p>
&lt;p>&lt;img src="https://linked365.blog/images/2020/03-image-8.png?w=1024"
loading="lazy"
>&lt;/p>
&lt;h2 id="the-results">The results&lt;/h2>
&lt;p>When I started this challenge, I wanted to compare and contrast the response time between Flow and Functions. I would assume Functions would be quicker, but what would the difference be?&lt;/p>
&lt;p>Caveats here - Both the flow and the function are my code. I am sure that there are better ways of doing both. Both follow each other functionally so it is a fair comparison.&lt;/p>
&lt;p>To test, I swapped the end point and then did 5 runs to &amp;ldquo;warm up the code&amp;rdquo;. I found , particularly Azure function, took a while to come up to speed. This can be explained by cold starting of functions which will be the case in our scenario. Even flows run faster the second time through.&lt;/p>
&lt;p>I then ran the test cycle 10 times then used the monitoring within Alexa to monitor response time. Both sections I checked I was getting work orders being created correctly.&lt;/p>
&lt;p>The first blip in the chart is the Flow configuration. This has a P90 (90 percent of the requests where responded within this time) of over 4 seconds. It drops to a more respectable 1 second as the Flow is warmed up.&lt;/p>
&lt;p>&lt;img src="https://linked365.blog/images/2020/03-image-9.png?w=1024"
loading="lazy"
>&lt;/p>
&lt;p>The second blip is when the configuration swaps to Azure Function. You can see this has a peek around 2 seconds for the first call. then dropping to 300ms for each subsequent call. This is a vast improvement for the responsiveness of your app.&lt;/p>
&lt;p>Don&amp;rsquo;t get me wrong, I am not telling you to revert everything to a Function, it is using the right tool for the job.&lt;/p>
&lt;p>Power Automate got me this far, it is great for scenarios where you don&amp;rsquo;t need an immediate response, it is great to prove that developing Alexa into your toolset is beneficial, but if you want to get serious about the user experience, in this instance, a Function serves you better.&lt;/p></description></item></channel></rss>