How can I remove members from TFS groups programmatically in c#? - tfs

I have access to TFS users. I can get them into list but need to remove some of them from all the groups in our TFS. I've done so many researchers so far. Simply, I need to remove user from TFS groups.
I am open to any suggestions. Even for the crazy ones!
I've tried programmatical stuff. Don't have any clue.
After so many tries, finally got somewhere. My final code:
bool isError = false;
TeamFoundationIdentity memberId = ims.ReadIdentity(IdentitySearchFactor.DisplayName, id.DisplayName, MembershipQuery.Expanded, ReadIdentityOptions.None);
IIdentityManagementService2 ims2 = tcs.GetService<IIdentityManagementService2>();
string group = "Confidential Group;
TeamFoundationIdentity groupId = ims2.ReadIdentity(group);
if (groupId == null)
{
isError = true;
}
if (memberId == null)
{
isError = true;
}
if (!isError)
{
ims2.RemoveMemberFromApplicationGroup(groupId.Descriptor, memberId.Descriptor);
}
The error:
'TF50621: The Team Foundation group that you wish to manage is not owned by service host TEAM FOUNDATION, it is owned by . Please target your request at the correct host.'**

Check out the Azure Boards Team Tools.
It does most of what you need already:
message = string.Empty;
bool ret = true;
TeamFoundationTeam t = this.teamService.ReadTeam(this.projectInfo.Uri, team, null);
TeamFoundationIdentity i = this.identityManagementService.ReadIdentity(IdentitySearchFactor.AccountName, user, MembershipQuery.Direct, ReadIdentityOptions.None);
if (t == null)
{
message = "Team [" + team + "] not found";
ret = false;
}
if (i == null)
{
message = "User [" + user + "] not found";
ret = false;
}
if (ret)
{
this.identityManagementService.RemoveMemberFromApplicationGroup( t.Identity.Descriptor, i.Descriptor);
message = "User removed ";
}
return ret;
This function removes a user from a Team. A Team is a special kind of group, the function is easy to adapt to retrieve a Group to delete the user from though:
Replace
TeamFoundationTeam t = this.teamService.ReadTeam(this.projectInfo.Uri, team, null);
With:
string group = ...
var t = this.identityManagementService.ReadIdentity(group);
Or use the REST API :
DELETE https://vsaex.dev.azure.com/{organization}/_apis/GroupEntitlements/{groupId}/members/{memberId}?api-version=7.1-preview.1

Related

MSAL Exchange Roles Group

I am trying to retrieve the "Members)" information from Exchange "Role assignments" (eg. Compliance Management, Discovery Management, ExchangeServiceAdmins_xx, etc) using the following code but am not able to find them. It is only able to list the "Assigned Roles" in the Azure Active Directory.
Any idea what I have doing wrong here?
DirectoryObjectCollectionWithReferencesPage roles = gGraphClient.users(username).memberOf().buildRequest().get();
DirectoryObject r;
DirectoryRole role;
Group g;
if (roles == null)
{
throw new Exception("Bye");
}
List<DirectoryObject> currentPageGroup = roles.getCurrentPage();
Iterator itr = currentPageGroup.iterator();
while (itr.hasNext())
{
r = (DirectoryObject) itr.next();
if (r.oDataType.endsWith("group"))
{
g = (Group) r;
System.out.println(g.displayName);
}
else
{
role = (DirectoryRole) r;
System.out.println(role.displayName);
}
}

TFS Azure 2017 workitem change event handler - branch relation

