<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>DevOps on Hat Full of Data</title><link>https://hatfullofdata.blog/categories/devops/</link><description>Recent content in DevOps on Hat Full of Data</description><generator>Hugo -- gohugo.io</generator><language>en-gb</language><lastBuildDate>Fri, 14 Feb 2025 10:58:01 +0000</lastBuildDate><atom:link href="https://hatfullofdata.blog/categories/devops/index.xml" rel="self" type="application/rss+xml"/><item><title>Inherited Value in a Parent Child Pattern</title><link>https://hatfullofdata.blog/inherited-value-in-a-parent-child-pattern/</link><pubDate>Mon, 23 Sep 2024 08:03:22 +0000</pubDate><guid>https://hatfullofdata.blog/inherited-value-in-a-parent-child-pattern/</guid><description>&lt;img src="https://hatfullofdata.blog/inherited-value-in-a-parent-child-pattern/cover.png" alt="Featured image of post Inherited Value in a Parent Child Pattern" /&gt;&lt;p&gt;In the previous post in this series we created a Parent Child hierarchy using a Dax Pattern from SQLBI. There are times it is useful to calculate an inherited value from parent item to child. The assigned to person on a User Story to be passed onto the child tasks that are not assigned to anyone, the target date of a feature to be passed down to the child user story and then onto the child tasks. The child items need to be completed before the parent target date.&lt;/p&gt;
&lt;h2 id="power-bi-and-devops-series"&gt;Power BI and DevOps Series
&lt;/h2&gt;&lt;p&gt;This post is part of a series:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/devops-data-into-power-bi/" target="_blank" rel="noopener"
 &gt;Get DevOps Data into Power BI&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/devops-parent-child-hierarchy-in-power-bi/" target="_blank" rel="noopener"
 &gt;Add Parent Child Hierarchy using DAX Patterns&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/inherited-value-in-a-parent-child-pattern/" target="_blank" rel="noopener"
 &gt;Inherited Value in a Parent Child pattern&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add conditional formatting icons the easy way&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="the-logic"&gt;The Logic
