Power to the People (Windows PowerShell Logon Scripts)

posh-logon

It’s true that PowerShell is addictive. But it’s primarily designed for use as an *Administrative* scripting and management environment. Many people ask about doing ASP.NET web content via PowerShell and also getting script to affect users, such as the following fusion of a question from a PowerShell class I taught last week, and another from a few months ago.

“Wow. PowerShell scripts are pretty handy. We have logon scripts written in VB script now. How can we use PowerShell scripts as logon scripts?”

 

Excellent question. You can still use a VB script as a logon script, and have that launch PowerShell just to run a specific command or to run a PowerShell script. VBscript calls PowerShell. Consider the following example which is described in a Microsoft article <http://www.microsoft.com/technet/scriptcenter/topics/winpsh/manual/run.mspx>.

’ logon.vbs - brief example set objShell = CreateObject( "Wscript.Shell" ) objShell.run( "powershell.exe -noexit c:scriptslogon.ps1" )

 

With that VBscript as your logon script, assigned through either Local Users and Groups, Active Directory Users and Computers, or via AD-based Group Policy, we can effectively run PowerShell scripts as logon scripts.

Here are some requirements.

  1. In order to execute the script locally, each workstation or server on which this will be run will need to have Windows PowerShell installed.
  2. PowerShell’s execution policy on each computer must allow the execution of the scripts in question.
  3. The scripts must be accessible at a path visible to the client.
  4. Any modules and extensions (e.g. Exchange Management Shell) would need to be loaded.

Of course, normal local, site, domain, organization unit, … (L, S, D, OU…) scope for Group Policy applies to the users (for Logon/Logoff scripts), or computers (for Startup/Shutdown scripts) in Active Directory affected by the policy.

The example quoted above from the Microsoft article includes -noexit parameter when launching PowerShell. That typically would not be used when running a logon script, as it leaves the shell open for the user on the target system after executing the script. Of course there are cases where that may be the appropriate desired behavior. Simply removing the -noexit parameter from that example reverses that behavior – as soon as the script completes, the shell will exit.

To find out what additional parameters are available when launching PowerShell, simply type powershell -?. That shows a usage message followed by a description of each parameter. We’ll include just the usage text at the top of that output here.

powershell[.exe] [-PSConsoleFile <file> | -Version <version>] [-NoLogo] [-NoExit] [-NoProfile] [-NonInteractive] [-OutputFormat {Text | XML}] [-InputFormat {Text | XML }] [-Command { - | <script-block> [-args <arg-array>] | <string> [<CommandParameters>] } ]

 

Beside not using the -NoExit parameter, it is likely that a lot of logon scripts should also run with the -NonInteractive parameter instead. There is much power in many of the other parameters, especially the ability to use XML input and output formats, yet -PSConsoleFile and -Command are the most dramatic.

Briefly, -PSConsoleFile is typically used to extend the capabilities of Windows PowerShell with new providers or cmdlets. If you don’t know a cmdlet or provider is with respect to PowerShell, just remember the words “extend the capabilities.” Details really are beyond the scope of this blog post, but again, remember to include modules or extensions that your script(s) are expecting are available.

Giving a command string is the most important part of the invocation of PowerShell, unless you’ve specified -NoExit. The -Command parameter name is actually optional. The command parameter value can be a complicated script block if you’re invoking one instance of PowerShell from another, but that doesn’t apply to logon scripts launched the way we’re describing here. Script blocks are delimited by braces { }. The hyphen option allows PowerShell to take its standard input stream and use that as the command string to use. Here’s a quick example.

"get-date" | powershell -

 

In this example, the string “get-date” is taken as the command that PowerShell should run. The same thing as this simplistic example could have accomplished by simply giving a command string as the -Command parameter value to PowerShell. This string form is what is most often used with logon scripts. To see how this can be used, consider the following examples which are derived from the help information from powershell -?.

