Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Сервисы Azure DevOps | Azure DevOps Server | Azure DevOps Server 2022
Создание рабочих элементов программным способом — это распространенный сценарий автоматизации в службах Azure DevOps. В этой статье показано, как создать ошибку (или любой рабочий элемент), используя клиентские библиотеки .NET с современными методами проверки подлинности.
Подсказка
Вы можете использовать ИИ, чтобы помочь с этой задачей позже в этой статье или ознакомиться с включение помощи ИИ в Azure DevOps MCP Server, чтобы начать работу.
Необходимые условия
| Категория | Требования |
|---|---|
| Azure DevOps |
-
Организация — доступ к проекту, в котором можно создавать рабочие элементы |
| Аутентификация | Выберите один из следующих вариантов: - Microsoft Entra ID аутентификация (рекомендуется) - Личный маркер доступа (PAT) ( для тестирования) |
| среды разработки | Среда разработки C#. Можно использовать Visual Studio |
Это важно
Рассмотрите возможность использования более безопасных токенов Microsoft Entra вместо более рискованных персональных токенов доступа. Дополнительные сведения см. в разделе "Сокращение использования PAT". Просмотрите рекомендации по проверке подлинности , чтобы выбрать правильный механизм проверки подлинности для ваших потребностей.
Варианты проверки подлинности
В этой статье показано несколько методов проверки подлинности для различных сценариев:
проверка подлинности Microsoft Entra ID (рекомендуется для пользовательских приложений)
Для рабочих приложений с взаимодействием с пользователем используйте проверку подлинности Microsoft Entra ID:
<PackageReference Include="Microsoft.TeamFoundationServer.Client" Version="19.232.1" />
<PackageReference Include="Microsoft.VisualStudio.Services.InteractiveClient" Version="19.232.1" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.67.2" />
Проверка подлинности субъекта-службы (рекомендуется для автоматизации)
Для автоматизированных сценариев, конвейеров CI/CD и серверных приложений:
<PackageReference Include="Microsoft.TeamFoundationServer.Client" Version="19.232.1" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.67.2" />
Проверка подлинности управляемого удостоверения (рекомендуется для приложений, размещенных в Azure)
Для приложений, работающих в службах Azure (функции, служба приложений и т. д.):
<PackageReference Include="Microsoft.TeamFoundationServer.Client" Version="19.232.1" />
<PackageReference Include="Azure.Identity" Version="1.13.1" />
Проверка подлинности токена личного доступа
Для сценариев разработки и тестирования:
<PackageReference Include="Microsoft.TeamFoundationServer.Client" Version="19.232.1" />
Примеры кода C#
В следующих примерах показано, как создавать рабочие элементы с помощью различных методов проверки подлинности.
Пример 1. Проверка подлинности Microsoft Entra ID (интерактивный режим)
Замечание
Для использования в этом примере класса VssAadCredential требуется пакет Microsoft.VisualStudio.Services.InteractiveClient и поддержка .NET Framework. Для приложений .NET Core/.NET 5+ используйте подход на основе MSAL, показанный в Example 2 (субъект-служба) или Example 3 (управляемое удостоверение) с VssOAuthAccessTokenCredential.
// NuGet packages:
// Microsoft.TeamFoundationServer.Client
// Microsoft.VisualStudio.Services.InteractiveClient
// Microsoft.Identity.Client
using System;
using System.Threading.Tasks;
using Microsoft.TeamFoundation.WorkItemTracking.WebApi;
using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models;
using Microsoft.VisualStudio.Services.Common;
using Microsoft.VisualStudio.Services.WebApi;
using Microsoft.VisualStudio.Services.WebApi.Patch;
using Microsoft.VisualStudio.Services.WebApi.Patch.Json;
public class EntraIdBugCreator
{
private readonly Uri uri;
/// <summary>
/// Initializes a new instance using Microsoft Entra ID authentication.
/// </summary>
/// <param name="orgName">Your Azure DevOps organization name</param>
public EntraIdBugCreator(string orgName)
{
this.uri = new Uri($"https://dev.azure.com/{orgName}");
}
/// <summary>
/// Create a bug using Microsoft Entra ID authentication.
/// </summary>
/// <param name="project">The name of your project</param>
/// <param name="title">Bug title</param>
/// <param name="reproSteps">Reproduction steps</param>
/// <param name="priority">Priority level (1-4)</param>
/// <param name="severity">Severity level</param>
/// <returns>The created WorkItem</returns>
public async Task<WorkItem> CreateBugAsync(string project, string title, string reproSteps, int priority = 2, string severity = "3 - Medium")
{
// Use Microsoft Entra ID authentication
var credentials = new VssAadCredential();
var patchDocument = new JsonPatchDocument();
// Add required and optional fields
patchDocument.Add(new JsonPatchOperation()
{
Operation = Operation.Add,
Path = "/fields/System.Title",
Value = title
});
if (!string.IsNullOrEmpty(reproSteps))
{
patchDocument.Add(new JsonPatchOperation()
{
Operation = Operation.Add,
Path = "/fields/Microsoft.VSTS.TCM.ReproSteps",
Value = reproSteps
});
}
patchDocument.Add(new JsonPatchOperation()
{
Operation = Operation.Add,
Path = "/fields/Microsoft.VSTS.Common.Priority",
Value = priority.ToString()
});
patchDocument.Add(new JsonPatchOperation()
{
Operation = Operation.Add,
Path = "/fields/Microsoft.VSTS.Common.Severity",
Value = severity
});
using (var connection = new VssConnection(this.uri, new VssCredentials(credentials)))
{
var workItemTrackingHttpClient = connection.GetClient<WorkItemTrackingHttpClient>();
try
{
var result = await workItemTrackingHttpClient.CreateWorkItemAsync(patchDocument, project, "Bug").ConfigureAwait(false);
Console.WriteLine($"Bug successfully created: Bug #{result.Id}");
return result;
}
catch (Exception ex)
{
Console.WriteLine($"Error creating bug: {ex.Message}");
throw;
}
}
}
}
Пример 2. Аутентификация Service Principal (автоматизированные сценарии)
// NuGet packages:
// Microsoft.TeamFoundationServer.Client
// Microsoft.Identity.Client
using System;
using System.Threading.Tasks;
using Microsoft.Identity.Client;
using Microsoft.TeamFoundation.WorkItemTracking.WebApi;
using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models;
using Microsoft.VisualStudio.Services.Common;
using Microsoft.VisualStudio.Services.WebApi;
using Microsoft.VisualStudio.Services.WebApi.Patch;
using Microsoft.VisualStudio.Services.WebApi.Patch.Json;
public class ServicePrincipalBugCreator
{
private readonly Uri uri;
private readonly string clientId;
private readonly string clientSecret;
private readonly string tenantId;
/// <summary>
/// Initializes a new instance using Service Principal authentication.
/// </summary>
/// <param name="orgName">Your Azure DevOps organization name</param>
/// <param name="clientId">Service principal client ID</param>
/// <param name="clientSecret">Service principal client secret</param>
/// <param name="tenantId">Microsoft Entra tenant ID</param>
public ServicePrincipalBugCreator(string orgName, string clientId, string clientSecret, string tenantId)
{
this.uri = new Uri($"https://dev.azure.com/{orgName}");
this.clientId = clientId;
this.clientSecret = clientSecret;
this.tenantId = tenantId;
}
/// <summary>
/// Create a bug using Service Principal authentication.
/// </summary>
public async Task<WorkItem> CreateBugAsync(string project, string title, string reproSteps, int priority = 2, string severity = "3 - Medium")
{
// Acquire token using Service Principal
var app = ConfidentialClientApplicationBuilder
.Create(this.clientId)
.WithClientSecret(this.clientSecret)
.WithAuthority($"https://login.microsoftonline.com/{this.tenantId}")
.Build();
var scopes = new[] { "https://app.vssps.visualstudio.com/.default" };
var result = await app.AcquireTokenForClient(scopes).ExecuteAsync();
var credentials = new VssOAuthAccessTokenCredential(result.AccessToken);
var patchDocument = new JsonPatchDocument();
// Add work item fields
patchDocument.Add(new JsonPatchOperation()
{
Operation = Operation.Add,
Path = "/fields/System.Title",
Value = title
});
if (!string.IsNullOrEmpty(reproSteps))
{
patchDocument.Add(new JsonPatchOperation()
{
Operation = Operation.Add,
Path = "/fields/Microsoft.VSTS.TCM.ReproSteps",
Value = reproSteps
});
}
patchDocument.Add(new JsonPatchOperation()
{
Operation = Operation.Add,
Path = "/fields/Microsoft.VSTS.Common.Priority",
Value = priority.ToString()
});
patchDocument.Add(new JsonPatchOperation()
{
Operation = Operation.Add,
Path = "/fields/Microsoft.VSTS.Common.Severity",
Value = severity
});
using (var connection = new VssConnection(this.uri, new VssCredentials(credentials)))
{
var workItemTrackingHttpClient = connection.GetClient<WorkItemTrackingHttpClient>();
try
{
var workItem = await workItemTrackingHttpClient.CreateWorkItemAsync(patchDocument, project, "Bug").ConfigureAwait(false);
Console.WriteLine($"Bug successfully created: Bug #{workItem.Id}");
return workItem;
}
catch (Exception ex)
{
Console.WriteLine($"Error creating bug: {ex.Message}");
throw;
}
}
}
}
Пример 3: Проверка подлинности управляемого удостоверения (приложения, размещенные в Azure)
// NuGet packages:
// Microsoft.TeamFoundationServer.Client
// Azure.Identity
using System;
using System.Threading.Tasks;
using Azure.Core;
using Azure.Identity;
using Microsoft.TeamFoundation.WorkItemTracking.WebApi;
using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models;
using Microsoft.VisualStudio.Services.Common;
using Microsoft.VisualStudio.Services.WebApi;
using Microsoft.VisualStudio.Services.WebApi.Patch;
using Microsoft.VisualStudio.Services.WebApi.Patch.Json;
public class ManagedIdentityBugCreator
{
private readonly Uri uri;
/// <summary>
/// Initializes a new instance using Managed Identity authentication.
/// </summary>
/// <param name="orgName">Your Azure DevOps organization name</param>
public ManagedIdentityBugCreator(string orgName)
{
this.uri = new Uri($"https://dev.azure.com/{orgName}");
}
/// <summary>
/// Create a bug using Managed Identity authentication.
/// </summary>
public async Task<WorkItem> CreateBugAsync(string project, string title, string reproSteps, int priority = 2, string severity = "3 - Medium")
{
// Use Managed Identity to acquire token
var credential = new DefaultAzureCredential();
var tokenRequestContext = new TokenRequestContext(new[] { "https://app.vssps.visualstudio.com/.default" });
var tokenResult = await credential.GetTokenAsync(tokenRequestContext);
var credentials = new VssOAuthAccessTokenCredential(tokenResult.Token);
var patchDocument = new JsonPatchDocument();
// Add work item fields
patchDocument.Add(new JsonPatchOperation()
{
Operation = Operation.Add,
Path = "/fields/System.Title",
Value = title
});
if (!string.IsNullOrEmpty(reproSteps))
{
patchDocument.Add(new JsonPatchOperation()
{
Operation = Operation.Add,
Path = "/fields/Microsoft.VSTS.TCM.ReproSteps",
Value = reproSteps
});
}
patchDocument.Add(new JsonPatchOperation()
{
Operation = Operation.Add,
Path = "/fields/Microsoft.VSTS.Common.Priority",
Value = priority.ToString()
});
patchDocument.Add(new JsonPatchOperation()
{
Operation = Operation.Add,
Path = "/fields/Microsoft.VSTS.Common.Severity",
Value = severity
});
using (var connection = new VssConnection(this.uri, new VssCredentials(credentials)))
{
var workItemTrackingHttpClient = connection.GetClient<WorkItemTrackingHttpClient>();
try
{
var workItem = await workItemTrackingHttpClient.CreateWorkItemAsync(patchDocument, project, "Bug").ConfigureAwait(false);
Console.WriteLine($"Bug successfully created: Bug #{workItem.Id}");
return workItem;
}
catch (Exception ex)
{
Console.WriteLine($"Error creating bug: {ex.Message}");
throw;
}
}
}
}
Пример 4: Аутентификация с использованием персонального токена доступа
// NuGet package: Microsoft.TeamFoundationServer.Client
using System;
using System.Threading.Tasks;
using Microsoft.TeamFoundation.WorkItemTracking.WebApi;
using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models;
using Microsoft.VisualStudio.Services.Common;
using Microsoft.VisualStudio.Services.WebApi;
using Microsoft.VisualStudio.Services.WebApi.Patch;
using Microsoft.VisualStudio.Services.WebApi.Patch.Json;
public class PatBugCreator
{
private readonly Uri uri;
private readonly string personalAccessToken;
/// <summary>
/// Initializes a new instance using Personal Access Token authentication.
/// </summary>
/// <param name="orgName">Your Azure DevOps organization name</param>
/// <param name="personalAccessToken">Your Personal Access Token</param>
public PatBugCreator(string orgName, string personalAccessToken)
{
this.uri = new Uri($"https://dev.azure.com/{orgName}");
this.personalAccessToken = personalAccessToken;
}
/// <summary>
/// Create a bug using Personal Access Token authentication.
/// </summary>
/// <param name="project">The name of your project</param>
/// <param name="title">Bug title</param>
/// <param name="reproSteps">Reproduction steps</param>
/// <param name="priority">Priority level (1-4)</param>
/// <param name="severity">Severity level</param>
/// <returns>The created WorkItem</returns>
public async Task<WorkItem> CreateBugAsync(string project, string title, string reproSteps, int priority = 2, string severity = "3 - Medium")
{
var credentials = new VssBasicCredential(string.Empty, this.personalAccessToken);
var patchDocument = new JsonPatchDocument();
// Add required and optional fields
patchDocument.Add(new JsonPatchOperation()
{
Operation = Operation.Add,
Path = "/fields/System.Title",
Value = title
});
if (!string.IsNullOrEmpty(reproSteps))
{
patchDocument.Add(new JsonPatchOperation()
{
Operation = Operation.Add,
Path = "/fields/Microsoft.VSTS.TCM.ReproSteps",
Value = reproSteps
});
}
patchDocument.Add(new JsonPatchOperation()
{
Operation = Operation.Add,
Path = "/fields/Microsoft.VSTS.Common.Priority",
Value = priority.ToString()
});
patchDocument.Add(new JsonPatchOperation()
{
Operation = Operation.Add,
Path = "/fields/Microsoft.VSTS.Common.Severity",
Value = severity
});
using (var connection = new VssConnection(this.uri, new VssCredentials(credentials)))
{
var workItemTrackingHttpClient = connection.GetClient<WorkItemTrackingHttpClient>();
try
{
var result = await workItemTrackingHttpClient.CreateWorkItemAsync(patchDocument, project, "Bug").ConfigureAwait(false);
Console.WriteLine($"Bug successfully created: Bug #{result.Id}");
return result;
}
catch (Exception ex)
{
Console.WriteLine($"Error creating bug: {ex.Message}");
throw;
}
}
}
}
Примеры использования
Использование проверки подлинности Microsoft Entra ID (интерактивный режим)
class Program
{
static async Task Main(string[] args)
{
var bugCreator = new EntraIdBugCreator("your-organization-name");
var bug = await bugCreator.CreateBugAsync(
project: "your-project-name",
title: "Authorization Errors with Microsoft Accounts",
reproSteps: "Our authorization logic needs to allow for users with Microsoft accounts (formerly Live IDs) - https://learn.microsoft.com/entra/identity-platform/",
priority: 1,
severity: "2 - High"
);
Console.WriteLine($"Created bug with ID: {bug.Id}");
}
}
Использование проверки подлинности субъекта-службы (сценарии CI/CD)
class Program
{
static async Task Main(string[] args)
{
// These values should come from environment variables or Azure Key Vault
var clientId = Environment.GetEnvironmentVariable("AZURE_CLIENT_ID");
var clientSecret = Environment.GetEnvironmentVariable("AZURE_CLIENT_SECRET");
var tenantId = Environment.GetEnvironmentVariable("AZURE_TENANT_ID");
var bugCreator = new ServicePrincipalBugCreator("your-organization-name", clientId, clientSecret, tenantId);
var bug = await bugCreator.CreateBugAsync(
project: "your-project-name",
title: "Automated Bug Report",
reproSteps: "Issue detected by automated testing...",
priority: 2,
severity: "3 - Medium"
);
Console.WriteLine($"Automated bug created: #{bug.Id}");
}
}
Использование аутентификации с управляемым удостоверением (Функции Azure/служба приложений)
public class BugReportFunction
{
private readonly ILogger<BugReportFunction> _logger;
public BugReportFunction(ILogger<BugReportFunction> logger)
{
_logger = logger;
}
[Function("CreateBugReport")]
public async Task<HttpResponseData> Run(
[HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequestData req)
{
var bugCreator = new ManagedIdentityBugCreator("your-organization-name");
var bug = await bugCreator.CreateBugAsync(
project: "your-project-name",
title: "Function-detected Issue",
reproSteps: "Issue reported through Azure Function...",
priority: 3,
severity: "4 - Low"
);
var response = req.CreateResponse(System.Net.HttpStatusCode.OK);
await response.WriteStringAsync($"Bug created: {bug.Id}");
return response;
}
}
Использование аутентификации с личным токеном доступа (разработка и тестирование)
class Program
{
static async Task Main(string[] args)
{
var pat = Environment.GetEnvironmentVariable("AZURE_DEVOPS_PAT"); // Never hardcode PATs
var bugCreator = new PatBugCreator("your-organization-name", pat);
var bug = await bugCreator.CreateBugAsync(
project: "your-project-name",
title: "Sample Bug Title",
reproSteps: "Steps to reproduce the issue...",
priority: 2,
severity: "3 - Medium"
);
Console.WriteLine($"Bug created successfully: #{bug.Id}");
}
}
Справочник по полю рабочего элемента
При создании рабочих элементов обычно используются следующие поля:
Обязательные поля
- System.Title: название рабочего элемента (обязательно для всех типов рабочих элементов)
- System.WorkItemType: автоматически устанавливается при указании типа в вызове API
Общие необязательные поля
- Microsoft.VSTS.TCM.ReproSteps: подробные действия по воспроизведению
- Microsoft.VSTS.Common.Priority: уровень приоритета (1=самый высокий, 4=самый низкий)
- Microsoft.VSTS.Common.Severity: классификация уровня серьезности
- System.Description: Общее описание или дополнительные сведения
- System.AssignedTo: лицо, ответственное за рабочий элемент
- System.AreaPath: классификация областей
- System.IterationPath: назначение итерации/спринта
Значения приоритета
- 1. Критически важный/самый высокий приоритет
- 2. Высокий приоритет
- 3. Средний приоритет (по умолчанию)
- 4. Низкий приоритет
Общие значения серьезности
- 1. Критическое: неиспользуемая система, блокировка хода выполнения
- 2. Высокий: основные функциональные возможности нарушены
- 3 — средний: некоторые функциональные возможности нарушены (по умолчанию)
- 4 - Низкий: мелкие проблемы или косметические дефекты
Лучшие практики
Аутентификация
- Use Microsoft Entra ID для интерактивных приложений с входом пользователя
- Используйте учетную запись службы для автоматизированных сценариев, потоков CI/CD и серверных приложений
- Use Managed Identity для приложений, работающих в службах Azure (Функции, служба приложений, виртуальные машины)
- Избегайте личных маркеров доступа в рабочей среде; используется только для разработки и тестирования
- Никогда не используйте учетные данные в исходном коде напрямую; используйте переменные окружения или Azure Key Vault
- Реализуйте ротацию учетных данных для долго работающих приложений
- Ensure правильные области доступа: Для создания рабочих элементов требуются соответствующие разрешения в Azure DevOps
Обработка ошибок
- Реализация правильной обработки исключений для сбоев проверки подлинности и API
- Проверка значений полей перед попыткой создания рабочих элементов
- Обработка ошибок проверки поля , возвращаемых API
- Использование шаблонов async/await для повышения скорости реагирования приложений
Производительность
- Пакетные операции при создании нескольких элементов работы
- Кэширование подключений при выполнении нескольких вызовов API
- Использование соответствующих значений времени ожидания для длительных операций
- Реализовать логику повторных попыток с экспоненциальной задержкой для временных сбоев
Проверка данных
- Проверка обязательных полей перед вызовами API
- Проверка разрешений поля и правил типов рабочих элементов
- Обработка входных данных пользователей, чтобы предотвратить инъекционные атаки
- Следуйте требованиям к полям для конкретного проекта и соглашениям об именовании
Устранение неполадок
Проблемы аутентификации
- Microsoft Entra ID сбои аутентификации. Убедитесь, что у пользователя есть соответствующие разрешения для создания рабочих элементов
- Сбои аутентификации учетной записи службы: проверьте правильность идентификатора клиента, секрета и идентификатора арендатора; проверьте разрешения учетной записи службы в Azure DevOps
- Ошибка аутентификации Managed Identity: Убедитесь, что ресурс Azure имеет включённое управляемое удостоверение и соответствующие разрешения.
-
Сбои проверки подлинности PAT: убедитесь, что токен имеет
vso.work_writeобласть и срок его действия не истек. - 403 Запрещенные ошибки: проверка разрешений проекта и доступа к типу рабочего элемента
Ошибки проверки полей
- Отсутствует обязательное поле: убедитесь, что все обязательные поля включены в документ патча
- Недопустимые значения полей: проверка значений полей соответствует ожидаемому формату и допустимым значениям
- Поле не найдено: проверьте, правильно ли написаны имена полей и существуют для типа рабочего элемента
- Ошибки полей только для чтения: некоторые поля нельзя задать во время создания (например, System.CreatedBy)
Распространенные исключения
- VssUnauthorizedException: сбой проверки подлинности или недостаточно разрешений
- VssServiceException: ошибки проверки на стороне сервера или проблемы с API
- ArgumentException: недопустимые параметры или неправильный документ исправления
- JsonReaderException: проблемы с сериализацией и десериализацией JSON
Проблемы с производительностью
- Проблемы с медленной работой API: проверьте сетевое подключение и статус сервиса Azure DevOps.
- Использование памяти: правильное удаление подключений и клиентов
- Ограничение скорости. Реализация соответствующих задержек между вызовами API
Использование искусственного интеллекта для программного создания рабочих элементов
Если Azure DevOps MCP Server подключен к вашему агенту ИИ в режиме агента, вы можете использовать естественно-языковые запросы для создания рабочих элементов.
| задачи | Пример запроса |
|---|---|
| Создание кода для создания багов | Write a C# console app that creates a bug in Azure DevOps project <Contoso> using Microsoft Entra ID authentication and the .NET client libraries |
| Создайте с настраиваемыми полями | Write code to create a work item with priority, severity, and repro steps fields in Azure DevOps using a managed identity |
| Пакетное создание рабочих элементов | Show me how to create multiple bugs in Azure DevOps from a CSV file using the .NET client libraries with service principal authentication |
| Создание из функции Azure | Generate an Azure Function that creates bugs in Azure DevOps project <Contoso> using a system-assigned managed identity |
| Добавление вложений | Write C# code to create a bug in Azure DevOps and attach a log file using the .NET client libraries |
| Создание связанных рабочих элементов | Write code to create a bug in Azure DevOps and link it to an existing user story using the .NET client libraries |
Замечание
Режим агента и сервер MCP используют естественный язык, чтобы настроить эти запросы или задать дальнейшие вопросы, чтобы уточнить результаты.