Aracılığıyla paylaş


.NET istemci kitaplıklarını kullanarak Azure DevOps Services'te hata oluşturma

Azure DevOps Services | Azure DevOps Server | Azure DevOps Server 2022

Program aracılığıyla iş öğeleri oluşturmak, Azure DevOps Services'te yaygın bir otomasyon senaryosudur. Bu makalede, modern kimlik doğrulama yöntemleriyle .NET istemci kitaplıklarını kullanarak bir hatanın (veya herhangi bir iş öğesinin) nasıl oluşturulacağı gösterilmektedir.

Önkoşullar

Kategori Gereksinimler
Azure DevOps - Kuruluş
- İş öğeleri oluşturabileceğiniz bir projeye erişim
Kimlik Doğrulaması Aşağıdakilerden birini seçin:
- Microsoft Entra Id kimlik doğrulaması (önerilir)
- Kişisel Erişim Belirteci (PAT) (test için)
Geliştirme ortamı C# geliştirme ortamı. Visual Studio'yu kullanabilirsiniz

Önemli

Üretim uygulamaları için, Kişisel Erişim Belirteçleri yerine Microsoft Entra Id kimlik doğrulamasını kullanmanızı öneririz. PAT'ler test ve geliştirme senaryoları için uygundur. Doğru kimlik doğrulama yöntemini seçme yönergeleri için bkz. Kimlik doğrulama kılavuzu.

Kimlik doğrulama seçenekleri

Bu makalede, farklı senaryolara uyacak birden çok kimlik doğrulama yöntemi gösterilmektedir:

Kullanıcı etkileşimi olan üretim uygulamaları için Microsoft Entra ID kimlik doğrulamasını kullanın:

<PackageReference Include="Microsoft.TeamFoundationServer.Client" Version="19.225.1" />
<PackageReference Include="Microsoft.VisualStudio.Services.InteractiveClient" Version="19.225.1" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.61.3" />

Otomatik senaryolar, CI/CD işlem hatları ve sunucu uygulamaları için:

<PackageReference Include="Microsoft.TeamFoundationServer.Client" Version="19.225.1" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.61.3" />

Azure hizmetlerinde (İşlevler, App Service vb.) çalışan uygulamalar için:

<PackageReference Include="Microsoft.TeamFoundationServer.Client" Version="19.225.1" />
<PackageReference Include="Azure.Identity" Version="1.10.4" />

Kişisel Erişim Belirteci kimlik doğrulaması

Geliştirme ve test senaryoları için:

<PackageReference Include="Microsoft.TeamFoundationServer.Client" Version="19.225.1" />

C# kod örnekleri

Aşağıdaki örneklerde farklı kimlik doğrulama yöntemlerini kullanarak iş öğelerinin nasıl oluşturulacağı gösterilmektedir.

Örnek 1: Microsoft Entra Id kimlik doğrulaması (Etkileşimli)

// 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;
            }
        }
    }
}

Örnek 2: Hizmet Sorumlusu kimlik doğrulaması (Otomatik senaryolar)

// 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">Azure AD 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;
            }
        }
    }
}

Örnek 3: Yönetilen Kimlik ile kimlik doğrulama (Azure'da barındırılan uygulamalar)

// 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;
            }
        }
    }
}

Örnek 4: Kişisel Erişim Belirteci kimlik doğrulaması

// 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;
            }
        }
    }
}

Kullanım örnekleri

Microsoft Entra Id kimlik doğrulamayı kullanma (Etkileşimli)

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://docs.microsoft.com/library/live/hh826547.aspx",
            priority: 1,
            severity: "2 - High"
        );
        
        Console.WriteLine($"Created bug with ID: {bug.Id}");
    }
}

Hizmet Sorumlusu kimlik doğrulamayı kullanma (CI/CD senaryoları)

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}");
    }
}

Managed Identity kimlik doğrulamasını kullanma (Azure İşlevleri/App Service)

public class BugReportFunction
{
    [FunctionName("CreateBugReport")]
    public static async Task<IActionResult> Run(
        [HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequest req,
        ILogger log)
    {
        var bugCreator = new ManagedIdentityBugCreator("your-organization-name");
        
        var bug = await bugCreator.CreateBugAsync(
            project: "your-project-name",
            title: "Function-detected Issue",
            reproSteps: "Issue reported via Azure Function...",
            priority: 3,
            severity: "4 - Low"
        );
        
        return new OkObjectResult($"Bug created: {bug.Id}");
    }
}

Kişisel Erişim Belirteci kimlik doğrulamayı kullanma (Geliştirme/Test)

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}");
    }
}

İş öğesi alan referansı

İş öğeleri oluştururken yaygın olarak şu alanları kullanırsınız:

Gerekli alanlar

  • System.Title: İş öğesi başlığı (tüm iş öğesi türleri için gereklidir)
  • System.WorkItemType: API çağrısında türü belirtirken otomatik olarak ayarlanır

