How do I write this in Powershell script? - powershell

I am very new to Powershell script.
I am trying to run a powershell script from a cmd
powershell.exe Set-ExecutionPolicy Unrestricted
powershell.exe .\startup.ps1
I need two lines within powershell script.
%WINDIR%\system32\inetsrv\appcmd.exe add apppool /name:"VCPool" /managedRuntimeVersion:"v4.0" /managedPipelineMode:"Integrated"
%WINDIR%\system32\inetsrv\appcmd.exe set app "WebRole_IN_0_VC/" /applicationPool:"VCPool"
Can I simply write it like this within the ps1 file?
& %WINDIR%\system32\inetsrv\appcmd.exe add apppool /name:"VCPool" /managedRuntimeVersion:"v4.0" /managedPipelineMode:"Integrated"
Many Thanks

Short answer, including a question: Why the hell does that need to be a PowerShell script?
You can simply create a batch file containing
%WINDIR%\system32\inetsrv\appcmd.exe add apppool /name:"VCPool" /managedRuntimeVersion:"v4.0" /managedPipelineMode:"Integrated"
%WINDIR%\system32\inetsrv\appcmd.exe set app "WebRole_IN_0_VC/" /applicationPool:"VCPool"
and run that directly instead of trying to figure out execution policies, etc.
Furthermore, appcmd should probably be in your PATH, so you can run it directly without needing to specify the full path to the program.
Longer answer, actually using PowerShell: There are two problems here.
You want to run a PowerShell script without having the appropriate execution policy set. This can be done with
powershell -ExecutionPolicy Unrestricted -File myscript.ps1
You need to adjust environment variable usage within PowerShell scripts, as % is not used to expand environment variables there. So you actually need
& $Env:WinDir\system32\inetsrv\appcmd.exe add apppool /name:VCPool /managedRuntimeVersion:v4.0 /managedPipelineMode:Integrated
& $Env:WinDir\system32\inetsrv\appcmd.exe set app WebRole_IN_0_VC/ /applicationPool:VCPool
Note also that you need an ampersand (&) before each line as a variable name in the beginning of a line switches into expression mode while you want to run a command, therefore needing command mode.
Furthermore quoted arguments can be a bit of a pain in PowerShell. PowerShell tries quoting arguments when necessary and it's not always obvious when things go wrong what actually comes out on the other end. In this case the easiest way is to not quote the arguments in any way whcih ensures that they come out correctly:
PS Home:\> args add apppool /name:VCPool /managedRuntimeVersion:v4.0 /managedPipelineMode:Integrated
argv[1] = add
argv[2] = apppool
argv[3] = /name:VCPool
argv[4] = /managedRuntimeVersion:v4.0
argv[5] = /managedPipelineMode:Integrated
PS Home:\> args set app WebRole_IN_0_VC/ /applicationPool:VCPool
argv[1] = set
argv[2] = app
argv[3] = WebRole_IN_0_VC/
argv[4] = /applicationPool:VCPool
However, if appcmd actually needs the quotes around the argument after a colon, then you need to quote the whole argument with single quotes and add the double quotes back in:
& $Env:WinDir\system32\inetsrv\appcmd.exe set app WebRole_IN_0_VC/ '/applicationPool:"VCPool"'

You might like to know you can combine the two launch statements into a single call within your CMD file:
powershell.exe -noprofile -executionpolicy unrestricted -file .\startup.ps1
The only slightly strange thing about calling appcmd.exe from PowerShell is you must surround the parameter in quotes, even if it contains no spaces. You have already done this, so yes it should work.

Related

Why doesn't this msiexec.exe command work in powershell?