We have a plugin in tfs. This plugin notifies us when any work item changes. We can get work item details like id, changeset list as follows. But we need work item related branch name also.
Could you please help me, is there any way to get related branch name when workitem changed in below code.
Thanks
public EventNotificationStatus ProcessEvent(
IVssRequestContext requestContext,
NotificationType notificationType,
object notificationEventArgs,
out int statusCode,
out string statusMessage,
out ExceptionPropertyCollection properties)
{
statusCode = 0;
properties = null;
statusMessage = String.Empty;
try
{
if (notificationType == NotificationType.Notification && notificationEventArgs is WorkItemChangedEvent)
{
WorkItemChangedEvent ev = notificationEventArgs as WorkItemChangedEvent;
int workItemId = Convert.ToInt32(ev.CoreFields.IntegerFields[0].NewValue);
WriteLog("1WorkItemChangedEventHandler WorkItem " + ev.WorkItemTitle + " - Id " + workItemId.ToString() + " was modified");
}
}
catch (Exception)
{
}
return EventNotificationStatus.ActionPermitted;
}
You can use workItemStore.GetWorkItem(workItemId ) to get all work item info. Check the similar question here: How can I get a reference to the TFS WorkItem in a WorkItemChangedEvent?
If you use git, you can try the following example to get branch reference:
int workitemId = YOUR_ID;
WorkItemStore wiStore = new WorkItemStore("{YOUR_URI}");
WorkItem wi = wiStore.GetWorkItem(workitemId);
foreach (Link link in wi.Links)
{
if (link.BaseType == BaseLinkType.ExternalLink)
{
var brList = ((ExternalLink)link).LinkedArtifactUri.Split(new string[] { "%2F" }, StringSplitOptions.RemoveEmptyEntries).ToList();
brList.RemoveAt(0); //remove project and repo giuds
brList.RemoveAt(0);
brList[0] = brList[0].Replace("GB", ""); // remove GB prefix
Console.WriteLine("Branch: {0}", string.Join("/", brList));
}
}

'Create copy of work item' via REST API for Azure DevOps?

I'm wanting to 'Create copy of work item' which is available via the UI, ideally via the API.
I know how to create a new work item, but the feature in the UI to connect all current parent links / related links, and all other details is quite useful.
Creating via this API is here: https://learn.microsoft.com/en-us/rest/api/azure/devops/wit/work%20items/create?view=azure-devops-rest-5.1
Any help would be greatly appreciated.
We cannot just copy a work item because it contains system fields that we should skip. Additionally your process may have some rules that may block some fields on the creation step. Here is the small example to clone a work item through REST API with https://www.nuget.org/packages/Microsoft.TeamFoundationServer.Client:
class Program
{
static string[] systemFields = { "System.IterationId", "System.ExternalLinkCount", "System.HyperLinkCount", "System.AttachedFileCount", "System.NodeName",
"System.RevisedDate", "System.ChangedDate", "System.Id", "System.AreaId", "System.AuthorizedAs", "System.State", "System.AuthorizedDate", "System.Watermark",
"System.Rev", "System.ChangedBy", "System.Reason", "System.WorkItemType", "System.CreatedDate", "System.CreatedBy", "System.History", "System.RelatedLinkCount",
"System.BoardColumn", "System.BoardColumnDone", "System.BoardLane", "System.CommentCount", "System.TeamProject"}; //system fields to skip
static string[] customFields = { "Microsoft.VSTS.Common.ActivatedDate", "Microsoft.VSTS.Common.ActivatedBy", "Microsoft.VSTS.Common.ResolvedDate",
"Microsoft.VSTS.Common.ResolvedBy", "Microsoft.VSTS.Common.ResolvedReason", "Microsoft.VSTS.Common.ClosedDate", "Microsoft.VSTS.Common.ClosedBy",
"Microsoft.VSTS.Common.StateChangeDate"}; //unneeded fields to skip
const string ChildRefStr = "System.LinkTypes.Hierarchy-Forward"; //should be only one parent
static void Main(string[] args)
{
string pat = "<pat>"; //https://learn.microsoft.com/en-us/azure/devops/organizations/accounts/use-personal-access-tokens-to-authenticate
string orgUrl = "https://dev.azure.com/<org>";
string newProjectName = "";
int wiIdToClone = 0;
VssConnection connection = new VssConnection(new Uri(orgUrl), new VssBasicCredential(string.Empty, pat));
var witClient = connection.GetClient<WorkItemTrackingHttpClient>();
CloneWorkItem(witClient, wiIdToClone, newProjectName, true);
}
private static void CloneWorkItem(WorkItemTrackingHttpClient witClient, int wiIdToClone, string NewTeamProject = "", bool CopyLink = false)
{
WorkItem wiToClone = (CopyLink) ? witClient.GetWorkItemAsync(wiIdToClone, expand: WorkItemExpand.Relations).Result
: witClient.GetWorkItemAsync(wiIdToClone).Result;
string teamProjectName = (NewTeamProject != "") ? NewTeamProject : wiToClone.Fields["System.TeamProject"].ToString();
string wiType = wiToClone.Fields["System.WorkItemType"].ToString();
JsonPatchDocument patchDocument = new JsonPatchDocument();
foreach (var key in wiToClone.Fields.Keys) //copy fields
if (!systemFields.Contains(key) && !customFields.Contains(key))
if (NewTeamProject == "" ||
(NewTeamProject != "" && key != "System.AreaPath" && key != "System.IterationPath")) //do not copy area and iteration into another project
patchDocument.Add(new JsonPatchOperation()
{
Operation = Operation.Add,
Path = "/fields/" + key,
Value = wiToClone.Fields[key]
});
if (CopyLink) //copy links
foreach (var link in wiToClone.Relations)
{
if (link.Rel != ChildRefStr)
{
patchDocument.Add(new JsonPatchOperation()
{
Operation = Operation.Add,
Path = "/relations/-",
Value = new
{
rel = link.Rel,
url = link.Url
}
});
}
}
WorkItem clonedWi = witClient.CreateWorkItemAsync(patchDocument, teamProjectName, wiType).Result;
Console.WriteLine("New work item: " + clonedWi.Id);
}
}
Link to full project: https://github.com/ashamrai/AzureDevOpsExtensions/tree/master/CustomNetTasks/CloneWorkItem

