-2

So here's the deal. Because of a number of... let's just say not PowerShell smart people who will be using an incredibly complex application that I just finished, I need the ability to package it in an exe wrapper.

This shouldn't be that hard

I was able to successfully use PS2EXE, except for some reason with AD, it throws out a whooooole bunch of AD text that I can't get rid of. Tried to fix that for a few days before getting frustrated and moving on.

Then, I discovered PowerGUI. I can't say that I like it, at all. However, its compiler was exactly what I was looking for! Except for the fact that Exchange 2010 snap-ins are not compatible with .NET 4.5 through this application.

I want to make it very clear that my script works perfectly on multiple different computers, but as soon as I use any of these tools, everything breaks.

An exe is the best thing that I can think of to simplify the interface, and keep the Technically Intellectually Stunted from breaking everything, or running to me with every little error because they somehow got into the code and typed something and saved it, and now nothing works and it's the end of the world and they have no idea what happened.

If you guys know of any tools to wrap this up into an exe, or have any other ideas on how to help, I would really appreciate anything you guys can give me.

You have never failed me in the past!

sodawillow
  • 12,497
  • 4
  • 34
  • 44
Nick
  • 1,178
  • 3
  • 24
  • 36
  • This question should be asked on [Software Recommendations](http://softwarerecs.stackexchange.com/). – Paebbels Nov 23 '15 at 20:46
  • Why is it so import that it be delivered as an executable? Why not publish the modules/scripts to the target computers and put shortcuts on the users desktop using GPO/SCCM/MDT/DSC/PackageManagement (ie. whatever your have available) – Mathias R. Jessen Nov 23 '15 at 20:48
  • I don't want them messing with it. All the errors they're having are entirely user error. I'm hoping that an exe will limit their ability to break things... – Nick Nov 23 '15 at 20:49
  • 3
    I agree with @MathiasR.Jessen, but if your only stumbling block is with the exchange snap-ins, just [stop using the snap-in for Exchange and use implicit remoting](http://nsfw.ibnmasud.com/remote-management-of-exchange-2010-using-powershell/). – briantist Nov 23 '15 at 20:49
  • you can use Sapien PowerShell Studio, will do the work... – Avshalom Nov 23 '15 at 20:53
  • 3
    If your chief concern is users "messing" with the application, you could configure a [constrained endpoint](https://4sysops.com/archives/constrained-powershell-endpoints-visible-cmdlets-session-types-and-language-modes/) on an application server, then write wrapper/proxy functions for the functions you want to give the users access to. That way they can't mess with the "internals" of your scripts – Mathias R. Jessen Nov 23 '15 at 20:55
  • 1
    Maybe post the AD errors you got with `PS2EXE`? – sodawillow Nov 23 '15 at 21:05
  • 1
    When I add a similar deployment problem - 1) user's didn't know powershell 2) I didn't want them to mess with things like execution policy, 3) how to start PS, 4) etc. I wrapped it in a batch file. I also wanted to make sure that experienced PS users still had the capabilities of PS, so batch file determined if it was running under PS or not and ran in the current PS session if applicable. I was never too worried that users would mess with the script - they were happy if it "just worked". So whether users liked Explorer, CMD.EXE, or PS, they all were accommodated. – Χpẘ Nov 23 '15 at 21:52
  • @sodawillow I posted the output in a different question: http://stackoverflow.com/questions/33894464/ps2exe-and-active-directory-integration – Nick Nov 24 '15 at 13:05
  • @user2460798 Do you know if wrapping it in a batch file still allows for interaction? A large part of the script requires user input and interaction. I've never wrapped a ps in a batch file before because I've always deployed to people with knowledge of powershell. – Nick Nov 24 '15 at 13:06
  • Yes, the solution I deployed brings up a PS console session that displays how to get help to use the app. You could say as much or little as you want when the console first comes up. As users become used to the system they probably won't want to see a bunch of output every time they start the app, but that's a trade off against new users of the system. I'll turn this comment into an answer to give more details. – Χpẘ Nov 24 '15 at 18:59

2 Answers2

1

From my point of view if you really want an EXE file you should write a .NET application, it's not so hard to embed PowerShell CmdLets.

In order to avoid end user modifying your code I know two solutions :

First : set execution policy to AllSigned on the user computer and sign the scripts you deploy. You can manage to use our own certificates (not expensive at all) or public certificates (more expensive). One of the drawback of this solution is that it does not prevent users from seeing the code. Another big drawback is that a PKI and sign code infrastructure is a lot of wast time.

Second : for non interactive scripts (be carefull it's a kind of makeshift job) :

  1. Create a new user account
  2. Only allow access to the script file for the new account.
  3. Set up a task in the Windows scheduler to run that script file with PowerShell under that specific account. The permissions for the scheduled tasks allow read and execute access to the user(s). Then set the task to "disabled".
  4. Whenever the script file needs to be run, the corresponding task is manually started by the user.

Using this solution will also allow you to remote execute your script.

JPBlanc
  • 70,406
  • 17
  • 130
  • 175
  • I've tried signing it before and that was a horribly failed venture. In addition, I'm learning C# and Java, that's just a slow process because I'm still teaching myself PowerShell as well. Thank you for your advice! – Nick Nov 24 '15 at 12:31
0

When I had a similar deployment problem - 1) user's didn't know powershell 2) I didn't want them to have to understand things like execution policy, 3) how to start PS, 4) etc. I wrapped it in a batch file. I also wanted to make sure that experienced PS users still had the capabilities of PS, so the batch file determined if it was running under PS or not and ran in the current PS session if applicable. I was never too worried that users would mess with the script - they were happy if it "just worked". So whether users liked Explorer, CMD.EXE, or PS, they all were accommodated.

