Azure Devops'ta Yaml Dosyaları ile Pipeline Oluşturma

Azure Devops

Azure DevOps, yazılım geliştirme sürecini desteklemeyi ve  bir işbirliği kültürü oluşturmayı amaçlayan bir dizi hizmet ve araçtır. Şirketler geliştiricileri, proje yöneticilerini ve katkıda bulunanları bir araya getirerek ürünleri geleneksel yazılım geliştirme yaklaşımlarından daha hızlı geliştirebilirler. Azure DevOps, bulutta Azure DevOps Hizmetleri olarak veya şirket içinde Azure DevOps Server olarak kullanılabilir.

Azure DevOps YAML

Azure DevOps YAML, Azure Pipelines’ta sürekli entegrasyon (CI) ve sürekli teslim (CD) işlemlerini tanımlamak için kullanılan bir yapılandırma dilidir. YAML, “YAML İşaretleme Dili Değildir” ifadesinin yinelemeli bir kısaltmasıdır ve verileri serileştirmek için kullanılır. Azure DevOps, kodun nasıl oluşturulduğunu, test edildiğini ve dağıtıldığını tanımlayan işlem hatları oluşturmak için YAML dosyalarını kullanır.

YAML dosyaları bir işlem hattında kullanılan aşamaları, işleri, adımları ve kaynakları belirtir. Bu, işlem hattınızın herhangi bir yönünün kod olarak ifade edilmesine ve bir sürüm kontrol sisteminde saklanmasına olanak tanır. Bu, kodunuzda yaptığınız gibi işlem hattı yapılandırmanızdaki değişiklikleri izlemenize ve geri almanıza olanak tanır.

Azure DevOps YAML Sözdizimi

Azure DevOps YAML sözdizimi, Azure Pipelines ile CI/CD işlemlerini otomatikleştirmek için kullanılan yapılandırma dosyalarının yapısını tanımlar. İskelet yapısı boru hattının farklı kısımlarını ve bunların çalışma sırasını belirler. Temel bileşenler ve işlevleri şunlardır:

Stages: İşlem hattını mantıksal bölümlere ayırmak için kullanılır. Örneğin, bir oluşturma aşaması, test aşaması ve dağıtım aşaması oluşturabilirsiniz. Her aşama bir veya daha fazla görevi içerebilir. İş: Aşama içindeki işi tanımlar. Her iş, belirli bir görevi veya görev dizisini gerçekleştiren adımlardan oluşur. İşler paralel veya sıralı olarak yürütülebilir.

Steps: Bunlar bir boru hattının gerçekleştirdiği en küçük görev birimleridir. Komutlar, komut dosyaları ve görevler gibi eylemleri içerebilir.

Pool: İşin çalıştırılacağı aracı veya aracı havuzunu belirtin. Örneğin “vmImage:ubuntu-latest” gibi bir sanal makine görüntüsü tanımlayabilirsiniz.

Triggers: İşlem hattının ne zaman tetikleneceğini belirler. Bu tetikleyiciler kod değişiklikleri, planlanmış tetikleyiciler veya harici olaylar olabilir.

Resources: İşlem hattı tarafından kullanılan dış kaynakları tanımlayın. Bunlar diğer depolar, paketler olabilir.

Variables: İşlem hattında kullanılan değişkenleri tanımlayın. Bu değişkenler Pipeline’in farklı aşamalarında yeniden kullanılabilir.

Parameters: İşlem hattı çalıştırıldığında içe aktarılan parametreleri tanımlayın. Bu, boru hattını daha esnek hale getirir.

Azure DevOps YAML dosyasının temel bir iskelet yapısı şöyle olabilir:

trigger:
- main

stages:
- stage: Build
  jobs:
  - job: BuildJob
    pool:
      vmImage: 'ubuntu-latest'
    steps:
    - script: echo Building the project...
      displayName: 'Build'

- stage: Test
  jobs:
  - job: TestJob
    pool:
      vmImage: 'ubuntu-latest'
    steps:
    - script: echo Running tests...
      displayName: 'Test'