I am trying to execute the following command through powershell, in a script invoked by an Advanced Installer generated installation program. The problem is that when the script executes, it chokes on a call to MSIEXEC.exe. More specifically, it puts up a windows dialog of the msiexec help screen.
Ok so maybe it doesn't like the way advanced installer is executing it. So I take the actual line that is causing problems:
msiexec.exe /q /i 'C:\Users\ADMINI~1\AppData\Local\Temp\mongo-server-3.4-latest.msi' INSTALLLOCATION='C:\Program Files\MongoDB\Server\3.4\' ADDLOCAL='all'
And when I execute this directly in powershell, I still get the same stupid help screen. I have tried every conceivable variation of this command line:
/a and /passive instead of /i and /q
with double quotes
with single quotes
the msi unquoted
in an admin elevated shell
in a normal privilege shell
the msi located on the desktop instead of the temp folder
using /x to uninstall in case it was already installed
In all cases, I get the damnable "help" dialog. The only thing that appears to make a difference is if I leave off the INSTALLLOCATION and ADDLOCAL options. (These are apparently used as per "Unattended Installation part 2" found here: https://docs.mongodb.com/tutorials/install-mongodb-on-windows/). In that case it just exits quietly without installing anything.
I'm honestly at my wits' end having been beating my head against the wall on this all afternoon.
By the way, the reason I'm installing mongo in such an absurd way is I need a method of having a single-install system for my company's product. It depends on Mongo, and we have to have it run as a server and use authentication, so I have to have scripts to create the admin and database user and put it into authenticated mode. Since I needed to know where mongo was installed (to execute mongod.exe and mongo.exe) I need to query the user first for the location, then pass on the install location to the mongo installer. If I'm completely off the rails here please let me know that there's a better way.
Thanks
EDITED: I forgot to mention I wrote my complete powershell script and tested it before trying to execute it through advanced installer. The script worked until I tried to run it through the installer. Strange that I still can't execute the command though manually now.
It seems that in order to pass paths with embedded spaces to msiexec, you must use explicit embedded "..." quoting around them.
In your case, this means that instead of passing
INSTALLLOCATION='C:\Program Files\MongoDB\Server\3.4\', you must pass INSTALLLOCATION='"C:\Program Files\MongoDB\Server\3.4\\"'[1]
Note the embedded "..." and the extra \ at the end of the path to ensure that \" alone isn't mistaken for an escaped " by msiexec (though it may work without the extra \ too).
To put it all together:
msiexec.exe /q /i `
'C:\Users\ADMINI~1\AppData\Local\Temp\mongo-server-3.4-latest.msi' `
INSTALLLOCATION='"C:\Program Files\MongoDB\Server\3.4\\"' ADDLOCAL='all'
Caveat:
This embedded-quoting technique relies on longstanding, but broken PowerShell behavior - see this answer; should it ever get fixed, the technique will stop working; by contrast, the
--% approach shown below will continue to work.
A workaround-free, future-proof method is to use the PSv3+ ie helper function from the Native module (in PSv5+, install with Install-Module Native from the PowerShell Gallery), which internally compensates for all broken behavior and allows passing arguments as expected; that is, simply prepending ie to your original command would be enough:
# No workarounds needed with the 'ie' function from the 'Native' module.
ie msiexec.exe /q /i 'C:\Users\ADMINI~1\AppData\Local\Temp\mongo-server-3.4-latest.msi' INSTALLLOCATION='C:\Program Files\MongoDB\Server\3.4\' ADDLOCAL='all'
The alternative is to stick with the original quoting and use --%, the stop-parsing symbol, but note that this means that you cannot use PowerShell variables in all subsequent arguments:
msiexec.exe /q /i `
'C:\Users\ADMINI~1\AppData\Local\Temp\mongo-server-3.4-latest.msi' `
--% INSTALLLOCATION="C:\Program Files\MongoDB\Server\3.4\\" ADDLOCAL='all'
Note that msiexec, despite having a CLI (command-line interface), is a GUI-subsystem application, so it runs asynchronously by default; if you want to run it synchronously, use
Start-Process -Wait:
$msiArgs = '/q /i "C:\Users\ADMINI~1\AppData\Local\Temp\mongo-server-3.4-latest.msi" INSTALLLOCATION="C:\Program Files\MongoDB\Server\3.4\\" ADDLOCAL=all'
$ps = Start-Process -PassThru -Wait msiexec -ArgumentList $msiArgs
# $ps.ExitCode contains msiexec's exit code.
Note that the argument-list string, $msiArgs, is used as-is by Start-Process as part of the command line used to invoke the target program (msiexec), which means:
only (embedded) double-quoting must be used.
use "..." with embedded " escaped as `" to embed PowerShell variables and expressions in the string.
conversely, however, no workaround for partially quoted arguments is needed.
Even though Start-Process technically supports passing the arguments individually, as an array, this is best avoided due to a longstanding bug - see GitHub issue #5576.
[1] The reason that INSTALLLOCATION='C:\Program Files\MongoDB\Server\3.4\' doesn't work is that PowerShell transforms the argument by "..."-quoting it as a whole, which msiexec doesn't recognize; specifically, what is passed to msiexec in this case is:
"INSTALLLOCATION=C:\Program Files\MongoDB\Server\3.4\"

Running PowerShell code inside VBS with many nested quotes, in one line

I'm starting to see double here from staring at this so long. I am trying to use an environment variable inside a script path that I'm going to launch from PowerShell, all initiated from a single line inside an mshta command run from a scheduled task on logon.
In case you were wondering, MSHTA can execute HTML/VBS/JS as if a local GUI app.
mshta vbscript:Execute("CreateObject(""Wscript.Shell"").Run ""powershell -NoLogo -Command """"& '\\$env:USERDNSDOMAIN\FOLDER1\FOLDER with Spaces\Folder3\Script-To-Run.ps1'"""""", 0 : window.close")
Premise: I have to do it this way to prevent momentary popups that flash onscreen when running from a scheduled task in user context. I cannot run this script in the System context.
I know that putting anything inside two single quotes gives me a string literal but if I try double quotes it seems to then use the space in the path. I've tried separating the two and concatenating them in all sorts of ways to no avail.
VBS equivalent of $env:USERDNSDOMAIN is
CreateObject("Wscript.Shell").ExpandEnvironmentStrings( "%USERDNSDOMAIN%" )
If the goal/purpose is to execute a PowerShell script without displaying the powershell.exe console window, you can do this by running it from a VBScript script executed by wscript.exe (not cscript.exe).
Example VBScript script:
Dim WshShell
Set WshShell = CreateObject("WScript.Shell")
WshShell.Run "%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -NoProfile -ExecutionPolicy Bypass -File """d:\path name\whatever.ps1"" [parameter [...]]", 0, False
The second parameter of the WshShell object's Run method is 0, so powershell.exe executes from a hidden window.
(Hint: The -File parameter to powershell.exe is generally easier to use than -Command. It must be the last parameter on the powershell.exe command line.)
End result: VBScript script runs the PowerShell script from a hidden window. Also, the VBScript itself has no console window, because you executed it using wscript.exe (the GUI host).

Run Powershell script from inside other Powershell script with dynamic redirection to file

I have been struggling with this problem and researching around but can't get a solution
My problem: I need to run a Powershell script from inside another Powershell script and redirect the output stream to a file. So far so good.
The real issue comes when I need to control the amount of logging through a variable (e.g. write only errors or only error + warning + success output streams).
I can get around it by hard-coding the command as in:
Powershell -File "\path\myscript.ps1" 2>&1> $logFilePathAndName
However, I want to give the user a couple of options for the redirect operator and want to avoid hardcoding each one of them. For that, I was thinking to just code something similar to :
$logStreams = "2>&1>"
Powershell -File "\path\myscript.ps1" $logStreams $logFilePathAndName
The last command does run my script (myscript.ps1 has no input params) but it does not write anything to the file at $logFilePathAndName.
I tried various syntax with Invoke-Command, Invoke-Expression, the call operator and Powershell -Command with no luck.
Looked at this post, had several tries but I can't just get it to work.
For example this runs my script but does not write anything to the output log:
$logStreams = "2>&1>"
$command = '"C:\myscript.ps1" $_logStreams "C:\outputlog.txt"'
iex "& $command"
Is there a way to pass a variable string for the redirect operator OR run a string containing the entire command with a variable interpolated for the output redirector ?
Your last bit there is very close, but I'm not sure why you have the underscore in there, and you need to escape your dollar sign, and close the entire thing in double quotes to cause string extrapolation.
$logStreams = "2>&1>"
$command = "'C:\myscript.ps1' $logStreams 'C:\outputlog.txt'"
iex "& $command"
I just tested that locally and it works fine.

What is the dash ("-") when used with pipe ("|") in CMD?

I wanted to create some clickable PowerShell scripts, and I found this answer that I modified slightly to be:
;#Findstr -bv ;#F %0 | powershell -noprofile -command - & goto:eof
# PowerShell Code goes here.
I understand Findstr is passing all lines that don't begin with ;#F to the right-hand side of the pipe and the dash specifies where the input should go, but what is the dash character called and where is it documented?
I found an explanation of CMD's pipe operator on Microsoft's Using command redirection operators, but it doesn't mention anything about the dash character.
I presume you mean the - that precedes the &. It has nothing to do with the pipe operator, it is a directive for powershell.
Here is a description of the -Command option excerpted from powershell help (accessed by powershell /?)
-Command
Executes the specified commands (and any parameters) as though they were
typed at the Windows PowerShell command prompt, and then exits, unless
NoExit is specified. The value of Command can be "-", a string. or a
script block.
If the value of Command is "-", the command text is read from standard
input.
BTW - I did not realize FINDSTR accepted - as an option indicator until I saw your question. I've only seen and used /. Good info to know.
The - is to Powershell saying accept the command(s) from stdin rather than from arguments. This is not a feature in cmd / batch and piping. It would work with < as well.
Powershell version 2 adds a "Run with Powershell" right-click context menu item to run scripts . Here you'll find some enhanced shell extensions to run Powershell scripts with elevated privileges. However if you just want to run a Powershell script by double clicking a file, I recommend just calling the Powershell script from a batch script instead of trying to embed Powershell code in the batch script. In the batch script use this: powershell.exe -file "%~dp0MyScript.ps1" where %~dp0 expands to the current directory. This essentially creates a bootstrapper for your Powershell script that you can double click to launch your Powershell script.

Set up PowerShell Script for Automatic Execution

I have a few lines of PowerShell code that I would like to use as an automated script. The way I would like it to be able to work is to be able to call it using one of the following options:
One command line that opens PowerShell, executes script and closes PowerShell (this would be used for a global build-routine)
A file that I can double-click to run the above (I would use this method when manually testing components of my build process)
I have been going through PowerShell documentation online, and although I can find lots of scripts, I have been unable to find instructions on how to do what I need. Thanks for the help.
From http://blogs.msdn.com/b/jaybaz_ms/archive/2007/04/26/powershell-polyglot.aspx
If you're willing to sully your beautiful PowerShell script with a little CMD, you can use a PowerShell-CMD polyglot trick. Save your PowerShell script as a .CMD file, and put this line at the top:
#PowerShell -ExecutionPolicy Bypass -Command Invoke-Expression $('$args=#(^&{$args} %*);'+[String]::Join(';',(Get-Content '%~f0') -notmatch '^^#PowerShell.*EOF$')) & goto :EOF
If you need to support quoted arguments, there's a longer version, which also allows comments. (note the unusual CMD commenting trick of double #).
##:: This prolog allows a PowerShell script to be embedded in a .CMD file.
##:: Any non-PowerShell content must be preceeded by "##"
##setlocal
##set POWERSHELL_BAT_ARGS=%*
##if defined POWERSHELL_BAT_ARGS set POWERSHELL_BAT_ARGS=%POWERSHELL_BAT_ARGS:"=\"%
##PowerShell -ExecutionPolicy Bypass -Command Invoke-Expression $('$args=#(^&{$args} %POWERSHELL_BAT_ARGS%);'+[String]::Join(';',$((Get-Content '%~f0') -notmatch '^^##'))) & goto :EOF
Save your script as a .ps1 file and launch it using powershell.exe, like this:
powershell.exe .\foo.ps1
Make sure you specify the full path to the script, and make sure you have set your execution policy level to at least "RemoteSigned" so that unsigned local scripts can be run.
Run Script Automatically From Another Script (e.g. Batch File)
As Matt Hamilton suggested, simply create your PowerShell .ps1 script and call it using:
PowerShell C:\Path\To\YourPowerShellScript.ps1
or if your batch file's working directory is the same directory that the PowerShell script is in, you can use a relative path:
PowerShell .\YourPowerShellScript.ps1
And before this will work you will need to set the PC's Execution Policy, which I show how to do down below.
Run Script Manually Method 1
You can see my blog post for more information, but essentially create your PowerShell .ps1 script file to do what you want, and then create a .cmd batch file in the same directory and use the following for the file's contents:
#ECHO OFF
SET ThisScriptsDirectory=%~dp0
SET PowerShellScriptPath=%ThisScriptsDirectory%MyPowerShellScript.ps1
PowerShell -NoProfile -ExecutionPolicy Bypass -Command "& '%PowerShellScriptPath%'"
Replacing MyPowerShellScript.ps1 on the 3rd line with the file name of your PowerShell script.
This will allow you to simply double click the batch file to run your PowerShell script, and will avoid you having to change your PowerShell Execution Policy.
My blog post also shows how to run the PowerShell script as an admin if that is something you need to do.
Run Script Manually Method 2
Alternatively, if you don't want to create a batch file for each of your PowerShell scripts, you can change the default PowerShell script behavior from Edit to Run, allowing you to double-click your .ps1 files to run them.
There is an additional registry setting that you will want to modify so that you can run scripts whose file path contains spaces. I show how to do both of these things on this blog post.
With this method however, you will first need to set your execution policy to allow scripts to be ran. You only need to do this once per PC and it can be done by running this line in a PowerShell command prompt.
Start-Process PowerShell -ArgumentList 'Set-ExecutionPolicy RemoteSigned -Force' -Verb RunAs
Set-ExecutionPolicy RemoteSigned -Force is the command that actually changes the execution policy; this sets it to RemoteSigned, so you can change that to something else if you need. Also, this line will automatically run PowerShell as an admin for you, which is required in order to change the execution policy.
Source for Matt's answer.
I can get it to run by double-clicking a file by creating a batch file with the following in it:
C:\WINDOWS\system32\windowspowershell\v1.0\powershell.exe LocationOfPS1File
you can use this command :
powershell.exe -argument c:\scriptPath\Script.ps1