TFS 2017 Plugin Issues - Caught an ExceptionTF30063: You are not authorized to access "https://tfs.com//TeamProject

I'm trying to create a plugin in TFS 2017 ,that checks for Associated workitems before check-in and rejects the check-in if it does not meet few conditions. This was working for One Collection and One TeamProject. Now I'm trying to implement this this across multiple collections and team projects and it throws the following error "TF30063: You are not authorized to access "https://tfs.com//TeamProject" I'm pasting the code below, Please let me know what the possible issue would be .
{
// Logic Goes here ...
bool isNullComment = false;
bool isDevBranchChange = false;
bool isCodeReviewRequest = false;
bool isCodeReviewResponse = false;
bool isTaskAssociated = false;
//Read all submitted items
var changes = notification.GetSubmittedItems(requestContext);
//Check if the changes have any DEV branch changes
isDevBranchChange = changes.Any(change => change.ToUpper().Contains("/DEV/"));
if (isDevBranchChange)
{
isNullComment = string.IsNullOrEmpty(notification.Comment.ToString());
var assoWorkItems = notification.NotificationInfo.WorkItemInfo.Select(x => x.Id); //Read all associated workitem id's
TfsTeamProjectCollection connection = new TfsTeamProjectCollection(GetTfsUri(requestContext));
WorkItemStore wiStore = connection.GetService<WorkItemStore>();
//check if any of associated workitem has code review
foreach (int id in assoWorkItems)
{
WorkItem wi = wiStore.GetWorkItem(id);
// Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models.WorkItem wi = witClient.GetWorkItemAsync(id).Result;
if (wi.Type.Name.ToUpper().Equals("CODE REVIEW REQUEST"))
{
isCodeReviewRequest = true;
}
if (wi.Type.Name.ToUpper().Equals("TASK"))
{
isTaskAssociated = true;
}
if (wi.Type.Name.ToUpper().Equals("CODE REVIEW RESPONSE"))
{
isCodeReviewResponse = true;
}
}
if (!isCodeReviewRequest || isNullComment || !isCodeReviewResponse || !isTaskAssociated)
{
statusMessage = "Check-in Rejected. Check-in Comment, Task, Code Review Request and Code Review Response workitems are Required. \nFor more information, please contact TFS Administrators: vsalm#abc.com";
return EventNotificationStatus.ActionDenied;
}
}
}
catch (Exception exception)
{
statusMessage = "Caught an Exception" + exception.Message;
return EventNotificationStatus.ActionDenied;
// Any exception, TFS will disable it. So, Log it and eat it.
TeamFoundationApplicationCore.LogException("DecisionPoint plugin encountered the following error while processing events", exception);
}
}
return EventNotificationStatus.ActionPermitted;
}
private Uri GetTfsUri(IVssRequestContext _requestContext)
{
var locationService = _requestContext.GetService<ILocationService>();
return new Uri(locationService.GetServerAccessMapping(_requestContext).AccessPoint + "/" + _requestContext.ServiceHost.Name);
}
}
}

