Wednesday, December 10, 2008

Config Manager Usability Study

Microsoft is conducting a usability study for the next version of SCCM.  If your in the Redmond area and available Jan 12-23rd you can participate.  To join send an email to itusable@microsoft.com.  Put ConfigMgr in the subject line: Source

Monday, December 1, 2008

Client Health Check Script- Take 2

In final testing of the Client Health Check Script, I found that ccmsetup doesn't finish running the install during machine startup on any XP machine that I tested.  I spent over 16 hours trying to figure out why before going with an alternative solution.  Instead of running ccmsetup during machine startup, I now create a system scheduled task to run ccmsetup 5 minutes in the future.  Since it's a one time task it cleans itself up.  Here is the modified script:

Attached: Download

'==========================================================================
' VBScript Source File -- Created with SAPIEN Technologies PrimalScript 4.0
'
' NAME: SCCM_Client_Health_Check.vbs
' AUTHOR: Bill Phillips , ESRI
' DATE : 10/20/2008
'
' COMMENT: Code rewrite for SCCM client taken from 1E ClientHealth Script for SMS client
' Run sccm_client_health_check.vbs /smsserver:smsserver /email:1stemail@company.com /email1:2ndemail@company.com
'==========================================================================
On Error Resume Next

Dim smsserver, platform, email
Dim domainrole, ComSpec, diffdate, enddate, fso, filedate, SmsClient, returncode, Results
Dim ISmsClient, DiscoveredSite, AssignedSite
Dim servicename, startdate, strMessage, strSMSPolEval, windir, wmi, colItems, wShShell, Compname, present, objShare
Dim BitsVersion, tempdir, logfile, logsize

Set WshShell = WScript.CreateObject("WScript.Shell")
WinDir = WshShell.ExpandEnvironmentStrings("%windir%")
Compname = WshShell.ExpandEnvironmentStrings("%COMPUTERNAME%")
ComSpec = WshShell.ExpandEnvironmentStrings("%COMSPEC%")
tempdir = WshShell.ExpandEnvironmentStrings("%temp%")

'Set up the loggong
Set fso = CreateObject("Scripting.FileSystemObject")
Set logfile = fso.OpenTextFile(tempdir & "\SCCM_Client_Health_Check.Log",2,True)



logfile.writeline "####################################"
logfile.writeline "Begining SCCM Client Health Check Script"
logfile.writeline "####################################"

'********************************************************HARD CODED COMMAND LINE

OVERRIDES********************************************************
'***********************************************************UNCOMMENT ONLY IF

NECESSARY***********************************************************
'smsserver = "smsserver" 'Should reflect the PMP/PDP for each office
'platform = "" 'No need to modify
'Email = "wphillips@company.com" 'Should be set to go to an alias that includes the needed peoeple.
'********************************************************HARD CODED COMMAND LINE

OVERRIDES********************************************************
'***********************************************************UNCOMMENT ONLY IF

NECESSARY***********************************************************
checkSCCMserverCMD()
checkPlatformCMD()
checkEmailCMD()
checkEmail1CMD()
checkAdminShare()
checkCCMSetupRunning()
checkClient()
checkLogsUpdate()
checkBITSversion()
checkServices()
checkAssignment()
logfile.writeline "Cleaning Up"
Call Cleanup
logfile.writeline "Ending Processing"
WScript.Quit


'Check to see if SCCM server is specifed as an argument or hardcoded into script
Function checkSCCMserverCMD()
On Error Resume Next
If smsserver = "" Then
If Wscript.Arguments.Named.Exists("smsserver") Then
If Wscript.Arguments.Named("smsserver") <> "" Then
logfile.writeline "smsserver specified in command line is " & WScript.Arguments.Named("smsserver")
Else
logfile.writeline "/smsserver parameter is the incorrect format. Please see documentation"
WScript.Quit
End If
Else
logfile.writeline "Missing /smsserver: in command line"
WScript.Quit
End If
Else
logfile.WriteLine "The SCCMserver hardcoded command line override specified as = " & smsserver
End If
End Function

Function checkPlatformCMD()
On Error Resume Next
logfile.writeline "CheckingPlatformCMD"
If platform = "" Then
domainrole = GetDomainRole()
If Wscript.Arguments.Named.Exists("platform") Then
If Wscript.Arguments.Named("platform") <> "" Then
platform = Wscript.Arguments.Named("platform")
logfile.WriteLine "platform = " & platform
If Not CInt(platform) = CInt(domainrole) Then
logfile.WriteLine "System running is not the correct platform as specified"
WScript.Quit
End If
End If
End If
Else
logfile.WriteLine "platform hardcoded command line override specified as = " & platform
domainrole = GetDomainRole()
If Not CInt(Platform) = CInt(domainrole) Then
logfile.WriteLine "System running is not the correct platform as specified"
WScript.Quit
End If
End If
End Function

Function checkEmailCMD()
On Error Resume Next
logfile.WriteLine "Inside checkEmailCMD"
If email = "" Then
If WScript.Arguments.Named.Exists("email") Then
email = True
logfile.WriteLine ("Email command line argument specified as = " & WScript.Arguments.Named("email"))
End If
Else
logfile.WriteLine ("Email hardcoded command line override specified as = " & email)
email = True
End If
End Function
Function checkEmail1CMD()
On Error Resume Next
logfile.WriteLine "Inside checkEmail1CMD"
If email1 = "" Then
If WScript.Arguments.Named.Exists("email1") Then
email1 = True
logfile.WriteLine ("Email1 command line argument specified as = " & WScript.Arguments.Named("email1"))
End If
Else
logfile.WriteLine ("Additional email hardcoded command line override specified as = " & email)
email1 = True
End If
End Function


Function checkAdminShare()
On Error Resume Next
logfile.WriteLine "Inside checkAdminShare"
'Check for Admin$ - If not present then log
Set wmi = getobject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
Set colItems = wmi.ExecQuery("Select * From Win32_Share",,48)
present = "FALSE"
For Each objShare In colItems
If LCASE(objShare.Name) = "admin$" Then
present = "TRUE"
End If
Next
If present <> "TRUE" Then
If Email = True Then
logfile.writeline "Sending email that Admin$ is missing"
Call EmailMessage("Admin$ Missing", Compname & " does not have an Admin$.")
Else
logfile.writeline "Admin$ is missing."
End If
End If
End Function

