PowerShell code behaving differently under SCCM and MDT TaskSequences - powershell

I've got code that has been extensively tested across multiple PowerShell sessions, multiple machines, etc all doing exactly what I expect.
switch ($e.Data.Type) `
{
{$_ -in [Policy.Reg]::SZ, [Policy.Reg]::EXPAND_SZ} `
{
$e.Data[$e.Data.Type] = ([string]$e.Data[$e.Data.Type]) -replace "{PC}", "$PC"
}
{$_ -in [Policy.Reg]::MULTI_SZ} `
{
$e.Data[$e.Data.Type] = [string[]]($e.Data[$e.Data.Type] | % { $_ -replace "{PC}", "$PC" })
}
}
[Policy.Reg] is an Enum that $e.Data.Type is a variable of that enum
Running the code from the ISE or the Powershell command line code works every time.
When I add it as a RunPowerShell task sequence step (MDT 2012/2013 and SCCM 2012 all tried) the task sequence fails with
you must provide a value expression on the right-hand side of the '-' operator. [Policy.Reg]::SZ, [Policy.Reg]::EXPAND_SZ
I'm at a complete loss what is going on when a TaskSequence runs a Powershell script that makes it different than when I manually execute the some code.

So really quick it seems my issue was with MDT itself.
The standard wsf and vbs files that control PowerShell Execution check for the existence in the registry for the currently installed version and then runs one of two executables that sets up a unique PowerShell environment.
One of the Exes just runs the most recent version of powershell.
The other forces it to run in powershell 2.0 (for compatibility i'm guessing)
Here's the issue the LOGIC in the script file is all wrong
Instead of being something like
if (PSVersion >= 2.0) then
Run newest PowerShell
else
Run PowerShell 2.0
end if
It looks like
if (PSVersion = 3.0 then
Run newest Powershell
else
Run Powershell 2.0
end if
So since all my images have PowerShell 4.0 installed on them, well as LOGIC dictates 4.0 is NOT equal to 3.0 so off to PowerShell 2.0 we go and toss out ALL the things we have gotten used to having since we spent a LONG time ensuring every system in our environment is at least on PowerShell 4.0
So in the end a little code change to the code Launching the PowerShell Scripts fixed the issue.

Related

Output all Invoke-Expression in PowerShell v2

Trying to run an command-line executable from within a PowerShell script, unfortunately the current limitation is the host running only version 2.
The command runs fine, however it appears to only be partial output that is added to the variable, and just by chance the most important information is being missed. Is there anyway to capture everything into the output?
$output = Invoke-Expression ($itemCommand)
Write-Output $output
Testing on v3 and v4 works just fine, in fact was able to get away without using $output.
Anyone know if it is possible to have a portable copy of PowerShell v3 or v4 on a host running v2?

CmdLet works in PowerShell but not in PowerShell ISE

Lately I have been developing PowerShell scripts on a Window 8 machine instead of on a Windows Server 2012R2 which I normally do.
I have encountered two very strange things that never happened on the server.
I know, two questions in one post, but I believe they might be connected.
After a while cmdlets just stop producing any output whatsoever, no error messages, no verbose output. It doesn't matter if I run it in the "lower" command part or in the "upper" script editor, and no difference if I run a selected part of the script or the whole thing. If I switch to a "normal" PowerShell the cmdlet works fine. If I restart PowerShell ISE, the cmdlet start working again, but only for a like 2-5 execution (not at all exact number) after which it stops working again. Not all cmdlets behave this way, mostly had problems with Storage related cmdlets (Format-Partition, Get-Disk, Get-Volume and so on).
After a while cmdlets that used to work fine in the "upper" script part of PowerShell ISE just stops working. The cmdlets still work fine in the "lower" command part of the ISE GUI, but does not work when used in the "upper" script editor, even though they did just a second ago. And it doesn't matter if I run the whole script or just a selected part. And by not working I mean it can't even find the cmdlet (The term '' is not recognized as the name of a cmdlet). The cmdlet is available in the Commands Side-Bar where it show up without any problem and I can access help for it.
So any ideas what is going on here?

How to determine which version of PowerShell is installed on a PC

I am writing a script which check the version of PowerShell. If it's version 4 then proceed, otherwise stop the execution. What I am trying to achieve here is I have five scripts in a folder which are run in order. I would like to put a PowerShell script right at the top which checks the version.
Here is my code:
$psversion= $psversiontable
if($psversion -eq 4.0)
{
./01. Run All Script.ps1
}
"Run All Script" runs the rest of the scripts in order, so I would like to give PowerShell script as 02. check_psversion.ps1
requires -version 4
write-host "this is your version"
You don't need to write any if tests; use the requires directive in all of your scripts (never assume or depend upon execution order, be explicit in each script).
#requires -version 4
If you attempt to execute the script on version 3 or earlier, it will stop because of the requires directive.
Edit: To get the Powershell version number, use $psversiontable.psversion.major, unless you need finer resolution (exact build number).

Can I set the PowerShell execution policy using a script?

I would not be surprised if this is not possible, but I need to set the execution policy (on the 32 bit PowerShell environment) on several build servers - it would be much quicker if I could script this, but my current attempt,
%SystemRoot%\syswow64\WindowsPowerShell\v1.0\
powershell.exe -Version 2
-Command "& {Set-ExecutionPolicy -ExecutionPolicy RemoteSigned}"
completes without any visible error, but the execution
policy is not being set.
I guess it would create a bit of a security hole if users could be tricked into running a script that changed their execution policy, but I thought I would ask the question anyway.
OK - so Richard and Craika are entirely correct and I am a little bit stupid.
After retrying the command - I find that it does work (despite what I said in the question). I guess I must have been getting mixed up between 32 and 64 PowerShell windows (i.e. setting the execution policy in one and then checking the value in another).
Apologies.
You can do this, but the script will be run or not run under the currently (ie. before the script) in force execution policy.
The obvious approach would be to sign the script with a trusted certificate.
However if you want to manage the servers collectively, why not put them in an Active Directory OU or group and then use Group Policy to set the execution policy?
(And don't forget you'll need to set it for both 32 and 64bit processes.)
Your command will work (and does work on my computer) - the execution policy won't affect anything you pass directly into the Command parameter of powershell.exe (and even if it did there is also an ExecutionPolicy parameter). You're definitely running from a 64-bit session?
If you did want to script it, you could run this from your local workstation:
$LaunchLine = 'powershell.exe -Version 2 -Command "& {Set-ExecutionPolicy -ExecutionPolicy RemoteSigned}"'
$ComputerList = "PC01", "PC02"
foreach($Computer in $ComputerList)
{
[String]$wmiPath = "\\{0}\root\cimv2:win32_process" -f $computer
try
{
[wmiclass]$Executor = $wmiPath
$executor.Create($LaunchLine)
}
catch
{
continue;
}
}
It creates a new process on each computer in $ComputerList and executes your command. Like I say, your command does work on my computer. I would think the problem lies in whether whether it's actually running the version of PowerShell you're after.

Creating an executable .exe file from a PowerShell Script?

Is it possible to create an executable file.exe file from a PowerShell Script?
No. At least, not yet, and after 5 versions of PowerShell, it seems unlikely ever.
I wish I could just leave it at that, but other people have provided a bunch of "workaround" type answers I feel compelled to address:
You can wrap your .ps1 script in another script type to make it double-clickable, and you can even generate an executable with the script embedded (see the other answers on this thread) ... but those "executables" require the right version of PowerShell to be already present on the system, so you're not gaining anything by doing that, and you loose a lot of the features of PowerShell (like streaming object output, help documentation, and automatic parameter handling with tab completion for users).
Here is a simple .cmd batch file wrapper (you could extend this to allow parameters):
REM <#
copy %0 %0.ps1
PowerShell.exe -ExecutionPolicy Unrestricted -NoProfile -Command "&{Set-Alias REM Write-Host; .\%0.ps1}"
del %0.ps1
exit
REM #>
### Your PowerShell script goes below here.
### I've put a couple of lines as an example ...
ls | sort length -desc | select -first 5 | ft
ps | sort ws -desc | select -first 10 | ft
I know ...
With Portable PowerShell, it would probably be possible to package up a sort of self-extracting zip that would contain the right version of PowerShell and a script and would work. That's not an executable in any normal sense of the word -- it's a bit like if Valve had decided to just ship a vmware image on a thumbdrive as their solution to letting Linux users play Half Life. However, the product seems abandoned.
With PrimalScript (or PowerShell Studio) or PowerGui or pShellExec, your script can be encrypted, so it's slightly secured against prying eyes ... but this is basically just obfuscation, and essentially no different than the batch file, and in some ways worse.
Out of the box - no. However I have built a PowerShell script that can take a script and create an EXE wrapper around it. I created this script a while ago but decided to blog it here for folks to check out.
Use PowerGUI's Script Editor (it is free and works). Open your script in the PowerGUI Script Editor > Tools > Compile script > Choose whatever options you would like for your .exe (password protect source code, automatically close console after .exe runs, etc.).
Yes, there is a option with PS2EXE to create such *.exe Files.
Usage
The whole thing is really simple and well explained nevertheless
here is a snippet of it:
C:\Scripts\PS2EXE\PS2EXE_0.5.0.0.0\ps2exe.ps1
-inputFile C:\Scripts\ExampleEXE\example.ps1
-outputFile C:\Scripts\ExampleEXE\example.exe -sta -noConsole -runtime40 -verbose -x64
The only bad thing is that the project is depreciated. No Updates or new Versions since 2015.
EDIT:
This projected has been picked up and is being maintained by a new person now. You can find the updated code here, last updated 01/04/2018 as of this edit.
https://gallery.technet.microsoft.com/scriptcenter/PS2EXE-GUI-Convert-e7cb69d5
Version Information
For adding and editing the version information use something like VERPATCH.
UPDATE 2019
Shout out to a git-repo which is called PythonEXE.
It demonstrates how to create an executable from a Python project and also provides a YouTube Step-By-Step Guide.
I understood your question as "Can my PowerShell script generate an executable?" That is what I was trying to do when I found this post. That is possible using the Add-Type command.
Add-Type -TypeDefinition #"
using System;
class Program {
public static void Main(string[] args) {
Console.WriteLine("Hello World!");
}
}
"# -CompilerParameters #{
"GenerateExecutable" = $true
"OutputAssembly" = "test2.exe"
}
PrimalScript from Sapien will generate an exe from a PowerShell script. The machine one which the executable is run must have PowerShell installed.
The solution I found best to distribute a PowerShell script as exe was to wrap it in a NSIS executable.
I write a .nsi script like this:
Name "Maintenance task"
OutFile "maintenance.exe"
ShowInstDetails show
Section "Main"
;Executes the "script-to-run.ps1" PowerShell script
InitPluginsDir
SetOutPath "$pluginsdir\MyOrg" ;It is better to put stuff in $pluginsdir, $temp is shared
;extract the .ps1 and run it, collecting output into details.
File script-to-run.ps1
nsExec::ExecToLog 'powershell -inputformat none -ExecutionPolicy RemoteSigned -File "$pluginsdir\MyOrg\script-to-run.ps1" '
SetOutPath $exedir
SectionEnd
I just have to compile the script to an exe with the NSIS toolchain, and it will run on any OS that has PowerShell, no matter what is the execution policy.
This was inspired by this question How to call PowerShell in NSIS.
There's a project called Portable PowerShell that is still in beta after a couple of years ... might be worth looking at for your needs.
http://shelltools.wik.is/Portable_PowerShell