Summary: How to setup a build and release pipeline in Azure DevOps Server 2018.
Date: Around 2020
Refactor: 29 April 2025: Checked links and formatting. Rebranded Team Foundation Server (TFS) to Azure Devops Server.
After installing all requirements we can now create a build and release it to the deployment group.
When you created the project, somewhere in the project was a change to initialize the repostory and add some code to the project. Make sure you have a folder in your code with a recognizable name like “code” and make sure you have an index.html file in there so you actually have something to deploy. Because we need a html file that we can modify with data that is collected with PowerShell you could use this html/bootstrap template:
<!DOCTYPE html> <html lang="en"> <head> <title>SHIFT Portal</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"></script> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script> </head> <body> <div class="jumbotron text-center" style="margin-bottom:0"> <h1>SHIFT Portal</h1> </div> <div class="container-fluid" style="margin-top:30px"> <div class="row"> <div class="col-sm-12"> <h2>VM environment</h2> <h5>Refreshed on: __SHIFT-DEVELOPMENT-REFRESHEDDATE__ </h5> <h5>Number of VMs: __SHIFT-DEVELOPMENT-NUMBEROFVMS__ </h5> <h5>In use for: </h5> <p>Note: Notes for this environment</p> <a href="https://www.getshifting.com" role="button" class="btn btn-danger">Go</a> </div> </div> </div> <div id="contact" class="jumbotron text-center" style="margin-bottom:0"> <p>Contact information</p> </div> </body> </html>
Note that there is already an environment with variables that will be replaced in the process.
We will also need a powershell script to provide the information to replace the variables in the html code. Create a folder in the code repository called powershell and add a script called for example vminfo.ps1:
Param( [string]$mgtuser, [string]$mgtpass ) # mgtpass is now a string so must be converted $mgtpasssec = ConvertTo-SecureString -String $mgtpass -AsPlainText -Force #Testing with variable secrets - Note that the $mgtpass appears in the logfile as ***** and the $mgtpasssec as "System.Security.SecureString" Write-Host "Read user $mgtuser" Write-Host "Read pass $mgtpass en $mgtpasssec" # Connect to scvmm $vmmserver = "vmm.shift.local" $mgtcreds = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $mgtuser,$mgtpasssec Get-SCVMMServer $vmmserver -Credential $mgtcreds # Collect all clouds $clouds = Get-SCCloud # Get vmcount and fill array ForEach ($cloud in $clouds){ #Create temporary object to store information #$temp = "" | Select DTAPName,VMCount,Created $cloudname = $cloud.name $vmcount = (Get-SCVirtualMachine -cloud $cloud).count ### Get creationtime of oldest VM $oldestvm = Get-SCVirtualMachine -cloud $cloud | sort {(get-date $_.creationtime)} | select -First 1 $oldestvmdate = $oldestvm.creationtime ### Use write-host to store found information as system environments for TFS write-host "Cloud $cloudname has $vmcount VMs and was probably created on $oldestvmdate" Write-Host "##vso[task.setvariable variable=$cloudname-NumberOfVMs]$vmcount" Write-Host "##vso[task.setvariable variable=$cloudname-RefreshedDate]$oldestvmdate" }
Notice it would even be nicer to also have the vmm server provided in the same way as the user and the password.
Also notice that the build agent as installed here needs to have the proper PowerShell modules installed. You can install the SCVMM console on the server which will also install the required powershell modules.
Follow these steps to create a build:
You have now created an empty build definition.
We will now add a couple of tasks to the build definition to make sure we get the correct files.
We need to run the script so follow these steps to add a taks:
-mgtuser "$(automationaccount)" -mgtpass "$(automationaccountpass)"
The variables set with the script above will be set as system variables, so we can create an additional powershell task to display all variables into the log so we can check of the variables were set correctly:
get-childitem -path env:*
You can now run a testbuild to check if the variables are set correctly.
To replace the variables in index.html we need the Replace Tokens extensions we added earlier:
**/index.html
__
(double underscore)__
(double underscore)Now that we have created a build we need to store the files (also known as an artifact) to a temporary directory so the release can pick this up for deployment:
Note that when your run a build now you can go to the build by clicking on the build number and then to Artifacts, and select Explore to actually see the files that are stored in the temporary folder and download them for inspection to check if everything went well.
Follow these steps to create a release:
You have now created a Release definition, but it won't work yet. In our artifact we did not create a zip file, but we just have some code, which means we have to change the file(s) that have to be deployed. For easy browsing, we will first add the artifact so we can browse to the files later on.
Note that the default name for the Release is “New Release Definition”. You can change this by hovering your mouse over the name and clicking the pencip icon which appears when you do so.
You need to add the artifact which was published in the build. Click on the pipeline tab, click on +Add in the Artifact section and select the Artifact from the Source dropdown.
In the pipeline tab, in the environments section, click the phase and tasks link again. Select the IIS Web App Deploy Task and change the “Package or Folder” from the default:
$(System.DefaultWorkingDirectory)\**\*.zip
$(System.DefaultWorkingDirectory)/HTMLCode/htmlcode
(you can click the three dots on the right to browse to the folder)If you want every commit you make in the code to be automatically added in the build, you can enable Continuous Integration (CI). Follow these steps to do so:
See here for more information on preventing an automatic build on each checkin, while still keeping Continuous Integration enabled.
From here:
Select this check box if you have a lot of team members uploading changes often and you want to reduce the number of builds you are running. If you select this option, when a build is running, the system waits until the build is completed and then queues another build of all changes that have not yet been built.
In the same section where you enable CI you can also enable a scheduled build. If you enable the Scheduled trigger you get a default schedule to build on weekdays at 03:00 (AM). You can modify this, but unfortunately only for weekly schedules. It is not possible yet to create a monthly schedule, or a one-time scheduled build.
If you want every build to be automatically deployed into an environment you can enable Continuous Deployment (CD). Follow these steps to do so:
Note that if you have multiple environments you can add approvals to prevent the release to also deploy to the next environment
You can also add automatic testing using selenium and powershell.