Listing printers on remote machines. Not seeing the same results as I would if I were logged on locally as the user - vbscript

My script is supposed to list all the printers installed on a remote machine and write that data to a text file while designating if the printer is Local or Network. When I run the script against my local machine with my profile logged on I get the following results:
Local
Microsoft XPS Document Writer
Network
\\PrintServer\PT-NJ-CPR-B-CORPIT-1
Network
\\PrintServer\PT-NJ-CPR-B-ITTEMP-1
Network
\\PrintServer\CPR5A26D1A
These results are exactly what I want however when I run the same script against a remote machine I still get results but they seem to be for a more generic user
Local
Send To OneNote 2010
Local
Microsoft XPS Document Writer
Local
Fax
My question is how do I customize my script to truly impersonate the logged on user thus returning me the full results even from a remote machine?
Const ForAppending = 8
Const ForReading = 1
Dim WshNetwork, objPrinter, intDrive, intNetLetter, fso
Set fso = CreateObject("Scripting.FileSystemObject")
Set InputFile = fso.OpenTextFile("C:\xVBS Scripts\Printer Scripts\Computers.txt", 1)
Do Until InputFile.AtEndOfStream
strComputer = InputFile.ReadLine
Set WshNetwork = CreateObject("WScript.Network")
Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set colInstalledPrinters = objWMIService.ExecQuery("Select * from Win32_Printer")
Set colItems = objWMIService.ExecQuery("Select * from Win32_ComputerSystem",,48)
Set WshShell = WScript.CreateObject("WScript.Shell")
Set objFSO = CreateObject("Scripting.FileSystemObject")
For Each objItem in colItems
UserName = objItem.UserName
arrUserName = Split(UserName, "\", -1, 1)
varUserName = arrUserName(1)
Next
filOutput = varUserName & ".txt"
If objFSO.FileExists(filOutput) Then
objFSO.DeleteFile(filOutput)
End If
Set objOutputFile = objFSO.OpenTextFile (filOutput, ForAppending, True)
For Each objPrinter in colInstalledPrinters
If objPrinter.Attributes And 64 Then
strPrinterType = "Local"
strTest = Left(objPrinter.Name, 2)
objOutputFile.WriteLine(strPrinterType)
objOutputFile.WriteLine(objPrinter.Name)
objOutputFile.WriteLine(vbNewLine)
Else
strPrinterType = "Network"
strTest = Left(objPrinter.Name, 2)
objOutputFile.WriteLine(strPrinterType)
objOutputFile.WriteLine(objPrinter.Name)
objOutputFile.WriteLine(vbNewLine)
End If
Next
Wscript.Sleep 1500
MsgBox "Printer mapping report is located" & vbNewLine & "in the following directory: " & filOutput , vbInformation, "Report Located At"
WshShell.Run "Notepad " & filOutput,1,False
Loop
InputFile.Close
Wscript.Quit

I dont think there is an actual answer to this. The more I learn about VB Script and Powershell it appears as if WMI is most useful when run interactively. It doesn't know how to process users who are not currently logged in. I bypass this problem by running the script as a GPO Link/Enforced that calls the script as a log on script. – JRN just now edit

Related

Run BAT file on remote server using VBScript. No psexec, and as a different user

I'm trying to execute a a BAT file on a remote server using VBScript. Further requirements:
psexec is not allowed
I need the script to operate under the permissions of another user, not those of my own workstation
I have consulted this article: https://learn.microsoft.com/en-us/windows/desktop/WmiSdk/connecting-to-wmi-remotely-with-vbscript
I see how creating the connection works, but I can't figure out how to then create a process using that same connection.
I believe this solution is really close, the only problem is I think it impersonates the user of the computer it is currently running on:
strCommand = "C:\temp\copyall.bat"
strPath = "C:\temp"
strcomputer="."
process = "winmgmts:" & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2"
msgbox process
Set objWMIService = GetObject(process)
Set objProcess = objWMIService.Get("Win32_Process")
errReturn = objProcess.Create(strCommand, strPath, Null, intProcessID)
If errReturn = 0 Then
WScript.Echo "scan success: " & intProcessID
Else
WScript.Echo "scan fail: " & errReturn
End If
This example from Microsoft's site shows how to create the connection properly but I don't know how to then use that connection.
' Full Computer Name
' can be found by right-clicking My Computer,
' then click Properties, then click the Computer Name tab)
' or use the computer's IP address
strComputer = "FullComputerName"
strDomain = "DOMAIN"
Wscript.StdOut.Write "Please enter your user name:"
strUser = Wscript.StdIn.ReadLine
Set objPassword = CreateObject("ScriptPW.Password")
Wscript.StdOut.Write "Please enter your password:"
strPassword = objPassword.GetPassword()
Set objSWbemLocator = CreateObject("WbemScripting.SWbemLocator")
Set objSWbemServices = objSWbemLocator.ConnectServer(strComputer, _
"Root\CIMv2", _
strUser, _
strPassword, _
"MS_409", _
"ntlmdomain:" + strDomain)
Set colSwbemObjectSet = objSWbemServices.ExecQuery("Select * From Win32_Process")
For Each objProcess in colSWbemObjectSet
Wscript.Echo "Process Name: " & objProcess.Name
Next
The answer is probably staring me in the face but I just can't see it right now. Ideas?
After connecting to the remote server simply get the Win32_Process object and call the Create() method like you'd do locally.
Set objSWbemServices = objSWbemLocator.ConnectServer(...)
Set objProcess = objSWbemServices.Get("Win32_Process")
errReturn = objProcess.Create(strCommand, strPath, Null, intProcessID)
The file you want to run must exist locally on the remote server for this to work.
Also note that this normally requires admin privileges on the remote system.

ignore workstations that are offline and resume

I have this VBScript to help me remove local users from the local admins. I can't seem to get it to ignore workstations that are not on the network.
Is there a way to ignore workstations that are not found and have it continue to the next line under the computers.txt file?
For example, say PC1 and PC3 are found but PC2 is not found I want it to ignore not found workstations and continue until the end of the list of computers.
I've tried On Error Resume Next (didnt work), and I tried Const ForReading = 1, Const ForAppending = 8, Const OverwriteExisting = True (didnt work either).
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("C:\adminScript\computers.txt")
strComputer = objFile.ReadLine
Set objGroup = GetObject("WinNT://" & strComputer & "/Administrators")
For Each objUser In objGroup.Members
If objUser.Name <> "Administrator" AND objUser.Name <> "Domain Admins" AND objUser.Name <> "G_SCCMAgent" AND objUser.Name <> "User" Then
Wscript.Echo objUser.Name
objGroup.Remove(objUser.ADsPath)
End If
Next
wscript.Echo "I am done!"
Contrary to popular belief On Error Resume Next doesn't magically make errors go away. Neither does defining symbolic constants for parameters of the OpenTextFile method.
If you want to skip over computers that aren't available you need to actually test the availability of each computer. A common way to do this is the Win32_PingStatus WMI class.
Set wmi = GetObject("winmgmts://./root/cimv2")
isAvailable = False
qry = "SELECT * FROM Win32_PingStatus WHERE Address='" & strComputer & "'"
For Each res In wmi.ExecQuery(qry)
If res.StatusCode = 0 Then isAvailable = True
Next
If isAvailable Then
'modify administrators group
End If
Also, you probably need to process the content of computers.txt in a loop. Your current code reads only the first line. To process more than one line from the file use something like this:
Set objFile = objFSO.OpenTextFile("C:\adminScript\computers.txt")
Do Until objFile.AtEndOfStream
strComputer = objFile.ReadLine
'...
Loop
objFile.Close

VBScript Strange Issue with HTA and Type mismatch error

When I run the following script on it's own by double clicking, it works just fine. It returns the last logged on user as expected. But when I run it from the HTA I have been developing as a front end to all of my scripts, I get a type mismatch error on the "wscript.echo strvalue" line. I have tried everything to get it to work, like changing permissions on mshta.exe to full control for myself. I simply can't get it to run from the HTA without getting an error, but it works 100% as expected on its own. I am completely stumped.
strinput = "myserver"
Set objRegistry = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & _
strinput & "\root\default:StdRegProv")
strKeyPath = "SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI"
strValueName = "LastLoggedOnUser"
objRegistry.GetStringValue HKEY_LOCAL_MACHINE, strKeyPath, strValueName, strValue
Wscript.Echo strValue
By default, Windows 64-bit uses MSHTA.EXE 32-bit. The registry has a separate branches for 64-bit and 32-bit apps, thus WMI can't find the registry value you are looking for.
Save the code below to e. g. C:\test\tmp.hta, try to launch it from explorer by double-click (32-bit by default) - you will get null, and then launch via Run dialog (Win+R) with path: %windir%\system32\mshta.exe "C:\test\tmp.hta" (64-bit), the result will be your username.
<html>
<head>
<script language="vbscript">
Sub window_onload()
Const HKEY_LOCAL_MACHINE = &H80000002
Set objRegistry = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\default:StdRegProv")
strKeyPath = "SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI"
strValueName = "LastLoggedOnUser"
objRegistry.GetStringValue HKEY_LOCAL_MACHINE, strKeyPath, strValueName, strValue
document.body.innerText = strValue
End Sub
</script>
</head>
<body>
</body>
</html>
Note that many other stuff within scripts depends on application architecture, e. g. number of ActiveX are available only in 32-bit version, so they should be launched via %windir%\SysWOW64\ (Windows 32-bit on Windows 64-bit subsystem).
Use Msgbox function instead of Wscript.Echo method. HTAs use the Internet Explorer Scripting Object Model which does not contain Wscript object (this belongs to Windows Script Host Object Model).
Read HTA: Why Can’t I Use Wscript.Echo?:
You might have noticed that when it came time to report back the
operating system version we used the VBScript Msgbox function rather
than the more common Wscript.Echo. Why didn’t we use Wscript.Echo?
Here’s why:
As it turns out the various Wscript methods - Wscript.Echo,
Wscript.Sleep, Wscript.Quit, etc. - are designed solely to run under
the Windows Script Host environment. When we’re working in an HTA
we’re not running under WSH; instead we’re running under the MSHTA
process. Because of that the Wscript methods are not available to us
(nor can we create them). Consequently we need to find workarounds for
each method, and Msgbox is a perfectly adequate replacement for
Wscript.Echo. (We’ll talk about workarounds for other methods - such
as Wscript.Sleep - when we get to them.)
The moral of the story: Don’t bother with Wscript.Echo; it won’t work.
Edit: with Wscript.Echo TypeName(strValue) & vbNewLine & VarType(strValue):
==> C:\Windows\System32\cscript.exe D:\VB_scripts\SO\33505295.vbs
String
8
==> C:\Windows\SysWOW64\cscript.exe D:\VB_scripts\SO\33505295.vbs
Null
1
Tried in a simple HTA which gives the same (different) result
==> C:\Windows\System32\mshta.exe 33505295.hta
versus
==> C:\Windows\SysWOW64\mshta.exe 33505295.hta
Conclusion. Check HTA file type association. For instance, ftype htafile in my Windows 8 (64bit) returns (surprisingly?) the same value which causes wrong behaviour on double click:
==> assoc .hta
.hta=htafile
==> ftype htafile
htafile=C:\Windows\SysWOW64\mshta.exe "%1" {1E460BD7-F1C3-4B2E-88BF-4E770A288AF5}%U{1E460BD7-F1C3-4B2E-88BF-4E770A288AF5} %*
I have had the same challenge a few weeks ago.
The following code provided me the possibility to see who is currently logged onto a remote computer.
I hope this can help you.
Sub ActionGetCurrentUser(strCPU) 'strCPU is the computername
set objWMI = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strCPU & "\root\cimv2")
set Items = objWMI.ExecQuery("Select * From Win32_ComputerSystem")
For Each obj in Items
OutStr = right(obj.username,9)
Next
Resultstring = "Logged in User is: " & OutStr
Set objRootDSE = GetObject("LDAP://RootDSE")
strDNSDomain = objRootDSE.Get("defaultNamingContext")
strTarget = "LDAP://" & strDNSDomain
' ---------------- Write the User's account & password to a variable -------------------
strCurrentuser = Currentuser.value
strPassword = PasswordArea.value
' ---------------- Connect to Ad Provider ----------------
Set objConnection = CreateObject("ADODB.Connection")
objConnection.Provider = "ADsDSOObject"
objConnection.Properties("User ID") = strCurrentUser ' pass credentials - if you omit this, the search is performed....
objConnection.Properties("Password") = strPassword ' ... with the current credentials
objConnection.Properties("Encrypt Password") = True ' only needed if you set "User ID" and "Password"
objConnection.Open "Active Directory Provider"
Set objCmd = CreateObject("ADODB.Command")
Set objCmd.ActiveConnection = objConnection
objCmd.CommandText = "SELECT DisplayName FROM '" & strTarget & "' WHERE extensionAttribute11 = '" & OutStr & "'"
Const ADS_SCOPE_SUBTREE = 2
objCmd.Properties("Page Size") = 100
objCmd.Properties("Timeout") = 30
objCmd.Properties("Searchscope") = ADS_SCOPE_SUBTREE
objCmd.Properties("Cache Results") = False
Set objRecordSet = objCmd.Execute
If objRecordset.Recordcount = 0 then ' If no user is found then the recordcount will be 0
msgbox "No user is logged on"
Resultstring = ""
Set objCmd = Nothing
Set objRootDSE = Nothing
Set objRecordSet = Nothing
Set objWMI = Nothing
Set Items = Nothing
exit sub
End if
Set objRecordSet = objCmd.Execute
objRecordSet.MoveFirst
Resultstring = Resultstring & vbcrlf & "Name: " & objRecordset.fields("DisplayName")
Msgbox Resultstring
Resultstring = ""
Set objCmd = Nothing
Set objRootDSE = Nothing
Set objRecordSet = Nothing
Set objWMI = Nothing
Set Items = Nothing
End Sub

