DevOps

Building CI/CD Pipelines with Azure DevOps

January 10, 2026
CRX Partners Team
15 min read

What is CI/CD?

Continuous Integration (CI) and Continuous Deployment (CD) are practices that automate the building, testing, and deployment of applications. Azure Pipelines provides a powerful, flexible platform to implement CI/CD for any technology stack, including Dynamics 365 and Power Platform solutions. Azure Pipelines.

CI/CD Benefits

  • Faster delivery: Automate repetitive tasks
  • Higher quality: Catch issues early with automated testing
  • Reduced risk: Smaller, incremental changes
  • Consistency: Same process every deployment
  • Traceability: Link deployments to code changes

Azure Pipelines Overview

Pipeline Types

  • YAML Pipelines: Pipeline-as-code, version controlled, recommended approach
  • Classic Pipelines: Visual designer, UI-based configuration

Key Concepts

  • Trigger: What starts the pipeline (commit, schedule, manual)
  • Stage: Logical division (Build, Test, Deploy)
  • Job: Unit of work running on an agent
  • Step: Individual task or script
  • Agent: Machine that runs the pipeline

YAML Pipeline Basics

Simple Build Pipeline

# azure-pipelines.yml
      trigger:
        branches:
          include:
            - main
            - develop

      pool:
        vmImage: 'windows-latest'

      variables:
        solution: '**/*.sln'
        buildPlatform: 'Any CPU'
        buildConfiguration: 'Release'

      stages:
      - stage: Build
        displayName: 'Build Stage'
        jobs:
        - job: BuildJob
          displayName: 'Build Solution'
          steps:
          - task: NuGetToolInstaller@1
            displayName: 'Install NuGet'

          - task: NuGetCommand@2
            displayName: 'Restore NuGet Packages'
            inputs:
              restoreSolution: '$(solution)'

          - task: VSBuild@1
            displayName: 'Build Solution'
            inputs:
              solution: '$(solution)'
              platform: '$(buildPlatform)'
              configuration: '$(buildConfiguration)'

          - task: VSTest@2
            displayName: 'Run Unit Tests'
            inputs:
              platform: '$(buildPlatform)'
              configuration: '$(buildConfiguration)'

          - task: PublishBuildArtifacts@1
            displayName: 'Publish Artifacts'
            inputs:
              pathtoPublish: '$(Build.ArtifactStagingDirectory)'
              artifactName: 'drop'

Dynamics 365 CI/CD Pipeline

Power Platform Build Tools

Microsoft provides Power Platform Build Tools for Azure DevOps to automate solution management:

# Dynamics 365 Solution Pipeline
      trigger:
        branches:
          include:
            - main

      pool:
        vmImage: 'windows-latest'

      variables:
        SolutionName: 'MySolution'
        ServiceConnection: 'PowerPlatform-Dev'

      stages:
      - stage: Export
        displayName: 'Export from Dev'
        jobs:
        - job: ExportSolution
          steps:
          - task: PowerPlatformToolInstaller@2
            displayName: 'Install Power Platform Tools'

          - task: PowerPlatformExportSolution@2
            displayName: 'Export Unmanaged Solution'
            inputs:
              authenticationType: 'PowerPlatformSPN'
              PowerPlatformSPN: '$(ServiceConnection)'
              SolutionName: '$(SolutionName)'
              SolutionOutputFile: '$(Build.ArtifactStagingDirectory)/$(SolutionName).zip'
              Managed: false

          - task: PowerPlatformUnpackSolution@2
            displayName: 'Unpack Solution'
            inputs:
              SolutionInputFile: '$(Build.ArtifactStagingDirectory)/$(SolutionName).zip'
              SolutionTargetFolder: '$(Build.SourcesDirectory)/solutions/$(SolutionName)'
              SolutionType: 'Both'

          - task: PublishBuildArtifacts@1
            displayName: 'Publish Solution Artifact'
            inputs:
              pathtoPublish: '$(Build.ArtifactStagingDirectory)'
              artifactName: 'solution'

      - stage: Build
        displayName: 'Build Managed Solution'
        dependsOn: Export
        jobs:
        - job: PackSolution
          steps:
          - task: PowerPlatformToolInstaller@2

          - task: DownloadBuildArtifacts@1
            inputs:
              buildType: 'current'
              downloadType: 'single'
              artifactName: 'solution'

          - task: PowerPlatformPackSolution@2
            displayName: 'Pack as Managed'
            inputs:
              SolutionSourceFolder: '$(Build.SourcesDirectory)/solutions/$(SolutionName)'
              SolutionOutputFile: '$(Build.ArtifactStagingDirectory)/$(SolutionName)_managed.zip'
              SolutionType: 'Managed'

          - task: PublishBuildArtifacts@1
            inputs:
              pathtoPublish: '$(Build.ArtifactStagingDirectory)'
              artifactName: 'managed-solution'

