Mert Özen Her Satırda Daha İleri
DevOps Azure DevOps Pipelines — #09

Classic Release Pipeline'larından YAML'a Geçiş ve 2023–2026'da Azure Pipelines'ta Değişenler

Mert Özen 15 Kas 2026 10 dk 6 görüntülenme
Classic Release Pipeline'larından YAML'a Geçiş ve 2023–2026'da Azure Pipelines'ta Değişenler

Classic release pipeline'ları YAML'a dışa aktarılamaz; build'ler Export to YAML ile başlar, release'ler stage, check ve template'lerle elle yeniden kurulur. Bookstore'da task group'u step template'ine, eski release'i yeniden deploy etmeyi pipeline resource'lu bir pipeline'a çeviriyor, classic oluşturmayı kapatıyor ve 2023–2026 arasındaki değişiklikleri, roadmap'i ve Azure DevOps'ta kalma kararını tarihleriyle topluyoruz.

Sürüm notu: Bu yazıdaki pipeline ve template dosyaları yq 4.54 ile ayrıştırılarak denetlenmiştir. Classic pipeline'ların, geçiş araçlarının ve 2023–2026 arasındaki değişikliklerin anlatımında Microsoft Learn'deki resmî belgelerden, Azure DevOps sürüm notlarından ve roadmap sayfasından (Kasım 2026 sürümleri) yararlanılmıştır.

Önce Tahmin Et

Hakan'ın ekibi, sitedeki serilerin ortak örnek uygulaması Bookstore'un (bir online kitapçı) eski bir kopyasını yıllardır classic pipeline'larla yayınlıyor: bir classic build, ondan beslenen dört stage'li bir classic release ve hafta içi her gece 02:00'de çalışan zamanlanmış bir build. Plan iki adım: build'i de release'i de YAML (girintiyle yapı kuran veri formatı) dosyasına "Export to YAML" ile aktarmak ve zamanlanmış build'i cron: "0 2 * * 1-5" olarak taşımak. İki adım da düşünüldüğü gibi çalışır mı?

İkisi de çalışmaz. Belgelere göre YAML'a dışa aktarma yalnızca classic build designer'da oluşturulmuş pipeline'larda var; classic release pipeline'larının dışa aktarması yok, task'ları tek tek taşımak gerekiyor. Zamanlama da kayıyor: classic pipeline'lar organization'ın yerel saat dilimini kullanıyor, YAML'daki cron ise UTC (eşgüdümlü evrensel saat). İstanbul saatiyle 02:00'de çalışan build, aynı cron'la taşınırsa 05:00'te çalışmaya başlıyor. Doğru karşılığı Pazar'dan Perşembe'ye UTC 23:00, yani "0 23 * * 0-4". Bu yazıda geçişin bu tür ayrıntılarını, serinin kurduğu yapının üzerinden bir araya getiriyoruz.

Eski Kurulumu Okumak ve Geçiş Haritası

Geçişe bir envanterle başlamak gerekiyor, çünkü classic tarafta bilgi YAML gibi tek dosyada değil, web arayüzünde birçok yere dağılmış durumda. Her classic build ve release için şunlara bakıyoruz: hangi artifact'ları tükettiği, stage'leri ve sırası, her stage'in pre- ve post-deployment koşulları (approval, gate, zamanlama), kullandığı task group'lar ve deployment group'lar, release ve stage değişkenleri ve hangi service connection'lara dokunduğu. Envanter bittiğinde eşleme şöyle:

ClassicBookstore'daki YAML karşılığıNot
Build definitionazure-pipelines.yml + pipelines/bookstore-ci.yml iskeletiExport to YAML başlangıç noktası olabilir
Release definition ve stage'leriİskeletteki Deploy_dev, Deploy_staging, Deploy_productionDışa aktarma yok; elle yazılır
Build artifactrelease pipeline artifact'ıBuild once, deploy many
Task groupStep template (steps/smoke-test.yml)Task group'lar YAML'da desteklenmiyor
Pre-deployment approval ve gateEnvironment ve service connection check'leriAltıncı yazıdaki tablo
Release ve stage değişkenleriVariable group'lar ve Key Vault'a bağlı grupSecret'lar dosyaya taşınmaz
Eski bir release'i bir stage'e yeniden deployazure-pipelines.redeploy.ymlPipeline resource ile
Zamanlanmış build/releaseschedules cronUTC; classic'te yerel saat
Deployment groupSanal makine resource'u olan environmentBookstore App Service kullandığı için gerekmedi

Export to YAML'ın verdiği dosya bir başlangıç. Belgeler classic editörde tanımlanmış değişkenlerin dosyaya taşınmadığını, YAML'da ya da pipeline ayarlarında yeniden tanımlanması gerektiğini söylüyor. Bizim için asıl iş zaten dışa aktarılan step'leri ortak iskeletin, template'lerin ve check'lerin içine yerleştirmekti; bunu serinin ilk sekiz yazısında yaptık.

