Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Сервисы Azure DevOps | Azure DevOps Server | Azure DevOps Server 2022
Узнайте, как расширить и интегрировать Azure DevOps с помощью клиентских библиотек .NET с современными методами проверки подлинности и безопасными методами программирования.
Подсказка
Вы можете использовать ИИ, чтобы помочь с этой задачей позже в этой статье или ознакомиться с включение помощи ИИ в Azure DevOps MCP Server, чтобы начать работу.
Предпосылки
Обязательные пакеты NuGet:
- Microsoft.TeamFoundationServer.Client — ОСНОВНЫЕ API Azure DevOps
- Microsoft.VisualStudio.Services.Client — подключение и проверка подлинности
- Microsoft.VisualStudio.Services.InteractiveClient — интерактивные потоки проверки подлинности
Рекомендации по проверке подлинности:
- приложения, размещенные в Azure: используйте управляемые удостоверения
- Конвейеры CI/CD: использование служебных принципалов
- Интерактивные приложения: используйте аутентификацию Microsoft Entra
- Устаревшие сценарии: используйте токены личного доступа
Это важно
В этой статье показано несколько методов проверки подлинности для различных сценариев. Выберите наиболее подходящий метод на основе требований к среде развертывания и безопасности.
Пример основного подключения и рабочего элемента
В этом комплексном примере демонстрируются рекомендации по подключению к Azure DevOps и работе с рабочими элементами:
using Microsoft.TeamFoundation.WorkItemTracking.WebApi;
using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models;
using Microsoft.VisualStudio.Services.Client;
using Microsoft.VisualStudio.Services.Common;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
/// <summary>
/// Demonstrates secure Azure DevOps integration with proper error handling and resource management
/// </summary>
public class AzureDevOpsService
{
private readonly VssConnection _connection;
private readonly WorkItemTrackingHttpClient _witClient;
public AzureDevOpsService(string organizationUrl, VssCredentials credentials)
{
// Create connection with proper credential management
_connection = new VssConnection(new Uri(organizationUrl), credentials);
// Get work item tracking client (reused for efficiency)
_witClient = _connection.GetClient<WorkItemTrackingHttpClient>();
}
/// <summary>
/// Creates a work item query, executes it, and returns results with proper error handling
/// </summary>
public async Task<IEnumerable<WorkItem>> GetNewBugsAsync(string projectName)
{
try
{
// Get query hierarchy with proper depth control
var queryHierarchyItems = await _witClient.GetQueriesAsync(projectName, depth: 2);
// Find 'My Queries' folder using safe navigation
var myQueriesFolder = queryHierarchyItems
.FirstOrDefault(qhi => qhi.Name.Equals("My Queries", StringComparison.OrdinalIgnoreCase));
if (myQueriesFolder == null)
{
throw new InvalidOperationException("'My Queries' folder not found in project.");
}
const string queryName = "New Bugs Query";
// Check if query already exists
var existingQuery = myQueriesFolder.Children?
.FirstOrDefault(qhi => qhi.Name.Equals(queryName, StringComparison.OrdinalIgnoreCase));
QueryHierarchyItem query;
if (existingQuery == null)
{
// Create new query with proper WIQL
query = new QueryHierarchyItem
{
Name = queryName,
Wiql = @"
SELECT [System.Id], [System.WorkItemType], [System.Title],
[System.AssignedTo], [System.State], [System.Tags]
FROM WorkItems
WHERE [System.TeamProject] = @project
AND [System.WorkItemType] = 'Bug'
AND [System.State] = 'New'
ORDER BY [System.CreatedDate] DESC",
IsFolder = false
};
query = await _witClient.CreateQueryAsync(query, projectName, myQueriesFolder.Name);
}
else
{
query = existingQuery;
}
// Execute query and get results
var queryResult = await _witClient.QueryByIdAsync(query.Id);
if (!queryResult.WorkItems.Any())
{
return Enumerable.Empty<WorkItem>();
}
// Batch process work items for efficiency
const int batchSize = 100;
var allWorkItems = new List<WorkItem>();
for (int skip = 0; skip < queryResult.WorkItems.Count(); skip += batchSize)
{
var batch = queryResult.WorkItems.Skip(skip).Take(batchSize);
var workItemIds = batch.Select(wir => wir.Id).ToArray();
// Get detailed work item information
var workItems = await _witClient.GetWorkItemsAsync(
ids: workItemIds,
fields: new[] { "System.Id", "System.Title", "System.State",
"System.AssignedTo", "System.CreatedDate" });
allWorkItems.AddRange(workItems);
}
return allWorkItems;
}
catch (Exception ex)
{
// Log error appropriately in real applications
throw new InvalidOperationException($"Failed to retrieve work items: {ex.Message}", ex);
}
}
/// <summary>
/// Properly dispose of resources
/// </summary>
public void Dispose()
{
_witClient?.Dispose();
_connection?.Dispose();
}
}
Методы аутентификации
проверка подлинности Microsoft Entra (рекомендуется)
Для приложений, поддерживающих интерактивную проверку подлинности или имеющих маркеры Microsoft Entra:
using Microsoft.VisualStudio.Services.Client;
using Microsoft.VisualStudio.Services.Common;
/// <summary>
/// Authenticate using Microsoft Entra ID credentials
/// Recommended for interactive applications and modern authentication scenarios
/// </summary>
public static VssConnection CreateEntraConnection(string organizationUrl, string accessToken)
{
// Use Microsoft Entra access token for authentication
var credentials = new VssOAuthAccessTokenCredential(accessToken);
return new VssConnection(new Uri(organizationUrl), credentials);
}
/// <summary>
/// For device code flow (cross-platform interactive authentication)
/// Works with .NET Core, .NET 5+, and .NET Framework
/// </summary>
public static async Task<VssConnection> CreateEntraDeviceCodeConnectionAsync(
string organizationUrl, string clientId, string tenantId)
{
var app = PublicClientApplicationBuilder
.Create(clientId)
.WithAuthority(new Uri($"https://login.microsoftonline.com/{tenantId}"))
.Build();
var result = await app
.AcquireTokenWithDeviceCode(
new[] { "https://app.vssps.visualstudio.com/.default" },
callback =>
{
Console.WriteLine(callback.Message);
return Task.CompletedTask;
})
.ExecuteAsync();
var credentials = new VssOAuthAccessTokenCredential(result.AccessToken);
return new VssConnection(new Uri(organizationUrl), credentials);
}
Аутентификация служебного принципала
Для автоматизированных сценариев и конвейеров CI/CD:
using Microsoft.Identity.Client;
using Microsoft.VisualStudio.Services.Client;
/// <summary>
/// Authenticate using service principal with certificate (most secure)
/// Recommended for production automation scenarios
/// </summary>
public static async Task<VssConnection> CreateServicePrincipalConnectionAsync(
string organizationUrl,
string clientId,
string tenantId,
X509Certificate2 certificate)
{
try
{
// Create confidential client application with certificate
var app = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithCertificate(certificate)
.WithAuthority(new Uri($"https://login.microsoftonline.com/{tenantId}"))
.Build();
// Acquire token for Azure DevOps
var result = await app
.AcquireTokenForClient(new[] { "https://app.vssps.visualstudio.com/.default" })
.ExecuteAsync();
// Create connection with acquired token
var credentials = new VssOAuthAccessTokenCredential(result.AccessToken);
return new VssConnection(new Uri(organizationUrl), credentials);
}
catch (Exception ex)
{
throw new AuthenticationException($"Failed to authenticate service principal: {ex.Message}", ex);
}
}
/// <summary>
/// Service principal with client secret (less secure than certificate)
/// </summary>
public static async Task<VssConnection> CreateServicePrincipalSecretConnectionAsync(
string organizationUrl,
string clientId,
string tenantId,
string clientSecret)
{
var app = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithClientSecret(clientSecret)
.WithAuthority(new Uri($"https://login.microsoftonline.com/{tenantId}"))
.Build();
var result = await app
.AcquireTokenForClient(new[] { "https://app.vssps.visualstudio.com/.default" })
.ExecuteAsync();
var credentials = new VssOAuthAccessTokenCredential(result.AccessToken);
return new VssConnection(new Uri(organizationUrl), credentials);
}
Аутентификация управляемой идентификации
Для Azure размещенных приложений (рекомендуется для облачных сценариев):
using Azure.Identity;
using Azure.Core;
using Microsoft.VisualStudio.Services.Client;
/// <summary>
/// Authenticate using managed identity (most secure for Azure-hosted apps)
/// No credentials to manage - Azure handles everything automatically
/// </summary>
public static async Task<VssConnection> CreateManagedIdentityConnectionAsync(string organizationUrl)
{
try
{
// Use system-assigned managed identity
var credential = new ManagedIdentityCredential();
// Acquire token for Azure DevOps
var tokenRequest = new TokenRequestContext(new[] { "https://app.vssps.visualstudio.com/.default" });
var tokenResponse = await credential.GetTokenAsync(tokenRequest);
// Create connection with managed identity token
var credentials = new VssOAuthAccessTokenCredential(tokenResponse.Token);
return new VssConnection(new Uri(organizationUrl), credentials);
}
catch (Exception ex)
{
throw new AuthenticationException($"Failed to authenticate with managed identity: {ex.Message}", ex);
}
}
/// <summary>
/// Use user-assigned managed identity with specific client ID
/// </summary>
public static async Task<VssConnection> CreateUserAssignedManagedIdentityConnectionAsync(
string organizationUrl,
string managedIdentityClientId)
{
var credential = new ManagedIdentityCredential(managedIdentityClientId);
var tokenRequest = new TokenRequestContext(new[] { "https://app.vssps.visualstudio.com/.default" });
var tokenResponse = await credential.GetTokenAsync(tokenRequest);
var credentials = new VssOAuthAccessTokenCredential(tokenResponse.Token);
return new VssConnection(new Uri(organizationUrl), credentials);
}
Интерактивная проверка подлинности
Для настольных приложений, требующих авторизации пользователей:
платформа .NET
/// <summary>
/// Interactive authentication with Visual Studio sign-in prompt
/// .NET Framework only - not supported in .NET Core/.NET 5+
/// </summary>
public static VssConnection CreateInteractiveConnection(string organizationUrl)
{
var credentials = new VssClientCredentials();
return new VssConnection(new Uri(organizationUrl), credentials);
}
.NET Core / .NET 5+
Используйте код устройства MSAL или поток системного браузера для кроссплатформенной интерактивной проверки подлинности. См. пример CreateEntraDeviceCodeConnectionAsync в аутентификация Microsoft Entra или используйте системный браузер:
var app = PublicClientApplicationBuilder
.Create(clientId)
.WithRedirectUri("http://localhost")
.WithAuthority(new Uri($"https://login.microsoftonline.com/{tenantId}"))
.Build();
var result = await app
.AcquireTokenInteractive(new[] { "https://app.vssps.visualstudio.com/.default" })
.ExecuteAsync();
var credentials = new VssOAuthAccessTokenCredential(result.AccessToken);
var connection = new VssConnection(new Uri(organizationUrl), credentials);
Проверка подлинности токена личного доступа (устаревший)
Это важно
Рассмотрите возможность использования более безопасных токенов Microsoft Entra вместо более рискованных персональных токенов доступа. Дополнительные сведения см. в разделе "Сокращение использования PAT". Просмотрите рекомендации по проверке подлинности , чтобы выбрать правильный механизм проверки подлинности для ваших потребностей.
Если необходимо использовать PAT, см. раздел «Использование личных маркеров доступа» для их создания. Затем передайте его в виде VssBasicCredential:
var credentials = new VssBasicCredential(string.Empty, personalAccessToken);
var connection = new VssConnection(new Uri(organizationUrl), credentials);
Полные примеры использования
функция Azure с управляемым удостоверением
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;
public class AzureDevOpsFunction
{
private readonly ILogger<AzureDevOpsFunction> _logger;
public AzureDevOpsFunction(ILogger<AzureDevOpsFunction> logger)
{
_logger = logger;
}
[Function("ProcessWorkItems")]
public async Task<string> ProcessWorkItems(
[TimerTrigger("0 0 8 * * MON")] TimerInfo timer)
{
try
{
var organizationUrl = Environment.GetEnvironmentVariable("AZURE_DEVOPS_ORG_URL");
var projectName = Environment.GetEnvironmentVariable("AZURE_DEVOPS_PROJECT");
// Use managed identity for secure authentication
using var connection = await CreateManagedIdentityConnectionAsync(organizationUrl);
using var service = new AzureDevOpsService(organizationUrl, connection.Credentials);
var workItems = await service.GetNewBugsAsync(projectName);
_logger.LogInformation($"Processed {workItems.Count()} work items");
return $"Successfully processed {workItems.Count()} work items";
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to process work items");
throw;
}
}
}
Консольное приложение с служебным принципалом
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
class Program
{
static async Task Main(string[] args)
{
// Configure logging and configuration
var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddEnvironmentVariables()
.Build();
using var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole());
var logger = loggerFactory.CreateLogger<Program>();
try
{
var settings = configuration.GetSection("AzureDevOps");
var organizationUrl = settings["OrganizationUrl"];
var projectName = settings["ProjectName"];
var clientId = settings["ClientId"];
var tenantId = settings["TenantId"];
var clientSecret = settings["ClientSecret"]; // Better: use Key Vault
// Authenticate with service principal
using var connection = await CreateServicePrincipalSecretConnectionAsync(
organizationUrl, clientId, tenantId, clientSecret);
using var service = new AzureDevOpsService(organizationUrl, connection.Credentials);
// Process work items
var workItems = await service.GetNewBugsAsync(projectName);
foreach (var workItem in workItems)
{
Console.WriteLine($"Bug {workItem.Id}: {workItem.Fields["System.Title"]}");
}
logger.LogInformation($"Successfully processed {workItems.Count()} work items");
}
catch (Exception ex)
{
logger.LogError(ex, "Application failed");
Environment.Exit(1);
}
}
}
Лучшие практики
Вопросы безопасности
Управление учетными данными:
- Никогда не прописывайте учетные данные жестко в исходном коде
- Использование Azure Key Vault для хранения секретов
- Предпочитайте управляемые удостоверения для приложений, размещенных в Azure
- Используйте сертификаты вместо секретов клиента для сервисных принципалов.
- Регулярно сменяйте учетные данные, следуя политикам безопасности
Управление доступом:
- Применение принципа наименьших привилегий
- Используйте определенные области при получении токенов
- Мониторинг и аудит событий проверки подлинности
- Реализация политик условного доступа при необходимости
Оптимизация производительности
Управление подключениями:
- Повторное использование экземпляров VssConnection в операциях
- Пул HTTP-клиентов через объект подключения
- Реализовать корректные шаблоны утилизации
- Настройка времени ожидания соответствующим образом
Пакетные операции:
- Обработка рабочих элементов в пакетах (рекомендуется: 100 элементов)
- Использование параллельной обработки для независимых операций
- Реализуйте логику повторных попыток с экспоненциальным замедлением
- Кэшируйте часто обращаемые данные при необходимости
Обработка ошибок
public async Task<T> ExecuteWithRetryAsync<T>(Func<Task<T>> operation, int maxRetries = 3)
{
var retryCount = 0;
var baseDelay = TimeSpan.FromSeconds(1);
while (retryCount < maxRetries)
{
try
{
return await operation();
}
catch (Exception ex) when (IsTransientError(ex) && retryCount < maxRetries - 1)
{
retryCount++;
var delay = TimeSpan.FromMilliseconds(baseDelay.TotalMilliseconds * Math.Pow(2, retryCount));
await Task.Delay(delay);
}
}
// Final attempt without catch
return await operation();
}
private static bool IsTransientError(Exception ex)
{
return ex is HttpRequestException ||
ex is TaskCanceledException ||
(ex is VssServiceException vssEx && vssEx.HttpStatusCode >= 500);
}
Руководство по миграции
От PATs до современной проверки подлинности
Шаг 1. Оценка текущего использования
- Определение всех приложений с помощью PATs
- Определение сред развертывания (Azure и локальной среды)
- Оценка требований к безопасности
Шаг 2. Выбор метода замены
- Размещено в Azure: миграция на управляемые удостоверения
- Конвейеры CI/CD: использование сервисных принципов
- Interactive apps: реализация проверки подлинности Microsoft Entra
- Настольные приложения: рассмотрите поток авторизации устройства
Шаг 3. Реализация
- Обновление кода проверки подлинности с помощью предыдущих примеров
- Тщательное тестирование в среде разработки
- Постепенное развертывание в рабочей среде
- Мониторинг проблем с проверкой подлинности
Подробные рекомендации по миграции см. в разделе Замена PATs на токены Microsoft Entra.
Использование ИИ для создания кода клиента .NET
Если у вас есть Azure DevOps MCP Server подключен к агенту ИИ в режиме агента, можно использовать запросы естественного языка для создания кода клиентской библиотеки .NET для Azure DevOps.
| задачи | Пример запроса |
|---|---|
| Создание запроса рабочего элемента | Write C# code using the Azure DevOps .NET client library to create a work item query, execute it, and process the results |
| Перечисление репозиториев и коммитов Git | Show me how to use the Azure DevOps GitHttpClient to list repositories and get recent commits in a project |
| Подключение с помощью управляемой идентификации | Create a .NET application that connects to Azure DevOps using managed identity and retrieves build definitions |
| Интерактивный вход в Entra | Write code to authenticate to Azure DevOps using the .NET client library with interactive Microsoft Entra sign-in |
| Управление параметрами команды | Write C# code using the Azure DevOps .NET client to get team members and iteration paths for a project |
| Создание конвейера | Show me how to trigger a pipeline run in Azure DevOps using the .NET client libraries with service principal authentication |
Замечание
Режим агента и сервер MCP используют естественный язык, чтобы настроить эти запросы или задать дальнейшие вопросы, чтобы уточнить результаты.
Связанные ресурсы
- Руководство по аутентификации для Azure DevOps
- Идентификаторы служб и управляемые удостоверения
- Аутентификация Microsoft Entra
- Основные понятия клиентских библиотек .NET
- примеры проверки подлинности Azure DevOps
- Документация по платформе идентификаций Майкрософт