Azure DevOps Pipeline ile UDE Build ve Package Üretimi

Dynamics 365 F&O UDE Serisi | Bölüm 9

Azure DevOps Pipeline ile UDE Build ve Package Üretimi

NuGet paketleri, Azure Artifacts, YAML pipeline, X++ build ve Power Platform unified package üretimi
Ağustos 2026 | fatihdemirci.net

Giriş

Serinin önceki bölümünde lokal build ile online UDE ortamına deploy işleminin farklı adımlar olduğunu ele aldık. Günlük geliştirmede Visual Studio ile hızlı bir project build alıp değişiklikleri bağlı olduğumuz ortama gönderebiliyoruz.

UAT veya production gibi kontrollü ortamlara çıktığımızda ise geliştiricinin bilgisayarında alınan build yeterli değil. Aynı Git commit’inin aynı referanslarla derlenmesi ve ortaya çıkan package’ın pipeline artifact’i olarak saklanması gerekiyor.

Bu bölümde Microsoft-hosted Windows agent üzerinde çalışan bir Azure DevOps YAML pipeline kuracağız. Pipeline repository’deki X++ metadata’yı derleyecek, unified package ve ihtiyaç varsa klasik deployable package üretecek, ardından çıktıları artifact olarak yayınlayacak.

Yazının kapsamı: Bu bölüm build ve package üretimine odaklanıyor. Üretilen unified package’ın Test, UAT ve Production ortamlarına approval mekanizmalarıyla dağıtılması bir sonraki bölümün konusu olacak.

Genel Akış

Build pipeline’ın yaptığı işi en basit haliyle aşağıdaki akışta düşünebiliriz:

Girdi İşlem Çıktı
Git repository NuGet restore Compiler ve reference paketleri
Metadata + .sln/.rnrproj VSBuild / MSBuild ile X++ compile X++ binary çıktıları
X++ binary çıktıları XppCreatePackage@3 Unified package ve/veya deployable package
Package çıktıları PublishBuildArtifacts Release pipeline’ın kullanacağı artifact

Ön Koşullar

Pipeline’a geçmeden önce repository ve Azure DevOps organizasyonu tarafında bazı bileşenlerin hazır olması gerekiyor.

  1. Azure DevOps organizasyonu, proje ve Git repository
  2. Dynamics 365 Finance and Operations Tools ile Power Platform Build Tools extension’ları
  3. Build edilecek package’ı temsil eden .rnrproj ve tercihen .sln dosyası
  4. Custom metadata, Descriptor klasörü ve model descriptor dosyaları
  5. Hedef environment sürümüyle uyumlu X++ Compiler ve Build Reference NuGet paketleri
  6. Azure Artifacts feed üzerinde pipeline build service hesabına Reader yetkisi

Önemli: Pipeline yalnızca AxClass, AxTable veya AxForm XML dosyalarını görerek neyi build edeceğini anlayamaz. Build edilecek package’ı temsil eden .rnrproj dosyası ve model descriptor bilgileri repository’de olmalıdır.

Repository Yapısını Bölüm 7 ve 8 ile Hizalayalım

Önceki bölümlerde kullandığımız repository yapısını build pipeline için biraz daha somutlaştırabiliriz:

D365FO-UDE
│
├── Metadata
│   └── DMRCustomizations
│       ├── Descriptor
│       └── DMRCustomizations
│
├── Projects
│   └── DMR_FD_AITest1
│       ├── DMR_FD_AITest1.sln
│       └── DMR_FD_AITest1.rnrproj
│
├── Build
│   ├── azure-pipelines.yml
│   ├── packages.config
│   └── nuget.config
│
└── README.md

Burada MetadataPath, SolutionPath ve Build klasörünün konumu YAML içinde değişken olarak tanımlanacak. Böylece pipeline dosyasını farklı projelere uyarlarken tüm adımların içine tek tek path yazmak zorunda kalmayacağız.

Gerekli Azure DevOps Extension’ları

Azure DevOps organizasyonunda iki extension’a ihtiyacımız var. Dynamics 365 Finance and Operations Tools, XppCreatePackage@3 görevini; Power Platform Build Tools ise sonraki release ve deploy adımlarını sağlar.

Resim 1 - Azure DevOps organizasyonunda gerekli extension'lar
Resim 1 – Azure DevOps organizasyonunda gerekli extension'lar

X++ Build İçin Gerekli NuGet Paketleri