Task Group'tan Step Template'e

Task group, classic'te birkaç task'ı tek bir yeniden kullanılabilir task'a çeviriyor; task'lardaki $(MyVariable) biçimindeki değerler task group'un parametreleri oluyor. Belgeler task group'ların YAML'da desteklenmediğini ve yerine template'lerin kullanılması gerektiğini söylüyor. Eski Bookstore'daki "Bookstore - Smoke test" task group'unu bir step template'ine çevirdik:

# Classic "Bookstore - Smoke test" task group'unun yerini alır: aynı step'ler, $(değişkenler) yerine parameter'lar.
parameters:
  - name: baseUrl
    type: string
  - name: attempts
    type: number
    default: 10
  - name: delaySeconds
    type: number
    default: 15

steps:
  - script: bash scripts/smoke-test.sh ${{ parameters.baseUrl }}
    displayName: Smoke test
    target:
      settableVariables: none
    env:
      SMOKE_ATTEMPTS: ${{ parameters.attempts }}
      SMOKE_DELAY_SECONDS: ${{ parameters.delaySeconds }}

Deploy template'i artık script'i doğrudan çağırmıyor, bu template'i kullanıyor:

                - template: ../steps/smoke-test.yml
                  parameters:
                    baseUrl: $(deployApi.AppServiceApplicationUrl)

İki fark önemli. Task group'ta parametreler metin; template'te number gibi tipleri ve izinli değer listeleri var, yanlış değer dosya işlenirken yakalanıyor. Task group'ta merkezi bir değişiklik onu kullanan bütün tanımlara anında yansıyor; template'te ise değişiklik yeni bir tag'le geliyor ve her pipeline ref'i yükselttiğinde alıyor. Bu ikincisi kısıt gibi görünse de üçüncü yazıda sürüm sabitlemeyi bilerek seçmiştik: bir template değişikliği bütün pipeline'ları aynı anda kıramıyor.

Eski Bir Release'i Yeniden Deploy Etmek

Classic release'in sık kullanılan bir özelliği, geçmişteki bir release'i seçip bir stage'e yeniden deploy etmek. YAML'da bunun karşılığı ayrı bir pipeline: CI pipeline'ını (Azure DevOps'taki adı bookstore-ci) bir pipeline resource'u olarak tanımlıyor ve bir runtime parameter ile ortam seçtiriyor. İkinci yazıda gördüğümüz environment seçimi burada asıl yerini buluyor:

# Daha önceki bir bookstore-ci run'ının release artifact'ını tek bir environment'a yeniden deploy eder.
# Run'ı bu pipeline'ı başlatırken "Resources" altında seç; environment'ın check'leri yine geçerli.
trigger: none
pr: none

parameters:
  - name: environment
    displayName: Environment
    type: string
    default: staging
    values:
      - dev
      - staging
      - production

resources:
  repositories:
    - repository: templates
      type: git
      name: Bookstore/pipeline-templates
      ref: refs/tags/v1.6.0
  pipelines:
    - pipeline: ci
      source: bookstore-ci
      trigger: none

extends:
  template: pipelines/bookstore-redeploy.yml@templates
  parameters:
    environment: ${{ parameters.environment }}

Belgelere göre run'ı elle başlatırken Resources bölümünden ci resource'u için istediğin run'ı seçebiliyorsun; seçmezsen son başarılı CI run'ı kullanılıyor. Yeni iskelet (pipelines/bookstore-redeploy.yml) tek bir deploy stage'i üretiyor ve deploy template'ine artifact'ın nereden geleceğini söylüyor:

stages:
  - template: ../stages/deploy.yml
    parameters:
      environment: ${{ parameters.environment }}
      dependsOn: []
      mainOnly: ${{ eq(parameters.environment, 'production') }}
      useSlot: ${{ eq(parameters.environment, 'production') }}
      artifactPipeline: ${{ parameters.artifactPipeline }}