powershell -command {get-eventlog -logname security} powershell -command "{get-eventlog -logname security}" powershell -command "&{get-eventlog -logname security}"

 

The first of these takes the code block {get-eventlog -logname security} and acts upon it. The second form takes the string “{get-eventlog -logname security}” which is considered as a string because it’s delimited with quotation marks, and runs it as a PowerShell which… I hope this isn’t a surprise for those of you just learning PowerShell, but it echoes back the string. Perhaps that’s not what you wanted? You wanted to run the code block which is within the string? Ah, then you should say so. How? With the powerful ampersand (&) operator – this is sometimes called the “call” or “run” operator – as shown in the third example here. In this case, the string “&{get-eventlog -logname security}” is passed as a PowerShell command and this does explicitly say to execute the rest of the string as a code block. This is mentioned in “get-help about_Script_Block.”

At what location do the logon scripts run? Remember that we are having Group Policy configured with the VBscript file which in turn launches the PowerShell script. Therefore, it depends on the location where the VBscript will run. When adding the script to the Group Policy Object (GPO), you would navigate to the User Configuration, Windows Settings Scripts (Logon/Logoff), Logon. The properties of that Logon node of the GPO allows you to use the Show Files… button to check the default path to the script. For the Local Computer Policy, this would normally be C:WindowsSystem32GroupPolicyUserScriptsLogon. Similarly, the Add… button on the Logon node brings up the Add a Script dialog with a Browse… button to select the VBscript file to add to the GPO. Script Parameters can also be specified in that dialog. Therefore, in the Local Computer GPO, if the default path is used, the location of the VBscript would be where the script runs. Let’s assume that the GPO is configured with the following VBscript file as a logon script, logon.vbs.

’ logon.vbs - brief example set objShell = CreateObject( "Wscript.Shell" ) objShell.run( "powershell.exe logon.ps1" )

 

Because this script invokes powershell.exe with an anonymous -Command parameter of simply logon.ps1 – unqualified, therefore in the local folder – that PowerShell script will run at the same location as the VBscript. Consider the following example of a test script logon.ps1.

"hello, world" $n = read-host -prompt "Name" "hello $n" get-childitem get-childitem | out-file -append xyzzy.txt &{ "Log in to computer: {0} at {1}" -f (hostname), (get-date) $os = get-wmiobject win32_operatingsystem "Using {0} (version {1})" -f $os.Caption, $os.Version "Logged on as {0} ({1})" -f (whoami /upn), (whoami /fqdn) } >>plugh.txt

 

Any files which this script references would be in the same folder. By default, this would be in C:WindowsSystem32GroupPolicyUserScriptsLogon for the Local Computer Policy. The VBscript, PowerShell script, and any files which those scripts reference without folder specifications would be in that folder.

How would this path be different for an Active Directory-based GPO? The path would normally be in the SYSVOL share. For example, in the domain nanoware.net, we might have:

\nanoware.netSysVolnanoware.netPolicies{335E7174-A68E-4431-9258-CAFFA948895A}UserScriptsLogon

Note that this example script would run visibly in a command window and display the text “hello, world,” then prompt for the user to enter a name. This kind of user interaction is not typical behavior for a logon script, however it’s worth noting that it is possible to do this. In similar fashion, this script continues with another “hello” message with the name which the user had entered, and then a listing of the files in the current location where the script is running (by virtue of Get-ChildItem). If this input/output is not desired, these lines could be removed from the script. An alternative would be to have the VBscript invoke powershell.exe with the -NonInteractive parameter. Doing so would prevent this console output and would also not wait for user input, effectively running the script invisibly.

Note that the rest of this script redirects its output to files. As the paths to these files are not relative to the user’s documents folder or other location to which an ordinary user would normally have write permissions, this could fail with an access denied error for non-administrative users. For example, normal users typically don’t have write access into a GPO.

Although there is so much more to delve into on this subject, I hope that this tiny bit has helped get you started if you’ve ever had that question… Can I use Windows PowerShell for logon scripts?

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.