Microsoft-hosted agent üzerinde klasik development VM’de bulunan PackagesLocalDirectory hazır gelmez. X++ compiler ve Microsoft reference binary’lerini pipeline’a NuGet paketleri üzerinden sağlamamız gerekir.

Microsoft’un güncel örneğinde tam bir X++ build için aşağıdaki beş paket kullanılıyor:

NuGet paketi Amaç
Microsoft.Dynamics.AX.Platform.CompilerPackage xppc.exe, MSBuild task’ları ve X++ build araçları
Microsoft.Dynamics.AX.Platform.DevALM.BuildXpp Platform modülü için build reference binary’leri
Microsoft.Dynamics.AX.Application1.DevALM.BuildXpp Application reference paketinin birinci bölümü
Microsoft.Dynamics.AX.Application2.DevALM.BuildXpp Application reference paketinin ikinci bölümü
Microsoft.Dynamics.AX.ApplicationSuite.DevALM.BuildXpp Application Suite build reference binary’leri

Application paketinin iki parçaya ayrılmasının nedeni Azure DevOps package boyutu sınırlarıdır. Yalnızca platform seviyesinde geliştirme yapan sınırlı projelerde iki paket yeterli olabilir; Finance veya Supply Chain fonksiyonlarını genişleten projelerde ise beş paketin tamamını kullanmak daha güvenli.

Sürüm kuralı: Build sırasında kullanılan NuGet referanslarının sürümü hedef environment sürümüyle uyumlu olmalıdır. Ortama yeni bir quality update uygulandığında pipeline paket sürümleri de kontrol edilmelidir.

NuGet Paketlerini Nereden İndireceğiz?

Güncel Unified Experience akışında paketler Power Platform Visual Studio extension içindeki Download Dynamics 365 FnO NuGets for CI/CD seçeneğiyle indirilebiliyor. Erişim veya tenant senaryosuna göre LCS Shared Asset Library de kullanılabilir.

Azure Artifacts Üzerinde FinOpsNuGet Feed Oluşturma

İndirdiğimiz NuGet paketlerini repository’ye koymak yerine Azure Artifacts feed üzerinde tutacağız. Böylece source control gereksiz binary dosyalarla büyümez ve hangi paket sürümünün kullanılacağı packages.config üzerinden yönetilebilir.

  • Azure DevOps projesinde Artifacts → Create Feed ekranını açın.
  • Feed adını FinOpsNuGet olarak belirleyin.
  • Hedef environment sürümüyle uyumlu beş .nupkg dosyasını feed’e push edin.
  • Feed Settings → Permissions bölümünde proje build service hesabına Reader yetkisi verin.
  • Connect to feed ekranından v3 endpoint adresini alın.
nuget.exe push -Source "https://pkgs.dev.azure.com/<org>/<project>/_packaging/FinOpsNuGet/nuget/v3/index.json" -ApiKey AZ <paket-adı>.nupkg
Resim 2 - FinOpsNuGet feed'ine yüklenen compiler ve build reference paketleri
Resim 2 – FinOpsNuGet feed'ine yüklenen compiler ve build reference paketleri

nuget.config Dosyası

nuget.config, pipeline’ın paketleri hangi feed’den indireceğini belirler. Feed URL’sini Azure DevOps Connect to feed ekranından almak en güvenli yöntemdir.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <clear />
    <add key="FinOpsNuGet"
         value="https://pkgs.dev.azure.com/<org>/<project>/_packaging/FinOpsNuGet/nuget/v3/index.json" />
  </packageSources>
</configuration>

Güvenlik notu: Feed için kullanıcı adı veya kişisel access token bilgisini nuget.config içine yazmıyoruz. Azure DevOps pipeline’ın kendi build identity’si ve feed permission yapısı kullanılmalı.

packages.config Dosyası

packages.config, hangi NuGet paketlerinin hangi tam sürümle restore edileceğini belirler. Aşağıdaki değerler ekran görüntülerindeki 10.0.47 / PU71 örneğine aittir; kendi feed’inizdeki .nupkg sürümleriyle bire bir kontrol edilmelidir.

<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="Microsoft.Dynamics.AX.Platform.CompilerPackage"
           version="7.0.7858.115" targetFramework="net40" />
  <package id="Microsoft.Dynamics.AX.Platform.DevALM.BuildXpp"
           version="7.0.7858.115" targetFramework="net40" />
  <package id="Microsoft.Dynamics.AX.Application1.DevALM.BuildXpp"
           version="10.0.2527.135" targetFramework="net40" />
  <package id="Microsoft.Dynamics.AX.Application2.DevALM.BuildXpp"
           version="10.0.2527.135" targetFramework="net40" />
  <package id="Microsoft.Dynamics.AX.ApplicationSuite.DevALM.BuildXpp"
           version="10.0.2527.135" targetFramework="net40" />
