Azure DevOps Pipeline ile UDE Build ve Package Üretimi
Azure DevOps Pipeline ile UDE Build ve Package Üretimi
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.
- Azure DevOps organizasyonu, proje ve Git repository
- Dynamics 365 Finance and Operations Tools ile Power Platform Build Tools extension’ları
- Build edilecek package’ı temsil eden .rnrproj ve tercihen .sln dosyası
- Custom metadata, Descriptor klasörü ve model descriptor dosyaları
- Hedef environment sürümüyle uyumlu X++ Compiler ve Build Reference NuGet paketleri
- 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.

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

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. |

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
- Build/azure-pipelines.yml dosyasını repository’ye commit edin.
- Pipelines → New Pipeline ekranında Azure Repos Git ve ilgili repository’yi seçin.
- Existing Azure Pipelines YAML file seçeneğiyle Build/azure-pipelines.yml dosyasını gösterin.
- MetadataPath, SolutionPath, PlatformVersion ve ApplicationVersion değerlerini kontrol edip pipeline’ı manuel çalıştırın.
- Önce NuGet restore ve VSBuild, ardından XppCreatePackage@3 loglarını doğrulayın.
- Run Summary → Artifacts altında UnifiedPackage ve gerekiyorsa LCSPackage çıktılarını kontrol edin.


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







No comments yet.