Function checkCCMSetupRunning()
On Error Resume Next
logfile.writeline "Inside checkCCMSetupRunning"
'Abort if ccmsetup running
Results = ServiceState("ccmsetup")
If LCase(Results) = LCase("Running")Then
logfile.writeline "Sending email that ccmsetup service is running and script is aborting"
Call EmailMessage("Aborting Client Installation", "ccmsetup is running on " & CompName)
WScript.Quit

logfile.writeline "Exiting script processing because ccmsetup service is running"
WScript.Quit

Elseif LCase(Results) = LCase("Stopped")Then
logfile.WriteLine "ccmsetup service is in a stopped state, attempting to start"
KickService("ccmsetup")
End If
End Function

Function checkBITSrunning()
On Error Resume Next
logfile.writeline "Inside checkBITSRunning"
'Start BITS running
Results = ServiceState("BITS")
If LCase(Results) = LCase("Running")Then
logfile.writeline "BITS is running"

Else
logfile.WriteLine "BITS service is in a stopped state, attempting to start"
KickService("BITS")
End If
End Function

Function checkClient()
On Error Resume Next
logfile.writeline "Inside checkClient"
' SMS Client COM object available, Version Installed, & WMI Namespace available
Set SmsClient = GetObject("winmgmts:ROOT/CCM:SMS_Client=@")
If Err Then
'Advanced client not installed
logfile.writeline "Advanced Client not installed, calling AdvCliInst to install the client"
Call AdvCliInst(ComSpec)
WScript.Sleep 1000
Call Cleanup
WScript.Quit
Else
logfile.writeline SmsClient.ClientVersion
Select Case SmsClient.ClientVersion
'IMPORTANT! >>>>>>>>> Adjust CASE as necessary, but do *not* remove it! <<<<<<<<IMPORTANT!
'Alter this by adding an additional CASE statement followed by the version in quotes for each SMS client
'version which is allowed in the hierarchy. This can also be used as an additional cleanup method after
'upgrading clients for those that might have missed this via software distribution
'Case "2.50.3174.1018"
'Case "2.50.4160.2000" 'SP2 Version
Case "4.00.6221.1000" 'configMGR sp1 client
logfile.writeline "SMS Client Version Passed"
WScript.Sleep 1
Case Else
logfile.writeline "Calling AdvCliInst routine to install SMS Advanced client"
Call AdvCliInst(ComSpec)
WScript.Sleep 1000
Call Cleanup
WScript.Quit
End Select
End If
End Function

Function checkLogsUpdate()
On Error Resume Next
Set SmsClient = GetObject("winmgmts://./root/ccm:SMS_Client")
' SMS Logs recently updated
logfile.writeline "Begining to evaluate " & windir & "\system32\CCM\Logs\PolicyEvaluator.log"
strSMSPolEval = windir & "\system32\CCM\Logs\PolicyEvaluator.log"
startdate = ShowFileAccessInfo(strSMSPolEval, Compname)
logfile.writeline "startdate = " & startdate
enddate = date()
logfile.writeline "enddate = " & enddate

If isDate(startdate) Then
diffdate = DateDiff("d", startdate, enddate)
logfile.writeline "diffdate = " & diffdate
End If

If diffdate > 21 Then
If Email = True Then
logfile.writeline "diffdate is greater than 21 days, sending email"
Call EmailMessage("Log files are out of date", Compname & " has not updated is logs in 21 days or more - attempting client repair")
End If
logfile.writeline "diffdate is greater than 21 days, attempting to repair SMS Client"
smsClient.RepairClient
wscript.quit
End If
End Function

Function checkBITSversion()
On Error Resume Next
'Check BITS version, email if out of date
logfile.writeline "Checking BITS version by looking at " & windir & "\system32\QMgr.dll"
If fso.FileExists(windir & "\system32\QMgr.dll") Then
BitsVersion = fso.GetFileVersion(windir & "\system32\QMgr.dll")
logfile.writeline "BitsVersion is " & BitsVersion
Select Case BitsVersion

' Case for Windows 2000 Server and Pro
Case "6.6.2600.1596"
logfile.writeline "BitsVersion Passed"
WScript.Sleep 1

' Case for Windows XP SP2
Case "6.7.2600.3143"
logfile.writeline "BitsVersion Passed"
WScript.Sleep 1

' Case for Windows XP SP3
Case "6.7.2600.5512"
logfile.writeline "BitsVersion Passed"
WScript.Sleep 1

' Case for Server 2003 SP1
Case "6.6.3790.1830"
logfile.writeline "BitsVersion Passed"
WScript.Sleep 1

' Case for Server 2003 SP2
Case "6.6.3790.3959"
logfile.writeline "BitsVersion Passed"
WScript.Sleep 1

' Case for Vista
Case "7.0.6000.16386"
logfile.writeline "BitsVersion Passed"
WScript.Sleep 1

' Case for Server 2008 SP1
Case "7.0.6001.18000"
logfile.writeline "BitsVersion Passed"
WScript.Sleep 1


' Case for failure
Case Else
If Email = True Then
logfile.writeline "BITS is out of date, sending email"
Call EmailMessage("BITS out of date", Compname & " - BITS version is at " & BitsVersion)
Else
logfile.writeline "BITS is out of date, exiting script processing"
WScript.Quit
End If
End Select
Else
If Email = True Then
logfile.writeline "Unable to process BITS version because " & windir & "\system32\QMgr.dll is missing. Sending email."
Call EmailMessage("File Missing", "%system32%\QMgr.dll" & " is missing on " & Compname)
Else
logfile.writeline "Unable to process BITS version because " & windir & "\system32\QMgr.dll is missing. Exiting Script processing."
WScript.Quit
End If
End If
End Function

