Propósito:
El objetivo de este post es avisar a los usuarios que tienen pendiente un reinicio.
Para mi una de las peores cosas que tiene Microsoft es como alertar o advertir a los usuarios de un evento, sinceramente no me gusta nada los pequeños mensajes que pasan desapercibidos en la parte izquierda de la pantalla.
Otra cosa que no da muchas opciones Microsoft, es forzar un reinicio con un numero importante de alertas a los usuarios. Sin utilizar comandos, solo lo permite el Wsus un reinicio forzoso y sus avisos no son los mejores. Con lo que he tenido que pensar en otra manera de alertar a mis usuarios, de las muchas que hay se me ha ocurrido la siguiente, solo avisando, que he querido compartir aquí.
Aunque lo parezca, es uno de los script que me ha llevado más tiempo crear, he pasado por muchas formas y versiones y con la ayuda de Copilot ha acabado como aquí voy a explicar.
Lo he preparado para que salte en el logon de inicio y en cada bloqueo y desbloqueo de la pantalla. Dejará de aparecer cuando ya no tenga ningún reinicio pendiente.
Pasos
La GPO.
Como veréis es una GPO de usuario, no de máquina, ya que es la mejor manera de que las alertas interactúen con el usuario conectado.
Tenemos que crear en la GPO una tarea programada, esta tarea la inyectaremos en los PCs.
Argumentos: -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -Command "& { $Source='\\Midominio.com\SYSVOL\midominio.com\scripts\WSUS\avisoreiniciopendiente.ps1'; $Target=$env:ProgramData+'\RebootAlert\avisoreiniciopendiente.ps1'; if (-not (Test-Path (Split-Path $Target))) { New-Item -ItemType Directory -Path (Split-Path $Target) -Force | Out-Null }; Copy-Item $Source $Target -Force; Start-Process powershell.exe -ArgumentList @('-NoProfile','-ExecutionPolicy','Bypass','-File', $Target) -WindowStyle Hidden }"
Poner el script en un recurso compartido donde puedan leer todos los usuarios.
Código:
|
#
================================================== # ALERTA DE
REINICIO PENDIENTE - PRODUCCIÓN #
================================================== # - Muestra
aviso WPF si hay reinicio pendiente # - Sin
control de tiempo (sale siempre que haya reboot) # - Evita
doble ejecución mediante Mutex (que no salga dos veces seguidas la alerta) # - Pensado
para ejecución vía GPO / Tarea programada #
================================================== Add-Type
-AssemblyName PresentationFramework #
-------------------------------------------------- # EVITAR
DOBLE EJECUCIÓN DEL SCRIPT # (necesario
porque se lanza PowerShell oculto)#
================================================== # ALERTA DE
REINICIO PENDIENTE - PRODUCCIÓN #
================================================== # - Muestra
aviso WPF si hay reinicio pendiente # - Sin
control de tiempo (sale siempre que haya reboot) # - Evita
doble ejecución mediante Mutex # - Pensado
para ejecución vía GPO / Tarea programada #
================================================== Add-Type
-AssemblyName PresentationFramework #
-------------------------------------------------- # EVITAR
DOBLE EJECUCIÓN DEL SCRIPT # (necesario
porque se lanza PowerShell oculto) #
-------------------------------------------------- $mutexName =
"Global\RebootPendingAlertMutex" $createdNew = $false $mutex = New-Object
System.Threading.Mutex($true, $mutexName, [ref]$createdNew) if (-not
$createdNew) { # Ya hay otra instancia ejecutándose exit } #
-------------------------------------------------- # FUNCIÓN:
Detectar reinicio pendiente #
-------------------------------------------------- function Test-PendingReboot {
if (Test-Path
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto
Update\RebootRequired") { return $true
}
if (Test-Path
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based
Servicing\RebootPending") { return $true } return $false } #
-------------------------------------------------- # FUNCIÓN:
Motivo del reinicio pendiente #
-------------------------------------------------- function Get-PendingRebootReason {
if (Test-Path
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto
Update\RebootRequired") { return "actualizaciones de
Windows"
}
if (Test-Path
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based
Servicing\RebootPending") { return "actualizaciones
del sistema" } return "tareas de mantenimiento del
sistema" } #
-------------------------------------------------- # SI NO HAY
REINICIO PENDIENTE, SALIR #
-------------------------------------------------- if (-not (Test-PendingReboot)) {
exit } #
-------------------------------------------------- # OBTENER MOTIVO DEL REINICIO #
-------------------------------------------------- $rebootReason =
Get-PendingRebootReason #
-------------------------------------------------- # VENTANA WPF #
-------------------------------------------------- [xml]$xaml = @" <Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" Title="Miempresa. Reinicio
pendiente" Height="240" Width="540"
WindowStartupLocation="CenterScreen" ResizeMode="NoResize" Topmost="True">
<Grid Margin="10"> <Grid.RowDefinitions> <RowDefinition
Height="Auto"/> <RowDefinition
Height="*"/> <RowDefinition
Height="Auto"/> </Grid.RowDefinitions> <!-- BARRA SUPERIOR ROJA
--> <Border Background="#B71C1C" Grid.Row="0"
Padding="10"> <TextBlock Text="Reinicio
pendiente."
Foreground="White"
FontSize="16"
FontWeight="Bold"
HorizontalAlignment="Center"/> </Border> <!-- TEXTO PRINCIPAL --> <Border Grid.Row="1"
Padding="20"> <TextBlock Text="El equipo necesita
reiniciarse para completar $rebootReason. Por favor,
guarda tu trabajo y reinicia cuando te sea posible." TextWrapping="Wrap"
TextAlignment="Center"
VerticalAlignment="Center" FontSize="13"/> </Border> <!-- BOTÓN --> <Button Name="AceptarBtn" Grid.Row="2" Width="120" Height="35" Margin="10"
HorizontalAlignment="Right"
Background="#D32F2F" Foreground="White" FontWeight="Bold"
Content="Aceptar"/>
</Grid> </Window> "@ $reader = New-Object
System.Xml.XmlNodeReader $xaml $window =
[Windows.Markup.XamlReader]::Load($reader) $button =
$window.FindName("AceptarBtn") $button.Add_Click({
$window.Close() }) $window.ShowDialog() | Out-Null #
-------------------------------------------------- $mutexName =
"Global\RebootPendingAlertMutex" $createdNew = $false $mutex = New-Object
System.Threading.Mutex($true, $mutexName, [ref]$createdNew) if (-not
$createdNew) { # Ya hay otra instancia ejecutándose exit } #
-------------------------------------------------- # FUNCIÓN:
Detectar reinicio pendiente #
-------------------------------------------------- function Test-PendingReboot {
if (Test-Path
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto
Update\RebootRequired") { return $true
}
if (Test-Path
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based
Servicing\RebootPending") { return $true } return $false } #
-------------------------------------------------- # FUNCIÓN:
Motivo del reinicio pendiente #
-------------------------------------------------- function Get-PendingRebootReason {
if (Test-Path
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto
Update\RebootRequired") { return "actualizaciones de
Windows"
}
if (Test-Path
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based
Servicing\RebootPending") { return "actualizaciones
del sistema" } return "tareas de mantenimiento del
sistema" } #
-------------------------------------------------- # SI NO HAY
REINICIO PENDIENTE, SALIR #
-------------------------------------------------- if (-not (Test-PendingReboot)) {
exit } #
-------------------------------------------------- # OBTENER MOTIVO DEL REINICIO #
-------------------------------------------------- $rebootReason =
Get-PendingRebootReason #
-------------------------------------------------- # VENTANA WPF #
-------------------------------------------------- [xml]$xaml = @" <Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" Title="Midominio.
Reinicio pendiente" Height="240" Width="540"
WindowStartupLocation="CenterScreen" ResizeMode="NoResize" Topmost="True">
<Grid Margin="10"> <Grid.RowDefinitions> <RowDefinition
Height="Auto"/> <RowDefinition
Height="*"/> <RowDefinition
Height="Auto"/> </Grid.RowDefinitions> <!-- BARRA SUPERIOR ROJA --> <Border
Background="#B71C1C" Grid.Row="0"
Padding="10"> <TextBlock Text="Reinicio
pendiente."
Foreground="White"
FontSize="16"
FontWeight="Bold"
HorizontalAlignment="Center"/> </Border> <!-- TEXTO PRINCIPAL --> <Border Grid.Row="1"
Padding="20"> <TextBlock Text="El equipo
necesita reiniciarse para completar $rebootReason. Por favor,
guarda tu trabajo y reinicia cuando te sea posible." TextWrapping="Wrap"
TextAlignment="Center" VerticalAlignment="Center" FontSize="13"/> </Border> <!-- BOTÓN --> <Button
Name="AceptarBtn" Grid.Row="2" Width="120" Height="35" Margin="10"
HorizontalAlignment="Right"
Background="#D32F2F" Foreground="White" FontWeight="Bold"
Content="Aceptar"/>
</Grid> </Window> "@ $reader = New-Object
System.Xml.XmlNodeReader $xaml $window =
[Windows.Markup.XamlReader]::Load($reader) $button =
$window.FindName("AceptarBtn") $button.Add_Click({
$window.Close() }) $window.ShowDialog()
| Out-Null |
Se me ocurre muchos puntos de mejora, pero los dejo para más adelante.
Sinceramente, se puede usar para lanzar cualquier tipo de alertas.
by GoN | Published: May 2026 | Last Updated:







No hay comentarios:
Publicar un comentario