Skip navigation

Tag Archives: vbscript

So I haven’t written anything even mildly technical lately so I thought I’d show a small script I use to copy and restore my scripts and PowerShell profile across multiple machines.  It’s fairly straight forward.  It will backup my scripts directory from my desktop and PowerShell folder from my documents directory; and recreates the the folder structure in the directory the script is run from.

Conversely it will restore as well provided the content exists.  It is USER specific, so if you use multiple user id’s like I do, it’s important that you manage them accordingly.


Option Explicit
On Error Resume Next
Dim oWShell, oFSO, oNet
Dim strCurUser, strSourceDrive, strTargetDrive, strOption, strMsg
Set oWShell = CreateObject("Wscript.Shell")
Set oFSO 	= CreateObject("Scripting.Filesystemobject")
Set oNet 	= CreateObject("Wscript.Network")

strCurUser = oNet.UserName
strTargetDrive = Left(WScript.ScriptFullName, Len(WScript.ScriptFullName)- _
				Len(WScript.ScriptName)) & strCurUser
strSourceDrive = oWShell.ExpandEnvironmentStrings("%userprofile%")
strMsg = "Filecopy Summary: "& vbcrlf
strOption = LCase(InputBox("Restore|Backup"))

Select Case strOption
    Case "restore"	Call Restore()
    Case Else	Call Backup()
End Select

WScript.Echo strMsg
'-------------------
Function Backup()
	
Dim strScripts, strPShell
	strScripts = "\Desktop\Scripts" : strPShell = "\Documents\windowspowershell"

If Not CreateFolder(strTargetDrive & strScripts) Then
	strMsg = strMsg & "Failed Creating: "& strTargetDrive & strScripts & vbCrLf
Else
	strMsg = strMsg & "Created: " & strTargetDrive & strScripts & vbCrlf
End If

If Not CreateFolder(strTargetDrive & strPShell) Then
	strMsg = strMsg & "Failed Creating: "& strTargetDrive & strPShell & vbCrLf
Else
	strMsg = strMsg & "Created: " & strTargetDrive & strPShell & vbCrlf
End If
	
If Not CopyFolder(strSourceDrive & strScripts, strTargetDrive & strScripts) Then
	strMsg = strMsg & "Failed to Copy: " &  strSourceDrive & strScripts & vbCrLf
Else
	strMsg = strMsg & "Copied: " & strSourceDrive & strScripts & vbCrLf
End If

If Not CopyFolder(strSourceDrive & strPShell, strTargetDrive & strPShell) Then
	strMsg = strMsg & "Failed to Copy: " & strSourceDrive & strPShell & vbCrLf
Else
	strMsg = strMsg & "Copied: " & strSourceDrive & strPShell & vbCrLf
End If

End Function
'-------------------
Function Restore()

Dim strScripts, strPShell, temp1, temp2
	strScripts = "\Desktop\Scripts" : strPShell = "\Documents\windowspowershell"
temp1 = strSourceDrive : temp2 = strTargetDrive
strTargetDrive = temp1 : strSourceDrive = temp2

If Not CreateFolder(strTargetDrive & strScripts) Then
	strMsg = strMsg & "Failed Creating: "& strTargetDrive & strScripts & vbCrLf
Else
	strMsg = strMsg & "Created: " & strTargetDrive & strScripts & vbCrlf
End If

If Not CreateFolder(strTargetDrive & strPShell) Then
	strMsg = strMsg & "Failed Creating: "& strTargetDrive & strPShell & vbCrLf
Else
	strMsg = strMsg & "Created: " & strTargetDrive & strPShell & vbCrlf
End If
	
If Not CopyFolder(strSourceDrive & strScripts, strTargetDrive & strScripts) Then
	strMsg = strMsg & "Failed to Copy: " &  strSourceDrive & strScripts & vbCrLf
Else
	strMsg = strMsg & "Copied: " & strSourceDrive & strScripts & vbCrLf
End If

If Not CopyFolder(strSourceDrive & strPShell, strTargetDrive & strPShell) Then
	strMsg = strMsg & "Failed to Copy: " & strSourceDrive & strPShell & vbCrLf
Else
	strMsg = strMsg & "Copied: " & strSourceDrive & strPShell & vbCrLf
End If