</packages>

Dikkat: Platform ve application paketleri farklı version formatları kullanır. Platform paketlerinin 7.0.x, application paketlerinin 10.0.x şeklinde olması normaldir.

YAML Pipeline’ı Oluşturma

Repository içindeki Build/azure-pipelines.yml dosyasını aşağıdaki temel akışla hazırlayabiliriz. İlk denemede pipeline’ı manuel çalıştırmak daha sağlıklı. Build stabil hale geldikten sonra nightly schedule veya dev/main branch trigger eklenebilir.

Aşağıdaki örnek DMR_FD_AITest1 solution’ını ve Metadata klasörünü esas alıyor:

trigger: none

pool:
  vmImage: 'windows-latest'

variables:
  PlatformVersion: '7.0.7858.115'
  ApplicationVersion: '10.0.2527.135'

  BuildConfigPath: '$(Build.SourcesDirectory)/Build'
  NuGetInstallDir: '$(Build.SourcesDirectory)/NuGets'
  MetadataPath: '$(Build.SourcesDirectory)/Metadata'
  SolutionPath: '$(Build.SourcesDirectory)/Projects/DMR_FD_AITest1/DMR_FD_AITest1.sln'

  CompilerPackage: '$(NuGetInstallDir)/Microsoft.Dynamics.AX.Platform.CompilerPackage'
  PlatformBuildRef: '$(NuGetInstallDir)/Microsoft.Dynamics.AX.Platform.DevALM.BuildXpp'
  App1BuildRef: '$(NuGetInstallDir)/Microsoft.Dynamics.AX.Application1.DevALM.BuildXpp'
  App2BuildRef: '$(NuGetInstallDir)/Microsoft.Dynamics.AX.Application2.DevALM.BuildXpp'
  AppSuiteBuildRef: '$(NuGetInstallDir)/Microsoft.Dynamics.AX.ApplicationSuite.DevALM.BuildXpp'

  UnifiedPackageOutput: '$(Build.ArtifactStagingDirectory)/UnifiedPackage'

stages:
- stage: Build
  displayName: 'Compile X++ and Create Package'
  jobs:
  - job: BuildXpp
    displayName: 'Build X++ and package'
    timeoutInMinutes: 120
    steps:
    - checkout: self
      clean: true

    - task: NuGetCommand@2
      displayName: 'Restore X++ build packages'
      inputs:
        command: 'custom'
        arguments: >
          install "$(BuildConfigPath)/packages.config"
          -ConfigFile "$(BuildConfigPath)/nuget.config"
          -OutputDirectory "$(NuGetInstallDir)"
          -ExcludeVersion
          -Verbosity Detailed
          -Noninteractive

    - task: VSBuild@1
      displayName: 'Build X++ solution'
      inputs:
        solution: '$(SolutionPath)'
        vsVersion: '17.0'
        msbuildArgs: >
          /p:BuildTasksDirectory="$(CompilerPackage)/DevAlm"
          /p:MetadataDirectory="$(MetadataPath)"
          /p:FrameworkDirectory="$(CompilerPackage)"
          /p:ReferenceFolder="$(PlatformBuildRef)/ref/net40;$(App1BuildRef)/ref/net40;$(App2BuildRef)/ref/net40;$(AppSuiteBuildRef)/ref/net40;$(MetadataPath);$(Build.BinariesDirectory)"
          /p:ReferencePath="$(CompilerPackage)"
          /p:OutputDirectory="$(Build.BinariesDirectory)"
          /p:CompilerMetadata="$(Build.BinariesDirectory)"

    - task: NuGetToolInstaller@1
      displayName: 'Install NuGet 3.3.0 for packaging'
      inputs:
        versionSpec: '3.3.0'

    - task: XppCreatePackage@3
      displayName: 'Create Power Platform unified package'
      inputs:
        XppToolsPath: '$(CompilerPackage)'
        CreateCloudPackage: true
        CloudPackagePlatVersion: '$(PlatformVersion)'
        CloudPackageAppVersion: '$(ApplicationVersion)'
        CloudPackageOutputLocation: '$(UnifiedPackageOutput)'
        DeployablePackagePath: '$(Build.ArtifactStagingDirectory)/AXDeployableRuntime.zip'

    - task: ArchiveFiles@2
      displayName: 'Zip unified package'
      inputs:
        rootFolderOrFile: '$(UnifiedPackageOutput)'
        includeRootFolder: false
        archiveType: 'zip'
        archiveFile: '$(Build.ArtifactStagingDirectory)/UnifiedPackage.zip'

    - task: PublishBuildArtifacts@1
      displayName: 'Publish unified package artifact'
      inputs:
        PathtoPublish: '$(Build.ArtifactStagingDirectory)/UnifiedPackage.zip'
        ArtifactName: 'UnifiedPackage'

    - task: PublishBuildArtifacts@1
      displayName: 'Publish traditional deployable package'
      inputs:
        PathtoPublish: '$(Build.ArtifactStagingDirectory)/AXDeployableRuntime.zip'
        ArtifactName: 'LCSPackage'

