Windows 10 Image Series – Part 6 – YAML/Bicep SessionHost Deployment

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:

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)


19 thoughts on “Windows 10 Image Series – Part 6 – YAML/Bicep SessionHost Deployment”

  1. 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

    Reply
  2. 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.

    Reply
    • 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

      Reply
  3. 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.

    Reply
    • 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

      Reply

Leave a Comment