En esta historia de poltergeist veremos como poder actualizar una o cientos de VDAs de Citrix en remoto y de forma desatendida. Lo primero que necesitamos es colocarnos nuestro Proton Blaster (PB) en la espalda y sacar un café doble; esta historia será larga y terrorífica.
Como de costumbre, os cuento mi experiencia y como yo lo he hecho, luego cada uno que cace sus propios fantasmas en su plataforma y adecue las partes del scripts en su entorno.
Si miramos la docu de Citrix, todo es muy sencillo (y no digo que no), pero creo que no es suficiente para poderlo hacer de manera totalmente automática en un entorno real.
Esto es aplicable a cualquier entorno, siempre y cuando se adapte él. Yo lo hago con Hyperv, pero si es vmware o cualquier otro hipervisor es cuestión de seguir el mismo procedimiento adaptado.
Mi idea era hacer un proceso completo, que tuviera una forma rápida de hacer rollback (marcha atrás) de una manera rápida e indolora en cada caso. Os podéis imaginar como acabo mi PB, lleno de fantasmas que capture por el camino.
Empecemos por parametrizar y tengamos a mano todo lo que queremos que sea variable. En mi caso:
vdi (Nombre completo), ddc (Nombre de un delivery controller para comprobaciones y poner en mantenimiento el escritorio), vmm (virtual machine manager) version_citrix (solo para meter info en los checkpoint), cred (credenciales para conectar al vdi) y credInfra (para conectar a la infra)
Param ( $vdi, $ddc, $vmm, $version_citrix="2402", [Parameter()] [ValidateNotNull()] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Cred = [System.Management.Automation.PSCredential]::Empty , [Parameter()] [ValidateNotNull()] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $credInfra = [System.Management.Automation.PSCredential]::Empty )
Lo siguiente es cargar los módulos que necesitamos en Powershell
# Cargando modulos $ErrorActionPreference = "Stop" Write-Output "$vdi === INFO === Importando modulos" Import-Module virtualmachinemanager Import-Module citrix* try { Write-Output "$vdi === INFO === Conectando a $vmm" $vmmserver= Get-SCVMMServer -ComputerName $vmm -Credential $credInfra } catch { Write-Output "$vdi === ERROR === No he podiddo conectarme al VMM" Write-Error "$vdi : No he podiddo conectarme al VMM" }
Seguimos haciendo unas comprobaciones previas. Primero si tenemos conectividad desde nuestra central de alertas de caza-fantasmas a los servidores. Testeo el puerto 5985 que es el de powershell remoto y el 445 porque copiaremos software a local. Después el acceso con las credenciales y para ello se comprueba que exista la carpeta c:\windows\temp que es donde dejaremos la basura temporal para luego sacarla toda de golpe. Y por ultimo que haya espacio en el disco. Con 5gb suficiente.
# Comprobamos conectiviad y acceso # =================================== Write-Output "$vdi === INFO === Comprobando conectividad desde $env:COMPUTERNAME hasta $vdi ..." if ((Test-NetConnection $vdi -port 5985 -ErrorAction SilentlyContinue -WarningAction SilentlyContinue).TcpTestSucceeded -ne $true -or (Test-NetConnection $vdi -port 445 -ErrorAction SilentlyContinue -WarningAction SilentlyContinue).TcpTestSucceeded -ne $true) { Write-Output "$vdi === ERROR === No conecto al puerto 5985/445 de $vdi, no puedo seguir asi" Write-Error "$vdi : Error de conexion al puerto 5985/445" } else { Write-Output "$vdi === INFO === Conectividad OK" #Comprobamos accesos Write-Output "$vdi === INFO === Comprobando Acceso al VDI con las credenciales..." try { Invoke-Command -ComputerName $vdi -Credential $cred -ScriptBlock { if (!(Test-Path C:\Windows\Temp)) {mkdir C:\windows\Temp | Out-Null ; Write-Output "$using:vdi === INFO === Creado directorio c:\windows\temp"} else {Write-Output "$using:vdi === INFO === El directorio c:\windows\temp existe"} } Write-Output "$vdi === INFO === Acceso al VDI OK" } catch { Write-Output "$vdi === ERROR === No tengo acceso al servidor, revisa las credenciales o el acceso remoto" Write-error "$vdi : Error al validar las crecenciales" } } # Comprobamos requisitos del sistema # =================================== $Req1 = $null $Req1=Invoke-Command -ComputerName $vdi -Credential $cred -ErrorAction SilentlyContinue -ScriptBlock { $Requirements= $false #Testeo de espacio disponible $DiskSpace = (Get-PSDrive -Name C).Free/1gb if ($DiskSpace -ge 5) { Write-Output "$using:vdi === INFO === El espacio en disco de $([int]$DiskSpace) GB es suficiente." $Requirements= $true return $Requirements } else { Write-Output "$using:vdi === ERROR === Espacio en disco insuficiente. ($([int]$DiskSpace) GB). Se necesitan al menos 5GB libres" $Requirements= $false return $Requirements } } Write-Output $req1[0]
Si todo va bien, continuamos con el siguiente bloque. Ahora creamos un checkpoint para poder tener un punto de partida y de regreso si algo va mal. Ante la duda, volvemos a este punto y todo debería de funcionar como estaba. Luego, hay que poner en mantenimiento el VDI en Citrix para que el cliente no pueda usarlo durante el proceso. Por ultimo mapeamos un Drive virtual y copiamos el software necesario a local. Os lo recomiendo si no queréis morir en el intento de ejecutarlo en remoto.
if ($req1[1] -ne $true) { Write-Output "$vdi === ERROR === Amplia el disco para poder seguir." Write-Error "$vdi : Espacio insuficiente en disco" } else { # Creamos checkpiont Invoke-Command -ComputerName $vdi -Credential $cred -ErrorAction SilentlyContinue -ScriptBlock { Write-Output "$using:vdi === INFO === Parando el servicio VSS de hyperv para hacer el checkpoint" Get-Service vmicvss -ErrorAction SilentlyContinue | Stop-Service -ErrorAction SilentlyContinue -Force Get-Service vssvc -ErrorAction SilentlyContinue | Stop-Service -ErrorAction SilentlyContinue -Force Write-Output "$using:vdi === INFO === Desactivando chequeo de disco" chkntfs /x c: | Out-Null } Write-Output "$vdi === INFO === Creando checkpoint a $($vdi.split(".")[0])" $vmm_vm=Get-SCVirtualMachine $($vdi.split(".")[0]) if ($vmm_vm -ne $null) { if ($vmm_vm | Get-SCVMCheckpoint | ? {$_.name -eq "VDA_upgrade_$version_citrix"}) { Write-Error "$vdi === ERROR === Ya hay un checkpoint para $($vdi.split(".")[0]) relacionado con el upgrade. Elimina los restos antes de seguir" } try { $vmm_vm | New-SCVMCheckpoint -Name "VDA_upgrade_$version_citrix" | out-null } catch { Write-Output "$vdi === ERROR === No se ha creado el checkpoint a $($vdi.split(".")[0]). Sin checkpoint no sigo" Write-Error "$vdi : No se ha creado el checkpoint" } if ($vmm_vm | Get-SCVMCheckpoint | ? {$_.name -eq "VDA_upgrade_$version_citrix"}) { Write-Output "$vdi === INFO === Checkpoint a $($vdi.split(".")[0]) creado correctamente" } else { Write-Output "$vdi === ERROR === No se ha creado el checkpoint a $($vdi.split(".")[0]). Sin checkpoint no sigo" Write-Error "$vdi : No se ha creado el checkpoint" } } else { Write-Output "$vdi === ERROR === VM: $($vdi.split(".")[0]) no encontrda en el VMM. Sin checkpoint no sigo" Write-Error "$vdi : No se ha encontrado la VM en el VMM" } #Ponemos en mantenimiento el vdi $InMaintenanceMode = $null $InMaintenanceMode = Invoke-Command -ComputerName $ddc -Credential $credInfra -ScriptBlock { $machine = $null $machine = Get-BrokerMachine -MachineName "$using:domain\$(($using:vdi).split(".")[0])" if ($machine -ne $null) { Set-BrokerMachineMaintenanceMode -InputObject $machine $true | Out-Null $machine = Get-BrokerMachine -MachineName "$using:domain\$(($using:vdi).split(".")[0])" if ($machine.InMaintenanceMode -eq $true) { Write-Output "$using:vdi === INFO === VDI $using:domain\$(($using:vdi).split(".")[0]) puesto en mantenimiento" $Requirements= $true return $Requirements } else { Write-Output "$using:vdi === ERROR === No he podido poner el VDI $using:domain\$(($using:vdi).split(".")[0]) en mantenimiento. no puedo seguir asi" Write-Error "$using:vdi : No se ha puesto en mantenimiento" $Requirements= $false return $Requirements } } else { Write-Output "$using:vdi === ERROR === VDI $using:domain\$(($using:vdi).split(".")[0]) no encontrda en Citrix. Sin ponerla en mantenimiento no sigo" Write-Error "$using:vdi : No se ha encontrado en Citrix" $Requirements= $false return $Requirements } } if ($InMaintenanceMode[1] -eq $true) { Write-Output $InMaintenanceMode[0] } else { return "$InMaintenanceMode[0]" } #Mapeo de drive y copia de datos $psdive_name= $("FolderDeploy_" + $vdi.split(".")[0]) Write-Output "$vdi === INFO === Creando unidad $psdive_name" New-PSDrive -Name $psdive_name -PSProvider FileSystem -Root "\\$vdi\c$\windows\temp" -Credential $cred | Out-Null if (Test-Path "$($psdive_name):") { Write-Output "$vdi === INFO === Unidad $psdive_name creada correctamente" Write-Output "$vdi === INFO === Copiando software a $vdi localmente (c:\windows\temp\FolderDeploy)" Copy-Item "C:\FolderDeploy\VDAupgrades\2402_2100" -Destination "$($psdive_name):\FolderDeploy" -Force -Recurse if (Test-Path "$($psdive_name):\FolderDeploy") { Write-Output "$vdi === INFO === Software copiado correctamente" $Requirements= $true #Limpieza de drive Remove-PSDrive -Name $psdive_name -Force |Out-Null if (!(Test-Path "$($psdive_name):")) { Write-Output "$vdi === INFO === Unidad $psdive_name desmapeada correctamente" } else { Write-Warning "$vdi === WARN === La unidad $psdive_name no se ha desmapeado correctamente" } } else { $Requirements= $false #Limpieza de drive Remove-PSDrive -Name $psdive_name -Force |Out-Null if (!(Test-Path "$($psdive_name):")) { Write-Output "$vdi === INFO === Unidad $psdive_name desmapeada correctamente" } else { Write-Warning "$vdi === WARN === La unidad $psdive_name no se ha desmapeado correctamente" } Write-Output "$vdi === ERROR === El software no se ha copiado" Write-Error "$vdi : El software no se ha copiado" } } else { $Requirements= $false Write-Output "$vdi === ERROR === La unidad $psdive_name no se ha mapeao correctamente" Write-Error "$vdi : La unidad $psdive_name no se ha mapeao" } }
Pues hasta aquí todo ha sido para ponernos guapos para el baile. Ahora es cuando nos toca darlo todo en la pista y triunfar. Instalamos los requisitos previos: NET Framework 4.8 y Microsoft Visual C++ (x86 y x64). El script comprueba los códigos de salida y los reinicios necesarios y reengancha con el vdi tras el reinicio. Aquí empezaron a aparecer los primeros fantasmas y veréis mismas cosas hechas de diferente manera, pues depende de ciertos problemas que fueron apareciéndome, servicios que bloqueaban operaciones, o que se paran en cierto proceso y rompe la comunicación… cada uno es libre de cambiar lo que necesite. Suerte… ejem… Sigamos.
# Proceso de upgrade # ====================== Write-Output "$vdi === INFO === Comprobando .NET Framework 4.8" $req2 = $null $req2=Invoke-Command -ComputerName $vdi -Credential $cred -ErrorAction SilentlyContinue -ScriptBlock { # .NET Framework 4.8 if ((Get-ItemProperty 'HKLM:\Software\Microsoft\NET Framework Setup\NDP\v4\Full' | Where-Object {$_.Version -ge '4.8'}) -eq $null) { Write-Output "$using:vdi === INFO === .NET Framework 4.8 es necesario instalarlo." $Requirements= $false return $Requirements } else { Write-Output "$using:vdi === INFO === .NET Framework 4.8 esta instalado." $Requirements= $true return $Requirements } } Write-Output $req2[0] if ($req2[1] -ne $true) { Write-Output "$vdi === INFO === Instalando... NET Framework 4.8" $req3 = $null $req3=Invoke-Command -ComputerName $vdi -Credential $cred -ErrorAction SilentlyContinue -ScriptBlock { # .NET Framework 4.8 $Path_Framework = "C:\windows\Temp\FolderDeploy\Support\DotNet48\ndp48-x86-x64-allos-enu.exe" $Arguments = "/q /norestart /ChainingPackage ADMINDEPLOYMENT" $p=Start-Process $Path_Framework -ArgumentList $Arguments -Wait -PassThru Write-Output "$($p.ExitCode)" if ($p.ExitCode -eq 3010) { Write-Output "$using:vdi === INFO === .NET Framework 4.8 se ha instalado correctamente." } else { Write-Output "$using:vdi === ERROR === .NET Framework 4.8 ha fallado en la instalacion" } } Write-Output $req3[1] if ($req3[0] -eq 3010) { try { Write-Output "$vdi === INFO === Reiniciando el VDI, toca esperar a que vuelva a la vida." Restart-Computer -ComputerName $vdi -Credential $cred -Wait -Timeout 600 -Force -Protocol WSMan } catch { Write-Output "$vdi === ERROR === Ha pasado mas de 10 minutos desde el reincio, algo va mal. echale un ojo" Write-Error "$vdi : Timeout tras el reinicio" } } else { Write-Output "$vdi === ERROR === Sin NET Framework 4.8 no puedo seguir " Write-Error "$vdi : No se ha instalado NET Framework 4.8" } } Write-Output "$vdi === INFO === Instalando vcredist_x64 y vcredist_x86" $req4 = $null $req4=Invoke-Command -ComputerName $vdi -Credential $cred -ErrorAction SilentlyContinue -ScriptBlock { $Requirements= $false $Path_vcredist_x64= "C:\windows\Temp\FolderDeploy\Support\VcRedist\VC_redist.x64.exe" $Path_vcredist_x86= "C:\windows\Temp\FolderDeploy\Support\VcRedist\VC_redist.x86.exe" $Arguments = '/install /quiet /norestart' if (Get-WmiObject -Class win32_product | ? {$_.Name -like "*Microsoft Visual C++ * X64 *" -and $_.version -ge "14.40.*"}) { $mvcx64= "Isinstalled" } else { $p1 = $null $p1=Start-Process $Path_vcredist_x64 -ArgumentList $Arguments -Wait -PassThru } if ($p1.ExitCode -eq 3010 -or $p1.ExitCode -eq 0 -or $mvcx64 -eq "Isinstalled") { $p1.ExitCode if (Get-WmiObject -Class win32_product | ? {$_.Name -like "*Microsoft Visual C++ * X86 *" -and $_.version -ge "14.40.*"}) { $mvcx86= "Isinstalled" } else { $p2 = $null $p2=Start-Process $Path_vcredist_x86 -ArgumentList $Arguments -Wait -PassThru } if ($p2.ExitCode -eq 3010 -or $p2.ExitCode -eq 0 -or $mvcx86 -eq "Isinstalled") { $p2.ExitCode $Requirements= $true return $Requirements } else { $Requirements= $false return $Requirements } } else { $Requirements= $false return $Requirements } } if ($req4[2] -eq $true -and ($req4[0] -eq 3010 -or $req4[1] -eq 3010)) { try { Write-Output "$vdi === INFO === Vcredist se ha instaldo correctamente" Write-Output "$vdi === INFO === Reiniciando el VDI, toca esperar a que vuelva a la vida." Restart-Computer -ComputerName $vdi -Credential $cred -Wait -Timeout 600 -Force -Protocol WSMan } catch { Write-Output "$vdi === ERROR === Ha pasado mas de 10 minutos desde el reincio, algo va mal. Echale un ojo" Write-Error "$vdi : Timeout tras el reinicio" } } elseif ($req4[2] -eq $true) { Write-Output "$vdi === INFO === Vcredist esta instalado correctamente" } else { Write-Output "$vdi === ERROR === Sin Vcredist no puedo seguir " Write-Error "$vdi : Vcredist no instalado" }
Y por fin nos toca bailar nuestra canción favorita, es hora de actualizar la VDA. En este bloque instalaremos la VDA, este proceso requiere de uno o más reinicios que contemplo, proceso y controlo para seguir con la ejecución en el código. Es complicado decir porqué hago cada cosa tal y como la hago, pero lo que os he dicho, básicamente he cazado fantasmas… Además de instalar la VDA, si todo va bien se hacen algunos cambios en BrokerAgent.exe.config, podéis añadir o quitar a gusto. Por ultimo se elimina la carpeta temporal con todo el software y los logs. En caso de fallo los logs se mantienen en ese directorio para poder hacer debug.
Write-Output "$vdi === INFO === Instalado VDA... Espera, esto llevara un rato" sleep 10 $req5 = $null $req5=Invoke-Command -ComputerName $vdi -Credential $cred -ErrorAction SilentlyContinue -ScriptBlock { $Path_VDA = "C:\windows\Temp\FolderDeploy\x64\XenDesktop Setup\XenDesktopVdaSetup.exe" $Arguments = '/quiet /NOREBOOT /NORESUME /logpath "C:\windows\Temp\FolderDeploy\upgrade_logs" /remove_appdisk_ack /components vda /enable_hdx_ports /enable_hdx_udp_ports /enable_real_time_transport /virtualmachine /enable_remote_assistance /exclude "Citrix Personalization for App-V - VDA"' $p = $null $p=Start-Process $Path_VDA -ArgumentList $Arguments -Wait -PassThru Write-Output $p.ExitCode } $vda_version = $null while ($req5 -eq 3 -and $vda_version -ne $true) { Write-Output "$vdi === INFO === Reinicio de VDA necesario. Reinicio para continuar" $lastBoot1 = Invoke-Command -ComputerName $vdi -Credential $cred -ScriptBlock { (Get-CimInstance -ClassName Win32_OperatingSystem -ErrorAction SilentlyContinue).LastBootUpTime} Write-Output "$vdi === INFO === Last Boot Up Time: $lastBoot1" $kk=Invoke-Command -ComputerName $vdi -Credential $cred -ScriptBlock { Restart-Computer -Force } $lastBoot1_status="No restarted" $time=0 while ($lastBoot1_status -eq "No restarted") { $time += +1 Write-Output "$vdi === INFO === Esperando al reinicio, no te impacientes. $time misisipis" $lastBoot1_check = Invoke-Command -ComputerName $vdi -Credential $cred -ErrorAction SilentlyContinue -ScriptBlock { (Get-CimInstance -ErrorAction SilentlyContinue -ClassName Win32_OperatingSystem).LastBootUpTime} if ($lastBoot1 -gt $lastBoot1_check) { sleep 10 } else { $lastBoot1_status = "OK" } } Write-Output "$vdi === INFO === Reinicio de VDA completado. Continuo con la instalacion de la VDA." $req5 = $null $req5=Invoke-Command -ComputerName $vdi -Credential $cred -ErrorAction SilentlyContinue -ScriptBlock { $Path_VDA = "C:\windows\Temp\FolderDeploy\x64\XenDesktop Setup\XenDesktopVdaSetup.exe" $p=Start-Process $Path_VDA -Wait -PassThru Write-Output $p.ExitCode } Write-Output "$vdi === INFO === Continuo con la instalacion de la VDA ha dado como resultado: $req5 " #Comprobamos vesion vda para ultimo reinicio $vda_version = $null $vda_version=Invoke-Command -ComputerName $vdi -Credential $cred -ErrorAction SilentlyContinue -ScriptBlock { $vda_version=(Get-ItemProperty "hklm:\\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\citrix virtual desktop agent" -Name DisplayName).DisplayName if ($vda_version -like "*2402 LTSR CU2*") { return $true } else {$false} } Write-Output "$vdi === INFO === Vda en 2404: $vda_version " } if ($req5 -eq 0 -or $req5 -eq 8 -or $req5 -eq 9 -or $vda_version -eq $true) { Write-Output "$vdi === INFO === VDA instalada correctamente." try { Invoke-Command -ComputerName $vdi -Credential $cred -ErrorAction SilentlyContinue -ScriptBlock { #allowNtlm = true in BrokerAgent.exe.config Write-Output "$using:vdi === INFO === Configurando NTLM en el servicio de broker." $PathToBrokerConfig = $env:programfiles + '\Citrix\Virtual Desktop Agent\BrokerAgent.exe.config' $allowNtlmTrue = 'allowNtlm=' + [char]34 + 'true' + [char]34 $allowNtlmFalse = 'allowNtlm=' + [char]34 + 'false' + [char]34 (Get-Content $pathToBrokerConfig) -Replace $allowNtlmFalse, $allowNtlmTrue | Set-Content $pathToBrokerConfig -ErrorAction Stop Write-Output "$using:vdi === INFO === Configurando PortIca" if (!(Test-Path "HKLM:\SOFTWARE\Citrix\PortICA\")) { New-Item -Path "HKLM:\SOFTWARE\Citrix\PortICA\" -Force | Out-Null } New-ItemProperty -Path "HKLM:\SOFTWARE\Citrix\PortICA\" -Name "DelayLogoffNotificationEnabled" -PropertyType "DWord" -Value 1 -Force -ErrorAction Stop | Out-Null } } catch { Write-Output "$vdi === ERROR === Configuracion PortICA o NTLM en el servicio de broker ha fallado. Revisalo, no puedo seguir," Write-Error "$vdi : Configuracion PortICA o NTLM ha fallado" } #Eliminar software Write-Output "$vdi === INFO === Limpiando mierdas temporales" Invoke-Command -ComputerName $vdi -Credential $cred -ErrorAction SilentlyContinue -ScriptBlock { Remove-Item -Path C:\windows\Temp\FolderDeploy -Recurse -Force } Write-Output "$vdi === INFO === Configuracion VDA terminada correctamente. Reinicio para finalizar" #Restart-Computer -ComputerName $vdi -Credential $cred -Wait -Timeout 600 -Force -Protocol WSMan $lastBoot2 = Invoke-Command -ComputerName $vdi -Credential $cred -ScriptBlock { (Get-CimInstance -ClassName Win32_OperatingSystem -ErrorAction SilentlyContinue).LastBootUpTime} Write-Output "$vdi === INFO === Last Boot Up Time: $lastBoot2" $kk=Invoke-Command -ComputerName $vdi -Credential $cred -ErrorAction SilentlyContinue -ScriptBlock { Restart-Computer -Force } $lastBoot2_status="No restarted" $time=0 while ($lastBoot2_status -eq "No restarted") { $time += +1 Write-Output "$vdi === INFO === Esperando al reinicio, no te impacientes. $time misisipis" $lastBoot2_check = Invoke-Command -ComputerName $vdi -Credential $cred -ErrorAction SilentlyContinue -ScriptBlock { (Get-CimInstance -ErrorAction SilentlyContinue -ClassName Win32_OperatingSystem).LastBootUpTime} if ($lastBoot2 -gt $lastBoot2_check) { sleep 10 Write-Output "$vdi === INFO === Esperando aun... ten paciencia" } else { $lastBoot2_status = "OK" } if ($time -eq 20) { Write-Output "$using:vdi === ERROR === Esto esta tardando demasiado, revisa que pasa." Write-Error "$using:vdi === ERROR === Error en la reinstalacion" } } } else { Write-Output "$vdi === ERROR === La instalacion de la VDA ha fallado con el codigo: $req5" Write-Error "$vdi : La instalacion de la VDA ha fallado" }
Llegados a este punto, ya tenemos todo listo. Solo nos queda quitar el mantenimiento del escritorio y comprobar que esta registrado.
#Quitamos mantenimiento al vdi $InMaintenanceMode = $null $InMaintenanceMode = Invoke-Command -ComputerName $ddc -Credential $credInfra -ScriptBlock { $machine = $null $machine = Get-BrokerMachine -MachineName "$using:domain\$(($using:vdi).split(".")[0])" if ($machine -ne $null) { Set-BrokerMachineMaintenanceMode -InputObject $machine $false | Out-Null $machine = Get-BrokerMachine -MachineName "$using:domain\$(($using:vdi).split(".")[0])" if ($machine.InMaintenanceMode -eq $false) { Write-Output "$using:vdi === INFO === VDI $using:domain\$(($using:vdi).split(".")[0]) quitado de mantenimiento" $Requirements= $true return $Requirements } else { Write-Warning "$using:vdi === WARN === No he podido quitar el VDI $using:domain\$(($using:vdi).split(".")[0]) de mantenimiento. " $Requirements= $false return $Requirements } } else { Write-Warning "$using:vdi === WARN === VDI $using:domain\$(($using:vdi).split(".")[0]) no encontrdo en Citrix. Algo ha pasado porque antes si que estaba" $Requirements= $false return $Requirements } } if ($InMaintenanceMode[1] -eq $true) { Write-Output $InMaintenanceMode[0] } else { return $InMaintenanceMode[0] } # Finalizado Write-Output "$vdi === INFO === Esperando un par de minutos a que se registre" sleep 120 $fin = $null $fin=Invoke-Command -ComputerName $ddc -Credential $credInfra -ScriptBlock { $machine = Get-BrokerMachine -MachineName "$using:domain\$(($using:vdi).split(".")[0])" Write-Output $($machine) } if ($fin.RegistrationState -eq "Registered") { if ($fin.AgentVersion -like "*7.41*" -or $fin.AgentVersion -like "*2402*"){ Write-Output "$vdi === INFO === Upgrade completado correctamente. Version de VDI: $($fin.AgentVersion) y estado: $($fin.RegistrationState)" Write-Output "$vdi === INFO === Hasta la proxima!" } else { Write-warning "$vdi === WARNING === Upgrade completado correctamente, pero no se ha reportado a Citrix la version correcta. Version reportada: $($fin.AgentVersion) y estado: $($fin.RegistrationState)" Write-Warning "$vdi === WARNING === Revisalo. Es posible que no haya arrancado correctmente la VDA y necesite un reinicio." } } else { Write-Warning "$vdi === WARN === Upgrade completado, pero el VDI no esta registrado. Version de VDI: $($fin.AgentVersion) y estado: $($fin.RegistrationState)" Write-Output "$vdi === INFO === La proxima vez habra mas suerte" }
Si juntas todo esto en un script y te lo dejas a gusto tendras una manera de actualizar cientos de Vdas, bien de forma secuencial o en bloques. Para eso te dejo que uses tu imaginación, tal vez en otro post hable sobre mis experiencias de como hacer eso de manera fácil, por hoy ha sido suficiente.
Y si has llegado hasta aquí leyendo, eres un héroe: Espero que tu VDA este totalmente a salvo, no hayas tenido que usar el PB y hayas disfrutado de tu canción favorita en la pista de baile.