Reputation: 5
While running below Azure CLI command, I am able to login and perform Azure operations. Please note the application is not attached to any role, so I am passing --allow-no-subscriptions
here.
az login --service-principal --username $app_id --password $password
--tenant $tenant_id --allow-no-subscriptions
However when I try to run the same command in this C# code, I am getting an error:
services.AddTransient(x =>
{
var options = x.GetService<IOptions<SynchronizationOptions>>().Value;
var credentials = new ClientSecretCredential(options.AzureWorkItemSettings.tenantId, options.AzureWorkItemSettings.clientId, options.AzureWorkItemSettings.clientSecret);
var accessToken = credentials.GetToken(new Azure.Core.TokenRequestContext(new[] { options.AzureWorkItemSettings.azureDevopsAppScope }));
var vssAadToken = new VssAadToken("Bearer", accessToken);
var vssAadCredentials = new VssAadCredential(vssAadToken);
VssConnection connection = new VssConnection(options.AzureWorkItemSettings.Uri, vssAadCredentials);
return connection.GetClient<WorkItemTrackingHttpClient>();
});
Here is the error - it seems it's because --allow-no-subscriptions
is not passed in the C# code, but I am not able to see any option where I could pass --allow-no-subscriptions
from my C# code. Please let me know how to fix this error?
Unhandled exception. Azure.Identity.AuthenticationFailedException: ClientSecretCredential authentication failed:
MSAL. NetCore.4.67.2.0.MsalUiRequiredException:
ErrorCode: invalid_grant
Microsoft.Identity.Client.MsalUiRequiredException: AADSTS501051: Application 'xxxxxxxxx' is not assigned to a role for the application 'xxxxxxxxx'.
Upvotes: 0
Views: 86
Reputation: 16064
Note : az login CLI command doesn't have this issue because it allows no subscriptions to be required (--allow-no-subscriptions). This functionality isn't exposed in the
ClientSecretCredential
API and hence the error.
To use ClientSecretCredential
flow to access Azure DevOps, you need to add the service principal to the Azure DevOps organization. Refer this MsDoc
Hence to resolve the error, Go to Azure DevOps Portal > Organization Settings > Permissions. Find your Service Principal (under Security or User section) and add it to an appropriate group (e.g., Project Administrators, Contributors, or a custom security group with sufficient access:
Now I used the below code to used to query work items:
Make sure to pass scope as 499b84ac-1321-427f-aa17-267ca6975798/.default
public class Program
{
public static void Main(string[] args)
{
// Set up Dependency Injection (DI) container
var serviceProvider = new ServiceCollection()
.Configure<SynchronizationOptions>(options =>
{
options.AzureWorkItemSettings = new AzureWorkItemSettings
{
TenantId = "TenantID",
ClientId = "ClientID",
ClientSecret = "Secret",
AzureDevOpsOrg = "rukorg",
AzureDevOpsAppScope = "499b84ac-1321-427f-aa17-267ca6975798/.default",
Uri = new Uri("https://dev.azure.com/rukorg")
};
})
.AddTransient(x =>
{
var options = x.GetService<IOptions<SynchronizationOptions>>().Value;
var credentials = new ClientSecretCredential(options.AzureWorkItemSettings.TenantId, options.AzureWorkItemSettings.ClientId, options.AzureWorkItemSettings.ClientSecret);
var accessToken = credentials.GetToken(new TokenRequestContext(new[] { options.AzureWorkItemSettings.AzureDevOpsAppScope }));
var vssAadToken = new VssAadToken("Bearer", accessToken.Token);
var vssAadCredentials = new VssAadCredential(vssAadToken);
VssConnection connection = new VssConnection(options.AzureWorkItemSettings.Uri, vssAadCredentials);
return connection.GetClient<WorkItemTrackingHttpClient>();
})
.BuildServiceProvider();
// Get the WorkItemTrackingHttpClient from DI container
var workItemTrackingClient = serviceProvider.GetService<WorkItemTrackingHttpClient>();
// Call your method to query work items
var workItemService = new WorkItemService(workItemTrackingClient);
workItemService.GetWorkItemsAsync().Wait();
}
}
public class WorkItemService
{
private readonly WorkItemTrackingHttpClient _workItemTrackingClient;
public WorkItemService(WorkItemTrackingHttpClient workItemTrackingClient)
{
_workItemTrackingClient = workItemTrackingClient;
}
public async Task GetWorkItemsAsync()
{
// Define your WIQL query to fetch work items
var wiql = new Wiql
{
Query = "SELECT [System.Id], [System.Title], [System.State] FROM WorkItems"
};
// Execute the WIQL query to get work items
var result = await _workItemTrackingClient.QueryByWiqlAsync(wiql);
Console.WriteLine("Work Items:");
foreach (var workItemReference in result.WorkItems)
{
// Fetch the full details of each work item
var workItemDetails = await _workItemTrackingClient.GetWorkItemAsync(workItemReference.Id);
Console.WriteLine($"ID: {workItemDetails.Id}, Title: {workItemDetails.Fields["System.Title"]}, State: {workItemDetails.Fields["System.State"]}");
}
}
}
public class AzureWorkItemSettings
{
public string TenantId { get; set; }
public string ClientId { get; set; }
public string ClientSecret { get; set; }
public string AzureDevOpsOrg { get; set; }
public string AzureDevOpsAppScope { get; set; }
public Uri Uri { get; set; }
}
public class SynchronizationOptions
{
public AzureWorkItemSettings AzureWorkItemSettings { get; set; }
}
Upvotes: 0