In this series I am going to show you how build a Windows 10 Image via Azure Pipelines and DevOps without 3rd party tooling, welcome to part 6!
We built the image, but I would like to show you another sessionhost deployment type. The type where YAML and Bicep are used to create the sessionhost.
Prerequisites
Part 1, 2 & 3 of this series needs to be completed. These are the links:
1. Windows 10 Image Series – Part 1 (Creating the Windows VM Pipeline)
2. Windows 10 Image Series – Part 2 (Artifacts & Application Installation)
3. Windows 10 Image Series – Part 3 (Shared Image Gallery)
Therefore, the prerequisites from part 1 are also required for this part. So:
Firstly, I am assuming that you have knowledge of Azure DevOps. These are the parts that already need be setup:
- Service Connection via Service Principal to ARM (more information)
- Azure Key Vault with connection to Azure DevOps (more information)
- GIT Repo in Azure DevOps (more information)
In addition, if you don’t have knowledge about Azure DevOps and still want to follow this series please let me know. I might write a blog about the preparing Azure DevOps.
Checkout/Skip to other parts:
0. Windows 10 Image Series – Part 0 (Preparing Azure/Azure DevOps)
1. Windows 10 Image Series – Part 1 (Creating the Windows VM Pipeline)
2. Windows 10 Image Series – Part 2 (Artifacts & Application Installation)
3. Windows 10 Image Series – Part 3 (Shared Image Gallery)
3.1 Windows 10 Image Series – Part 3.1 (Create test VM from Shared Image Gallery)
4. Windows 10 Image Series – Part 4 (SessionHost Deployment via Powershell and Classic Pipelines)
5. Windows 10 Image Series – Part 5 (Converted the Image build pipeline to YAML)
Creating the Azure Virtual Desktop Hostpool
Firstly, we need te create an Azure Virtual Desktop Hostpool to deploy the virtual machine to. We are using a Powershell function I created in an earlier blogpost.
Please copy this Powershell function:
Function CreateWVDHostPools { Param ( [Parameter(Mandatory = $True, Position = 1, ValueFromPipeline = $False)] [String]$ResourceGroupName, [Parameter(Mandatory = $True, Position = 2, ValueFromPipeline = $False)] [string[]]$HostPools ) $Location = "WestEurope" $ExistingResourceGroups = Get-AzResourceGroup if ($ExistingResourceGroups.ResourceGroupName -notcontains $ResourceGroupName) { Write-Host "ResourceGroup $($ResourceGroupName) does not exist. Creating new ResourceGroup" -ForegroundColor Green New-AzResourceGroup -Name $ResourceGroupName -Location $Location } else { Write-Host "ResourceGroup $($ResourceGroupName) already exists" -ForegroundColor Yellow } foreach ($HostPoolName in $HostPools){ New-AzWvdWorkspace -ResourceGroupName $ResourceGroupName ` -Name "$($HostPoolName)-Workspace" ` -Location $Location ` -FriendlyName "$($HostPoolName)-Workspace" ` -ApplicationGroupReference $null ` -Description "$($HostPoolName)-Workspace" New-AzWvdHostPool -Name $HostPoolName ` -ResourceGroupName $ResourceGroupName ` -Location $Location ` -HostPoolType Pooled ` -PreferredAppGroupType 'Desktop' ` -LoadBalancerType DepthFirst ` -MaxSessionLimit '12' ` $HostPool = Get-AzWvdHostPool -Name $HostPoolName -ResourceGroupName $ResourceGroupName New-AzWvdApplicationGroup -Name "$($HostPoolName)-DAG" ` -ResourceGroupName $ResourceGroupName ` -ApplicationGroupType 'Desktop' ` -HostPoolArmPath $HostPool.id ` -Location $Location $DAG = Get-AzWvdApplicationGroup -Name "$($HostPoolName)-DAG" -ResourceGroupName $ResourceGroupName Register-AzWvdApplicationGroup -ResourceGroupName $ResourceGroupName ` -WorkspaceName "$($HostPoolName)-Workspace" ` -ApplicationGroupPath $DAG.id } }
After that, run the following code:
CreateWVDHostPools -ResourceGroupName DemoPart4 -HostPools NielskokdemoPart4
As a result, a new resourcegroup is created:
And the AVD Hostpool, Workspace and Applicationgroup are created:
After that, we are creating the YAML Pipeline for the sessionhost deployment.
Creating the YAML Pipeline
Firstly, log on to Azure DevOps.
After that, go to repositories and create these files:
In addition, you can find the files in my public Github repository:
JSON File
Bicep File
YAML File
Next, add some variables to the variable group “Windows10ImageVariables”. Go to Pipelines and after that go to library and select the variable group:
Add these variables:
Fill the variables accordingly:
ARM_Subscription_ID –> Subscription ID of the subscription you want to deploy your resources in.
az_tenant_id –> Tenant ID for your Azure Active Directory tenant.
ServiceConnectionName –> Name of the Service Connection you use to connect Azure DevOps to your Azure tenant. (more information)
After that, go to Pipelines and create a new Pipeline:
Select Azure Repos Git:
After that, select your repository:
Select Existing YAML file:
After that, select the YAML file created earlier:
Please change to following parameters and variables in the YAML file:
- <<Domain Name>>
- <<OU NAME>>
- <<VNET Resource Group>>
- <<VNet Name>>
- <<Subnet Name>>
- <<Service Principal Secret>>
- <<Service Principal APP ID>>
- <<Resource Group for Hostpool>>
- <<Hostpool Name>>
After that, make sure that these file path are in line with your repository:
And:
Save the pipeline!
Now it is time to run the pipeline!
Running the Pipeline
Go to the pipeline and click on run:
After that, these parameters appear:
Edit everything to your liking and click on run.
Furthermore, you can set these defaults for each parameters in the YAML file by editting these values:
After 8 – 10 minutes the deployment has completed and this the result:
The VMs are added to the hostpool and available:
This was Windows 10 Image Series – Part 6, check out the other parts:
0. Windows 10 Image Series – Part 0 (Preparing Azure/Azure DevOps)
1. Windows 10 Image Series – Part 1 (Creating the Windows VM Pipeline)
2. Windows 10 Image Series – Part 2 (Artifacts & Application Installation)
3. Windows 10 Image Series – Part 3 (Shared Image Gallery)
3.1 Windows 10 Image Series – Part 3.1 (Create test VM from Shared Image Gallery)
4. Windows 10 Image Series – Part 4 (SessionHost Deployment via Powershell and Classic Pipelines)
5. Windows 10 Image Series – Part 5 (Converted the Image build pipeline to YAML)
Hi Niels
Great stuff,
One question, what would you recommend to do for this.
I have a golden image and I want to make a templet to creation AVD host (personal) for helpdesk
– create vm form a template or so,
– the VM will be intune joined
– add a use the the vm
– Add the same user to a IAM group
Hi David,
Thanks mate!
I would create a YAML pipeline where I dynamically can add the user to VM. This is doable via Bicep.
Thanks,
Niels
Thx,
a other option ?
As for my I cant use devops
Hi David,
You can also only use the Bicep template with parameters. Is that doable for you?
Thanks,
Niels
sure do you have an example to do build it and use it ?
Hi David,
Take a look at part 3.1 of this series. You need to edit the template but it should be everything you need.
https://www.nielskok.tech/azure-virtual-desktop/windows-10-image-series-part-3-1-deploy-test-vm/
Thanks,
Niels
Hi Niels
sorry to by a pain,
I have a question how to make a pipeline that will Add a user a a session host add him to the AVD pool etc, the code for that I have but am thinking how to get in to a pipeline so I would update 1 variable i a user will get added in the right places.
Hi David,
You want a Powershell script to add an assignment to the Application group for a hostpool?
Thanks,
Niels
yes but via a pipilnie
so I could have stages like 1. add user to pool 2. assignee vm 3. add RBAC
Oke, you should add an Azure Powershell task as a 3rd stage. In that stage you should put the powershell script that performs that task.
Thanks,
Niels
Hello Niels,
When I run the pipeline to create the sessions hosts and add them to the pool, I get an error at the stage where it wants to create the token.
At C:\Agents\_work\_temp\azureclitaskscript1672666811299_inlinescript.ps1:28 char:3
2023-01-02T13:40:22.0047105Z + if !(Get-Path C:\Agents\_work\12\variables){New-Item -Path C:\Agents\ …
2023-01-02T13:40:22.0049788Z + ~
2023-01-02T13:40:22.0051314Z Missing ‘(‘ after ‘if’ in if statement.
2023-01-02T13:40:22.0055251Z At C:\Agents\_work\_temp\azureclitaskscript1672666811299_inlinescript.ps1:28 char:44
2023-01-02T13:40:22.0057700Z + if !(Get-Path C:\Agents\_work\12\variables){New-Item -Path C:\Agents\ …
2023-01-02T13:40:22.0062888Z + ~
2023-01-02T13:40:22.0064858Z Unexpected token ‘{‘ in expression or statement.
2023-01-02T13:40:22.0066942Z + CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
2023-01-02T13:40:22.0075880Z + FullyQualifiedErrorId : MissingOpenParenthesisInIfStatement
2023-01-02T13:40:22.0177603Z
2023-01-02T13:40:22.1033512Z ##[error]Script failed with exit code: 1
Hope you can help.
Hi Alex,
Thanks for your comment. I have created a new solution for this. You can use this code to generate the token and use it in your pipeline:
$Date = (Get-Date).AddDays(14)
$NewTokenDate = $Date.ToString(“yyyy-MM-ddTHH:mm:ssZ”)
az config set extension.use_dynamic_install=yes_without_prompt
$hostpoolToken = az desktopvirtualization hostpool update –resource-group RG_WE_AVD_HostPools –name AVD-Development –registration-info expiration-time=$NewTokenDate registration-token-operation=”Update” –query ‘registrationInfo.token’
If you have any questions, don’t hesitate to ask!
Thanks,
Niels