Deploy template'i artifactPipeline current değilse iki şeyi değiştiriyor: artifact'ı download: ci ile indiriyor ve klasörü $(Pipeline.Workspace)/ci/release olarak kullanıyor (belgelere göre pipeline resource artifact'ları <pipeline-identifier>/<artifact-identifier> altına iniyor). Beklenen commit de $(resources.pipeline.ci.sourceCommit) oluyor. Altıncı yazıdaki commit kontrolünün değeri burada ortaya çıkıyor: seçilen run'ın artifact'ı başka bir commit'ten gelmişse doğrulama step'i deploy'u durduruyor.

Check'ler için bir adım daha var. bookstore-production environment'ındaki ve bookstore-azure-production connection'ındaki required template check'ine ikinci bir şablon olarak pipelines/bookstore-redeploy.yml'ı ekliyoruz; aksi hâlde redeploy pipeline'ı production'a giremiyor. Approval, branch control ve exclusive lock aynen geçerli. Template'lerin bu sürümü v1.6.0.

Paralel Çalıştırma ve Classic'i Kapatmak

Geçişi tek seferde yapmak yerine bir süre iki yolu yan yana çalıştırdık. İlk hafta YAML pipeline'ı yalnızca dev ve staging'e deploy etti, production hâlâ classic release'teydi; aynı build'in iki yoldan çıkan paketlerini manifest'teki commit ve dosya sayılarıyla karşılaştırdık. Production'ı YAML'a geçirdiğimiz gün classic release'in trigger'larını kapattık ama tanımı silmedik; geri dönüş gerekirse orada duruyordu. İki sprint sonra classic tanımları sildik.

Son adım classic'in geri gelmesini engellemek. Organization ya da project ayarlarında "Disable creation of classic build pipelines" ve "Disable creation of classic release pipelines" ayrı ayrı açılabiliyor; ikisi birden açıkken classic build, classic release, task group ve deployment group ne arayüzden ne REST (HTTP üzerinden kaynak odaklı API tarzı) API'den oluşturulabiliyor. Belgelerin gerekçesi güvenlik: YAML ve classic pipeline'lar aynı service connection'ları paylaşabildiği için, classic bir tanım YAML'daki check'lerin etrafından dolaşan bir yol açabiliyor. Sprint 226'dan beri yeni organization'larda bu ayar varsayılan olarak açık geliyor. Classic release'ler için ayrı bir job authorization scope ayarı da var; geçiş bitene kadar onu da project'e daraltmak gerekiyor.

2023–2026'da Azure Pipelines'ta Değişenler

Serinin dayandığı değişiklikleri tarih sırasıyla toplayalım. Her madde sürüm notlarından ya da ilgili belge sayfasından:

  • Ağustos 2023, Sprint 226: yeni organization'larda classic build ve release oluşturma varsayılan olarak kapalı (Sprint 225'te önceden duyuruldu).
  • Şubat 2024, Sprint 234: Azure Resource Manager service connection'ları için workload identity federation GA (genel kullanıma açık).
  • 18 Kasım 2024: Managed DevOps Pools GA olarak duyuruldu; Sprint 251 sürüm notları da GA'yı listeliyor. Roadmap'e göre yeni özellikler Scale Set pool'larına değil buraya geliyor.
  • Mart 2025, Sprint 253: yeni Azure ve Docker service connection'ları Microsoft Entra issuer'ı ile oluşturuluyor.
  • 31 Aralık 2025: windows-2019 hosted image'ı emekliye ayrıldı.
  • 1 Temmuz 2026: Azure DevOps issuer'ı deprecated; 1 Temmuz 2027'de emekliye ayrılıyor.
  • 29 Eylül 2026: ubuntu-26.04 ve windows-2025-vs2026 image'ları GA; ubuntu-latest şu an ubuntu-24.04, ubuntu-22.04 17 Nisan 2027'de kaldırılacak.
  • 24 Kasım 2026: Node.js 6, 10 ve 16 agent'tan kaldırılıyor; agent Node.js 24'ü içeriyor, resmî task'ların Node.js 24'e geçişi roadmap'te tarihsiz.
  • 2027: public project'ler private'a çevriliyor.

Roadmap'in "YAML and release pipelines feature parity" bölümü Microsoft'un yönünü açıkça yazıyor: son yıllarda bütün pipeline yatırımları ve bütün güvenlik iyileştirmeleri YAML tarafında yapıldı ve classic release'ten YAML'a geçişi zorlaştıran CD eksiklerini kapatmak hedefleniyor. Bu bölümdeki iki madde serinin ilgilendiği eksiklere dokunuyor: "On-demand out of order execution of stages", yani bağımlılıklarını beklemeden bir sonraki stage'i elle başlatabilmek, 2027 Q1 (yılın ilk çeyreği) olarak görünüyor. "Stage-level traceability", yani bir stage'i en son hangi run'ın çalıştırdığını gösteren stage odaklı görünüm ise sayfada iki farklı yerde iki farklı zamanla geçiyor: tüm özellikler tablosunda 2026 Q4 (yılın son çeyreği) altında, aynı bölümün kendi tablosunda 2027 Q1 olarak. "Service connections in checks" ve "Checks extensibility" tarihsiz "Future" maddeleri.

Karar Notu: Azure DevOps'ta Kalmak mı, GitHub Actions'a Geçmek mi

Classic'ten çıkan bir ekip bu soruyu da soruyor: madem bir geçiş yapılıyor, doğrudan GitHub Actions'a mı gidilmeli? İki seçeneği karşılaştıralım.