Attempting to extract printers from users machine and then outputting to a text fill.

I am attempting to extract the printers from a users machine and then output to a text file but when I run the test I get a invalid procedure call or argument for this specific line of code.
Set objOutputFile = objFSO.OpenTextFile(outFile, ForAppending, True)
I have attempted to change OpenTextFileto CreateTextFile but I need the lines to appended to file as it will be running as a log on script.
I have done some research and used the Microsoft developer articles to help me debug the issue in the code but I don't have much experience in Visual Basic.
I have added the entire script to give context to the what is going on.
dim objComputerName, ObjNetwork , strText , objfile, StrComputer
dim wshnetwork
Set wshnetwork = CreateObject ("Wscript.network")
StrComputer = WshNetwork.ComputerName
If IsEmpty(StrComputer) Then Wscript.Quit
Set WshNetwork = CreateObject("WScript.Network")
Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set colInstalledPrinters = objWMIService.ExecQuery("Select * from Win32_Printer")
Set colItems = objWMIService.ExecQuery("Select * from Win32_ComputerSystem",,48)
Set WshShell = WScript.CreateObject("WScript.Shell")
Set objFSO = CreateObject("Scripting.FileSystemObject")
outFile = "C:\scripts\Printers" & StrComputer
Set objOutputFile = objFSO.OpenTextFile(outFile, ForAppending, True)
For Each objPrinter in colInstalledPrinters
strTest = Left(objPrinter.Name, 2)
objOutputFile.WriteLine(objPrinter.Name)
objfile.close
Next
Set objPrinter = WshNetwork.EnumPrinterConnections
'Set objOutputFile = objFSO.OpenTextFile (filOutput, ForAppending, True)
If objPrinter.Count = 0 Then
WScript.Echo "No Printers Mapped "
else
For intDrive = 0 To (objPrinter.Count -1) Step 2
intNetLetter = IntNetLetter +1
printer = "UNC Path " & objPrinter.Item(intDrive) & " = " & objPrinter.Item(intDrive +1) & " Printer : " & intDrive
objOutputFile.WriteLine(printer)
Next
end if
objOutputFile.Close``*
Invalid procedure call or argument
You passed an invalid parameter in your procedure call. This could be because the parameter was out of range, or contained invalid data. Alternately, you may have invoked a procedure at an unexpected time.
To correct this error
Verify that the parameters being passed to the procedure are valid.
Verify that you are calling the function at an appropriate time.
My guess is this line is an ilegal filename.
outFile = "C:\scripts\Printers" & StrComputer
On my computer this is c:\scripts\PrintersSerenity which is probably not right that your text file is called PrintersSerenity without an extension.

