I'm upgrading a C# app that talks to TFS/VSTS to use the latest TeamFoundation sdk.
I would like to connect and have the app prompt for credentials in the same way that Visual Studio does if you use that to connect to TFS.
I've downloaded the latest stable VSTS Api from nuget.org which is:
microsoft.teamfoundationserver.extendedclient.15.112.1.nupkg
I also reference assemblies it uses from my VS2017 install, here:
C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer.
I've tried a number of combinations, but can't get it to prompt. My current code looks like this:
static void Main(string[] args)
{
try
{
var netCred = new NetworkCredential();
var basicCred = new VssBasicCredential(netCred);
var vssCred = new VssCredentials(basicCred);
vssCred.PromptType = CredentialPromptType.PromptIfNeeded;
var server = new TfsTeamProjectCollection(new Uri(serverName), vssCred);
server.Authenticate();
}
catch( Exception ex )
{
System.Console.WriteLine(ex.ToString());
}
System.Console.ReadKey();
}
It doesn't prompt, and instead outputs this exception:
Microsoft.TeamFoundation.TeamFoundationServerUnauthorizedException:
TF30063: You are not authorized to access
https://.visualstudio.com/. ---> System.Net.WebException: The
remote server returned an error: (401) Unauthorized. at
System.Net.HttpWebRequest.GetResponse() at
Microsoft.TeamFoundation.Client.Channels.TfsHttpWebRequest.SendRequestAndGetResponse(HttpWebRequest
webRequest, WebException& webException) --- End of inner exception
stack trace --- at
Microsoft.TeamFoundation.Client.Channels.TfsHttpWebRequest.SendRequest()
at
Microsoft.TeamFoundation.Client.Channels.TfsHttpRequestChannel.Request(TfsMessage
message, TimeSpan timeout) at
Microsoft.TeamFoundation.Client.Channels.TfsHttpClientBase.Invoke(TfsClientOperation
operation, Object[] parameters, TimeSpan timeout, Object[]& outputs)
at
Microsoft.TeamFoundation.Framework.Client.LocationWebService.Connect(Int32
connectOptions, Int32 lastChangeId, Int32 features) at
Microsoft.TeamFoundation.Framework.Client.FrameworkServerDataProvider.Connect(ConnectOptions
connectOptions) at
Microsoft.TeamFoundation.Framework.Client.FrameworkServerDataProvider.Authenticate()
at Microsoft.TeamFoundation.Client.TfsConnection.Authenticate() at
VstsAuthTest.Program.Main(String[] args) in
S:\VstsAuthTest\Program.cs:line 26
How do I get it to prompt for and cache credentials?
The old version of the TeamFoundation sdk dlls I was using seemed to work ok. The reason I'm upgrading is because the C# app seems to refuse to connect to TFS when installed on a machine with only VS2017 and not VS2015. I was hopeful upgrading to the latest SDK dlls might help solve the connection issue.
I've seen this, but it seems out of date and uses classes that are now deprecated. It's also about connecting without a prompt, but the comments include some discussion of how to get a prompt.
https://blogs.msdn.microsoft.com/buckh/2013/01/07/how-to-connect-to-tf-service-without-a-prompt-for-liveid-credentials/
I've also seen these samples which appear recent, but which also use deprecated apis.
https://www.visualstudio.com/en-us/docs/integrate/get-started/client-libraries/samples
Just use Microsoft Team Foundation Server Extended Client package with VssClientCredentials.
Simple code:
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.WorkItemTracking.WebApi;
using Microsoft.VisualStudio.Services.Client;
using Microsoft.VisualStudio.Services.WebApi;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TFSAPIConsoleApp
{
class Program
{
static void Main(string[] args)
{
var u = new Uri("https://XXX.visualstudio.com");
TfsTeamProjectCollection collection = new TfsTeamProjectCollection(u, new VssClientCredentials());
collection.Authenticate();
Console.WriteLine(collection.Name);
Console.Read();
}
}
}
Related
Intro: I'm trying to get Azure Pod Identity to work in our cluster to read secrets from a KeyVault, and am mostly succeeding (so far so good). For the time being, we have two keyvaults, two AzureIdentity's, two AzureIdentityBinding's and two Pods using each their keyvault.
While testing, both pods are equal - only difference being their aadpodidbinding and an environment variable indicating what keyvault to use. At startup, the pod connects to the KeyVault, reads two values and prints them with Console.WriteLine. If the connection fails, the pod will crash and k8s will restart it.
The problem: One pod might startup being able to read from the keyvault immediately, while the other will crash and restart for - what seems to be - rather consistently 5 times before being able to get an access token.
When it fails, the following Exception is thrown:
Unhandled Exception: Microsoft.Azure.Services.AppAuthentication.AzureServiceTokenProviderException: Parameters: Connection String: [No connection string specified], Resource: https://vault.azure.net, Authority: https://login.windows.net/******************. Exception Message: Tried the following 3 methods to get an access token, but none of them worked.
Parameters: Connection String: [No connection string specified], Resource: https://vault.azure.net, Authority: https://login.windows.net/******************. Exception Message: Tried to get token using Managed Service Identity. Access token could not be acquired. MSI ResponseCode: Forbidden, Response: no AzureAssignedIdentity found for pod:default/kv-test-be
Parameters: Connection String: [No connection string specified], Resource: https://vault.azure.net, Authority: https://login.windows.net/******************. Exception Message: Tried to get token using Visual Studio. Access token could not be acquired. Environment variable LOCALAPPDATA not set.
Parameters: Connection String: [No connection string specified], Resource: https://vault.azure.net, Authority: https://login.windows.net/******************. Exception Message: Tried to get token using Azure CLI. Access token could not be acquired. No such file or directory
at Microsoft.Azure.Services.AppAuthentication.AzureServiceTokenProvider.GetAuthResultAsyncImpl(String authority, String resource, String scope)
at Microsoft.Azure.Services.AppAuthentication.AzureServiceTokenProvider.<get_KeyVaultTokenCallback>b__8_0(String authority, String resource, String scope)
at Microsoft.Azure.KeyVault.KeyVaultCredential.PostAuthenticate(HttpResponseMessage response)
at Microsoft.Azure.KeyVault.KeyVaultCredential.ProcessHttpRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at Microsoft.Azure.KeyVault.KeyVaultClient.GetSecretsWithHttpMessagesAsync(String vaultBaseUrl, Nullable`1 maxresults, Dictionary`2 customHeaders, CancellationToken cancellationToken)
at Microsoft.Azure.KeyVault.KeyVaultClientExtensions.GetSecretsAsync(IKeyVaultClient operations, String vaultBaseUrl, Nullable`1 maxresults, CancellationToken cancellationToken)
at Microsoft.Extensions.Configuration.AzureKeyVault.AzureKeyVaultConfigurationProvider.LoadAsync()
at Microsoft.Extensions.Configuration.AzureKeyVault.AzureKeyVaultConfigurationProvider.Load()
at Microsoft.Extensions.Configuration.ConfigurationRoot..ctor(IList`1 providers)
at Microsoft.Extensions.Configuration.ConfigurationBuilder.Build()
at KeyvaultTest.Program.Main(String[] args) in /app/src/Program.cs:line 16
The behaviour is similar when using FlexVolume (which eventually one group of our pods will use in production), but I find it easier to relate to the error with two equal pods.
While waiting for the pod to succeed, I'm seeing both "binding removed" and "binding applied" messages in mic's log.
My questions:
Is this behaviour "as intendend" and perhaps documented somewhere?
Is there a setting I can apply to make the "remove - apply" cycle faster?
Is there anything else that can be done to improve the time between pod creation and the identity binding being applied? Is this issue perhaps related to https://github.com/Azure/aad-pod-identity/issues/145
Sourcecode:
Program.cs
using System;
using System.IO;
using System.Threading;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
namespace KeyvaultTest
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Starting Keyvault read");
var configuration = new ConfigurationBuilder()
.AddAzureKeyVault()
.Build();
var test1 = configuration.GetValue<string>("jtest");
Console.WriteLine(test1);
var test2 = configuration.GetValue<string>("jtest:jtest");
Console.WriteLine(test2);
Console.WriteLine("Finished Keyvault read");
}
}
}
KeyVaultConfiguration.cs.cs
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Threading;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Azure.KeyVault;
using Microsoft.Azure.Services.AppAuthentication;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.AzureKeyVault;
namespace KeyvaultTest
{
public static class KeyVaultConfiguration
{
public static IConfigurationBuilder AddAzureKeyVault(this IConfigurationBuilder builder)
{
var builtConfig = builder.Build();
var keyVaultName = Environment.GetEnvironmentVariable("KV_NAME");
if (string.IsNullOrWhiteSpace(keyVaultName))
{
throw new Exception("KV_NAME is not defined");
}
Console.WriteLine($"Using KV_NAME = {keyVaultName}");
var azureServiceTokenProvider = new AzureServiceTokenProvider();
var keyVaultClient = new KeyVaultClient(
new KeyVaultClient.AuthenticationCallback(
azureServiceTokenProvider.KeyVaultTokenCallback));
builder.AddAzureKeyVault(
$"https://{keyVaultName}.vault.azure.net/",
keyVaultClient,
new DefaultKeyVaultSecretManager());
return builder;
}
}
}
Any help, hints or ideas are much appreciated.
Note: I've posted this same question to the Issue board on of the project's github page https://github.com/Azure/aad-pod-identity/issues/181
We were facing the same issue. We overcame this issue by upgrading AAD Pod Identity. Our version was 1.5 and upgrading this one to 1.7 resolved our issue.
Before that, we had also upgraded the packages (Microsoft.Azure.Services.AppAuthentication & Azure.Security.KeyVault.Secrets) that our applications were using to the latest versions but it wasn't enough.
We are moving to the web style builds on TFS 2015 (Update 4).
I've always been able to retrieve information about builds using the code below, but that is not retrieving our new builds created via the web interface.
Is there a reasonable way to modify my code to bring in both legacy builds and the new style builds?
If not, I assume it is time for me to figure out how to use the REST API. Any tips for an equivalent query would be appreciated.
TfsTeamProjectCollection tfs = new TfsTeamProjectCollection(new Uri("http://SERVERINFO"));
IBuildServer buildServer = (IBuildServer) tfs.GetService(typeof (IBuildServer));
var buildDetail = buildServer.CreateBuildDetailSpec("*");
buildDetail.MinFinishTime = DateTime.Now.Date.AddDays(-1);
buildDetail.InformationTypes = null;
buildDetail.QueryDeletedOption = QueryDeletedOption.IncludeDeleted;
buildDetail.MaxBuildsPerDefinition = 1; //Only return the most recent of each build type...or comment out to return all builds with this definition
var builds = buildServer.QueryBuilds(buildDetail).Builds.Select(....
The old XAML build system uses a SOAP API.The task-based new vNet build system does not have a SOAP API. It's using REST API. I'm afraid you could not just modify the code to get new builds. They do not support Build vNext as they were written before their time.
Besides the SOAP API is slowly being replaced with a REST API, especially in some new features.Since you are moving to vNext build on TFS2015 update4. Highly recommend you stat to use Rest API.
You can access it from C# code either by querying the REST API directly or by using the Team Foundation Server Client NuGet package. A Sample:
using System;
using System.Collections.Generic;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Build.WebApi;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Uri tfsurl = new Uri("http://xxxx:8080/tfs/CollectionName");
TfsTeamProjectCollection ttpc = new TfsTeamProjectCollection(tfsurl);
BuildHttpClient bhc = ttpc.GetClient<BuildHttpClient>();
List<Build> builds = bhc.GetBuildsAsync("ProjectName").Result;
foreach (Build bu in builds)
{
Console.WriteLine(bu.BuildNumber);
}
Console.ReadLine();
}
}
}
By using the Rest API in the libraries as above, you could be able to get both XAML and vNext builds.
I am trying to fetch Build Warning from MS Build,(in Build which contain or having number of solutions)
Is it possible to fetch using TFS API, or any TFS DB using QUERY ?
You could use this TFS REST API to get logs of a TFS builds. To get those logs out, you need to fetch those warnings by yourself. There's no API to only get warnings.
Http method: GET
http:/servername"8080/tfs/DefaultCollection/teamproject/_apis/build/builds/391/logs?api-version=2.0
You could also install a TFS ExtendedClient Nuget package to use TFS object model API.
Here is the code snippet:
Like the comment said above, the VNext build definition information couldn't be reached using the old version API. Install this TFS ExtendedClient Nuget package for your project, using the method below to get all build definitions.
using Microsoft.VisualStudio.Services.WebApi;
using Microsoft.VisualStudio.Services.Common;
using Microsoft.TeamFoundation.Build.WebApi;
using Microsoft.TeamFoundation.Core.WebApi;
using Microsoft.VisualStudio.Services.Operations;
private static void GetBuildWarnings()
{
var u = new Uri("http://v-tinmo-12r2:8080/tfs/MyCollection/");
VssCredentials c = new VssCredentials(new Microsoft.VisualStudio.Services.Common.WindowsCredential(new NetworkCredential("username", "password", "domain")));
var connection = new VssConnection(u, c);
BuildHttpClient buildServer = connection.GetClient<BuildHttpClient>();
List<BuildLog> logs = buildServer.GetBuildLogsAsync("teamprojectname",buildId).Result;
foreach (BuildLog log in logs)
{
var list = buildServer.GetBuildLogLinesAsync("A92FB795-A956-45B5-A017-7A7DFB96A040",buildId,log.Id).Result; //A92FB795-A956-45B5-A017-7A7DFB96A040 is the team project Guid
foreach (var line in list)
{
if (l.Contains("[Warning]"))
{
Console.WriteLine(line);
}
}
}
Console.ReadLine();
}
I want to add or remove users from TFS using REST API. Any help appreciated.
Afraid this can't be achieved through Rest API for now. If you really want to do it programmatically. You can use client API.
You can try to use IIdentityManagementService.ReadIdentity() along with IIdentityManagementService.AddMemberToApplicationGroup() to add Windows users to TFS groups, even if those Windows users are not known to TFS yet.
This is accomplished by specifying the ReadIdentityOptions.IncludeReadFromSource option.
Below is an example of adding a Windows user VSALM\Barry to the Fabrikam Fiber Web Team (TFS Group), in the FabrikamFiber Team Project, in the http://vsalm:8080/tfs/FabrikamFiberCollection (Also applies to server level)
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Framework.Client;
using Microsoft.TeamFoundation.Framework.Common;
using System;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var tpc = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri("http://vsalm:8080/tfs/FabrikamFiberCollection"));
var ims = tpc.GetService<IIdentityManagementService>();
var tfsGroupIdentity = ims.ReadIdentity(IdentitySearchFactor.AccountName,
"[FabrikamFiber]\\Fabrikam Fiber Web Team",
MembershipQuery.None,
ReadIdentityOptions.IncludeReadFromSource);
var userIdentity = ims.ReadIdentity(IdentitySearchFactor.AccountName,
"VSALM\\Barry",
MembershipQuery.None,
ReadIdentityOptions.IncludeReadFromSource);
ims.AddMemberToApplicationGroup(tfsGroupIdentity.Descriptor, userIdentity.Descriptor);
}
}
}
I've tried two ways of connecting to the workitemstore for the TFS server we're running. Attempt A was to connect to the configuration server and use GetService<WorkItemStore>() method. This always returns null.
Attempt B was to connect to the TfsTeamProjectCollection and use the GetService<WorkItemStore>() method or pass the project collection into the WorkItemStore constructor. On attempt B, I get an exception stating "Error HRESULT E_FAIL has been returned from a call to a COM component." The only info I can find on that seems to indicate some permissions problem, but I've confirmed I'm authenticated as a user with read access to the whole project collection and I connect and meddle appropriately via VS 2011 dev preview.
Here's how I'm connecting...
public TfsConfigurationServer GetConfigurationServer()
{
Uri tfsUri = new Uri(configs.TfsUri);
TfsConfigurationServer server = TfsConfigurationServerFactory.GetConfigurationServer(tfsUri, credProvider);
server.Authenticate();
if (server.HasAuthenticated == false)
throw new InvalidOperationException("You can't authenticate against the tfs instance.");
return server;
}
public TfsTeamProjectCollection GetProjectCollectionInstance(string projectCollectionName)
{
Uri tfsUri = new Uri(configs.TfsUri + "/" + projectCollectionName);
TfsTeamProjectCollection collection = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(tfsUri, credProvider);
collection.Authenticate();
if (collection.HasAuthenticated == false)
throw new InvalidOperationException("You can't authenticate against the tfs instance.");
return collection;
}
and here's how I'm trying to get the WorkItemStore (silly code to illustrate the problem)...
public WorkItemProvider()
{
if (workItems == null)
workItems = ServerProvider.ServerInstance.GetService<WorkItemStore>();
if (workItems == null)
workItems = ServerProvider.ProjectCollectionInstance.GetService<WorkItemStore>();
if (workItems == null)
workItems = new WorkItemStore(ServerProvider.ProjectCollectionInstance);
if (workItems == null)
throw new NullReferenceException("Couldn't load work item store.");
}
I'm not on the same domain as the server, but I'm authenticating as a domain user with an ICredentialsProvider and I've confirmed I'm authenticated as that user. Any pointers would be helpful.
Check if this does what you need:
using System;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
namespace GetsWorkItem
{
class Program
{
static void Main()
{
TfsTeamProjectCollection teamProjectCollection = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri("http://<TFS>:8080/tfs/<COLLECTION>"));
WorkItemStore workItemStore= (WorkItemStore) teamProjectCollection.GetService(typeof (WorkItemStore));
WorkItem workItem = workItemStore.GetWorkItem(1234);
}
}
}
I believe this article might be able to answer your question. It says that if you instantiate your WorkItemStore in a slightly different way, you'll get a different exception:
System.TypeInitializationException: The type initializer for ‘Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore’ threw an exception. —> System.IO.FileLoadException: Mixed mode assembly is built against version ‘v2.0.50727′ of the runtime and cannot be loaded in the 4.0 runtime without additional configuration information.
The fix is a simple web.config change, by adding the following:
<?xml version="1.0"?>
<configuration>
<startup useLegacyV2RuntimeActivationPolicy="true">
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
</startup>
</configuration>
Hope this helps! Worked for me when I was getting the same error.