Azure DevOps'ta kalıp YAML'a geçmek: Boards, Repos ve Artifacts ile mevcut entegrasyonlar, izinler ve Entra ID yapısı yerinde kalıyor; geçişin kapsamı yalnızca pipeline'lar. Microsoft'un yatırımı da sürüyor: roadmap'te Azure Repos için Copilot code review'ı 2026 Q3 (üçüncü çeyrek) içinde public preview oldu, GitHub Advanced Security for Azure DevOps için 2026 Q4'e planlı maddeler var, Managed DevOps Pools düzenli yeni özellik alıyor. Bedeli, bazı CD özelliklerinin hâlâ roadmap'te beklemesi.

GitHub Actions'a geçmek: kod ve CI aynı yerde, marketplace ve topluluk geniş. Ama bu bir pipeline geçişi değil, bir platform geçişi: repository'ler, izinler, paket feed'leri ve iş takibi birlikte taşınıyor. Kavramların birebir karşılıkları ilk yazıdaki tabloda; Actions'ın kendisi sitedeki GitHub Actions serisinde anlatıldı. Bookstore ekibi için karar Azure DevOps'ta kalmak oldu: geçişin riski pipeline'larla sınırlı kaldı ve bu seride kurduğumuz iskelet, check'ler ve federation connection'ları zaten YAML tarafındaki yatırımın üzerinde duruyor.

Azure DevOps Server kullananlar için bir not: bu seri Services'ı anlattı. Belgelere göre Microsoft-hosted agent'lar Server'da yok, self-hosted agent gerekiyor. Azure service connection belgesinin Server sürümü de secret ya da sertifikayla çalışan bir service principal'ı anlatıyor; workload identity federation bölümleri Services için yazılmış. Azure DevOps Server 2022'de agent kaydı için belgeler PAT (kişisel erişim token'ı) ve Alternate kimlik doğrulamasını sayıyor.

Üretimde Dikkat

Classic tanımı silmeden önce geçmişini dışarı al. Release'lerin kim tarafından, hangi artifact'la, hangi onayla yapıldığı denetim için gerekebiliyor; YAML tarafındaki environment geçmişi geçiş gününden başlıyor.

Gate'leri birebir taşıyamayabilirsin. Query work items ve Azure Policy compliance gate'lerinin check karşılığı yok; bunları ya Invoke REST API check'iyle kendi servisine taşıman ya da süreçten çıkarman gerekiyor.

Zamanlamaları UTC'ye çevirirken yaz saati uygulayan ülkeleri unutma. UTC sabit kalıyor, yerel saat değişiyor; böyle bir ofisle çalışıyorsan cron'u yılda iki kez gözden geçirmek gerekebilir.

Sık Düşülen Tuzaklar

Release'i de dışa aktarmayı beklemek. Export to YAML yalnızca classic build designer'daki pipeline'larda var ve tanımın kendisinde görünüyor, belirli bir run'da değil.

Classic değişkenleri unutmak. Dışa aktarılan dosya classic editörde tanımlanmış değişkenleri içermiyor; ilk YAML run'ı boş bir değişkenle sessizce yanlış bir şey yapabilir.

Classic'i açık bırakmak. Geçiş bittikten sonra classic oluşturma kapatılmazsa biri yeni bir classic release açıp YAML'daki check'lerin dışından aynı service connection'ı kullanabilir.

Redeploy pipeline'ını required template check'ine eklemeyi unutmak. Pipeline doğru yazılsa da production'da check'e takılıyor; hata mesajı bir şablon eksikliğini söylüyor, pipeline'ın kendisini değil.

Küçük Bir Deneme

★☆☆ Hafta içi her sabah İstanbul saatiyle 07:30'da çalışması gereken bir build için YAML cron ifadesini yaz.

★★☆ Bir classic release'inin envanterini bu yazıdaki tabloya göre çıkar: stage'ler, approval'lar, gate'ler, task group'lar, değişkenler, service connection'lar. Her satırın YAML karşılığını yaz.

★★★ Redeploy pipeline'ına ikinci bir runtime parameter ekle: yalnızca API'yi ya da yalnızca web'i deploy etmeyi seçtirsin. Bunu deploy template'inde koşullu ekleme ile yap ve production check'lerinin bundan nasıl etkileneceğini düşün.

Seri burada bitiyor. Dokuz yazıda Bookstore'un pipeline'larını kodla yazdık; web arayüzünde yalnızca resource'ları, izinleri ve check'leri tanımladık: ortak template'ler, .NET ve Angular CI, Azure Artifacts feed'i, onaylı ve check'li deploy stage'leri, şifresiz Azure bağlantıları ve sıkılaştırılmış agent ayarları. Buradan devam etmek isteyenler için Azure kaynaklarını kodla kurmak Infrastructure as Code: Terraform serisinin, dependency ve code scanning ise DevSecOps serisinin konusu.