Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Azure DevOps Services | Azure DevOps Server | Azure DevOps Server 2022
Vytváření pracovních položek prostřednictvím kódu programu je běžným scénářem automatizace ve službě Azure DevOps Services. Tento článek ukazuje, jak vytvořit chybu (nebo jakoukoli pracovní položku) pomocí .NET klientských knihoven s moderními metodami ověřování.
Návod
S touto úlohou můžete využít umělou inteligenci dále v tomto článku nebo si můžete projít Povolit asistenci umělé inteligence s Azure DevOps MCP Server začít.
Požadavky
| Kategorie | Požadavky |
|---|---|
| Azure DevOps |
-
Organizace - Přístup k projektu, kde můžete vytvářet pracovní položky |
| Autentizace | Vyberte jednu z následujících možností: - Microsoft Entra ID ověřování (doporučeno) - Osobní přístupový token (PAT) ( pro testování) |
| vývojového prostředí | Vývojové prostředí jazyka C#. Můžete použít Visual Studio |
Důležité
Zvažte použití bezpečnějších tokenů Microsoft Entra místo vysoce rizikových osobních přístupových tokenů. Další informace najdete v tématu Snížení využití PAT. Projděte si doprovodné materiály k ověřování a zvolte správný mechanismus ověřování pro vaše potřeby.
Možnosti ověřování
Tento článek ukazuje několik metod ověřování, které vyhovují různým scénářům:
ověřování Microsoft Entra ID (doporučeno pro uživatelské aplikace)
Pro produkční aplikace s interakcí uživatele použijte ověřování 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" />
Ověřování Service Principal (služební principál) (doporučeno pro automatizaci)
Pro automatizované scénáře, kanály CI/CD a serverové aplikace:
<PackageReference Include="Microsoft.TeamFoundationServer.Client" Version="19.232.1" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.67.2" />
Ověřování spravované identity (doporučeno pro Azure hostované aplikace)
Pro aplikace běžící na službách Azure (Functions, App Service atd.):
<PackageReference Include="Microsoft.TeamFoundationServer.Client" Version="19.232.1" />
<PackageReference Include="Azure.Identity" Version="1.13.1" />
Ověřování pomocí osobního přístupového tokenu
Scénáře vývoje a testování:
<PackageReference Include="Microsoft.TeamFoundationServer.Client" Version="19.232.1" />
Příklady kódu jazyka C#
Následující příklady ukazují, jak vytvářet pracovní položky pomocí různých metod ověřování.
Příklad 1: Ověřování Microsoft Entra ID (Interaktivní)
Poznámka:
Třída VssAadCredential použitá v tomto příkladu vyžaduje balíček Microsoft.VisualStudio.Services.InteractiveClient a zaměřuje se na .NET Framework. Pro aplikace .NET Core/.NET 5 nebo novější použijte přístup založený na knihovně MSAL zobrazený v Example 2 (instanční objekt) nebo Example 3 (spravovaná identita) s 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;
}
}
}
}
Příklad 2: Ověřování principálu služby (automatizované scénáře)
// 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;
}
}
}
}
Příklad 3: Ověřování spravované identity (Azure hostované aplikace)
// 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;
}
}
}
}
Příklad 4: Ověřování pomocí osobního přístupového tokenu
// 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;
}
}
}
}
Příklady použití
Použití ověřování Microsoft Entra ID (Interaktivní)
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}");
}
}
Použití ověřování principálu služby (scénáře 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}");
}
}
Použití ověřování spravované identity (Azure Functions/App Service)
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;
}
}
Použití ověřování osobního přístupového tokenu (vývoj/testování)
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}");
}
}
Odkaz na pole pracovní položky
Při vytváření pracovních položek obvykle použijete tato pole:
Povinná pole
- System.Title: Název pracovní položky (povinný pro všechny typy pracovních položek)
- System.WorkItemType: Automaticky nastaveno při zadávání typu ve volání rozhraní API
Běžná volitelná pole
- Microsoft.VSTS.TCM.ReproSteps: Podrobné kroky pro reprodukci
- Microsoft.VSTS.Common.Priority: Úroveň priority (1=nejvyšší, 4=nejnižší)
- Microsoft.VSTS.Common.Severity: Klasifikace závažnosti
- System.Description: Obecný popis nebo další podrobnosti
- System.AssignedTo: Osoba odpovědná za pracovní položku
- System.AreaPath: Klasifikace oblasti
- System.IterationPath: Přiřazení iterace/sprintu
Hodnoty priority
- 1: Kritická/nejvyšší priorita
- 2: Vysoká priorita
- 3: Střední priorita (výchozí)
- 4: Nízká priorita
Běžné hodnoty závažnosti
- 1 – Kritický: Nepoužitelný systém, blokování průběhu
- 2 – Vysoká: Hlavní funkčnost byla přerušena.
- 3 – střední: Některé funkce jsou poškozené (výchozí)
- 4 - Nízký: Menší problémy nebo kosmetické problémy
Osvědčené postupy
Autentizace
- Použití Microsoft Entra ID pro interaktivní aplikace s přihlášením uživatele
- Use Service Principal pro automatizované scénáře, kanály CI/CD a serverové aplikace
- Použít spravovanou identitu pro aplikace spuštěné na službách Azure (Functions, App Service, VMs)
- Vyhněte se osobním přístupovým tokenům v produkčním prostředí; použití pouze pro vývoj a testování
- Nikdy nezakódovávejte přihlašovací údaje ve zdrojovém kódu; používejte proměnné prostředí nebo Azure Key Vault
- Implementace obměně přihlašovacích údajů pro dlouhotrvající aplikace
- Zkontrolovat správné obory: Vytvoření pracovní položky vyžaduje odpovídající oprávnění v Azure DevOps
Zpracování chyb
- Implementace správného zpracování výjimek pro chyby ověřování a rozhraní API
- Před pokusem o vytvoření pracovních položek ověřte hodnoty polí.
- Zpracování chyb ověření polí vrácených rozhraním API
- Použití vzorů async/await pro lepší odezvu aplikace
Výkon
- Dávkové operace při vytváření více úkolů
- Ukládání připojení do mezipaměti při provádění více volání rozhraní API
- Použijte vhodné hodnoty časového limitu pro dlouhotrvající operace.
- Implementace logiky opětovného pokusu s exponenciálním odkladem při přechodných selháních
Ověření dat
- Ověření požadovaných polí před voláním rozhraní API
- Kontrola oprávnění polí a pravidel typů pracovních položek
- Sanitizace uživatelského vstupu , aby se zabránilo útokům injekce kódu
- Dodržujte požadavky na pole specifické pro projekt a zásady vytváření názvů.
Řešení problémů
Problémy s ověřováním
- Chyby ověřování Microsoft Entra ID: Zkontrolujte, zda má uživatel správná oprávnění k vytváření pracovních položek.
- Selhání ověřování služebního principála: Ověřte, zda ID klienta, tajný klíč a ID tenanta jsou správné; zkontrolujte oprávnění služebního principála v Azure DevOps
- Selhání ověřování spravované identity: Ujistěte se, že prostředek Azure má povolenou spravovanou identitu a má přiřazená odpovídající oprávnění.
-
Selhání ověření PAT: Ověřte, že token má
vso.work_writeurčený obor a jeho platnost nevypršela. - Chyby 403 Zakázáno: Kontrola oprávnění projektu a přístupu typu pracovní položky
Chyby ověřování polí
- Chybí povinné pole: Ujistěte se, že všechna požadovaná pole jsou zahrnutá v dokumentu opravy.
- Neplatné hodnoty polí: Ověřte, zda hodnoty polí odpovídají očekávanému formátu a povoleným hodnotám
- Pole nebylo nalezeno: Zkontrolujte, zda jsou názvy polí správně napsány a existují pro daný typ pracovní položky.
- Chyby polí jen pro čtení: Některá pole nelze nastavit při vytváření (například System.CreatedBy)
Běžné výjimky
- VssUnauthorizedException: Ověřování selhalo nebo nemá dostatečná oprávnění
- VssServiceException: Chyby ověřování na straně serveru nebo problémy s rozhraním API
- ArgumentException: Neplatný parametr nebo poškozený dokument opravy
- JsonReaderException: Problémy se serializací nebo deserializací JSON
Problémy s výkonem
- Pomalu reagující rozhraní API: Zkontrolujte síťové připojení a stav služby Azure DevOps.
- Využití paměti: Správně vyřaďte připojení a klienty.
- Omezování rychlosti: Implementace odpovídajících zpoždění mezi voláními rozhraní API
Použití AI k programovému vytváření pracovních položek
Pokud máte Azure DevOps MCP Server připojený k agentu AI v režimu agenta, můžete pomocí výzev přirozeného jazyka vygenerovat kód pro vytváření pracovních položek.
| Úkol | Příklad výzvy |
|---|---|
| Generování kódu pro vytváření chyb | Write a C# console app that creates a bug in Azure DevOps project <Contoso> using Microsoft Entra ID authentication and the .NET client libraries |
| Vytvořte s vlastními poli | Write code to create a work item with priority, severity, and repro steps fields in Azure DevOps using a managed identity |
| Dávkové vytváření pracovních položek | Show me how to create multiple bugs in Azure DevOps from a CSV file using the .NET client libraries with service principal authentication |
| Vytvoření z funkce Azure | Generate an Azure Function that creates bugs in Azure DevOps project <Contoso> using a system-assigned managed identity |
| Přidat přílohy | Write C# code to create a bug in Azure DevOps and attach a log file using the .NET client libraries |
| Vytvoření propojených pracovních položek | Write code to create a bug in Azure DevOps and link it to an existing user story using the .NET client libraries |
Poznámka:
Režim agenta a server MCP používají přirozený jazyk, takže tyto výzvy můžete upravit nebo položit následné otázky, abyste výsledky upřesněte.