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:
- Create Azure AD App Registration
- Grant Dataverse API permissions
- Create Application User in Power Platform Admin Center
- Assign appropriate security roles
- 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.