Pipeline Adımlarını Tek Tek Anlayalım

Adım Görev Ne yapıyor?
1. Checkout checkout: self İlgili Git commit’ini temiz çalışma alanına alır.
2. NuGet restore NuGetCommand@2 Compiler ve reference paketlerini indirir. -ExcludeVersion, klasör yollarını sürüm değişikliklerine karşı sabit tutar.
3. X++ compile VSBuild@1 Solution ve metadata’yı MSBuild ile derler. Hata durumunda önce restore logları ile ReferenceFolder yollarını kontrol etmek gerekir.
4. NuGet 3.3.0 NuGetToolInstaller@1 Deployable package formatıyla uyumluluk için NuGet 3.3.0′ı kurar.
5. Package XppCreatePackage@3 Power Platform unified package’ı; ihtiyaç varsa AXDeployableRuntime.zip çıktısını üretir.
6. Zip ArchiveFiles@2 Unified package klasörünü deployment görevinin beklediği zip dosyasına dönüştürür.
7. Artifact PublishBuildArtifacts@1 Çıktıları belirli bir build run altında saklar ve release pipeline’a hazırlar.
Resim 3 - Bizdeki örnek pipeline: build/package ve UAT deploy ayrı stage'ler halinde çalışıyor
Resim 3 – Bizdeki örnek pipeline: build/package ve UAT deploy ayrı stage'ler halinde çalışıyor

Unified Package ile Klasik Deployable Package Arasındaki Fark

Konu Klasik deployable package Power Platform unified package
Ana kullanım LCS tabanlı klasik deployment akışları Power Platform unified environment CI/CD akışı
X++ çıktısı AOT package binary’lerini içerir X++ runtime package’ını içerir
Dataverse solution Ayrı deployment akışı gerekir İstenirse aynı package içine eklenebilir
Pipeline görevi XppCreatePackage ile üretilebilir XppCreatePackage@3 + CreateCloudPackage
Deploy yaklaşımı LCS / klasik environment update PowerPlatformPackageDeploy veya pac package deploy

Yeni Unified Experience projelerinde benim yaklaşımım unified package’ı ana çıktı kabul etmek. Müşteri landscape’inde klasik LCS tabanlı ortamlar varsa geçiş sürecinde aynı build’den geleneksel deployable package üretmek faydalı olabilir. Gerektiğinde Dataverse managed solution’ları da unified package’a eklenebilir.

Pipeline’ı İlk Kez Çalıştırma

  1. Build/azure-pipelines.yml dosyasını repository’ye commit edin.
  2. Pipelines → New Pipeline ekranında Azure Repos Git ve ilgili repository’yi seçin.
  3. Existing Azure Pipelines YAML file seçeneğiyle Build/azure-pipelines.yml dosyasını gösterin.
  4. MetadataPath, SolutionPath, PlatformVersion ve ApplicationVersion değerlerini kontrol edip pipeline’ı manuel çalıştırın.
  5. Önce NuGet restore ve VSBuild, ardından XppCreatePackage@3 loglarını doğrulayın.
  6. Run Summary → Artifacts altında UnifiedPackage ve gerekiyorsa LCSPackage çıktılarını kontrol edin.
Resim 4 - Azure Repos Git üzerinden mevcut YAML dosyasıyla pipeline oluşturma
Resim 4 – Azure Repos Git üzerinden mevcut YAML dosyasıyla pipeline oluşturma
Resim 5 - Pipeline sonunda oluşan deployable ve unified package çıktıları
Resim 5 – Pipeline sonunda oluşan deployable ve unified package çıktıları

