So you want to run powershell scripts without admin rights

First logon as a limited, non admin user on windows 7. Should be easy because large organizations are yanking admin rights and apps are running better without admin rights, so whining to the help desk isn’t as effective as it was.

Create an empty file, say test.ps1

Try to run it using

.\test.ps1

You can’t. Execution of scripts has been disabled. (You try modules and profile scripts, same issue) So you read up and try running

set-executionpolicy remotesigned

And you get an error message about not being able to modify the registry because you are not admin on your machine you are a limited user. And then you think to your self…There’s a time when the operation of the machine becomes so odious, makes you so sick at heart, that you can’t take part! You can’t even passively take part! And you’ve got to put your bodies upon the gears and upon the wheels…upon the levers, upon all the apparatus, and you’ve got to make it stop! And you’ve got to indicate to the people who run it, to the people who own it, that unless you’re free, the machine will be prevented from working at all!

And with a rebel cry we jump to google. A non-admin can still run a batch file as a limited user. So can we execute .ps1 equivallent as a .bat?

Yes! Our friends from the CCCP know how.

Wrap everything in your .ps1 file in a

$code={ #code goes here }

Encode it

[convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($code))

Put it in a batch file like so, names test.bat

powershell.exe -NoExit -EncodedCommand
DQAKAAkAIwBpAHQAZQByAGEAdABlACAAbgB1
AG0AYgBlAHIAcwAgADEAIAB0AGgAcgBvAHUAZwBoACAAMQ
AwAA0ACgAJADEALgAuADEAMAAgAHwAIABmAG8AcgBlAGEA
YwBoAC0AbwBiAGoAZQBjAHQAIAB7AA0ACgAJ
ACMAIABqAHUAcwB0ACAAbwB1AHQAcAB1AHQAIAB0AGgAZQB
tAA0ACgAJACIAQwB1AHIAcgBlAG4AdAAgAG
8AdQB0AHAAdQB0ADoAIgANAAoACQAkAF8ADQAKAAkAfQANAAoA

The code executes and is the moral equivallent of executing a .ps1 file. Except you have no clue what the source is by casual inspection. And it means all non-admin users have to run their ps1 code through a build step.

Jeffrey Snover, tear down this wall! Thanks.

Walking with PowerShell

Now let us go back in time when dinosaurs wrote .BAT files and find out how they should have done it in PowerShell

%PATH%

becomes

get-content env:PATH

SET FOO=Bar

becomes

set-content env:FOO

Running a Ps1 File and PowerShell Signing

Invoking a Powershell Script

By default, powershell does not run any unsigned ps1 files. Also, to invoke a script, you need to prefix it with .\

.\myScript.ps1

The following will get a bemusing error message:

myScript.ps1

If you don’t sign it, you will get an error message about not trusting the code.

Should we sign it?

If you distribute the code, you might want to sign it. If you are paranoid about accidentally running malicious code, you might want to sign your code, maybe if you work at a bank or some other attractive target for hackers. Signing PowerShell Scripts is a very challenging chore.

  • If the script moves to another machine, you will need to bring the certification authority with it.
  • If the script’s signature expires, you will need to resign the script (and this will probably be a different user, with different access to tools and documentation)
  • If you don’t do self signatures, you will have to harass the system administrator who has access to the companies SSL cert, or worse spend money on a SSL cert.

Here is how to turn off signing and return to the security policies that govern .BAT files.

Set-ExecutionPolicy Unrestricted

Set-ExecutionPolicy RemoteSigned

Remote signed is not a hassle like the other two because it only applies to scripts that were sent to you, say by the Mafia.

Registering the PowerShell MSAccess Provider

Compile the provider. It is called a provider, but treated like a snap in.

set-alias installutil $env:windir\Microsoft.NET\Framework\v2.0.50727\installutil.exe

installutil c:\….. path to the .net assembly

see if it worked

get-PSsnapin -registered

In the next step use the snap in name

add-pssnapin “AccessDBProviderPSSnapIn05

This was the part I missed. This time the name is the provider name.

New-PSDrive -Name AccessDB -PSProvider FileSystem -Root “c:\myaccess.mdb”

To get to the new ‘drive’, use

set-location … name of drive…

You should now be able to navigate to the MS-Access database as if it was a file system and use PowerShell file system commands to do stuff.

Hosting PowerShell in ASP.NET

PowerShell can be run inside of your application, even an ASP.NET one. I started with Dominick’s sample on hosting powershell in ASP.NET.

The UI was modestly difficult. I used a label and a textbox that uses AJAX to update the label with the output of a PowerShell command each time I hit enter and trigger the Ajax updated event.

First odd thing, the runspace (which is like the powshell session) is stored in cache, so it is a global object. If the application is multi-user or multi-browser with one user, effects in one browser or user will bleed over into the other. So far my only idea for possible improvement is to store the user session ID or user name into the cache. The user docs state that there needs to be a one to one relationship between the PSHost and the runspace.

Next odd thing, not all commands work. When using the default PSHost and if you don’t add a “output-default” command, the runspace emits a pipeline of objects (roughly corresponding to rows of output you’d get from a cmd.exe command). In Dominick’s example, the rows are converted to text and usually look okay. I added the ‘output-default’ command to the pipeline and switched to a custom host:

So I wrote a class that extended PSHost
and
PSHostUserInterface
. The former has to do with starting and stopping PowerShell. The latter is the basic user interface. With the PSHostUserInterface, you can provide a way to output warnings, writeline’s etc. There is also a Raw UI that needs to be extended to get commands like CLS to work. The PSHostRawUserInterface lets the host know what to do when someone’s code wants to write to a particular part of the screen. From the docs: “The user interface model is based on a two-dimensional grid of cells referred to as the screen buffer.”

Communication between the ASP.NET page and the PSHost was challenging. They appear to run on different threads. I ended up passing state between the page (which displays the output) and the PSHost (which has first access to the output after an invoke) by shuffling the text rows to a database and back. There isn’t an obvious way to let the PSHost keep a usable reference to a page or to pass the PSHost a usable reference to the calling page.

Now if I run my hosted PowerShell, the popularity of commands like “more” create a problem. More launches a process and waits for input. The process is launched as the ASPNET user (I’m running IIS on XP), even though I have impersonation turned on in web.config.

So my next challenges:

  1. Figure out if it is possible to get impersonation to work (either by launching the PowerShell host with credentials of my choosing or changing the credentials once inside of PowerShell)
  2. Implement PSHostRawUserInterface. It looks like it will be required to get anywhere close to approximating a windows PowerShell user experience.
  3. Figure out what to do when a process gets launched accidentally from ASP.NET–and is invisible! Unfortunately almost no applications check to see if they are being run remotely and can’t expect to get user input.
  4. Determine what constitutes a carriage return that means “line continuation inside a command or expression” and what means “send this command to the pipeline and invoke it!” This will also be tricky to do inside of a multiline HTML textbox.