viernes, 27 de abril de 2012

Script para enviar alertas por falta de inventario de hardware

En el post anterior veíamos como generar un reporte que nos liste las computadoras que en determinada cantidad de días no enviaron su inventario de hardware al servidor.
En esta ocasión veremos como armar un sencillo script que envía un correo con el reporte que hicimos antes.
El mismo script puede usarse para enviar cualquier otro reporte de SCCM. Si además combinamos este script con una tarea programada de Windows podremos tener alertas periódicas con datos sobre las computadoras que administramos con SCCM.

Este es el script:

Remitente="correo del remitente"
Destinatario="correo del destinatario"
Asunto= "Asunto del correo"

'1 para SMTP local, 2 para SMTP remoto
TipoSMTP = 2 

'Dirección del servidor SMTP
SMTP = "dirección o IP de nuestro servidor SMTP"

'Puerto SMTP utilizado por nuestro servidor
Puerto = 25

'URL del reporte que enviaremos por correo
Reporte = "http://hellboy/Reportes/Report.asp?ReportID=431&Dias=2&SortRs1Col=3&SortRs1Dir=2"


'************************Envío del correo***************************************

Set objMessage = CreateObject("CDO.Message")
Set Stm = CreateObject("ADODB.Stream")


objMessage.Subject = Asunto
objMessage.From = Remitente
objMessage.To = Destinatario

