Tuesday, November 19, 2013

Deleting Sub-Folders with VBScript, Coffee and French Fries

Delete all sub-folders beneath a given root folder on multiple (remote) computers.  The old RD/RMDIR command will delete the named root folder along with sub-folders, by default.  This script leaves the root folder alone and only deletes the sub-folders.  Feel free to modify as needed.

'****************************************************************
' Filename..: fso_delete_subfolders.vbs
' Author....: David M. Stein
' Date......: 11/19/2013
' Purpose...: delete all sub-folders beneath a root path, on multiple computers
' NO WARRANTIES - USE AT YOUR OWN RISK - YOU DAREDEVIL YOU
'****************************************************************

Dim strServer, objSubFolder
Dim strFolderRoot, strSubFolder, x

Const strServerList = "SERVER1,SERVER2,SERVER3"
Const strRootPath = "D$\TEMP"

Set objFSO = CreateObject("Scripting.FileSystemObject")

For each strServer in Split(strServerList, ",")
wscript.echo "info: server is " & strServer
On Error Resume Next
strFolderRoot = "\\" & strServer & "\" & strRootPath
Set objFolder = objFSO.GetFolder(strFolderRoot)
If err.Number = 0 Then
For each objSubFolder in objFolder.SubFolders
strSubFolder = objSubFolder.Name
wscript.echo "info: deleting folder --> " & strFolderRoot & "\" & strSubFolder
x = objFSO.DeleteFolder(strFolderRoot & "\" & strSubFolder, True)
wscript.echo "info: result is " & x
Next
Else
wscript.echo "error [" & err.Number & "]: " & err.Description
wscript.echo "info: could be caused by folder-not-found."
End If
Next

Tuesday, July 31, 2012

File Search using WMI CIM_DataFile with VBScript

I looked around, but didn't find a script example that did exactly what I wanted, but I ran across several good alternatives. One thing I found was that if I don't specify the drive letter, it takes WAAAAAAAYYYYY longer to execute. Just a tip. In any case, I hope this helps someone out there...

'****************************************************************
' Filename..: fileSearch.vbs
' Author....: ScriptZilla / SkatterBrainz / Dave
' Date......: 07/30/2012
' Purpose...: search for files using WMI/CIM_DataFile
'****************************************************************
time1 = Timer

'----------------------------------------------------------------
' comment: search parameters
'----------------------------------------------------------------

strFileExt  = "syn"
strFileName = "*"
strDriveLtr = "c:"
strComputer = "."

'----------------------------------------------------------------
' function: 
'----------------------------------------------------------------

Function StringDate(dv)
 Dim xdy, xdm, xdd, xdh, xdn, tmp
 ' example: 20120729195837.171181-240
 xdy = Mid(dv,1,4) ' year
 xdm = Mid(dv,5,2) ' month
 xdd = Mid(dv,7,2) ' day
 xdh = Mid(dv,9,2) ' hour
 xdn = Mid(dv,11,2) ' minute
 tmp = xdm & "/" & xdd & "/" & xdy & " " & xdh & ":" & xdn
 StringDate = FormatDateTime(tmp, vbShortDate) & " " & _
  FormatDateTime(tmp, vbLongTime)
End Function

'----------------------------------------------------------------
' comment: main script code begins
'----------------------------------------------------------------

Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")

If strFileName = "*" Then
 query = "Select * from CIM_DataFile Where Drive='" & strDriveLtr & "'" & _
  " AND Extension='" & strFileExt & "'"
Else
 query = "Select * from CIM_DataFile Where Drive='" & strDriveLtr & "'" & _
  " AND FileName = '" & strFileName & "'" & _
  " AND Extension='" & strFileExt & "'"
End If

wscript.echo "info: search criteria = " & strFileName & "." & strFileExt & " on " & strDriveLtr
Set colFiles = objWMIService.ExecQuery(query)

wscript.echo "info: beginning search..."

counter = 0
For Each objFile in colFiles
 counter = counter + 1
    wscript.echo objFile.Drive & objFile.Path & _
     objFile.FileName & "." & objFile.Extension & _
     vbTab & StringDate(objFile.CreationDate) & _
     vbTab & StringDate(objFile.LastModified) & _
     vbTab & objFile.FileSize
Next

wscript.echo "info: " & counter & " matching files found"
wscript.echo "info: " & Timer - time1 & " seconds"

Sunday, April 15, 2012

Query AD Computers with Custom HOSTS File Entries

'****************************************************************
' Filename..: enum_ad_host_files.vbs
' Author....: David M. Stein
' Date......: 04/15/2012
' Purpose...: search for hosts files with custom entries
'****************************************************************
dns_netbios = "short_name_of_your_active_directory_domain"

Const ForReading = 1
Const ForWriting = 2

wscript.echo "info: scanning domain = " & dns_netbios

Set objDom = GetObject( "WinNT://" & dns_netbios )
Set objFSO = CreateObject("Scripting.FileSystemObject")

tcount = 0

For each obj in objDom
 If Lcase(obj.Class) = "computer" Then
  computerName = obj.Name
  wscript.echo "info: " & computerName
  tcount = tcount + 1
  CheckHosts computerName
 End If
Next

Sub CheckHosts(cn)
 Dim filename, objFile, strLine, found
 filename = "\\" & cn & "\admin$\system32\drivers\etc\hosts"
 wscript.echo "info: searching for: " & filename
 If objFSO.FileExists(filename) Then
  On Error Resume Next
  Set objFile = objFSO.OpenTextFile(filename, ForReading)
  If err.Number = 0 Then
   Do Until objFile.AtEndOfStream
    strLine = Trim(objFile.Readline)
    If Left(strLine,1) <> "#" And strLine <> "" Then
     found = True
    End If
   Loop
   objFile.Close
   
   If found = True Then 
    wscript.echo "info: custom entry found!"
   Else
    wscript.echo "info: no custom entries found."
   End If
  Else
   wscript.echo "fail: error (" & err.Number & ") = " & err.Description
  End If
   
 Else
  wscript.echo "fail: unable to locate hosts file on " & cn
 End If
End Sub

wscript.echo "info: " & tcount & " account objects found"

Thursday, October 20, 2011

Query Installed Apps a Different Way

For whatever reason, in some environments, a WMI query of Win32_Product is God-awful slow.  I've seen this on Windows 7 and Windows 7 SP1 clients, as well as on Windows Server 2008 and 2008 R2.  The symptom can be seen from WIM script, WBEM, and using WMIC from a command console with very similar results:  The query hangs for 20-25 seconds and then begins executing in spurts.  Other Win32 classes work fine, from what I've seen, it's just Win32_Product for some reason.  One workaround is to dump a registry output file, and scrub it to make a "clean" output file.  You can port this to PowerShell or KiXtart if you want (or whatever you prefer, I really don't care as long as you're happy and that makes me happy so we're all happy. yay!)

