Azure DevOps Pipelines & Dynamics 365 Deployment
Pipeline Types & Official Resources
Two Azure DevOps pipeline flavors mentioned
Classic (no new features being added)
YAML (all modern development done here; presenter started directly with YAML)
Recommended reading: Microsoft documentation for Azure DevOps Pipelines (link referenced in session)
Build/Release automation toolsets used
Power Platform Build Tools (current, covers every required task)
Formerly used XRM CI Framework / CRM DevOps Tools (still viable but no longer primary)
From Monolith to Modular
Original "big pipeline" characteristics
Single YAML handled web resources, middle-tier, Azure Functions, solutions … “everything under the sun”
Cons:
Always overwrote entire environment because all solutions were pushed regardless of what changed
Slow: full build time ≈ (example call-out )
Refactor mandate → 7 focussed pipelines
Each dedicated to a bounded context (core, CTI, clinical, etc.)
Re-useable templates ensure all look & behave identically
YAML Pipeline Anatomy (generic pattern)
Top-level keys demonstrate repeatable structure across all 7 definitions
trigger/pr→ branch filtersInclude:
developExclude:
release/*(future-proofing), feature branches fire only via manual runpathsfilter so pipeline fires only when specific folders changevariablesReference Library → Variable Groups (e.g.,
group: common variables)Templates for variable sets allowed (
template: vars-global.yml)stagesBuild ⇒ Test ⇒ CI CD (QA1) ⇒ CI CD 2 (QA2) ⇒ Trunk (UAT) ⇒ Release-UAT ⇒ Release-Training ⇒ Release-PreProd ⇒ Release-Prod
Each stage contains jobs (deploy middle tier, deploy functions, etc.)
conditionsExample: UAT stage only executes when
build.configuration == 'Debug'ANDnot pullRequest.Release stages run when
build.configuration == 'Release'.environment:tag on every deploy job → ties to Azure DevOps Environments view for traceability ("what’s in UAT?")
Branch Policies & Build Validation
developprotected by Pull Request (PR) policyBuild must succeed (branch-specific pipeline triggered via
build-validation)
Manual pipeline run still possible (used for hot-fixes or direct UAT validation)
Variable Logic Example
variables:
- template: vars-global.yml
# inside vars-global.yml
- name: Configuration
value: ${{ if startsWith(variables['Build.SourceBranch'], 'refs/heads/release') }} \
Release \
${{ else }} Debug
Same template also stores Dev vs Prod connection strings & script paths.
Template-Based Re-use
"Think of a template as a function": YAML fragments parameterised and shared
Key templates
functions-build.yml&functions-deploy.ymlAccept list of Azure Functions → loop restore, build, archive, deploy
Saves re-specifying steps for QA, QA2, UAT, Training…
solution-build.ymlParameters:
solutions:array, each withfriendlyName,uniqueName,dataFiles, etc.Handles: restore NPM, run Webpack (
npm run build:<env>), build plug-ins if present, pack solution, publish as Build Artifactsolution-import.ymlParameters allow multi-org deployment (ex:
PSW Play,PSW Engage,PSW Port)Turns off SLA, evaluates minor-version upgrade logic, stages for upgrade when needed, imports data, shares records, re-enables SLA.
Function Build/Deploy Workflow
RestoreNuGet packages (only for projects in supplied list)dotnet buildspecific functions → artefact archiveLoop in deploy template pushes each .zip to corresponding App Service.
Optimisation: only build & restore what actually changed (time saver vs monolith).
Hot-Fix & Stage Skipping
Hotfix pipeline run detected by branch name containing “hotfix”
Logic: skip Release-UAT stage (UAT already received change via develop → trunk) preventing overwriting newer commits users may be testing.
Solution / Data Versioning Rules
Dynamics 365 version format
Minor bumped when update/upgrade required.
Import script:
$deployed = <get version from target>
$incoming = <version in artefact>
if($incoming.minor > $deployed.minor){ importMode = 'StageForUpgrade' }
else { importMode = 'Update' }
Always imports data files; future enhancement will import only when the data file actually changed (work item created).
Multiple PR scenario clarified
Solutions skip import when version identical → safe to test independent PRs concurrently
Data files overwrite previous content irrespective of author order (current design)
Shared Records Automation
Script invoked only from Core pipeline
Creates Business Unit owner teams, shares environment records, card libraries, etc.
To add new “global” shares, edit
scripts/CreateSharedRecords.ps1(executed every Core run).
Plug-in Package Caveats
Current mapping XML cannot rebuild plug-in package from source → exports whatever was inside CRM at pack time (risk of stale package).
Issues observed during last release (package missing/incorrect).
Action items
Re-work mapping so plug-in package pulled from repo artefact, not dev export
or split pack commands per pipeline to avoid collisions.
Rebasing & Merge Conflicts (PR hygiene)
Required when two developers touch same component within same solution folder
Example: Both modify
ConsumerSolutions\CustomRules.xml→ whichever PR merges first updatesdevelop, second dev mustgit pull --rebaseso export contains latest base.
If components do not overlap, only potential action is to adjust solution version number so import isn’t skipped in next environment.
Manual UAT Runs & Release Impact
Manual pipeline run on feature branch (Run → choose branch)
Skips PR gates, goes Build→Test→UAT directly (because
not pullRequestmet)
Later merge to
developtriggers pipeline again; UAT import skipped (identical versions) so no harm.For production hot-fix
Create
release/*branch, bump version (usually append revision) so Prod receives new build.
Key Numbers & Defaults
Full monolithic build: (peak example )
Current pipelines: 7 parallel YAML definitions
Data import concurrency: parallel threads (
Power Platform Import Datatask setting)Function app count example: QA, QA2, QA3, QA4, UAT, Training, Prod (loop deploy via template)
Troubleshooting & Visibility Tips
Azure DevOps → Pipelines → Environments shows which build/branch is live in each env.
Artifacts tab →
CRM Build→ downloadEnvironmentSettings_<org>.jsonto inspect environment variable values that will be applied.For "Why did UAT fail?" check conditions section in stage log (skipped, failed, never triggered, etc.)
Best Practices & Future Enhancements
ALWAYS commit source (web resources, plug-ins, data) before exporting solution → ensures pack maps find latest artefacts.
Keep solutions small & feature-centric to minimise overlap.
Plan shared-component edits (Consumer Solutions et al.) – daily PR cadence or dedicated day-1/day-2 branches.
Upcoming tasks
Detect data-file deltas to avoid unnecessary imports in lower envs
Fix plug-in package mapping or introduce versioning strategy
Consider disabling "skip lower version" in dev/QA to simplify concurrent work; discuss before enabling.