- stage: Deploy
  jobs:
  - job: DeployJob
    pool:
      vmImage: 'ubuntu-latest'
    steps:
    - script: echo Deploying to production...
      displayName: 'Deploy'

Bu örnekte, trigger anahtar kelimesi ile pipeline’ın hangi dalın değişikliklerinde çalışacağı belirlenir. stages ile farklı aşamalar tanımlanır ve her aşama kendi içinde jobs ile işleri barındırır. Her işin pool ile çalışacağı agent ortamı ve steps ile gerçekleştireceği adımlar belirtilir.

Azure DevOps YAML sözdizimi ve iskelet yapısı hakkında daha fazla bilgi almak için Microsoft Learn adresini ziyaret edebilirsiniz.

Basit Düzeyde Sözdizimi Örnekleri

Trigger ve Pool Örneği: Bu örnekte, main dalındaki herhangi bir değişiklik olduğunda pipeline tetiklenir ve ubuntu-latest sanal makine imajında çalışır.

trigger:
- main

pool:
  vmImage: 'ubuntu-latest'

Adım Örneği: Burada, bir script çalıştırılır ve ardından bir sonraki adımda bir görev (task) kullanılır.

steps:
- script: echo Hello, Azure DevOps!
  displayName: 'Greet Azure DevOps'

- task: CopyFiles@2
  inputs:
    SourceFolder: 'source'
    TargetFolder: 'target'

Job ve Adımlar Örneği: Bu örnekte, bir iş (job) tanımlanır ve iki adım içerir: bir script çalıştırma ve bir dosya kopyalama görevi.

jobs:
- job: ExampleJob
  pool:
    vmImage: 'ubuntu-latest'
  steps:
  - script: echo Compiling the code...
    displayName: 'Compile Code'

  - task: CopyFiles@2
    inputs:
      SourceFolder: 'bin'
      TargetFolder: 'artifact'

Aşama ve İşler Örneği: Bu örnekte, iki aşamalı bir pipeline tanımlanır: Build ve Deploy. Her aşama kendi işlerini içerir.

stages:
- stage: Build
  jobs:
  - job: BuildJob
    pool:
      vmImage: 'ubuntu-latest'
    steps:
    - script: echo Building the project...
      displayName: 'Build Project'

- stage: Deploy
  jobs:
  - job: DeployJob
    pool:
      vmImage: 'ubuntu-latest'
    steps:
    - script: echo Deploying to production...
      displayName: 'Deploy Project'

Çoklu İşler ve Koşullu Adımlar Örneği: Burada, iki farklı iş tanımlanır ve her işin koşullu adımları vardır.

jobs:
- job: Build
  steps:
  - script: echo Building the project...
    displayName: 'Build Project'
    condition: and(succeeded(), eq(variables['Build.Reason'], 'IndividualCI'))

- job: Deploy
  steps:
  - script: echo Deploying to production...
    displayName: 'Deploy Project'
    condition: and(succeeded(), eq(variables['Build.Reason'], 'Manual'))

Matris Stratejisi Örneği: Bu örnekte, farklı konfigürasyonlar için paralel işler çalıştırılır.

jobs:
- job: Build
  strategy:
    matrix:
      Windows:
        imageName: 'windows-latest'
      Linux:
        imageName: 'ubuntu-latest'
  pool:
    vmImage: $(imageName)
  steps:
  - script: echo Building the project on $(imageName)...
    displayName: 'Build Project'

Kaynaklar ve Servis Bağlantıları Örneği: Burada, diğer repository’ler ve servis bağlantıları tanımlanır.

resources:
  repositories:
  - repository: MyGitHubRepo
    type: github
    name: MyOrg/MyRepo
    endpoint: MyGitHubServiceConnection

  services:
  - service: MyDockerRegistry
    type: dockerregistry
    connection: MyDockerRegistryServiceConnection

Aşamalar ve Koşullu Aşamalar Örneği: Bu örnekte, koşullu aşamalar kullanılarak farklı senaryolara göre aşamalar çalıştırılır.