How to create an ad-hoc email and send it using Acumatica

In Acumatica you can use notifications to automate some emails.
In my scenario, we are creating a process that will at non-specific (non-set) times need to send an email when a specific condition is triggered, such as an employee needs to know they need to do something.
We are building this logic into the system and I am looking for a code sample of how to send the email when this happens.
We will be using an email template, but need to accomplish the feat in code.
I would hope there should be some kind of acumatica email class where we could just call it and pass the required info something like:
PX.Common.email.Send(params)...
Any example code would be appreciated.
It turns out that there is a KB article that gives an example of how to do this.
for our scenario, Here is a more recent version of the code that has been verified to send an email using either of 2 email templates.
private void mSendEmail(string toEmail, int? emailTemplateID, long? noteid, string source, string toDisplayName)
{
bool sent = false;
string sError = "Failed to send E-mail.";
POOrder porec = poOrder.Select(noteid);
EPExpenseClaim eprec = epExpense.Select(noteid);
try
{
Notification rowNotification = PXSelect<Notification,
Where<Notification.notificationID, Equal<Required<Notification.notificationID>>>>.Select(this, emailTemplateID);
if (rowNotification == null)
throw new PXException(PXMessages.Localize("Notification Template for Escalation is not specified."));
if (String.IsNullOrEmpty(toEmail))
throw new PXException(PXMessages.Localize("E-mail is not specified for Escalation Employee. Name=[" + toDisplayName +"]"));
if (source == "PO")
{
var sender = TemplateNotificationGenerator.Create(porec, rowNotification.NotificationID.Value);
sender.MailAccountId = rowNotification.NFrom.HasValue ?
rowNotification.NFrom.Value :
PX.Data.EP.MailAccountManager.DefaultMailAccountID;
sender.To = toEmail;
IEnumerable<EPActivity> epActivityArray = sender.Send();
if (epActivityArray.Count() > 0)
{ sent = true; }
}
if (source == "EP")
{
var sender = TemplateNotificationGenerator.Create(eprec, rowNotification.NotificationID.Value);
sender.MailAccountId = rowNotification.NFrom.HasValue ?
rowNotification.NFrom.Value :
PX.Data.EP.MailAccountManager.DefaultMailAccountID;
sender.To = toEmail;
IEnumerable<EPActivity> epActivityArray = sender.Send();
if (epActivityArray.Count() > 0)
{ sent = true; }
}
}
catch (Exception Err)
{
sent = false;
sError = Err.Message;
}
if (!sent)
throw new PXException(PXMessages.Localize(sError));
}
Here I want to present shorter version of sending email:
using PX.Objects.EP;
using PX.Data.EP;
**...**
var sender = new NotificationGenerator
{
To = "someone#example.com",
Subject = $"Subject information {DateTime.Now:d}",
Body = "Body of message",
BodyFormat = EmailFormatListAttribute.Text
};
sender.Send();

Resources