wvd-templates/DSC/Script-CleanupOldRdshSessionHosts.ps1 (264 lines of code) (raw):

<# .SYNOPSIS Removing old hosts from Existing Hostpool. .DESCRIPTION This script will Remove/Stop old sessionhost servers from existing Hostpool. The supported Operating Systems Windows Server 2016/windows 10 multisession. .ROLE Readers #> param( [Parameter(mandatory = $true)] [string]$RDBrokerURL, [Parameter(mandatory = $true)] [string]$definedTenantGroupName, [Parameter(mandatory = $true)] [string]$TenantName, [Parameter(mandatory = $true)] [string]$HostPoolName, [Parameter(mandatory = $true)] [pscredential]$TenantAdminCredentials, [Parameter(mandatory = $true)] [pscredential]$AdAdminCredentials, [Parameter(mandatory = $false)] [string]$isServicePrincipal = "False", [Parameter(mandatory = $false)] [AllowEmptyString()] [string]$AadTenantId = "", [Parameter(mandatory = $true)] [string]$SubscriptionId, [Parameter(mandatory = $true)] [int]$userLogoffDelayInMinutes, [Parameter(mandatory = $true)] [string]$userNotificationMessege, [Parameter(mandatory = $true)] [string]$messageTitle, [Parameter(mandatory = $true)] [string]$deleteordeallocateVMs, [Parameter(mandatory = $true)] [string]$DomainName, [Parameter(mandatory = $true)] [int]$rdshNumberOfInstances, [Parameter(mandatory = $true)] [string]$rdshPrefix, [Parameter(mandatory = $false)] [string]$RDPSModSource = 'attached' ) $ScriptPath = [System.IO.Path]::GetDirectoryName($PSCommandPath) # Dot sourcing Functions.ps1 file . (Join-Path $ScriptPath "Functions.ps1") # Setting ErrorActionPreference to stop script execution when error occurs $ErrorActionPreference = "Stop" write-log -message 'Script being executed: Cleanup old session hosts' # Testing if it is a ServicePrincipal and validade that AadTenant ID in this case is not null or empty ValidateServicePrincipal -IsServicePrincipal $isServicePrincipal -AADTenantId $AadTenantId ImportRDPSMod -Source $RDPSModSource -ArtifactsPath $ScriptPath # Authenticating to Windows Virtual Desktop . AuthenticateRdsAccount -DeploymentUrl $RDBrokerURL -Credential $TenantAdminCredentials -ServicePrincipal:($isServicePrincipal -eq 'True') -TenantId $AadTenantId SetTenantGroupContextAndValidate -TenantGroupName $definedTenantGroupName -TenantName $TenantName # Checking if host pool exists. Write-Log -Message "Checking Hostpool exists inside the Tenant" $HostPool = Get-RdsHostPool -TenantName "$TenantName" -Name "$HostPoolName" -ErrorAction SilentlyContinue if (!$HostPool) { throw "$HostpoolName Hostpool does not exist in $TenantName Tenant" } Write-Log -Message "Hostpool exists inside tenant: $TenantName" $RequiredModules = @("AzureRM.Resources", "Azurerm.Profile", "Azurerm.Compute", "Azurerm.Network", "Azurerm.Storage") Write-Log "checking if nuget package exists" if (!(Get-PackageProvider -Name NuGet -ErrorAction SilentlyContinue -ListAvailable)) { Write-Log "installing nuget package inside vm: $env:COMPUTERNAME" Install-PackageProvider -Name nuget -Force } foreach ($ModuleName in $RequiredModules) { do { #Check if Module exists $InstalledModule = Get-InstalledModule -Name $ModuleName -ErrorAction SilentlyContinue if (!$InstalledModule) { Write-Log "Installing azureRMModule '$ModuleName' inside vm: $env:COMPUTERNAME" Install-Module $ModuleName -AllowClobber -Force } } until ($InstalledModule) } Import-Module AzureRM.Resources Import-Module Azurerm.Profile Import-Module Azurerm.Compute Import-Module Azurerm.Network Import-Module Azurerm.Storage #Authenticate AzureRM $authentication = $null try { if ($isServicePrincipal -eq "True") { $authentication = Add-AzureRmAccount -Credential $TenantAdminCredentials -SubscriptionId $SubscriptionId -ServicePrincipal -TenantId $AadTenantId } else { $authentication = Add-AzureRmAccount -Credential $TenantAdminCredentials -SubscriptionId $SubscriptionId } if (!$authentication) { throw $authentication } } catch { throw [System.Exception]::new("Error authenticating AzureRM account, isServicePrincipal = $isServicePrincipal", $PSItem.Exception) } Write-Log -Message "AzureRM account authentication successful. Result:`n$($authentication | Out-String)" if ($authentication.Context.Subscription.Id -ne $SubscriptionId) { Write-Log -Err "AzureRM auth subscription ID '$($authentication.Context.Subscription.Id)' doesn't match the subscription ID of the deployment '$SubscriptionId'" } # collect new session hosts $NewSessionHostNames = @{ } for ($i = 0; $i -lt $rdshNumberOfInstances; ++$i) { $NewSessionHostNames.Add("${rdshPrefix}${i}.${DomainName}".ToLower(), $true) } Write-Log -Message "List of new Session Host servers in $HostPoolName :`n$($NewSessionHostNames.Keys | Out-String)" $ListOfSessionHosts = Get-RdsSessionHost -TenantName "$Tenantname" -HostPoolName "$HostPoolName" $ShslogObj = $ListOfSessionHosts.SessionHostName | Out-String Write-Log -Message "List of Session Host servers in $HostPoolName :`n$ShslogObj" #Collect the session hostnames $SessionHostNames = 0 $SessionHostNames = @() foreach ($SessionHost in $ListOfSessionHosts) { if (!$NewSessionHostNames.ContainsKey($SessionHost.SessionHostName.ToLower())) { $SessionHostNames += $SessionHost.SessionHostName } } $UniqueSessionHostNames = $SessionHostNames | Select-Object -Unique $ListOfUserSessions = Get-RdsUserSession -TenantName "$TenantName" -HostPoolName "$HostPoolName" if ($ListOfUserSessions) { foreach ($UserSession in $ListOfUserSessions) { $SessionHostName = $UserSession.SessionHostName if ($NewSessionHostNames.ContainsKey($SessionHostName.ToLower())) { continue } $SessionId = $UserSession.SessionId $UserPrincipalName = $UserSession.UserPrincipalName | Out-String # Before removing session hosts from hostpool, sending User session message to User Send-RdsUserSessionMessage -TenantName "$TenantName" -HostPoolName "$HostPoolName" -SessionHostName "$SessionHostName" -SessionId $SessionId -MessageTitle $messageTitle -MessageBody $userNotificationMessege -NoUserPrompt Write-Log -Message "Sent a user session message to $UserPrincipalName and sessionid was $SessionId" } } $allShsNames = $UniqueSessionHostNames | Out-String Write-Log -Message "Collected old sessionhosts and remove from $HostPoolName hostpool : `n$allShsNames" $rdshIsServer = isRdshServer if ($rdshIsServer) { Add-WindowsFeature RSAT-AD-PowerShell #Get Domaincontroller $DName = Get-ADDomainController -Discover -DomainName $DomainName $DControllerVM = $DName.Name $ZoneName = $DName.Forest } $ConvertSeconds = $userLogoffDelayInMinutes * 60 Start-Sleep -Seconds $ConvertSeconds foreach ($SessionHostName in $UniqueSessionHostNames) { # Keeping session host in drain mode $shsDrain = Set-RdsSessionHost -TenantName "$Tenantname" -HostPoolName "$HostPoolName" -Name "$SessionHostName" -AllowNewSession $false $shsDrainlog = $shsDrain | Out-String Write-Log -Message "Sesssion host server in drain mode : `n$shsDrainlog" Remove-RdsSessionHost -TenantName "$TenantName" -HostPoolName "$HostPoolName" -Name "$SessionHostName" -Force Write-Log -Message "Successfully $SessionHostName removed from hostpool" $VMName = $SessionHostName.Split(".")[0] if ($deleteordeallocateVMs -eq "Delete") { # Remove the VM's and then remove the datadisks, osdisk, NICs Get-AzureRmVM | Where-Object { $_.Name -eq $VMName } | ForEach-Object { $a = $_ $DataDisks = @($_.StorageProfile.DataDisks.Name) $OSDisk = @($_.StorageProfile.OSDisk.Name) Write-Log -Message "Removing $VMName VM and associated resources from Azure" #Write-Warning -Message "Removing VM: $($_.Name)" $_ | Remove-AzureRmVM -Force -Confirm:$false Write-Log -Message "Successfully removed VM from Azure" $_.NetworkProfile.NetworkInterfaces | ForEach-Object { $NICName = Split-Path -Path $_.Id -Leaf Get-AzureRmNetworkInterface | Where-Object { $_.Name -eq $NICName } | Remove-AzureRmNetworkInterface -Force } Write-Log -Message "Successfully removed $VMName vm NIC" # Support to remove managed disks if ($a.StorageProfile.OSDisk.ManagedDisk) { if ($OSDisk) { foreach ($ODisk in $OSDisk) { Get-AzureRmDisk -ResourceGroupName $_.ResourceGroupName -DiskName $ODisk | Remove-AzureRmDisk -Force } } if ($DataDisks) { foreach ($DDisk in $DataDisks) { Get-AzureRmDisk -ResourceGroupName $_.ResourceGroupName -DiskName $DDisk | Remove-AzureRmDisk -Force } } } # Support to remove unmanaged disks (from Storage Account Blob) else { # This assumes that OSDISK and DATADisks are on the same blob storage account # Modify the function if that is not the case. $saname = ($a.StorageProfile.OSDisk.Vhd.Uri -split '\.' | Select-Object -First 1) -split '//' | Select-Object -Last 1 $sa = Get-AzureRmStorageAccount | Where-Object { $_.StorageAccountName -eq $saname } # Remove DATA disks $a.StorageProfile.DataDisks | ForEach-Object { $disk = $_.Vhd.Uri | Split-Path -Leaf Get-AzureStorageContainer -Name vhds -Context $Sa.Context | Get-AzureStorageBlob -Blob $disk | Remove-AzureStorageBlob Write-Log -Message "Removed DataDisk $disk successfully" } # Remove OSDisk disk $disk = $a.StorageProfile.OSDisk.Vhd.Uri | Split-Path -Leaf Get-AzureStorageContainer -Name vhds -Context $Sa.Context | Get-AzureStorageBlob -Blob $disk | Remove-AzureStorageBlob Write-Log -Message "Removed OSDisk $disk successfully" # Remove Boot Diagnostic $diagVMName = 0 $diag = $_.Name.ToLower() $diagVMName = $diag -replace '[\-]', '' $dCount = $diagVMName.Length if ($dCount -cgt 9) { $digsplt = $diagVMName.substring(0, 9) $diagVMName = $digsplt } $diagContainerName = ('bootdiagnostics-{0}-{1}' -f $diagVMName, $_.VmId) Set-AzureRmCurrentStorageAccount -Context $sa.Context Remove-AzureStorageContainer -Name $diagContainerName -Force Write-Log -Message "Successfully removed boot diagnostic" } #$avSet=Get-AzureRmVM | Where-Object {$_.Name -eq $VMName} | Remove-AzureRmAvailabilitySet -Force # //todo check if this VM belongs to avail set before deleting $avset = Get-AzureRmAvailabilitySet -ResourceGroupName $a.ResourceGroupName if ($null -eq $avset.VirtualMachinesReferences.Id) { Get-AzureRmAvailabilitySet -ResourceGroupName $a.ResourceGroupName -ErrorAction SilentlyContinue | Remove-AzureRmAvailabilitySet -Force Write-Log -Message "Successfully removed availabilityset" } $checkResources = Get-AzureRmResource -ResourceGroupName $a.ResourceGroupName if (!$checkResources) { Remove-AzureRmResourceGroup -Name $a.ResourceGroupName -Force Write-Log -Message "Successfully removed ResourceGroup" } } #Removing VM from domain controller and DNS Record if ($rdshIsServer) { $result = Invoke-Command -ComputerName $DControllerVM -Credential $AdAdminCredentials -ScriptBlock { param($ZoneName, $VMName) Get-ADComputer -Identity $VMName | Remove-ADObject -Recursive -Confirm:$false Remove-DnsServerResourceRecord -ZoneName $ZoneName -RRType "A" -Name $VMName -Force -Confirm:$false } -ArgumentList ($ZoneName, $VMName) -ErrorAction SilentlyContinue # //todo check: $result might be $null even if the above cmd succeeds if ($result) { Write-Log -Message "Successfully removed $VMName from domaincontroller" Write-Log -Message "successfully removed dns record of $VMName" } } } else { #Deallocate the VM Get-AzureRmVM | Where-Object { $_.Name -eq $VMName } | Stop-AzureRmVM -Force $StateOftheVM = $false while (!$StateOftheVM) { $ProvisioningState = Get-AzureRmVM -Status | Where-Object { $_.Name -eq $VMName } if ($ProvisioningState.PowerState -eq "VM deallocated") { $StateOftheVM = $true Write-Log -Message "VM has been stopped: $VMName" } else { Write-Log -Message "Waiting for $VMName VM to stop... [current state: $($ProvisioningState.PowerState)]" } } } } $AllSessionHosts = Get-RdsSessionHost -TenantName "$TenantName" -HostPoolName "$HostPoolName" $OldSessionHosts = $AllSessionHosts.SessionHostName | Where-Object { !$NewSessionHostNames.ContainsKey($_.ToLower()) } if ($OldSessionHosts) { throw "Old Session Hosts were not removed from hostpool $HostPoolName" }