stages:
- stage: Build
  condition: eq(variables['Build.SourceBranch'], 'refs/heads/main')
  jobs:
  - job: BuildJob
    steps:
    - script: echo Building the project...
      displayName: 'Build'

- stage: Deploy
  condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
  jobs:
  - job: DeployJob
    steps:
    - script: echo Deploying to production...
      displayName: 'Deploy'

İleri Düzeyde Sözdizimi Örnekleri

Bu örnekte, birden fazla aşama, iş ve koşullu adımlar içeren bir pipeline tanımlanmaktadır. Ayrıca, matris stratejisi kullanılarak farklı konfigürasyonlar için paralel işler çalıştırılmaktadır.

trigger:
- main
- feature/*

variables:
  buildConfiguration: 'Release'
  buildPlatform: 'Any CPU'

stages:
- stage: Build
  displayName: 'Build stage'
  jobs:
  - job: BuildJob
    displayName: 'Build'
    pool:
      vmImage: 'windows-latest'
    strategy:
      matrix:
        Debug:
          buildConfiguration: 'Debug'
        Release:
          buildConfiguration: 'Release'
    steps:
    - script: dotnet build --configuration $(buildConfiguration)
      displayName: 'Dotnet Build $(buildConfiguration)'

- stage: Test
  displayName: 'Test stage'
  jobs:
  - job: TestJob
    displayName: 'Run Tests'
    pool:
      vmImage: 'windows-latest'
    steps:
    - script: dotnet test --configuration $(buildConfiguration) --collect "Code coverage"
      displayName: 'Dotnet Test'

- stage: Deploy
  displayName: 'Deploy stage'
  condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
  jobs:
  - deployment: DeployJob
    displayName: 'Deploy to Production'
    environment: 'production'
    strategy:
      runOnce:
        deploy:
          steps:
          - script: echo Deploying to production...
            displayName: 'Deploy Script'
          - task: AzureRmWebAppDeployment@4
            inputs:
              azureSubscription: 'AzureSubscription'
              appType: 'webApp'
              appName: 'MyProductionApp'
              package: '$(Pipeline.Workspace)/drop/app.zip'

Bu örnekte, trigger anahtar kelimesi ile main ve feature/* dallarındaki değişikliklerde pipeline’ın tetiklenmesi sağlanır. variables ile genel değişkenler tanımlanır. stages anahtar kelimesi ile üç aşama (BuildTestDeploy) belirlenir ve her aşama kendi içinde jobs ile işleri barındırır. Build aşamasında, strategy ile Debug ve Release konfigürasyonları için paralel işler tanımlanır. Test aşamasında, testler çalıştırılır ve kod kapsamı toplanırDeploy aşaması, sadece main dalındaki başarılı build’ler için çalışacak şekilde koşullu olarak ayarlanır ve deployment tipinde bir iş içerir.

Çoklu Ajan Kullanımı

Bu örnekte bir yml dosyası içerisinde birden fazla ajan kullanımının nasıl gerçekleştireleceği örneği verilmiştir.

stages:
- stage: Build
  displayName: 'Build Application'
  jobs:
  - job: BuildJob
    pool:
      name: WindowsAgents
    steps:
    - script: build.cmd
      displayName: 'Build'

- stage: Test
  displayName: 'Run Tests'
  jobs:
  - job: TestJob
    pool:
      name: LinuxAgents
    steps:
    - script: test.sh
      displayName: 'Test'

- stage: Deploy
  displayName: 'Deploy to Staging'
  jobs:
  - job: DeployJob
    pool:
      name: MacAgents
    steps:
    - script: deploy.sh
      displayName: 'Deploy'

Azure Devops Ne Kadar Esnek?

Azure DevOps YAML yapılandırmalarının dinamik ve esnek olduğunu gösteren ileri düzey örnekler aşağıdaki gibidir.

  1. Dinamik Değişkenler ve Parametreler: Bu örnekte, pipeline parametreleri ve koşullu ifadeler kullanılarak dinamik yapılandırmalar sağlanmıştır.
parameters:
- name: deployEnvironment
  displayName: 'Deploy Environment'
  type: string
  default: 'staging'
  values:
  - staging
  - production

stages:
- stage: Build
  jobs:
  - job: BuildJob
    steps:
    - script: echo Building the project...
      displayName: 'Build'

- $:
  - stage: DeployStaging
    jobs:
    - deployment: StagingJob
      environment: staging
      steps:
      - script: echo Deploying to staging...
        displayName: 'Deploy to Staging'

- $:
  - stage: DeployProduction
    jobs:
    - deployment: ProductionJob
      environment: production
      steps:
      - script: echo Deploying to production...
        displayName: 'Deploy to Production'

  1. Şablon Kullanımı: Bu yapılandırmada, tekrar eden yapılandırmaları azaltmak için şablonlar kullanılmaktadır.
jobs:
- template: build-template.yml  # Template reference
  parameters:
    solution: '**/*.sln'
    buildPlatform: 'Any CPU'
    buildConfiguration: 'Release'