End Function
'-----------
Function CreateFolder(strFolder)
On Error Resume Next
	Dim FolderArray, Folder, Path, booErr
	FolderArray = Split(strFolder,"\")
		For Each Folder In FolderArray
			If Path <> "" Then
				If Not oFSO.FolderExists(Path) Then
					oFSO.CreateFolder(Path)
				End If
			End If
				Path = Path & LCase(Folder) & "\"
			If Err.Number <> 0 Then
				booErr = True
			End If
		Next
		If booErr Then
			CreateFolder = False
		Else
			CreateFolder = True
		End If
End Function
'-----------
Function CopyFolder(strSource, strDest)
On Error Resume Next
	If oFSO.FolderExists(strDest) Then
		oFSO.DeleteFolder strDest, True
	End If
		oFSO.CopyFolder strSource, strDest, True

	If Err.Number <> 0 Then
		CopyFolder = False
	Else
		CopyFolder = True
	End If
End Function


There you go, a bit lacking on the sophistication side of things, but it gets the job done for me.  Feel free to modify the script for your own personal file backup needs, it can really help to streamline the process.

So I had another problem, and one I know I’m not alone in…

I’ve got clients that are failing, won’t install, have corrupt wmi repositories, and obviously I can’t deploy a repair solution to them so what do we do?

Well like most people in an enterprise environment (or smaller I’m sure) we maintain a logon script, and considering that this script will run come hail or high water it is clearly the best possible automated resolution point possible.

There have been some really great log on health check scripts written by a lot of great people in the MyITForum community, and having used two of the more popular ones I’ve decided there are some great things in both of them, but in one case more than what I need; and the other just short of my needs.

So what do we do?  Simple, write our own; which you can download from a link at the bottom of this post.

So lets first look at what it takes to run this script:

CmLogonFrameWork.wsf + config.xml

At run time you can configure 4 override settings:

  • /log:”c:\windows”

This will write the logfile to the windows directory.

  • /config:”somefilesomewhere.xml”

This will load the specific config file you need, useful if you have multiple configurations for multiple machine/user types.

  • /events

This will force the script to write events to the event log regardless of how it’s defined in the config.xml

  • /debug

This will force the script to error verbosely, which will become useful if you are going to extend the code and need to see the error output.

So what does the script do?  Well I’ve put my horrible Visio skills to work to give you the obfuscated flowchart that might very well make you want to claw your face off (included in the script download).  However I’ll give you a quick overview of the intended functionality to save you from all the head scratching and the “what was this guy smoking” statements you might have as you read it.

It’s best to think about this script in 5 blocks:

  1. Initialization
  2. System Check
  3. SCCM Check
  4. SCCM Configuration
  5. Close Out

 

Initialization:

This block is fairly straight forward.  We gather arguments, enforce a cscript execution (for cleanliness more than anything), generate our first log entry, load the configuration file, and write a LastRun entry to HKLM\Software\SccmHealth for tracking of changes and repair mechanisms later.

The only potential repair function during this phase would be to the XMLDom (and all the script will attempt to do is register the component).  If OS version checks fail, or the XML file cannot be read then the script will close with an error in the log.

System Check:

The system check is also fairly straight forward.  We first check the admin account if one is configured, we check the admin shares, we check the list of services defined in the config, and finally we verify that the root/cimv2 is accessible in WMI.

Repairs that would occur in this phase are the enabling of admin shares via service and registry values.  Services are configured per the config file if they are not presently in that state. And finally if it’s enabled, an attempted wmi rebuild would be performed if wmi fails its check (there are two distinct repair types determined by the version of windows).

SCCM Check:

Now things get a bit more confusing, but I’ll keep it high level since I have a bit of an election system in the script on what repair method is the best option.

Check the CM namespace in WMI, then the service, then the executable, and finally the setup files.  If all 4 fail or 3 of 4 then a standard installation will occur.  If only the namespace fails then a reinstallation is attempted provided that option is allowed, if not then a wmi repair is attempted if it is allowed.  There are other checks that manipulate the outcome such as your configuration options obviously and when the last repair date was attempted on the asset.

SCCM Configuration:

This section is very straightforward.

Check the Site code, and check the cache size, if they don’t match the config, we change them to match.

Close Out:

As our final phase we load any additional scripts identified in the config file, generate our System Check, SCCM Check, and SCCM Configuration check report card.  If remote logging is enabled we write the final output there as well, and finally we deconstruct our objects and  terminate the script with or without errors depending on the outcome of course.

Tips for Extending:

The two custom objects being used here in this script are cls_Logging and cls_Dict which are instantiated as Logging and Config respectively.

A list of these classes and how they work can be found here (logging) and here (config).

I’ve also included a test.vbs that extends the script to give you an idea of some of very basic examples of extended functionality.  I would recommend you avoid adding anything to the primary script, but instead extend it all as a separate script and have it performed at the end of the script, unless of course it’s an entirely new object that you wish to build into this tool then by all means have at it.

 

If you have any questions, suggestions, or if you find bugs please comment so I can answer them or repair them, thanks.

Download:

Version 2.1.3

Alright, so Active Directory Asset Management.  What is it?

Well a, not so, unique problem facing every enterprise large or small, is asset management.

Now asset management is not a one dimensional issue, there are requirements and facets to it that shift depending on the person you ask.  From an engineering and maintenance standpoint at my company we have needs of tying important information as closely to the asset as possible, yet removed enough that it’s accessible even when the asset is not.  So what does that mean?

I want to have information tied to the machine but not dependent on the machine.  Something that contains properties that can be modified by the machine, yet retrievable apart from the machine.  Well that’s a relatively simple solution, LDAP, or Active Directory as the case may be.

In this case, my co-worker David Renfrow has opted to utilize the AD Computer Object Description property to store the strings that uniquely identifies our asset.  At present the tool is used to search for specific values in objects, to get/set the string values, and lastly to export findings from the search to a csv report bundled into a single self contained HTA.

image

For the next release I intend to implement error handling, and a potential DB write out function as well as domain search base targeting. At present it only pulls from the Domain of the querying asset, so in a multi domain forest that can be a problem.

Current Version Code Below:


<html>
	<head>
		<title>Active Directory Asset Manager (ADAM)</title>
<!--<description>
	Tool:		Active Directory Asset Manager
	
	Authors: 	David Renfrow, Daniel Belcher 
	
	Tool Info: 	The tools purpose is to retrieve, query, and 
				set system specific information	to a string in 
				the ADO Description property.  This string is 
				specially formatted	to retrieve in a specific way:
				
		<Server>;<Sixdot>;<Purpose>;<Location>;<Domain>;<SLA>;
	<SrvContact>;<SrvManager>;<SGPrimary>;<SGBackup>;<DBAPrimary>;<DBABackup>
				
</description>-->
	<HTA:APPLICATION 
		ID="ADAM" 
		APPLICATIONNAME="ADAM"
		BORDER="thin"
		SINGLEINSTANCE="yes"
	>

<SCRIPT LANGUAGE="VBScript">
'-----------Application Initialization-----------------------------------------
'On Error Resume Next
Dim oConn, oCmd, item, oComputer, strDomain, Dict

Sub Window_onLoad
	window.resizeTo 625,725
	
	Set oConn = CreateObject("ADODB.Connection")
		oConn.Provider = "ADsDSOObject"
	Set oCmd =  CreateObject("ADODB.Command")
	Set Dict = New cls_Dict

	strDomain = Dict.DistinguishedDomainName
	
	
End Sub 
'----------------Controls------------------------------------------------------
Sub getproc()
	On Error Resume Next

	strComputer = ServerName.Value
	ADLookUp(strComputer)
	Description = oComputer.Get("Description")
	If Err.Number <> 0 Then
		Err.Clear
		Description = ";;;;;;;;;;;"
	End If
	strOut = Split(Description,";")

	DataArea.innerHtml =	"<table border=""""1"""">" & "<tr>" & _
		"<td>Server</td>"&"<td>"&UCase(strComputer)& "</td>" & "</tr><tr>" & _
		"<td>SixDot</td><td>"&strOut(1) & "</td>" & "</tr><tr>" & _
		"<td>Purpose</td><td>"&strOut(2) & "</td>" & "</tr><tr>" & _
		"<td>Location</td><td>"&strOut(3) & "</td>" & "</tr><tr>" & _
		"<td>Domain</td><td>"&strOut(4) & "</td>" & "</tr><tr>" & _
		"<td>SLA</td><td>"&strOut(5) & "</td>" & "</tr><tr>" & _
		"<td>Srv Contact</td><td>"&strOut(6) & "</td>" & "</tr><tr>" & _
		"<td>Srv Manager</td><td>"&strOut(7) & "</td>" & "</tr><tr>" & _
		"<td>SG Primary</td><td>"&strOut(8) & "</td>" & "</tr><tr>" & _
		"<td>SG Backup</td><td>"&strOut(9) & "</td>" & "</tr><tr>" & _
		"<td>DBA Primary</td><td>"&strOut(10) & "</td>" & "</tr><tr>" & _
		"<td>DBA Backup</td><td>"&strOut(11) & "</td>" & "</tr><tr>" & _
		"<td>Last Updated</td><td>"&strOut(12) & "</tr></table>"
			
	servername.value = UCase(strComputer)
	sixdot.value = strOut(1)
	purpose.value = strOut(2)
	loc.value = strOut(3)
	domain.value = strOut(4)
	sla.value = strOut(5)
	srvcontact.value = strOut(6)
	srvmanager.value = strOut(7)
	sgprimary.value = strOut(8)
	sgbackup.value = strOut(9)
	dbaprimary.value = strOut(10)
	dbabackup.value = strOut(11)

	oConn.Close
End Sub

Sub setproc()
	strComputer = ServerName.Value
	ADLookUp(strComputer)
	if sixdot.value = "" then sixdot.value = "NA"
	if purpose.value = "" then purpose.value = "NA"
	if loc.value = "" then loc.value = "NA"
	if domain.value = "" then domain.value = "NA"
	if sla.value = "" then sla.value = "NA"
	if srvcontact.value = "" then srvcontact.value = "NA"
	if srvmanager.value = "" then srvmanager.value = "NA"
	if sgprimary.value = "" then sgprimary.value = "NA"
	if sgbackup.value = "" then sgbackup.value = "NA"
	if dbaprimary.value = "" then dbaprimary.value = "NA"
	if dbabackup.value = "" then dbabackup.value = "NA"
	oComputer.Put "Description", servername.value&";"&sixdot.value&";"& _
			purpose.value&";"&loc.value&";"&domain.value&";"&SLA.value&";"& _
			srvcontact.value&";"&srvmanager.value&";"&sgprimary.value&";"& _
			sgbackup.value&";"&dbaprimary.value&";"&dbabackup.value&";"&date
	oComputer.SetInfo
	oConn.Close
End Sub

Sub clearproc
	dataarea.InnerHTML = ""
	servername.value = ""
	srvcontact.value = ""
	sixdot.value = ""
	srvmanager.value = ""
	purpose.value = ""
	sgprimary.value = ""
	loc.value = ""
	sgbackup.value = ""
	domain.value = ""
	dbaprimary.value = ""
	sla.value = ""
	dbabackup.value = ""
End Sub

Sub searchproc()

	If Dict.Exists("ADRecords") Then
		Dict.Remove("ADRecords")
	End If
	
	msg = Null
	
	Call ADSearch(search.value)
	For Each item In Dict.ReturnArray("ADRecords")
		temp = Split(item,"|x|")	
		msg = msg &"<b>Name:</b><em> " & UCase(temp(0)) & "</em><br>" _
				& "<b>Description:</b><em> " & temp(1) & "</em><br><br>"
	Next
	
	dataarea.innerhtml = "<input type="&Chr(34)&"button"&Chr(34)&" value="& _
	Chr(34)& "Export to CSV"&Chr(34)&" onclick="&Chr(34)&"export"&Chr(34)& _
	"/><br>" & msg
	
End Sub

Sub export()

	Dim oFso, FileHandle
	Set oFso = CreateObject("Scripting.FileSystemObject")
	temp = Split(Date,"/")
	sDate = temp(0)&temp(1)&temp(2)
		
	Set FileHandle = oFso.OpenTextFile _
		(Dict.CurrentDir&"ServerOut-"&sDate&".csv", 2, True)
		FileHandle.WriteLine "Server,SIXDOT,Purpose,Location,Domain,SLA," & _
"Srv Contact,Srv Manager,SG Primary,SG Backup,DBA Primary,DBA Backup,Modified"
		
		For Each item In Dict.ReturnArray("AdRecords")
			temp = Split(item,"|x|")
			strWrite = Replace(temp(1),",","-")
				strWrite = Replace(strWrite,";",",")
					strWrite = Replace(strWrite,"-",";")
			FileHandle.WriteLine strWrite
		Next
	FileHandle.Close
	
	dataarea.innerhtml = "<p>Report written to:<br>" & _
					"<em>"&Dict.CurrentDir&"serverout-"&sDate&".csv</em></p>"
End Sub

'-----------------Working Functions--------------------------------------------
Public Function ADLookUp(strComputer)

	oConn.Open "Active Directory Provider"
	Set oCmd.ActiveConnection = oConn
	oCmd.CommandText = _
		"Select * from 'LDAP://"&strDomain&"' " _
        & "Where objectCategory='computer' AND name = '"& strComputer &"'" 
	oCmd.Properties("searchscope") = 2
	oCmd.Properties("Page Size") = 1000
	Set oRecord = oCmd.Execute

	For Each item In oRecord.Fields
		Set oComputer = GetObject(item)
	Next
End Function

Public Function ADSearch(strProperty)

	oConn.Open "Active Directory Provider"
	Set oCmd.ActiveConnection = oConn
	oCmd.CommandText = "Select Name, Description, DistinguishedName from " &_
	"'LDAP://"&strDomain&"' Where objectCategory='computer'" 
	oCmd.Properties("Page Size") = 1000
	oCmd.Properties("searchscope") = 2
	Set oRecord = oCmd.Execute
	oRecord.MoveFirst
	
	Do Until oRecord.EOF
	On Error Resume Next 
		Set oComputer = GetObject _
		("LDAP://" & orecord.Fields("distinguishedName").value)
			Description = oComputer.Get("Description")
			If InStr(1,LCase(Description), LCase(strProperty)) <> 0 Then
				Call Dict.ItemList("ADRecords",orecord.Fields("name").value& _
				"|x|" & Description)
			End If
		Description = Null
		oRecord.MoveNext
	Loop
	oConn.Close
End Function
'----------------Class Objects-------------------------------------------------

Class cls_Dict
'Class wrapper for the scripting.dictionary
	Private oDict, oNet, Comparemode, strSplit, oFso, oWShell, oADSI

'---------------------------------------------------------

Private Sub Class_Initialize()
'Dictionary class init subroutine    
    If Debugmode Then On Error Goto 0 Else On Error Resume Next
		Set oDict 	= CreateObject("Scripting.Dictionary")
		Set oNet	= CreateObject("Wscript.Network")
		Set oFso	= CreateObject("Scripting.FileSystemObject")
		Set oWShell = CreateObject("Wscript.Shell")
		Set oADSI = CreateObject("ADSystemInfo")
		
		Dim strUserDomain : strUserDomain = oADSI.DomainDNSName
		Dim strDomain : strDomain = Split(strUserDomain,".")
		For Each item In strDomain
			strDNDomain = strDNDomain & "DC="&item&","
		Next
		oDict.CompareMode = 1
			strSplit = "|:|"
		Call oDict.Add("CurrentDir",oWShell.CurrentDirectory&"")
		Call oDict.Add("computername", oNet.Computername)
		Call oDict.Add("Windir",LCase(oWShell.ExpandEnvironmentStrings _
			("%windir%")))
		Call oDict.Add("CurrentUser",LCase(oNet.UserName))
		Call oDict.Add("Domain",LCase(oNet.UserDomain))
		Call oDict.Add("DomainDN",Left(strDNDomain,(Len(strDNDomain)-1)))
		Call SetOsVer
End Sub

'---------------------------------------------------------

Private Sub Class_Terminate()
'Dictionary class termination subroutine
	If IsObject(oDict) then Set oDict = Nothing
End Sub

'---------------------------------------------------------

Public Property Get CurrentDir
'Returns Current Directory for the script
	CurrentDir = oDict.Item("CurrentDir")
End Property

'---------------------------------------------------------

Public Property Get ComputerName
'Returns the machine name for the current machine
	ComputerName = oDict.Item("computername")
End Property

'---------------------------------------------------------

Public Property Get CurrentUser
'Returns the machine name for the current machine
	CurrentUser = oDict.Item("CurrentUser")
End Property

'---------------------------------------------------------

Public Property Get Domain
'Returns the machine name for the current machine
	Domain = oDict.Item("Domain")
End Property

'---------------------------------------------------------

Public Property Get DistinguishedDomainName
'Returns the Distinguished Name for the Domain
	DistinguishedDomainName = oDict.Item("DomainDN")
End Property

Public Property Get Windir
'Returns the windows directory for the local machine
	Windir = oDict.Item("windir")
End Property

Public Property Get SystemRoot
'Returns the appropriate system directory system32 or syswow64

	If InStr(StrReverse(oDict.Item("CurrentOsVer")), "46x") <> 0 Then 
		SystemRoot = Windir & "syswow64"
	Else
		SystemRoot = Windir & "system32"
	End If

End Property
'---------------------------------------------------------

Public Sub Add(strKey,strValue)
'Method to Add a key and item
	If Debugmode Then On Error Goto 0 Else On Error Resume Next

    Dim EnvVariable, strSplit

    	strSplit = Split(strValue, "%")
	If IsArray(strSplit) Then 
    	EnvVariable = oWShell.ExpandEnvironmentStrings _
    		("%" & strSplit(1) & "%")
    	strValue = strSplit(0) & EnvVariable & strSplit(2)
    			If strValue = "" Then
    				strValue = strSplit(0)
    			End If
    End If
	If oDict.Exists(strKey) Then
		oDict(strKey) = Trim(strValue)
	Else
		oDict.Add strKey, Trim(strValue)
	End If
End Sub

'---------------------------------------------------------

Public Function Exists( strkey)
'Method to check existance of a key
    If Debugmode Then On Error Goto 0 Else On Error Resume Next
	
	If oDict.Exists(strKey) then 
		Exists = True
	Else
		Exists = False
	End If

End Function

'---------------------------------------------------------

Public Function Keys()
'Method to retrieve an array of keys
    If Debugmode Then On Error Goto 0 Else On Error Resume Next

	If IsObject(oDict) Then
		Keys = oDict.Keys
	End If
End Function

'---------------------------------------------------------

Public Function Items()
'Method to retrieve an array of items
    If Debugmode Then On Error Goto 0 Else On Error Resume Next

	If IsObject(oDict) Then
		Items = oDict.Items
	End If
End Function

'---------------------------------------------------------
Private Sub SetOsVer()
'Sets a comparable OSVer key item into the dictionary
		If DebugMode Then On Error Goto 0 Else On Error Resume Next

		Dim x, VersionCheck	
		
	VersionCheck = owShell.RegRead("HKLMsoftwaremicrosoft" _
					& "windows ntcurrentversionproductname")

			If ofso.folderexists("c:windowssyswow64") Then
				x = "x64"
			Else
				x = "x86"
			End If
		Call oDict.Add("CurrentOsVer",VersionCheck & " " & x)
End Sub
	Public Property Get OsVer()
		OsVer = oDict.Item("CurrentOsVer")
	End Property

'---------------------------------------------------------
Public Property Get AppName()

	AppName = Left(WScript.ScriptName, Len(WScript.ScriptName) - 4)
End Property 

'---------------------------------------------------------

Public Property Get Key( strKey)
'Property to retrieve item value from specific key
    If Debugmode Then On Error Goto 0 Else On Error Resume Next

		Key = Empty
	If IsObject(oDict) Then
		If oDict.Exists(strKey) Then Key = oDict.Item(strKey)
	End If
End Property

'---------------------------------------------------------

Public Sub ItemJoin(strKey, strItem)
'Method to concactenate new items under one key at the end of the string
    If Debugmode Then On Error Goto 0 Else On Error Resume Next
	Dim concat
	If Not oDict.Exists(strKey) Then
		Call oDict.Add(strkey, stritem)
	Else
		concat = oDict.Item(strKey)
		concat = concat & " " & strItem
			oDict.Remove(strKey)
		Call oDict.Add(strKey,concat)
	End If
End Sub

'---------------------------------------------------------

Public Sub ItemList( strKey,  strItem)
'Method to concactenate new items under one key at the end of the string
    If Debugmode Then On Error Goto 0 Else On Error Resume Next
	Dim concat
	If Not oDict.Exists(strKey) Then
		Call oDict.Add(strkey, stritem)
	Else
		concat = oDict.Item(strKey)
		concat = concat & "|:|" & strItem
			oDict.Remove(strKey)
		Call oDict.Add(strKey,concat)
	End If
End Sub

'---------------------------------------------------------

Public Sub ItemJoinRev( strKey,  strItem)
'Method to concactenate new items under one key at the start of the string
    If Debugmode Then On Error Goto 0 Else On Error Resume Next
	Dim concat
	If Not oDict.Exists(strKey) Then
		Call oDict.Add(strkey, stritem)
		Exit Sub
	Else
		concat = oDict.Item(strKey)
		concat = strItem & " " & concat
			oDict.Remove(strKey)
		Call oDict.Add(strKey,concat)
	End If
End Sub
	
'---------------------------------------------------------	

Public Function ReturnArray( strKey)
'Method to return an item as an array
    If Debugmode Then On Error Goto 0 Else On Error Resume Next
	
	Dim ItemToSplit, ItemArray
	
	ItemToSplit = oDict.item(strKey)
	
	ItemArray = Split(ItemToSplit, strSplit)
	
	ReturnArray = ItemArray	
	
End Function
	
'---------------------------------------------------------	

Public Sub Remove( strKey)
'Method to remove a key value
	oDict.Remove(strKey)
End Sub

'---------------------------------------------------------

Public Sub RemoveAll()
'Method to remove all data from the dictionary
	oDict.RemoveAll
End Sub

End Class
'------------------------------------------------------------------------------
</script>

<body bgcolor="silver">
	<table>
	<tr>
	<td>Server</td><td><input type="text" name="servername" size="30"></td>
	<td>Srv Contact</td><td><input type="text" name="srvcontact" size="30"></td>
	</tr>
	<tr>
	<td>SIXDOT</td><td><input type="text" name="sixdot" size="30"></td>
	<td>Srv Manager</td><td><input type="text" name="srvmanager" size="30"></td>
	</tr>
	<tr>
	<td>Purpose</td><td><input type="text" name="purpose" size="30"></td>
	<td>SG Primary</td><td><input type="text" name="sgprimary" size="30"></td>
	</tr>
	<tr>
	<td>Location</td><td><input type="text" name="loc" size="30"></td>
	<td>SG Backup</td><td><input type="text" name="sgbackup" size="30"></td>
	</tr>
	<tr>
	<td>Domain</td><td><input type="text" name="domain" size="30"></td>
	<td>DBA Primary</td><td><input type="text" name="dbaprimary" size="30"></td>
	</tr>
	<tr>
	<td>SLA</td><td><input type="text" name="sla" size="30"></td>
	<td>DBA Backup</td><td><input type="text" name="dbabackup" size="30"></td>
	</tr>
	</table>
	<p>
	<input type="button" value="Get" onclick="getproc"/>
	<input type="button" value="Set" onclick="setproc"/>
	<input type="button" value="Clear" onclick="clearproc"/>
	<input type="button" value="Search" onclick="searchproc"/>
	<input type="text" value="keyword" name="search">
	</p>
	<hr>
	<div id = "DataArea"></div>
</body>

</html>

A simple vbscript I use for manual client installation. It does some basic quick checks and fixes before begining an install. One could feasibly use this for health checking, but it’s not nearly as robust as my actual logon framework or other scripts from people like Jason Sandys or Dan Thompson.

The variables strAdmin (local admin service account), strCcmSetup (path to folder with the ccmsetup.exe), and strArguement (install string) need to be defined before running the script. It’s worth mentioning that strCcmSetup is just the path to ccmsetup.exe, there is no need to actually type ccmsetup.exe into the path and it’s best if you don’t since I didn’t bother writing anything in to verify if it is or isn’t. The script will auto append the executable to that variable so ccmsetup.exe in the path will give you ccmsetup.execcmsetup.exe

'Author Daniel Belcher             ||
'CCMsetup Function                 ||
'Date 2/11/2011 rev 5/20/2011      ||
'==================================||
'|Objects, Variables, and Constants ********************************************
'=============================================================================||
'|| Non-Standard Installer Variables - Please define them                     ||
'|| Admin Account
	strAdmin = "domain\user"
'|| Path to ccmsetup.exe
	strCcmSetup = "\\path\where\ccmsetup.exe\resides\"
'|| Install string
	strArguement = "/noservice SMSSITECODE=ABC SMSSLP=SERVER.ADDRESS " _
				& "SMSFSP=SERVER.ADDRESS"
'                                                                             ||
'=============================================================================||
Const DEBUGMSG = False 'Boolean to use for testing False = Silent True = Verbose
Const ForAppending = 8
Dim oWShell
Set oWShell = CreateObject("WScript.Shell")
Dim oFSo
Set oFSO = CreateObject("Scripting.FileSystemObject")
Dim oNet
Set oNet = CreateObject("Wscript.Network")
ComputerName = oNet.ComputerName
Dim Target
Target = "."
Dim oWMISvc
Set oWMISvc = GetObject("winmgmts:\\"&Target)
'|Main Run *******************************************************************||
'=============================================================================||
WriteLog "Starting "&Wscript.Scriptname&" on "&ComputerName,2
PreReq
CCMSetup
Report
'SubRoutines *****************************************************************||
'=============================================================================||
Sub WriteLog(msg,mtype)
'Subroutine for writing the log
logname = wscript.scriptname&".log"
if not oFSo.FileExists(logname) then
oFSo.CreateTextFile(logname)
end if

msgline = "<![LOG["&msg&"]LOG]!><time="&""""&DatePart("h",Time)
msgline = msgline &":"&DatePart("n",Time)&":"&DatePart("s",Time)
msgline = msgline &".000+0"""&" date="""&Replace(Date,"/","-")
msgline = msgline &""""&" component="""&WScript.ScriptName
msgline = msgline &""" context="""" type="""&mtype
msgline = msgline &""" thread="""" file="""&WScript.ScriptName& """>"

Set oLogFile = oFSo.OpenTextFile(logname, ForAppending, True)
    oLogFile.WriteLine msgline
oLogFile.Close
if DEBUGMSG then wscript.echo msg&" "& mtype
End Sub
'*******************************************************************************
Sub SvcStart(service)
'Attempts to start a service
Set WMIServices = oWMISvc.ExecQuery _
("Select * from Win32_Service where name = '"&service&"'")
     For Each item in WMIServices
         if lcase(item.startmode) <> "automatic" then
                             item.ChangeStartMode("Automatic")
         end if
         start = item.startservice()
         wscript.sleep 4000
           if start <> 0 Then
                   WriteLog "ERROR: Failed to start the "&service&" Service, " _
                   & "investigation required", 3
                             wscript.quit(0)
                    else
                    WriteLog "Succesfully started "& service &" Service",1
           end if
     next
End Sub
'|Functions ******************************************************************||
'=============================================================================||
Function PreReq
'Prerequisite check for client installation
Set WMIServices = oWMISvc.ExecQuery _
("Select * from Win32_Service")
strService = "lanmanworkstation"
regAdminSPath = "HKLM\SYSTEM\CurrentControlSet\services\LanmanServer\" _
				& "Parameters\"
 For Each item in WMIServices
          if lcase(item.name) = strService then
             if lcase(item.state) <> "running" then
                writelog "Warning: "&strService&" was not running, " _
                & "attempting to start...", 2
                SvcStart strService
             else
                 writelog strService& " was found running, OK", 1
             end if
          end if
 next
if not oFSO.FolderExists("\\"&computername&"\admin$") then
WriteLog "Warning: Admin shares not available, attempting to add " _
		& "registry key...", 2
    On Error Resume Next
oWShell.RegWrite regAdminSPath & "AutoShareWKS", 1, "REG_SZ"
              if err.Number <> 0 then
WriteLog "ERROR: "&regAdminSPath&"AutoShareWKS, 1 failed to write, or " _
		&"already exists.", 3
                  else
WriteLog regAdminSPath&"Notice: AutoShareWKS, 1 written succesfully. " _
					& " Restart required to take effect.", 2
              end if
Wscript.Echo "AutoShareWKS key was written to: " &vbcrlf _
          &regAdminSPath & vbcrlf _
          &"Please restart the machine, and rerun this script"
else
WriteLog "Admin$ shares found, and working.", 1
end if
Set AdminCheck = GetObject("WinNT://" & oNet.ComputerName _
							& "/Administrators,group")
if AdminCheck.IsMember("WinNT://"& strAdmin) then
WriteLog "User "&strAdmin&" found in Local Adminstrators group", 1
        else
WriteLog "Warning: User "&strAdmin&" not found in Local Administrators group",2
        on Error Resume Next
           AdminCheck.Add("WinNT://"&strAdmin)
        if Err.Number <> 0 then
WriteLog "ERROR: Unable to add "&strAdmin&" to the Local Administrators group",3
           Wscript.Echo "Unable to add "&strAdmin&" to local Admins." & vbcrlf _
           &"Please do so manually and rerun this script."
           Wscript.Quit(0)
        end if
end if

End Function
'*******************************************************************************
Function CCMSetup
'Client detection and installation
Set WMIServices = oWMIsvc.ExecQuery _
("Select * from Win32_Service where name ='ccmexec'")
boorun = true
   for each objservice in WMIServices
       strservice = lcase(objservice.name)
           if strservice = "ccmexec" then boorun = "False"
   next
if not boorun then
For Each service in WMIServices
  if lcase(service.state) <> "running" then
     svcStart "ccmexec" 'Attempt to start service if stopped
WriteLog "Warning: CcmExec service found, but was not running, " _
		& "attempting to start...", 2
  end if
next
WriteLog "SCCM client service, CcmExec found, closing script", 1
	exit function
end if
if boorun then
oWShell.run strCcmsetup&"ccmsetup.exe " & strArguement
        WriteLog "CcmSetup has begun using: ccmsetup.exe"& strArguement,1
end if

End Function
'******************************************************************************
Function Report
 If DEBUGMSG then msgbox "Complete" 'Set for popup on script complete
End Function
'End *************************************************************************||
'=============================================================================||

As has become my standard, the logs are written in a markup that works with trace32.

So similarly to the logging class object I posted back on December 16th; I’m posting a dictionary wrapper object I’ve built to enhance it’s (scripting.dictionary) usage for creating indexed arrays, concatenating strings, etc…

As with the Logging class object, it must be instantiated to use, so…

Dim Dict
Set Dict = New cls_Dict
 
Call Dict.Add("stuff","my message")
	Wscript.Echo Dict.Key("stuff") 

Wscript.Quit(0)

The object code is below, I’ll give a walkthrough on it’s usage after the code.

'============================================================================
'Dictionary Class ===========================================================
'============================================================================

Class cls_Dict
'Class wrapper for the scripting.dictionary
	Private oDict, oNet, Comparemode, strSplit, oFso, oWShell

'---------------------------------------------------------

Private Sub Class_Initialize()
'Dictionary class init subroutine    
    If Debugmode Then On Error Goto 0 Else On Error Resume Next
		Set oDict 	= CreateObject("Scripting.Dictionary")
		Set oNet	= CreateObject("Wscript.Network")
		Set oFso	= CreateObject("Scripting.FileSystemObject")
		Set oWShell = CreateObject("Wscript.Shell")
		oDict.CompareMode = 1
			strSplit = "|:|"
		Call oDict.Add("CurrentDir",Left(WScript.ScriptFullName, _
			(Len(WScript.ScriptFullName)-Len(WScript.ScriptName))))
		Call oDict.Add("computername", oNet.Computername)
		Call oDict.Add("Windir",LCase(oWShell.ExpandEnvironmentStrings _
			("%windir%")))
		Call oDict.Add("CurrentUser",LCase(oNet.UserName))
		Call SetOsVer
End Sub

'---------------------------------------------------------

Private Sub Class_Terminate()
'Dictionary class termination subroutine
	If IsObject(oDict) then Set oDict = Nothing
End Sub

'---------------------------------------------------------

Public Property Get CurrentDir
'Returns Current Directory for the script
	CurrentDir = oDict.Item("CurrentDir")
End Property

'---------------------------------------------------------

Public Property Get ComputerName
'Returns the machine name for the current machine
	ComputerName = oDict.Item("computername")
End Property

'---------------------------------------------------------

Public Property Get CurrentUser
'Returns the machine name for the current machine
	CurrentUser = oDict.Item("CurrentUser")
End Property

'---------------------------------------------------------

Public Property Get Windir
'Returns the windows directory for the local machine
	Windir = oDict.Item("windir")
End Property

Public Property Get SystemRoot
'Returns the appropriate system directory system32 or syswow64

	If InStr(StrReverse(oDict.Item("CurrentOsVer")), "46x") <> 0 Then 
		SystemRoot = Windir & "\syswow64"
	Else
		SystemRoot = Windir & "\system32"
	End If

End Property
'---------------------------------------------------------

Public Sub Add(strKey,strValue)
'Method to Add a key and item
	If Debugmode Then On Error Goto 0 Else On Error Resume Next

    Dim EnvVariable, strSplit

    	strSplit = Split(strValue, "%")
	If IsArray(strSplit) Then 
    	EnvVariable = oWShell.ExpandEnvironmentStrings _
    		("%" & strSplit(1) & "%")
    	strValue = strSplit(0) & EnvVariable & strSplit(2)
    			If strValue = "" Then
    				strValue = strSplit(0)
    			End If
    End If
	If oDict.Exists(strKey) Then
		oDict(strKey) = Trim(strValue)
	Else
		oDict.Add strKey, Trim(strValue)
	End If
End Sub

'---------------------------------------------------------

Public Function Exists( strkey)
'Method to check existance of a key
    If Debugmode Then On Error Goto 0 Else On Error Resume Next
	
	If oDict.Exists(strKey) then 
		Exists = True
	Else
		Exists = False
	End If

End Function

'---------------------------------------------------------

Public Function Keys()
'Method to retrieve an array of keys
    If Debugmode Then On Error Goto 0 Else On Error Resume Next

	If IsObject(oDict) Then
		Keys = oDict.Keys
	End If
End Function

'---------------------------------------------------------

Public Function Items()
'Method to retrieve an array of items
    If Debugmode Then On Error Goto 0 Else On Error Resume Next

	If IsObject(oDict) Then
		Items = oDict.Items
	End If
End Function

'---------------------------------------------------------
Private Sub SetOsVer()
'Sets a comparable OSVer key item into the dictionary
		If DebugMode Then On Error Goto 0 Else On Error Resume Next

		Dim x, VersionCheck	
		
	VersionCheck = owShell.RegRead("HKLM\software\microsoft\" _
					& "windows nt\currentversion\productname")

			If ofso.folderexists("c:\windows\syswow64") Then
				x = "x64"
			Else
				x = "x86"
			End If
		Call oDict.Add("CurrentOsVer",VersionCheck & " " & x)
End Sub
	Public Property Get OsVer()
		OsVer = oDict.Item("CurrentOsVer")
	End Property

'---------------------------------------------------------
Public Property Get AppName()

	AppName = Left(WScript.ScriptName, Len(WScript.ScriptName) - 4)
End Property 

'---------------------------------------------------------

Public Property Get Key( strKey)
'Property to retrieve item value from specific key
    If Debugmode Then On Error Goto 0 Else On Error Resume Next

		Key = Empty
	If IsObject(oDict) Then
		If oDict.Exists(strKey) Then Key = oDict.Item(strKey)
	End If
End Property

'---------------------------------------------------------

Public Sub ItemJoin(strKey, strItem)
'Method to concactenate new items under one key at the end of the string
    If Debugmode Then On Error Goto 0 Else On Error Resume Next
	Dim concat
	If Not oDict.Exists(strKey) Then
		Call oDict.Add(strkey, stritem)
	Else
		concat = oDict.Item(strKey)
		concat = concat & " " & strItem
			oDict.Remove(strKey)
		Call oDict.Add(strKey,concat)
	End If
End Sub

'---------------------------------------------------------

Public Sub ItemList( strKey,  strItem)
'Method to concactenate new items under one key at the end of the string
    If Debugmode Then On Error Goto 0 Else On Error Resume Next
	Dim concat
	If Not oDict.Exists(strKey) Then
		Call oDict.Add(strkey, stritem)
	Else
		concat = oDict.Item(strKey)
		concat = concat & "|:|" & strItem
			oDict.Remove(strKey)
		Call oDict.Add(strKey,concat)
	End If
End Sub

'---------------------------------------------------------

Public Sub ItemJoinRev( strKey,  strItem)
'Method to concactenate new items under one key at the start of the string
    If Debugmode Then On Error Goto 0 Else On Error Resume Next
	Dim concat
	If Not oDict.Exists(strKey) Then
		Call oDict.Add(strkey, stritem)
		Exit Sub
	Else
		concat = oDict.Item(strKey)
		concat = strItem & " " & concat
			oDict.Remove(strKey)
		Call oDict.Add(strKey,concat)
	End If
End Sub
	
'---------------------------------------------------------	

Public Function ReturnArray( strKey)
'Method to return an item as an array
    If Debugmode Then On Error Goto 0 Else On Error Resume Next
	
	Dim ItemToSplit, ItemArray
	
	ItemToSplit = oDict.item(strKey)
	
	ItemArray = Split(ItemToSplit, strSplit)
	
	ReturnArray = ItemArray	
	
End Function
	
'---------------------------------------------------------	

Public Sub Remove( strKey)
'Method to remove a key value
	oDict.Remove(strKey)
End Sub

'---------------------------------------------------------

Public Sub RemoveAll()
'Method to remove all data from the dictionary
	oDict.RemoveAll
End Sub

End Class

 

So let’s look at the exposed functionality added to or exposed from the scripting dictionary by this object. 

Methods:

 

  • Add

Modified to resolve and accept environment variables, and is also the sole method for adding and replacing keys and their item values

  • ItemJoin

Concatenates the item value to the end of the existing key item

  • ItemJoinRev

Concatenates the item value to the beginning of the existing key item

  • ItemList

Creates an array under an existing key or creates if it doesn’t exist

  • ReturnArray

Returns an array if array exists under the key

  • Exists

Checks if key exists, returns boolean value

  • Items

Returns all items in an array

  • Keys

Returns all keys in an array

  • Remove

Remove a key and item value

  • RemoveAll

Dump entire contents of the dictionary object

 

Properties:

 

  • Key

This is modified from the original usage, and returns the item for the given key. Add is used to modify the key only.

  • AppName

Returns script name, minus extension value

  • ComputerName

Returns the netbios name of the machine

  • CurrentDir

Returns the current working directory of the script

  • CurrentUser

Returns the current logged on user ID (or executing id)

  • OsVer

Returns a OS Version and arch

  • SystemRoot

Returns the environment path for the system root

  • Windir

Returns the environment path for the Windows directory


Feel free to hit me up about usage or any other questions you might have regarding why I did things a certain way, or with any changes you might have made to improve this code.

Ok, so I’ve not posted anything for a few days and I felt the need to throw a brief technical post up with a code snippet from a current project I’m working on. I’m building a health check logon script and as part of that framework I wanted to build a logging object. Since the target format has to be vbscript for what I’m doing, I’ve built it as such, and in a format that views nicely inside of trace32 and trace64.

The idea was to build an object that would perform a simple task, write a log file…. then write a log file, or an event, then write only error events, but could also buffer and dump a final error or success log as a split log to another remote location. All configurable via properties, but with only two exposed methods to control it all (write & writeremote). This object is instantiated by:

Dim Logging
Set Logging = New cls_Logging

Call Logging.Write("my message",1)

Wscript.Quit(0)

Simple enough, no? It’s also worth mentioning I wrote the code so that a constant could be set within the instantiating script of DEBUGMODE and if TRUE it will error, else suppresses all error output.

Here’s the code:

'=================================================================================
'Logging Class ===================================================================
'=================================================================================

'Not required for WSF, but is when in standard VBS
Const ForAppending = 8

'Log and Event writer object
Class cls_Logging
'Class for logging to file and event viewer

Private oWShell,oNet,oFSo,Filehandle,rFilehandle
Private fPath,strRFPath,fMaxSize,fLogname,strRemoteErr,BoolEvent,BoolRemote,oDict

Private Sub Class_Initialize()
    'Object Init subroutine
    If Debugmode Then On Error Goto 0 Else On Error Resume Next
    Set oWShell 	= CreateObject("Wscript.Shell")
	Set oNet 		= CreateObject("Wscript.Network")
	Set oFSo 		= CreateObject("Scripting.FileSystemObject")
	Set oDict		= CreateObject("Scripting.Dictionary")
		LogEvent 	= False
		RemoteLog	= False
		Path 		= Left(WScript.ScriptFullName,(Len(WScript.ScriptFullName)_
						-Len(WScript.ScriptName)))
		File 		= LCase(oNet.ComputerName)
		MaxSize = 2
End Sub

'---------------------------------------------------------

Private Sub Class_Terminate()
	'Object Termination subroutine
    If Debugmode Then On Error Goto 0 Else On Error Resume Next
	If bOpen Then
		Filehandle.close
	End If
		Set oWShell = Nothing
		Set oNet = Nothing
		Set oFSo = Nothing
		Set Filehandle = Nothing
		Set fPath = Nothing
		Set fMaxSize = Nothing
		Set fLogname = Nothing
		Set BoolEvent = Nothing
End Sub

'---------------------------------------------------------
'File name properties, for changing and retrieving the log file name
Public Property Let File(strFile)
    If Debugmode Then On Error Goto 0 Else On Error Resume Next
	If (InStr(StrReverse(strFile),"gol.")) <> 0 Then
		fLogname = strFile
	Else
		fLogname = strFile & ".log"
	End If
End Property
		Public Property Get File()
		    If Debugmode Then On Error Goto 0 Else On Error Resume Next
				File = fLogname
		End Property

'---------------------------------------------------------
'Path name properties, for changing and retrieving the path to logs
Public Property Let Path(strPath)
    If Debugmode Then On Error Goto 0 Else On Error Resume Next
		If (InStr(StrReverse(strpath),"\")) <> 1 Then
			fPath = strPath & "\"
		Else
			fPath = strPath
		End If
End Property
		Public Property Get Path()
		    If Debugmode Then On Error Goto 0 Else On Error Resume Next
			Path = fPath
		End Property
		
'---------------------------------------------------------
'Fully concatenated file name property for retrival.
		Public Property Get FullFileName()
			FullFileName = Path & File
		End Property

'---------------------------------------------------------
'Property for setting maximum file size of log file
Public Property Let MaxSize(strVal)
    If Debugmode Then On Error Goto 0 Else On Error Resume Next
	fMaxSize = Cint(strVal) * 1048576
End Property
		Public Property Get MaxSize()
    		If Debugmode Then On Error Goto 0 Else On Error Resume Next
			MaxSize = fMaxSize
		End Property

'---------------------------------------------------------
'Boolean property to determine if the filehandle is in use
Private Property Get bOpen()
	If Debugmode Then On Error Goto 0 Else On Error Resume Next	
		If IsObject(Filehandle) Then
			bOpen = True
		Else
			bOpen = False
		End If
End Property

'---------------------------------------------------------
Public Property Let LogEvent( blValue)
'Bool property that dictates event viewer rights
	BoolEvent = blValue
End Property
	Private Property Get LogEvent()
		LogEvent = BoolEvent
	End Property
	
Public Property Let RemoteLog( blValue)
'Bool property that dictates if logging occurs to remote location
	BoolRemote = blValue
End Property
	Private Property Get RemoteLog()
		RemoteLog = BoolRemote
	End Property
Public Property Let RemotePath( strPath)
		If (InStr(StrReverse(strpath),"\")) <> 1 Then
			strRFPath = strPath & "\"
		Else
			strRFPath = strPath
		End If
End Property
	Public Property Get RemotePath()
		RemotePath = strRFPath
	End Property

'---------------------------------------------------------
Private Sub RemoteErrBuffer( strKey,  strItem)
'Method to concactenate new items under one key at the end of the string
    If Debugmode Then On Error Goto 0 Else On Error Resume Next
		Dim concat

	If Not oDict.Exists(strKey) Then
		Call oDict.Add(strkey, stritem)
	Else
		concat = oDict.Item(strKey)
		concat = concat & "|:|" & strItem
			oDict.Remove(strKey)
		Call oDict.Add(strKey,concat)
	End If

End Sub

'---------------------------------------------------------
Public Function ErrBuffer()
'Method to return contents of the error buffer
    If Debugmode Then On Error Goto 0 Else On Error Resume Next
    
    Dim ItemToSplit, ItemArray, item
	
	ItemToSplit = oDict.item("remotelog")
	
	ItemArray = Split(ItemToSplit, "|:|")
	
	ErrBuffer = ItemArray
	
End Function
'---------------------------------------------------------
Public Sub WriteRemote(strVal)
    If Debugmode Then On Error Goto 0 Else On Error Resume Next
    	
	If Not CreateRemote Then
		Exit Sub
	End If

		rFilehandle.WriteLine strVal

End Sub

'---------------------------------------------------------
'Subroutine for creating the remote log file and instantiating the handle
Private Function CreateRemote()
    If Debugmode Then On Error Goto 0 Else On Error Resume Next

	Dim FileProperty,Logsize

	CreateRemote = False

	If Not oFso.FolderExists(RemotePath) Then 
		Call Write(RemotePath & " Does not exist, or is unreachable.",3)
			Exit Function
	End If 

	If Not oFSo.FileExists(RemotePath & File) Then
		oFso.CreateTextFile(RemotePath & File)
	Else
		oFSo.DeleteFile(RemotePath & File)
			oFso.CreateTextFile(RemotePath & File)
	End If    
		If Not IsObject(rFilehandle) Then
	       	Set rFileHandle = oFSo.OpenTextFile(RemotePath & File, _
	       	 ForAppending, True)
	    End If
	If oFSo.FolderExists(RemotePath) Then
		CreateRemote = True
	End If
End Function

'---------------------------------------------------------
'Subroutine for writing log entries
Public Function Write( msg,  mtype)
    If Debugmode Then On Error Goto 0 Else On Error Resume Next
	Dim msgline, etype
		Call Create()

		If Not bOpen Then
			Call Create()
		End If
    msgline = "<![LOG["&msg&"]LOG]!><time="&""""&DatePart("h",Time) _
    &":"&DatePart("n",Time)&":"&DatePart("s",Time)&".000+0"""&" date=""" _
    &Replace(Date,"/","-")&""""&" component="""&Left(WScript.ScriptName, _
    Len(WScript.ScriptName)-Len(".vbs"))&""" context="""" type="""&mtype _
    &""" thread="""" file="""&Left(WScript.ScriptName,Len(WScript.ScriptName)_
    -Len(".vbs"))& """>"

	Filehandle.WriteLine msgline


		Select Case Mtype
			Case 1
				etype = 0
			Case 2
				etype = 2
						If LogEvent Then
					oWShell.LogEvent etype, msg
						End If 
					Call RemoteErrBuffer("remotelog", msg  & "," & "2")
			Case Else
				etype = 1
						If LogEvent Then
					oWShell.LogEvent etype, msg
						End If
					Call RemoteErrBuffer("remotelog", msg  & "," & "1")
		End Select
	
End Function

'---------------------------------------------------------
'Subroutine for rolling over log file at file size limit
Private Sub Rollover()
    If Debugmode Then On Error Goto 0 Else On Error Resume Next
	If bOpen Then
		Filehandle.Close
	End If
	oFso.CopyFile FullFileName, Left(FullFileName,(Len(FullFileName)-1)), True
		oFSo.DeleteFile FullFileName
	Set FileHandle = oFSo.OpenTextFile(FullFileName, ForAppending, True)			
End Sub

'---------------------------------------------------------
'Subroutine for creating the log file and instantiating the handle
Private Sub Create()
    If Debugmode Then On Error Goto 0 Else On Error Resume Next

	Dim FileProperty,Logsize

	If Not oFSo.FileExists(FullFileName) Then
		oFSo.CreateTextFile(FullFileName)
	End If    
		If Not bOpen Then
	       	Set FileHandle = oFSo.OpenTextFile(FullFileName, ForAppending, True)
	    End if
	Set FileProperty = oFSo.GetFile(FullFileName)
				Logsize = FileProperty.size
	If Logsize > MaxSize Then
			Filehandle.WriteLine "\\\\\\\\\\File Size Reached//////////"
		Call Rollover()
	End If
End Sub
End Class

I built a scripting dictionary wrapper as well which was inspired by work from Dan Thomson in his health check script. I’ll most likely post it next after I feel it’s complete.

If you found this object helpful, or otherwise, I would appreciate it if you rated it on script center.

So I ran into a rather straight forward script request recently that turned into a ton of research for what is a very simple solution in the end. I needed to disable anonymous FTP on multiple XP machines. Now normally this wouldn’t have been an issue except that they are using version 5.1 of IIS.

Why is that a problem?

Well it wasn’t until IIS version 6.0 that WMI components were added.  Generally finding support information for these types of tasks can be the lion share of the work anyway, and in this case; that was most certainly the outcome.

In the end I found a comprehensive list of IIS metabase properties (the original list read as if they were only accessible via WMI which I found later to be untrue) and I was free to begin exploring them all individually and building some very simple scripts to modify our server properties.

First, lets disable anonymous FTP since that’s what started us on this:

Set oFtpServer = GetObject("IIS://localhost/MSFtpsvc")
	oFtpServer.Put "AllowAnonymous", 0 
	oFtpServer.SetInfo

 

Now for our VDs I’d like to disable anonymous while utilizing the integrated windows authentication instead.

Set oWebServer = GetObject("IIS://localhost/W3SVC")
	owebserver.put "authflags", 4
	owebserver.setinfo

 

Simple enough, instantiate the object, put a property change in, then commit the change.  In these examples I’m setting the changes at the root, but it’s possible to drill them down to specific VDs by finishing the full path when instantiating.

Thankfully technet saved my behind after some digging, but here’s the list of Metabase Properties for anyone reading this who might find themselves at a point of frustration over dealing with legacy IIS installs.

Be sure to also familiarize yourself with the data types etc if this is new to you.

Update: Code has been re-written from the previous post to make full use of WMI as the means of execution, and scripting dictionary to simplify reporting and controls

Alright, so I’ve got a bit of a problem.

A large volume of data_loader kick backs, and resynch requests.  The problem is, for one reason or another, the clients inventory data is being held up.  Sure, plenty of machines are only missing records for maybe 3, 4, or 5 days.  However I’ve started to see machines that aren’t getting a record in for about 15 days or more.

This shall not stand!  Mind you I’m not going to fix these one by one either.  So what now?

Script!

So I begin to dive into the CCM namespace looking for a method to force a full inventory. Which brings me to the InvAgt class.  Great!  But wait, I don’t see any method for just forcing an inventory.

This is no longer the object used in the script, but leaving here for reference:

I’ll save you the rest of the story and just get to what I found.  According to Technet, the best way to “script” the solution is to delete the InventoryActionID from the InventoryActionStatus in WMI and use the CPApplet COM automation class to invoke the rescan.

They were also nice enough to provide their own code so it was made increasingly more simple to build the following script as I already had the core functionality objects defined for me.  Now, I needed to weaponize code for deployment…. Perhaps weaponize isn’t the best term, but I needed it to deploy and do some work via SCCM.

So I decided to allow 4 arguments and 2 switches that would allow me to perform:

  1. Hardware Inventory (hinv)
  2. Software Inventory (sinv)
  3. Discovery Data (ddr)
  4. File Collection (file)

 

I also wanted to be able to fire this off in 2 methods, one to clear the old inventory instance ID information and force a full inventory or just force an inventory.  I also wanted to be able to run this in a verbose mode to check and see if all actions were being completed as requested.  So I added the following switches:

  1. /full
  2. /debug

 

So the end result is the ability to build collections based off of machines who haven’t reported to one or more inventory types for X number of days and have the script deployed to them auto-magically with the corresponding inventory type.

I also set the script so someone could use it ad-hoc on a machine by double-clicking.  The default behavior is to NOT delete the IDs but to initialize all 4 of the inventory calls.  I also provided CONST declarations at the top so anyone could add or remove what they want in there (they will however need to modify the arrays arrinvtypes and arrkeys to match as well as the select case statement to add additional arguments).

For people that don’t give a hoot about SCCM (there are plenty of you, shockingly enough) feel free to examine the way the loops and conditionals are being used in this script.  They coorelate back to the fundamentals I have covered previously in my stick to the script series.

  1. Stick to the script…
  2. Stick to the script #!/BIN/BASH…
  3. Stick to the script CreateObject(“Wscript.Shell”)…
  4. Stick to the script PS C:> PowerShell…

 

Finally, the code…..

'||============================||
'||Author Daniel Belcher       ||
'||Full HInv                   ||
'||Date 9/22/2011			   ||
'||Updated 11/7/12             ||
'||============================||
'==================================================================================
'Usage: For any prefered deployment method
'This script can:
'	1) Initiate Soft, Hard, Discover, File Collection on a client PC
'	2) Clear agent inv cache and perform full collection
'
'	cscript.exe inventory.vbs
'
'It's possible to target single inventory types or cherry pick them as needed.
'Accepted arguements are:
'1) hinv (performs a hardware inventory)
'2) sinv (performs a software inventory)
'3) ddr  (performs a discovery data)
'4) file (performs a file collection)
'
'Accepted switches are:
'1) /full (blows away all inv cache for declared inventories and initiates request)
'2) /debug (runs the script verbose to check things are running)
'Using /full or /debug it's best to target an inventory type, but not required.
'
'All arguements and switches are case insensitive.
'
'|Objects, Variables, and Constants ***********************************************
'==================================================================================
'Constant strings for cleanup and initialization subroutines.
'==================================================================================
Option Explicit
Dim oWShell,oWmiLoc,oDict,Args,sArgs,nArgs
Dim Item,x,Full,DEBUGMSG
	Set Args 	= Wscript.Arguments
	Set	NArgs 	= Wscript.Arguments.Named
    Set oWShell = CreateObject("WScript.Shell")
    Set oWmiLoc = CreateObject("WbemScripting.SWbemLocator")
    Set oDict 	= CreateObject("Scripting.Dictionary")
'Add additional INV types here with IDs and update Case & dictionary key item pairs
Const sHinv = "Hardware Inventory Collection Cycle"	
	CONST Hinv = "{00000000-0000-0000-0000-000000000001}":oDict.Add sHinv, Hinv
Const sSinv = "Software Inventory Collection Cycle"
	CONST Sinv = "{00000000-0000-0000-0000-000000000002}":oDict.Add sSinv, Sinv
Const sDDR  = "Discovery Data Collection Cycle"
	CONST DDR  = "{00000000-0000-0000-0000-000000000003}":oDict.Add sDDR,  DDR
Const sFile = "Standard File Collection Cycle"
	Const File = "{00000000-0000-0000-0000-000000000010}":oDict.Add sFile, File
'|Main Run ***********************************************************************
'=================================================================================
'Checking for Named arguments
If NArgs.Exists("Full") Then
	Full = TRUE
		Else
	Full = FALSE
End If
		if NArgs.Exists("debug") Then
				DEBUGMSG = TRUE
					Else
				DEBUGMSG = FALSE
		End If
'Check for Unnamed arguments
If Args.Unnamed.Count > 0 Then
	If DEBUGMSG then wscript.echo "Unnamed arguments Found..."
	'Looping arguments looking for declared Inv types
	For Each item In Args
		Select Case lcase(item)
			Case "hinv"
				If Full Then
					DelInvActionID sHinv
						wscript.sleep 1500
				End If
					RunInvAction sHinv
			Case "sinv"
				If Full Then
					DelInvActionID sSinv
						wscript.sleep 1500
				End If
					RunInvAction sSinv
			Case "ddr"
				If Full Then
					DelInvActionID sDDR
						wscript.sleep 1500
				End If
					RunInvAction sDDR
			Case "file"
				If Full Then
					DelInvActionID sFile
						wscript.sleep 1500
				End If
					RunInvAction sFile
		End Select
	Next
else
	'If no Unnamed arguments then loop through appropriate arrays
	'based on /full or not
	If DEBUGMSG Then wscript.echo "No Unnamed Arguments found, " _
	&"checking for full switch..."
	If Full Then
		If DEBUGMSG Then wscript.echo "/Full switch used, " _
		&"clearing caches..."
		For Each item in oDict.Keys
			DelInvActionID item
				wscript.sleep 1500
		Next
	End If
		If DEBUGMSG Then wscript.echo "Initiating HINV, SINV, DDR, " _
		&"and File Collection now..."
	For Each item in oDict.Keys
		RunInvAction item
			wscript.sleep 1500
	Next
End If
Report
Wscript.Quit(0)
'SubRoutines *********************************************************************
'=================================================================================
'Sub to delete the InventoryActionID as requested
Sub DelInvActionID(guid)
		On Error Resume Next
	Set oInvAgt = oWmiLoc.ConnectServer(,"root\ccm\invagt")
			oInvAgt.Delete "InventoryActionStatus.InventoryActionID='"& _
				oDict.Item(guid)&"'"
	If DEBUGMSG Then wscript.echo "Clearing "& _
		oDict.Item(guid)&" from InventoryActionStatus"
End Sub
'*********************************************************************************
'Sub to initialize Inventory Cycle requested
Sub RunInvAction(name)
		On Error Resume Next
Dim smsclient:Set smsclient=GetObject("winmgmts://./root/ccm:sms_client")
	smsclient.triggerschedule(oDict.Item(name))
		If DEBUGMSG Then wscript.echo "Running "&name
End Sub
'**********************************************************************************
'Sub to report script completion if /debug
Sub Report
         If DEBUGMSG Then wscript.echo "Complete" 'Set for popup on script complete
End Sub
'End ******************************************************************************
'==================================================================================

I haven’t written a scripting post in a while, but I’ve wanted to.  So in keeping with the spirit of my stick to the script posts lets look at something that is common among all scripting languages (even if the syntax isn’t).

Let’s talk about strings…….

 kitty-yarn

Awwww, but no.  These kind of strings.  In the case of scripting, I think the best way to think about it is, text, what you are reading or able to read.  They aren’t used mathematically (usually), but can and will be a huge component in your scripting.  Especially when automating things around a desktop or server environment.

Oh really?  Yes, really.  General uses for strings in a script are:

  • User messages
  • Reporting or logging
  • Comparisons
  • Explicit paths
  • Application execution
  • Conditionals

Ok, so maybe that list doesn’t look that impressive, but when you consider how much of that is done within a script, it becomes obvious the importance of string values to scripting.  It’s also important to recognize that in certain scripting environments, it’s important to define a string value as such so that it can be properly used. 

(Powershell for instance, requires you to properly define a value type to use the relevant functions… but I’m getting ahead of myself.)

So wait?  There are more than strings in a script?  Yes; Strings, Integers, and Booleans are your standard value types. Integers are numbers (math!) and Booleans are True or False.  So given those value types, perhaps it is a bit more obvious how frequently you will use string values?

So lets get into some sample code and evaluate strings some shall we?


VBScript:

strTest = "Hey, I'm a string value"

wscript.echo strTest
'Shows the string value
wscript.echo strTest&", and I've been concatenated to the value."
'& operator joins values
wscript.echo lcase(strTest)
'Lower case
wscript.echo ucase(strTest)
'Upper case
wscript.echo strReverse(strTest)
'Reverses the string
wscript.echo len(strTest)
'Gives the total length of a string
wscript.echo mid(strTest,10,8)
'Returns fix number of characters in string
wscript.echo left(strTest,11)
'11 chars from the left
wscript.echo right(strTest,13)
'13 chars from the right
'----------------------------------------------------------------
wscript.echo inStr(strTest,"a")
'Returns position of string from left to right
wscript.echo inStrRev(strTest,"a")
'Returns position of string from right to left
'----------------------------------------------------------------
a = split(strTest)
'Splits strTest by it's spaces
for each item in a
    wscript.echo item
   'echoes each dimension from the split array
next

wscript.echo a(3)&" "&a(4)

'echoes the split arrays dimensions 3 and 4
'---------------------------------------------------------------

strReturn=inputbox("Here, you try!")

if strReturn = "" then
   wscript.echo "Fine, don't play along"
else
    wscript.echo "So you said: "&trim(ucase(strReturn))&vbcrlf _
    &"Sheesh, no need to shout!"
end if
'---------------------------------------------------------------


Running the above script will give you a better understanding of what I’m about to explain.  I wanted to show some common functions in vbscript (syntax is different but these will be universal functions you will use).  The above are common string manipulation tools

 

Code explained… line by line

First we are defining our string to a variable strTest.  Now “in the wild” as it were, this string could be pulled from an object property, read from a file, registry, user input, output from another application, etc.  It’s best to define a string to a variable though, no matter the method for input.  This of course is the most direct way to do it for our example

Now we begin with the simplest string usage, output.

Now we raise the stakes a bit by joining an additional string value to our current strTest.  This action is known as concatenation.  This is a very common thing with string usage and manipulation.  Building complex values/messages/logs from various predefined and/or dynamically pulled string values.

Our next two examples have to do with manipulating case between upper and lower.  This is fairly self explanatory, and in the interest of string comparisons it’s usually a good practice (and often necessary) to force a case set, especially if the comparison function is case sensitive.

String reversal, this may not seem important initially, but makes a huge difference when you are forced to chop strings up.  The ability to reverse a string can go a long way for string chopping.  Especially if you are dealing with filenames.

The length function is another that may seem arbitrary to some, but allows for great flexibility as well in chopping up strings such as file names.  If you have a fixed number of characters to remove it’s sometimes simpler then splitting the string.  (so I wasn’t completely honest about the math stuff and strings)

Mid, Left, and Right.  These 3 can be used in conjunction with length to return a fixed number of characters from the left right, or middle (specified) of a string.  Here’s a quick easy example:

test="I am 18 chars long"
wscript.echo test
count=len(test)-4
wscript.echo right(test,count)

Very simply, we take the total length of Test and subtract it by 4, then return the sum of remaining characters from right to left of the original string.  In this example we, had a fixed value to subtract, be mindful you could use the length value of multiple strings to achieve the same type of results.

Now, InStr and InStrRev, or “In String” and “In String Reverse”.  These two functions make for great conditionals.  They, along with strComp, are excellent for determining like strings and taking action.  Especially when parsing through files and directories looking for specific returns. 

One of my favorites, especially in PowerShell, split.  Split takes a string, looks for a delimiter (space by default) and breaks the string up into an array.  Why do I like it so much?  Put simply, it allows you to quickly whittle down long path names into a single filename.  It also allows you to quickly and efficiently modify lists of data into manageable formats.  And last but not least, it can easily turn files like csv’s into an array for manipulation.

Finally, user input.  This is pretty self explanatory.  Prompt for input, receive and control input, use input.


In PowerShell, string functions are called like this:

$a=”This is a string value”

$a.ToString()

$a.ToUpper()

$a.ToLower()

$a.Replace(“a “,”my “)

$a.split(“ “)

$a.contains(“string”)

$a.StartsWith(“t”)

$a.EndsWith(“e”)

$a.Length

$a.CompareTo(“this is a string value”)

$a.Length.Equals(22)

$a.CharArray()

$a.PadLeft(“30”)

$a.PadRight(“30”)

$a.Trim()

$a.TrimEnd()

$a.TrimStart()

Given the previous examples in vbscript, you should be able to easily adapt your knowledge to using these in PowerShell.  The idea and purpose is still the same, again, the syntax is just different.


String manipulation inside Bash is, admitedly, a bit more convoluted so I won’t be touching on it in this post.  However I’d highly recommend an online source like: Mendel Cooper’s guide.  Again, the methodology will still be fairly the same, but the syntax will differ.  The largest issue with Bash is the myriad of ways of performing the string manipulation.

 

Anyway, I hope this has been informative for you.  Good luck, and happy scripting!

I’ve honestly been trying to post my final part to the “stick to the script” series.  However those actions have been soundly thwarted by an abundance of work, and a new workout regiment.  I hope to have it up and syndicated to myitforum before the weekend is over.

I’ve also built a nice sccm client install script, and thanks to John Marcum, I’ve built a nice remote cm wmi repair tool in powershell as well.

So stay tuned as I plan to start releasing those scripts after scrubbing them.