Deployment Pipeline

- stage: DeployTest
        displayName: 'Deploy to Test'
        dependsOn: Build
        condition: succeeded()
        jobs:
        - deployment: DeployToTest
          displayName: 'Deploy to Test Environment'
          environment: 'Test'
          strategy:
            runOnce:
              deploy:
                steps:
                - task: PowerPlatformToolInstaller@2

                - task: PowerPlatformImportSolution@2
                  displayName: 'Import Solution'
                  inputs:
                    authenticationType: 'PowerPlatformSPN'
                    PowerPlatformSPN: 'PowerPlatform-Test'
                    SolutionInputFile: '$(Pipeline.Workspace)/managed-solution/$(SolutionName)_managed.zip'
                    AsyncOperation: true
                    MaxAsyncWaitTime: '60'

                - task: PowerPlatformPublishCustomizations@2
                  displayName: 'Publish Customizations'
                  inputs:
                    authenticationType: 'PowerPlatformSPN'
                    PowerPlatformSPN: 'PowerPlatform-Test'

      - stage: DeployProd
        displayName: 'Deploy to Production'
        dependsOn: DeployTest
        condition: succeeded()
        jobs:
        - deployment: DeployToProd
          displayName: 'Deploy to Production'
          environment: 'Production'
          strategy:
            runOnce:
              deploy:
                steps:
                - task: PowerPlatformToolInstaller@2

                - task: PowerPlatformImportSolution@2
                  inputs:
                    authenticationType: 'PowerPlatformSPN'
                    PowerPlatformSPN: 'PowerPlatform-Prod'
                    SolutionInputFile: '$(Pipeline.Workspace)/managed-solution/$(SolutionName)_managed.zip'
                    AsyncOperation: true
                    MaxAsyncWaitTime: '120'

Service Connections

Power Platform Service Principal

Configure service connections for automated deployments:

  1. Create Azure AD App Registration
  2. Grant Dataverse API permissions
  3. Create Application User in Power Platform Admin Center
  4. Assign appropriate security roles
  5. Create Service Connection in Azure DevOps project settings

Environment Approvals and Gates

Manual Approvals

Configure approval gates for production deployments:

  • Define approvers for each environment
  • Set timeout and approval policies
  • Require multiple approvers for critical environments
  • Email notifications for pending approvals

Automated Gates

  • Azure Monitor alerts: Check for active incidents
  • Work item queries: Ensure all bugs are resolved
  • REST API calls: Custom validation endpoints
  • Azure Policy compliance: Security checks

Variables and Secrets

Variable Groups

Organize variables by environment:

variables:
      - group: 'PowerPlatform-Dev-Variables'
      - group: 'Common-Variables'
      - name: localVariable
        value: 'someValue'

Azure Key Vault Integration

Link variable groups to Azure Key Vault for secure secret management:

  • Store connection strings and API keys in Key Vault
  • Create variable group linked to Key Vault
  • Secrets automatically fetched at runtime
  • Audit access through Key Vault logs

Testing in Pipelines

Unit Testing

- task: VSTest@2
        displayName: 'Run Unit Tests'
        inputs:
          testSelector: 'testAssemblies'
          testAssemblyVer2: |
            **/*Tests.dll
            !**/obj/**
          searchFolder: '$(System.DefaultWorkingDirectory)'
          codeCoverageEnabled: true
          platform: '$(buildPlatform)'
          configuration: '$(buildConfiguration)'

Solution Checker

Run static analysis on Power Platform solutions:

- task: PowerPlatformChecker@2
        displayName: 'Run Solution Checker'
        inputs:
          authenticationType: 'PowerPlatformSPN'
          PowerPlatformSPN: '$(ServiceConnection)'
          FilesToAnalyze: '$(Build.ArtifactStagingDirectory)/*.zip'
          RuleSet: 'Solution Checker'

Best Practices

Pipeline Design

  • Use YAML pipelines for version control
  • Keep pipelines DRY with templates
  • Implement meaningful stage/job names
  • Add appropriate timeout values
  • Use conditions to control execution flow

Security

  • Never store secrets in YAML files
  • Use service principals with minimal permissions
  • Enable branch policies for pipeline changes
  • Review pipeline runs for unexpected behavior
  • Rotate credentials regularly

Performance

  • Cache dependencies between runs
  • Parallelize independent jobs
  • Use appropriate agent sizes
  • Skip unnecessary steps with conditions
  • Archive artifacts selectively

Conclusion

Azure Pipelines provides a robust platform for implementing CI/CD for Dynamics 365 and Power Platform solutions. By automating the build, test, and deployment process, teams can deliver changes faster with greater confidence. Start with a simple pipeline and gradually add stages, tests, and approvals as your process matures.

Need Help with Your Dynamics 365 Project?

Our team of experts can help you set up CI/CD pipelines for your Power Platform and Dynamics 365 solutions.