Update 24 Aug 2014:  Changed the PowerShell to use a pipe based filter as opposed to nested foreach loops

The TFS Scrum process template’s Product Backlog Item work item type has an acceptance criteria field. It is good practice to make sure any PBI has this field completed; however it is not always possible to enter this content when the work item is initially create i.e. before it is approved. We oftan find we add a PBI that is basically a title and add the summary and acceptance criteria as the product is planned.

It would be really nice to have a TFS work item query that listed all the PBIs that did not have the acceptance criteria field complete. Unfortunately there is not way to check a rich text or html field is empty in TFS queries It has been requested via UserVoice, but there is no sign of it appearing in the near future.

So we are left the TFS API to save the day, the following PowerShell function does the job, returning a list of non-completed PBI work items that have empty Acceptance Criteria.

\# Load the one we have to find, might be more than we truly need for this single function  
\# but I usually keep all these functions in a single module so share the references  
$ReferenceDllLocation = "C:Program Files (x86)Microsoft Visual Studio 12.0Common7IDEReferenceAssembliesv2.0"  
Add-Type -Path $ReferenceDllLocation"Microsoft.TeamFoundation.Client.dll" -ErrorAction Stop -Verbose  
Add-Type -Path $ReferenceDllLocation"Microsoft.TeamFoundation.Common.dll" -ErrorAction Stop -Verbose  
Add-Type -Path $ReferenceDllLocation"Microsoft.TeamFoundation.WorkItemTracking.Client.dll"  -ErrorAction Stop –Verbose


function Get-TfsPBIWIthNoAcceptanceCriteria {  <# .SYNOPSIS This function get the list of PBI work items that have no acceptance criteria  .DESCRIPTION This function allows a check to be made that all PBIs have a set of acceptance criteria  .PARAMETER CollectionUri TFS Collection URI  .PARAMETER TeamProject Team Project Name  .EXAMPLE  Get-TfsPBIWIthNoAcceptanceCriteria -CollectionUri "http://server1:8080/tfs/defaultcollection" -TeamProject "My Project"  #>      Param     (     \[Parameter(Mandatory=$true)\]     \[uri\] $CollectionUri ,       \[Parameter(Mandatory=$true)\]     \[string\] $TeamProject      )      # get the source TPC     $teamProjectCollection = New-Object Microsoft.TeamFoundation.Client.TfsTeamProjectCollection($CollectionUri)      try     {         $teamProjectCollection.EnsureAuthenticated()     }     catch     {         Write-Error "Error occurred trying to connect to project collection: $\_ "         exit 1     }          #Get the work item store     $wiService = $teamProjectCollection.GetService(\[Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore\])          # find each work item, we can't check for acceptance crieria state in the query     $pbi = $wiService.Query("SELECT \[System.Id\] FROM WorkItems WHERE \[System.TeamProject\] = '{0}'  AND  \[System.WorkItemType\] = 'Product Backlog Item'  AND  \[System.State\] <> 'Done' ORDER BY \[System.Id\]" -f $teamproject)     $pbi |  where-Object { $\_.fields | where-object {$\_.ReferenceName -eq 'Microsoft.VSTS.Common.AcceptanceCriteria' -and $\_.Value -eq ""}}       # Using a single piped line to filter the wi     # this is equivalent to the following nested loops for those who like a more winded structure      # $results = @()     # foreach ($wi in $pbi)     # {     #    foreach ($field in $wi.Fields)     #    {     #        if  ($field.ReferenceName -eq 'Microsoft.VSTS.Common.AcceptanceCriteria' -and $field.Value -eq "")      #        {     #            $results += $wi     #        }     #    }     # }     # $results }