I have an update...
~Multi-user Environment Warning~
On 6/26/2009, the author discovered a problem with this process's behavior in a multi-user environment: ie. "Terminal Server" for Windows Server 2008 Release 1. When an Administrator runs the code, he is able to overwrite other Scheduled Tasks where as a general user cannot. Therefore this will work: Admin1 logs in all of his logon scripts execute properly by creating or refreshing previous scheduled tasks. Then Admin2 logs in. His logon scripts also execute properly because he has full privileges over all scheduled tasks. Now along comes User1. He logs in but none of his logon scripts execute because he cannot overwrite the scheduled tasks that were previously created by Admin1. The only way I've found to get around this hiccup is to append a unique ID to the end of each task name. Something like the user ID or full user name.
~Terminal Server Security Settings~
7/1/2009. I was still having massive issues with this code in a Terminal Server environment. ~Sometimes~ users were not able to "recreate" jobs they created on their last login. For the life of me I couldn't figure out why. Because documentation that could explain this behavior is a little thin, I had to grant Domain Users full access to the registry path "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache" and then use another script I wrote to purge old entries from the list. I will post that to
www.VisualBasicScript.com too (If I can find it). The downside to this is that a healthy portion of the Task Scheduler has had its security settings opened up widely and all calls to "rootFolder.RegisterTaskDefinition()" work / behave like and administrator was calling it.
<job args="\\oraucoc.org\sysvol\oraucoc.org\Policies\{6AACA3D9-F347-494D-AD78-9C5BA193641A}\User\Scripts\Logon\Drive_Mapping.vbs">
<script language="VBScript">
Option Explicit
On Error Resume Next
'More Details can be found here:
'http://technet.microsoft.com/en-us/library/cc766208.aspx
'Background Information:
'NOTE: This text (below) is taken directly from the link
' above and is quite possibly one of the most
' important things you will ever read about scripting
' for Vista.
' Consumption of Windows Vista settings
' Group Policy Scripts can fail due to User Account Control
'
' The main goal of User Account Control (UAC) is to lessen
' the exposure and attack surface of the operating system.
' UAC does this by requiring all users to run in standard
' user mode. This limit minimizes the ability for users To
' make changes that could destabilize their computers Or
' unintentionally expose the network to viruses through
' undetected malicious software (also called malware) that
' has infected their computer.
'
' With UAC, you can run most applications, components, And
' processes with a limited privilege, but have "elevation
' potential" for specific administrative tasks and
' application functions. Windows accomplishes this by using
' two access tokens for each user: limited and elevated
' access tokens. Access tokens identify the user, the user's
' groups, and the user's privileges. The system uses access
' tokens to control access to securable objects and To
' control the ability of the user to perform various
' system-related operations on the local computer.
'
' An elevated token, for a local administrator, includes And
' enables all of the administrative privileges. UAC requires
' local administrators to use their elevated token when
' attempting to perform a system-only task or administrative
' task. A limited token, for a local administrator, includes
' all of the administrative privileges; however, these
' privileges are disabled. This allows Windows to view the
' administrative user and a normal user, with the option To
' elevate their privileges.
'
' By default, all users logging on to Windows Vista use
' their full token to process Group Policy and logon scripts.
' However, they use their limited user token to load the
' desktop and all subsequent processes. Nonadministrative
' limited and elevated tokens are mostly identical, With
' regard to privileges and groups. Therefore, a process
' started with a nonadministrative limited user token can
' view processes started with a nonadministrative elevated
' token. Windows allows this because the viewing application
' does not require any elevation to view the process started
' with the elevated token.
'
' Windows processes a locally logging on administrator the
' same way. Group Policy and logon scripts process using the
' elevated user token, and the desktop and all subsequent
' processes use the limited token. However, there is a
' privilege difference between the limited and elevated user
' token. Therefore, Windows restricts processes started With
' a limited token from the ability to share information With
' processes started with the elevated token.
'
' UAC may prevent Group Policy logon scripts from appearing
' to work properly. For example, a domain environment
' contains a GPO that includes a logon script to map network
' drives. A nonadministrative user logs on to the domain from
' a Windows Vista computer. After Windows Vista loads the
' desktop, the nonadministrative user starts Windows
' Explorer. The user sees their mapped drives. Under the same
' environment, an administrative user logs on to the domain
' from a Windows Vista computer. After Windows Vista loads
' the desktop, the administrative user starts Windows
' Explorer. The user does not see their mapped drives.
'
' When the administrative user logs on, Windows processes the
' logon scripts using the elevated token. The script actually
' works and maps the drive. However, Windows blocks the view
' of the mapped network drives because the desktop uses the
' limited token while the drives were mapped using the
' elevated token.
'
' To get around this issue, administrative users should map
' network drives under the limited user token. This mapping
' is accomplished by using the launchapp.wsf script shown In
' Appendix A, which works by scheduling the commands using
' the task scheduler. The task scheduler launches the script
' under the administrative full token, thereby allowing
' Windows Explorer, other limited token processes, and the
' elevated token process to view the mapped network drives.
'NOTE: ~Multi-user Environment Warning~
' On 6/26/2009, the author discovered a problem with this
' process's behavior in a multi-user environment: ie.
' "Terminal Server"for Windows Server 2008 Release 1.
' When an Administrator runs the code,
' he is able to overwrite other Scheduled Tasks where as
' a general user cannot. Therefore this will work: Admin1
' logs in all of his logon scripts execute properly by
' creating or refreshing previous scheduled tasks. Then
' Admin2 logs in. His logon scripts also execute properly
' because he has full privileges over all scheduled tasks.
' Now along comes User1. He logs in but none of his logon
' scripts execute because he cannot overwrite the scheduled
' tasks that were previously created by Admin1.
' The only way I've found to get around this hiccup is to
' append a unique ID to the end of each task name.
' Something like the user ID or full user name.
'NOTE: ~Terminal Server Security Settings~
' 7/1/2009. I was still having massive issues with this code in a
' Terminal Server environment. ~Sometimes~ users were not
' able to "recreate" jobs they created on their last login.
' For the life of me I couldn't figure out why. Because
' documentation that could explain this behavior is a little
' thin, I had to grant Domain Users full access to the
' registry path
' "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache"
' and then use another script I wrote to purge old entries
' from the list. I will post that to
' www.VisualBasicScript.com too. The downside to this
' is that a healthy portion of the Task Scheduler has had
' its security settings opened up widely and all calls to
' "rootFolder.RegisterTaskDefinition()" work / behave like
' and administrator was calling it.
'================================
' Define Constants
'================================
const TriggerTypeRegistration = 7
const ActionTypeExecutable = 0
const FlagTaskCreate = 2
const LogonTypeInteractive = 3
'================================
' Define Variants
'================================
Dim strAppPath 'The full path and script name of what Is
' to be executed. Think "UNC Path".
Dim boolDebug 'Enables / Disables comments to the screen
Dim objService 'An object of something that interacts
' With Vista's Task Scheduler Service
Dim strTaskName 'The Task's Name
Dim rootFolder 'The task definition will reside in this folder
Dim taskDefinition 'The task that will be run with reduced credentials
Dim triggers 'A reference to the triggers functionality
' within the task object 'taskDefinition'
Dim trigger 'The act of creating a trigger returns an object
' The sole purpose of this Variant is to catch
' the returning object. Nothing else is done with
' it during the life of the script.
Dim Action 'The action of the trigger.
Dim objLog_File 'Log File
Dim boolLog_Screen 'Send debug messages to the screen
Dim boolLog_File 'Send debug messages to a file located at %USERPROFILE%\Drive_Mapping_Log.txt
Dim objFSO
Dim objShell
Dim strUserName 'The Name of the user running this code
'================================
' Populate Variants
'================================
boolLog_Screen = False
boolLog_File = True
strAppPath = WScript.Arguments(0)
boolDebug = False
strUserName = WScript.CreateObject("WScript.Network").UserName
strTaskName = "Launch App As Interactive User " & strUserName & " - " & Split(strAppPath,"\")(Ubound(Split(strAppPath,"\")))
strTaskName = Replace(strTaskName,".","_")
Set objShell = WScript.CreateObject("WScript.Shell")
Set objFSO = WScript.CreateObject("Scripting.FileSystemObject")
Set objLog_File = objFSO.CreateTextFile(objShell.ExpandEnvironmentStrings(objShell.Environment("PROCESS")("USERPROFILE")) & _
"\Vista_Launch_Script_Log_of_" & Split(strAppPath,"\")(Ubound(Split(strAppPath,"\"))) & ".txt",True)
If Err.Number <> 0 Then
Set objLog_File = Nothing
boolLog_File = False
End If
Log_Event "Vista Launch Script Started", boolLog_Screen, boolLog_File
'================================
' Check prerequisites
' before continuing
'================================
If WScript.Arguments.Length <> 1 Then
Log_Event "Usage: cscript launchapp.wsf <AppPath>" , boolLog_Screen, boolLog_File
Set objLog_File = Nothing
WScript.Quit
End If
If Is_UAC_Enabled(".") = False Then
Log_Event "Vista's UAC (User Account Control) is not enabled. ", boolLog_Screen, boolLog_File
Log_Event "Running code without Task Scheduler using Cscript.", boolLog_Screen, boolLog_File
Run_Command Wscript.Arguments(0)
Set objLogFile = Nothing
WScript.Quit
End If
'================================
' Main Code
'================================
'This block of code connects to Vista's Task
' Scheduler service and removes the task that
' will be installed if it is already installed.
' An error might be generated. Catch and
' clear it.
Log_Event "Creating Schedule.Service Object", boolLog_Screen, boolLog_File
Set objService = CreateObject("Schedule.Service")
Log_Event "Connecting to Scheduler Service", boolLog_Screen, boolLog_File
call objService.Connect()
Log_Event "Setting Root Folder and Deleting old Scheduled Event", boolLog_Screen, boolLog_File
Set rootFolder = objService.GetFolder("\")
On Error Resume Next
call rootFolder.DeleteTask(strTaskName, 0)
If Err.Number <> 0 Then Err.Clear
'On Error Goto 0
'Create a new Task. Access its Trigger
' properties. Set the task to trigger
' (and therefore execute) upon registration.
Log_Event "Creating New Task", boolLog_Screen, boolLog_File
Set taskDefinition = objService.NewTask(0)
Log_Event "Setting Task Trigger to Execute at end of Task Creation", boolLog_Screen, boolLog_File
Set triggers = taskDefinition.triggers
Set trigger = triggers.Create(TriggerTypeRegistration)
'Add an action to the task.
'As best as I can figure it, 'Action' is not receiving a
' newly generated object from 'taskDefinition', but rather
' is being told to act as a "pointer" to an object within
' 'taskDefinition'. This is why 'Action' does not appear
' in the command "call rootFolder.RegisterTaskDefinition()"
' that comes up in a few lines. By editing 'Action.Path',
' you are really editing something within 'taskDefinition'.
Set Action = taskDefinition.Actions.Create( ActionTypeExecutable )
Action.Path = strAppPath
Log_Event "Task definition created. About to submit the task..." & VbCrLf & VbCrLf, boolLog_Screen, boolLog_File
Log_Event "If the creation of the task fails then the user is probably ", boolLog_Screen, boolLog_File
Log_Event "not an Administrator.", boolLog_Screen, boolLog_File
'Register (create) the task.
' Notice the inclusion of 'LogonTypeInteractive'.
' Now the script that was passed to this script as an
' argument will run with the reduced credentials.
Log_Event "Registering Task...", boolLog_Screen, boolLog_File
call rootFolder.RegisterTaskDefinition( _
strTaskName, taskDefinition, FlagTaskCreate,,, LogonTypeInteractive)
If Err.Number = 0 Then
Log_Event "Task submitted without Error", boolLog_Screen, boolLog_File
Log_Event "This script is now exiting.", boolLog_Screen, boolLog_File
'Remove_Task_CLI strTaskName, Wscript.Arguments(0)
Else
Log_Event "Programatic Task submission Errored. Trying CLI Task Scheduler.", boolLog_Screen, boolLog_File
'If WScript.CreateObject("WScript.Network").UserName = "gbread" then
Schedule_Task_CLI strTaskName, Wscript.Arguments(0)
'Remove_Task_CLI strTaskName, Wscript.Arguments(0)
Log_Event "...Script Ended...", boolLog_Screen, boolLog_File
'Else
' Run_Command Wscript.Arguments(0)
' Log_Event "Command Was Run without Task Scheduler.", boolLog_Screen, boolLog_File
'End IF
end If
Set objLog_File = Nothing
WScript.Quit
'============================
'Schedule_Task_CLI
'============================
Sub Schedule_Task_CLI(TaskName, strCommand)
'Created: Feb 9th 2009
'
'Revisions:
'(Feb 9th 2009) - Writing Function
'Description: Create a task using Vista's CLI Task Scheduler
'
'INPUT: strCommand -> A string
'
Dim objShell
'Dim TaskName
'TaskName = Split(strCommand,"\")(UBound(Split(strCommand,"\")))
'TaskName = Replace(TaskName,".","_")
Set objShell = WScript.CreateObject("WScript.Shell")
Select Case Lcase(Right(strCommand,4))
Case ".vbs"
strCommand = "%systemroot%\system32\cscript.exe " & strCommand
Case ".wsf"
strCommand = "%systemroot%\system32\cscript.exe " & strCommand
Case ".ps1"
Case Else
End Select
'/F -> Forcefully creates the task and suppresses warnings if the task already exists
'/RL LIMITED -> Run level for the job
'/SC ONCE -> The scheduled frequency
'/TN <blah> -> The task name
'/ST -> The start time
'/TR -> The path and file name of the program to be run at the scheduled time
strCommand = objShell.ExpandEnvironmentStrings("%systemroot%") & _
"\system32\schtasks.exe /create /F /RL LIMITED /SC ONCE /TN " & _
TaskName & " /ST " & chr(34) & split(formatdatetime(Now,4)," ")(0) & chr(34) & _
" /TR " & chr(34) & strCommand & chr(34)
'Zero Hides the window and activates another window.
'False does not wait for the run window to finish executing
Log_Event "About to Register: " & vbcrlf & vbcrlf & _
vbtab & strCommand & vbtab & _
vbcrlf & vbcrlf & "with the CLI Task Scheduler.", _
boolLog_Screen, boolLog_File
If boolLog_Screen Then
'Run the command in a visible window
Log_Event "Running visible", boolLog_Screen, boolLog_File
objShell.Run strCommand, 1, True
Else
'Run the command in a hidden window
Log_Event "Running minimized", boolLog_Screen, boolLog_File
objShell.Run strcommand, 0, True
End If
strCommand = objShell.ExpandEnvironmentStrings("%systemroot%") & _
"\system32\schtasks.exe /Run /TN " & TaskName
Log_Event "About to Start: " & vbcrlf & vbcrlf & _
vbtab & strCommand & vbtab & _
vbcrlf & vbcrlf & "with the CLI Task Scheduler.", _
boolLog_Screen, boolLog_File
objShell.Run strCommand, 0, True
Set objShell = Nothing
End Sub
'============================
'Remove_Task_CLI
'============================
Sub Remove_Task_CLI(TaskName, strCommand)
'Created: Feb 9th 2009
'
'Revisions:
'(Feb 9th 2009) - Writing Function
'Description: Remove a task using Vista's CLI Task Scheduler
'
'INPUT: strCommand -> A string
'
Dim objShell
'Dim TaskName
'TaskName = Split(strCommand,"\")(UBound(Split(strCommand,"\")))
'TaskName = Replace(TaskName,".","_")
Set objShell = WScript.CreateObject("WScript.Shell")
'Now we are going to forcefully end the task and delete it.
strCommand = objShell.ExpandEnvironmentStrings("%systemroot%") & _
"\system32\schtasks.exe /End /TN " & TaskName
Log_Event "About to End the Task: " & vbcrlf & vbcrlf & _
vbtab & strCommand, _
boolLog_Screen, boolLog_File
objShell.Run strCommand, 0, True
wscript.sleep 5000
strCommand = objShell.ExpandEnvironmentStrings("%systemroot%") & _
"\system32\schtasks.exe /Delete /F /TN " & TaskName
Log_Event "About to Delete the Task: " & vbcrlf & vbcrlf & _
vbtab & strCommand, _
boolLog_Screen, boolLog_File
objShell.Run strCommand, 0, True
Set objShell = Nothing
End Sub
'============================
'Is_UAC_Enabled
'============================
Function Is_UAC_Enabled (strComputer)
'Created: Nov 25th 2008
'
'Revisions:
'(Nov 25th 2008) - Writing Function
'
'Description: Call to see if Vista's UAC Enabled?
'
'INPUT: strComputer -> A string
' May contain a computer name or "." for local
'Output: Return Value -> True, LUA is Enabled
' -> False, LUA is NOT Enabled
'
Const HKLM = &H80000002
Dim strKeyPath, strEntryName, strValue
Dim objRegistry
Log_Event "Connecting to / Reading Registry", boolLog_Screen, boolLog_File
Set objRegistry = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\default:StdRegProv")
Is_UAC_Enabled = False
strKeyPath = "SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System"
strEntryName = "EnableLUA"
strValue = ""
objRegistry.GetDWORDValue HKLM,strKeyPath,strEntryName,strValue
Select Case strValue
Case 1
Is_UAC_Enabled = True
Case 0
Is_UAC_Enabled = False
Case Else
Is_UAC_Enabled = False
End Select
End Function
'============================
'Run_Command
'============================
Sub Run_Command(strCommand)
'Created: Nov 25th 2008
'
'Revisions:
'(Nov 25th 2008) - Writing Function
'(Nov 26th 2008) - Call Cscript Interpreter if command is a script file
' - Make objShell.Run windows visible if boolDebug is True
'
'Description: Call to see if Vista's UAC Enabled?
'
'INPUT: strComputer -> A string
' May contain a computer name or "." for local
'Output: Return Value -> True, LUA is Enabled
' -> False, LUA is NOT Enabled
'
Dim objShell
Set objShell = WScript.CreateObject("WScript.Shell")
Select Case Lcase(Right(strCommand,4))
Case ".vbs"
strCommand = "%systemroot%\system32\cscript.exe " & strCommand
Case ".wsf"
strCommand = "%systemroot%\system32\cscript.exe " & strCommand
Case ".ps1"
Case Else
End Select
'Zero Hides the window and activates another window.
'False does not wait for the run window to finish executing
Log_Event "About to start: " & vbcrlf & vbcrlf & _
vbtab & strCommand & vbtab & _
vbcrlf & vbcrlf & "without the task scheduler.", _
boolLog_Screen, boolLog_File
If boolLog_Screen Then
'Run the command in a visible window
Log_Event "Running visible", boolLog_Screen, boolLog_File
objShell.Run strCommand, 1, True
Else
'Run the command in a hidden window
Log_Event "Running minimized", boolLog_Screen, boolLog_File
objShell.Run strcommand, 0, True
End If
Set objShell = Nothing
End Sub
'============================
'Log_Event
'============================
Sub Log_Event(ByVal strEvent_Text,ByRef boolLog_To_Screen,ByRef boolLog_To_File)
If boolLog_To_Screen Then
WScript.Echo strEvent_Text
End If
If boolLog_To_File Then
Err.Clear
objLog_File.Writeline Now & vbTab & strEvent_Text
If Err.Number <> 0 Then
'Error writing to file. Attempt to close file .
' set boolLog_To_File = False so no further file writes
' will occur.
objLog_File.Close
Set objLog_File = Nothing
bool_Log_to_File = False
Err.Clear
End IF
End If
End Sub
</script>
</job>
<message edited by turranx on Friday, September 25, 2009 4:22 AM>