viernes, 4 de enero de 2019

Antes de reinstalar WSUS intente esto...

A muchos en el ambiente de TI nos ha pasado que Windows Server Update Services, mejor conocido como WSUS, se nos vuelve un dolor de cabeza debido a  constantes errores en la interfaz de administración, caídas de los servicios de WSUS y de los grupos de aplicaciones de WSUS en el IIS, procesos de aprobación y rechazo de actualizaciones eternos, etc. y, para empeorar las cosas, WSUS es un devorador de espacio en disco terrible que consume cualquier cantidad de almacenamiento que le pongan.



El error más común cuando estamos trabajando con WSUS es que en cualquier operación que ejecutemos en la consola de administración, nos salta un error como el siguiente, y la operación que intentábamos hacer no se puede realizar.

Uno de los errores más comunes de la consola de WSUS

Error: error de conexión
Error al intentar conectar con el servidor WSUS. Esto puede deberse a varios motivos. Compruebe la conectividad con el servidor. Si el problema persiste, póngase en contacto con el administrador de la red.

Esto puede pasar con algo tan simple como hacer clic en "Todas las actualizaciones", realizando una sincronización, haciendo una búsqueda de  un KB, etc.
Si hacemos clic en Copiar al portapapeles nos encontramos con un error similar a este:
La consola de administración de WSUS no pudo conectar con el servidor WSUS a través de la API remota.

Compruebe si IIS, SQL y el servicio Update Services se están ejecutando en el servidor. Si el problema persiste, intente reiniciar IIS, SQL y el servicio Update Services.

System.Net.WebException -- Se excedió el tiempo de espera de la operación

Source
System.Web.Services

Stack Trace:
   en System.Web.Services.Protocols.WebClientProtocol.GetWebResponse(WebRequest request)
   en Microsoft.UpdateServices.Internal.DatabaseAccess.ApiRemotingCompressionProxy.GetWebResponse(WebRequest webRequest)
   en System.Web.Services.Protocols.SoapHttpClientProtocol.Invoke(String methodName, Object[] parameters)
   en Microsoft.UpdateServices.Internal.ApiRemoting.ExecuteSPSearchUpdates(String updateScopeXml, String preferredCulture, Int32 publicationState)
   en Microsoft.UpdateServices.Internal.DatabaseAccess.AdminDataAccessProxy.ExecuteSPSearchUpdates(String updateScopeXml, String preferredCulture, ExtendedPublicationState publicationState)
   en Microsoft.UpdateServices.Internal.BaseApi.Update.SearchUpdates(UpdateScope searchScope, ExtendedPublicationState publicationState, UpdateServer updateServer)
   en Microsoft.UpdateServices.UI.AdminApiAccess.UpdateManager.GetUpdates(ExtendedUpdateScope filter)
   en Microsoft.UpdateServices.UI.AdminApiAccess.BulkUpdatePropertiesCache.GetAndCacheUpdates(ExtendedUpdateScope updateScope, ComputerTargetScope computerTargetScope)
   en Microsoft.UpdateServices.UI.SnapIn.Pages.UpdatesListPage.GetListRows()

Después de mucho pelear con el sistema, me di cuenta que estos problemas de timeout son típicos en programas que usan bases de datos con problemas de afinación y mantenimiento. Entonces decidí darle un enfoque de DBA a la situación y encontré varias cosas que afectan negativamente el funcionamiento del sistema tales como:
  • Fragmentación de indices
  • Falta de indices
  • Restricciones de memoria
Para solucionar el problema tengo una serie de pasos que he condensado en este tutorial paso a paso que está dirigido a quienes no están familiarizados con la administracion de SQL Server por lo que está altamente detallado. Si ya sabes de SQL y quieres la ayuda rápida, la encontrarás al final de esta pagina.

Como darle un poco de cariño a la base de datos SUSDB.
Es decir hacer un mantenimiento de indices, estadísticas, etc. para ello vamos a necesitar instalar alguna herramientas de administración de SQL Server en el servidor si la instalación está trabajando con Base de datos interna de Windows. Si no sabemos cual base de datos está usando, podemos ir al registro del sistema (Usando REGEDIT.EXE) y mirar en la clave HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Update Services\Server\Setup y ver el nombre de valor SqlServerName; si el dato es MICROSOFT##WID, la base de datos es local y necesitaremos instalar en la maquina el SQL Server Management Studio u otra herramienta de administración de SQL Server, tal como mssql-cli o sqlcmd.

Como se muestra el valor SQLServerName con la base de datos interna de Windows
En este caso usaremos la herramienta grafica, es decir el SQL Server Management Studio que se puede obtener directamente de Microsoft, para mayor facilidad pueden encontrar el link aqui. Debo resaltar que es obligatorio instalarla localmente en el servidor debido a que esta no permite conectarse de otro modo.
Después de  instalar el programa, debemos ejecutarlos con privilegios de administrador, es decir, haciendo clic derecho sobre el icono del Management Studio, vamos a Mas y clic en Ejecutar como administrador. Esto es MUY importante puesto que si no tenemos el privilegio adecuado, el motor de base de datos no nos dejará conectar.
Cuando el programa nos este pidiendo credenciales, nos aparecerá una ventana similar a la siguiente:

En Nombre de servidor servidor debe poner una de las siguientes lineas y hacemos clic en Conectar:
 Cuando ya estemos conectados al motor nos debe aparecer algo como esto a la izquierda de la ventana:


Hacemos clic en el botón Nueva consulta
Esto nos abre un editor de consultas, aquí pegaremos el script de re-indexación del TechNet de Microsoft que está aquí. Este script lo he probado en varias versiones de WSUS, incluyendo Windows Server 2016 y funciona muy bien gracias a que está escrito de manera muy genérica aunque en la pagina indiquen que ha sido verificado unicamente en Windows 2003 y 2008.
Cuando ya tengamos listo el script, haremos clic en Ejecutar.
Esta operación puede tardar varias horas, así que de aquí en adelante hay que armarse de paciencia. Para saber si el script está corriendo, podemos darnos cuenta en la parte inferior de la ventana, cuando aparezca Consulta ejecutada correctamente, sabremos que ya terminó.

Usualmente, después de esta operación, la ejecución de WSUS mejora bastante pero aún no es lo suficiente para que funcione correctamente en las operaciones más usadas. Es por ello que se hace necesario crear algunos indices que mejoran substancialmente el funcionamiento del sistema. Los siguientes indices son los mínimos que, en mi concepto, mejoran el desempeño a unos niveles aceptables. Para ello nuevamente hacemos clic en el botón Nueva consulta
Esto nos abre otro editor de consultas en blanco, aquí pegaremos el script que aparece al final de esas lineas. y haremos clic en Ejecutar.
A diferencia del anterior script, esta operación usualmente no tarda mucho y al finalizar podemos comprobar directamente sobre la consola de administración de WSUS el efecto de estos cambios.


/****************************************************************************** 
Este Script agrega indices necesarios a la base de datos SUSDB para mejorar la
ejecucion de algunas de las consultas más usadas por WSUS.
Este script ha sido publicado originalmente en https://historiasdeunsysadmin.blogspot.com/
y se entrega como codigo abierto 'tal cual' sin garantia de ningun tipo. Todo 
riesgo que surja del uso o el rendimiento del script sera responsabilidad 
de quien lo ejecuta.
******************************************************************************/ 

USE [SUSDB]
GO
CREATE NONCLUSTERED INDEX [IX_ivwApiUpdateRevision_IsLatestRevision_IsHidden_RevisionID]
ON [dbo].[ivwApiUpdateRevision] ([IsLatestRevision],[IsHidden])
INCLUDE ([RevisionID])
GO

CREATE NONCLUSTERED INDEX [IX_ivwApiUpdateRevision_IsLatestRevision_LocalUpdateID_RevisionID]
ON [dbo].[ivwApiUpdateRevision] ([IsLatestRevision])
INCLUDE ([LocalUpdateID],[RevisionID])
GO

CREATE NONCLUSTERED INDEX [IX_ivwApiUpdateRevision_IsLatestRevision_RevisionID]
ON [dbo].[ivwApiUpdateRevision] ([IsLatestRevision])
INCLUDE ([RevisionID])
GO

CREATE NONCLUSTERED INDEX [IX_tbDeployment_ActionID_IsAssigned_RevisionID_TargetGroupID_Priority]
ON [dbo].[tbDeployment] ([ActionID])
INCLUDE ([IsAssigned],[RevisionID],[TargetGroupID],[Priority])
GO

CREATE NONCLUSTERED INDEX [IX_tbDeployment_ActionID_RevisionID_TargetGroupID]
ON [dbo].[tbDeployment] ([ActionID])
INCLUDE ([RevisionID],[TargetGroupID])
GO

CREATE NONCLUSTERED INDEX [IX_tbDeployment_TargetGroupTypeID_ActionID_RevisionID]
ON [dbo].[tbDeployment] ([TargetGroupTypeID])
INCLUDE ([ActionID],[RevisionID])
GO

CREATE NONCLUSTERED INDEX [IX_tbDistributionComputerHardwareId_RevisionID_HardwareID]
ON [dbo].[tbDistributionComputerHardwareId] ([RevisionID],[HardwareID])
GO

CREATE NONCLUSTERED INDEX [IX_tbLocalizedPropertyForRevision_LocalizedPropertyID]
ON [dbo].[tbLocalizedPropertyForRevision] ([LocalizedPropertyID])
GO

CREATE NONCLUSTERED INDEX [IX_tbRevisionInCategory_CategoryID_Expanded_RevisionID]
ON [dbo].[tbRevisionInCategory] ([CategoryID],[Expanded])
INCLUDE ([RevisionID])
GO

CREATE NONCLUSTERED INDEX [IX_tbRevisionSupersedesUpdate_SupersededUpdateID]
ON [dbo].[tbRevisionSupersedesUpdate] ([SupersededUpdateID])
GO

CREATE NONCLUSTERED INDEX [IX_tbRevision_IsLeaf_RevisionID_LocalUpdateID]
ON [dbo].[tbRevision] ([IsLeaf])
INCLUDE ([RevisionID],[LocalUpdateID])
GO