'****************************************************************
' Filename..: installedApps.vbs
' Author....: David M. Stein aka Scriptzilla aka dipshit
' Date......: 10/20/2011
' Purpose...: save query of installed applications to local file
'****************************************************************

Const strInputFile  = "c:\regoutput.txt"
Const strOutputFile = "c:\installedApps.txt"

Const ForReading = 1
Const ForWriting = 2
Const adVarChar = 200

cmd = "reg query hklm\software\microsoft\windows\currentversion\uninstall /s >" & strInputFile

On Error Resume Next

Set objShell = CreateObject("Wscript.Shell")
Set objFSO = CreateObject("Scripting.FileSystemObject")

wscript.echo "info: executing shell command to create temp file..."

objShell.Run "cmd /c " & cmd, 7, True

wscript.echo "info: getting temp file for input..."

If objFSO.FileExists(strInputFile) Then
 wscript.echo "info: reading temp file..."
 Set objFile = objFSO.OpenTextFile(strInputFile, ForReading)
 Set objFile2 = objFSO.CreateTextFile(strOutputFile, True)

 Set rs = CreateObject("ADODB.RecordSet")

 rs.CursorLocation = adUseClient
 rs.Fields.Append "productname", adVarChar, 255
 rs.Open

 Do Until objFile.AtEndOfStream
     strLine = objFile.Readline
     If Left(strLine, 25) = "    DisplayName    REG_SZ" Then
      strOutput = Trim(Mid(strLine, 30))
   rs.AddNew
   rs.Fields("productname").value = strOutput
   rs.Update
     End If
 Loop
 
 rs.Sort = "productname"
 
 Do Until rs.EOF
     objFile2.WriteLine(rs.Fields("productname").value)
  rs.MoveNext
 Loop
 rs.CLose
 Set rs = Nothing
 
 objFile.Close
 objFile2.Close
 wscript.echo "info: finished scrubbing input to new output file"
Else
 wscript.echo "fail: temp file not found"
End If

Set objFSO = Nothing
Set objShell = Nothing
'----------------------------------------------------------------

wscript.echo "info: processing complete!"

Friday, May 13, 2011

Two Ways to Do String Matching in VBscript

The ugly but easier way (using InStr function)

Const strValue = "Microsoft Windows 7 Enterprise Edition"
If InStr(strValue, "Windows 7") > 0 Or InStr(strValue, "Windows XP") > 0 Then
wscript.echo "match found"
End If


