{"id":66806,"date":"2023-04-24T13:00:59","date_gmt":"2023-04-24T21:00:59","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/devops\/?p=66806"},"modified":"2023-04-24T13:00:59","modified_gmt":"2023-04-24T21:00:59","slug":"use-power-automate-to-update-azure-devops-queries","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/devops\/use-power-automate-to-update-azure-devops-queries\/","title":{"rendered":"Use Power Automate to update Azure DevOps queries"},"content":{"rendered":"<p>In this blog post, I\u2019ll explain how to set up automation to automatically update Azure DevOps (ADO) <a href=\"https:\/\/learn.microsoft.com\/azure\/devops\/boards\/queries\/using-queries\">queries<\/a>. Using an automated <a href=\"https:\/\/learn.microsoft.com\/power-automate\/get-started-logic-flow\">Power Automate flow<\/a>, you save time by not having to manually update your queries for each sprint. Everyone using your queries benefits by having data and details that are always current and accurate.<\/p>\n<h3>Solution overview<\/h3>\n<p>The solution uses a Power Automate flow to update ADO queries. The queries contain a sprint number. For example, the sprint query contains <strong>2303<\/strong>. This number represents the third month (March) of the year 2023. Fortunately, the sprints that I\u2019m working with follow strictly to the month.<\/p>\n<p>The following query is an example of a simple sprint query:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2023\/04\/query-flow-01.png\" alt=\"Simple sprint query\" \/><\/p>\n<p>In addition, the <strong>previous<\/strong> sprint queries contain <strong>2302<\/strong>, and the <strong>next<\/strong> sprint query contains <strong>2304<\/strong>. We use dozens of sprint queries for each sprint, so having an automated update process is a big time saver.<\/p>\n<p>I\u2019ve organized the queries into an overall folder called <strong>AllSprints<\/strong>. The <strong>AllSprints<\/strong> folder contains the <strong>PreviousSprint<\/strong>, <strong>CurrentSprint<\/strong>, and <strong>NextSprint<\/strong> folders. Each of these folders contains queries.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2023\/04\/query-flow-02.png\" alt=\"Folders containing queries\" \/><\/p>\n<p>The flow is set to automatically run on the first of each month. Variables within the flow are created to track query paths, query folders, query numbers, and the <strong>Wiql<\/strong> value. The <a href=\"https:\/\/learn.microsoft.com\/azure\/devops\/boards\/queries\/query-support-integration-cross-service-extensions\">Work Item Query Language (Wiql)<\/a> value defines your query as a hyperlink and can easily be modified as a string variable.<\/p>\n<p>You use an array to loop through the sprint folders. Within this loop, you&#8217;ll determine all the queries that must be updated. Then, you use another loop to step through each query to determine if you need to update it. Updating a query involves changing the sprint number within the <strong>Wiql<\/strong> value of the query. Once all queries have been checked and updated, the flow finishes.<\/p>\n<h3>Basic query update process<\/h3>\n<p>The solution finds each query ID by using the <a href=\"https:\/\/learn.microsoft.com\/connectors\/visualstudioteamservices\/#list-queries-within-folder\">List queries within folder<\/a> action. It then uses the results to help you <strong>GET<\/strong> the details of each query by using the <a href=\"https:\/\/learn.microsoft.com\/connectors\/visualstudioteamservices\/#send-an-http-request-to-azure-devops\">Send HTTP request to Azure DevOps<\/a> action (shown below). The results allow you to find the <code>Wiql<\/code> value for each query. You then use a <strong>Send an HTTP request to Azure DevOps<\/strong> action again to <strong>UPDATE<\/strong> the <code>Wiql<\/code> value for the related query. All of these steps are detailed in the information below.<\/p>\n<h3>Complete query update process<\/h3>\n<p>The flow starts with a <a href=\"https:\/\/learn.microsoft.com\/power-automate\/run-scheduled-tasks\">Recurrence<\/a> action that is set to trigger at the beginning of each month.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2023\/04\/query-flow-03.png\" alt=\"Recurrence action\" \/><\/p>\n<p>Next, the following <a href=\"https:\/\/learn.microsoft.com\/power-automate\/create-variable-store-values\">variables<\/a> are created:<\/p>\n<ul>\n<li>A string variable containing the path of the primary folder containing the sprint subfolders. <\/li>\n<li>String variables containing the name of the previous, current, and next sprint subfolders. <\/li>\n<li>An array containing the sprint subfolder names. <\/li>\n<li>A string variable containing the primary sprint path. <\/li>\n<li>String variables containing the previous, current, and next sprint numbers. <\/li>\n<li>String variables containing the former values for the previous, current, and next sprint numbers. <\/li>\n<li>A string variable for the <code>Wiql<\/code> value. <\/li>\n<\/ul>\n<p>To find the queries, you use an <a href=\"https:\/\/learn.microsoft.com\/power-automate\/add-condition\">Apply to each<\/a> condition to step through the sprint array created above. Within this loop, you use the <strong>List queries within folder<\/strong> action to determine all the queries that you need to update.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2023\/04\/query-flow-04.png\" alt=\"Apply to each - Folder\" \/><\/p>\n<p>Then, you use another <strong>Apply to each<\/strong> action to do the main work.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2023\/04\/query-flow-05.png\" alt=\"Apply to each - Query node\" \/><\/p>\n<p>The above <strong>Apply to each \u2013 Query node<\/strong> steps through the values returned from <strong>List queries within folder \u2013 Each folder<\/strong>. At this point, you need to get the query details for each query. You use the action <a href=\"https:\/\/learn.microsoft.com\/connectors\/visualstudioteamservices\/#send-an-http-request-to-azure-devops\">Send HTTP request to Azure DevOps<\/a> to pass the <strong>GET<\/strong> method along with URL values containing the <strong>ID<\/strong> of the current query.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2023\/04\/query-flow-06.png\" alt=\"Send HTTP request to Azure DevOps - Get query details\" \/><\/p>\n<p>Next, you must use the <strong>Parse JSON<\/strong> action to get the values from the HTTP request.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2023\/04\/query-flow-07.png\" alt=\"Parse JSON\" \/><\/p>\n<p>The schema that I used is the following JSON:<\/p>\n<pre><code>{ \n\u202f\u202f\u202f\u202f\"type\":\u202f\"object\", \n\u202f\u202f\u202f\u202f\"properties\":\u202f{ \n\u202f\u202f\u202f\u202f\u202f\u202f\u202f\u202f\"id\":\u202f{ \n\u202f\u202f\u202f\u202f\u202f\u202f\u202f\u202f\u202f\u202f\u202f\u202f\"type\":\u202f\"string\" \n\u202f\u202f\u202f\u202f\u202f\u202f\u202f\u202f}, \n\u202f\u202f\u202f\u202f\u202f\u202f\u202f\u202f\"name\":\u202f{ \n\u202f\u202f\u202f\u202f\u202f\u202f\u202f\u202f\u202f\u202f\u202f\u202f\"type\":\u202f\"string\" \n\u202f\u202f\u202f\u202f\u202f\u202f\u202f\u202f}, \n\u202f\u202f\u202f\u202f\u202f\u202f\u202f\u202f\"path\":\u202f{ \n\u202f\u202f\u202f\u202f\u202f\u202f\u202f\u202f\u202f\u202f\u202f\u202f\"type\":\u202f\"string\" \n\u202f\u202f\u202f\u202f\u202f\u202f\u202f\u202f}, \n\u202f\u202f\u202f\u202f\u202f\u202f\u202f\u202f\"queryType\":\u202f{ \n\u202f\u202f\u202f\u202f\u202f\u202f\u202f\u202f\u202f\u202f\u202f\u202f\"type\":\u202f\"string\" \n\u202f\u202f\u202f\u202f\u202f\u202f\u202f\u202f}, \n\u202f\u202f\u202f\u202f\u202f\u202f\u202f\u202f\"wiql\":\u202f{ \n\u202f\u202f\u202f\u202f\u202f\u202f\u202f\u202f\u202f\u202f\u202f\u202f\"type\":\u202f\"string\" \n\u202f\u202f\u202f\u202f\u202f\u202f\u202f\u202f}, \n\u202f\u202f\u202f\u202f\u202f\u202f\u202f\u202f\"isPublic\":\u202f{ \n\u202f\u202f\u202f\u202f\u202f\u202f\u202f\u202f\u202f\u202f\u202f\u202f\"type\":\u202f\"boolean\" \n\u202f\u202f\u202f\u202f\u202f\u202f\u202f\u202f} \n\u202f\u202f\u202f\u202f} \n} \n<\/code><\/pre>\n<p>Additionally, you can set the <code>Wiql<\/code> output to a string variable so you can modify it later:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2023\/04\/query-flow-08.png\" alt=\"Set variable - strWiql\" \/><\/p>\n<p>Next, you must check which sprint is contained in the <em>path<\/em> of the query by checking if the <code>path<\/code> contains the string variable for the next sprint (<code>strNextSprintName<\/code>). For instance, if the current sprint is <strong>2303<\/strong> and the next sprint is <strong>2304<\/strong>, you must check if the <code>path<\/code> contains <strong>2304<\/strong>. Note that the <code>path<\/code> was defined from the above JSON.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2023\/04\/query-flow-09.png\" alt=\"Condition - Check if NextSprint\" \/><\/p>\n<p>Additionally, you must update the <code>Wiql<\/code> to use the next sprint number, in this case <strong>2304<\/strong>. To do this, you update the <code>strWiql<\/code> using the <code>replace<\/code> method.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2023\/04\/query-flow-10.png\" alt=\"Condition - Check if NextSprint and set variable using replace method\" \/><\/p>\n<p>The <code>replace<\/code> expression is the following:<\/p>\n<pre><code>replace(uriComponentToString(replace(body('Parse_JSON')?['wiql'],variables('strFormerNextSprintNumber'),variables('strNextSprintNumber'))),'\\','\\\\') \n<\/code><\/pre>\n<p>Note that we used two <code>replace<\/code> methods to make sure that the <code>Wiql<\/code> value was properly escaped.<\/p>\n<p>You must follow the same pattern to check the current sprint value and update it, as well as the previous sprint value.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2023\/04\/query-flow-11.png\" alt=\"Add additional conditions for CurrentSprint and Previous Sprint\" \/><\/p>\n<p>Next, you must use another <strong>Condition<\/strong> action to check whether <code>strWiql<\/code> has changed. If it has, you then use the <strong>Send an HTTP request to Azure DevOps<\/strong> action to update the <code>wiql<\/code> value for the related query.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2023\/04\/query-flow-12.png\" alt=\"Send an HTTP request to Azure DevOps\" \/><\/p>\n<p>To complete the loop, you can use a <strong>Set variable<\/strong> action to clear <code>strWiql<\/code>.<\/p>\n<p>Once you have successfully built and saved your Power Automate flow, you can run the <strong>Flow checker<\/strong> and <strong>Test<\/strong> the flow.<\/p>\n<p>If you\u2019re interested in learning more about Power Automate, see the following resources:<\/p>\n<ul>\n<li><a href=\"https:\/\/learn.microsoft.com\/power-automate\/overview-cloud\">Overview of cloud flows<\/a> <\/li>\n<li><a href=\"https:\/\/learn.microsoft.com\/power-automate\/multi-step-logic-flow\">Add multiple actions and advanced options to a cloud flow<\/a> <\/li>\n<li><a href=\"https:\/\/learn.microsoft.com\/power-automate\/add-condition\">Add a condition to a cloud flow<\/a> <\/li>\n<li><a href=\"https:\/\/learn.microsoft.com\/azure\/logic-apps\/workflow-definition-language-functions-reference\">Reference guide to workflow expression functions in Azure Logic Apps and Power Automate<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>In this blog post, I\u2019ll explain how to set up automation to automatically update Azure DevOps (ADO) queries. Using an automated Power Automate flow, you save time by not having to manually update your queries for each sprint. Everyone using your queries benefits by having data and details that are always current and accurate. Solution [&hellip;]<\/p>\n","protected":false},"author":112548,"featured_media":66821,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[227,229,1],"tags":[7263,7283],"class_list":["post-66806","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-agile","category-community","category-devops","tag-devops","tag-powerautomate"],"acf":[],"blog_post_summary":"<p>In this blog post, I\u2019ll explain how to set up automation to automatically update Azure DevOps (ADO) queries. Using an automated Power Automate flow, you save time by not having to manually update your queries for each sprint. Everyone using your queries benefits by having data and details that are always current and accurate. Solution [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/posts\/66806","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/users\/112548"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/comments?post=66806"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/posts\/66806\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/media\/66821"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/media?parent=66806"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/categories?post=66806"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/tags?post=66806"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}