Public/New-AvdSessionHost.ps1
function New-AvdSessionHost { <# .SYNOPSIS Deploys session hosts into a hostpool .DESCRIPTION Deploys new session hosts into the provided hostpool .PARAMETER HostpoolName Enter the AVD Hostpool name .PARAMETER HostpoolResourceGroup Enter the AVD Hostpool resourcegroup name .PARAMETER SessionHostCount Integer value how many session hosts will be deployed .PARAMETER InitialNumber The start number of the sessionhost (use Get-AvdLatestSessionhost -numonly) .PARAMETER ResourceGroupName The session hosts resource group .PARAMETER imageVersionId The image resourceId, from existing image or image version .PARAMETER Publisher In case of an Azure Markeplace image, provide the publisher .PARAMETER Offer In case of an Azure Markeplace image, provide the offer .PARAMETER Sku In case of an Azure Markeplace image, provide the sku .PARAMETER Version In case of an Azure Markeplace image, provide the version (default latest) .PARAMETER Location Enter the session host location .PARAMETER DiskType Enter the session host diskType .PARAMETER LocalAdmin Enter the session host local admin account .PARAMETER LocalPass Enter the session host local admins password .PARAMETER Prefix Enter the session host prefix .PARAMETER SubnetId Enter the subnet resource ID where the session host is in .PARAMETER Domain Provide the native domain name. domain.local .PARAMETER Domain Provide the native domain name. domain.local .PARAMETER OU Enter the OU to store the hosts at .PARAMETER DomainJoinAccount Provide an account with domain join permissions, mostly domain admin .PARAMETER DomainJoinPassword The domain admin password, must be a secure string .PARAMETER AzureAd Provide this switch parameter if the session host is Azure AD joined, otherwise it is native AD joined .PARAMETER Intune Switch parameter if you want to add the session host into Intune. Only supported with AzureAD enrollment. .EXAMPLE New-AvdSessionHost -HostpoolName avd-hostpool -HostpoolResourceGroup rg-avd-01 -sessionHostCount 1 -ResourceGroupName rg-sessionhosts-01 -Publisher "MicrosoftWindowsDesktop" -Offer "windows-10" -Sku "21h1-ent-g2" -VmSize "Standard_D2s_v3" -Location "westeurope" -diskType "Standard_LRS" -LocalAdmin "ladmin" -LocalPass "lpass" -Prefix "AVD" -SubnetId "/subscriptions/../resourceGroups/../providers/Microsoft.Network/virtualNetworks/../subnets/../" -Intune -AzureAd .EXAMPLE New-AvdSessionHost -HostpoolName avd-hostpool -HostpoolResourceGroup rg-avd-01 -sessionHostCount 1 -ResourceGroupName rg-sessionhosts-01 -imageVersionId "/subscriptions/..galleries/../images/../version/21.0.0" -VmSize "Standard_D2s_v3" -Location "westeurope" -diskType "Standard_LRS" -LocalAdmin "ladmin" -LocalPass "lpass" -Prefix "AVD" -SubnetId "/subscriptions/../resourceGroups/../providers/Microsoft.Network/virtualNetworks/../subnets/../" -Domain domain.local -OU "OU=AVD,DC=domain,DC=local" -DomainAdmin vmjoiner@domain.local -DomainPassword "P@sswrd123" #> [CmdletBinding(DefaultParameterSetName = 'AADWithSig')] param ( [parameter(Mandatory)] [string]$HostpoolName, [parameter(Mandatory)] [string]$ResourceGroupName, [parameter(Mandatory, ParameterSetName = 'AADWithSig')] [parameter(Mandatory, ParameterSetName = 'NativeADWithSig')] [string]$ImageVersionId, [parameter(Mandatory)] [int]$SessionHostCount, [parameter()] [int]$InitialNumber, [parameter(Mandatory)] [string]$Prefix, [parameter(Mandatory, ParameterSetName = 'AADWithMarketPlace')] [parameter(Mandatory, ParameterSetName = 'NativeADWithMarketPlace')] [string]$Publisher, [parameter(Mandatory, ParameterSetName = 'AADWithMarketPlace')] [parameter(Mandatory, ParameterSetName = 'NativeADWithMarketPlace')] [string]$Offer, [parameter(Mandatory, ParameterSetName = 'AADWithMarketPlace')] [parameter(Mandatory, ParameterSetName = 'NativeADWithMarketPlace')] [string]$Sku, [parameter(Mandatory, ParameterSetName = 'AADWithSig')] [parameter(Mandatory, ParameterSetName = 'NativeADWithSig')] [string]$Version = "latest", [parameter(Mandatory)] [string]$VmSize, [parameter(Mandatory)] [string]$Location, [parameter(Mandatory)] [ValidateSet("Premium_LRS", "Premium_ZRS", "StandardSSD_LRS", "StandardSSD_ZRS", "Standard_LRS", "UltraSSD_LRS")] [string]$DiskType, [parameter()] [ValidateNotNullOrEmpty()] [string]$LocalAdmin = (Get-RandomString -type string), [parameter()] [ValidateNotNullOrEmpty()] [string]$LocalPass = (Get-RandomString -type password), [parameter(Mandatory)] [string]$SubnetId, # [parameter(Mandatory, ParameterSetName = 'AzureADJoin')] [parameter(Mandatory, ParameterSetName = 'AADWithSig')] [parameter(Mandatory, ParameterSetName = 'AADWithMarketPlace')] [switch]$AzureAd, [parameter(Mandatory, ParameterSetName = 'NativeADWithSig')] [parameter(Mandatory, ParameterSetName = 'NativeADWithMarketPlace')] [string]$Domain, [parameter(Mandatory, ParameterSetName = 'NativeADWithSig')] [parameter(Mandatory, ParameterSetName = 'NativeADWithMarketPlace')] [string]$OU, [parameter(Mandatory, ParameterSetName = 'NativeADWithSig')] [parameter(Mandatory, ParameterSetName = 'NativeADWithMarketPlace')] [string]$DomainJoinAccount, [parameter(Mandatory, ParameterSetName = 'NativeADWithSig')] [parameter(Mandatory, ParameterSetName = 'NativeADWithMarketPlace')] [System.Security.SecureString]$DomainJoinPassword, [parameter()] [switch]$Intune, [parameter()] [switch]$TrustedLaunch, [Parameter()] [ValidateNotNullOrEmpty()] [int]$MaxParallel = 5 ) Begin { Write-Verbose "Start creating session hosts" AuthenticationCheck $token = GetAuthToken -resource $global:AzureApiUrl $registrationToken = Update-AvdRegistrationToken -HostpoolName $Hostpoolname $ResourceGroupName -HoursActive 4 | Select-Object -ExpandProperty properties $vmNames = [System.Collections.ArrayList]::new() } Process { switch -Wildcard ($PsCmdlet.ParameterSetName) { *Sig { $imageReference = @{ id = $ImageVersionId } } *MarketPlace { $imageReference = @{ "sku" = $Sku "publisher" = $Publisher "version" = $Version "offer" = $Offer } } Default { Throw "No source for image provided. Please provide a compute imageId or marketplace sources (publisher, offer, sku, version)" } } switch -Wildcard ($PsCmdlet.ParameterSetName) { NativeAD* { Write-Verbose "Provided parameters to join native AD" $extensionName = "AADLoginForWindows" $domainJoinExtension = @{ properties = @{ publisher = "Microsoft.Compute" type = "JsonADDomainExtension" typeHandlerVersion = "1.3" autoUpgradeMinorVersion = $true settings = @{ name = $Domain user = $DomainJoinAccount restart = $true options = "3" } protectedSettings = @{ password = $DomainJoinPassword | ConvertFrom-SecureString -AsPlainText } } location = $Location } if ($OU) { $domainJoinExtension.properties.settings.Add("ouPath", $OU) } } AAD* { Write-Verbose "Provided -AzureAD switch, joining AzureAD" $extensionName = "AADLoginForWindows" $domainJoinExtension = @{ properties = @{ Type = "AADLoginForWindows" Publisher = "Microsoft.Azure.ActiveDirectory" typeHandlerVersion = "2.0" } location = $Location } if ($Intune.isPresent) { $settings = @{ mdmId = "0000000a-0000-0000-c000-000000000000" } $domainJoinExtension.properties.Add("Settings", $settings) } } Default { Throw "No AD environment provided, please provide -AzureAD switch parameter or provide native domain OU and credentials" } } Do { try { if ($null -eq $InitialNumber) { $InitialNumber = Get-AvdLatestSessionHost -HostpoolName $HostpoolName -ResourceGroupName $ResourceGroupName -NumOnly -Verbose } $vmName = "{0}-{1}" -f $Prefix, $InitialNumber $vmNames.Add(($vmName)) > $null $InitialNumber++ $sessionHostCount-- Write-Verbose "Session host $vmName added to the list" } catch { Throw "Not able to create session hosts list, $_" } } while ($sessionHostCount -gt 0) { Write-Verbose "Session hosts added to the deployment list" Write-Verbose "$($vmNames)" } $vmNames | Foreach-Object -Verbose -ThrottleLimit $MaxParallel -Parallel { try { # Checking every 15 seconds till the max of 60 (is 15 minutes) if the VM is created successfully $maxRetries = 30 $checkCount = 0 $vmName = $_ Write-Verbose "Creating session host $vmName nic" $nicName = "{0}-nic" -f $vmName $nicBody = @{ "properties" = @{ "enableAcceleratedNetworking" = $true "ipConfigurations" = @( @{ "name" = "ipconfig1" "properties" = @{ "subnet" = @{ id = $using:SubnetId } } } ) } "location" = $using:Location } $nicJson = $nicBody | ConvertTo-Json -Depth 15 $nicUrl = "{0}/subscriptions/{1}/resourceGroups/{2}/providers/Microsoft.Network/networkInterfaces/{3}?api-version=2021-03-01" -f $using:AzureApiUrl, $using:subscriptionId, $using:ResourceGroupName, $nicName $nic = Invoke-RestMethod -Method PUT -Uri $nicUrl -Headers $using:token -Body $nicJson Write-Verbose "Creating session host vm" $vmBody = @{ location = $using:Location identity = @{ type = "SystemAssigned" } "properties" = @{ licenseType = "Windows_Client" "hardwareProfile" = @{ "vmSize" = $using:VmSize } "storageProfile" = @{ "imageReference" = $using:imageReference "osDisk" = @{ "caching" = "ReadWrite" "managedDisk" = @{ "storageAccountType" = $using:diskType } "name" = "{0}-os" -f $vmName "createOption" = "FromImage" } } "osProfile" = @{ "adminUsername" = $using:LocalAdmin "computerName" = $using:vmName "adminPassword" = $using:LocalPass } "networkProfile" = @{ "networkInterfaces" = @( @{ "id" = $nic.Id "properties" = @{ "primary" = $true } } ) } } } if ($using:TrustedLaunch.IsPresent) { Write-Verbose "Enabling Trusted Launch" $securityProfile = @{ securityType = "TrustedLaunch" uefiSettings = @{ secureBootEnabled = $true vTpmEnabled = $true } } $vmBody.properties.Add("securityProfile", $securityProfile) } $vmUrl = "{0}/subscriptions/{1}/resourceGroups/{2}/providers/Microsoft.Compute/virtualMachines/{3}?api-version={4}" -f $using:AzureApiUrl, $using:subscriptionId, $using:ResourceGroupName, $vmName, $using:virtualMachineVersion $vmJsonBody = $vmBody | ConvertTo-Json -Depth 99 Invoke-RestMethod -Method PUT -Uri $vmUrl -Headers $using:token -Body $vmJsonBody Do { $status = Invoke-RestMethod -Method GET -Uri $vmUrl -Headers $using:token Start-Sleep 5 } While ($status.properties.provisioningState -ne "Succeeded") { Write-Verbose "Host $vmName is ready" } $domainJoinUrl = "{0}/subscriptions/{1}/resourceGroups/{2}/providers/Microsoft.Compute/virtualMachines/{3}/extensions/{4}?api-version={5}" -f $using:AzureApiUrl, $using:subscriptionId, $using:ResourceGroupName, $vmName, $using:extensionName , $using:virtualMachineVersion $domainJoinBody = $using:domainJoinExtension | ConvertTo-Json -Depth 99 Invoke-RestMethod -Method PUT -Uri $domainJoinUrl -Headers $using:token -Body $domainJoinBody Do { # Wait for the extension to be ready, till the max of 15 minutes $checkCount++ $domainJoinStatus = Invoke-RestMethod -Method GET -Uri $domainJoinUrl -Headers $using:token Start-Sleep 5 } # Check state for succesfull installation till the max retries. While ($domainJoinStatus.properties.provisioningState -ne "Succeeded") { if ($checkCount -gt $maxRetries) { Throw "Joining domain type $($domainJoinExtension.properties.type) took to long " } else { Write-Verbose "Extension for AVD is ready" } } # Reset the counter for next check round $checkCount = 0 $avdExtensionName = "Microsoft.PowerShell.DSC" $avdUrl = "{0}/subscriptions/{1}/resourceGroups/{2}/providers/Microsoft.Compute/virtualMachines/{3}/extensions/{4}?api-version={5}" -f $using:AzureApiUrl, $using:subscriptionId, $using:ResourceGroupName, $vmName, $avdExtensionName , $using:virtualMachineVersion $avdDscExtension = @{ properties = @{ Type = "DSC" Publisher = "Microsoft.Powershell" typeHandlerVersion = "2.73" Settings = @{ modulesUrl = $using:AvdModuleLocation ConfigurationFunction = "Configuration.ps1\AddSessionHost" Properties = @{ hostPoolName = $using:HostpoolName registrationInfoToken = $using:registrationToken.registrationInfo.token aadJoin = 1 } } } location = $using:Location } $avdExtensionBody = $avdDscExtension | ConvertTo-Json -Depth 99 Invoke-RestMethod -Method PUT -Uri $avdUrl -Headers $using:token -Body $avdExtensionBody Do { # Wait for the extension to be ready, till the max of 15 minutes $checkCount++ $status = Invoke-RestMethod -Method GET -Uri $avdUrl -Headers $using:token Start-Sleep 10 } # Check state for succesfull installation till the max retries. While ($status.properties.provisioningState -ne "Succeeded") { if ($checkCount -gt $maxRetries) { Throw "Installing AVD extension took to long " } else { Write-Verbose "Extension for AVD is ready" } } } catch { "VM $vmName not created, $_" Throw } } } End { Write-Verbose "Task completed, created $SessionHostCount session host(s)" } } |