The fancier but more irritating way (using the REGEX object):



Function MatchString(strToSearch, strPattern)
Dim objRegEx, colMatches, matchFound
Set objRegEx = CreateObject("VBScript.RegExp")
objRegEx.Global = True
objRegEx.Pattern = strPattern
Set colMatches = objRegEx.Execute(strToSearch)
If colMatches.Count > 0 Then
matchFound = True
End If
Set objRegEx = Nothing
MatchString = matchFound
End Function

If MatchString(strValue, "Windows XP|Windows 7") Then
wscript.echo "match found"
End If

Tuesday, February 15, 2011

Template CMD Script for App Installs

This is obviously bare-bones and will need to be modified to suit specific implementations.  There are dozens of ways to execute a software installation, including setup.exe files, extracting ZIP and other archive files, registering DLLs with REGSVR32, and the ugly-ass, vice-grip + screwdriver + duct tape approach where you manually build folder trees, copy files, register DLLs, add and modify registry keys and make or replace shortcuts.  Oh, the torture.

EDIT: Updated 7/30/2011 to include errorlevel 3010 (reboot pending)


@echo off
rem ****************************************************************
rem  Filename..: setup.cmd
rem  Author....: David M. Stein
rem  Date......: 07/30/2011
rem  Purpose...: install apps in controlled sequence
rem ****************************************************************
rem Additional Notes:
rem
rem ****************************************************************
title Installing Applications
CLS
echo Installing Applications...
SETLOCAL
set APPNAME=MyApplicationSuite2011
set LOG=%TMP%\%APPNAME%_install.log
set MSI=/quiet /norestart
echo %DATE% %TIME% installing... %APPNAME%... >%LOG%
echo %DATE% %TIME% source....... %~dps0 >>%LOG%
echo %DATE% %TIME% target....... %COMPUTERNAME% >>%LOG%
echo %DATE% %TIME% windir....... %WINDIR% >>%LOG%
echo %DATE% %TIME% progfiles.... %PROGRAMFILES% >>%LOG%
echo %DATE% %TIME% temp......... %TMP% >>%LOG%
echo INSTALL LOG: %LOG%
echo ----------------------------------------------- >>%LOG%
echo *** APPLICATION NAME 1 >>%LOG%
echo %DATE% %TIME% info: checking if application is already installed... >>%LOG%
if exist "%ProgramFiles%\FolderName\filename.exe" (
echo %DATE% %TIME% info: ## application is already installed >>%LOG%
) else (
echo %DATE% %TIME% info: ## installing application... >>%LOG%
echo %DATE% %TIME% command = msiexec /i "%~dps0Folder\filename.msi" TRANSFORMS="%~dps0Folder\filename.MST" >>%LOG%
   msiexec /i "%~dps0Folder\filename.msi" TRANSFORMS="%~dps0Folder\filename.MST" %MSI%
   if %errorlevel%==0 (
      echo %DATE% %TIME% info: installation SUCCESSFUL >>%LOG%
   ) else (
      if %errorlevel%==3010 (
         echo %DATE% %TIME% info: installation SUCCESSFUL [reboot pending] >>%LOG%
      ) else (
         echo %DATE% %TIME% file: exit code is %errorlevel% >>%LOG%
         rem Raise error to parent process!!
         exit %errorlevel%
      )

   )
)
rem ------------------------------------------------
rem echo *** APPLICATION NAME 2 >>%LOG%
rem ------------------------------------------------
rem
rem repeat code above with modifications as needed
rem
rem ------------------------------------------------
echo %DATE% %TIME% info: adjusting application folder permissions... >>%LOG%
cacls "%ProgramFiles%\FolderName" /T /E /C /G Users:C
echo ----------------------------------------------- >>%LOG%
echo %DATE% %TIME% info: applying attachmate file association fix... >>%LOG%
REG DEL HKCR\.xxx /f
REG ADD HKCR\.xxx /ve /d "ApplicationClass.ProgName.1" /f
echo ----------------------------------------------- >>%LOG%
echo %DATE% %TIME% info: adjusting registry permissions... >>%LOG%
REGINI.exe %~dps0customregsettings.ini
echo ----------------------------------------------- >>%LOG%
echo %DATE% %TIME% completed! result code: %errorlevel% >>%LOG%
ENDLOCAL
rem Raise error to parent process!!
exit %errorlevel%

Querying Files Using Windows Search with VBScript

