changing owner of subfolders using icacls in a vbscript - vbscript

I have a network with over 700 users and I want to create a script that could change the owner of the home folders to domain admins and the sub-folders to the users themselves.
This is what I could create with the help of my colleges, but this doesn't work for some reason. Can anyone help me please. Thanks.
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("G:\Userhome\userdirlist.txt", 1)
Set oShell = WScript.CreateObject("WSCript.shell")
Do Until objFile.AtEndOfStream
struserfolder = objFile.ReadLine
oshell.run ("icacls G:\userhome\"+ struserfolder +"\*.* /setowner domainname\" + "struserfolder" + " /t")
oshell.run ("icacls G:\userhome\"+ struserfolder +"\*.* /setowner domainname\Domain Admins")
Loop

You use the string "struserfolder" when you probably mean to use the variable struserfolder. Change this:
oshell.run ("icacls G:\userhome\"+ struserfolder +"\*.* /setowner domainname\" _
+ "struserfolder" + " /t")
into this:
oshell.run "icacls G:\userhome\"+ struserfolder +"\*.* /setowner domainname\" _
+ struserfolder + " /t"
Also, you must quote arguments with spaces. Lack of quotes is probably what prevents the second command from working, because icacls tries to set the owner to domainname\Domain instead of domainname\Domain Admins. This should do:
oshell.run "icacls G:\userhome\" + struserfolder _
+ "\*.* /setowner ""domainname\Domain Admins"""
BTW, why are you trying to change the owner twice? Any object can have just one owner, and you don't gain anything if you make the domain admins group the owner of just the top-level objects in the folder.
If you want to give domain admins access to the users' home directories, change the owner to the local administrators group (domain admins are automatically members of that group) and grant full control on the folder to Administrators, SYSTEM and the user. Then propagate the changed permissions down the directory tree:
path = Chr(34) & "G:\userhome\" & struserfolder & Chr(34)
oshell.run "icacls " & path & " /setowner Administrators /t /c"
oshell.run "icacls " & path & " /grant Administrators:(OI)(CI)F " _
& "SYSTEM:(OI)(CI)F domainname\" & struserfolder & ":(OI)(CI)F"
oshell.run "icacls " & path & " /reset /t /c"
Edit: The Run method returns the exit status of the executed command, which may give you some pointers when things don't work as expected:
rc = oshell.run("icacls " & path & " /setowner Administrators /t /c", 0, True)
WScript.Echo "icacls returned with exit code " & rc & "."
One issue might be that by default Run is asynchronous (parameter bWaitOnReturn defaults to False), i.e. the call returns immediately while the command (icacls) is still running in the background. This may lead to situations where subsequent commands try to change permissions on objects where ownership hasn't been taken yet.
Even more helpful than the return code is ususally the output of the command. However, the way you execute the commands, the command window isn't displayed, and even if it were, it would automatically close as soon as the command finishes. You can force the command window to become visible and stay open after the command finishes, though.
oshell.run "%COMSPEC% /k icacls " & path & " /setowner Administrators /t /c" _
, 1, True
Of course you don't normally want this in production, but it's quite useful when debugging a script.
Another option would be to avoid Run entirely and run the commands via the Exec method. That way you have access to the StdOut and StdErr of the created process:
Set icacls = oshell.Exec("icacls " & path & " /setowner Administrators /t /c")
Do While icacls.Status = 0
WScript.Sleep 100
Loop
WScript.Echo "icacls returned with exit code " & icacls.ExitCode & "."
WScript.Echo icacls.StdOut.ReadAll & icacls.StdErr.ReadAll

Related

Microsoft VBScript runtime error: Path not found

I want to execute a vbs script which provides me the size of a given folder but at the execution it returns me the error:
Microsoft VBScript runtime error: Path not found
Previously, the error was
Microsoft VBScript runtime error: Permission denied
but after these commands:
takeown /f C:\Users /r /d y
icacls C:\Users /grant administrators:F /T
it turned into "path not found". As you can see the folder that I want the size is C:\Users.
Here's my code:
'Created the 18.03.2010
'Easy script for check space folder. You need NRPE_NT daemon on win computer
'##########################################################'
'Install'
'##########################################################'
'1.copy file to c:\ for example... c:\nrpe_nt\bin\check_folder_size.vbs'
'2.set your nrpe.cfg for command for example
'command[check_foldersize]=c:\windows\system32\cscript.exe //NoLogo //T:30 c:\nrpe_nt\bin\check_folder_size.vbs c:\yourfolder 50 78
'50 70 are parameters for warning and critical value in MB'
'3.restart your nrpe_nt daemon in command prompt example.. net stop nrpe_nt and net start nrpe_nt'
'4. try from linux example.: ./check_nrpe -H yourcomputer -c check_foldersize and result can be OK:22,8 MB'
'it is all'
'##########################################################'
Dim strfolder
Dim intwarning
Dim intcritic
Dim wsh
Dim intvelkost
Dim intjednotka
'##########################################################'
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set wsh = CreateObject("WScript.Shell")
'##########################################################'
If WScript.Arguments.Count = 3 Then
strfolder = WScript.Arguments(0)
intwarning = WScript.Arguments(1)
intcritic = WScript.Arguments(2)
Set objFolder = objFSO.GetFolder(strfolder)
intjednotka = 1048576 '1MB->bytes'
intvelkost = objFolder.Size/intjednotka
If (objFolder.Size/1048576) > CInt(intwarning) Then
WScript.Echo "WARNING:" & round (objFolder.Size / 1048576,1) & " MB"
WScript.Quit(1)
ElseIf (objFolder.Size/1024000) > CInt(intcritic) Then
WScript.Echo "CRITICAL:" & Round(objFolder.Size / 1048576,1) & " MB"
WScript.Quit(2)
Else
WScript.Echo "OK:" & Round(objFolder.Size /1048576,1) & " MB"
WScript.Quit(0)
End If
Else
WScript.Echo "UNKNOWN:"& strfolder &"-" & intwarning & "-" & intcritic
WScript.Quit(3)
End If
The line of the call of the script:
check_foldersize = cscript.exe //nologo //T:60 scripts\check_folder_size.vbs C:\Users 4000 5000
Edit: One more useful point : There are no errors when I change the folder C:\Users to C:\Intel for example. So it seems to be a problem linked to the Users folder itself. Then I don't think that iis_iusrs permissions are the cause.

Slashes in windows registry key

I'm trying to set full app path of test.exe in registry as name. But it gives me wrong result.
Expected Output :
Output :
This is the code I'm using
Dim WshShell, bKey
Set WshShell = WScript.CreateObject("WScript.Shell")
WshShell.RegWrite "HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers\D:\\\Program Files\\\test.exe", "RUNASADMIN", "REG_SZ"
Is there any workaround for this?
This MSDN KB article says:
Due to the limitations of the RegWrite method of Windows Script Host (WSH) it is not possible to write a "\" (backslash) in a key name or value name.
This is by design and there is no workaround with WSH. The article goes on to suggest using alternative scripting objects (WMI, RegObj.dll) to set such key and value names.
Still using vbscript, try to create a .reg file and execute it.
Some code that does it in another path of the registry:
Set fs = CreateObject("Scripting.FileSystemObject")
Set shell = CreateObject("WScript.Shell")
'create .reg file:
Set reg_file = fs.CreateTextFile("slash.reg")
reg_file.WriteLine "Windows Registry Editor Version 5.00"
reg_file.WriteLine "[HKEY_CLASSES_ROOT\.txt]" 'put your path here
key_name = "D:\\Program Files\\test.exe" 'must be escaped inside the .reg file, so they enter as single slash in the registry
key_value = "RUNASADMIN"
reg_file.WriteLine """" & key_name & """=""" & key_value & """" 'escaping quotes inside vbscript string literal
reg_file.Close
'run it automatically to insert data (may ask for elevated privileges):
path = Replace(WScript.ScriptFullName, WScript.ScriptName, "")
shell.run "regedit.exe /s """ & path & "slash.reg"""
Just click OK when asked for elevation. You may want to check the created file, so I am not deleting it in my code.
Another approach is to use WMI Registry provider
Const REG_HIVE_HKLM = &H80000002
Const ROOT = "Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers"
Set SWBemlocator = CreateObject("WbemScripting.SWbemLocator")
Set SWbemServicesReg = SWBemlocator.ConnectServer(".", "root\DEFAULT","","")
Set reg = SWbemServicesReg.Get("StdRegProv")
' if key is missing - create first, otherwise value won't be saved (without exception)
reg.CreateKey REG_HIVE_HKLM, ROOT
' set value
reg.SetStringValue REG_HIVE_HKLM, ROOT, "D:\Program Files\test.exe", "RUNASADMIN"
Try using slash (/) as your file system path separator. WSH will correctly write A *nix style path to a registry value while a Windows style path will write as a sequence of sub keys. However this depends on the software that is reading the registry value to grok the path correctly. Many components of Windows will now accept either path separator. Give it a try.
You can use .ShellExecute to edit the registry with reg.exe.
ShellExecute guide
Syntax:
CreateObject("Shell.Application").ShellExecute "application", "parameters", "dir", "verb", window
CreateObject("Shell.Application").ShellExecute 'some program.exe', '"some parameters with spaces"', , "runas", 1
Key:
Keyword
Action
application
The file to execute (required)
parameters
Arguments for the executable
dir
Working directory
verb
The operation to execute (runas/open/edit/print)
window
(1=normal, 0=hide, 2=Min, 3=max, 4=restore, 5=current, 7=min/inactive, 10=default) View mode application window
Example:
CreateObject("Shell.Application").ShellExecute "reg.exe", "add " & """HKCU\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers""" & " /v " & """" & Command & """" & " /t REG_SZ /d " & """~ DISABLEDXMAXIMIZEDWINDOWEDMODE RUNASADMIN HIGHDPIAWARE""" & " /f ", , , 0
Where the Command is a path with a backslash to a .exe (like D:\Path with backslash\some program.exe) that passed to your application as command line parameters (like start "" "C:\Path\your application.exe" "D:\Path with backslash\some program.exe".
I used MsgBox to ensure that is correct:
MsgBox "add " & """HKCU\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers""" & " /v " & """" & Command & """" & " /t REG_SZ /d " & """~ DISABLEDXMAXIMIZEDWINDOWEDMODE RUNASADMIN HIGHDPIAWARE""" & " /f "
You can also use CreateObject("WScript.Shell").Run as an alternative to run reg.exe and edit the registry.
Example:
CreateObject("WScript.Shell").Run "reg.exe" & " delete " & """HKCU\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers""" & " /v " & """" & Command & """" & " /f ", 0

Run cmd from vbscript silently

I created the following runas.vbs script:
**************
Option explicit
Dim oShell, k
Const PASSWORD = "Pass123~"
set oShell= Wscript.CreateObject("WScript.Shell")
WScript.Sleep 500
oShell.run("RunAs /noprofile /user:%computername%\postgres " & Chr(34) & "cmd /c\" &
Chr(34) & WScript.Arguments(0) & "\" & Chr(34) & Chr(34))
WScript.Sleep 1000
For k=1 To Len(PASSWORD)
WScript.Sleep 200
oShell.SendKeys Mid(PASSWORD,k,1)
Next
Wscript.Quit
**************
I use this vbscript in a Batch file to run initdb.exe (Postgresql).
Used as:
cscript //Nologo //B runasNSPostgres.vbs ""%LG_PATH%\initdb.exe" --locale=C --encoding=UTF-8 -U %DBADMIN% -D "%DBDATA%""
When this command is executed, another command prompt screen opens up which starts the initdb processing. I do not want the new cmd prompt screen to show up. I want the initdb.exe to run in the background.
If you hide the command prompt window, you won't be able to use SendKeys to send it your password keystrokes.
You can use another method, though. Try using the ECHO command and pipe its output to RunAs.
oShell.Run "echo " & PASSWORD & " | runas /noprofile ...", 0
Use a 0 as the 2nd parameter to prevent the window from appearing.

Permission elevation from VBScript

We run Dynamics GP. Because of the way it stores forms/reports, I need to have some install scripts that copy a .SET file into the program directory. This can be done manually, but it's much quicker to just have a user run an installer script which installs the proper files for them.
I've been building a VBScript installer that copies the necessary files around. The tricky part is that some clients are running Windows XP, and some are running Windows 7 (or even 8). UAC is enabled, so permissions come into play.
The way I've tried to do it is by blindly attempting to copy the files, and if a permission error is detected, it relaunches the script with administrator permissions. Where we've run into problems is some (all?) Windows 7 machines have virtualized file/registry writes enabled, so when the script tries to copy files into C:\Program Files\Microsoft Dynamics\GP2010, it silently fails and copies them to the user's AppData\Local\VirtualStore directory. This doesn't work properly with GP.
So what I need to do is have the script copy the files to C:\Program Files (not the VirtualStore directory), and elevate permissions only if necessary. If I have it elevate across the board, this causes the Windows XP machines to simply pop up a cryptic "Run As" dialog box when launching the script.
Here's what I have so far:
Dim WSHShell, FSO, Desktop, DesktopPath
Set FSO = CreateObject("Scripting.FileSystemObject")
Set WSHShell = CreateObject("WScript.Shell")
Desktop = WSHShell.SpecialFolders("Desktop")
DesktopPath = FSO.GetAbsolutePathName(Desktop)
'Set working directory to directory the script is in.
'This ends up being C:\Windows\System32 if the script is
'started from ShellExecute, or a link in an email, thus breaking
'relative paths.
WSHShell.CurrentDirectory = FSO.GetFile(WScript.ScriptFullName).ParentFolder
On Error Resume Next
If FSO.FolderExists("C:\Program Files (x86)") Then
WScript.Echo "Installing 64-bit."
FSO.CopyFile "64-bit\*.set", "C:\Program Files (x86)\Microsoft Dynamics\GP2010\", True
FSO.CopyFile "64-bit\*.lnk", DesktopPath, True
ElseIf FSO.FolderExists("C:\Program Files\Microsoft Dynamics\GP2010\Mekorma MICR") Then
WScript.Echo "Installing 32-bit (with MICR)."
FSO.CopyFile "32-bit MICR\*.set", "C:\Program Files\Microsoft Dynamics\GP2010\", True
FSO.CopyFile "32-bit MICR\*.lnk", DesktopPath, True
Else
WScript.Echo "Installing 32-bit."
FSO.CopyFile "32-bit\*.SET", "C:\Program Files\Microsoft Dynamics\GP2010\", True
FSO.CopyFile "32-bit\*.lnk", DesktopPath, True
End If
If Err.Number = 70 Then
CreateObject("Shell.Application").ShellExecute "wscript.exe", """" & WScript.ScriptFullName & """" , "", "runas", 1
WScript.Quit
ElseIf Err.Number <> 0 Then
MsgBox "Error " & Err.Number & vbCrLf & Err.Source & vbCrLf & Err.Description
Else
MsgBox "Installed successfully."
End If
In summary: How do I have a VBScript elevate permissions without causing XP to stall at a "Run As" dialog box, and without causing Windows 7 to copy the files to AppData\Local\VirtualStore instead?
Improved on #db2 answer:
real elevation testing, without depending on passed arguments
passes all original arguments to the elevated script
uses the same host of the initial script: wscript.exe, cscript.exe, whatever
Code:
Set OSList = GetObject("winmgmts:").InstancesOf("Win32_OperatingSystem")
For Each OS In OSList
If InStr(1, OS.Caption, "XP") = 0 And InStr(1, OS.Caption, "Server 2003") = 0 Then
With CreateObject("WScript.Shell")
IsElevated = .Run("cmd.exe /c ""whoami /groups|findstr S-1-16-12288""", 0, true) = 0
If Not IsElevated Then
Dim AllArgs
For Each Arg In WScript.Arguments
If InStr( Arg, " " ) Then Arg = """" & Arg & """"
AllArgs = AllArgs & " " & Arg
Next
Command = """" & WScript.ScriptFullName & """" & AllArgs
With CreateObject("Shell.Application")
.ShellExecute WScript.FullName, " //nologo " & Command, "", "runas", 1
WScript.Echo WScript.FullName & " //nologo " & Command
End With
WScript.Quit
End If
End With
End If
Next
' Place code to run elevated here
Seems like this is the simplest way to do it.
Check OS version.
If it's not XP or 2003 (I don't anticipate this running on anything older), re-execute with elevation.
Here's the code block I added to the beginning of the script:
Dim OSList, OS, UAC
UAC = False
If WScript.Arguments.Count >= 1 Then
If WScript.Arguments.Item(0) = "elevated" Then UAC = True
End If
If Not(UAC) Then
Set OSList = GetObject("winmgmts:").InstancesOf("Win32_OperatingSystem")
For Each OS In OSList
If InStr(1, OS.Caption, "XP") = 0 And InStr(1, OS.Caption, "Server 2003") = 0 Then
CreateObject("Shell.Application").ShellExecute "wscript.exe", """" & WScript.ScriptFullName & """ elevated" , "", "runas", 1
WScript.Quit
End If
Next
End If

