Now that Microsoft will also update additional Dynamics 365 Finance and Operations Sandbox environments, partners and customers will only need to take care of updating cloud-hosted environments, as we’ve always done.
I’m sure each team manages this differently, maybe leaving it to each developer to update their VM, or there’s someone in the customer or partner side that will do it. That’s in the best cases, maybe nobody is updating the developer machines…
Now let’s take a look at the steps.
I’m installing d365fo.tools in the first step with the following script:
And in the second task, I’ll be running the update script we’ve just seen at the beginning of this post.
Of course, you can do it all in a single task, but I prefer to split it in two because it looks prettier to me.
If you want to know more about builds, releases, and the Dev ALM of Dynamics 365 you can read my full guide on MSDyn365 & Azure DevOps ALM.
Today, I’m bringing you a PowerShell script that you can run in a pipeline that will automatically update all your developer virtual machines!Update script #
As I’ve already done many times, I’ll be using Mötz Jensen‘s d365fo.tools to run all the operations. This is the complete script:# CHANGE THIS!!
$AssetId = "LCS_ASSET_ID"
$User = "YOUR_USER"
$Pass = "YOUR_USER_PASSWORD"
$ClientId = "AAD AppId"
$ProjectId = "LCS_PROJECT_ID"
#Get LCS auth token
Get-D365LcsApiToken -ClientId $ClientId -Username $User -Password $Pass -LcsApiUri https://lcsapi.lcs.dynamics.com | Set-D365LcsApiConfig -ProjectId $ProjectId
Get-D365LcsApiConfig
# Get list of all LCS project environments
$Environments = Get-D365LcsEnvironmentMetadata -TraverseAllPages
$StartedEnvs = @()
Write-Host "=================== STARTING ENVIRONMENTS ==================="
Foreach ($Env in $Environments)
{
# Start Dev VMs only
if ($Env.EnvironmentType -eq "DevTestDev" -and $Env.CanStart)
{
$EnvStatus = Invoke-D365LcsEnvironmentStart -EnvironmentId $Env.EnvironmentId
if ($EnvStatus.IsSuccess -eq "True") {
Write-Host ("Environment {0} started." -f $Env.EnvironmentName)
$StartedEnvs += $Env.EnvironmentId
}
else {
Write-Host ("Environment {0} couldn't be started. Error message: {1}" -f $Env.EnvironmentName, $EnvStatus.ErrorMessage)
}
}
}
Write-Host "=================== STARTING ENVIRONMENTS DONE ==================="
Write-Host "=================== SLEEPING FOR 180 seconds ==================="
# Wait 3 minutes for the VMs to start
Start-Sleep -Seconds 180
$Retries = 0
Write-Host "=================== STARTING DEPLOYMENT ==================="
Do
{
Foreach ($EnvD in $StartedEnvs)
{
$EnvStatus = Get-D365LcsEnvironmentMetadata -EnvironmentId $EnvD
# If the VM has started, deploy the DP
if ($EnvStatus.DeploymentStatusDisplay -eq "Deployed")
{
$OpResult = Invoke-D365LcsDeployment -AssetId $AssetId -EnvironmentId $EnvD
if ($OpResult.IsSuccess -eq "True") {
Write-Host ("Updating environment {0} has started." -f $EnvD)
$StartedEnvs = $StartedEnvs -notmatch $EnvD
}
else {
Write-Host ("Updating environment {0} has failed. Error Message: {1}." -f $EnvD, $OpResult.ErrorMessage)
Write-Host ("Will retry {0} more time(s)" -f 3 - $Retries)
}
}
}
$Retries++
} While ($StartedEnvs.Count -ne 0 -or $Retries -eq 3)
Write-Host "=================== STARTING DEPLOYMENT DONE ==================="
Write-Host "Done"
Authenticating and getting environments #
The first step will be authenticating to LCS with the Get-D365LcsApiToken cmdlet and getting a list of all our environments with Get-D365LcsEnvironmentMetadata. This includes the sandbox and prod environments, but don’t worry, these won’t be updated. In the last line, we’ll be initializing an array to store the IDs of started environments in the next step.# CHANGE THIS!!
$AssetId = "LCS_ASSET_ID"
$User = "YOUR_USER"
$Pass = "YOUR_USER_PASSWORD"
$ClientId = "AAD AppId"
$ProjectId = "LCS_PROJECT_ID"
#Get LCS auth token
Get-D365LcsApiToken -ClientId $ClientId -Username $User -Password $Pass -LcsApiUri https://lcsapi.lcs.dynamics.com | Set-D365LcsApiConfig -ProjectId $ProjectId
Get-D365LcsApiConfig
# Get list of all LCS project environments
$Environments = Get-D365LcsEnvironmentMetadata -TraverseAllPages
$StartedEnvs = @()
Starting developer VMs #
Now that we have a list with our environments, we need to start only the cloud-hosted ones. We will attain this by looping through the list we got in the first part and filtering on the EnvironmentType property where it equals DevTestDev. And using the Invoke-D365LcsEnvironmentStart cmdlet we will start each VM. Next we will check if the operation succeeds, or it doesn’t. When we’ve done this for all VMs, we’ll call the Start-Sleep cmdlet and give 3 minutes to the VMs to start.Write-Host "=================== STARTING ENVIRONMENTS ==================="
Foreach ($Env in $Environments)
{
# Start Dev VMs only
if ($Env.EnvironmentType -eq "DevTestDev" -and $Env.CanStart)
{
$EnvStatus = Invoke-D365LcsEnvironmentStart -EnvironmentId $Env.EnvironmentId
if ($EnvStatus.IsSuccess -eq "True") {
Write-Host ("Environment {0} started." -f $Env.EnvironmentName)
$StartedEnvs += $Env.EnvironmentId
}
else {
Write-Host ("Environment {0} couldn't be started. Error message: {1}" -f $Env.EnvironmentName, $EnvStatus.ErrorMessage)
}
}
}
Write-Host "=================== STARTING ENVIRONMENTS DONE ==================="
Write-Host "=================== SLEEPING FOR 180 seconds ==================="
# Wait 3 minutes for the VMs to start
Start-Sleep -Seconds 180
Trigger the updates #
In the final part, we will start deploying the update to each running VM. Looping through the array we created in the beginning, we’ll use the Get-D365LcsEnvironmentMetadata command to get the status of the VM, and if it’s running we’ll start the deployment using the Invoke-D365LcsDeployment cmdlet. If the operation succeeds, we’ll remove that environment from the array and continue, otherwise we’ll try again up until three times (note that everything is inside a Do-While loop).$Retries = 0 Write-Host "=================== STARTING DEPLOYMENT ===================" Do { Foreach ($EnvD in $StartedEnvs) { $EnvStatus = Get-D365LcsEnvironmentMetadata -EnvironmentId $EnvD # If the VM has started, deploy the DP if ($EnvStatus.DeploymentStatusDisplay -eq "Deployed") { $OpResult = Invoke-D365LcsDeployment -AssetId $AssetId -EnvironmentId $EnvD if ($OpResult.IsSuccess -eq "True") { Write-Host ("Updating environment {0} has started." -f $EnvD) $StartedEnvs = $StartedEnvs -notmatch $EnvD } else { Write-Host ("Updating environment {0} has failed. Error Message: {1}." -f $EnvD, $OpResult.ErrorMessage) Write-Host ("Will retry {0} more time(s)" -f 3 - $Retries) } } } $Retries++ } While ($StartedEnvs.Count -ne 0 -or $Retries -eq 3) Write-Host "=================== STARTING DEPLOYMENT DONE ===================" Write-Host "Done"And after this we should see all our dev VMs servicing on LCS.
Running it in a pipeline #
Once the script is working, running it in a pipeline is totally trivial, and you can do it in a build or a release pipeline, it’s up to you. My pipeline looks like this:Install-Module -Name d365fo.tools -AllowClobber -Scope CurrentUser -Force -Confirm:$false