I have a worker role in azure which uses a service account 'myservice' and password is stored in keyvault. Now I have access TFS work Items using TFS APIs.
For testing, I have passed my credentials as
NetworkCredential networkCred = new NetworkCredential("myusername","mypassword");
ICredentials cred = (ICredentials)networkCred;
TfsConfigurationServer configurationServer = new TfsConfigurationServer(tfsUri, cred);
But now I have to pass the service account name 'myservice' and its password.
How do I use service account to access the TFS work Items?
You should be able to consume TFS API using OData Services as explained below:
https://blogs.msdn.microsoft.com/briankel/2011/10/26/odata-service-for-team-foundation-server-2010-v1/
Have a read of this link, then follow the instructions from "Team Foundation Service authentication:" to set up your account/profile to access the Api.
You can then access the resources via the web Api.
Related
I am looking forward to setup a Service Principal in the main Devops of my company to use as Token access to update or create work items with the Devops Api inside an application in C#...
We are already using the api but with personal tokens, as we know this is not the best practice, because in case any person goes off work their personal access tokens will expires...
So, in order with that I followed this guide: https://cann0nf0dder.wordpress.com/2020/09/27/programmatically-connecting-to-azure-devops-with-a-service-principal-subscription/
Then I added the service principal into the azure active directory group that has all of our users ( the ppl who access into devops )
public void UpdateAzureDevopsPullReviewed(List<int> user_story_numbers, string assigned_to)
{
#region Azure DevOps data connection
Uri orgUrl = new Uri("https://dev.azure.com/nfpnso/");
String tokenWrite = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
//create a connection
VssConnection connection = new VssConnection(orgUrl, new VssBasicCredential(string.Empty, tokenWrite));
#endregion
PullReviewedWorkItem(connection, user_story_numbers, assigned_to).Wait();
}
What I did in Azure was create a new APP registration, in Active Directory, there I got Application (client) ID, Directory (tenant) ID, Object ID and then I created a new secret, this means the ID and the Value ... probe with all these strings and the connection is not executed, it tells me that I am not authorized to access the devops .
I don't think you can use a service principal to call the Azure DevOps API.
Please see Choosing the right authentication mechanism.
Pay attention to the Note tip:
The Azure DevOps API doesn't support non-interactive service access
via service principals.
The only Non-interactive client-side type is Device Profile Authentication mechanism.
I have an existing application (a console app that runs as a WebJob) that uses Exchange Web Services to read emails in a shared Outlook 365 mailbox. This works, but it's using basic authentication and I want to use OAuth instead. I'm attempting to do this using Microsoft.Identity.Client.ConfidentialClientApplicationBuilder to get an access token. I've read various articles and posts online which seem to give conflicting advice about what the 'scope' parameter should be when calling AcquireTokenForClient. Some say https://graph.microsoft.com/.default, others say https://outlook.office.com/.default or https://outlook.office365.com/.default. Others seem to suggest that it should be Mail.Read rather than .Default. I've tried all of the above without success. Can anyone tell me what the correct value for 'scope' is?
I assume that you have registered your app for an Office 365 tenant. We are using EWS with modern authentication successfully for some time now. To access the users' mailboxes in your tenant using OAuth authentication you have to grant the registered application the API permission Exchange - full_access_as_app and use https://outlook.office.com/.default as scope.
var clientApp = ConfidentialClientApplicationBuilder
.Create("applicationId")
.WithTenantId("tenantId")
.WithClientSecret("secret")
.Build();
var authenticationResult = await clientApp.AcquireTokenForClient(new[] { "https://outlook.office.com/.default" }).ExecuteAsync();
var accessToken = authenticationResult.AccessToken;
Then add the token to the authorization header of the EWS requests.
Scenario
I have a Dynamics 365 v9 organisation hosted online. I have a set of Azure Functions hosted in an Azure Function App on a different tenant to my Dynamics organisation.
I've created web hooks using the Dynamics Plugin Registration Tool, which at certain events (such as when a Contact is created in Dynamics), POST data to my Azure Functions via their endpoint URLs.
Authentication between Dynamics 365 and my Azure Functions is achieved by passing an x-functions-key value in the HTTP request's authentication HttpHeader.
The Azure Functions receive data from the event in Dynamics in the form of a RemoteExecutionContext which I can read using the following code:
using System.Net;
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
var jsonContent = await req.Content.ReadAsStringAsync();
log.Info(jsonContent);
return req.CreateResponse(HttpStatusCode.OK);
}
Question
How can the Azure Function then authenticate back with the calling Dynamics 365 organisation to read and write data?
What I've tried
Xrm Tooling
The simplest way to authenticate would be to use the CrmServiceClient from Microsoft.Xrm.Tooling.Connector.dll. However, I don't necessarily have a username and password to provide the CrmServiceClient's constructor. Perhaps credentials could be passed securely via the HTTP POST request?
Application User
I've tried registering an Application User in Dynamics. I supply the client id and client secret to my Azure Functions, but authentication fails because the user is in a different tenant to my Azure Functions.
Considered Solutions
One object of the received jsonContent string is called ParentContext . Perhaps this can be reused to authenticate back with the calling Dynamics organisation.
Marc Schweigert has recommended using S2S and has provided a sample to his AzureFunctionApp repository. If I can get this approach to work I'll post the solution here.
I wouldn't have thought you can sensibly use the 'real' users credentials to connect to CRM.
I would use a service account to connect back into CRM. Create a new CRM
user especially for this purpose, if you make the user non-interactive you shouldn't consume a license. You can then use the credentials of that service account to connect to CRM using CrmServiceClient. Alternatively have a look at Server to Server authentication.
If you are able to deliver a user id to your Function App, you use the service account to impersonate 'real' users via the CRM web services.
To impersonate a user, set the CallerId property on an instance of
OrganizationServiceProxy before calling the service’s Web methods.
I have done something similar recently, but without relying on the Azure subscription authentication functionality for connecting back into D365. In my case calls were coming to Azure functions from other places, but the connection back is no different. Authentication does NOT pass through in any of these cases. If an AAD user authenticates to your Function application, you still need to connect to D365 using an application user, and then impersonate the user that called you.
First, make sure that the application you registered in Azure AD under App Registrations is of the type "Web app / API" and not "Native". Edit the settings of the registered app and ensure the following:
Take not of the Application ID, which I'll refer to later as appId.
Under "API Access - Required Permissions", add Dynamics CRM Online (Microsoft.CRM) and NOT Dynamics 365.
Under "API Access - Keys", create a key with an appropriate expiry. You can create multiple keys if you have multiple functions/applications connecting back as this "App". I'll refer to this key as "clientSecret" later.
If the "Keys" option isn't available, you've registered a Native app.
I stored the appId and clientSecret in the application configuration section of the Function App, and accessed them using the usual System.Configuration.ConfigurationManager.AppSettings collection.
The below examples use a call to AuthenticationParameters to find the authority and resource URLs, but you could just as easily build those URLs manually using the countless examples online. I find this will just update itself if they ever change, so less work later.
These are simple examples and I'm glossing over the need to refresh tokens and all those things.
Then to access D365 using OData:
string odataUrl = "https://org.crm6.dynamics.com/api/data/v8.2/"; // trailing slash actually matters
string appId = "some-guid";
string clientSecret = "some key";
AuthenticationParameters authArg = AuthenticationParameters.CreateFromResourceUrlAsync(new Uri(odataUrl)).Result;
AuthenticationContext authCtx = new AuthenticationContext(authArg.Authority);
AuthenticationResult authRes = authCtx.AcquireTokenAsync(authArg.Resource, new ClientCredential(appId, clientSecret)).Result;
using (HttpClient client = new HttpClient()) {
client.TimeOut = TimeSpan.FromMinutes (2);
client.DefaultRequestHeaders.Add("Authorization", authRes.CreateAuthorizationHeader ());
using (HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Get, $"{odataUrl}accounts?$select=name&$top=10")) {
using (HttpResponseMessage res = client.SendAsync(req).Result) {
if (res.IsSuccessStatusCode) {
Console.WriteLine(res.Content.ReadAsStringAsync().Result);
}
else {
// cry
}
}
}
}
If you want to access D365 using the Organization service, and LINQ, use the following. The two main parts that took me a while to find out are the format of that odd looking organization.svc URL, and using Microsoft.Xrm.Sdk.WebServiceClient.OrganizationWebProxyClient instead of Tooling:
string odataUrl = "https://org.crm6.dynamics.com/xrmservices/2011/organization.svc/web?SdkClientVersion=8.2"; // don't question the url, just accept it.
string appId = "some-guid";
string clientSecret = "some key";
AuthenticationParameters authArg = AuthenticationParameters.CreateFromResourceUrlAsync(new Uri(odataUrl)).Result;
AuthenticationContext authCtx = new AuthenticationContext(authArg.Authority);
AuthenticationResult authRes = authCtx.AcquireTokenAsync(authArg.Resource, new ClientCredential(appId, clientSecret)).Result;
using (OrganizationWebProxyClient webProxyClient = new OrganizationWebProxyClient(new Uri(orgSvcUrl), false)) {
webProxyClient.HeaderToken = authRes.AccessToken;
using (OrganizationServiceContext ctx = new OrganizationServiceContext((IOrganizationService)webProxyClient)) {
var accounts = (from i in ctx.CreateQuery("account") orderby i["name"] select i).Take(10);
foreach (var account in accounts)
Console.WriteLine(account["name"]);
}
}
Not sure what context you get back in your Webhook registration, not tried that yet, but just making sure that there's a bearer token in the Authorization header generally does it, and the two examples above inject it in different ways so you should be able to splice together what's needed from here.
This is something I'm curious about as well but I have not had the opportunity to experiment on this.
For your second option have you registered the application and granted consent in the target AAD?
https://learn.microsoft.com/en-us/dynamics365/customer-engagement/developer/use-multi-tenant-server-server-authentication
When they grant consent, your registered application will be added to the Azure AD Enterprise applications list and it is available to the users of the Azure AD tenant.
Only after an administrator has granted consent, you must then create the application user in the subscriber’s Dynamics 365 tenant.
I believe the root of the access issue is related to the Application's Service Principal Object (the Object local to the target Tenant)
Service Principal Object
https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-application-objects#service-principal-object
In order to access resources that are secured by an Azure AD tenant, the entity that requires access must be represented by a security principal. This is true for both users (user principal) and applications (service principal). The security principal defines the access policy and permissions for the user/application in that tenant. This enables core features such as authentication of the user/application during sign-in, and authorization during resource access.
Consider the application object as the global representation of your application for use across all tenants, and the service principal as the local representation for use in a specific tenant.
HTH
-Chris
Using S2S you can use AcquireToken to retrieve the Bearer
var clientcred = new ClientCredential(clientId, clientSecret);
AuthenticationContext authContext = new AuthenticationContext(aadInstance, false);
AuthenticationResult result = authContext.AcquireToken(organizationUrl, clientcred);
token = result.AccessToken;
ExpireDate = result.ExpiresOn.DateTime;
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
We're trying to authenticate to our hosted TFS service account in c# using TeamFoundationServer .net control, here is my code :
NetworkCredential tfsCredential = new NetworkCredential(username, password);
TeamFoundationServer tfsServer = new TeamFoundationServer(tfsAddress, tfsCredential);
tfsServer.Authenticate();
Note that this is not an on-premises TFS server, it is the hosted TFS service at tfspreview.com and we try to sign-in with windows live account and with alternate authentication credentials but every time we try to authenticate, internet explorer open in a new windows and ask for credentials.
If we use the IE prompt to connect it works but we want to store the credentials and connect to the server without asking for the credentials every time,
You can either configure basic authentication under your profile or you can use a service credential. It all depends on what sort of permission you need. The basic auth operates under a user account which tends to be bad practice while the service account had elevated permissions.
Configure basic authentication for TF Service
For basic user authentication you should connect to TF Service and open your profile as indicated. There is a "Credentials" tab on your profile which will let you configure those credentials. This is good for per/user access through the API but is not good if you want to run things through a server or service.
Retrieve TFS Service Credentials
I created an application called the TFS Service Credential Viewer that allows you to retrieve the service credentials for your TF Service instance. This is the same thing that the Build & Test servers do when you configure them locally to work against the cloud.
I hope this helps...
You can try with this code based on impersonation of server
var serverUrl = "";
ICredentials credentials = new NetworkCredential(username, password, domain);
ICredentialsProvider TFSProxyCredentials = new NetworkCredentialsProvider(credentials);
TfsTeamProjectCollection currentCollection = new TfsTeamProjectCollection(new Uri(serverUrl), credentials);
// Get the TFS Identity Management Service
IIdentityManagementService identityManagementService = currentCollection.GetService<IIdentityManagementService>();
// Look up the user that we want to impersonate
TeamFoundationIdentity identity = identityManagementService.ReadIdentity(IdentitySearchFactor.AccountName, username, MembershipQuery.None, ReadIdentityOptions.None);
// Open collection impersonated
TfsTeamProjectCollection tfs = new TfsTeamProjectCollection(new Uri(serverUrl), credentials, TFSProxyCredentials, identity.Descriptor);
//For example we can access to service WorkItemStore
var workItemStore = tfs.GetService<WorkItemStore>();
Tfspreview.com now supports basic authentication which would eliminate IE being displayed at all. See here for details on how to set this up for your tfspreview.com and then use the username and password you configured.
I wrtitting a windows service. This service has to connect SharePoint service and get data different for each user. sharePoint service return data based on user credentials whitch are set before calling service.
How can I get user credentials by user name and user domain if the service can be run under any account that needs to get this credentials?
Depending on how you add the sharepoint service (web reference or service reference) there are different methods to send credentials with the request.
With a web reference you would add a NetworkCredentials object along the lines of:
SomerService ws = ... //instantiate your service
ws.PreAuthenticate = true;
ws.Credentials = new System.Net.NetworkCredentials("username","pw","domain");
if it´s a service reference with wsHttpBinding then something like this:
Service client = ... //instantiate your service
client.ClientCredentials.UserName.UserName = "domain\\username";
client.ClientCredentials.UserName.Password = "password";
Then you need to store the username/password for each user you are retrieving content for.
If this is a pass-thru service where the client accesses your service and it access sharepoint on the users behalf you have to set up Kerberos authentication and allow impersonation to pass thru. Maybe you could expand on what you are trying to accomplish.