Ortak isteğe bağlı alanlar

  • Microsoft.VSTS.TCM.ReproSteps: Ayrıntılı çoğaltma adımları
  • Microsoft.VSTS.Common.Priority: Öncelik düzeyi (1=en yüksek, 4=en düşük)
  • Microsoft.VSTS.Common.Severity: Ciddiyet sınıflandırması
  • System.Description: Genel açıklama veya ek ayrıntılar
  • System.AssignedTo: İş öğesinden sorumlu kişi
  • System.AreaPath: Alan sınıflandırması
  • System.IterationPath: Yineleme/sprint ataması

Öncelik değerleri

  • 1: Kritik/En Yüksek öncelik
  • 2: Yüksek öncelikli
  • 3: Orta öncelikli (varsayılan)
  • 4: Düşük öncelikli

Ortak önem derecesi değerleri

  • 1 - Kritik: Sistem kullanılamaz, ilerlemeyi engelliyor
  • 2 - Yüksek: Ana işlevsellik bozuk
  • 3 - Orta: Bazı işlevler bozuk (varsayılan)
  • 4 - Düşük: Küçük sorunlar veya kozmetik sorunlar

En iyi yöntemler

Kimlik doğrulama

  • Kullanıcı oturum açma ile etkileşimli uygulamalar için Microsoft Entra Id kullanma
  • Otomatik senaryolar, CI/CD işlem hatları ve sunucu uygulamaları için Hizmet İlkesi Kullan
  • Azure hizmetlerinde (İşlevler, App Service, VM'ler) çalışan uygulamalar için Yönetilen Kimlik kullanma
  • Üretimde Kişisel Erişim Belirteçlerinden kaçının; yalnızca geliştirme ve test için kullanın
  • Kaynak kodunda kimlik bilgilerini asla sabit kodlama; ortam değişkenlerini veya Azure Key Vault'ları kullanma
  • Uzun süre çalışan uygulamalar için kimlik bilgisi döndürmeyi uygulama
  • Uygun kapsamları güvence altına alma: İş öğesi oluşturma için Azure DevOps'ta uygun izinler gerekir

Hata yönetimi

  • Kimlik doğrulaması ve API hataları için uygun özel durum işlemeyi uygulama
  • İş öğeleri oluşturmaya çalışmadan önce alan değerlerini doğrulama
  • API tarafından döndürülen alan doğrulama hatalarını işleme
  • Daha iyi uygulama yanıt hızı için zaman uyumsuz/await desenleri kullanma

Gösteri

  • Birden çok iş öğesi oluştururken toplu işlem
  • Birden çok API çağrısı yaparken bağlantıları önbelleğe alma
  • Uzun süre çalışan işlemler için uygun zaman aşımı değerlerini kullanma
  • Geçici hatalar için üstel geri alma ile yeniden deneme mantığını uygulama

Veri doğrulama

  • API çağrıları öncesinde gerekli alanları doğrulama
  • Alan izinlerini ve iş öğesi türü kurallarını denetleme
  • Enjeksiyon saldırılarını önlemek için kullanıcı girişini temizleyin
  • Projeye özgü alan gereksinimlerini ve adlandırma kurallarını izleyin

Sorun giderme

Kimlik doğrulaması sorunları

  • Microsoft Entra Id kimlik doğrulaması hataları: Kullanıcının iş öğeleri oluşturmak için uygun izinlere sahip olduğundan emin olun
  • Hizmet Sorumlusu kimlik doğrulaması hataları: İstemci kimliği, gizli dizi ve kiracı kimliğinin doğru olduğunu doğrulayın; Azure DevOps'ta hizmet sorumlusu izinlerini denetleme
  • Yönetilen Kimlik kimlik doğrulaması hataları: Azure kaynağının yönetilen kimliğin etkinleştirildiğinden ve uygun izinlere sahip olduğundan emin olun
  • PAT kimlik doğrulama hataları: Jetonun kapsamı olduğundan ve süresinin dolmadığından emin olun
  • 403 Yasak hataları: Proje izinlerini ve iş öğesi türü erişimini denetleme

Alan doğrulama hataları

  • Gerekli alan eksik: Tüm gerekli alanların düzeltme eki belgesine eklendiğinden emin olun
  • Geçersiz alan değerleri: Alan değerlerinin beklenen biçimle ve izin verilen değerlerle eşleşip eşleşmedığını doğrulayın
  • Alan bulunamadı: Alan adlarının doğru yazıldığını ve iş öğesi türü için mevcut olduğunu denetleyin
  • Salt okunur alan hataları: Bazı alanlar oluşturma sırasında ayarlanamaz (örneğin, System.CreatedBy)

Yaygın özel durumlar

  • VssUnauthorizedException: Kimlik doğrulaması başarısız oldu veya izinler yetersiz
  • VssServiceException: Sunucu tarafı doğrulama hataları veya API sorunları
  • ArgumentException: Geçersiz parametreler veya hatalı biçimlendirilmiş düzeltme eki belgesi
  • JsonReaderException: JSON serileştirme/seri durumdan çıkarma ile ilgili sorunlar

Performans sorunları

  • Yavaş API yanıtları: Ağ bağlantısını ve Azure DevOps hizmetinin durumunu denetleme
  • Bellek kullanımı: Bağlantıları ve istemcileri uygun şekilde sonlandır
  • Hız sınırlama: API çağrıları arasında uygun gecikmeleri uygulama

Sonraki adımlar