&lt;/h2&gt;&lt;p&gt;We are going to add this as a calculated column into the work items table. If you were doing this by hand you would go through the items in the path column in reverse, looking up each target date until you found a non-blank one. In DAX we do this by creating a table of the WorkItemIDs in the path and looking up their target dates. Then we filter to only rows with target dates and take the first one.&lt;/p&gt;
&lt;h2 id="inherited-value-dax"&gt;Inherited Value DAX
&lt;/h2&gt;&lt;p&gt;Lets work through an example.&lt;/p&gt;
&lt;p&gt;WorkItem 46 has an ItemPath of 38|39|45|46, which means it has a Path length of 4. So we start by using GENERATESERIES to create a column 1-4&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GENERATESERIES( 1 , PATHLENGTH( WorkItems[ItemPath] ), 1 )
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Value1234&lt;/p&gt;
&lt;p&gt;We put that inside a GENERATE function and use PATHITEMREVERSE to get the work item ids from the path and then LOOKUP to use that WorkItemID to get the TargetDate of each item. GENERATE function and nested VARs is one of my favourite combinations.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;VAR tabWorkItems = 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; GENERATE(
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; GENERATESERIES( 1 , PATHLENGTH( WorkItems[ItemPath] ), 1 ),
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; VAR tableRow = [Value]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; VAR _WorkItemID = VALUE( PATHITEMREVERSE( WorkItems[ItemPath] , tableRow ) )
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; VAR _TargetDate = LOOKUPVALUE( WorkItems[TargetDate] , WorkItems[WorkItemId] , _WorkItemID )
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; RETURN ROW ( 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;WorkItemID&amp;#34;,_WorkItemID,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;TargetDate&amp;#34;,_TargetDate
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;ValueWorkItemIDTargetDate1462453392024-09-094382024-11-1&lt;/p&gt;
&lt;p&gt;Then we filter the table to rows with a TargetDate and take the first row ordered by Value. This gives us one row of data.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;VAR tabFiltered =
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; TOPN( 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 1,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; FILTER(tabWorkItems,[TargetDate]&amp;gt;BLANK()),
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; [Value],ASC
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;ValueWorkItemIDTargetDate3392024-09-09&lt;/p&gt;
&lt;p&gt;Finally we use MINX to extract the TargetDate of that single row. The final calculated column DAX looks like this.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Inherited Target Date = 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;// Create a table of Value, WorkitemID and TargetDate
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;VAR tabWorkItems = 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; GENERATE(
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; GENERATESERIES( 1 , PATHLENGTH( WorkItems[ItemPath] ), 1 ),
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; VAR tableRow = [Value]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; VAR _WorkItemID = VALUE( PATHITEMREVERSE( WorkItems[ItemPath] , tableRow ) )
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; VAR _TargetDate = LOOKUPVALUE( WorkItems[TargetDate] , WorkItems[WorkItemId] , _WorkItemID )
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; RETURN ROW ( 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;WorkItemID&amp;#34;,_WorkItemID,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;TargetDate&amp;#34;,_TargetDate
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;// Take the first row of the filtered to non-blank
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;VAR tabFiltered =
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; TOPN( 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 1,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; FILTER(tabWorkItems,[TargetDate]&amp;gt;BLANK()),
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; [Value],ASC
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;// Return the Target Date
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;VAR Result = MINX( tabFiltered , [TargetDate] )
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;RETURN Result
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;img alt="Table showing WorkItemId, Title, TargetDate and Inherited Value of Target Date for items 38,39,45 and 46" class="gallery-image" data-flex-basis="983px" data-flex-grow="409" height="166" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/inherited-value-in-a-parent-child-pattern/image.png" width="680"&gt;&lt;/p&gt;
&lt;h2 id="inherited-value-conclusion"&gt;Inherited Value Conclusion
&lt;/h2&gt;&lt;p&gt;The Parent Child pattern is very useful and adding in the inherited values adds extra possibilities. I wanted to be able to identify the child items that were making the parent items late.&lt;/p&gt;
&lt;h2 id="references"&gt;References
&lt;/h2&gt;&lt;p&gt;This post builds on the last post in this series and using the SQLBI Parent Child found at &lt;a class="link" href="https://www.daxpatterns.com/parent-child-hierarchies/" target="_blank" rel="noopener"
 &gt;https://www.daxpatterns.com/parent-child-hierarchies/&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="more-power-bi-posts"&gt;More Power BI Posts
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-bi-conditional-formatting-update/" target="_blank" rel="noopener"
 &gt;Conditional Formatting Update&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-bi-data-refresh-date/" target="_blank" rel="noopener"
 &gt;Data Refresh Date&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-bi-inactive-relationships-in-a-measure/" target="_blank" rel="noopener"
 &gt;Using Inactive Relationships in a Measure&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-bi-dax-crossfilter-function/" target="_blank" rel="noopener"
 &gt;DAX CrossFilter Function&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-bi-coalesce-function-to-remove-blanks/" target="_blank" rel="noopener"
 &gt;COALESCE Function to Remove Blanks&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-bi-personalize-visuals/" target="_blank" rel="noopener"
 &gt;Personalize Visuals&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-bi-gradient-legends/" target="_blank" rel="noopener"
 &gt;Gradient Legends&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-bi-endorse-a-dataset/" target="_blank" rel="noopener"
 &gt;Endorse a Dataset as Promoted or Certified&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-bi-qa-synonyms-update/" target="_blank" rel="noopener"
 &gt;Q&amp;amp;A Synonyms Update&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-bi-import-text-using-examples/" target="_blank" rel="noopener"
 &gt;Import Text Using Examples&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/paginated-report-resources/" target="_blank" rel="noopener"
 &gt;Paginated Report Resources&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/refreshing-datasets-automatically-with-dataflow/" target="_blank" rel="noopener"
 &gt;Refreshing Datasets Automatically with Power BI Dataflows&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/charticulator-simple-custom-chart/" target="_blank" rel="noopener"
 &gt;Charticulator&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-bi-dataverse-connector-july-2022-update/" target="_blank" rel="noopener"
 &gt;Dataverse Connector – July 2022 Update&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-bi-dataverse-choices-and-choice-column/" target="_blank" rel="noopener"
 &gt;Dataverse Choice Columns&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-bi-switch-dataverse-tenancy/" target="_blank" rel="noopener"
 &gt;Switch Dataverse Tenancy&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-bi-connecting-to-google-analytics/" target="_blank" rel="noopener"
 &gt;Connecting to Google Analytics&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-bi-take-over-a-dataset/" target="_blank" rel="noopener"
 &gt;Take Over a Dataset&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/export-data-from-power-bi-visuals/" target="_blank" rel="noopener"
 &gt;Export Data from Power BI Visuals&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-bi-embed-a-paginated-report/" target="_blank" rel="noopener"
 &gt;Embed a Paginated Report&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/using-sql-on-dataverse-for-power-bi/" target="_blank" rel="noopener"
 &gt;Using SQL on Dataverse for Power BI&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-platform-solution-and-power-bi-part-1/" target="_blank" rel="noopener"
 &gt;Power Platform Solution and Power BI Series&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-bi-creating-a-custom-smart-narrative/" target="_blank" rel="noopener"
 &gt;Creating a Custom Smart Narrative&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-button-in-a-power-bi-report/" target="_blank" rel="noopener"
 &gt;Power Automate Button in a Power BI Report&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="power-bi-series"&gt;Power BI Series
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/svg-in-power-bi-part-1-svg-basics/" target="_blank" rel="noopener"
 &gt;SVG in Power BI series&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-bi-connecting-to-project-online/" target="_blank" rel="noopener"
 &gt;Power BI and Project Online series&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-bi-slicers-introduction/" target="_blank" rel="noopener"
 &gt;Slicers series&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-bi-create-a-dataflow/" target="_blank" rel="noopener"
 &gt;Dataflow series&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/svg-in-power-bi-part-1-svg-basics/" target="_blank" rel="noopener"
 &gt;Power BI SVG series&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-and-power-bi-rest-api/" target="_blank" rel="noopener"
 &gt;Power Automate and Power BI Rest API series&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/devops-data-into-power-bi/" target="_blank" rel="noopener"
 &gt;Power BI and DevOps series&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>DevOps Parent Child Hierarchy in Power BI</title><link>https://hatfullofdata.blog/devops-parent-child-hierarchy-in-power-bi/</link><pubDate>Mon, 02 Sep 2024 08:27:43 +0000</pubDate><guid>https://hatfullofdata.blog/devops-parent-child-hierarchy-in-power-bi/</guid><description>&lt;img src="https://hatfullofdata.blog/devops-parent-child-hierarchy-in-power-bi/cover.png" alt="Featured image of post DevOps Parent Child Hierarchy in Power BI" /&gt;&lt;p&gt;Azure DevOps is built around the pattern than items have child items and so a backlog view will show the items in a hierarchy. When the work items some into Power BI that is not shown. So this gives me the chance to use one of my favourite resources from SQLBI, DAX Patterns and their Parent-Child pattern.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Screen grab from DevOps showing a backlog view of nested items" class="gallery-image" data-flex-basis="468px" data-flex-grow="195" height="348" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/devops-parent-child-hierarchy-in-power-bi/image-8.png" width="680"&gt;&lt;/p&gt;
&lt;p&gt;I am using the Agile process so the hierarchy is Epic – Feature – User Story – Task. I have made sure my project includes all the layers correctly. You can work around oddities but for this post we will assume all layers of the items are as we expect.&lt;/p&gt;
&lt;h2 id="power-bi-and-devops-series"&gt;Power BI and DevOps Series
&lt;/h2&gt;&lt;p&gt;This post is part of a series:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/devops-data-into-power-bi/" target="_blank" rel="noopener"
 &gt;Get DevOps Data into Power BI&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/devops-parent-child-hierarchy-in-power-bi/" target="_blank" rel="noopener"
 &gt;Add Parent Child Hierarchy using DAX Patterns&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/inherited-value-in-a-parent-child-pattern/" target="_blank" rel="noopener"
 &gt;Inherited Value in a Parent Child pattern&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add conditional formatting icons the easy way&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="dax-patterns--parent-child-hierarchy"&gt;DAX Patterns – Parent Child Hierarchy
&lt;/h2&gt;&lt;p&gt;&lt;img alt="Dax Patterns logo" class="gallery-image" data-flex-basis="652px" data-flex-grow="272" height="136" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/devops-parent-child-hierarchy-in-power-bi/Dax-Patterns.png" width="370"&gt;&lt;/p&gt;
&lt;p&gt;DAX patterns comes in multiple formats, a book, a website and videos. They are written by SQLBI and have saved me hours and hours. For this post we are going to adapt the Parent-Child pattern to DevOps data. I am going to use the report built in the previous post in this series. The pattern is fully explained here &lt;a class="link" href="https://www.daxpatterns.com/parent-child-hierarchies" target="_blank" rel="noopener"
 &gt;https://www.daxpatterns.com/parent-child-hierarchies&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="adding-the-path"&gt;Adding the Path
&lt;/h2&gt;&lt;p&gt;The pattern uses a function called path. This function takes an row id and the parent row id and walks up the path until it reaches a row with no parent. Work Items in DevOps have WorkItemID and ParentWorkItemID. So following the instructions we add a column to the WorkItems table.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ItemPath = PATH(WorkItems[WorkItemId],WorkItems[ParentWorkItemId])
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;img alt="WorkItems table table showing WorkitemID, Title, ParentWorkItemId and the new column ItemPath" class="gallery-image" data-flex-basis="759px" data-flex-grow="316" height="215" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/devops-parent-child-hierarchy-in-power-bi/image-7.png" width="680"&gt;&lt;/p&gt;
&lt;p&gt;The ItemPath gives us the list of IDs in the hierarchy above the current row. It does requires the path to be complete so it might not work if you have filtered out some of the items.&lt;/p&gt;
&lt;h2 id="adding-devops-hierarchy-level-columns"&gt;Adding DevOps Hierarchy Level Columns
&lt;/h2&gt;&lt;p&gt;From the above post these are the Level 1 to Level 4 columns, we just need to swap out the right column names. We also know the the names of our levels from DevOps so we can use slightly more meaningful names, Level 1 = Epic, Level 2 = Feature etc. Add the 4 columns using the pattern below, copied straight from the SQLBI post.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Epic = 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;VAR LevelNumber = 1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;VAR LevelKey = PATHITEM ( WorkItems[ItemPath], LevelNumber, INTEGER )
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;VAR LevelName = LOOKUPVALUE ( WorkItems[Title], WorkItems[WorkItemId], LevelKey )
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;VAR Result = LevelName
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;RETURN
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Result
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;img alt="Table of data showing ItemPath, Epic, Feature, User Story and Task columns." class="gallery-image" data-flex-basis="602px" data-flex-grow="250" height="271" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/devops-parent-child-hierarchy-in-power-bi/image-9.png" width="680"&gt;&lt;/p&gt;
&lt;h2 id="adding-a-matrix"&gt;Adding a Matrix
&lt;/h2&gt;&lt;p&gt;The quickest way to see the hierarchy in action is to add a matrix visual. Into the rows we put the levels of the hierarchy to whatever levels we want. Into the values I put # Work Items. It looks great we can expand the different levels and see the 72 work items split out into features etc.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Picture of the matrix showing the 4 fields added to Rows and Work Items measure added to Values and the blank lines that appear in the Matrix at the top of each expanded section" class="gallery-image" data-flex-basis="348px" data-flex-grow="145" height="468" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/devops-parent-child-hierarchy-in-power-bi/image-10.png" width="680"&gt;&lt;/p&gt;
&lt;p&gt;But we get blank rows at the top of each section. Not to worry of course our friends at SQLBI have a solution.&lt;/p&gt;
&lt;h2 id="fixing-the-measures"&gt;Fixing the measures
&lt;/h2&gt;&lt;p&gt;In order for this to work we need to compare the maximum path length with the level being displayed. The path length we add as a column Depth to Work Items and then add a measure MaxRowDepth. Then using ISINSCOPE function we calculate the display depth in a measure called ItemBrowseDepth.&lt;/p&gt;
&lt;h3 id="column-added-to-workitems-table"&gt;Column Added to WorkItems table
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Depth = PATHLENGTH( WorkItems[ItemPath] )
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="2-measures-added-to-workitems-table"&gt;2 Measures Added to WorkItems table
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;MaxRowDepth = MAX( WorkItems[Depth] )
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ItemBrowseDepth =
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ISINSCOPE ( WorkItems[Epic] ) 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; + ISINSCOPE ( WorkItems[Feature] )
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; + ISINSCOPE ( WorkItems[User Story] )
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; + ISINSCOPE ( WorkItems[Task] )
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Now we can add measures that use these values to decide if to display the row or not. The logic being we only show rows where the browse depth is less than or equal to the max row depth. So I fix the # Work Items using the example from the SQLBI Pattern.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;span class="lnt"&gt;37
&lt;/span&gt;&lt;span class="lnt"&gt;38
&lt;/span&gt;&lt;span class="lnt"&gt;39
&lt;/span&gt;&lt;span class="lnt"&gt;40
&lt;/span&gt;&lt;span class="lnt"&gt;41
&lt;/span&gt;&lt;span class="lnt"&gt;42
&lt;/span&gt;&lt;span class="lnt"&gt;43
&lt;/span&gt;&lt;span class="lnt"&gt;44
&lt;/span&gt;&lt;span class="lnt"&gt;45
&lt;/span&gt;&lt;span class="lnt"&gt;46
&lt;/span&gt;&lt;span class="lnt"&gt;47
&lt;/span&gt;&lt;span class="lnt"&gt;48
&lt;/span&gt;&lt;span class="lnt"&gt;49
&lt;/span&gt;&lt;span class="lnt"&gt;50
&lt;/span&gt;&lt;span class="lnt"&gt;51
&lt;/span&gt;&lt;span class="lnt"&gt;52
&lt;/span&gt;&lt;span class="lnt"&gt;53
&lt;/span&gt;&lt;span class="lnt"&gt;54
&lt;/span&gt;&lt;span class="lnt"&gt;55
&lt;/span&gt;&lt;span class="lnt"&gt;56
&lt;/span&gt;&lt;span class="lnt"&gt;57
&lt;/span&gt;&lt;span class="lnt"&gt;58
&lt;/span&gt;&lt;span class="lnt"&gt;59
&lt;/span&gt;&lt;span class="lnt"&gt;60
&lt;/span&gt;&lt;span class="lnt"&gt;61
&lt;/span&gt;&lt;span class="lnt"&gt;62
&lt;/span&gt;&lt;span class="lnt"&gt;63
&lt;/span&gt;&lt;span class="lnt"&gt;64
&lt;/span&gt;&lt;span class="lnt"&gt;65
&lt;/span&gt;&lt;span class="lnt"&gt;66
&lt;/span&gt;&lt;span class="lnt"&gt;67
&lt;/span&gt;&lt;span class="lnt"&gt;68
&lt;/span&gt;&lt;span class="lnt"&gt;69
&lt;/span&gt;&lt;span class="lnt"&gt;70
&lt;/span&gt;&lt;span class="lnt"&gt;71
&lt;/span&gt;&lt;span class="lnt"&gt;72
&lt;/span&gt;&lt;span class="lnt"&gt;73
&lt;/span&gt;&lt;span class="lnt"&gt;74
&lt;/span&gt;&lt;span class="lnt"&gt;75
&lt;/span&gt;&lt;span class="lnt"&gt;76
&lt;/span&gt;&lt;span class="lnt"&gt;77
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# Work Items =
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;VAR Val = COUNTROWS ( WorkItems )
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;VAR ShowRow = [ItemBrowseDepth] 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;This now fixes the matrix to not show blank rows
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;![Matrix with no blank rows](image-11.png)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;## Conclusion on Parent Child Hierarchy
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;This makes the DevOps data easy to report on. The measures roll up the data to the different levels so the total hours can be calculated etc. Handling DevOps plans with missing layers is hard work, I’ve done it where some User Stories were children of the Epic. That made the pattern clunky so I’d probably push back now and insist the DevOps plan was kept clean.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;## More Power BI Posts
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- [Conditional Formatting Update](https://hatfullofdata.blog/power-bi-conditional-formatting-update/)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- [Data Refresh Date](https://hatfullofdata.blog/power-bi-data-refresh-date/)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- [Using Inactive Relationships in a Measure](https://hatfullofdata.blog/power-bi-inactive-relationships-in-a-measure/)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- [DAX CrossFilter Function](https://hatfullofdata.blog/power-bi-dax-crossfilter-function/)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- [COALESCE Function to Remove Blanks](https://hatfullofdata.blog/power-bi-coalesce-function-to-remove-blanks/)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- [Personalize Visuals](https://hatfullofdata.blog/power-bi-personalize-visuals/)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- [Gradient Legends](https://hatfullofdata.blog/power-bi-gradient-legends/)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- [Endorse a Dataset as Promoted or Certified](https://hatfullofdata.blog/power-bi-endorse-a-dataset/)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- [Q&lt;span class="err"&gt;&amp;amp;&lt;/span&gt;A Synonyms Update](https://hatfullofdata.blog/power-bi-qa-synonyms-update/)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- [Import Text Using Examples](https://hatfullofdata.blog/power-bi-import-text-using-examples/)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- [Paginated Report Resources](https://hatfullofdata.blog/paginated-report-resources/)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- [Refreshing Datasets Automatically with Power BI Dataflows](https://hatfullofdata.blog/refreshing-datasets-automatically-with-dataflow/)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- [Charticulator](https://hatfullofdata.blog/charticulator-simple-custom-chart/)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- [Dataverse Connector – July 2022 Update](https://hatfullofdata.blog/power-bi-dataverse-connector-july-2022-update/)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- [Dataverse Choice Columns](https://hatfullofdata.blog/power-bi-dataverse-choices-and-choice-column/)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- [Switch Dataverse Tenancy](https://hatfullofdata.blog/power-bi-switch-dataverse-tenancy/)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- [Connecting to Google Analytics](https://hatfullofdata.blog/power-bi-connecting-to-google-analytics/)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- [Take Over a Dataset](https://hatfullofdata.blog/power-bi-take-over-a-dataset/)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- [Export Data from Power BI Visuals](https://hatfullofdata.blog/export-data-from-power-bi-visuals/)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- [Embed a Paginated Report](https://hatfullofdata.blog/power-bi-embed-a-paginated-report/)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- [Using SQL on Dataverse for Power BI](https://hatfullofdata.blog/using-sql-on-dataverse-for-power-bi/)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- [Power Platform Solution and Power BI Series](https://hatfullofdata.blog/power-platform-solution-and-power-bi-part-1/)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- [Creating a Custom Smart Narrative](https://hatfullofdata.blog/power-bi-creating-a-custom-smart-narrative/)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- [Power Automate Button in a Power BI Report](https://hatfullofdata.blog/power-automate-button-in-a-power-bi-report/)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;## Power BI Series
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- [SVG in Power BI series](https://hatfullofdata.blog/svg-in-power-bi-part-1-svg-basics/)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- [Power BI and Project Online series](https://hatfullofdata.blog/power-bi-connecting-to-project-online/)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- [Slicers series](https://hatfullofdata.blog/power-bi-slicers-introduction/)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- [Dataflow series](https://hatfullofdata.blog/power-bi-create-a-dataflow/)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- [Power BI SVG series](https://hatfullofdata.blog/svg-in-power-bi-part-1-svg-basics/)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- [Power Automate and Power BI Rest API series](https://hatfullofdata.blog/power-automate-and-power-bi-rest-api/)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- [Power BI and DevOps series](https://hatfullofdata.blog/devops-data-into-power-bi/)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;</description></item><item><title>Get DevOps Data into Power BI</title><link>https://hatfullofdata.blog/devops-data-into-power-bi/</link><pubDate>Tue, 27 Aug 2024 09:09:57 +0000</pubDate><guid>https://hatfullofdata.blog/devops-data-into-power-bi/</guid><description>&lt;img src="https://hatfullofdata.blog/devops-data-into-power-bi/cover.png" alt="Featured image of post Get DevOps Data into Power BI" /&gt;&lt;p&gt;Azure DevOps is full of data that management want to to report on. Adding a Power BI report is an obvious way to do this. There are plenty of sample reports and Microsoft posts giving you guidance how to get DevOps data into Power BI. This post is my take on how I would build the reports. My focus is making them easy to extend and easy to point at a different organisation and project.&lt;/p&gt;
&lt;h2 id="power-bi-and-devops-series"&gt;Power BI and DevOps Series
&lt;/h2&gt;&lt;p&gt;This post is part of a series:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/devops-data-into-power-bi/" target="_blank" rel="noopener"
 &gt;Get DevOps Data into Power BI&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/devops-parent-child-hierarchy-in-power-bi/" target="_blank" rel="noopener"
 &gt;Add Parent Child Hierarchy using DAX Patterns&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/inherited-value-in-a-parent-child-pattern/" target="_blank" rel="noopener"
 &gt;Inherited Value in a Parent Child pattern&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add conditional formatting icons the easy way&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="youtube-version"&gt;YouTube Version
&lt;/h2&gt;&lt;p&gt;&lt;a class="link" href="https://youtu.be/DAHEv2YoTP8" target="_blank" rel="noopener"
 &gt;&lt;img class="gallery-image" data-flex-basis="426px" data-flex-grow="177" height="383" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/devops-data-into-power-bi/Power-BI-and-Devops-Data-Time-0_00_0000.png" width="680"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="microsoft-references-for-devops-data-into-power-bi"&gt;Microsoft References for DevOps Data into Power BI
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://learn.microsoft.com/en-us/azure/devops/report/powerbi/odataquery-connect?wt.mc_id=DX-MVP-5003563" target="_blank" rel="noopener"
 &gt;https://learn.microsoft.com/en-us/azure/devops/report/powerbi/odataquery-connect&lt;/a&gt;Great post showing you to use Visual Studio Code to write the query, test it and then use it in Power BI&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://learn.microsoft.com/en-us/azure/devops/report/powerbi/sample-odata-overview?wt.mc_id=DX-MVP-5003563" target="_blank" rel="noopener"
 &gt;https://learn.microsoft.com/en-us/azure/devops/report/powerbi/sample-odata-overview&lt;/a&gt;Lots of sample reports&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="odata-queries"&gt;OData Queries
&lt;/h2&gt;&lt;p&gt;We get DevOps Data into Power BI using an OData query. From the first Microsoft link above it shows the query is made up of different parts. The first part is a path and then extras that select columns, filter the data and sort the data.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Example OData query to get DevOps Data into Power BI. Scroll down to find a code version" class="gallery-image" data-flex-basis="1717px" data-flex-grow="715" height="95" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/devops-data-into-power-bi/image-1.png" width="680"&gt;&lt;/p&gt;
&lt;p&gt;The simplest form though is just the URL, that will bring through all the columns and all the rows of data. We will start with that.&lt;/p&gt;
&lt;h2 id="simplest-query-for-work-items"&gt;Simplest Query for Work Items
&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;https://analytics.dev.azure.com/{Organisation}/{Project}/_odata/v3.0-preview/WorkItems?
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;The path includes the organisation and the project names. So we start in Power Query, by clicking on Transform data on the Home ribbon in Power BI. Then click on Manage Parameters to open a dialog. You need to add 2 parameters, Organisation and Project. Click on New to start a new parameter, enter in a name and description (yes add that description, future you will thank you), select type text and enter in the current value for the Organisation and the Project.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Screen shots of the Transform data button, the Manage Parameters button and then the dialog showing the list of Parameters and the form to fill in" class="gallery-image" data-flex-basis="226px" data-flex-grow="94" height="722" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/devops-data-into-power-bi/image-2.png" width="680"&gt;&lt;/p&gt;
&lt;p&gt;We could now use Get Data and select OData and build the string… But that is hard work and not easy to read! So I’ve written a template to walk you through. Click on New Source and select blank query. Click on Advanced Editor and paste in the code below.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;let
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; // Path using 2 parameters
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Path = &amp;#34;https://analytics.dev.azure.com/&amp;#34; &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; Organisation &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; &amp;#34;/&amp;#34; &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; Project &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; &amp;#34;/_odata/v3.0-preview/WorkItems?&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; // URL made up of parts
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; URL = Path,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; // Do the OData query
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Source = OData.Feed(URL, null, [Implementation=&amp;#34;2.0&amp;#34;,OmitValues = ODataOmitValues.Nulls,ODataVersion = 4])
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;in
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Source
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;The Path line uses the 2 parameters and points to the WorkItems. The URL line looks redundant now but will make more sense when the query has more parts. The Source line uses the URL and includes the flags recommended by the link at the top of this post to prevent throttling.&lt;/p&gt;
&lt;p&gt;When you click OK and set up the connection if required you should get a table of data with lots of columns.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Table of data from DevOps showing various columns" class="gallery-image" data-flex-basis="766px" data-flex-grow="319" height="213" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/devops-data-into-power-bi/image-3.png" width="680"&gt;&lt;/p&gt;
&lt;h2 id="selecting-the-columns"&gt;Selecting the columns
&lt;/h2&gt;&lt;p&gt;The above query gives us more columns than we want. Rather than removing the columns in Power Query we can add a select line to the query which will make the OData query faster. So we need to pick the columns we want while we can see them all. I selected and put them into a comma separated string to make up a $select string to add to the Power Query&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;let
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; // Path using 2 parameters and select columns
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Path = &amp;#34;https://analytics.dev.azure.com/&amp;#34; &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; Organisation &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; &amp;#34;/&amp;#34; &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; Project &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; &amp;#34;/_odata/v3.0-preview/WorkItems?&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Select = &amp;#34;$select=WorkItemID,Title,WorkItemType,State,TargetDate,ParentWorkItemID,OriginalEstimate,AssignedToUserSK&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; // URL made up of parts
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; URL = Path &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; Select,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; // Do the OData query
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Source = OData.Feed(URL, null, [Implementation=&amp;#34;2.0&amp;#34;,OmitValues = ODataOmitValues.Nulls,ODataVersion = 4])
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;in
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Source
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;When you finish editing the query you will get a table of the work items from DevOps.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Table of data just showing the 8 columns selected." class="gallery-image" data-flex-basis="1066px" data-flex-grow="444" height="153" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/devops-data-into-power-bi/image-4.png" width="680"&gt;&lt;/p&gt;
&lt;p&gt;You could add more parts to the query for filtering and sorting if required.&lt;/p&gt;
&lt;h2 id="user-and-calendar-table"&gt;User and Calendar Table
&lt;/h2&gt;&lt;p&gt;DevOps has other tables including a user list and a calendar. These can be pulled through to Power BI using the same pattern.&lt;/p&gt;
&lt;h3 id="users"&gt;Users
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;let
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; // Path using 2 parameters and select columns
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Path = &amp;#34;https://analytics.dev.azure.com/&amp;#34; &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; Organisation &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; &amp;#34;/&amp;#34; &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; Project &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; &amp;#34;/_odata/v3.0-preview/Users?&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Select = &amp;#34;$select=UserSK,UserName,UserEmail&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; // URL made up of parts 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; URL = Path &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; Select,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; // Do the OData query
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Source = OData.Feed(URL, null, [Implementation=&amp;#34;2.0&amp;#34;,OmitValues = ODataOmitValues.Nulls,ODataVersion = 4])
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;in
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Source
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="calendar"&gt;Calendar
&lt;/h3&gt;&lt;p&gt;This includes a filter for the Year to be greater than or equal to, ge, 2023.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;let
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; // Path using 2 parameters and select columns
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Path = &amp;#34;https://analytics.dev.azure.com/&amp;#34; &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; Organisation &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; &amp;#34;/&amp;#34; &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; Project &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; &amp;#34;/_odata/v3.0-preview/Dates?&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Select = &amp;#34;$select=Date,MonthName,MonthOfYear,Year&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Filter = &amp;#34;&lt;span class="err"&gt;&amp;amp;&lt;/span&gt;$filter=Year ge 2023&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; // URL made up of parts 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; URL = Path &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; Select &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; Filter,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; // Do the OData query
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Source = OData.Feed(URL, null, [Implementation=&amp;#34;2.0&amp;#34;,OmitValues = ODataOmitValues.Nulls,ODataVersion = 4])
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;in
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Source
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="some-modelling"&gt;Some Modelling
&lt;/h2&gt;&lt;p&gt;Once the queries are written and loaded into Power BI desktop we are ready to do some modelling. I’m keeping it very light as I have another post coming that will implement the Parent Child hierarchy needed.&lt;/p&gt;
&lt;h3 id="relationships"&gt;Relationships
&lt;/h3&gt;&lt;p&gt;The relationship model is only three tables, fact table WorkItems and the 2 dimension tables Users and Calendar. The relationships are&lt;/p&gt;
&lt;p&gt;TableColumnRelationshipTableColumnUsersUserSK1 to ManyWorkItemsAssignedToUserSKCalendarDate1 to ManyWorkItemsTargetDate&lt;/p&gt;
&lt;p&gt;&lt;img alt="Screen grab from the relationships screen" class="gallery-image" data-flex-basis="396px" data-flex-grow="165" height="412" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/devops-data-into-power-bi/image-5.png" width="680"&gt;&lt;/p&gt;
&lt;h3 id="measures"&gt;Measures
&lt;/h3&gt;&lt;p&gt;I only created two measures. The first was for the total hours in the original estimate column. If we had included completed and remaining hours I would have created measures for them as well. The second one was for the count of items&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Total Hours = SUM(WorkItems[OriginalEstimate])
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# Work Items = COUNTROWS(WorkItems)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="create-visuals"&gt;Create Visuals
&lt;/h2&gt;&lt;p&gt;I kept it simple. Not all the tasks had Target dates so we could not do much via dates. So I had total hours and number of tasks and Users and Item States. I created a table and a tree map visual.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Screen grab of a table listing the users with # Work Items and Total Hours and then below a treemap showing Total Hours by State" class="gallery-image" data-flex-basis="257px" data-flex-grow="107" height="634" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/devops-data-into-power-bi/image-6.png" width="680"&gt;&lt;/p&gt;
&lt;h2 id="conclusion-on-devops-data-into-power-bi"&gt;Conclusion on DevOps Data into Power BI
&lt;/h2&gt;&lt;p&gt;The above model is limited, I can’t see the structure of the project, I can’t rollup the tasks into user stories etc. It does have the data in it to get there though. The next post on adding the Parent-Child hierarchy will help.&lt;/p&gt;
&lt;h2 id="more-power-bi-posts"&gt;More Power BI Posts
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-bi-conditional-formatting-update/" target="_blank" rel="noopener"
 &gt;Conditional Formatting Update&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-bi-data-refresh-date/" target="_blank" rel="noopener"
 &gt;Data Refresh Date&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-bi-inactive-relationships-in-a-measure/" target="_blank" rel="noopener"
 &gt;Using Inactive Relationships in a Measure&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-bi-dax-crossfilter-function/" target="_blank" rel="noopener"
 &gt;DAX CrossFilter Function&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-bi-coalesce-function-to-remove-blanks/" target="_blank" rel="noopener"
 &gt;COALESCE Function to Remove Blanks&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-bi-personalize-visuals/" target="_blank" rel="noopener"
 &gt;Personalize Visuals&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-bi-gradient-legends/" target="_blank" rel="noopener"
 &gt;Gradient Legends&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-bi-endorse-a-dataset/" target="_blank" rel="noopener"
 &gt;Endorse a Dataset as Promoted or Certified&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-bi-qa-synonyms-update/" target="_blank" rel="noopener"
 &gt;Q&amp;amp;A Synonyms Update&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-bi-import-text-using-examples/" target="_blank" rel="noopener"
 &gt;Import Text Using Examples&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/paginated-report-resources/" target="_blank" rel="noopener"
 &gt;Paginated Report Resources&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/refreshing-datasets-automatically-with-dataflow/" target="_blank" rel="noopener"
 &gt;Refreshing Datasets Automatically with Power BI Dataflows&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/charticulator-simple-custom-chart/" target="_blank" rel="noopener"
 &gt;Charticulator&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-bi-dataverse-connector-july-2022-update/" target="_blank" rel="noopener"
 &gt;Dataverse Connector – July 2022 Update&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-bi-dataverse-choices-and-choice-column/" target="_blank" rel="noopener"
 &gt;Dataverse Choice Columns&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-bi-switch-dataverse-tenancy/" target="_blank" rel="noopener"
 &gt;Switch Dataverse Tenancy&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-bi-connecting-to-google-analytics/" target="_blank" rel="noopener"
 &gt;Connecting to Google Analytics&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-bi-take-over-a-dataset/" target="_blank" rel="noopener"
 &gt;Take Over a Dataset&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/export-data-from-power-bi-visuals/" target="_blank" rel="noopener"
 &gt;Export Data from Power BI Visuals&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-bi-embed-a-paginated-report/" target="_blank" rel="noopener"
 &gt;Embed a Paginated Report&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/using-sql-on-dataverse-for-power-bi/" target="_blank" rel="noopener"
 &gt;Using SQL on Dataverse for Power BI&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-platform-solution-and-power-bi-part-1/" target="_blank" rel="noopener"
 &gt;Power Platform Solution and Power BI Series&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-bi-creating-a-custom-smart-narrative/" target="_blank" rel="noopener"
 &gt;Creating a Custom Smart Narrative&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-button-in-a-power-bi-report/" target="_blank" rel="noopener"
 &gt;Power Automate Button in a Power BI Report&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="power-bi-series"&gt;Power BI Series
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/svg-in-power-bi-part-1-svg-basics/" target="_blank" rel="noopener"
 &gt;SVG in Power BI series&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-bi-connecting-to-project-online/" target="_blank" rel="noopener"
 &gt;Power BI and Project Online series&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-bi-slicers-introduction/" target="_blank" rel="noopener"
 &gt;Slicers series&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-bi-create-a-dataflow/" target="_blank" rel="noopener"
 &gt;Dataflow series&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/svg-in-power-bi-part-1-svg-basics/" target="_blank" rel="noopener"
 &gt;Power BI SVG series&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-and-power-bi-rest-api/" target="_blank" rel="noopener"
 &gt;Power Automate and Power BI Rest API series&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/devops-data-into-power-bi/" target="_blank" rel="noopener"
 &gt;Power BI and DevOps series&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Editing Azure DevOps items in Excel</title><link>https://hatfullofdata.blog/editing-azure-devops-items-in-excel/</link><pubDate>Tue, 20 Aug 2024 08:30:38 +0000</pubDate><guid>https://hatfullofdata.blog/editing-azure-devops-items-in-excel/</guid><description>&lt;img src="https://hatfullofdata.blog/editing-azure-devops-items-in-excel/cover.png" alt="Featured image of post Editing Azure DevOps items in Excel" /&gt;&lt;p&gt;One of the easiest ways to edit a project in Azure DevOps is to connect Excel to the project. This post walks you through connecting to a project and updating the DevOps items in Excel. Its mostly for selfish reasons as I need to remember how to do this.&lt;/p&gt;
&lt;p&gt;Microsoft also has a post about this at &lt;a class="link" href="https://learn.microsoft.com/en-us/azure/devops/boards/backlogs/office/bulk-add-modify-work-items-excel?wt.mc_id=DX-MVP-5003563" target="_blank" rel="noopener"
 &gt;https://learn.microsoft.com/en-us/azure/devops/boards/backlogs/office/bulk-add-modify-work-items-excel&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="add-excel-extension"&gt;Add Excel Extension
&lt;/h2&gt;&lt;p&gt;This needs an Excel extension added from the Visual Studio site. You need to download Azure DevOps Office Integration 2019 from here and execute&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://visualstudio.microsoft.com/downloads/#azure-devops-office-integration-2019" target="_blank" rel="noopener"
 &gt;Download Visual Studio Tools – Install Free for Windows, Mac, Linux (microsoft.com)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Screen grab from the link showing the item to download, Azure DevOps Office Integration 2019" class="gallery-image" data-flex-basis="1569px" data-flex-grow="653" height="104" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/editing-azure-devops-items-in-excel/2024-08-14_15-17-51.png" width="680"&gt;&lt;/p&gt;
&lt;p&gt;Once you have done this, in Excel you will have a Team ribbon.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Team ribbon in Excel" class="gallery-image" data-flex-basis="1316px" data-flex-grow="548" height="124" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/editing-azure-devops-items-in-excel/2024-08-19_10-50-09.png" width="680"&gt;&lt;/p&gt;
&lt;h2 id="create-a-tree-query"&gt;Create a Tree Query
&lt;/h2&gt;&lt;p&gt;If you want to edit many items including child and parent hierarchy, then the easiest way is to write a tree query in DevOps that loads the right tasks and then use that to pull the right tasks into Excel. In DevOps under Boards select Queries. Then select New Query. Change query type to Tree of work items and type of tree to Parent/Child. Select the right filters to pull the items you want and then save your query. Save and run the query to make sure you get a hierarchy of tasks. In my example I wanted all the items in the project.&lt;/p&gt;
&lt;p&gt;&lt;img alt="creating a tree query in DevOps" class="gallery-image" data-flex-basis="319px" data-flex-grow="133" height="511" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/editing-azure-devops-items-in-excel/2024-08-19_11-39-24.png" width="680"&gt;&lt;/p&gt;
&lt;h2 id="connect-to-a-devops-project"&gt;Connect to a DevOps Project
&lt;/h2&gt;&lt;p&gt;On the Team ribbon, click on New List. If this is the first time you’ve connected you need to add a server first.&lt;/p&gt;
&lt;h3 id="adding-a-server"&gt;Adding a Server
&lt;/h3&gt;&lt;p&gt;Click on the Servers button. Then in the next dialog, click on Add. Into the next dialog enter in the url to the organisation, for example &lt;a class="link" href="https://dev.azure.com/HatFullOfData/" target="_blank" rel="noopener"
 &gt;https://dev.azure.com/HatFullOfData/&lt;/a&gt;. Click OK to finish adding the server, you will be prompted to login. Click Close to return to the original dialog.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Screen shots of the dialogs to add a server and finally the Connect window showing the Team Projects on the server." class="gallery-image" data-flex-basis="394px" data-flex-grow="164" height="414" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/editing-azure-devops-items-in-excel/2024-08-19_10-53-07.png" width="680"&gt;&lt;/p&gt;
&lt;h3 id="connecting-to-a-project"&gt;Connecting to a project
&lt;/h3&gt;&lt;p&gt;You start from the connect to Azure DevOps dialog, which will open from adding a server or opened by clicking on New List on the Team ribbon on a new sheet. Select the server and select the right project and click connect. Select your tree query from the drop down and click OK The tasks from the project should load as a table into Excel.&lt;/p&gt;
&lt;p&gt;&lt;img alt="The two dialogs described in the text and the table of DevOps items in Excel" class="gallery-image" data-flex-basis="383px" data-flex-grow="159" height="426" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/editing-azure-devops-items-in-excel/2024-08-19_14-57-18.png" width="680"&gt;&lt;/p&gt;
&lt;p&gt;The table format needs some explaining. Title 1 is the name of the highest level tasks and Title 2 etc are children of the Title 1 task above it. You can add another level of hierarchy by clicking on Add Tree Level from the Team ribbon. You can also add other DevOps columns by clicking Choose Columns and using the middle buttons to add or remove columns in the Choose columns dialog.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Team ribbon with Choose Columns and Add Tree Level highlighted and the Choose columns dialog." class="gallery-image" data-flex-basis="274px" data-flex-grow="114" height="595" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/editing-azure-devops-items-in-excel/2024-08-19_15-15-34-1.png" width="680"&gt;&lt;/p&gt;
&lt;h2 id="adding-and-publishing-devops-items-in-excel"&gt;Adding and Publishing DevOps items in Excel
&lt;/h2&gt;&lt;p&gt;Items can be added to the table just typing in new rows. The ID column leave blank, the Title 1 – 4 fill in making sure you understand the hierarchy. Some fields such as Target Date are read only for some Work Item Types. When you want to see the results in DevOps click on Publish. Then open DevOps to see the work items.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Screen grab of the DevOps items in Excel and then the items in DevOps" class="gallery-image" data-flex-basis="374px" data-flex-grow="155" height="436" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/editing-azure-devops-items-in-excel/2024-08-19_15-50-22.png" width="680"&gt;&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;I needed to create a project plan in DevOps from a list of tasks I had in a text file ready for a demo. There were 80 tasks and I knew I could it them quicker in Excel than I could in DevOps because I am more familiar with Excel. I also wanted to note down how I then loaded those tasks into DevOps from Excel.&lt;/p&gt;
&lt;p&gt;Other related series include:&lt;/p&gt;
&lt;h2 id="devops-with-power-automate-posts"&gt;DevOps with Power Automate posts
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/connecting-power-automate-to-devops/" target="_blank" rel="noopener"
 &gt;Connecting Power Automate to Azure DevOps&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-update-fields-in-azure-devops/" target="_blank" rel="noopener"
 &gt;Updating Start and Due dates and other fields&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/using-devops-rest-api-in-power-automate/" target="_blank" rel="noopener"
 &gt;Using DevOps Rest API&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/running-a-wiql-devops-query-in-power-automate/" target="_blank" rel="noopener"
 &gt;Running a WIQL query&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/update-devops-without-notifications-with-power-automate/" target="_blank" rel="noopener"
 &gt;Updating items without Notifications&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/devops-updates-on-behalf-of-another-with-power-automate/" target="_blank" rel="noopener"
 &gt;Updating a task on behalf of another person&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>DevOps updates on behalf of another with Power Automate</title><link>https://hatfullofdata.blog/devops-updates-on-behalf-of-another/</link><pubDate>Mon, 19 Aug 2024 09:34:27 +0000</pubDate><guid>https://hatfullofdata.blog/devops-updates-on-behalf-of-another/</guid><description>&lt;img src="https://hatfullofdata.blog/devops-updates-on-behalf-of-another/cover.png" alt="Featured image of post DevOps updates on behalf of another with Power Automate" /&gt;&lt;p&gt;I had a need to use automation to do DevOps updates on behalf of another person so when the DevOps adoption metrics were done we the stats were slightly more accurate. This can only be done using REST API and will need permissions. This post is part of the DevOps and Power Automate series.&lt;/p&gt;
&lt;h2 id="devops-with-power-automate-posts"&gt;DevOps with Power Automate posts
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/connecting-power-automate-to-devops/" target="_blank" rel="noopener"
 &gt;Connecting Power Automate to Azure DevOps&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-update-fields-in-azure-devops/" target="_blank" rel="noopener"
 &gt;Updating Start and Due dates and other fields&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/using-devops-rest-api-in-power-automate/" target="_blank" rel="noopener"
 &gt;Using DevOps Rest API&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/running-a-wiql-devops-query-in-power-automate/" target="_blank" rel="noopener"
 &gt;Running a WIQL query&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/update-devops-without-notifications-with-power-automate/" target="_blank" rel="noopener"
 &gt;Updating items without Notifications&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/devops-updates-on-behalf-of-another-with-power-automate/" target="_blank" rel="noopener"
 &gt;Updating a task on behalf of another person&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="youtube-version"&gt;YouTube Version
&lt;/h2&gt;&lt;p&gt;It’s on the backlog!&lt;/p&gt;
&lt;h2 id="permission-required-to-makes-updates-on-behalf-of-another"&gt;Permission required to makes updates on behalf of another
&lt;/h2&gt;&lt;p&gt;The trick to being able update on behalf of another is to update the ChangedBy field to another persons email address. This is not normally allowed, it breaks the rules. So the permission we need is to bypass the rules and make illegal updates.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Setting up permissions in project settings to allow for updates on behalf of another" class="gallery-image" data-flex-basis="1095px" data-flex-grow="456" height="149" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/devops-updates-on-behalf-of-another/2024-08-19_09-31-25.png" width="680"&gt;&lt;/p&gt;
&lt;p&gt;Permissions are managed in Project Settings, you then click on permissions. Click on Users at the top and then click on the user being used in the flow to show their permissions. Under Boards check the permission for Bypass rules on work item updates. This needs to be set to Allow.&lt;/p&gt;
&lt;h2 id="make-updates-on-behalf-of-another"&gt;Make Updates on behalf of another
&lt;/h2&gt;&lt;p&gt;Now we have permission is ignore the rules we can test out a flow. In the REST API documentation for updating an item &lt;a class="link" href="https://learn.microsoft.com/en-us/rest/api/azure/devops/wit/work-items/update?wt.mc_id=DX-MVP-5003563" target="_blank" rel="noopener"
 &gt;https://learn.microsoft.com/en-us/rest/api/azure/devops/wit/work-items/update&lt;/a&gt; you will see there is a boolean parameter bypassRules. This can be added to the relative URI using &amp;amp; to separate the parameters.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Send an HTTP request to Azure DevOps action with the bypassRules flag added to the URI and in the Body JSON an extra section to update the ChangedBy field. See Code below." class="gallery-image" data-flex-basis="236px" data-flex-grow="98" height="689" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/devops-updates-on-behalf-of-another/2024-08-19_09-53-45.png" width="680"&gt;&lt;/p&gt;
&lt;p&gt;In the body section I have added an update to the ChangedBy field. This update updates the State to active and performs it on behalf of Dan. In the task history we can see the update and see I did it on behalf of Dan.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Work item history showing the line Dan Cooke (via Laura Graham-Brown) changed State from New to Active" class="gallery-image" data-flex-basis="352px" data-flex-grow="146" height="463" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/devops-updates-on-behalf-of-another/2024-08-19_10-02-21-1.png" width="680"&gt;&lt;/p&gt;
&lt;h2 id="word-of-caution"&gt;Word of caution
&lt;/h2&gt;&lt;p&gt;By passing the rules, it also means that the updates behind the scenes that happen are turned off. If we change the state to closed using the same technique the we get an error when we open the work item.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Send an HPPP request to Azure DevOps action with bypassRules=true and changing the state to Closed. Plus a screen shot of the updated task opened and showing an error of Field Closed Date cannot be empty." class="gallery-image" data-flex-basis="473px" data-flex-grow="197" height="345" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/devops-updates-on-behalf-of-another/2024-08-19_10-11-52.png" width="680"&gt;&lt;/p&gt;
&lt;p&gt;So make sure you have checked the updates you are doing and include all the fields you need to update, even the hidden ones.&lt;/p&gt;
&lt;p&gt;Second word of warning is no checks are done on the updates. So you can assign a task to an email address that doesn’t have access, or update to a state that is not valid. So treat this one with caution.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;Updates on behalf of another is a very powerful trick. But should be treated with extra caution and it has made me double check permissions on a ew projects so access to by passing the rules is limited.&lt;/p&gt;
&lt;h2 id="more-power-automate-posts"&gt;More Power Automate Posts
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/microsoft-flow-creating-adaptive-cards/" target="_blank" rel="noopener"
 &gt;Creating Adaptive Cards&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/refreshing-datasets-automatically-with-dataflow/" target="_blank" rel="noopener"
 &gt;Refreshing Datasets Automatically with Power BI Dataflows&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-child-flow/" target="_blank" rel="noopener"
 &gt;Power Automate Child Flow&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-get-data-from-a-power-bi-dataset/" target="_blank" rel="noopener"
 &gt;Get data from a Power BI dataset&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-button-in-a-power-bi-report/" target="_blank" rel="noopener"
 &gt;Power Automate Button in a Power BI Report&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-write-me-a-flow/" target="_blank" rel="noopener"
 &gt;Write Me a Flow&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/connecting-power-automate-to-devops/" target="_blank" rel="noopener"
 &gt;Power Automate and DevOps series&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-and-power-bi-rest-api/" target="_blank" rel="noopener"
 &gt;Power Automate and Power BI Rest API series&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-save-a-file-to-onelake-lakehouse/" target="_blank" rel="noopener"
 &gt;Save a File to OneLake Lakehouse&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/trigger-microsoft-fabric-data-pipeline/" target="_blank" rel="noopener"
 &gt;Trigger Microsoft Fabric Data Pipeline using Power Automate&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Update DevOps without Notifications with Power Automate</title><link>https://hatfullofdata.blog/update-devops-without-notifications/</link><pubDate>Fri, 16 Aug 2024 07:52:49 +0000</pubDate><guid>https://hatfullofdata.blog/update-devops-without-notifications/</guid><description>&lt;img src="https://hatfullofdata.blog/update-devops-without-notifications/cover.png" alt="Featured image of post Update DevOps without Notifications with Power Automate" /&gt;&lt;p&gt;When a work item is assigned to you, by default you are sent a notification. Notifications can also be sent regarding updates to your items. Now imagine creating over 10 thousand tasks automatically and assigning them to people. I recently did this on a projects and potentially each person was going to get 100s of emails. So I needed Power Automate to assign the tasks without notifications. This post is part of the Power Automate and DevOps series.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Email telling you a work item has been assigned to you" class="gallery-image" data-flex-basis="370px" data-flex-grow="154" height="440" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/update-devops-without-notifications/2024-08-14_13-49-44.png" width="680"&gt;&lt;/p&gt;
&lt;h2 id="devops-with-power-automate-posts"&gt;DevOps with Power Automate posts
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/connecting-power-automate-to-devops/" target="_blank" rel="noopener"
 &gt;Connecting Power Automate to Azure DevOps&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-update-fields-in-azure-devops/" target="_blank" rel="noopener"
 &gt;Updating Start and Due dates and other fields&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/using-devops-rest-api-in-power-automate/" target="_blank" rel="noopener"
 &gt;Using DevOps Rest API&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/running-a-wiql-devops-query-in-power-automate/" target="_blank" rel="noopener"
 &gt;Running a WIQL query&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/update-devops-without-notifications-with-power-automate/" target="_blank" rel="noopener"
 &gt;Updating items without Notifications&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/devops-updates-on-behalf-of-another-with-power-automate/" target="_blank" rel="noopener"
 &gt;Updating a task on behalf of another person&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="youtube-version"&gt;YouTube Version
&lt;/h2&gt;&lt;p&gt;They are on the backlog!&lt;/p&gt;
&lt;h2 id="permissions-for-updates-without-notifications"&gt;Permissions for updates without notifications
&lt;/h2&gt;&lt;p&gt;Updating DevOps without notifications is not something that everyone should be able to do. Or at least the admins will want to be able to control who can do it.&lt;/p&gt;
&lt;p&gt;&lt;img class="gallery-image" data-flex-basis="922px" data-flex-grow="384" height="177" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/update-devops-without-notifications/2024-08-14_14-06-11.png" width="680"&gt;&lt;/p&gt;
&lt;p&gt;In the Project settings, click on Permissions. Then click on Users to list your users. When you click on a person you get to see all their permissions. They can be Allow, Deny or Not Set for all the different actions. (inherited) means it is set by being a member of a group. If you hover your cursor over the (i) you will get a description telling you which membership the permission as inherited from. A Deny overrides an allow, so you might need to manually override the inherited permission.&lt;/p&gt;
&lt;p&gt;The permission we are interested in is Suppress notifications for work item updates.&lt;/p&gt;
&lt;h2 id="updating-without-notifications"&gt;Updating without Notifications
&lt;/h2&gt;&lt;p&gt;Now we have the permission we can test it out in an update. This can only be done on updates done by a REST API call. If you look on &lt;a class="link" href="https://learn.microsoft.com/en-us/rest/api/azure/devops/wit/work-items/update?" target="_blank" rel="noopener"
 &gt;https://learn.&lt;/a&gt;&lt;a class="link" href="https://learn.microsoft.com/en-us/rest/api/azure/devops/wit/work-items/update?wt.mc_id=DX-MVP-5003563" target="_blank" rel="noopener"
 &gt;microsoft.com/en-us/rest/api/azure/devops/wit/work-items/update&lt;/a&gt; there is a URI parameter suppressNotifications which is a boolean.&lt;/p&gt;
&lt;p&gt;URI parameters get added after the ? and are separated by &amp;amp;. So the code to update item 36 without notifications would be&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{project}/_apis/wit/workitems/36?suppressNotifications=true&lt;span class="err"&gt;&amp;amp;&lt;/span&gt;api-version=7.0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;img alt="A send HTTO request to DevOps action with the suppressNotifications parameter added." class="gallery-image" data-flex-basis="292px" data-flex-grow="122" height="557" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/update-devops-without-notifications/2024-08-14_14-40-32-1.png" width="680"&gt;&lt;/p&gt;
&lt;h2 id="item-history"&gt;Item History
&lt;/h2&gt;&lt;p&gt;Suppressing the notifications does prevent automated updates filling inboxes with emails that will just get deleted. It does not however supress the history of the item being updated. So who did the update and when is still recorded.&lt;/p&gt;
&lt;p&gt;&lt;img alt="History of a work item showing the updates" class="gallery-image" data-flex-basis="502px" data-flex-grow="209" height="325" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/update-devops-without-notifications/2024-08-14_14-45-14.png" width="680"&gt;&lt;/p&gt;
&lt;h2 id="more-power-automate-posts"&gt;More Power Automate Posts
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/microsoft-flow-creating-adaptive-cards/" target="_blank" rel="noopener"
 &gt;Creating Adaptive Cards&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/refreshing-datasets-automatically-with-dataflow/" target="_blank" rel="noopener"
 &gt;Refreshing Datasets Automatically with Power BI Dataflows&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-child-flow/" target="_blank" rel="noopener"
 &gt;Power Automate Child Flow&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-get-data-from-a-power-bi-dataset/" target="_blank" rel="noopener"
 &gt;Get data from a Power BI dataset&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-button-in-a-power-bi-report/" target="_blank" rel="noopener"
 &gt;Power Automate Button in a Power BI Report&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-write-me-a-flow/" target="_blank" rel="noopener"
 &gt;Write Me a Flow&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/connecting-power-automate-to-devops/" target="_blank" rel="noopener"
 &gt;Power Automate and DevOps series&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-and-power-bi-rest-api/" target="_blank" rel="noopener"
 &gt;Power Automate and Power BI Rest API series&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-save-a-file-to-onelake-lakehouse/" target="_blank" rel="noopener"
 &gt;Save a File to OneLake Lakehouse&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/trigger-microsoft-fabric-data-pipeline/" target="_blank" rel="noopener"
 &gt;Trigger Microsoft Fabric Data Pipeline using Power Automate&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Running a Wiql DevOps query in Power Automate</title><link>https://hatfullofdata.blog/running-a-wiql-devops-query-in-power-automate/</link><pubDate>Wed, 14 Aug 2024 11:55:17 +0000</pubDate><guid>https://hatfullofdata.blog/running-a-wiql-devops-query-in-power-automate/</guid><description>&lt;img src="https://hatfullofdata.blog/running-a-wiql-devops-query-in-power-automate/cover.png" alt="Featured image of post Running a Wiql DevOps query in Power Automate" /&gt;&lt;p&gt;Wiql stands for Work Item Query Language and is the language used to define the queries in Azure DevOps. Its has a very similar syntax to SQL. Power Automate can use a REST API send a query to DevOps to execute the query. This post post is part of the Power Automate and DevOps series.&lt;/p&gt;
&lt;h2 id="devops-with-power-automate-posts"&gt;DevOps with Power Automate posts
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/connecting-power-automate-to-devops/" target="_blank" rel="noopener"
 &gt;Connecting Power Automate to Azure DevOps&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-update-fields-in-azure-devops/" target="_blank" rel="noopener"
 &gt;Updating Start and Due dates and other fields&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/using-devops-rest-api-in-power-automate/" target="_blank" rel="noopener"
 &gt;Using DevOps Rest API&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/running-a-wiql-devops-query-in-power-automate/" target="_blank" rel="noopener"
 &gt;Running a WIQL query&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/update-devops-without-notifications-with-power-automate/" target="_blank" rel="noopener"
 &gt;Updating items without Notifications&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/devops-updates-on-behalf-of-another-with-power-automate/" target="_blank" rel="noopener"
 &gt;Updating a task on behalf of another person&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The full reference can be found at &lt;a class="link" href="https://learn.microsoft.com/en-us/azure/devops/boards/queries/wiql-syntax?view=azure-devops&amp;amp;wt.mc_id=DX-MVP-5003563" target="_blank" rel="noopener"
 &gt;https://learn.microsoft.com/en-us/azure/devops/boards/queries/wiql-syntax?view=azure-devops&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="writing-the-query"&gt;Writing the Query
&lt;/h2&gt;&lt;p&gt;You can write it by hand, and I’m sure some people do. I use the query tools in DevOps to build the query and then view the Wiql code. For this post the example we are going to is to assign all the items in a hierarchy to one person. For example all the items in the Design Shiny feature.&lt;/p&gt;
&lt;p&gt;&lt;img alt="List of items including a feature called Design Shiny that has 2 user stories and they have multiple tasks." class="gallery-image" data-flex-basis="372px" data-flex-grow="155" height="438" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/running-a-wiql-devops-query-in-power-automate/2024-08-13_17-54-33.png" width="680"&gt;&lt;/p&gt;
&lt;h3 id="wiql-editor"&gt;Wiql Editor
&lt;/h3&gt;&lt;p&gt;Before we can work with WIQL we need to install the extension Wiql Playground in the organisation. You will need to be an organisation admin to do this. Head to the marketplace for Visual Studio, found here&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://marketplace.visualstudio.com/azuredevops" target="_blank" rel="noopener"
 &gt;Extensions for Visual Studio family of products | Visual Studio Marketplace&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Search for wiql and click on the Wiql Editor. When the extension information page opens click on Get it free. Then on the next page, select the right organisation and click Download. It will now appear under boards in your project.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Screen grab of the market place showing the results of searching for wiql and then the header of the Wiql editor page showing the Get it free button. Then the select an organisation page with a download button and finally the Boards side menu showing Wiql Playground listed" class="gallery-image" data-flex-basis="377px" data-flex-grow="157" height="432" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/running-a-wiql-devops-query-in-power-automate/2024-08-13_18-12-57.png" width="680"&gt;&lt;/p&gt;
&lt;h3 id="construct-query"&gt;Construct Query
&lt;/h3&gt;&lt;p&gt;Now in Queries construct the query. I am not here to teach you building queries in DevOps, there is a great post to get you started here &lt;a class="link" href="https://learn.microsoft.com/en-us/azure/devops/boards/queries/using-queries?wt.mc_id=DX-MVP-5003563" target="_blank" rel="noopener"
 &gt;https://learn.microsoft.com/en-us/azure/devops/boards/queries/using-queries&lt;/a&gt;. My query is a tree of work items, matching the top item first and only has 2 criteria of the top item is ID 17 and the child item is not closed or completed.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Showing how to get to the wiql of a query in DevOps" class="gallery-image" data-flex-basis="353px" data-flex-grow="147" height="462" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/running-a-wiql-devops-query-in-power-automate/2024-08-14_08-57-50.png" width="680"&gt;&lt;/p&gt;
&lt;p&gt;Once you have your query working and you have saved it you can view the Wiql. Click on the three dots in the top right. Then from the menu select Edit query wiql. This will open a pane containing the code that you can copy to your clipboard.&lt;/p&gt;
&lt;p&gt;Note you have to save after any changes for the changes to be in the Wiql code.&lt;/p&gt;
&lt;h2 id="running-the-wiql-in-power-automate"&gt;Running the Wiql in Power Automate
&lt;/h2&gt;&lt;p&gt;Now we have the Wiql code we can write the flow. The flow will have the parameters of the Parent Work Item ID and the email address to assign all the tasks to. We will then use a compose action to the Parent Work Item ID and Project name into the Wiql.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Screen shot of the flow with a manual trigger with a Parent Work Item ID as a parameter. Then a compose action with the Wiql code with the Project name environment variable and the Parent Work Item ID in the code" class="gallery-image" data-flex-basis="159px" data-flex-grow="66" height="1021" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/running-a-wiql-devops-query-in-power-automate/2024-08-14_09-20-21.png" width="680"&gt;&lt;/p&gt;
&lt;p&gt;The next step is to run the query. The documentation for the REST API call can be found here &lt;a class="link" href="https://learn.microsoft.com/en-us/rest/api/azure/devops/wit/wiql/query-by-wiql?wt.mc_id=DX-MVP-5003563" target="_blank" rel="noopener"
 &gt;https://learn.microsoft.com/en-us/rest/api/azure/devops/wit/wiql/query-by-wiql&lt;/a&gt; The method is Post and the URI will be the same for all Wiql&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;_apis/wit/wiql?api-version=7.1-preview.2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;The body is JSON with only one field query with the output from the compose action.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Send an HTTP request to Azure DevOps action with a Method of POST, the URI as above and the Body containing { “query” : “Compose Output” }" class="gallery-image" data-flex-basis="415px" data-flex-grow="173" height="393" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/running-a-wiql-devops-query-in-power-automate/2024-08-14_09-42-46.png" width="680"&gt;&lt;/p&gt;
&lt;p&gt;When we run the flow and enter in the Parent ID 17 and the email &lt;a class="link" href="mailto:Dan@lgb123.onmicrosoft.com" &gt;Dan@lgb123.onmicrosoft.com&lt;/a&gt; it executes and returns us the data.&lt;/p&gt;
&lt;p&gt;&lt;img alt="The Run flow dialog with the 2 parameters populated and then the HTTP action successfully run showing the JSON Output of the wiql query" class="gallery-image" data-flex-basis="273px" data-flex-grow="113" height="597" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/running-a-wiql-devops-query-in-power-automate/2024-08-14_09-46-52.png" width="680"&gt;&lt;/p&gt;
&lt;h2 id="handling-the-output-of-the-http-call"&gt;Handling the Output of the HTTP call
&lt;/h2&gt;&lt;p&gt;In the successful action we can click on Show raw output in the Outputs section to open a pane and explore the Json output. Scroll past the headers and then in the body section, past columns to find workItemRelations. This will list all the work items returned by the query, not their details, just the id and url in the target section.&lt;/p&gt;
&lt;p&gt;&lt;img alt="The output code with workItemRelations and Target with Id highlighted" class="gallery-image" data-flex-basis="164px" data-flex-grow="68" height="990" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/running-a-wiql-devops-query-in-power-automate/2024-08-14_11-53-29.png" width="680"&gt;&lt;/p&gt;
&lt;p&gt;To make this easy to work with we need to use a Parse JSON so copy all the output JSON and return to editing the flow. Add a Parse JSON step and add the Body returned by the HTTP to the Content. Next we need to add a schema, this can be generated. Click on Generate from sample. Then paste in the JSON you copied and click Done. And a schema will be added.&lt;/p&gt;
&lt;p&gt;&lt;img alt="A parse json action showing the content and how to generate the schema" class="gallery-image" data-flex-basis="213px" data-flex-grow="88" height="766" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/running-a-wiql-devops-query-in-power-automate/2024-08-14_11-45-40.png" width="680"&gt;&lt;/p&gt;
&lt;p&gt;The output schema is now known to the flow. So we can use the output to loop through all the items and update the work items.&lt;/p&gt;
&lt;h2 id="looping-through-workitemrelations"&gt;Looping through workItemRelations
&lt;/h2&gt;&lt;p&gt;We can loop through the workItemRelations using an Apply to each action. The piece of information I want from each item is the target – id. So I add a compose and add the following expression&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;item()?[&amp;#39;target&amp;#39;]?[&amp;#39;id&amp;#39;]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;I would then test to make sure you get the number of items you expect and that the compose does return the ids you expect&lt;/p&gt;
&lt;p&gt;&lt;img class="gallery-image" data-flex-basis="593px" data-flex-grow="247" height="275" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/running-a-wiql-devops-query-in-power-automate/2024-08-14_12-32-25.png" width="680"&gt;&lt;/p&gt;
&lt;p&gt;Finally we can add a step to assign each task to the email given in the parameter. I use a Send HTTP method rather than the Update a Work Item so I can as explained in a future post turn off notifications.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Added a send HTTP request to Azure DevOps to assign the task id from the compose step to the email " class="gallery-image" data-flex-basis="187px" data-flex-grow="78" height="869" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/running-a-wiql-devops-query-in-power-automate/image.png" width="680"&gt;&lt;/p&gt;
&lt;p&gt;We can then run the flow and check the results in DevOps and see the tasks have been assigned.&lt;/p&gt;
&lt;p&gt;&lt;img class="gallery-image" data-flex-basis="540px" data-flex-grow="225" height="302" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/running-a-wiql-devops-query-in-power-automate/2024-08-14_12-45-23.png" width="680"&gt;&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;this was one example of using Wiql inside Power Automate. I have used it to duplicate hierarchies and delete a hierarchy. The skill comes in being able to write the Wiql to achieve what you need.&lt;/p&gt;
&lt;h2 id="more-power-automate-posts"&gt;More Power Automate Posts
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/microsoft-flow-creating-adaptive-cards/" target="_blank" rel="noopener"
 &gt;Creating Adaptive Cards&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/refreshing-datasets-automatically-with-dataflow/" target="_blank" rel="noopener"
 &gt;Refreshing Datasets Automatically with Power BI Dataflows&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-child-flow/" target="_blank" rel="noopener"
 &gt;Power Automate Child Flow&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-get-data-from-a-power-bi-dataset/" target="_blank" rel="noopener"
 &gt;Get data from a Power BI dataset&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-button-in-a-power-bi-report/" target="_blank" rel="noopener"
 &gt;Power Automate Button in a Power BI Report&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-write-me-a-flow/" target="_blank" rel="noopener"
 &gt;Write Me a Flow&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/connecting-power-automate-to-devops/" target="_blank" rel="noopener"
 &gt;Power Automate and DevOps series&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-and-power-bi-rest-api/" target="_blank" rel="noopener"
 &gt;Power Automate and Power BI Rest API series&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-save-a-file-to-onelake-lakehouse/" target="_blank" rel="noopener"
 &gt;Save a File to OneLake Lakehouse&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/trigger-microsoft-fabric-data-pipeline/" target="_blank" rel="noopener"
 &gt;Trigger Microsoft Fabric Data Pipeline using Power Automate&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Using DevOps REST API in Power Automate</title><link>https://hatfullofdata.blog/using-devops-rest-api-in-power-automate/</link><pubDate>Tue, 13 Aug 2024 16:37:38 +0000</pubDate><guid>https://hatfullofdata.blog/using-devops-rest-api-in-power-automate/</guid><description>&lt;img src="https://hatfullofdata.blog/using-devops-rest-api-in-power-automate/cover.png" alt="Featured image of post Using DevOps REST API in Power Automate" /&gt;&lt;p&gt;DevOps REST API is a well documented API that we can easily use in Power Automate to perform a huge range of actions. The connector includes an action to make it easy. The API documentation can be found at&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://learn.microsoft.com/en-us/rest/api/azure/devops?wt.mc_id=DX-MVP-5003563" target="_blank" rel="noopener"
 &gt;https://learn.microsoft.com/en-us/rest/api/azure/devops&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In this post we will take some simple examples to get you started. This is the third post in the DevOps and Power Automate series.&lt;/p&gt;
&lt;h2 id="devops-with-power-automate-posts"&gt;DevOps with Power Automate posts
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/connecting-power-automate-to-devops/" target="_blank" rel="noopener"
 &gt;Connecting Power Automate to Azure DevOps&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-update-fields-in-azure-devops/" target="_blank" rel="noopener"
 &gt;Updating Start and Due dates and other fields&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/using-devops-rest-api-in-power-automate/" target="_blank" rel="noopener"
 &gt;Using DevOps Rest API&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/running-a-wiql-devops-query-in-power-automate/" target="_blank" rel="noopener"
 &gt;Running a WIQL query&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/update-devops-without-notifications-with-power-automate/" target="_blank" rel="noopener"
 &gt;Updating items without Notifications&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/devops-updates-on-behalf-of-another-with-power-automate/" target="_blank" rel="noopener"
 &gt;Updating a task on behalf of another person&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="youtube-version"&gt;YouTube Version
&lt;/h2&gt;&lt;p&gt;Its coming honest!&lt;/p&gt;
&lt;h2 id="get-task-details-using-devops-rest-api"&gt;Get Task Details using DevOps REST API
&lt;/h2&gt;&lt;p&gt;Power Automate includes an action to get the details of a Work Item. When we look we can see it has 4 required fields. Organization name, Project Name and Work Item Id are easy. We will have them. I had problems with Work Item Type, I didn’t always have that. So I looked to DevOps REST API for an alternative.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Get Work item details action showing the 4 required parameters with Work Item Type highlighted" class="gallery-image" data-flex-basis="632px" data-flex-grow="263" height="258" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/using-devops-rest-api-in-power-automate/2024-08-11_18-12-59.png" width="680"&gt;&lt;/p&gt;
&lt;p&gt;If we look in the documentation and then search for Get Work Item, you will find the syntax for fetching the details of one work item. The URL needs three pieces of information, shown in the {} brackets, organization, project and id. So no Work item type is required.&lt;/p&gt;
&lt;p&gt;&lt;img alt="screenshot of the HTTP statement in the API documentation. Code is below" class="gallery-image" data-flex-basis="1371px" data-flex-grow="571" height="119" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/using-devops-rest-api-in-power-automate/2024-08-11_18-17-44.png" width="680"&gt;&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://learn.microsoft.com/en-us/rest/api/azure/devops/wit/work-items/get-work-item?wt.mc_id=DX-MVP-5003563" target="_blank" rel="noopener"
 &gt;https://learn.microsoft.com/en-us/rest/api/azure/devops/wit/work-items/get-work-item&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We start with an instant flow. Then we add the a parameter to the trigger of the work item id. Then we add the action Send an HTTP request to Azure DevOps. The Organisation we can use our environment variable (or from the drop down). The method is GET. The Relative URI is everything after the organisation in the url we got from the documentation.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{project}/_apis/wit/workitems/{id}?api-version=7.2-preview.3
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;img alt="Manual trigger in a flow with a Work Item ID as a parameter followed by an action of Send an HTTP request to Azure DevOps" class="gallery-image" data-flex-basis="275px" data-flex-grow="114" height="593" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/using-devops-rest-api-in-power-automate/2024-08-11_18-29-38.png" width="680"&gt;&lt;/p&gt;
&lt;p&gt;When we run the flow, we get prompted to enter the number of a work item id. When it completes, we get returned the JSON of that work item details. If you need to extract information from that JSON, you will probably need a Parse JSON action. (Another post!)&lt;/p&gt;
&lt;p&gt;&lt;img alt="The Rn flow requesting Work Item ID and the result action showing the body contains a JSON statement" class="gallery-image" data-flex-basis="346px" data-flex-grow="144" height="471" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/using-devops-rest-api-in-power-automate/2024-08-11_18-29-12.png" width="680"&gt;&lt;/p&gt;
&lt;h2 id="update-a-work-item-using-devops-rest-api"&gt;Update a Work Item using DevOps REST API
&lt;/h2&gt;&lt;p&gt;We can update an item using the REST API. For this example the flow will update a given task to have the state Active and be assigned to the given email. So, we add Work Item ID and Email as two parameters to the trigger.&lt;/p&gt;
&lt;p&gt;&lt;img alt="A flow with a Manual trigger showing 2 parameters Work Item ID and Email. An action of Send a HTTP request to Azure DevOps using a path method and updating the State and AssignedTo fields." class="gallery-image" data-flex-basis="164px" data-flex-grow="68" height="992" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/using-devops-rest-api-in-power-automate/2024-08-12_16-34-04.png" width="680"&gt;&lt;/p&gt;
&lt;p&gt;Then we add a Send an HTTP request to Azure DevOps action. The Organisation is the same as before, the method is now Patch as we are updating data. The Relative URI is exactly the same as before. In the headers we need to add Content-Type and application/json-patch+json&lt;/p&gt;
&lt;p&gt;In the Body we need to add a JSON object that includes the fields we want to update. For this we need to know the field names, see the previous post in the series for ways to find them. It is just text so we can insert dynamic content, e.g. the email address. The above example picture and code below shows changing the State to Active and assigning it to the Email address&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;op&amp;#34;: &amp;#34;add&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;path&amp;#34;: &amp;#34;/fields/System.State&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;value&amp;#34;: &amp;#34;Active&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;op&amp;#34;: &amp;#34;add&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;path&amp;#34;: &amp;#34;/fields/System.AssignedTo&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;value&amp;#34;: &amp;#34;@{triggerBody()[&amp;#39;text_1&amp;#39;]}&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="conclusion"&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;Rest API is a very powerful way to work with Azure DevOps and the action in Power Automate does make it very easy to use. It is worth having a test project to try out some of the options. Other posts will look at using Rest API to make changes without notifications and on behalf of another person. Also a bulk delete process, which needs a post of its own due to a few oddities.&lt;/p&gt;
&lt;h2 id="more-power-automate-posts"&gt;More Power Automate Posts
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/microsoft-flow-creating-adaptive-cards/" target="_blank" rel="noopener"
 &gt;Creating Adaptive Cards&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/refreshing-datasets-automatically-with-dataflow/" target="_blank" rel="noopener"
 &gt;Refreshing Datasets Automatically with Power BI Dataflows&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-child-flow/" target="_blank" rel="noopener"
 &gt;Power Automate Child Flow&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-get-data-from-a-power-bi-dataset/" target="_blank" rel="noopener"
 &gt;Get data from a Power BI dataset&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-button-in-a-power-bi-report/" target="_blank" rel="noopener"
 &gt;Power Automate Button in a Power BI Report&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-write-me-a-flow/" target="_blank" rel="noopener"
 &gt;Write Me a Flow&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/connecting-power-automate-to-devops/" target="_blank" rel="noopener"
 &gt;Power Automate and DevOps series&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-and-power-bi-rest-api/" target="_blank" rel="noopener"
 &gt;Power Automate and Power BI Rest API series&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-save-a-file-to-onelake-lakehouse/" target="_blank" rel="noopener"
 &gt;Save a File to OneLake Lakehouse&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/trigger-microsoft-fabric-data-pipeline/" target="_blank" rel="noopener"
 &gt;Trigger Microsoft Fabric Data Pipeline using Power Automate&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Power Automate Update Fields in Azure DevOps</title><link>https://hatfullofdata.blog/power-automate-update-fields-in-azure-devops/</link><pubDate>Mon, 29 Jul 2024 08:09:46 +0000</pubDate><guid>https://hatfullofdata.blog/power-automate-update-fields-in-azure-devops/</guid><description>&lt;img src="https://hatfullofdata.blog/power-automate-update-fields-in-azure-devops/cover.png" alt="Featured image of post Power Automate Update Fields in Azure DevOps" /&gt;&lt;p&gt;In a previous post we created items in DevOps. Although the Power Automate create item action had lots of fields, it did not have all the fields. This post shows you how to find the names for those fields and update fields in Azure DevOps using Power Automate.&lt;/p&gt;
&lt;h2 id="devops-with-power-automate-posts"&gt;DevOps with Power Automate posts
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/connecting-power-automate-to-devops/" target="_blank" rel="noopener"
 &gt;Connecting Power Automate to Azure DevOps&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-update-fields-in-azure-devops/" target="_blank" rel="noopener"
 &gt;Updating Start and Due dates and other fields&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/using-devops-rest-api-in-power-automate/" target="_blank" rel="noopener"
 &gt;Using DevOps Rest API&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/running-a-wiql-devops-query-in-power-automate/" target="_blank" rel="noopener"
 &gt;Running a WIQL query&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/update-devops-without-notifications-with-power-automate/" target="_blank" rel="noopener"
 &gt;Updating items without Notifications&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/devops-updates-on-behalf-of-another-with-power-automate/" target="_blank" rel="noopener"
 &gt;Updating a task on behalf of another person&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="youtube-version"&gt;YouTube Version
&lt;/h2&gt;&lt;p&gt;Its coming 🙂&lt;/p&gt;
&lt;h2 id="the-problem"&gt;The Problem
&lt;/h2&gt;&lt;p&gt;If you compare the fields available in a standard Epic item in DevOps with the fields available in Power Automate create work item action you will notice some fields are missing. The obvious missing ones being Start Date and Target Date and other fields like Business Value etc and any custom fields you’ve added will not be there so we need to update fields in Azure DevOps.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Screen grabs of an Epic work item in DevOps and the DevOps Create a work item Power Automate action" class="gallery-image" data-flex-basis="480px" data-flex-grow="200" height="340" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/power-automate-update-fields-in-azure-devops/2024-07-26_11-14-53-1.png" width="680"&gt;&lt;/p&gt;
&lt;p&gt;The observant ones amongst you will have noticed the Other Fields option in the Power Automate action. It wants a key for your field which is the name of the field. And guess what that name is not going to be StartDate. So our first job is finding that name.&lt;/p&gt;
&lt;h2 id="finding-field-names-v1"&gt;Finding Field Names v1
&lt;/h2&gt;&lt;p&gt;This is the first method and I think the simplest method. Start by creating an item in DevOps that has values in the fields you would like the names of. I’ve populated Effort, Business Value, Time Criticality, Start Date and Target Date.&lt;/p&gt;
&lt;p&gt;&lt;img alt="DevOps Epic work item with fields populated" class="gallery-image" data-flex-basis="329px" data-flex-grow="137" height="496" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/power-automate-update-fields-in-azure-devops/2024-07-26_11-35-06.png" width="680"&gt;&lt;/p&gt;
&lt;p&gt;Then in a Power Automate instant flow I add an action to get the work item details of that item, notice it is number 21. In the Get work item details I populate Org&lt;a class="link" href="#a040a142-03b1-4e20-a10b-8fe2427f8d11" &gt;1&lt;/a&gt;, Project, Type=”Epic” and Id=21. Then I run that flow and look at the outputs from that step.&lt;/p&gt;
&lt;p&gt;&lt;img class="gallery-image" data-flex-basis="359px" data-flex-grow="149" height="454" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/power-automate-update-fields-in-azure-devops/2024-07-26_11-44-44.png" width="680"&gt;&lt;/p&gt;
&lt;p&gt;If you scroll through the fields you will eventually find Business Value etc with their full field names. You need to replace the _ with . as I assume JSON objects to dots in field names. Note down the ones you need.&lt;/p&gt;
&lt;h2 id="create-work-item-and-update-fields-in-azure-devops"&gt;Create Work Item and Update Fields in Azure DevOps
&lt;/h2&gt;&lt;p&gt;Now we have the field names we can create a work item and populate those fields. Note the date formats are YYYY-MM-DDThh:mm:ssZ.&lt;/p&gt;
&lt;p&gt;&lt;img class="gallery-image" data-flex-basis="203px" data-flex-grow="84" height="801" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/power-automate-update-fields-in-azure-devops/2024-07-26_13-11-22.png" width="680"&gt;&lt;/p&gt;
&lt;p&gt;When this flow is run, it creates an item and will successfully update fields in Azure DevOps.&lt;/p&gt;
&lt;p&gt;&lt;img class="gallery-image" data-flex-basis="334px" data-flex-grow="139" height="488" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/power-automate-update-fields-in-azure-devops/2024-07-26_13-22-58.png" width="680"&gt;&lt;/p&gt;
&lt;p&gt;The Update a work item action has exactly the same Other fields option so you can use the same technique.&lt;/p&gt;
&lt;h2 id="find-field-names-v2"&gt;Find Field Names v2
&lt;/h2&gt;&lt;p&gt;Azure DevOps can be accessed via a Rest API&lt;a class="link" href="#6ef2c091-087e-46c5-8e21-2bba1d5c5562" &gt;2&lt;/a&gt; and this includes the list of fields available in a project. There is quite good documentation for this which can be found at &lt;a class="link" href="https://learn.microsoft.com/en-us/rest/api/azure/devops/wit/fields/list" target="_blank" rel="noopener"
 &gt;https://learn.microsoft.com/en-us/rest/api/azure/devops/wit/fields/list&lt;/a&gt; . From the first code block on that post remove the GET and populate your org and project names. Then in a browser navigate to that url and it will return lots of JSON showing the definition of all the fields.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;https://dev.azure.com/{organization}/{project}/_apis/wit/fields
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;img class="gallery-image" data-flex-basis="336px" data-flex-grow="140" height="485" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/power-automate-update-fields-in-azure-devops/2024-07-26_13-35-35.png" width="680"&gt;&lt;/p&gt;
&lt;p&gt;Then search for the fields you want and look for the referenceName and use that. As my project got complex I saved this data and then used Power Query to make a simple list of fields (perhaps a resource I should make public).&lt;/p&gt;
&lt;h2 id="conclusion-on-update-fields-in-azure-devops"&gt;Conclusion on Update Fields in Azure DevOps
&lt;/h2&gt;&lt;p&gt;The Other Fields section in Create and Update work item actions gives us the option to update items however we want to. Some fields of course are read only and updating some fields will update other fields, for example if you change the state to Closed it will then populate the Closed date field.&lt;/p&gt;
&lt;h2 id="more-power-automate-posts"&gt;More Power Automate Posts
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/microsoft-flow-creating-adaptive-cards/" target="_blank" rel="noopener"
 &gt;Creating Adaptive Cards&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/refreshing-datasets-automatically-with-dataflow/" target="_blank" rel="noopener"
 &gt;Refreshing Datasets Automatically with Power BI Dataflows&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-child-flow/" target="_blank" rel="noopener"
 &gt;Power Automate Child Flow&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-get-data-from-a-power-bi-dataset/" target="_blank" rel="noopener"
 &gt;Get data from a Power BI dataset&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-button-in-a-power-bi-report/" target="_blank" rel="noopener"
 &gt;Power Automate Button in a Power BI Report&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-write-me-a-flow/" target="_blank" rel="noopener"
 &gt;Write Me a Flow&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/connecting-power-automate-to-devops/" target="_blank" rel="noopener"
 &gt;Power Automate and DevOps series&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-and-power-bi-rest-api/" target="_blank" rel="noopener"
 &gt;Power Automate and Power BI Rest API series&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-save-a-file-to-onelake-lakehouse/" target="_blank" rel="noopener"
 &gt;Save a File to OneLake Lakehouse&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/trigger-microsoft-fabric-data-pipeline/" target="_blank" rel="noopener"
 &gt;Trigger Microsoft Fabric Data Pipeline using Power Automate&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="footnotes"&gt;Footnotes
&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;I’ve used environment variables to populate these as my flow is in a solution and I want to be able to move the solution to other environments or tenancies! &lt;a class="link" href="#a040a142-03b1-4e20-a10b-8fe2427f8d11-link" &gt;↩︎&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Another blog post is coming to cover using Azure DevOps Rest api in Power Automate, I promise! &lt;a class="link" href="#6ef2c091-087e-46c5-8e21-2bba1d5c5562-link" &gt;↩︎&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Connecting Power Automate to DevOps</title><link>https://hatfullofdata.blog/connecting-power-automate-to-devops/</link><pubDate>Thu, 25 Jul 2024 10:54:26 +0000</pubDate><guid>https://hatfullofdata.blog/connecting-power-automate-to-devops/</guid><description>&lt;img src="https://hatfullofdata.blog/connecting-power-automate-to-devops/cover.png" alt="Featured image of post Connecting Power Automate to DevOps" /&gt;&lt;p&gt;I know Azure DevOps from being on projects. Mostly for tracking tasks, sprints etc. I recently had to automate some item creation and editing across 1000s of tasks in DevOps. So I had to explore connecting Power Automate to DevOps for item creation and item editing. This is the first post I have in a long list of posts regarding DevOps with Power Automate and Power BI, welcome to my way of remembering how I did something.&lt;/p&gt;
&lt;h2 id="devops-with-power-automate-posts"&gt;DevOps with Power Automate posts
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/connecting-power-automate-to-devops/" target="_blank" rel="noopener"
 &gt;Connecting Power Automate to Azure DevOps&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-update-fields-in-azure-devops/" target="_blank" rel="noopener"
 &gt;Updating Start and Due dates and other fields&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/using-devops-rest-api-in-power-automate/" target="_blank" rel="noopener"
 &gt;Using DevOps Rest API&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/running-a-wiql-devops-query-in-power-automate/" target="_blank" rel="noopener"
 &gt;Running a WIQL query&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/update-devops-without-notifications-with-power-automate/" target="_blank" rel="noopener"
 &gt;Updating items without Notifications&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/devops-updates-on-behalf-of-another-with-power-automate/" target="_blank" rel="noopener"
 &gt;Updating a task on behalf of another person&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="youtube-version"&gt;YouTube Version
&lt;/h2&gt;&lt;p&gt;&lt;a class="link" href="https://youtu.be/snZdRzfuNIo" target="_blank" rel="noopener"
 &gt;&lt;img class="gallery-image" data-flex-basis="426px" data-flex-grow="177" height="383" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/connecting-power-automate-to-devops/Power-Automate-Create-DevOps-Items-Time-0_00_0000.png" width="680"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="devops-connector"&gt;DevOps Connector
&lt;/h2&gt;&lt;p&gt;While editing a flow if you add action and search for DevOps you will find the Azure DevOps connector. If you look at the actions in the classic format&lt;a class="link" href="#65a785ed-7cea-41c5-b383-422365835ab1" &gt;1&lt;/a&gt; you will notice its a premium connector, i.e. you need a premium license. If you have not used this connector before it will prompt you to login. It assumes your DevOps login matches your Power Automate login.&lt;/p&gt;
&lt;p&gt;&lt;img alt="List of available actions for the DevOps connector which have the Premium label on and the dialog prompting you to Sign in if this is your first time using this connector." class="gallery-image" data-flex-basis="444px" data-flex-grow="185" height="367" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/connecting-power-automate-to-devops/2024-07-23_10-48-39.png" width="680"&gt;&lt;/p&gt;
&lt;h2 id="create-an-item"&gt;Create an item
&lt;/h2&gt;&lt;p&gt;Let’s start with a simple action to create a work item in a project. We start by adding the action Create a work item.&lt;/p&gt;
&lt;p&gt;The Organisation Name and the Project Name drop downs will contain the organisations and projects your login can see. (See further down about making this solution friendly). The Work Item Type drop down will contain all the types that the project allows from its process. The title is plain text and the Description is rich text, i.e. html.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Create a work item action dialog" class="gallery-image" data-flex-basis="438px" data-flex-grow="182" height="372" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/connecting-power-automate-to-devops/2024-07-23_10-55-59.png" width="680"&gt;&lt;/p&gt;
&lt;p&gt;When you test the flow you should get a work item created that matches the above.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Flow with green ticks showing it completed and a screen grab of the Epic work item created by the flow." class="gallery-image" data-flex-basis="632px" data-flex-grow="263" height="258" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/connecting-power-automate-to-devops/2024-07-23_17-35-09.png" width="680"&gt;&lt;/p&gt;
&lt;h2 id="troubleshoot-connecting-power-automate-to-devops"&gt;Troubleshoot Connecting Power Automate to DevOps
&lt;/h2&gt;&lt;p&gt;If the flow fails with an error regarding authorisation, you need to first check you are allowed to add an item in that project in DevOps. Then you need to check the organisation settings in DevOps. Look in Policies and make sure Third-party application access via OAuth is turned on.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Flow run showing the failure and then a screen grab of Organisation settings of Policies with Third-party application access via OAuth highlighted to allow connecting Power Automate to DevOps" class="gallery-image" data-flex-basis="422px" data-flex-grow="176" height="386" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/connecting-power-automate-to-devops/2024-07-23_17-43-58.png" width="680"&gt;&lt;/p&gt;
&lt;h2 id="advanced-options-in-create-an-item"&gt;Advanced Options in Create an Item
&lt;/h2&gt;&lt;p&gt;The Create a work item action includes advanced options, really they are just optional options. Two of the options are Link URL and Link Type. In this example I used the URL from the previously created item and the Type of Hierarchy-reverse to make this item a child of the previous item.&lt;/p&gt;
&lt;p&gt;Another option is the Assigned To field. I added my email so that when I look in the created item I see that it has been assigned to me.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Showing the expanded create work item action with the Link URL and Link Type highlighted and the Assigned to highlighted. And screen grabs from the backlog showing the 2 related items and the Feature item with me assigned" class="gallery-image" data-flex-basis="303px" data-flex-grow="126" height="538" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/connecting-power-automate-to-devops/2024-07-23_18-03-39.png" width="680"&gt;&lt;/p&gt;
&lt;p&gt;The first option is Return all fields, which the default is blank. This means the action returns all the item details when it successfully creates the item.&lt;/p&gt;
&lt;h2 id="making-it-solution-aware"&gt;Making it Solution Aware
&lt;/h2&gt;&lt;p&gt;If this flow is part of a solution that is going to move from a development environment to test and production, you will need to point the DevOps actions to the different Organisation and Project values. Thankfully the text values of the Organisation Name, e.g. HatFullOfData can be used.&lt;/p&gt;
&lt;p&gt;So I create 2 environment variables DevOps Organisation and DevOps Project. Then in the create a work item action, in Organisation Name and Project Name drop downs I select Custom Value and then the right value from Dynamic content.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Screen grab of creating the environment variable, what it looks like in the dynamic content and finally using the environment variables for connecting Power Automate to DevOps" class="gallery-image" data-flex-basis="273px" data-flex-grow="113" height="597" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://hatfullofdata.blog/connecting-power-automate-to-devops/2024-07-24_12-24-14.png" width="680"&gt;&lt;/p&gt;
&lt;p&gt;And yes of course after making these changes I test the flow again.&lt;/p&gt;
&lt;h2 id="conclusion-on-connecting-power-automate-to-devops"&gt;Conclusion on Connecting Power Automate to DevOps
&lt;/h2&gt;&lt;p&gt;For when you need to automate the creation of items, this is great. It does not replace importing an Excel file to create a whole plan but it does give an extra set of options to consider.&lt;/p&gt;
&lt;h2 id="more-power-automate-posts"&gt;More Power Automate Posts
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/microsoft-flow-creating-adaptive-cards/" target="_blank" rel="noopener"
 &gt;Creating Adaptive Cards&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/refreshing-datasets-automatically-with-dataflow/" target="_blank" rel="noopener"
 &gt;Refreshing Datasets Automatically with Power BI Dataflows&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-child-flow/" target="_blank" rel="noopener"
 &gt;Power Automate Child Flow&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-get-data-from-a-power-bi-dataset/" target="_blank" rel="noopener"
 &gt;Get data from a Power BI dataset&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-button-in-a-power-bi-report/" target="_blank" rel="noopener"
 &gt;Power Automate Button in a Power BI Report&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-write-me-a-flow/" target="_blank" rel="noopener"
 &gt;Write Me a Flow&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/connecting-power-automate-to-devops/" target="_blank" rel="noopener"
 &gt;Power Automate and DevOps series&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-and-power-bi-rest-api/" target="_blank" rel="noopener"
 &gt;Power Automate and Power BI Rest API series&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/power-automate-save-a-file-to-onelake-lakehouse/" target="_blank" rel="noopener"
 &gt;Save a File to OneLake Lakehouse&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://hatfullofdata.blog/trigger-microsoft-fabric-data-pipeline/" target="_blank" rel="noopener"
 &gt;Trigger Microsoft Fabric Data Pipeline using Power Automate&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Foot Notes&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Yes in the new Power Automate experience, at the time of publishing this post, there are no premium labels on connectors or actions &lt;a class="link" href="#65a785ed-7cea-41c5-b383-422365835ab1-link" &gt;↩︎&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>