The batch file I wrote first runs a bit of powershell code to determine if the process of the batch file is the grandchild of a powershell process. If it is then the batch file is being invoked from PS. The execution policy is also checked and if it is lenient enough then Wscript.SendKeys is used to send keystrokes to PS to get the script running in the current PS session. If it isn't then it starts a new PS session using -ExecutionPolicy parameter and passes the script as a command line argument (-Command).

This bit of powershell code communicates back to the .CMD file using a return code. Sorry it's cryptic, but the length of command line parameters is limited. Here's the code:

set scr=       $mp=[diagnostics.process]::getcurrentprocess().id
set scr=%scr%; $pp=([wmi]\"win32_process.handle='$mp'\").parentprocessid
set scr=%scr%; $gp=([wmi]\"win32_process.handle='$pp'\").parentprocessid
set scr=%scr%; $ep=[int][microsoft.powershell.executionpolicy](get-executionpolicy)
set scr=%scr%; try {$pnp=1-[int](([wmi]\"win32_process.handle='$gp'\").Name -eq \"powershell.exe\")
set scr=%scr%; } catch {$pnp=1}
set scr=%scr%; $ev = (8 * $pnp + $ep) -band 0xB; %wo% pp: $pp gp: $gp ev: $ev; if ($ev -le 1) {
set scr=%scr%    %wo% Launching within existing powershell session...`n;
set scr=%scr%    $w=new-object -com wscript.shell;$null=$w.appactivate($gp);

set scr=%scr%;   $w.sendkeys(\"^&{{}`$st =cat "%me%";`$sc=`$st -join [char]10 -split 'rem PS script';
set scr=%scr%     `$script:myArgs = `\" %*`\";`$sb=[scriptblock]::create{(} `$sc[3]{)};. `$sb{}}~\")
set scr=%scr%; }
set scr=%scr%; exit $ev
powershell -noprofile -Command %scr%

%wo% is to allow debugging this "checker script". If debugging is on the %wo% is set to write-host. Otherwise it is set to define a "null" function and then invoke the null function. The null doesn't do anything so the message that is the argument to the function is not output.

Note the escaping when invoking SendKeys. ^ is the CMD.EXE escape character and SendKeys has it's own escape mechanism, as does PS.

If run from PS you end up in a PS session thanks to SendKeys. Otherwise the batch file does this:

set scr=       ren function:prompt prompto
set scr=%scr%; function prompt{ 'myApp: '+(prompto)}
set scr=%scr%; $st= (cat %me%) -join \"`n\";
set scr=%scr%; $sx=($st -split 'rem PS script')
set scr=%scr%; $sc=$sx[3]
set scr=%scr%; %wo% myArgs: $myArgs script length: $sc.length 
set scr=%scr%; ^&{$script:myArgs=\"%*\"; iex $sc}

title MyApp
rem Change the number of lines on the console if currently set to 25
for /f "tokens=2" %%i in ('mode con^|findstr Lines:') do if %%i LEQ 25 (mode con lines=50&color 5F)
powershell -noexit -noprofile -command "%scr%"

This "helper script" also can't be too long. So the helper script reads the original .CMD file and then splits it by using the string 'rem PS script'. That string will be in both this helper script as well as in the batch file (separating the batch file statements from PS statements). In my case the string is also in the batch file comments, so that is why the index of 3 is used.

Your PS script can define functions or a module. Your PS script can also output some introductory info to explain to users how to get started, how to get help, or whatever you want.

Rather than just using the PS command line, your PS script could create it's own interactive environment (using Read-Host for example). However I didn't want to do that because it would have prevented experienced PS users from using their knowledge about PS. For example if your script requires a username/password, an experienced PS user could use get-credential to create a credential to send to your script.

Χpẘ
  • 3,403
  • 1
  • 13
  • 22