Before we begin, this topic is similar to what Benedikt Bergmann explains in his blog post. But like always, if I do not yet try it by myself = I do not yet fully understand 😀. The twist will be I will create the web resource project using Dataverse DevTools by Danish Naglekar (TypeScript for the win!) and to deploy and publish to the Dev environment, we will use the SparkleXrm by Scott Durow. What we will accomplish in today’s demonstration is in the below image:

DevOps is dynamic and depends on Organization/people’s perspective on how to run it. For me, the “truth” source to be considered when deploying and publishing customizations for Plugin or WebResources is the code itself (not from the plugin assembly or web resource deployed in the dev environment). Hence, this process is important to make sure the deployment solutions will be in the correct state (during the deployment process, the environment must be locked).
Prepare the project files
First, we need to create a .NET Library project (using .NET Framework 4.6.2 – to make it the same as the Plugin project) > install spkl via NuGet package:

For those who don’t know what is Dataverse DevTools and how to set up the project (including creating a sample web resource), you can read my blog post here. Once you setup the project, I generate sample typing and create a TypeScript file like the one below:

The benefit of using Dataverse DevTools is we can publish all the web resources/single web resource conventionally. Once you finish the changes > run “npm run build” > your file will be generated on the WebResources folder (CSS/HTML/scripts) > right click and then you can choose to “Link to Existing Web Resource” (Update) or “Upload & Publish Web Resource” (New):

Once you deploy, the system will automatically create dvdt.linker.xml which we can use to update spkl.json. Based on the information above, I update my spkl.json as follows (you also need to prepare Dataverse Solution for it):
{
"webresources": [
{
"profile": "default,debug",
"root": "Webresources/",
"solution": "DevopsWebResource",
"files": [
{
"uniquename": "dev_Tmy/Contact",
"file": "scripts/Contact.js",
"description": ""
}
]
}
]
}
Once you have done this, we can focus on the Azure Pipeline.
Azure Pipeline
Here is the final .yml setup:
trigger:
- master
pool:
vmImage: 'windows-latest'
stages:
- stage:
jobs:
- job: BuildWebResources
steps:
- task: NuGetToolInstaller@1
inputs:
versionSpec:
checkLatest: true
- task: NuGetCommand@2
inputs:
command: 'restore'
restoreSolution: '**/*.sln'
feedsToUse: 'select'
- task: NodeTool@0
inputs:
versionSpec: '19.x'
displayName: 'Install Node.js'
- script: |
npm install
workingDirectory: Src/Frontend
displayName: 'npm install'
- script: |
npm run build-dev
workingDirectory: Src/Frontend
displayName: 'npm build'
- task: CopyFiles@2
inputs:
sourceFolder: 'Src/Frontend/spkl/'
contents: 'deploy-webresources-devops.bat'
targetFolder: $(Build.ArtifactStagingDirectory)
displayName: 'Copy spkl.bat'
- task: CopyFiles@2
inputs:
sourceFolder: 'Src/Frontend/'
contents: 'spkl.json'
targetFolder: $(Build.ArtifactStagingDirectory)
displayName: 'Copy spkl.json'
- task: CopyFiles@2
inputs:
sourceFolder: 'Src/Frontend/WebResources'
contents: '**'
targetFolder: $(Build.ArtifactStagingDirectory)/Webresources
displayName: 'Copy Webresources build files'
- task: PublishPipelineArtifact@1
inputs:
targetPath: '$(Build.ArtifactStagingDirectory)'
artifactName: DataverseWebResources
displayName: 'Publish Dataverse artifact'
- task: BatchScript@1
displayName: DeployToDataverseDev
inputs:
filename: '$(Build.ArtifactStagingDirectory)/deploy-webresources-devops.bat'
arguments: '"$(DEVCONNECTION)" "$(DEVPASSWORD)"'
modifyEnvironment: false
workingFolder: '$(Build.ArtifactStagingDirectory)'
Here is the steps information:
- We need to use a Windows machine to do the build.
- Install the NuGet tool and restore the solution (this will ensure to download of the spkl files that are needed to run the publish)
- Install Node.js (to get the npm packages needed) and then run “npm run build-dev“. This will instruct the system to rebuild all the files in the WebResources folder.
- Copy the necessary files (deploy-webresources-devops.bat, spkl.json, and also all the files in the WebResources folder) to the Azure Artifacts.
- The last part is just to run the deploy-webresources-devops.bat and we are done 😎
When creating the pipeline, here are the variables that I created:

For the devConnection variable, the format is similar to what Benedikt provided:
Url=https://<your org>.crm4.dynamics.com/;Username=<your Username>;AuthType=Office365;RequireNewInstance=True;Password=
Then for the deploy-webresources-devops.bat is like below:
@echo off
set connection=%~1
set password=%~2
set package_root=..\..\
REM Find the spkl in the package folder (irrespective of version)
For /R %package_root% %%G IN (spkl.exe) do (
IF EXIST "%%G" (set spkl_path=%%G
goto :continue)
)
:continue
@echo Using '%spkl_path%'
REM spkl webresources [path] [connection-string]
"%spkl_path%" webresources ./ "%connection%%password%"
if errorlevel 1 (
echo Error Code=%errorlevel%
exit /b %errorlevel%
)
pause
Here is the demo if the master branch is updated and the pipeline triggered:

Happy DevOps-ing!
Other references:
One thought on “Learn how to deploy web resources with Azure DevOps Pipeline”