'****************************************************************
' Filename..: scanFiles.vbs
' Author....: David M. Stein aka Skatterbrainz aka Scriptzilla aka goofy
' Contact...: ds0934 (at) gmail (dot) com
' Date......: 02/14/2011
' Purpose...: generate file scan report using Windows Search queries
'****************************************************************
' COPYRIGHT (C) 2011 David M. Stein - All Rights Reserved.
' No portion of this software code may be reproduced, shared,
' transmitted, by any means, electronic or otherwise, for any
' purposes whatsoever, without the explicit prior written consent 
' of the author.  
'****************************************************************
' DESCRIPTION
'
' This script invokes the built-in Windows Desktop Search service
' to query the local index for files that contain specific string
' phrases.  The string phrases are defined in an external text
' file which allows for customization of phrases as needed.
' The output is echoed to the display screen unless redirected
' to a log file.  The associated .cmd file does just that. It is
' used to execute this script, and redirect the output to a log
' file which is saved to a central folder (network server share)
'****************************************************************


Option Explicit


Const verbose = False


'----------------------------------------------------------------
' comment: DO NOT CHANGE ANY CODE BELOW THIS POINT !!!
' comment: ...or ye shall perish in the land of stupidity
'----------------------------------------------------------------


echo "info: script initialized " & Now


Const ForReading = 1
Const ForWriting = 2


Dim objConnection, objRecordset, query, scriptPath, inputFile
Dim objFSO, objFile, strLine, searchList, itemCount : itemCount = 0
Dim filePath, phrase


Set objFSO = CreateObject("Scripting.FileSystemObject")


' comment: determine self-referential path location
scriptPath = Replace(wscript.ScriptFullName, "\" & wscript.ScriptName, "")
inputFile = "searchlist.txt"
' comment: determine input search list file location
filePath = scriptPath & "\" & inputFile


On Error Resume Next
echo "info: searching for searchlist.txt input file..."
If objFSO.FileExists(filePath) Then
    echo "info: reading search values..."
    Set objFile = objFSO.OpenTextFile(filePath, ForReading)
    Do Until objFile.AtEndOfStream
        strLine = objFile.Readline
        If Left(strLine,1) <> ";" Then
            If searchList <> "" Then
                searchList = searchList & vbTab & strLine
            Else
                searchList = strLine
            End If
            itemCount = itemCount + 1
        End If
    Loop
    objFile.Close
End If


echo "info: " & itemCount & " phrases were queued"


'----------------------------------------------------------------
 
echo "info: initializing window search interface..."

Set objConnection = CreateObject("ADODB.Connection")


echo "info: opening windows search data connection..."
objConnection.Open "Provider=Search.CollatorDSO;Extended Properties='Application=Windows';"
If err.Number <> 0 Then
    echo "fail: connection-open failure [" & _
        err.Number & ":" & err.Description & "]"
    wscript.quit(2)
End If


echo "info: beginning windows search scan process..."
For each phrase in Split(searchList, vbTab)
    wscript.echo "PHRASE: " & phrase

    ' comment: define search query expression to identify matching files

    query = "SELECT System.FileName, System.ItemPathDisplay, " & _
        "System.DateCreated, System.DateModified " & _
        "FROM SYSTEMINDEX WHERE Contains(System.FileName, '" & _
        Chr(34) & phrase & Chr(34) & "')" & _
        " OR Contains('" & Chr(34) & phrase & Chr(34) & "')"

    ' comment: open connection to service and submit query request

    Set objRecordSet  = CreateObject("ADODB.Recordset")
    objRecordSet.Open query, objConnection

    If err.Number <> 0 Then
        echo "fail: recordset-open failure [" & _
            err.Number & ":" & err.Description & "]"
        objConnection.Close
        Set objConnection = Nothing
        wscript.quit(3)
    End If

    ' comment: if results are not empty, iterate the dataset rows

    If Not (objRecordset.BOF and objRecordset.EOF) Then
        objRecordSet.MoveFirst


        echo "info: iterating recordset results..."
        Do Until objRecordset.EOF
            wscript.echo "MATCH: " & _
            objRecordset.Fields.Item("System.ItemPathDisplay").value & _
            vbTab & objRecordset.Fields.Item("System.DateCreated").value & _
            vbTab & objRecordset.Fields.Item("System.DateModified").value
            objRecordset.MoveNext
        Loop
        wscript.echo
    Else
        echo "info: no matching records were found"
    End If
    objRecordset.Close
    Set objRecordset = Nothing
Next


' comment: close connection to service


objConnection.Close
Set objConnection = Nothing


echo "info: processing completed " & Now


'----------------------------------------------------------------
' function: verbose printing
'----------------------------------------------------------------


Sub Echo(s)
    If verbose = True Then
        wscript.echo s
    End If
End Sub


EXAMPLE of searchlist.txt

; disable lines by inserting a semi-colon in front of them
This is a phrase to search for
this is another phrase
phrases are case insensitive