Branch Trigger mı, Nightly Build mi?

Pipeline ilk kurulduğunda trigger: none ile manuel çalıştırmak hata ayıklamayı kolaylaştırır. Yapı stabil hale geldikten sonra proje ihtiyacına göre farklı stratejiler kullanılabilir.

Yaklaşım Ne zaman uygun? Not
Pull Request validation Her PR’da compile kontrolü isteniyorsa Package üretimi opsiyonel tutulabilir
Dev branch trigger Dev branch’e her merge sonrası ortak build gerekiyorsa Hızlı geri bildirim sağlar
Nightly build Büyük solution’larda her commit pahalıysa Günlük tam build ve package üretimi için uygun
Main / release trigger Release adayında package üretilecekse Artifact release pipeline’a bağlanır

Büyük Dynamics 365 projelerinde her feature commit’inde full X++ package üretmek gereksiz maliyetli olabilir. PR aşamasında daha sınırlı validation, dev branch’te kontrollü build ve geceleri full package üretimi dengeli bir yaklaşım sağlar.

Sık Karşılaşılan Hatalar

Hata / belirti Muhtemel sebep ve kontrol
NuGet restore 401 Build service hesabının FinOpsNuGet feed üzerinde Reader yetkisi yoktur.
Reference bulunamıyor Beş NuGet paketinden biri eksik, packages.config sürümü yanlış veya ReferenceFolder path’i hatalıdır.
XppCreatePackage@3 task bulunamıyor Dynamics 365 Finance and Operations Tools extension yüklü değildir veya eski sürümdedir.
No X++ binary package(s) found VSBuild çıktısı beklenen Build.BinariesDirectory konumuna üretilmemiştir ya da .rnrproj doğru package’ı build etmiyordur.
fnomoduledefinition.json file not found CloudPackageOutputLocation veya XppToolsPath yanlıştır; package klasör yapısı oluşmamıştır.
Version mismatch CloudPackagePlatVersion / CloudPackageAppVersion ile restore edilen NuGet paketleri uyumlu değildir.
Package server hatası Package adımı için Microsoft örneğindeki NuGet 3.3.0 kurulumu eksiktir.
Descriptor / model bulunamıyor Metadata altında Descriptor klasörü repository’ye alınmamıştır.

Sürüm ve İzlenebilirlik

Pipeline yalnızca package üretmemeli; kaynağını da görünür tutmalı. Build.BuildNumber, Git commit SHA, branch, platform/application sürümleri, package adı ve pipeline run bağlantısı artifact ya da release notunda bulunmalı.

Böylece bir ortamda çalışan package’ın hangi commit’ten üretildiğini geriye dönük izleyebiliriz. “Bu kod Fatih’in bilgisayarında build olmuştu” yerine belirli bir commit ve pipeline run ile doğrulanabilir bir release kaydımız olur.

Quality Update Sonrasında

Yeni Compiler ve Build Reference paketlerini feed’e yükleyin; packages.config ile YAML içindeki PlatformVersion ve ApplicationVersion değerlerini aynı sürüm setiyle hizalayın. Ardından full X++ build ve package üretimini manuel bir run ile doğrulayın.

Benim önerim: Quality update ile production release’i aynı güne sıkıştırmadan önce build pipeline’ı yeni referans setiyle ayrı bir run üzerinde doğrulamak. NuGet paketleri ile environment sürümünü birlikte yönetmek UDE dönemindeki ALM disiplininin temel parçalarından biri.

Bu Bölümün Pratik Özeti

Azure DevOps pipeline ile source of truth Git’teki kontrollü commit olur; compiler ve reference set’i NuGet ile versiyonlanır, VSBuild temiz bir agent üzerinde derler ve XppCreatePackage@3 çıktıyı artifact olarak yayınlar.

Bana göre bu yapının en önemli faydası yalnızca otomasyon değil, tekrar üretilebilirlik. Aynı commit’in hangi referanslarla build edildiğini ve hangi package’ın ortaya çıktığını geriye dönük görebiliyoruz.

Bir Sonraki Bölüm

Bir sonraki bölümde UnifiedPackage artifact’ini Test, UAT ve Production ortamlarına taşıyan release/deploy pipeline’ı ele alacağım. Service connection, workload identity federation, approval mekanizmaları ve deployment history bu bölümün ana konuları olacak.

Selamlar.
Fatih Demirci
www.fatihdemirci.net

 
  • Trackback are closed
  • Comments (0)
  1. No comments yet.