Using VBScript to run Excel macro

I have a folder containing several Excel files, each containing a macro named “FETCH_LATEST_DATA”. The following steps will detail how I had scheduled a VBScript to run these Excel macros nightly.

First, I created the VBScript below that opens each Excel file successively and calls the particular macro. Note that I have disabled alerts and events since I intended to run it as an unattended job, and that I am using “Wscript.Echo” to output some start/end info for debugging purposes should something go wrong in the future.

excelFolder = "\\fileserver\share\folder"

Set objFso = CreateObject("Scripting.FileSystemObject")
Set objFolder = objFso.GetFolder(excelFolder)

Set excelApp = CreateObject("Excel.Application")
excelApp.DisplayAlerts = False
excelApp.EnableEvents = False

for each objFile In objFso.GetFolder(excelFolder).files
	Wscript.Echo "Starting " & objFile.name & " " & now
	
	filename = objFile.name
	
	Set excelBook = excelApp.Workbooks.Open(excelFolder & "\" & filename)

	If excelBook.ReadOnly Then
		Wscript.Echo "Readonly mode detected, skipping"
	Else
		excelApp.Run "FETCH_LATEST_DATA"
		excelBook.Save
	End if

	Wscript.Echo "Ending " & objFile.name & " " & now
	
	excelBook.Close False
	set excelBook = Nothing
Next

excelApp.Quit
Set excelApp = Nothing
set objFso = nothing
WScript.Quit

Next, I inserted a line in my already-existing batch file that contains all the other jobs that I typically run on this Windows server every evening. Note that I am using “>>” to direct any console output (see “Wscript.Echo” above) to a log file should there be any debugging needs.

[...]
C:\windows\syswow64\cscript.exe C:\scripts\update_excel.vbs >> update_excel.log
[...]

Before we can run Excel unattended, we must create these two folders. If these two folders are missing, the scheduled job would sit perpetually in Windows Task Scheduler as if running, but it would never actually complete; in the way that the logging info is setup above, you would see something like “Starting file1.xlsm 6/20/2019 10:15:00 AM” without any additional lines being logged. If your Windows server is running in 32-bit mode, you can ignore the second folder.

C:\Windows\System32\config\systemprofile\Desktop

C:\Windows\SysWOW64\config\systemprofile\Desktop

Running VBScript in 32-bit mode on a 64-bit Windows machine

I have a legacy VBScript program that has been recently been migrated to a new 64-bit Windows server by a system administrator due to a policy set by a higher power. By default, VBScript executed in 64-bit Windows automatically runs as a 64-bit program. Because I have reasons to have this program run in 32-bit mode, I modified my batch file to execute my program in a different way.

This is the BEFORE picture. When this batch file is run in 64-bit Windows, it will execute in 64-bit mode.

time /t
cscript ThisIsMyVbscriptProgram.vbs
time /t

Below is the AFTER picture. Note that I am passing my script into a new command prompt environment in the Windows SYSWOW64 folder; this new environment is strictly in 32-bit mode.

time /t
%WINDIR%SYSWOW64cmd.exe /c cscript ThisIsMyVbscriptProgram.vbs
time /t

As a side note: WOW stands for “Windows on Windows”.

Rollback Oracle transactions in VBScript

The key to allow transactions lies in the command “.BeginTrans” that you should declare immediately after making the database connection, as seen in the example below.

option explicit
dim dbType, dbHost, dbName, dbUser, dbPass

dbHost = "hostname"   ' Hostname of the Oracle database server
dbName = "database"   ' Name of the database/SID
dbUser = "username"   ' Name of the user
dbPass = "password"   ' Password of the above-named user

dim conn
set conn = CreateObject("ADODB.connection")
conn.ConnectionTimeout = 30
conn.CommandTimeout = 30
conn.open("Provider=MSDAORA.1;User ID=" & dbUser & ";Password=" & dbPass & ";Data Source=" & dbName & ";Persist Security Info=False")

conn.BeginTrans

conn.execute("update order set order_total=(select qty * unit_price from order_detail where order_id=123) where order_id=123")

dim checkOrderTotal
set checkOrderTotal = conn.execute("select order_total from order where order_id=123")

if (checkOrderTotal("order_total") < 0) then
	conn.RollbackTrans
	' TODO : Send an email to accounting department to check it out
else
	conn.CommitTrans
end if

conn.close
set conn = nothing

Notice the if-else clause near the bottom. If the order total somehow ended up a negative number, we rollback the transaction (and presumably would notify someone to check it out at this point), we roll back the transaction. Otherwise, as seen in the "else" clause, we proceed with committing the Oracle transaction.

It is important that you must issue either ".RollbackTrans" or ".CommitTrans" before the connection is terminated, otherwise you might get the VBScript ADODB connection error 800A0CAE, "Connection object cannot be explicitly closed while in transaction."

Logging Windows events with VBScript

The VBScript code to perform the insertion into the event log is shown below. Please note that the “event_type” input variable will only take the following three values, in upper case letters:

  • “ERROR” – Use when logging a problem
  • “WARN” – Use when logging a minor issue
  • “INFO” – Use when logging miscellaneous information

When we run the example code further below, we will find the log as seen here.

Example Code

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Description: This sample code demonstrates how to log information 
'       to the Windows application event log
' Author: C. Peter Chen, http://dev-notes.com
'
' Note: Allowable event_type values:
'         "ERROR" - Use when logging a problem
'         "WARN"  - Use when logging a minor issue
'         "INFO"  - Use when logging miscellaneous information
'
' Revision History:
'	1.0	20090310	Original release
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
option explicit

sub logWindowsEventViewer(event_type, event_text)
	dim WshShell, tp
	
	select case event_type
	case "ERROR"
		tp = "1"
	case "WARN"
		tp = "2"
	case "INFO"
		tp = "4"
	case else
		tp = "0"
	end select

	set WshShell = CreateObject("WScript.Shell")
	wshshell.Logevent tp, event_text
	set wshshell=nothing
end sub

' Usage example:
dim event_type, event_text
event_type = "ERROR"
event_text = "Application error: GUI crashed!"

call logWindowsEventViewer(event_type, event_text)

Building an AIM buddy list from your database employee table with VBScript

The code below can be used against Oracle, SQL Server, or MySQL database table to automatically create an AIM buddy list. The SQL statement should select out a “group_name” field (ideas: a department name such as “Accounting” or “Purchasing”, an office location such as “Taipei” or “New York”, etc.) and an “aim_name” field. The “group_name” will be used as AIM groups, while “aim_name” are the users’ actual registered AIM names. The output file is a flat text file in a format that can be imported into AIM.

'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' AIM Buddy List Builder                                        '
'                                                               '
' Description: Builds an AIM buddy list from your database      '
'      employee table.                                          '
' Author: C. Peter Chen                                         '
' Version Tracker:                                              '
'       1.0   20081021   Base version                           '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
option explicit
dim dbType, dbHost, dbName, dbUser, dbPass, outputFile, sqlstr

'''''''''''''''''
' Configuration '
'''''''''''''''''
dbType = "oracle"                ' Valid values: "oracle", "sqlserver", "mysql"
dbHost = "hostName"              ' Hostname of the database server
dbName = "dbName"                ' Name of the database/SID
dbUser = "user"                  ' Name of the user
dbPass = "password"              ' Password of the above-named user
outputFile = "c:buddylist.blt"  ' Path and file name of the output CSV file

' SQL statement below; be sure to select out "group_name" and "aim_name" in your SQL statement.
sqlStr = "select department_name as group_name, aim_name from employees_table where aim_name is not null order by department_name, aim_name"
'''''''''''''''''''''
' End Configuration '
'''''''''''''''''''''

dim fso, conn

'Create filesystem object 
set fso = CreateObject("Scripting.FileSystemObject")

'Database connection info
set Conn = CreateObject("ADODB.connection")
Conn.ConnectionTimeout = 30
Conn.CommandTimeout = 30
if dbType = "oracle" then
	conn.open("Provider=MSDAORA.1;User ID=" & dbUser & ";Password=" & dbPass & ";Data Source=" & dbName & ";Persist Security Info=False")
elseif dbType = "sqlserver" then
	conn.open("Driver={SQL Server};Server=" & dbHost & ";Database=" & dbName & ";Uid=" & dbUser & ";Pwd=" & dbPass & ";")
elseif dbType = "mysql" then
	conn.open("DRIVER={MySQL ODBC 3.51 Driver}; SERVER=" & dbHost & ";PORT=3306;DATABASE=" & dbName & "; UID=" & dbUser & "; PASSWORD=" & dbPass & "; OPTION=3")
end if

dim a, showList, prevGroup

set a = fso.createtextfile(outputFile)
a.writeline ("Config {")
a.writeline (" version 1")
a.writeline ("}")
a.writeline ("User {")
a.writeline (" screenName dummyAimName")
a.writeline ("}")
a.writeline ("Buddy {")
a.writeline (" list {")

set showList = conn.execute(sqlstr)

prevGroup = "placeholder"
do while not showList.eof
	if (showList("group_name") <> prevGroup) then
		if (prevGroup <> "placeholder") then
			a.writeline ("  }")
		end if
		a.writeline ("  """ + showList("group_name") + """ {")
	end if
	a.writeline ("   " + showList("aim_name"))
	prevGroup = showList("group_name")
	showList.movenext
loop
showList.close
set showList = nothing

a.writeline ("  }")

a.writeline (" }")
a.writeline ("}")

' Close
set a = nothing
set fso = nothing
conn.close
set conn = nothing

'You're all done!!  Enjoy the file created.
msgbox("AIM Buddy List Created!")

Interested in obtaining a generic AIM buddy list import file format? Please see below for an illustration with fictional data.

Config {
 version 1
}
User {
 screenName dummyAimName
}
Buddy {
 list {
  "Accounting" {
   MrCFO_fictionalUser
   BobAtAccounting_fictionalUser
   JaneDoe_fictionalUser
  }
  "Purchasing" {
   LewisTheBuyer_fictionalUser
  }
  "Useless Employees" {
   PaulJohnson_fictionalUser
  }
 }
}

Checking for unauthorized local Windows administrator group members with VBScript

The only item to configure is the arrRealAdmins array of strings, where you may put in a list of user names that you do not wish to show in the report. The example already include two common administrator names that should be valid, “Administrator” and “Domain Administrators”.

Note that the sample code below outputs the invalid local administrator group members in a msgbox() pop-up box. You may wish to substitute this output method with something that may be more useful to you, such as outputting them to a report, write into a database, send email, etc.

'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' List local admin users                                        '
'                                                               '
' Description: Finds a list of local admin users on a Windows   '
'     machine                                                   '
' Author: C. Peter Chen, http://dev-notes.com                   '
' Version Tracker:                                              '
'       1.0   20081021   Base version                           '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

option explicit
dim arrRealAdmins

'''''''''''''''''
' Configuration '
'''''''''''''''''
arrRealAdmins = Array("Administrator","Domain Admins")  ' List of users that *are* supposed to be administrators; we'll ignore these people later

'''''''''''''''''''''
' End configuration '
'''''''''''''''''''''

dim adminGroup, groupMember, ret

function isPermitedAdmin(MemberName)
	dim i
	for i = lbound(arrRealAdmins) to ubound(arrRealAdmins)
		if ucase(MemberName) = ucase(arrRealAdmins(i)) then
			isPermitedAdmin = true
			exit function
		end if
	next

	isPermitedAdmin = false
end function

set adminGroup = getObject("WinNT://./Administrators, group")
for each groupMember in adminGroup.members
	if not isPermitedAdmin(groupMember.name) then
    		ret = ret & groupMember.name & ","
	end if
next

if ret = "" then
	msgbox("No invalid local administrators found.")
else
	ret = mid(ret, 1, len(ret)-1) ' To get rid of the last comma
	msgbox("The following users are in the local admin group: " & vbcrlf & ret)
end if

Sending emails using VBScript

To do so, follow the sample code shown below. To use the code as-is, just alter the configuration parameters by providing the from address, to address, subject, body, and SMTP server information. Optionally, you may also provide a path to a file that you want to attach to the email. Usually SMTP port number is 25; if you are unsure, leave this parameter untouched and it will probably work at the default value of 25.

option explicit

dim fromAddress, toAddress, subj, body, smtp, attach, smtpPort

'''''''''''''''''
' Configuration '
'''''''''''''''''

' Required parameters
fromAddress = "my@email.address"      ' The from email address
toAddress = "recepient@email.address" ' The to email address
subj = "Email Subject"                ' The subject of the email
body = "Put a message here!"          ' The body message of the email
smtp = "mail.server.com"              ' Name of the SMTP server you wish to use

' Optional parameters
attach = "c:filename.txt"            ' Optional file you may wish to attach to the email
smtpPort = 25                         ' SMTP port used by your server, usually 25; if not provided, the script will default to 25

'''''''''''''''''''''
' End Configuration '
'''''''''''''''''''''


if fromAddress = "" then
	msgbox("Error: From email address not defined")
elseif toAddress = "" then
	msgbox("Error: To email address not defined")
elseif subj = "" then
	msgbox("Error: Subject not defined")
elseif body = "" then
	msgbox("Error: Body not defined")
elseif smtp = "" then
	msgbox("Error: SMTP server not defined")
else
	dim objMessage
	Set objMessage = CreateObject("CDO.Message")
	objMessage.Subject = subj
	objMessage.From = fromAddress
	objMessage.To = toAddress
	objMessage.TextBody = body
	if attach <> "" then
		objMessage.AddAttachment attach
	end if
	
	objMessage.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
	objMessage.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/smtpserver") = smtp
	if smtpPort <> "" then
		objMessage.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = smtpPort
	else
		objMessage.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 25
	end if
	objMessage.Configuration.Fields.Update
	
	objMessage.Send
	
	msgbox("Email sent successfully")
end if

Writing SQL output to CSV with VBScript

Enter your specific information in the “Configuration” section near the top of the script. For the “dbType” variable, the only accepted values are “oracle”, “sqlserver”, or “mysql”. Once this is done, just run the script and you should have your quote-delimited comma-separated CSV file!

The email-related variables are optional. To enable the emailing functionality (send the generated CSV file to the address as an attachment), enter the recipient email address in the box. If you do not wish to email, just leave that variable as empty string (“”), and the other email related variable such as smtp and smtpPort will be ignored.

'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Data Exporter                                                 '
'                                                               '
' Description: Allows the output of data to CSV file from a SQL '
'       statement to either Oracle, SQL Server, or MySQL        '
' Author: C. Peter Chen, http://dev-notes.com                   '
' Version Tracker:                                              '
'       1.0   20080414 Original version                         '
'	1.1   20080807 Added email functionality                '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
option explicit
dim dbType, dbHost, dbName, dbUser, dbPass, outputFile, email, subj, body, smtp, smtpPort, sqlstr

'''''''''''''''''
' Configuration '
'''''''''''''''''
dbType = "oracle"                 ' Valid values: "oracle", "sqlserver", "mysql"
dbHost = "dbhost"                 ' Hostname of the database server
dbName = "dbname"                 ' Name of the database/SID
dbUser = "username"               ' Name of the user
dbPass = "password"               ' Password of the above-named user
outputFile = "c:\output.csv"      ' Path and file name of the output CSV file
email = "email@me.here"           ' Enter email here should you wish to email the CSV file (as attachment); if no email, leave it as empty string ""
subj = "Email Subject"            ' The subject of your email; required only if you send the CSV over email
body = "Put a message here!"      ' The body of your email; required only if you send the CSV over email
smtp = "mail.server.com"          ' Name of your SMTP server; required only if you send the CSV over email
smtpPort = 25                     ' SMTP port used by your server, usually 25; required only if you send the CSV over email
sqlStr = "select user from dual"  ' SQL statement you wish to execute
'''''''''''''''''''''
' End Configuration '
'''''''''''''''''''''



dim fso, conn

'Create filesystem object 
set fso = CreateObject("Scripting.FileSystemObject")

'Database connection info
set Conn = CreateObject("ADODB.connection")
Conn.ConnectionTimeout = 30
Conn.CommandTimeout = 30
if dbType = "oracle" then
	conn.open("Provider=MSDAORA.1;User ID=" & dbUser & ";Password=" & dbPass & ";Data Source=" & dbName & ";Persist Security Info=False")
elseif dbType = "sqlserver" then
	conn.open("Driver={SQL Server};Server=" & dbHost & ";Database=" & dbName & ";Uid=" & dbUser & ";Pwd=" & dbPass & ";")
elseif dbType = "mysql" then
	conn.open("DRIVER={MySQL ODBC 3.51 Driver}; SERVER=" & dbHost & ";PORT=3306;DATABASE=" & dbName & "; UID=" & dbUser & "; PASSWORD=" & dbPass & "; OPTION=3")
end if

' Subprocedure to generate data.  Two parameters:
'   1. fPath=where to create the file
'   2. sqlstr=the database query
sub MakeDataFile(fPath, sqlstr)
	dim a, showList, intcount
	set a = fso.createtextfile(fPath)
	
	set showList = conn.execute(sqlstr)
	for intcount = 0 to showList.fields.count -1
		if intcount <> showList.fields.count-1 then
			a.write """" & showList.fields(intcount).name & ""","
		else
			a.write """" & showList.fields(intcount).name & """"
		end if
	next
	a.writeline ""
	
	do while not showList.eof
		for intcount = 0 to showList.fields.count - 1
			if intcount <> showList.fields.count - 1 then
				a.write """" & showList.fields(intcount).value & ""","
			else
				a.write """" & showList.fields(intcount).value & """"
			end if
		next
		a.writeline ""
		showList.movenext
	loop
	showList.close
	set showList = nothing

	set a = nothing
end sub

' Call the subprocedure
call MakeDataFile(outputFile,sqlstr)

' Close
set fso = nothing
conn.close
set conn = nothing

if email <> "" then
	dim objMessage
	Set objMessage = CreateObject("CDO.Message")
	objMessage.Subject = "Test Email from vbs"
	objMessage.From = email
	objMessage.To = email
	objMessage.TextBody = "Please see attached file."
	objMessage.AddAttachment outputFile
	
	objMessage.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
	objMessage.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/smtpserver") = smtp
	objMessage.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = smtpPort
	
objMessage.Configuration.Fields.Update
	
	objMessage.Send
end if

'You're all done!!  Enjoy the file created.
msgbox("Data Writer Done!")

Generating a report of user password expiration status

To start off, check out the setup section somewhat near the top. They should be fairly explanatory, with the only potentially tricky one being the “ouList” variable, which holds a list of Organization Units whose users you which to see in your report; it should be a tilde (“~”) delimited string. Some examples:

ou=contractors
ou=engineers,ou=contractors
ou=contractors~ou=engineers,ou=contractors

Compare example 1 and example 2; because the code is not meant to go into deeper levels, “ou=contractors” will only check for user objects at that particular level only. To include the nested OU “engineers”, it must be explicitly listed, such as in example 2.

The full sample is below:

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Description: Makes a CSV listing of password expiration status
' Author: C. Peter Chen, http://dev-notes.com
' Revision History:
'	1.0	20080318	Original concept written
'	1.0.1	20080319	Added column headings in the output
'				Added "excludeNoOu" switch to exclude objects 
'				  not in an Organizational Unit as a possible 
'				  way to exclude system objects like IWAM and 
'				  IUSR from output.
'	1.0.2	20080325	Added the "ouList" delimited string the 
'				  restriction of specific OUs to show up in 
'				  the report.
'				Added "debugMode" switch
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

option explicit
dim maxPwdAge, warningThreshold, ad, outputPath, outputFileName, keepDailyOutput, excludeNoOu, ouList, debugMode

''''''''''''''
' Some Setup '
''''''''''''''
maxPwdAge = 45			' What is the maximum age (in days) of passwords in your domain?
warningThreshold = 10		' The report will show warning (almost expired) status if expiration is within this many days.
ad = "dc=domain,dc=com"		' What is your domain? Example format: "dc=domainname,dc=com"
outputPath = "\\share\folder\"	' The path for the output CSV report with slash at the end; examples: "c:\", "c:\reports\", "\\fileshare\folder\"
outputFileName = "reportName"	' The file name of the output CSV report; no need for ".csv" at the end
keepDailyOutput = "N"		' Valid values include Y or N.  If Y, a new report will be created each time with file name format "filenameYYYY-MM-DD-HH24-MI.csv"; if N, each day's report will overwrite the previous day's as "filename.csv".
excludeNoOu = "Y"		' Valid values include Y or N.  If Y, objects not in an Organizational Unit will not appear in the report; it maybe a way to exclude system objects like IWAM and IUSR.

' ouList is a list of OUs, delimited by tilde ("~"), whose users will appear in the report
' Sample: "ou=sales,ou=fulltimers~ou=engineers,ou=contractors"
ouList = "ou=sales,ou=fulltimers~ou=engineers,ou=contractors"

debugMode = "N"			' If Y, any encountered errors will pop up on screen and a pop up will appear when the script completes

'''''''''''''
' End Setup '
'''''''''''''


''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''


'''''''''''''
' Constants '
'''''''''''''
Const ADS_UF_DONT_EXPIRE_PASSWD = &h10000

'''''''''''''
' Functions '
'''''''''''''
dim i, foundUser

function getAdObject (strUserFullName, ad, ouListArray)
	On Error Resume Next
	err.clear

	Set objUser = GetObject("LDAP://cn=" & strUserFullName & "," & ouListArray(0) & "," & ad)

	if err.number <> 0 then
		foundUser = "N"
		i = 1 ' Yes, start at the second one...
		do while i <= UBound(ouListArray)
			if err.number = -2147016656 then
				foundUser = "N"
				Set objUser = GetObject("LDAP://cn=" & strUserFullName & "," & ouListArray(i) & "," & ad)
			else
				foundUser = "Y"
				exit do
			end if
			i = i + 1
		loop
	else
		foundUser = "Y"
	end if
	
	if err.number <> 0 and foundUser = "Y" then
		exit function
	else
		getAdObject = objUser
		err.clear
		exit function
	end if
end function

'''''''''''''''''''''''''''''''
' The Main Part of the Script '
'''''''''''''''''''''''''''''''
dim fso, f, objConnection, objCommand, objRecordSet, intUAC, objUser, dtmValue, intTimeInterval

set fso = CreateObject("Scripting.FileSystemObject")

if keepDailyOutput="Y" then
	filedate=year(now) & "-" & month(now) & "-" & day(now) & "-" & hour(now) & "-" & minute(now)
	set f = fso.createtextfile(outputPath & outputFileName & filedate & ".csv")
else
	set f = fso.createtextfile(outputPath & outputFileName & ".csv")
end if

f.writeline("""User Name"",""Password Status"",""Password Age (Days)"",""LDAP Distinguished Name""")

Set objConnection = CreateObject("ADODB.Connection")
objConnection.Open "Provider=ADsDSOObject;"
Set objCommand = CreateObject("ADODB.Command")
objCommand.ActiveConnection = objConnection
objCommand.CommandText = ";(objectCategory=User);userAccountControl,distinguishedName,name;subtree"
Set objRecordSet = objCommand.Execute

dim ouListArray
ouListArray = split(ouList,"~",-1,1)

Do Until objRecordset.EOF
	if (excludeNoOu = "Y" and instr(objRecordset.Fields("distinguishedName"),"OU=") = 0) then ' Do not show user not in an OU; this may exclude system users like IWAM and IUSR
		' Do nothing and skip this user
	else
		intUAC=objRecordset.Fields("userAccountControl")

		If intUAC AND ADS_UF_DONT_EXPIRE_PASSWD Then ' Password never expires for this user
			on error resume next
			
			set objUser = nothing
			getAdObject objRecordset.Fields("name"), ad, ouListArray

			dtmValue = objUser.PasswordLastChanged ' Just to test if error occurs
			
			if err.number = 0 then
				f.writeline("""" & objRecordset.Fields("name") & """,""never expire"","""",""" & objRecordset.Fields("distinguishedName") & """")
			else ' Error encountered...
				if debugMode = "Y" then
					msgbox(objRecordset.Fields("name") & "error. err.number='" & err.number & "', err.description='" & err.description & "'")
				else
					' Not in debug mode, so we suppress the error and do nothing
				end if
			end if
		else ' Password will expire for this user
			set objUser = nothing
			getAdObject objRecordset.Fields("name"), ad, ouListArray

			if not (objUser is nothing) then
				if objUser.AccountDisabled = false then ' show ENABLED users only
					on error resume next

					dtmValue = objUser.PasswordLastChanged 

					if err.number = 0 then
						intTimeInterval = int(now - dtmValue)
						if intTimeInterval >= maxPwdAge then
							f.writeline("""" & objRecordset.Fields("name") & """,""expired"","""& intTimeInterval & """,""" & objRecordset.Fields("distinguishedName") & """")
						elseif intTimeInterval >= (maxPwdAge - warningThreshold) then
							f.writeline("""" & objRecordset.Fields("name") & """,""expiring soon"","""& intTimeInterval & """,""" & objRecordset.Fields("distinguishedName") & """")				
						else
							f.writeline("""" & objRecordset.Fields("name") & """,""ok"","""& intTimeInterval & """,""" & objRecordset.Fields("distinguishedName") & """")
						end if
					else ' This user's password is set to force-change at next logon
						'if objUser.name <> "" then ' We still need to make sure this user is in the list of OUs we want to check
							f.writeline("""" & objRecordset.Fields("name") & """,""forced to change at next logon"","" "",""" & objRecordset.Fields("distinguishedName") & """")
						'end if
						err.clear
					end if
				else
					' Do nothing for DISABLED users
				end if
			else
				' Do nothing for users not found; probably in a different OU then specified.
			end if
		end if
	end if
	
	objRecordset.MoveNext
Loop
 
objConnection.Close

if debugMode = "Y" then
	msgbox("AD user password expiration script completed.  Report = " & outputPath & outputFileName & ".csv")
end if

Checking admin share free space and folder space usage

The code to perform these two space checks is below, with the output file being generated in comma-delimited CSV format. When we use this, we need to first set a few configuration items in the first section.

  • outputFile : Enter the path and file name of the output report.
  • i : The number of items you wish to check; in the example, the number is 4 (2 drives and 2 folders)
  • arrayList(x, y) : These are the details of each drive/folder you wish to check…

The code is as follows:

option explicit
dim outputFile, arrayList, fso, obj, fl, j, drivepath, driveName, status, msg
'''''''Config''''''''''

' Where do you want to output the results to?
outputFile = "checkSpace.log"

' How many drives do you want to check for free space?
const i = 4
redim arrayList(i,5) 'Please do not touch this line.

' List the server drives you want to check for free space or folders to check for space usage
' Each set has 5 values:
' a. D=drive, F=folder
' b. server hostname
' c. admin share drive letter (applicable for type D only)
' d. warning level in gb
' e. alarm level in gb
' f. common name of this share

arrayList(0,0) = "D"
arrayList(0,1) = "web1"
arrayList(0,2) = "c"
arrayList(0,3) = 10
arrayList(0,4) = 5
arrayList(0,5) = "Windows 2003 web server, C-drive"

arrayList(1,0) = "D"
arrayList(1,1) = "exch2"
arrayList(1,2) = "d"
arrayList(1,3) = 200
arrayList(1,4) = 100
arrayList(1,5) = "Windows 2003 Exchange server, D-drive"

arrayList(2,0) = "F"
arrayList(2,1) = "\fileserverpublicfileShare1"
arrayList(2,2) = ""
arrayList(2,3) = 1
arrayList(2,4) = 2
arrayList(2,5) = "File share 1"

arrayList(3,0) = "F"
arrayList(3,1) = "\fileserverpublicfileShare2"
arrayList(3,2) = ""
arrayList(3,3) = 100
arrayList(3,4) = 200
arrayList(3,5) = "File share 2"

'''''''End Config'''''''''

set fso = CreateObject("Scripting.FileSystemObject")
set fl = fso.CreateTextFile(outputFile, true)

fl.writeline("""Item"",""Status"",""Message""")

j = 0
do while j <= i-1
	if arrayList(j,0) = "D" then
		drivepath = "\\" & arrayList(j,1) & "\" & arrayList(j,2) & "$"
		set obj = fso.GetDrive(fso.GetDriveName(drivepath))
	elseif arrayList(j,0) = "F" then
		drivepath = arrayList(j,1)
		set obj = fso.GetFolder(drivepath)
	else
		' shouldn't really get in here...
	end if
		
	driveName = arrayList(j,5)
	
	if arrayList(j,0) = "D" then
		if round(obj.FreeSpace/1024/1024/1024) < arrayList(j,4) then
			status = "alarm"
			msg = drivepath & " (" & driveName & ") only has " & round(obj.FreeSpace/1024/1024/1024) & "gb free"
		else
			if round(obj.FreeSpace/1024/1024/1024) < arrayList(j,3) then
				status = "warning"
				msg = drivepath & " (" & driveName & ") only has " & round(obj.FreeSpace/1024/1024/1024) & "gb free"
			else
				status = "ok"
				msg = drivepath & " (" & driveName & ") is ok with " & round(obj.FreeSpace/1024/1024/1024) & "gb free"
			end if
		end if
	elseif arrayList(j,0) = "F" then
		if round(obj.size/1024/1024/1024) > arrayList(j,4) then
			status = "alarm"
			msg = drivepath & " (" & driveName & ") has reached " & round(obj.size/1024/1024/1024) & "gb"
		elseif round(obj.size/1024/1024/1024) > arrayList(j,3) then
			status = "warning"
			msg = drivepath & " (" & driveName & ") has reached " & round(obj.size/1024/1024/1024) & "gb"
		else
			status = "ok"
			msg = drivepath & " (" & driveName & ") is ok at " & round(obj.size/1024/1024/1024) & "gb used"
		end if
	else
		status = "error"
		msg = "Configuration error"
	end if
	
	fl.writeline("""" & drivepath & """,""" & status & """,""" & msg & """")
	
	set obj = nothing
	j = j+1
loop

set fl=nothing
set fso=nothing

The output CSV file should look something like this.

"Item","Status","Message"
"\\web1\c$","ok","\\web1\c$ (Windows 2003 web server, C-drive) is ok with 10gb free"
"\\exch2\c$","alarm","\\exch2\c$ (Windows 2003 Exchange server, D-drive) only has 16gb free"
"\\fileserver\public\fileShare1","warning","\\fileserver\public\fileShare1 (File share 1) has reached 2gb"
"\\fileserver\public\fileShare2","ok","\\fileserver\public\fileShare2 (File sahre2) is ok at 91gb used"