Psexec not outputting to log file in VB script

I have a VB script which needs to run psexec to launch an app called md5 on a remote server. Md5 generates a hash key of a file and takes one parameter - the file path\name. I need to retrieve the has key that is generated to store in a variable. Below is the code I am using:
Set objShell = CreateObject("Wscript.Shell")
strcomputer = "remotecomputer"
tempDest = "C:\somedir"
filename = "somefile"
strCommand = "psexec -accepteula \\" & strcomputer & " -c md5.exe " & tempDest & "\" & filename & " > log.txt"
Set objExecObject = objShell.Exec("%comspec% /c " & strCommand)
Do While objExecObject.Status <> 1 'loop until previous process has finished
WScript.Sleep 100
Loop
The MD5 command is run however nothing is written to the log file. When I copy and paste strCommand (substituting all the variables for the actual data) into a cmd prompt and run it, it successfully writes the output of Md5 to the log file.
At the end of the day I just need the output of Md5, if anyone knows a better way than writing it to a log file please let me know. I have already tried using objExecObject.StdOut.Readall() to try and catch the output which resulted in random failures - sometimes it would catch the output, sometimes it wouldn't, without changing anything in the script.
Just a guess: Are you sure about what the current directory is when the script is running? Try giving an absolute path to the log file and see if it helps.
I found a solution for this. Instead of using the following code:
strCommand = "psexec -accepteula \\" & strcomputer & " -c md5.exe " & tempDest & "\" & filename & " > log.txt"
Set objExecObject = objShell.Exec("%comspec% /c " & strCommand)
Do While objExecObject.Status <> 1 'loop until previous process has finished
WScript.Sleep 100
Loop
I used this instead:
strCommand = "psexec -accepteula \\" & strcomputer & " -c md5.exe " & tempDest & "\" & filename & " > log.txt"
objShell.Run "%comspec% /c " & strCommand, 0, true
The script is now redirecting to log.txt properly.

Resources