Function checkServices()
On Error Resume Next
' SMS Agent Host Service started
logfile.writeline "Calling KickService"
Call KickService("CcmExec")

' Remote Registry Service started
logfile.writeline "Calling RemoteRegistry"
Call KickService("RemoteRegistry")
End Function

Function checkAssignment()
On Error Resume Next
'Ensure that the client is assigned to a site if its not assigned to any
logfile.writeline "Checking to make sure SMS Client has site assignment"
Set ISmsClient = CreateObject ("Microsoft.SMS.Client")
AssignedSite = ISmsClient.GetAssignedSite
If NOT Len(AssignedSite & "")>0 Then
logfile.writeline "Client is not assigned, attempting to AutoDiscover and set"
ISmsClient.EnableAutoAssignment 1
DiscoveredSite = ISmsClient.AutoDiscoverSite
ISmsClient.SetAssignedSite DiscoveredSite,0
logfile.writeline "Client is now assigned to " & ISmsClient.GetAssignedSite
End If
logfile.writeline "Client is already assigned to " & ISmsClient.GetAssignedSite
End Function




' =====================================================
' KickService function
' =====================================================
Function KickService(servicename)
On Error Resume Next
logfile.writeline "Inside KickService"
Dim Results, wmi, Service, returncode, Service2, Started
Results = ServiceState(servicename)
logfile.writeline "servicename = " & servicename
logfile.writeline "Results = " & Results
set wmi = getobject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")

If NOT LCase(Results) = LCase("Running")Then
set Results = wmi.execquery("select state from win32_service where name='" & servicename & "'")
For Each Service In Results
' Start service
returncode = Service.StartService
logfile.writeline "returncode = " & returncode
if returncode <> 0 Then
If Email = True Then
logfile.writeline "SMS Client Service Failure " & servicename & " failed to start on " & CompName
Call EmailMessage("Start Service Error", "SMS Client Service Failure " & servicename & " failed to start on " & CompName)
Call Cleanup
logfile.writeline "Quiting Script"
WScript.Quit
Else
logfile.writeline "Displaying message to user - Error starting service your Windows Management Service (" & servicename & ") -

Call The Help Desk immediately"

msgbox "Error starting service your Windows Management Service (" & servicename & ") - Call The Help Desk immediately"
Call Cleanup
logfile.writeline "Quiting Script"
WScript.Quit
End If
end If
Do Until Started = True
'IMPORTANT! >>>>>>>>> Adjust sleep as necessary, but do *not* remove it! <<<<<<<<IMPORTANT!
logfile.writeline "Sleeping for 2 seconds..."
logfile.writeline "Use the below text to see how many times the script looped to start the Service"
WScript.Sleep 2000 'Sleep for 2 Seconds
set Results = wmi.execquery("select state from win32_service where name='" & servicename & "'")
for each Service2 In Results
if lcase(Service2.State) = lcase("Running") Then
logfile.writeline "Started = " & Started
Started = True
end If
Next
Loop
Next
End If
End Function
' =====================================================
' ServiceState subprocedure
' =====================================================
Function ServiceState(servicename)
On Error Resume Next
logfile.writeline "Inside ServiceState"
logfile.WriteLine "Checking " & servicename & " service"
Dim wmi, Results, Service, StateResults, StartMode
set wmi = getobject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
set Results = wmi.execquery("select state from win32_service where name='" & servicename & "'")
For Each Service In Results
StateResults = Service.State
logfile.writeline "StateResults = " & StateResults
Next
ServiceState = StateResults
End Function
' =====================================================
' AdvCliInst subprocedure
' =====================================================
Function AdvCliInst(ComSpec)
On Error Resume Next
logfile.writeline "Inside AdvCliInst"
logfile.WriteLine "Calling Bits Check"
objCurTime = Time()
objCurHour = Hour(objCurTime)
objCurMin = Minute(objCurTime)+5
objInstallTime = objCurHour & ":" & objCurMin
Call checkBITSrunning()
Dim smsinstall, WshShell, InstallArgs
Set WshShell = WScript.CreateObject("WScript.Shell")
ComSpec = WshShell.ExpandEnvironmentStrings("%COMSPEC%")
If ComSpec = "" Then
If Email = True Then
logfile.writeline "SMS Client Installation Failure", "The SMS Client failed to Install On " & CompName
Call EmailMessage("SMS Client Installation Failure", "The SMS Client failed to Install On " & CompName)
Call Cleanup
logfile.writeline "Exiting Script Processing"
WScript.Quit
Else
logfile.writeline "Displaying Message to user - Windows Management Service Installation Failed. Please contact The Help Desk"
MsgBox "Windows Management Service Installation Failed. Please contact The Help Desk"
Call Cleanup
logfile.writeline "Exiting Script Processing"
WScript.Quit
End If
Else
InstallArgs = ""
If Wscript.Arguments.Named.Exists("params") Then
If Wscript.Arguments.Named("params") <> "" Then
InstallArgs = Wscript.Arguments.Named("params")
logfile.writeline = "InstallArgs = " & InstallArgs
End If
End If
If Exist (Wscript.Arguments.Named("smsserver") & "\SMSClient\ccmsetup.exe") Then
'smsinstall = ComSpec & " /c \\" & Wscript.Arguments.Named("smsserver") & "\SMSClient\ccmsetup.exe" & InstallArgs
smsinstall = "at " & objInstallTime & " \\" & Wscript.Arguments.Named("smsserver") & "\SMSClient\ccmsetup.exe" & InstallArgs
logfile.writeline "Calling SCCM Client installation with below command line:"
logfile.writeline "smsinstall = " & smsinstall
' Run SMS Client Installation
Call EmailMessage("SMS Install", Compname & " -Installing SCCM Client as " & smsinstall)
WshShell.Run smsinstall,0,False
Else
logfile.WriteLine "Cannot Find SCCM client installer"
Call EmailMessage("SMS Installer Missing", Compname & " -" & Wscript.Arguments.Named("smsserver") & "\SMSClient\ccmsetup.exe")
End If