How do I get the computer name of a system and output it to a file in VBScript

I am trying to get the computer name from the registry and write it to a file. At this point, my function call for obtaining the computer name from registry isn't working. Any advice would be appreciated.
Option Explicit
On Error Resume Next
Dim regComputerName, ComputerName
Set objShell = WScript.CreateObject("WScript.Shell")
Set objFileSystem = CreateObject("Scripting.FileSystemObject")
regComputerName = "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\ComputerName\ComputerName\Computername"
ComputerName = obj.shell.RegRead(regComputerName)
oWrite.WriteLine(ComputerName,C:\text)
Reading registry values is error prone and may require elevated privileges in Windows 7. There's another way of getting the computer name, very similar to what you are doing right now:
Set objNetwork = WScript.CreateObject("WScript.Network")
ComputerName = objNetwork.ComputerName
MsgBox ComputerName
Also, the last line in your script: oWrite.WriteLine(ComputerName,C:\text) will not work for 2 reasons:
C:\text has to be in quotes, like this: "C:\text.txt"
In VB, only a function that results a value can be called with parenthesis. Call WriteLine like this instead: oWrite.WriteLine ComputerName, "C:\text.txt"
Finally, are you sure you are not referring to VBScript instead of VB in your question?
Your code is not working because of an error in this line:
ComputerName = obj.shell.RegRead(regComputerName)
Instead of obj.shell you should be referencing objShell. It should look like this:
Set objShell = WScript.CreateObject("WScript.Shell")
strRegKey = "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\ComputerName\ComputerName\Computername"
strComputerName = objShell.RegRead(strRegKey)
WScript.Echo strComputerName
However, there are much more reliable ways of getting the computer name without having to deal with the registry.
From WSH (as suggested above)
Set WshNetwork = WScript.CreateObject("WScript.Network")
strComputerName = WshNetwork.ComputerName
WScript.Echo "Computer Name: " & strComputerName
From an environmental variable...
Set wshShell = WScript.CreateObject("WScript.Shell")
strComputerName = wshShell.ExpandEnvironmentStrings("%COMPUTERNAME%")
WScript.Echo "Computer Name: " & strComputerName
From WMI...
strcomputer = "."
Set objWMISvc = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colItems = objWMISvc.ExecQuery("Select * from Win32_ComputerSystem",, 48)
For Each objItem in colItems
strComputerName = objItem.Name
WScript.Echo "Computer Name: " & strComputerName
Next
From ADSI...
Set objSysInfo = CreateObject("WinNTSystemInfo")
strComputerName = objSysInfo.ComputerName
WScript.Echo "Computer Name: " & strComputerName
From ADSI (only works for domain members)...
Set objSysInfo = CreateObject("ADSystemInfo")
strComputerName = objSysInfo.ComputerName
WScript.Echo "Computer Name: " & strComputerName
...and one last way for Windows XP users only...
Set objPC = CreateObject("Shell.LocalMachine")
strComputerName = objPC.MachineName
WScript.Echo "Computer Name: " & strComputerName

Resources