- template: test-template.yml  # Template reference
  parameters:
    testFramework: 'NUnit'
    testFiles: '**/*Tests.dll'

  1. Koşullu Tetikleyiciler ve Stratejiler: Bu örnekte, farklı dallar ve etiketler için koşullu tetikleyiciler ve dağıtım stratejileri tanımlanmıştır.
trigger:
  branches:
    include:
    - main
    - feature/*
  tags:
    include:
    - v*

pr:
  branches:
    include:
    - main

stages:
- stage: Build
  jobs:
  - job: BuildJob
    steps:
    - script: echo Building the project...
      displayName: 'Build'

- stage: Deploy
  condition: startsWith(variables['Build.SourceBranch'], 'refs/tags/v')
  jobs:
  - job: DeployJob
    steps:
    - script: echo Deploying the project...
      displayName: 'Deploy'

Final (End-To-End Pipeline)

Test

trigger:
- master

stages:
- stage: sonatype
  pool: centos

  jobs:
    - job: sonatype
      steps:
      - task: NexusIqPipelineTask@1
        inputs:
          nexusIqService: 'sonatype'
          applicationId: 'webgoat'
          stage: 'release'
          scanTargets: '**/*.*'

- stage: fortify
  pool: batuhan-local
  
  jobs:
    - job: fortify
      steps: 
      
      - task: FortifySCA@7
        inputs:
           applicationType: 'java'
           buildSourceVersion: '11'
           fortifyBuildId: 'webgoatjava'
           fortifyScanType: 'LocalScan'
           runFortifyUpload: true
           fortifyServerName: 'ssc test'
           fortifyApplicationName: 'webgoatjava'
           fortifyApplicationVersion: 'webgoatjava'

- stage: trivy_file_scan
  pool: centos

  jobs: 
    - job: trivy 
      steps:
        
      - task: Bash@3
        inputs:
          targetType: 'inline'
          script: 'trivy repo https://github.com/nullx3d/WebGoat'

- stage: docker_up
  pool: centos

  jobs: 
    - job: docker
      steps:

      - task: CmdLine@2
        inputs:
         script: 'docker-compose up -d'
         workingDirectory: '/home/sancak/Downloads/WebGoat'

- stage: trivy_image_scan
  pool: centos

  jobs:
     - job: 
       steps:
         - task: Bash@3
           inputs:
            targetType: 'inline'
            script: 'trivy image webgoat/webgoat-8.0'

- stage: invicti
  
  jobs: 
    - job: invicti
      steps:

        - task: netsparker-cloud@1
          inputs:
           apiConnection: 'invicti'
           scanTypes: '0'
           scanWebSites: 'e15b8874-5cff-4dd7-02a7-ae43025f6459'
           scanWebSitesProfile: '5d21265b-4372-49fc-c209-b0ff051e9f2f'
           hasReport: true
           reportType: 'ScanDetail'

Written by

Batuhan Sancak