Does anybody know how to create a work item query in TFS that will query users against a TFS group? (ie, AssignedTo = [project]\Contributors)
In visual studio 2008, there is an 'In Group' operator in the query editor. You can use it and specify any TFS group.
If that doesn't work, try this. This is a fairly convoluted way to get the query working, but will work, involves using the group security identifier (SID) to bound the query.
SELECT [System.Id], [System.Title]
FROM WorkItems
WHERE [System.TeamProject] = #project
AND [System.AssignedTo] IN GROUP
'S-1-9-1551374245-1204400969-2402986413-2179408616-1-3207752628-1878591311-2685660887-
2074056714'
ORDER BY [System.Id]
To find the SID of the specific group your interested in, run the tfssecurity.exe utility as Run as Administrator with the /i Contributors and server parameter //server:MyTFSServer. This will return something like the following.
Resolving identity "Contributors"...
SID: S-1-9-1551374245-1204400969-2402986413-2179408616-1-3207752628-1878591311-2685660887-
2074056714
DN:
Identity type: Team Foundation Server application group
Group type: Contributors
Project scope: Server scope
Display name: Contributors
Description: Members of this application group can perform all privileged operations on the server.
Its long winded, but once you know the SID, and build the WIQ query, and save it, that will be it.
Hope that helps.
Related
There are APIs to query using an existing wiq file.
I want my app to create actual wiq files in a shared folder - which can be accessed outside my app too. But I cannot find any API to do that.
What's that mean for wiql files here?
WIQL is Work Item Query Language, if you mean create a query with WIQL and save the query as a file, then no any API to do that, however you can try below steps:
Create a query with the Query Editor, save the query.
Open the query in Visual Studio --> Edit Query
File -> Save {Query}[Editor] as...
Then you can open and edit the saved *.wiq file with text editor, then save/copy to a shared folder...
Reference below articles:
Export a query
Quickly generate Work Item Query for use in the TFS API
Yes, there is an API, see Create a query in the REST API.
The POST body should contains the WIQL Query and the name for it.
{
"name": "All Bugs",
"wiql": "Select [System.Id], [System.Title], [System.State] From WorkItems Where [System.WorkItemType] = 'Bug' order by [Microsoft.VSTS.Common.Priority] asc, [System.CreatedDate] desc"
}
To invoke the REST API, I suggest you to use a Microsoft library instead of writing all the code yourself, e.g. https://learn.microsoft.com/en-us/vsts/integrate/concepts/dotnet-client-libraries.
My customer are using TFS2018 update2 version. and AD is used to manager TFS user.
there are about 1000 users right now. and about 10 to 20 users reported that their user account can't be found in TFS workitem "Assigned to" field. so strange and confused!
1) we did check the 'AssignedTo' field of the workitem setting, is is of default setting, which allows existing user and valid users.
2) the users are even added to the team project, however, they can't be found when using search in 'assigned to 'field.
3) tried other type of workitems, any projects. same issues.
Generally speaking, such users can be added to team project successfully.and they can't be found in 'assigned to' filed of all workitems in all projects. so we doubt it is some kind of user account sync issues?
and we did do more investigation and debugging. and we found a para named operationScopes,and its value is 'ims';and if we add value 'ad' to this array. the users can be found! and we noticed the returned value bdifference between such 'bad users' and 'good users' are of the 'Local directory'and 'localId' field. the value is null here, for good user, neither local directory nor LocalID is null.
hope this clue works.
http://TFS2018server:8080/tfs/TFSCollection/_apis/IdentityPicker/Identities
and add more clues, we updated the Web Services_static\tfs\Dev16.M122.5_script\TFS\debug\VSS\Identities\Picker\Services.js; by adding queryScopes.push("ims") in the OperationScope.IMS switch, then the users can be found. and I know, it is not a good way to update TFS code, it is just to do debugging. hope it is useful.
Based on the investigation and testing, seems it's the AD sync issue. That means the missed users may not be synced to TFS.
So when you query the missed user in AD you can find it (Add value 'ad' to operationScopes), but in TFS you can not find them.
TFS use a background synchronization job, scheduled every hour, to look for changes in Active Directory (or the local machine workgroup if the server is not domain joined). You can force the job to run using any of these techniques: How to synchronize TFS users with AD
It could be that you still do not see the user or name listed in the UI even if synchronization is working. The synchronization job does not automatically creates a user profile in the database for every user or group in the database, to avoid useless growth in big enterprises.
In such a case, the first time you use a new AD account (user or group), you must refer to it using DOMAIN\account syntax so that TFS look up in AD on the fly and insert a profile record in the database for the account.
Further Troubleshooting
Mr. Hinsh has a good troubleshooting guide if you still have troubles. It's still apply to TFS 2018 Update2
I have the same issue in Azure DevOps Server 2019 (Version Dev17.M153.3)
when I tried to add a user to a security group it shows the user but it cannot retrieve the scopeName such that if there is any other user in other domains with the same username it raises the following error
Multiple identities found matching 'theusername'. Use the unique name
to specify one of the following identities:
fullname1 (unique name: domain1\theusername)
fullname2 (unique name: domain2\theusername)
but the same user in other collection is working.
in C:\Program Files\Azure DevOps Server 2019\Application Tier\Web Services_static\tfs\Dev17.M153.3_scripts\TFS\debug\VSS\Identities\Picker\Services.js queryScopes.push("ims") was already there.
after some investigation i could reduce the problem to the following powershell code
$url1 = "https://tfsserver/CB/_apis/IdentityPicker/Identities?api-version=5.1-preview.1"
$Body = '{"query":"mydomainname\\myusername","identityTypes":["user","group"],"operationScopes":["ims","ad","wmd"],"properties":["DisplayName","IsMru","ScopeName","SamAccountName","Active","SubjectDescriptor","Department","JobTitle","Mail","MailNickname","PhysicalDeliveryOfficeName","SignInAddress","Surname","Guest","TelephoneNumber","Description"],"filterByAncestorEntityIds":[],"filterByEntityIds":[],"options":{"MinResults":40,"MaxResults":40,"ExtensionId":"F12CA7AD-00EE-424F-B6D7-9123A60F424F","ProjectScopeName":"ateamprojectname","CollectionScopeName":"badcollection","Constraints":[]}}'
$x= Invoke-Webrequest $url1 -Method POST -ContentType application/json -UseDefaultCredentials -Body $Body
$y = $x.Content | ConvertFrom-Json
Write-Host "badcollection->", $y.results.identities.scopeName
$url1 = "https://tfsserver/CB_TestCollection/_apis/IdentityPicker/Identities?api-version=5.1-preview.1"
$Body = '{"query":"mydomainname\\myusername","identityTypes":["user","group"],"operationScopes":["ims","ad","wmd"],"properties":["DisplayName","IsMru","ScopeName","SamAccountName","Active","SubjectDescriptor","Department","JobTitle","Mail","MailNickname","PhysicalDeliveryOfficeName","SignInAddress","Surname","Guest","TelephoneNumber","Description"],"filterByAncestorEntityIds":[],"filterByEntityIds":[],"options":{"MinResults":40,"MaxResults":40,"ExtensionId":"F12CA7AD-00EE-424F-B6D7-9123A60F424F","ProjectScopeName":"ateamprojectname","CollectionScopeName":"goodCollection","Constraints":[]}}'
$x= Invoke-Webrequest $url1 -Method POST -ContentType application/json -UseDefaultCredentials -Body $Body
$y = $x.Content | ConvertFrom-Json
Write-Host "goodcollection->", $y.results.identities.scopeName
the output result for the first write-host is "badcollection->"
but the output result for the second write-host is "goodcollection->mydomainname"
the questions are:
Why does it depend on collection?
How can i force tfs to sychronize that user account for the badcollection as well as the good collection.
I am just configuring some WorkItem Types to manage our releases - I've got quite far but I cannot see how to get a query to list what I need.
I have User Stories that have "Application Deployment" workitem types as children. Each Application Deployment represents an application to deploy. A User Story might require two applications to be changed. This is a parent/child relationship.
Separately to that, I also have "Release" workitem types. Each Release represents a day on which we will deploy our software. A Release will have several User Stories associated with it - I think using the Network topology of LinkType. This is because a User Story might actually be associated with more than one Release (for example, if we release to one territory first, and then to all remaining territories a week later).
What I would like is a query that lists what applications need to be released:-
Release "14/03/2012"
Application Deployment "MyWebApp1"
Application Deployment "SomeWindowsService"
Release 14/03/2012
Application
If I understand correctly, you're looking to return all the "Application Deployment" work item types associated with a given "Release".
In the query editor, you should be able to select your query type as "Tree of Work Items", and set your query to be Work Item Type = Release, and select linked work items that match the query Work Item Type = Application Deployment.
You can of course add other query filters.
This will give you roughly the following WIQL:
SELECT
[System.Id], [System.WorkItemType], [System.Title],
[System.AssignedTo], [System.State]
FROM WorkItemLinks WHERE
(
[Source].[System.TeamProject] = #project
AND [Source].[System.WorkItemType] = 'Release'
AND [Source].[System.State] <> ''
)
AND ([System.Links.LinkType] = 'System.LinkTypes.Hierarchy-Forward')
AND [Target].[System.WorkItemType] = 'Application Deployment'
ORDER BY [System.Id]
mode(Recursive)
In my Team Foundation Server, I have a collection containing a Team Project. This Team Project has several contributors. The following lines of code get all contributors of that project:
TfsTeamProjectCollection collection = new TfsTeamProjectCollection(new Uri("http://tfs:8080/tfs/CollectionName"));
IGroupSecurityService groupSecurityService = collection.GetService<IGroupSecurityService>();
Identity contributors = groupSecurityService.ReadIdentity(SearchFactor.AccountName, "[ProjectName]\\Contributors", QueryMembership.Expanded);
Identity[] members = groupSecurityService.ReadIdentities(SearchFactor.Sid, contributors.Members, QueryMembership.None);
Each Identity in members has a Property MailAddress, which in my case is equal to string.Empty.
Where do I administrate those mail addresses?
My first idea was to have a look at the users in Start->Administrative Tools->Computer Management->Users
I selected one of the users and opened his properties. I thought there might be an email property that the TFS would take. But I couldn't find one.
Then I opened the TFS Administration Console, looked for Group Membership and navigated to one of the users. There is also no way of editing properties.
Does anyone know where to set that email address?
Great question! There is a TFS job that is scheduled to run every hour to update information about security identities stored in TFS against the details in Active Directory. Some of this information includes the display name, security identifier (SID), AD distinguished name, and e-mail address, among other details. You can find out this cache of details by looking at the tbl_security_identity_cache table in the configuration database.
Warning: Querying against or changing the database puts you in a position where you will likely not be able to get support from Microsoft. It's advised that you don't do this unless instructed by a Microsoft support representative in the context of an active support case. You were actually doing it right by using the TFS SDK to get this information.
If your TFS environment is not in an Active Directory environment, then it will attempt to synchronize information from the local machine where TFS is running. It won't have details about the e-mail address to use so it will be left blank.
Starting in the next version of TFS after TFS 2010, each user will be able to update their notification e-mail address in their profile using Team Web Access.
The following is for TFS 2013 Update 5
** WARNING ** Getting caught editing the TFS database directly
** will void your Microsoft Support Agreement. **
What follows is not for the uninitiated. ** Proceed at your own risk. **
Locate the user or users with email addresses needing to be set. There can be duplicates in the Identities table. I found that the ones with the highest SequenceId were the active Identities.
Use Tfs_TFSConfiguration
SELECT i1.AccountName, i1.Id FROM tbl_Identity AS i1
LEFT OUTER JOIN tbl_Identity AS i2
ON (i1.AccountName=i2.AccountName AND i1.SequenceId<i2.SequenceId)
WHERE i2.AccountName IS NULL
AND i1.AccountName in ('<your first user>','<another user>','<and so on>')
This gives a list of the most recent the Id(s), in GUID form, for the accounts that you need to update. These GUIDs must be reformatted into ArtifactId(s), which is a transformed binary format. This is accomplished by reversing the byte order (low to high) or each of the first three parts of the GUID, but leaving the last two parts in order. E.g.:
Returned 'Id' GUID =01020304-0506-0708-090A-0B0C0D0E0F10
Byte Swapped GUID =04030201-0605-0807-090A-0B0C0D0E0F10
Reformatted 'ArtifacId'=0x0403020106050807090A0B0C0D0E0F10
Next, you have to find the PropertyId(s) used by TFS for email notifications. In TFS 2013 U5, this can be found with the following query:
USE Tfs_TFSConfiguration
SELECT Name, PropertyId FROM tbl_PropertyDefinition WHERE Name LIKE '%Address%'
This will give you the PropertyId(s) for ConfirmedNotificationAddress and CustomNotificationAddresses; which are the two property fields used by TFS 2013 U5 to send notification emails.
Next, you have to find the InternalKindId for the Identity Framework for the TFS DatabaseCategory
USE Tfs_TFSConfiguration
SELECT Description, InternalKindId FROM tbl_PropertyArtifactKind
WHERE Description='Identity'
Now to put it all together, ...
If the configuration records for your user(s) already exist, you can update the settings with:
USE Tfs_TFSConfiguration
UPDATE tbl_PropertyValue SET LeadingStringValue='<user's notification email address>'
WHERE ArtifactId=<ArtifactId, reformatted from tbl_Identity query>
AND PropertyId IN ('<first PropertyId from tbl_PropertyDefinition>', '<second id>')
Note: that ArtifactId is a binary value, based upon a semi-byte-swapped database GUID, and will not match a quoted value in the UPDATE query. I.e. this part of the query will look something like:
WHERE ArtifactId=0x90D490F6BF7B31491CB894323F38A91F AND
Below I assume that the PartitionId is '1'; this should be verified before you continue by a brief scan of the records in the tbl_PropertyValue table.
If you are loading configuration settings that have not yet been set:
USE Tfs_TFSConfiguration
INSERT INTO tbl_PropertyValue
(PartitionId, ArtifactId, InternalKindId, Version, PropertyId, LeadingStringValue)
VALUES ('1', <ArtifactId, reformatted from tbl_Identity query>,
'<InternalKindId from tbl_PropertyArtifactKind>',
'0',
'<first PropertyId from tbl_PropertyDefinition>',
'<user's notification email address>'),
('1', <ArtifactId, reformatted from tbl_Identity query>,
'<InternalKindId from tbl_PropertyArtifactKind>',
'0',
'<second PropertyId from tbl_PropertyDefinition>',
'<user's notification email address>')
Note: that ArtifactId must be an unquoted binary value, transformed from the GUID returned from the tbl_Identity as explained above.
Note: that two records are created for each ArtifactId, one for each PropertyId.
** WARNING ** Getting caught editing the TFS database directly
** will void your Microsoft Support Agreement. **
** Proceed at your own risk. **
(This works for me, ... but, I do not have a Microsoft Support Agreement to invalidate.)
If Active Directory does not get synched with TFS, and assuming your goal to keep email address is for sending notifications you can use the IEventService.GetEventSubscriptions() methods.
var eventService = (IEventService)collection.GetService(typeof(IEventService));
foreach (var member in members)
{
var subscription = eventService.GetEventSubscriptions(member.DisplayName).First();
{
if (subscription != null && string.IsNullOrEmpty(member.MailAddress))
member.MailAddress = subscription.DeliveryPreference.Address;
}
}
I believe this is kept in Active Directory.
For TFS2017+, each user can have a preferred email address, that they can setup in their profile, on the web interface.
It can override, or replace the email set in Active Directory. It also has the benefit to be an instant change, no synchronisation needed.
The field will be initialized with the value that has been set in Active Directory. The synchronization doesn't seem to happen anymore.
In my Team Foundation Server, I have a collection containing a Team Project. This Team Project has several contributors. The following lines of code get all contributors of that project:
TfsTeamProjectCollection collection = new TfsTeamProjectCollection(new Uri("http://tfs:8080/tfs/CollectionName"));
IGroupSecurityService groupSecurityService = collection.GetService<IGroupSecurityService>();
Identity contributors = groupSecurityService.ReadIdentity(SearchFactor.AccountName, "[ProjectName]\\Contributors", QueryMembership.Expanded);
Identity[] members = groupSecurityService.ReadIdentities(SearchFactor.Sid, contributors.Members, QueryMembership.None);
Each Identity in members has a Property MailAddress, which in my case is equal to string.Empty.
Where do I administrate those mail addresses?
My first idea was to have a look at the users in Start->Administrative Tools->Computer Management->Users
I selected one of the users and opened his properties. I thought there might be an email property that the TFS would take. But I couldn't find one.
Then I opened the TFS Administration Console, looked for Group Membership and navigated to one of the users. There is also no way of editing properties.
Does anyone know where to set that email address?
Great question! There is a TFS job that is scheduled to run every hour to update information about security identities stored in TFS against the details in Active Directory. Some of this information includes the display name, security identifier (SID), AD distinguished name, and e-mail address, among other details. You can find out this cache of details by looking at the tbl_security_identity_cache table in the configuration database.
Warning: Querying against or changing the database puts you in a position where you will likely not be able to get support from Microsoft. It's advised that you don't do this unless instructed by a Microsoft support representative in the context of an active support case. You were actually doing it right by using the TFS SDK to get this information.
If your TFS environment is not in an Active Directory environment, then it will attempt to synchronize information from the local machine where TFS is running. It won't have details about the e-mail address to use so it will be left blank.
Starting in the next version of TFS after TFS 2010, each user will be able to update their notification e-mail address in their profile using Team Web Access.
The following is for TFS 2013 Update 5
** WARNING ** Getting caught editing the TFS database directly
** will void your Microsoft Support Agreement. **
What follows is not for the uninitiated. ** Proceed at your own risk. **
Locate the user or users with email addresses needing to be set. There can be duplicates in the Identities table. I found that the ones with the highest SequenceId were the active Identities.
Use Tfs_TFSConfiguration
SELECT i1.AccountName, i1.Id FROM tbl_Identity AS i1
LEFT OUTER JOIN tbl_Identity AS i2
ON (i1.AccountName=i2.AccountName AND i1.SequenceId<i2.SequenceId)
WHERE i2.AccountName IS NULL
AND i1.AccountName in ('<your first user>','<another user>','<and so on>')
This gives a list of the most recent the Id(s), in GUID form, for the accounts that you need to update. These GUIDs must be reformatted into ArtifactId(s), which is a transformed binary format. This is accomplished by reversing the byte order (low to high) or each of the first three parts of the GUID, but leaving the last two parts in order. E.g.:
Returned 'Id' GUID =01020304-0506-0708-090A-0B0C0D0E0F10
Byte Swapped GUID =04030201-0605-0807-090A-0B0C0D0E0F10
Reformatted 'ArtifacId'=0x0403020106050807090A0B0C0D0E0F10
Next, you have to find the PropertyId(s) used by TFS for email notifications. In TFS 2013 U5, this can be found with the following query:
USE Tfs_TFSConfiguration
SELECT Name, PropertyId FROM tbl_PropertyDefinition WHERE Name LIKE '%Address%'
This will give you the PropertyId(s) for ConfirmedNotificationAddress and CustomNotificationAddresses; which are the two property fields used by TFS 2013 U5 to send notification emails.
Next, you have to find the InternalKindId for the Identity Framework for the TFS DatabaseCategory
USE Tfs_TFSConfiguration
SELECT Description, InternalKindId FROM tbl_PropertyArtifactKind
WHERE Description='Identity'
Now to put it all together, ...
If the configuration records for your user(s) already exist, you can update the settings with:
USE Tfs_TFSConfiguration
UPDATE tbl_PropertyValue SET LeadingStringValue='<user's notification email address>'
WHERE ArtifactId=<ArtifactId, reformatted from tbl_Identity query>
AND PropertyId IN ('<first PropertyId from tbl_PropertyDefinition>', '<second id>')
Note: that ArtifactId is a binary value, based upon a semi-byte-swapped database GUID, and will not match a quoted value in the UPDATE query. I.e. this part of the query will look something like:
WHERE ArtifactId=0x90D490F6BF7B31491CB894323F38A91F AND
Below I assume that the PartitionId is '1'; this should be verified before you continue by a brief scan of the records in the tbl_PropertyValue table.
If you are loading configuration settings that have not yet been set:
USE Tfs_TFSConfiguration
INSERT INTO tbl_PropertyValue
(PartitionId, ArtifactId, InternalKindId, Version, PropertyId, LeadingStringValue)
VALUES ('1', <ArtifactId, reformatted from tbl_Identity query>,
'<InternalKindId from tbl_PropertyArtifactKind>',
'0',
'<first PropertyId from tbl_PropertyDefinition>',
'<user's notification email address>'),
('1', <ArtifactId, reformatted from tbl_Identity query>,
'<InternalKindId from tbl_PropertyArtifactKind>',
'0',
'<second PropertyId from tbl_PropertyDefinition>',
'<user's notification email address>')
Note: that ArtifactId must be an unquoted binary value, transformed from the GUID returned from the tbl_Identity as explained above.
Note: that two records are created for each ArtifactId, one for each PropertyId.
** WARNING ** Getting caught editing the TFS database directly
** will void your Microsoft Support Agreement. **
** Proceed at your own risk. **
(This works for me, ... but, I do not have a Microsoft Support Agreement to invalidate.)
If Active Directory does not get synched with TFS, and assuming your goal to keep email address is for sending notifications you can use the IEventService.GetEventSubscriptions() methods.
var eventService = (IEventService)collection.GetService(typeof(IEventService));
foreach (var member in members)
{
var subscription = eventService.GetEventSubscriptions(member.DisplayName).First();
{
if (subscription != null && string.IsNullOrEmpty(member.MailAddress))
member.MailAddress = subscription.DeliveryPreference.Address;
}
}
I believe this is kept in Active Directory.
For TFS2017+, each user can have a preferred email address, that they can setup in their profile, on the web interface.
It can override, or replace the email set in Active Directory. It also has the benefit to be an instant change, no synchronisation needed.
The field will be initialized with the value that has been set in Active Directory. The synchronization doesn't seem to happen anymore.