End If
End Function
' =====================================================
' GetDomainRole function
' =====================================================
Function GetDomainRole
On Error Resume Next
logfile.writeline "Inside GetDomainRole"
Dim domainroles, wmi, domainrole
Set wmi = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
Set domainroles = wmi.ExecQuery("SELECT DomainRole FROM Win32_ComputerSystem")
For Each domainrole in domainroles
GetDomainRole = domainrole.DomainRole
logfile.writeline "GetDomainRole = " & GetDomainRole
Next
Set domainroles = Nothing
Set wmi = Nothing
End Function
' =====================================================
' EmailMessage subprocedure
' =====================================================
Function EmailMessage(Subject, Body)
On Error Resume Next
logfile.writeline "Inside EmailMessage"
logfile.writeline "Email Subject: " & Subject
logfile.writeline "Email Body: " & Body
Dim objEmail, objemailfrom
' email using a generic user account as system is being booted up and user may not have logged on yet
Set objEmail = CreateObject("CDO.Message")
objemailfrom = WScript.Arguments.Named("smsserver") & "@company.com"
objEmail.From = objemailfrom
objEmail.To = WScript.Arguments.Named("email")
objEmail.CC = WScript.Arguments.Named("email1")
' objEmail.To = "smsadmins@company.com"
objEmail.Subject = Subject
objEmail.Textbody = Body
objEmail.Configuration.Fields.Item _
("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
objEmail.Configuration.Fields.Item _
("http://schemas.microsoft.com/cdo/configuration/smtpserver") = _
"smtp.company.com"
objEmail.Configuration.Fields.Item _
("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 25
objEmail.Configuration.Fields.Update
logfile.writeline "Sending Email"
objEmail.Send
Set objEmail = Nothing
End Function
' =====================================================
' ShowFileAccessInfo function
' =====================================================
Function ShowFileAccessInfo(filespec, Compname)
On Error Resume Next
logfile.writeline "Inside ShowFileAccessInfo"
Dim fso, f, filespec_date, FSpace
Set fso = CreateObject("Scripting.FileSystemObject")
If fso.FileExists(filespec) Then
Set f = fso.GetFile(filespec)
logfile.writeline "f = " & f
filespec_date = f.DateLastModified
logfile.writeline "filespec_date = " & filespec_date
FSpace = Instr(filespec_date," ") - 1
logfile.writeline "FSpace = " & FSpace
ShowFileAccessInfo = Left(filespec_date,FSpace)
logfile.writeline "ShowFileAccessInfo = " & ShowFileAccessInfo
Else
If Email = True Then
logfile.writeline "File Missing - " & filespec & " is missing On " & Compname
Call EmailMessage("File Missing", filespec & " is missing On " & Compname)
logfile.writeline "Exiting Script Processing"
WScript.Quit
End If
End If
End Function
' =====================================================
' Destroy any objects
' =====================================================
Sub Cleanup
On Error Resume Next
logfile.writeline "Inside Cleanup"
Set WshShell = Nothing
Set ComSpec = Nothing
Set windir = Nothing
Set strCompName = Nothing
Set SmsClient = Nothing
End Sub
' =====================================================

Friday, November 7, 2008

Interactive Services Detection Service

Yesterday another admin notified me that his Vista machine prompted him before showing a message from a program I had distributed via SCCM.  The name of the prompt was "Interactive Services Dialog Detection", and it was requesting to show him a message.

image001

Selecting "Show me the message" hides the desktop and allows the user to see the message.  After viewing the message, the user had to click on "Return Now" to get back to the desktop.

This event was a surprise to me.  I've been running Vista since it came out and the program we advertised has been advertised for the last 3 years, and we run it every month.  After researching this service, I discovered that it is a new security feature in Vista.  Designed to protect users by intercepting messages run under another security context.  In this case, the program is advertised to run whether or not a user is logged on, but allow users to interact with the program.  When these options are chosen in SCCM, the program runs under the machine account as a service.  Since this program pops up a message, we chose the option to allow users to interact so they can see the message and click okay on it.  Vista may see this as a security risk if the Interactive Services Detection service is running.  We checked 4 different Vista machines, and it appears the default behavior is the service is Stopped and set to Manual.   UAC did not appear to have any affect on this service.  Apparently this admin had done something else to set this service to Running.  There are several available ways to resolve this issue:

  • Choose the option to run the program only when a user is logged on, and run it as the user.
  • Don't make the program interactive (user will not see the program).
  • Stop the Interactive Services Detection service.

Ignore it.  Vista is doing it's job of being safer.  Users may have to click an extra prompt.

I don't really recommend stopping or disabling the service since that is a built in security feature of Vista.  But it will prevent the prompt from appearing if desired.

Wednesday, November 5, 2008

Organize Collections with Drag and Drop

One of the plans we've had since I took over the administration of Configuration Manager is to organize the objects in the admin console.  SCCM makes it easier than SMS since it supports drag and drop.  The catch is that it only supports it with certain types of objects.  Collections is one of the object types that doesn't support drag and drop.  The only way I found to reorganize collection objects was to make a link of a collection to a second collection.  Making this link actually makes a 2nd instance of the collection appear in the hierarchy.  Then you can go back and safely delete the original collection.  The other option is to use a Microsoft provided SMS tool called CollTree.  Yes, the program does work with SCCM.  After downloading the SMS 2003 SDK you can compile the executable from \Program Files\Microsoft Systems Management Server 2003 SDK V3\Samples\VB\CollTree\CollTree.vbp  It's not as great as if Microsoft had built this functionality into the Admin Console, but at least it works!  Download a compiled version of CollTree here: http://myitforum.com/cs2/blogs/bleary/attachment/68439.ashx

Credits to Brian Leary for pointing out this useful tool: http://myitforum.com/cs2/blogs/bleary/archive/2006/12/01/colltree-drag-and-drop-collections-for-sms-2003.aspx

Thursday, October 23, 2008

Upgrade BITS

As companies move from SMS 2003 to SCCM one of the items frequently mentioned is the desire to predeploy the latest version of BITS (Background Intelligent Transfer Service).  This makes it possible to deploy the client without requiring a reboot.  However, Microsoft didn't really provide a method for doing the BITS deployment.  So I wrote my own.  Download the latest versions of BITS from: 2000, XP, 2003, x64- XP and 2003

They will need to be in a subdirectory named bin of this script.  The script will require a drive letter to run, so when you create the program be sure to specify that option.  The nice thing about this script is that you can deploy to everybody, and it will only upgrade those that need it.

'==========================================================================
' VBScript Source File -- Created with SAPIEN Technologies PrimalScript 4.0
'
' NAME: Install_Bits.vbs
' AUTHOR: Bill Phillips , ESRI
' DATE : 10/22/2008
'
' COMMENT:
'==========================================================================
On Error Resume Next
Dim strOSName, strSPName, strComputerType, systemroot, detectfile
Dim objFile, strFileVersion, strInstallFile


Set objShell = CreateObject ("Wscript.Shell")
set objEnv = objShell.Environment("Process")
systemroot = objEnv("SYSTEMROOT")
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objExp = new regexp 'Create the RegExp object


detectOS()
detectFileversion(systemroot & "\system32\QMgr.dll")
compareBITSVersion(strFileVersion)
installBITS()

' ********************************************************************************
'Detect OS Function
'********************************************************************************
Function detectOS()
For Each objOS In GetObject("winmgmts:").InstancesOf ("Win32_OperatingSystem")
strOSName = objOS.Caption
strSPName = "SP" & objOS.ServicePackMajorVersion
Next

For Each objComputer In GetObject("winmgmts:").InstancesOf ("Win32_ComputerSystem")
strComputerType = objComputer.systemtype
Next

'need to normalize data (too many different versions of windows)
objExp.Pattern = "2000"
If objEXP.Test (strOSName) Then
strOSName = "win2k"
End If

objExp.Pattern = "XP"
If objEXP.Test (strOSName) Then
strOSName = "winXP"
End If

objExp.Pattern = "2003"
If objEXP.Test (strOSName) Then
strOSName = "w2k3"
End If

objExp.Pattern = "Vista"
If objEXP.Test (strOSName) Then
strOSName = "Vista"
End If

objExp.Pattern = "2008"
If objEXP.Test (strOSName) Then
strOSName = "w2k8"
End If
End Function

Function detectFileversion(detectfile)
If objFSO.FileExists(detectfile) Then
Set objFile = objFSO.GetFile(detectfile)
strFileVersion = objFSO.GetFileVersion(detectfile)
End If

End Function

Function compareBITSVersion(strFileVersion)
Select Case strFileVersion
' Case for Windows 2000 Server and Pro
Case "6.6.2600.1596"
'wscript.echo "BitsVersion Passed"
WScript.quit
WScript.Sleep 1

' Case for Windows XP SP2
'Case "6.7.2600.3143"
Case "Fake"
'wscript.echo "BitsVersion Passed"
WScript.quit
WScript.Sleep 1

' Case for Windows XP SP3
Case "6.7.2600.5512"
'wscript.echo "BitsVersion Passed"
WScript.quit
WScript.Sleep 1

' Case for Server 2003 SP1
Case "6.6.3790.1830"
'wscript.echo "BitsVersion Passed"
WScript.quit
WScript.Sleep 1

' Case for Server 2003 SP2
Case "6.6.3790.3959"
'wscript.echo "BitsVersion Passed"
WScript.quit
WScript.Sleep 1

' Case for Vista RTM
Case "7.0.6000.16386"
'wscript.echo "BitsVersion Passed"
WScript.quit
WScript.Sleep 1

' Case for Vista and Server 2008 SP1 x32 & x64
'Case "7.0.6001.18000"
Case "fake"
'wscript.echo "BitsVersion Passed"
WScript.quit
WScript.Sleep 1
' Case for failure
Case Else
'WScript.Echo "Bits Failed"
selectBITSinstall()
End Select
End Function

Function selectBITSinstall()
Select Case strComputerType
Case "x64-based PC"
Select Case strOSName
Case "w2k3"
strInstallFile = "WindowsServer2003.WindowsXP-KB923845-x64-ENU.exe"
Case "winXP"
strInstallFile = "WindowsServer2003.WindowsXP-KB923845-x64-ENU.exe"
End Select
Case "X86-based PC"
Select Case strOSName
Case "win2k"
strInstallFile = "Windows2000-KB842773-x86-ENU.exe"
Case "winXP"
strInstallFile = "WindowsXP-KB923845-x86-ENU.exe"
Case "w2k3"
strInstallFile = "WindowsServer2003-KB923845-x86-ENU.exe"
End Select
End Select

' no match found so quitting
If strInstallFile = "" Then
WScript.Quit
End If

End Function

Function installBITS()
'Turn off prompt for unknown locations
objEnv("SEE_MASK_NOZONECHECKS") = 1


objShell.Run ("bin\" & strInstallFile & " /passive /norestart /overwriteoem")', 1, True

'Turn prompt back On
objEnv.Remove("SEE_MASK_NOZONECHECKS")
End Function

Wednesday, October 22, 2008

Client Health Check Script

See updated post: http://smsimpossible.blogspot.com/2008/12/client-health-check-script-take-2.html

1e used to have a client health check script for sms 2003. The script would reinstall the client or force a repair depending on what part of the check it failed. Since the release of SCCM they have not updated the script. I believe the old script will probably still work just by changing the install directory, but I have a rewritten version of the script that we use so I'm providing it below. I don't think there is anything wrong with posting this, but I will remove it if 1e asks me to do so. To use the script you must specify the siteserver. Also the sccm/client directory must be shared out and the script will need to be modified to reflect the share directory. After meeting these 2 requirements the script is usable. You may also specify an smtp email server in the email section and who the email should be sent to. The siteserver and email recipient may be passed as arguments or hard coded into the script.

'==========================================================================
' VBScript Source File -- Created with SAPIEN Technologies PrimalScript 4.0
' NAME: SCCM_Client_Health_Check.vbs
' AUTHOR: Bill Phillips , ESRI
' DATE : 10/20/2008
'
' COMMENT: Code rewrite for SCCM client taken from 1E ClientHealth Script for SMS client
'==========================================================================
On Error Resume Next

Dim smsserver, platform, email
Dim domainrole, ComSpec, diffdate, enddate, fso, filedate, SmsClient, returncode, Results
Dim ISmsClient, DiscoveredSite, AssignedSite
Dim servicename, startdate, strMessage, strSMSPolEval, windir, wmi, colItems, wShShell, Compname, present, objShare
Dim BitsVersion, tempdir, logfile, logsize

Set WshShell = WScript.CreateObject("WScript.Shell")
WinDir = WshShell.ExpandEnvironmentStrings("%windir%")
Compname = WshShell.ExpandEnvironmentStrings("%COMPUTERNAME%")
ComSpec = WshShell.ExpandEnvironmentStrings("%COMSPEC%")
tempdir = WshShell.ExpandEnvironmentStrings("%temp%")

'Set up the loggong
Set fso = CreateObject("Scripting.FileSystemObject")
Set logfile = fso.OpenTextFile(tempdir & "\SCCM_Client_Health_Check.Log",2,True)


logfile.writeline "####################################"
logfile.writeline "Begining SCCM Client Health Check Script"
logfile.writeline "####################################"

'Either uncomment variables below or pass arguments to script:
'sccm_client_health_check.vbs /smsserver:smststserver /email:recepient@company.com
'********************************************************HARD CODED COMMAND LINE OVERRIDES********************************************************
'***********************************************************UNCOMMENT ONLY IF NECESSARY***********************************************************
'smsserver = "smststserver" 'Should reflect the PMP/PDP for each office
'platform = "" 'No need to modify
'Email = "recepient@company.com" 'Should be set to go to an alias that includes the needed peoeple.
'********************************************************HARD CODED COMMAND LINE OVERRIDES********************************************************
'***********************************************************UNCOMMENT ONLY IF NECESSARY***********************************************************
checkSCCMserverCMD()
checkPlatformCMD()
checkEmailCMD()
checkAdminShare()
checkCCMSetupRunning()
checkClient()
checkLogsUpdate()
checkBITSversion()
checkServices()
checkAssignment()
logfile.writeline "Cleaning Up"
Call Cleanup
logfile.writeline "Ending Processing"
WScript.Quit


'Check to see if SCCM server is specifed as an argument or hardcoded into script
Function checkSCCMserverCMD()
If smsserver = "" Then
If Wscript.Arguments.Named.Exists("smsserver") Then
If Wscript.Arguments.Named("smsserver") <> "" Then
logfile.writeline "smsserver specified in command line is " & WScript.Arguments.Named("smsserver")
Else
logfile.writeline "/smsserver parameter is the incorrect format. Please see documentation"
WScript.Quit
End If
Else
logfile.writeline "Missing /smsserver: in command line"
WScript.Quit
End If
Else
logfile.WriteLine "The SCCMserver hardcoded command line override specified as = " & smsserver
End If
End Function

Function checkPlatformCMD()
If platform = "" Then
domainrole = GetDomainRole()
If Wscript.Arguments.Named.Exists("platform") Then
If Wscript.Arguments.Named("platform") <> "" Then
platform = Wscript.Arguments.Named("platform")
logfile.WriteLine "platform = " & platform
If Not CInt(platform) = CInt(domainrole) Then
logfile.WriteLine "System running is not the correct platform as specified"
WScript.Quit
End If
End If
End If
Else
logfile.WriteLine "platform hardcoded command line override specified as = " & platform
domainrole = GetDomainRole()
If Not CInt(Platform) = CInt(domainrole) Then
logfile.WriteLine "System running is not the correct platform as specified"
WScript.Quit
End If
End If
End Function

Function checkEmailCMD()
If email = "" Then
If WScript.Arguments.Named.Exists("email") Then
email = True
End If
Else
logfile.WriteLine ("Email hardcoded command line override specified as = " & email)
'email = True 'uncomment if you use hardcoded email in script
End If
End Function

Function checkAdminShare()
'Check for Admin$ - If not present then log
Set wmi = getobject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
Set colItems = wmi.ExecQuery("Select * From Win32_Share",,48)
present = "FALSE"
For Each objShare In colItems
If LCASE(objShare.Name) = "admin$" Then
present = "TRUE"
End If
Next
If present <> "TRUE" Then
If Email = True Then
logfile.writeline "Sending email that Admin$ is missing"
Call EmailMessage("Admin$ Missing", Compname & " does not have an Admin$.")
Else
logfile.writeline "Admin$ is missing."
End If
End If
End Function

Function checkCCMSetupRunning()
'Abort if ccmsetup running
Results = ServiceState("ccmsetup")
If LCase(Results) = LCase("Running")Then
logfile.writeline "Sending email that ccmsetup service is running and script is aborting"
Call EmailMessage("Aborting Client Installation", "ccmsetup is running on " & CompName)
WScript.Quit

logfile.writeline "Exiting script processing because ccmsetup service is running"
WScript.Quit

Elseif LCase(Results) = LCase("Stopped")Then
logfile.WriteLine "ccmsetup service is in a stopped state, attempting to start"
KickService("ccmsetup")
End If
End Function

Function checkClient()
' SMS Client COM object available, Version Installed, & WMI Namespace available
Set SmsClient = GetObject("winmgmts:ROOT/CCM:SMS_Client=@")
If Err Then
'Advanced client not installed
logfile.writeline "Advanced Client not installed, calling AdvCliInst to install the client"
Call AdvCliInst(ComSpec)
WScript.Sleep 10000
Call Cleanup
WScript.Quit
Else
logfile.writeline SmsClient.ClientVersion
Select Case SmsClient.ClientVersion
'IMPORTANT! >>>>>>>>> Adjust CASE as necessary, but do *not* remove it! <<<<<<<<IMPORTANT!
'Alter this by adding an additional CASE statement followed by the version in quotes for each SMS client
'version which is allowed in the hierarchy. This can also be used as an additional cleanup method after
'upgrading clients for those that might have missed this via software distribution
'Case "2.50.3174.1018"
'Case "2.50.4160.2000" 'SP2 Version
Case "4.00.6221.1000" 'configMGR sp1 client
logfile.writeline "SMS Client Version Passed"
WScript.Sleep 1
Case Else
logfile.writeline "Calling AdvCliInst routine to install SMS Advanced client"
Call AdvCliInst(ComSpec)
WScript.Sleep 10000
Call Cleanup
WScript.Quit
End Select
End If
End Function

Function checkLogsUpdate()
Set SmsClient = GetObject("winmgmts://./root/ccm:SMS_Client")
' SMS Logs recently updated
logfile.writeline "Begining to evaluate " & windir & "\system32\CCM\Logs\PolicyEvaluator.log"
strSMSPolEval = windir & "\system32\CCM\Logs\PolicyEvaluator.log"
startdate = ShowFileAccessInfo(strSMSPolEval, Compname)
logfile.writeline "startdate = " & startdate
enddate = date()
logfile.writeline "enddate = " & enddate

If isDate(startdate) Then
diffdate = DateDiff("d", startdate, enddate)
logfile.writeline "diffdate = " & diffdate
End If

If diffdate > 21 Then
If Email = True Then
logfile.writeline "diffdate is greater than 21 days, sending email"
Call EmailMessage(CompName & " is out of date", Compname & " has not updated is logs in 21 days or more - attempting client repair")
End If
logfile.writeline "diffdate is greater than 21 days, attempting to repair SMS Client"
smsClient.RepairClient
wscript.quit
End If
End Function

Function checkBITSversion()

'Check BITS version, email if out of date
logfile.writeline "Checking BITS version by looking at " & windir & "\system32\QMgr.dll"
If fso.FileExists(windir & "\system32\QMgr.dll") Then
BitsVersion = fso.GetFileVersion(windir & "\system32\QMgr.dll")
logfile.writeline "BitsVersion is " & BitsVersion
Select Case BitsVersion

' Case for Windows 2000 Server and Pro
Case "6.6.2600.1596"
logfile.writeline "BitsVersion Passed"
WScript.Sleep 1

' Case for Windows XP SP2
Case "6.7.2600.3143"
logfile.writeline "BitsVersion Passed"
WScript.Sleep 1

' Case for Windows XP SP3
Case "6.7.2600.5512"
logfile.writeline "BitsVersion Passed"
WScript.Sleep 1

' Case for Server 2003 SP1
Case "6.6.3790.1830"
logfile.writeline "BitsVersion Passed"
WScript.Sleep 1

' Case for Server 2003 SP2
Case "6.6.3790.3959"
logfile.writeline "BitsVersion Passed"
WScript.Sleep 1

' Case for Vista
Case "7.0.6000.16386"
logfile.writeline "BitsVersion Passed"
WScript.Sleep 1

' Case for Server 2008 SP1
Case "7.0.6001.18000"
logfile.writeline "BitsVersion Passed"
WScript.Sleep 1


' Case for failure
Case Else
If Email = True Then
logfile.writeline "BITS is out of date, sending email"
Call EmailMessage("BITS out of date", Compname & " - BITS version is at " & BitsVersion)
Else
logfile.writeline "BITS is out of date, exiting script processing"
WScript.Quit
End If
End Select
Else
If Email = True Then
logfile.writeline "Unable to process BITS version because " & windir & "\system32\QMgr.dll is missing. Sending email."
Call EmailMessage("File Missing", "%system32%\QMgr.dll" & " is missing on " & Compname)
Else
logfile.writeline "Unable to process BITS version because " & windir & "\system32\QMgr.dll is missing. Exiting Script processing."
WScript.Quit
End If
End If
End Function

Function checkServices()

' SMS Agent Host Service started
logfile.writeline "Calling KickService"
Call KickService("CcmExec")

' Remote Registry Service started
logfile.writeline "Calling RemoteRegistry"
Call KickService("RemoteRegistry")
End Function

Function checkAssignment()
'Ensure that the client is assigned to a site if its not assigned to any
logfile.writeline "Checking to make sure SMS Client has site assignment"
Set ISmsClient = CreateObject ("Microsoft.SMS.Client")
AssignedSite = ISmsClient.GetAssignedSite
If NOT Len(AssignedSite & "")>0 Then
logfile.writeline "Client is not assigned, attempting to AutoDiscover and set"
ISmsClient.EnableAutoAssignment 1
DiscoveredSite = ISmsClient.AutoDiscoverSite
ISmsClient.SetAssignedSite DiscoveredSite,0
logfile.writeline "Client is now assigned to " & ISmsClient.GetAssignedSite
End If
logfile.writeline "Client is now assigned to " & ISmsClient.GetAssignedSite
End Function




' =====================================================
' KickService function
' =====================================================
Function KickService(servicename)
On Error Resume Next
logfile.writeline "Inside KickService"
Dim Results, wmi, Service, returncode, Service2, Started
Results = ServiceState(servicename)
logfile.writeline "servicename = " & servicename
logfile.writeline "Results = " & Results
set wmi = getobject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")

If NOT LCase(Results) = LCase("Running")Then
set Results = wmi.execquery("select state from win32_service where name='" & servicename & "'")
For Each Service In Results
' Start service
returncode = Service.StartService
logfile.writeline "returncode = " & returncode
if returncode <> 0 Then
If Email = True Then
logfile.writeline "SMS Client Service Failure " & servicename & " failed to start on " & CompName
Call EmailMessage("Start Service Error", "SMS Client Service Failure " & servicename & " failed to start on " & CompName)
Call Cleanup
logfile.writeline "Quiting Script"
WScript.Quit
Else
logfile.writeline "Displaying message to user - Error starting service your Windows Management Service (" & servicename & ") - Call The Help Desk immediately"
msgbox "Error starting service your Windows Management Service (" & servicename & ") - Call The Help Desk immediately"
Call Cleanup
logfile.writeline "Quiting Script"
WScript.Quit
End If
end If
Do Until Started = True
'IMPORTANT! >>>>>>>>> Adjust sleep as necessary, but do *not* remove it! <<<<<<<<IMPORTANT!
logfile.writeline "Sleeping for 2 seconds..."
logfile.writeline "Use the below text to see how many times the script looped to start the Service"
WScript.Sleep 2000 'Sleep for 2 Seconds
set Results = wmi.execquery("select state from win32_service where name='" & servicename & "'")
for each Service2 In Results
if lcase(Service2.State) = lcase("Running") Then
logfile.writeline "Started = " & Started
Started = True
end If
Next
Loop
Next
End If
End Function
' =====================================================
' ServiceState subprocedure
' =====================================================
Function ServiceState(servicename)
On Error Resume Next
logfile.writeline "Inside ServiceState"
logfile.WriteLine "Checking " & servicename & " service"
Dim wmi, Results, Service, StateResults, StartMode
set wmi = getobject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
set Results = wmi.execquery("select state from win32_service where name='" & servicename & "'")
For Each Service In Results
StateResults = Service.State
logfile.writeline "StateResults = " & StateResults
Next
ServiceState = StateResults
End Function
' =====================================================
' AdvCliInst subprocedure
' =====================================================
Sub AdvCliInst(ComSpec)
On Error Resume Next
logfile.writeline "Inside AdvCliInst"
Dim smsinstall, WshShell, InstallArgs

Set WshShell = WScript.CreateObject("WScript.Shell")
ComSpec = WshShell.ExpandEnvironmentStrings("%COMSPEC%")
If ComSpec = "" Then
If Email = True Then
logfile.writeline "SMS Client Installation Failure", "The SMS Client failed to Install On " & CompName
Call EmailMessage("SMS Client Installation Failure", "The SMS Client failed to Install On " & CompName)
Call Cleanup
logfile.writeline "Exiting Script Processing"
WScript.Quit
Else
logfile.writeline "Displaying Message to user - Windows Management Service Installation Failed. Please contact The Help Desk"
MsgBox "Windows Management Service Installation Failed. Please contact The Help Desk"
Call Cleanup
logfile.writeline "Exiting Script Processing"
WScript.Quit
End If
Else
InstallArgs = ""
If Wscript.Arguments.Named.Exists("params") Then
If Wscript.Arguments.Named("params") <> "" Then
InstallArgs = Wscript.Arguments.Named("params")
logfile.writeline = "InstallArgs = " & InstallArgs
End If
End If
smsinstall = ComSpec & " /c \\" & Wscript.Arguments.Named("smsserver") & "\SMSClient\ccmsetup.exe SMSSLP=smststserver Disablesiteopt=true FSP=smststserver SMSSITECODE=tst" & InstallArgs
logfile.writeline "Calling SCCM Client installation with below command line:"
logfile.writeline "smsinstall = " & smsinstall
' Run SMS Client Installation
WshShell.Run smsinstall,0,False
End If
End Sub
' =====================================================
' GetDomainRole function
' =====================================================
Function GetDomainRole
On Error Resume Next
logfile.writeline "Inside GetDomainRole"
Dim domainroles, wmi, domainrole
Set wmi = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
Set domainroles = wmi.ExecQuery("SELECT DomainRole FROM Win32_ComputerSystem")
For Each domainrole in domainroles
GetDomainRole = domainrole.DomainRole
logfile.writeline "GetDomainRole = " & GetDomainRole
Next
Set domainroles = Nothing
Set wmi = Nothing
End Function
' =====================================================
' EmailMessage subprocedure
' =====================================================
Function EmailMessage(Subject, Body)
On Error Resume Next
logfile.writeline "Inside EmailMessage"
logfile.writeline "Email Subject: " & Subject
logfile.writeline "Email Body: " & Body
Dim objEmail, objemailfrom
' email using a generic user account as system is being booted up and user may not have logged on yet
Set objEmail = CreateObject("CDO.Message")
objemailfrom = WScript.Arguments.Named("smsserver") & "@company.com"
objEmail.From = objemailfrom
objEmail.To = WScript.Arguments.Named("email")
' objEmail.To = "receipants@company.com"
objEmail.Subject = Subject
objEmail.Textbody = Body
objEmail.Configuration.Fields.Item _
("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
objEmail.Configuration.Fields.Item _
("http://schemas.microsoft.com/cdo/configuration/smtpserver") = _
"smtp.company.com"
objEmail.Configuration.Fields.Item _
("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 25
objEmail.Configuration.Fields.Update
logfile.writeline "Sending Email"
objEmail.Send
Set objEmail = Nothing
End Function
' =====================================================
' ShowFileAccessInfo function
' =====================================================
Function ShowFileAccessInfo(filespec, Compname)
On Error Resume Next
logfile.writeline "Inside ShowFileAccessInfo"
Dim fso, f, filespec_date, FSpace
Set fso = CreateObject("Scripting.FileSystemObject")
If fso.FileExists(filespec) Then
Set f = fso.GetFile(filespec)
logfile.writeline "f = " & f
filespec_date = f.DateLastModified
logfile.writeline "filespec_date = " & filespec_date
FSpace = Instr(filespec_date," ") - 1
logfile.writeline "FSpace = " & FSpace
ShowFileAccessInfo = Left(filespec_date,FSpace)
logfile.writeline "ShowFileAccessInfo = " & ShowFileAccessInfo
Else
If Email = True Then
logfile.writeline "File Missing - " & filespec & " is missing On " & Compname
Call EmailMessage("File Missing", filespec & " is missing On " & Compname)
logfile.writeline "Exiting Script Processing"
WScript.Quit
End If
End If
End Function
' =====================================================
' Destroy any objects
' =====================================================
Sub Cleanup
On Error Resume Next
logfile.writeline "Inside Cleanup"
Set WshShell = Nothing
Set ComSpec = Nothing
Set windir = Nothing
Set strCompName = Nothing
Set SmsClient = Nothing
End Sub
' =====================================================