Sorry for resurecting an old post, but my client's manager asked me the exact same question. I was able to do it using PowerQuery (which can be viewed in either Excel or PowerBI)
I have parameters in my query but you can change it with your actual value directly in the string.
So I have a first query that gives me the data from Azure DevOps analytics wokitem snapshot. This gives the history of your workitems. I called that one "Sprint Burndown".
let
Source = OData.Feed( "https://analytics.dev.azure.com/"&DevOps_Company&"/"&DevOps_Project&"/_odata/v4.0-preview/WorkItemSnapshot?
$apply=filter(
(WorkItemType eq 'User Story'
or WorkItemType eq 'Bug'
or WorkItemType eq 'Action Plan'
or WorkItemType eq 'Issue')
and DateValue ge Iteration/StartDate
and DateValue le Iteration/EndDate
)
/groupby(
(DateValue,State,WorkItemType,Priority,Area/AreaPath,Area/AreaName,Iteration/IterationPath,Iteration/StartDate,Iteration/EndDate,StateCategory,WorkItemId,Title,AssignedTo/UserName),
aggregate($count as Count, StoryPoints with sum as TotalStoryPoints)
)
&$orderby=WorkItemId asc, DateValue asc"
, null, [Implementation="2.0"]),
#"Area expanded" = Table.ExpandRecordColumn(Source, "Area", {"AreaName", "AreaPath"}, {"Team", "AreaPath"}),
#"Iteration expanded" = Table.ExpandRecordColumn(#"Area expanded", "Iteration", {"IterationPath", "StartDate", "EndDate"}, {"IterationPath", "StartDate", "EndDate"}),
#"AssignedTo expanded" = Table.ExpandRecordColumn(#"Iteration expanded", "AssignedTo", {"UserName"}, {"AssignedTo.UserName"}),
#"Split IterationPath" = Table.SplitColumn(#"AssignedTo expanded", "IterationPath", Splitter.SplitTextByEachDelimiter({"\"}, QuoteStyle.Csv, true), {"Iteration Folder", "Sprint"}),
#"TeamSprint added" = Table.AddColumn(#"Split IterationPath", "TeamSprint", each [Team] & "\" & [Sprint]),
#"correct types" = Table.TransformColumnTypes(#"TeamSprint added",{{"Iteration Folder", type text}, {"Sprint", type text}, {"TotalStoryPoints", Int64.Type}, {"DateValue", type date}, {"TeamSprint", type text}, {"StartDate", type date}, {"EndDate", type date}}),
#"change category" = Table.ReplaceValue(#"correct types","Resolved","InProgress",Replacer.ReplaceText,{"StateCategory"})
in
#"change category"
Since that query may have many other purpose for you, I let it be and create a second query for the filtering. I will call that one "Sprint Changes"
let
/*Start from burndown data with sprint informations*/
Source = #"Sprint Burndown",
/*Keep only data from first day and last day of sprint (today if sprint is not yet finished)*/
#"Just Start and End" = Table.SelectRows(Source, each ([DateValue] = [StartDate] or [DateValue] = List.Min({[EndDate],DateTime.Date(DateTime.LocalNow())}))),
/*Removed unuseful columns*/
#"Clean Dataset" = Table.RemoveColumns(#"Just Start and End",{"State", "Priority", "StateCategory", "Count", "AreaPath", "Iteration Folder", "AssignedTo.UserName", "TotalStoryPoints"}),
/* Remove Backlog */
#"Filtered Rows" = Table.SelectRows(#"Clean Dataset", each ([Team] <> DevOps_Project)),
/*Find all duplicaes in the same sprint/team */
#"Grouped Rows" = Table.Group(#"Filtered Rows", {"TeamSprint", "WorkItemId"}, {{"FoundItems", each Table.RowCount(_), Int64.Type}}),
/* Keep only unique items */
#"Find unique" = Table.SelectRows(#"Grouped Rows", each ([FoundItems] = 1)),
/*Remove all duplicaes in the same sprint/team */
#"Keep Unique" = Table.NestedJoin(#"Filtered Rows", {"WorkItemId","TeamSprint"}, #"Find unique", {"WorkItemId","TeamSprint"}, "matches", JoinKind.Inner),
/*If there at start but not anymore = REMOVED, If not there at start but there now = ADDED*/
#"Labeled Event" = Table.AddColumn(#"Keep Unique", "Event", each if [DateValue] = [StartDate] then "Removed" else "Added"),
#"Removed merge artifacts" = Table.RemoveColumns(#"Labeled Event",{"matches"})
in
#"Removed merge artifacts"
You can then use the result of that last query to identify which items were added or removed from your original planning.