objMessage.Configuration.Fields.Item _
("http://schemas.microsoft.com/cdo/configuration/sendusing") = TipoSMTP
'Servidor SMTP
objMessage.Configuration.Fields.Item _
("http://schemas.microsoft.com/cdo/configuration/smtpserver") = SMTP
'Puerto SMTP
objMessage.Configuration.Fields.Item _
("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = Puerto

objMessage.CreateMHTMLBody(Reporte)
'ObjMessage.AddAttachment (Adjunto)
objMessage.Configuration.Fields.Update

On Error Resume Next  
objMessage.Send
If not(Err.Number = 0) Then  
    MsgBox Err.Description, vbCritical, " Error al enviar el mail "  
End If

Así de simple.
En este caso el script envía el reporte que creamos en nuestro anterior post (el que buscaba computadoras sin inventario de hardware reciente), y le pasa como parámetro (dentro de la URL) la cantidad de días límite para la búsqueda (en este caso 2 días).
Para enviar cualquier otro reporte basta con ejecutarlo en el SCCM y copiar la URL dentro del script.
Para que las alertas lleguen periodicamente basta con crear una tarea programada que ejecute el script.

Saludos
Franco

miércoles, 25 de abril de 2012

Reporte con computadoras que no envían inventario de hardware durante X días

En un post anterior vimos la manera de armar una colección con clientes que no enviaban su inventario de hardware durante un tiempo determinado.
En este caso haremos algo parecido, pero con un reporte. La diferencia aquí es que al reporte le daremos como parámetro el número de días mínimo para que una computadora se considere que está fallando en su inventario.

La idea de tener un reporte de este tipo, además de la colección antes vista, es que este dato también pueda ser visto por usuarios que no tienen acceso a la consola, tal como puede ser personal de soporte técnico o seguridad.

La consulta en este caso es bastante sencilla. Se trata de una query con un único prompt y que utiliza la función DATEDIFF para obtener la cantidad de días desde el último inventario agregado a la base de datos.


SELECT     sys.Netbios_Name0 AS Servidor,
WS.LastHWScan AS [Último escaneo de HW],
DATEDIFF(day, WS.LastHWScan, GETDATE()) AS [Días desde el último escaneo]
FROM
v_GS_WORKSTATION_STATUS AS WS
INNER JOIN
v_R_System AS sys
ON sys.ResourceID = WS.ResourceID
WHERE  
(DATEDIFF(day, WS.LastHWScan, GETDATE()) > @dias)

Tan simple como eso. El resultado se verá más o menos así:

Saludos
Franco

lunes, 23 de abril de 2012

No funciona el inventario de hardware

En el anterior post veíamos la manera de armar una colección con todos los equipos que no reportan un inventario de hardware desde hace 30 días. Por lo general la razón para que el inventario de hardware no funcione se encuentra del lado del cliente. Problemas con el servicio de WMI, falta de espacio en disco, problemas de comunicación con el servidor, certificados, etc.
Pero si empezamos a notar que son muchas las computadoras que no reportan su inventario de hardware, entonces podemos estar ante un problema del lado del servidor, o para ser más precisos, un problema en la manera en que el cliente y el servidor se comunican para trasmitir la información del inventario de hardware.

El circuito del inventario de hardware

La información colectada por el cliente es empaquetada en un archivo MIF (Management Information Format).   Estos archivos son enviados por el cliente al servidor utilizando el servicio de transferencia en segundo plano (BITS). El servidor analiza cada uno de los MIFs recibidos, y si no hay ningún error agrega la información a la base de datos. En caso de error, el MIF es enviado a la carpeta BADMIFS, para que puedan ser analizados en busca del problema.

Errores en los MIFs

Muchas veces, los errores que impiden el procesamiento de un MIF son temporales. Algún valor faltante, información mal colectada, errores de formato, etc. Pero a veces se trata de errores que se repiten cada vez que el cliente envía información al servidor. Un ejemplo de este tipo de problemas son los archivos MIF demasiado grandes.

Cuando el tamaño importa

Si vamos a Site Settings/Client Agents/Hardware Inventory Client Agent, en el cuadro de diálogo que se abre, en la pestaña General, encontraremos la opción de  Maximum custom MIF file size (KB), dónde podremos especificar el valor máximo del archivo MIF a importar. El valor más grande admitido aquí es de 5120 KB, es decir 5 MB.


Si entramos a la carpeta C:\Program Files\Microsoft Configuration Manager\inboxes\auth\dataldr.box\BADMIFS, allí podremos ver todos los MIFS rechazados. En la vista Detalles de Windows, observaremos el tamaño de los archivos. Si este tamaño supera el establecido en el cuadro de diálogo mostrado anteriormente, los MIFS serán siempre rechazados y la información de inventario nunca pasará a la base de datos.

Por encima del límite

Si ninguno de los MIFs rechazados supera los 5MB que tiene como límite máximo el SCCM, entonces la solución a este problema consiste sencillamente en modificar la configuración establecida en la consola.
El problema es cuando nuestros MIFs superan los 5 MB, entonces deberemos recurrir a una modificación de la configuración de nuestro servidor un tanto más rústica.
Abrimos el editor de registro y buscamos la clave HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\SMS\Components\SMS_INVENTORY_DATA_LOADER\Max MIF Size. Modificamos su valor en decimal y establecemos el tamaño límite máximo para los MIFs en bytes.


El riesgo de los MIFs grandes

No es difícil suponer porque Microsoft decidió establecer un valor máximo para los MIFs a procesar. Cada MIF de un cliente debe transitar por nuestra red, almacenarse en nuestro servidor, y luego ser procesado, parseado y almacenado en nuestra base de datos. En una organización con 5000 clientes, establecer un MIF de 5 MB representa 24 GB de información saturando la red y ocupando espacio en disco y procesamiento de nuestros servidores.
Lo ideal, cuando nuestros MIFs crecen sin control, es empezar a limitar el inventario, reduciendo los items a recolectar. Para hacer esto deberemos modificar el tan temido y conocido SMS_DEF.MOF, del que hablaremos en otra oportunidad.

Saludos
Franco

jueves, 19 de abril de 2012

Colección de computadoras que no se reportan hace 30 días

El escaneo de hardware o Hardware Inventory es sin lugar a dudas una de las funciones más importantes del SCCM. En la mayoría de los casos, suele ser este inventario el que se realiza con mayor frecuencia, y el que nos marca la pauta de un cliente funcionando correctamente o no.
Existen reportes en el SCCM con los que fácilmente podremos buscar todos los equipos que no hayan enviado un inventario de hardware en los últimos días ¿pero que tal si nos interesa tener esos equipos en una colección?
De este modo podríamos tomar acciones de remediación en forma conjunta, o podemos hacerlo por el simple hecho de tener siempre a mano la lista de esos clientes problemáticos que nos veremos obligados a reparar.

Para armar una colección de este tipo no podremos utilizar el asistente de SCCM, sino que necesitaremos escribir la query en WQL dentro del cuadro de diálogo correspondiente. La consulta para esta colección quedaría así entonces:

select

SMS_R_SYSTEM.ResourceID,

SMS_R_SYSTEM.ResourceType,

SMS_R_SYSTEM.Name,

SMS_R_SYSTEM.SMSUniqueIdentifier,

SMS_R_SYSTEM.ResourceDomainORWorkgroup,

SMS_R_SYSTEM.Client

from SMS_R_System  inner join

SMS_G_System_WORKSTATION_STATUS

on SMS_G_System_WORKSTATION_STATUS.ResourceID = SMS_R_System.ResourceId

where SMS_G_System_WORKSTATION_STATUS.LastHardwareScan <=  DateAdd(dd,-30,GetDate())


Para modificar el tiempo mínimo desde el último reporte deben cambiar el valor al final de al consulta, dentro de  DateAdd(dd,-30,GetDate()). En este caso está configurado en 30 días, pero es posible asignar cualquier otro valor según sea necesario.

El resultado es una colección con clientes a los que deberemos echar una mirada.

Saludos
Franco

Colección para maquinas virtuales

Hoy una query sencillita, pero que puede resultar bastante útil. En este caso armaremos una colección dinámica poblada exclusivamente por maquinas virtuales.

La query para la colección sería esta:

select 
SMS_R_SYSTEM.ResourceID,
SMS_R_SYSTEM.ResourceType,
SMS_R_SYSTEM.Name,
SMS_R_SYSTEM.SMSUniqueIdentifier,
SMS_R_SYSTEM.ResourceDomainORWorkgroup,
SMS_R_SYSTEM.Client 
from SMS_R_System inner join 
SMS_G_System_COMPUTER_SYSTEM 
on SMS_G_System_COMPUTER_SYSTEM.ResourceId = SMS_R_System.ResourceId 
where SMS_G_System_COMPUTER_SYSTEM.Model in 
("Virtual Machine","VMware Virtual Platform")

En este caso agrupamos en la misma colección las maquinas virtuales creadas con software de Microsoft ("Virtual Machine") y aquellas que utilizan VMWare ("VMWare Virtual Platform").

Esta consulta puede armarse usando el asistente del SCCM, y no reviste mayor complejidad.



Del mismo modo, puede usarse una query similar, pero negando el operador "IN" para encontrar todas las computadoras que no son virtuales, las "físicas", por llamarlas de algún modo.

select 
SMS_R_SYSTEM.ResourceID,
SMS_R_SYSTEM.ResourceType,
SMS_R_SYSTEM.Name,
SMS_R_SYSTEM.SMSUniqueIdentifier,
SMS_R_SYSTEM.ResourceDomainORWorkgroup,
SMS_R_SYSTEM.Client 
from SMS_R_System inner join 
SMS_G_System_COMPUTER_SYSTEM 
on SMS_G_System_COMPUTER_SYSTEM.ResourceId = SMS_R_System.ResourceId 
where SMS_G_System_COMPUTER_SYSTEM.Model NOT IN 
("Virtual Machine","VMware Virtual Platform")

Como siempre que armamos colecciones dinámicas, es importante no olvidar crear un schedule para la actualización de los recursos, y en el caso de SCCM 2007 R3, tildar la opción para agregar recursos dinámicamente.

Saludos
Franco

martes, 17 de abril de 2012

[offTopic] AD Tidy, una tool imprescindible

Hoy no toca hablar de SCCM, sino de una tool para administradores que me resulta imprescindible, sobre todo si nuestro dominio tiene dimensiones desproporcionadas (en mi caso, más de 4000 computadoras y otros tantos usuarios).
En ambientes de este tipo es común que muchas cuentas queden obsoletas, sin referenciar realmente a un equipo o un usuario existente. En esos casos necesitamos realizar una limpieza, es decir buscar los equipos o usuarios que no se hayan logueado al dominio en mucho tiempo, y tomar con ellos alguna acción de remediación (deshabilitarlos, moverlos de OU, eliminarlos, etc.).

A este fin me encontré con la herramienta gratuita denominada AD Tidy que puede descargarse desde acá. Con esta herramienta solo debemos definir el criterio de búsqueda y obtener los resultados en apenas un par de segundos:



El resultado se ve así:

Y podemos encadenar acciones a tomar con todas las cuentas encontradas:

En fin, una herramienta altamente recomendable.

Saludos
Franco

lunes, 16 de abril de 2012

Colección con computadoras que NO tienen un software instalado

Una solicitud común, pero con cierta dificultad de resolver, es la de buscar todos los equipos que NO tengan determinado software instalado. Podemos realizar esta búsqueda a través de un reporte, de una query, o todavía mejor, de una colección dinámica.
La ventaja de usar la colección, es que una de las razones para solicitar el listado de equipos que NO tengan X software instalado, es, justamente, para instalarselo. Y el deploy debemos hacerlo sobre una colección.

La complejidad de la query en este caso reside en dos puntos. El primero es que se trata de una consulta anidada. Para quienes no están muy acostumbrados a los lenguajes de consulta (en este caso WQL), esto puede complicar un poco las cosas. El segundo inconveniente es que tenemos dos "Add/Remove Programs". El de los equipos con sistema operativo de 32 bits y el de los X64. Así que tendremos que buscar en ambas tablas y encontrar a quienes no aparezcan en ninguna de ellas.

Sin más rodeos, he aquí la query para obtener todas las computadoras que no cuenten con determinado software:

select SMS_R_SYSTEM.ResourceID,
SMS_R_SYSTEM.ResourceType,
SMS_R_SYSTEM.Name,
SMS_R_SYSTEM.SMSUniqueIdentifier,
SMS_R_SYSTEM.ResourceDomainORWorkgroup,
SMS_R_SYSTEM.Client
 from SMS_R_System inner join
 SMS_G_System_COMPUTER_SYSTEM
 on SMS_G_System_COMPUTER_SYSTEM.ResourceID = SMS_R_System.ResourceId
 where SMS_G_System_COMPUTER_SYSTEM.Name not in
 (select distinct SMS_G_System_COMPUTER_SYSTEM.Name from  SMS_R_System inner join SMS_G_System_COMPUTER_SYSTEM on SMS_G_System_COMPUTER_SYSTEM.ResourceID = SMS_R_System.ResourceId inner join SMS_G_System_ADD_REMOVE_PROGRAMS on SMS_G_System_ADD_REMOVE_PROGRAMS.ResourceID = SMS_R_System.ResourceId left join SMS_G_System_ADD_REMOVE_PROGRAMS_64 on SMS_G_System_ADD_REMOVE_PROGRAMS_64.ResourceID = SMS_R_System.ResourceId where SMS_G_System_ADD_REMOVE_PROGRAMS.DisplayName like
 "%NOMBRE DEL SOFTWARE%"
 or SMS_G_System_ADD_REMOVE_PROGRAMS_64.DisplayName like
 "%NOMBRE DEL SOFTWARE%")

Como podrán adivinar, deben reemplazar "NOMBRE DEL SOFTWARE" por el nombre del programa a buscar.

Esta query no puede armarse completamente en el diseñador de querys de SCCM, pero si les resulta más sencillo, pueden usarlo de este modo:

Tipo de criterio: Subselected values.
Donde: Computer System - Name
Operador: Is not in
Subselect: Y aquí pegan toda la subconsulta que aparece entre paréntesis en la consulta original:

select distinct SMS_G_System_COMPUTER_SYSTEM.Name from  SMS_R_System inner join SMS_G_System_COMPUTER_SYSTEM on SMS_G_System_COMPUTER_SYSTEM.ResourceID = SMS_R_System.ResourceId inner join SMS_G_System_ADD_REMOVE_PROGRAMS on SMS_G_System_ADD_REMOVE_PROGRAMS.ResourceID = SMS_R_System.ResourceId left join SMS_G_System_ADD_REMOVE_PROGRAMS_64 on SMS_G_System_ADD_REMOVE_PROGRAMS_64.ResourceID = SMS_R_System.ResourceId where SMS_G_System_ADD_REMOVE_PROGRAMS.DisplayName like
 "%NOMBRE DEL SOFTWARE%"
 or SMS_G_System_ADD_REMOVE_PROGRAMS_64.DisplayName like
 "%NOMBRE DEL SOFTWARE%"

El asistente nos dice que hay un error en la subconsulta, pero en realidad es una falla en el análisis, podemos obviar esta advertencia.

No olvidemos, que para que la coleccione funcione correctamente debemos actualizarla periódicamente y permitir el agregado dinámico de recursos:


Saludos
Franco



viernes, 13 de abril de 2012

Ya está el RTM de SCCM 2012

Atenti gente, según leo por acá salio la versión RTM de SCCM 2012 y ya pueden descargarla y empezar a usarla.
Tal como se sabía, tenemos el SCCM 2012 "a secas" y tenemos la versión que integra el antivirus de Microsoft, es decir el Forefront Endpoint Protection.

En mi experiencia personal, estuve probando las betas de esta última variante, es decir SCCM+FEP, y tengo que decir que me gusto. En mi organización utilizamos la solución de antivirus de Symantec, y si bien no es mi especialidad, la de Microsoft me pareció, al menos, igual de eficiente. Al sumarle la integración con SCCM y todas sus funcionalidades tenemos un producto muy potente.

El gran problema que había con FEP y SCCM 2007 eran las actualizaciones. En lo que, para mí, es un error gravísimo, la integración de FEP con SCCM 2007 obligaba a utilizar directamente WSUS para la descarga de firmas y actualizaciones, omitiendo totalmente la infraestructura de updates que teníamos armada en SCCM para el resto de las aplicaciones.

Según prometen los muchachos de Microsoft, en SCCM 2012 el problema está arreglado. En cuanto haga un par de pruebas, les cuento.


Les dejo un videito con el laburo que nos espera a muchos, la bendita migración:




Saludos
Franco

Comparar actualizaciones entre dos servidores

En forma predeterminada, el SCCM 2007 trae un reporte, el 97, que compara el inventario de software de dos computadoras. Esto puede ser muy útil en algunos casos, pero no lo es si lo que nos interesa saber es que actualizaciones fueron instaladas en un equipo y en otro no.
La idea de este reporte es, justamente, comparar las actualizaciones en dos computadoras, y devolvernos aquellas que están instaladas en uno de esos equipos y en el otro no.

Es una consulta compleja, pero que puede entenderse con un poco de análisis, y que fácilmente puede modificarse para cualquier otra comparación de inventario que les interese.

La query del reporte es la siguiente:

select
(case when inv1.Description0 is not null then @pc1 else @pc2 end) as Servidor,
  (case when inv1.Description0 is not null then inv1.User_Domain0 else inv2.User_Domain0 end) as Dominio,
  (case when inv1.Description0 is not null then inv1.User_Name0 else inv2.User_Name0 end) as Usuario,
  (case when inv1.Description0 is not null then inv1.Operating_System_Name_and0 else inv2.Operating_System_Name_and0 end) as SO,
  (case when inv1.Description0 is not null then inv1.InstalledOn else inv2.InstalledOn end) as Fecha,
  (case when inv1.Description0 is not null then inv1.InstalledBy0 else inv2.InstalledBy0 end) as [Instalado por],
  (case when inv1.Description0 is not null then inv1.ServicePackInEffect0 else inv2.ServicePackInEffect0 end) as [Service Pack],
  (case when inv1.Description0 is not null then inv1.Caption0 else inv2.Caption0 end) as Identificador,
  (case when inv1.Description0 is not null then inv1.Description0 else inv2.Description0 end) as Descripción
  from
(
Select distinct sys.Netbios_Name0, sys.User_Domain0, sys.User_Name0, sys.Operating_System_Name_and0,
dbo.ConvertirFecha(qfe.InstalledOn0) as InstalledOn,
qfe.InstalledBy0, qfe.ServicePackInEffect0, qfe.Caption0, qfe.Description0
FROM v_R_System sys
JOIN v_GS_QUICK_FIX_ENGINEERING qfe on sys.ResourceID=qfe.ResourceID
WHERE sys.Netbios_Name0=@pc1
union
Select distinct sys.Netbios_Name0,
 sys.User_Domain0,
 sys.User_Name0,
 sys.Operating_System_Name_and0,
dbo.ConvertirFecha(PSE.AgentInstallDate) as InstalledOn,
'NA' as InstalledBy0,
 'NA' as ServicePackInEffect0,
 PSE.ID as Caption0,
 PSE.Title as Description0
FROM v_R_System sys
JOIN v_GS_PatchStatusEx PSE on sys.ResourceID=PSE.ResourceID
WHERE sys.Netbios_Name0=@pc1
) as inv1
full outer join
(
Select distinct sys.Netbios_Name0, sys.User_Domain0, sys.User_Name0, sys.Operating_System_Name_and0,
dbo.ConvertirFecha(qfe.InstalledOn0) as InstalledOn,
qfe.InstalledBy0, qfe.ServicePackInEffect0, qfe.Caption0, qfe.Description0
FROM v_R_System sys
JOIN v_GS_QUICK_FIX_ENGINEERING qfe on sys.ResourceID=qfe.ResourceID
WHERE sys.Netbios_Name0=@pc2
union
Select distinct sys.Netbios_Name0,
 sys.User_Domain0,
 sys.User_Name0,
 sys.Operating_System_Name_and0,
dbo.ConvertirFecha(PSE.AgentInstallDate) as InstalledOn,
'NA' as InstalledBy0,
 'NA' as ServicePackInEffect0,
 PSE.ID as Caption0,
 PSE.Title as Description0
FROM v_R_System sys
JOIN v_GS_PatchStatusEx PSE on sys.ResourceID=PSE.ResourceID
WHERE sys.Netbios_Name0=@pc2
) as inv2
on inv1.Description0=inv2.Description0
where (inv1.Description0 is NULL) or (inv2.Description0 is NULL)
order by Servidor


Como pueden ver, es bastante extensa, pero no hay que asustarse. Es un gigantesco JOIN de dos "Select" que ya habíamos usado antes. El join es un full outer, es decir compara y obtiene solo los registros que no están en ninguna de las dos subconsultas. Además, utilizando un CASE para cada columna, mostramos solo los valores del equipo que tiene datos, en este caso, el equipo que tiene instalado el update que no se encuentra en el otro equipo.

Los parámetros de la consulta son el nombres de las PCs a comparar (@pc1 y @pc2). Para ambos podemos utilizar esta query:

begin

 if (@__filterwildcard = '')
  SELECT DISTINCT SYS.Netbios_Name0 from v_R_System SYS WHERE SYS.Client0=1 ORDER By SYS.Netbios_Name0
 else
  SELECT DISTINCT SYS.Netbios_Name0 from v_R_System SYS WHERE SYS.Client0=1
  and SYS.Netbios_Name0 like @__filterwildcard
  ORDER By SYS.Netbios_Name0
end

En las subconsultas para obtener las actualizaciones instaladas en cualquier servidor utilice una vez más la función ConvertirFecha, cuya query de creación ya dí en el post anterior.

El resultado del reporte debería verse más o menos así:



Espero que les sirva.
Saludos
Franco

miércoles, 11 de abril de 2012

Actualizaciones instaladas por fecha

Esta es quizás una de las consultas más comunes para quienes utilizan SCCM para el deploy de updates ¿cuándo se instalo X actualización?.
SCCM no está pensado para realizar este tipo de tareas de auditoria o seguimiento. Le alcanza con saber que todas las actualizaciones necesarias están o no instaladas. Pero en muchas organizaciones el proceso de deploy debe ser controlado rigurosamente, y los conflictos con determinados updates (sobre todo de aplicaciones de terceros) hace necesario tener el detalle de los procesos de deploy.

Lamentablemente, Windows ha ido cambiando su criterio para el registro de la instalación de updates según versión del sistema operativo y arquitectura, lo que hace muy complejo realizar este seguimiento de manera precisa y totalmente acertada. Pero podemos tener una aproximación bastante cercana (y chapucera) utilizando este reporte.

if len(@mes)<2 set @mes='0'+@mes
if len(@dia)<2 set @dia='0'+@dia

Select distinct sys.Netbios_Name0,
 sys.User_Domain0, sys.User_Name0, sys.Operating_System_Name_and0,
dbo.ConvertirFecha(qfe.InstalledOn0) as InstalledOn,
qfe.InstalledBy0, qfe.ServicePackInEffect0, qfe.Caption0, qfe.Description0
FROM v_R_System sys
JOIN v_GS_QUICK_FIX_ENGINEERING qfe on sys.ResourceID=qfe.ResourceID
WHERE dbo.ConvertirFecha(qfe.InstalledOn0)=@anio + @mes + @dia

union

Select distinct sys.Netbios_Name0,
 sys.User_Domain0,
 sys.User_Name0,
 sys.Operating_System_Name_and0,
dbo.ConvertirFecha(PSE.AgentInstallDate) as InstalledOn,
'NA' as InstalledBy0,
 'NA' as ServicePackInEffect0,
 PSE.ID as Caption0,
 PSE.Title as Description0
FROM v_R_System sys
JOIN v_GS_PatchStatusEx PSE on sys.ResourceID=PSE.ResourceID
WHERE dbo.ConvertirFecha(PSE.AgentInstallDate)=@anio + @mes + @dia
En este caso, los prompts no requieren query alguna, pero si quieren pueden armarse una sencilla consulta en SQL que le de al usuario los valores de día mes y año que puede utilizar.

Los que hayan revisado con detenimiento la query, habrán notado que hace uso de una función llamada "ConvertirFecha". Es necesario crear esta función en su servidor SQL y en la base del SCCM para que el reporte funcione.


CREATE FUNCTION [dbo].[ConvertirFecha] (@Hexa as nvarchar(max))

RETURNS nvarchar(50)
AS
BEGIN
 DECLARE @Low bigint
 Declare @HighHex varchar(20)
 Declare @LowHex varchar(20)
 DECLARE @High bigint
 Declare @Int64 bigint
 Declare @Retorno nvarchar(50)
 Declare @iSeconds bigint
 Declare @Potencia bigint
 Declare @Dias int
 Declare @Fecha datetime
 
 Declare @Query nvarchar(100)
    Declare @Parameters nvarchar(50)
    Declare @ReturnValue Int
    
    Declare @Anio varchar(4)
 Declare @Mes varchar(2)
 Declare @Dia varchar(2)
 
if len(@Hexa)=16 
Begin

 
 set @HighHex = Substring(@Hexa,1,8) 
 set @High = dbo.ConvertFromBase(@HighHex,16)
 
 set @LowHex = Substring(@Hexa,9,16)
 set @Low = dbo.ConvertFromBase(@LowHex,16)
 
 
 set @Potencia = Power(2.0,32)
 
 set @int64 = @high * @Potencia + @Low
 Set @iSeconds = Floor(@int64 / 10000000)
 Set @Dias = Floor(@iSeconds/86400)-149019
 
 Set @Fecha = DateAdd(d, @Dias, '2009-01-01')
 Set @anio=DatePart(YYYY,@Fecha)
 Set @mes=DatePart(mm,@Fecha)
 Set @dia=DatePart(dd,@Fecha)
 if len(@mes)<2 set @mes='0' + @mes
 if len(@dia)<2 set @dia='0' + @dia

 
 Set @Retorno=@Anio + @mes + @dia
End
Else
Begin
 if isdate(@Hexa)=1
  Begin
   Set @Fecha=cast(@Hexa as DateTime)
   Set @anio=DatePart(YYYY,@Fecha)
   Set @mes=DatePart(mm,@Fecha)
   Set @dia=DatePart(dd,@Fecha)
   if len(@mes)<2 set @mes='0' + @mes
   if len(@dia)<2 set @dia='0' + @dia
   Set @Retorno=@Anio + @mes + @dia  
  End
 Else
  Set @Retorno=NULL
 

End


Return (@Retorno)
END


Con esa query crean la función en su base de SCCM.

Además, necesitan una segunda función (utilizada por la anterior) que se denomina ConvertFromBase. Aquí la query para crearla:

CREATE FUNCTION [dbo].[ConvertFromBase] 

 ( 

     @value AS VARCHAR(MAX), 

     @base AS BIGINT 

 ) RETURNS BIGINT AS BEGIN 

  

     -- just some variables 

     DECLARE @characters CHAR(36), 

             @result BIGINT, 

             @index SMALLINT; 

  

     -- initialize our charater set, our result, and the index 

     SELECT @characters = '0123456789abcdefghijklmnopqrstuvwxyz', 

            @result = 0, 

            @index = 0; 

  

     -- make sure we can make the base conversion.  there can't 

     -- be a base 1, but you could support greater than base 36 

     -- if you add characters to the @charater string 

     IF @base < 2 OR @base > 36 RETURN NULL; 

  

     -- while we have characters to convert, convert them and 

     -- prepend them to the result.  we start on the far right 

     -- and move to the left until we run out of digits.  the 

     -- conversion is the standard (base ^ index) * digit 

  WHILE @index < LEN(@value) 

         SELECT @result = @result + POWER(@base, @index) *  

                          (CHARINDEX 

                             (SUBSTRING(@value, LEN(@value) - @index, 1) 

                             , @characters) - 1 

                          ), 

                @index = @index + 1; 

  

     -- return the result 

     RETURN @result; 

  

 END


La primera página del reporte debería verse más o menos así:



Y el resultado debería ser parecido al siguiente:


No es difícil modificar el reporte para filtrar por servidor, colección o cualquier otro parámetro típico de los reportes de SCCM.

Saludos
Franco

martes, 10 de abril de 2012

Obtener todas las actualizaciones requeridas por los equipos en una colección

Otro reporte personalizado para cubrir una necesidad personal. En este caso, necesitaba saber que actualizaciones son requeridas por todos los servidores dentro de una colección. Combinando los resultados de este reporte con datos como ventanas de mantenimiento y deployments es fácil saber que se va a instalar, cuando y dónde.

La query del reporte es la siguiente:
SELECT Name
      ,[Title]
      ,[Description]
      ,[InfoURL]
  FROM [SMS_SVR].[dbo].[v_UpdateComplianceStatus]
 , [SMS_SVR].[dbo].[v_UpdateInfo]
 , [SMS_SVR].[dbo].[v_FullCollectionMembership]
  Where Status=2
  and v_UpdateComplianceStatus.CI_ID=v_UpdateInfo.CI_ID
  and v_UpdateComplianceStatus.ResourceID=v_FullCollectionMembership.ResourceID
  and CollectionID=@ID
  order by Name

Y la query para el parámetro CollectionID es la siguiente:
begin
  if (@__filterwildcard = '')
   SELECT DISTINCT CollectionID, Name FROM v_Collection ORDER BY Name
  else
   SELECT DISTINCT CollectionID, Name FROM v_Collection
   WHERE CollectionID like @__filterwildcard
   ORDER BY Name
 end

El resultado será algo como esto:


Espero que les sirva.
Saludos.

lunes, 9 de abril de 2012

Reporte para ventanas de mantenimiento periódicas (semanales, mensuales, etc.)

En mi organización tengo colecciones con ventanas de mantenimiento mensuales. Las uso para tirar actualizaciones a todos los servidores pero de manera organizada, haciendo el deploy de los updates a distintos servidores según su criticidad, o en el caso de los clusters o NLB, asegurándome que no se reinicien todos los nodos al mismo tiempo.
Una pregunta común de compañeros, e incluso de mi mismo, es ¿y cuándo le tocan actualizaciones a X servidor? o ¿cuándo fue la última vez que se le instalaron actualizaciones a X servidor?

Para poder responder esa pregunta rápidamente me he armado un reporte que, tras pedirnos el nombre del equipo en cuestión, nos informa la última y la próxima ventana de mantenimiento que le corresponde.

Seguramente se puede hacer mejor, pero a mí me funciona así:

La query del reporte es esta:

Declare @Primero as datetime
Declare @Segundo as datetime
Declare @Tercero as datetime
Declare @Cuarto as datetime

Declare @ultPrimero as datetime
Declare @ultSegundo as datetime
Declare @ultTercero as datetime
Declare @ultCuarto as datetime

set @Primero=dateadd(dd,-1,DATEADD(wk, DATEDIFF(wk,0,dateadd(dd,7-datepart(day,getdate()),getdate())), 0))
set @Primero=(
case
 when @Primero<getdate() then
  dateadd(dd,-1,DATEADD(wk, DATEDIFF(wk,0,dateadd(dd,7-datepart(day,dateadd(mm,+1,getdate())),dateadd(mm,+1,getdate()))), 0))
 else @Primero
end)

set @Segundo=dateadd(dd,-1,DATEADD(wk, DATEDIFF(wk,0,dateadd(dd,7-datepart(day,getdate()),getdate())), 0))+7
set @Segundo=(
case
 when @Segundo<getdate() then
  dateadd(dd,-1,DATEADD(wk, DATEDIFF(wk,0,dateadd(dd,7-datepart(day,dateadd(mm,+1,getdate())),dateadd(mm,+1,getdate()))), 0))+7
 else @Segundo
end)

set @Tercero=dateadd(dd,-1,DATEADD(wk, DATEDIFF(wk,0,dateadd(dd,7-datepart(day,getdate()),getdate())), 0))+14
set @Tercero=(
case
 when @Tercero<getdate() then
 dateadd(dd,-1,DATEADD(wk, DATEDIFF(wk,0,dateadd(dd,7-datepart(day,dateadd(mm,+1,getdate())),dateadd(mm,+1,getdate()))), 0))+14
 else @Tercero
end)

set @Cuarto=dateadd(dd,-1,DATEADD(wk, DATEDIFF(wk,0,dateadd(dd,7-datepart(day,getdate()),getdate())), 0))+21
set @Cuarto=(
case
 when @Cuarto<getdate() then 
 dateadd(dd,-1,DATEADD(wk, DATEDIFF(wk,0,dateadd(dd,7-datepart(day,dateadd(mm,+1,getdate())),dateadd(mm,+1,getdate()))), 0))+21
 else @Cuarto
end)

set @ultPrimero=dateadd(dd,-1,DATEADD(wk, DATEDIFF(wk,0,dateadd(dd,7-datepart(day,getdate()),getdate())), 0))
set @ultPrimero=(
case
 when @ultPrimero>getdate() then
 dateadd(dd,-1,DATEADD(wk, DATEDIFF(wk,0,dateadd(dd,7-datepart(day,dateadd(mm,-1,getdate())),dateadd(mm,-1,getdate()))), 0))
 else @ultPrimero
end)

set @ultSegundo=dateadd(dd,-1,DATEADD(wk, DATEDIFF(wk,0,dateadd(dd,7-datepart(day,getdate()),getdate())), 0))+7
set @ultSegundo=(
case
 when @ultSegundo>getdate() then
 dateadd(dd,-1,DATEADD(wk, DATEDIFF(wk,0,dateadd(dd,7-datepart(day,dateadd(mm,-1,getdate())),dateadd(mm,-1,getdate()))), 0))+7
 else @ultSegundo
end)

set @ultTercero=dateadd(dd,-1,DATEADD(wk, DATEDIFF(wk,0,dateadd(dd,7-datepart(day,getdate()),getdate())), 0))+14
set @ultTercero=(
case
 when @ultTercero>getdate() then
 dateadd(dd,-1,DATEADD(wk, DATEDIFF(wk,0,dateadd(dd,7-datepart(day,dateadd(mm,-1,getdate())),dateadd(mm,-1,getdate()))), 0))+14
 else @ultTercero
end)

set @ultCuarto=dateadd(dd,-1,DATEADD(wk, DATEDIFF(wk,0,dateadd(dd,7-datepart(day,getdate()),getdate())), 0))+21
set @ultCuarto=(
case
 when @ultCuarto>getdate() then
 dateadd(dd,-1,DATEADD(wk, DATEDIFF(wk,0,dateadd(dd,7-datepart(day,dateadd(mm,-1,getdate())),dateadd(mm,-1,getdate()))), 0))+21
 else @ultCuarto
end)

SELECT dbo.v_R_System.Name0,
v_Collection.Name as 'Colección',
sw.Name AS [Ventana], 
 sw.Description,
  sw.Duration AS 'Duración en minutos',
  sw.IsEnabled AS 'Habilitada',
CASE substring(Description, 12,5)
         WHEN 'First' THEN @Primero
 WHEN 'Secon' THEN @Segundo
         WHEN 'Third' THEN @Tercero
         WHEN 'Fourt' THEN @Cuarto         
      END as [Próxima ejecución],
CASE substring(Description, 12,5)
         WHEN 'First' THEN @ultPrimero
 WHEN 'Secon' THEN @ultSegundo
         WHEN 'Third' THEN @ultTercero
         WHEN 'Fourt' THEN @ultCuarto         
      END as [Última ejecución] 
FROM         
 dbo.v_ServiceWindow AS sw INNER JOIN
    dbo.v_FullCollectionMembership AS fcm ON 
 sw.CollectionID = fcm.CollectionID INNER JOIN 
 v_Collection ON sw.CollectionID=v_Collection.CollectionID INNER JOIN
 dbo.v_R_System ON fcm.ResourceID = dbo.v_R_System.ResourceID 
WHERE (dbo.v_R_System.Operating_System_Name_and0 LIKE '%server%')
 and dbo.v_R_System.Name0=@servidor


La query para el prompt "@servidor" es:
begin
 if (@__filterwildcard = '')
  SELECT DISTINCT SYS.Netbios_Name0 from v_R_System SYS where SYS.Client0=1 and isnull(SYS.Obsolete0, 0) = 0 ORDER By SYS.Netbios_Name0
else
  SELECT DISTINCT SYS.Netbios_Name0 from v_R_System SYS
WHERE SYS.Netbios_Name0 like @__filterwildcard
  and SYS.Client0=1 and isnull(SYS.Obsolete0, 0) = 0
  ORDER By SYS.Netbios_Name0
end


El resultado del reporte se ve así:

Bienvenida

Hace ya tres años que estoy hasta el cuello con el SCCM (antes con el SMS) y por si mañana llega el apocalipsis zombie y mi cerebro termina siendo el temtempie de algún extra sobremaquillado, he decidido dejar asentadas aquí algunas de las cosas que voy haciendo con esta herramienta de Microsoft.

Saludos