How to pass needed parameters to script in Powershell ISE? - powershell

See Title.
I specified needed parameters in the head of a script:
param ($G_ARCHIVE = $(throw "Need file to upload!"),
$G_LOGFILE = $(throw "Need logfile!"))
When I want to debug the script with Powershell ISE: how can I fill these parameters?

Use the command pane. Open the script file in the ISE editor, set the breakpoints (F9). Then in the command pane type a command invoking this script with required parameters. I do not think there is another (built-in) way of doing this in ISE.

Open the script (myscript.ps1) in Windows Powershell ISE
Press F9 at the variable you want to inspect (debug). For instance 2nd line in the sample below where the $outputText variable is being assigned
In the shell window provide the relative path of the script along with the param value. For instance: .\myscript.ps1 "my value"
Hit enter (you don't need to hit F5)
You'll be able to see the debugging breakpoints in highlighted with yellow color. Place your cursor to the desired variable to inspect the current value.

There is another way. You can use the $PSDefaultParameterValues automatic variable, which exists (since v3) to provide new default arguments to cmdlets and advanced functions (doesn't work with normal functions). However, it does work for scripts, even when debugging in ISE. You have to declare [CmdletBinding()] or [Parameter()] like you would for an advanced function.
So for your example,
[CmdletBinding()]
param ($G_ARCHIVE = $(throw "Need file to upload!"),
$G_LOGFILE = $(throw "Need logfile!"))
you would execute something like this on the ISE Prompt:
$PSDefaultParameterValues.add("ExampleScript.ps1:G_ARCHIVE","File-to-upload.txt")
$PSDefaultParameterValues.add("ExampleScript.ps1:G_LOGFILE","Example.log")
You could also set the parameter value to a script block which will auto-execute at run-time:
$PSDefaultParameterValues["ExampleScript.ps1:G_LOGFILE"]={
"Example-{0:yyMMddHHmm}.log" -f [datetime]::Now
}
The variable is a hashtable and all the standard syntax applies, except the key must have the name of the script (or advanced function or cmdlet) followed by a colon then the parameter name. You can set defaults for multiple scripts or commands, and multiple parameters for each (each parameter is a new table entry).
By doing it this way, you can just hit F5 to run your script like normal. The parameters will be taken from the variable, so you don't have to type anything in.
Other use cases for $PSDefaultParameterValues might be customizations, like have the Get-History get only the last 10 entries, unless you specify the -Count parameter in the command. Because entries only persist for the current session, you would want to add customizations to your profile. You can read more by typing Get-Help about_Parameters_Default_Values at the prompt or view the same information on TechNet.

There is a much simpler way to set needed Parameters in ISE:
Before pressing F5 in ISE, set the Parameter you need. I usually comment the Parameter I need, example:
# $G_ARCHIVE = "C:\Temp\TestFile_001.txt"
I select everything after "#" and press F8. Next time I debug the script with F5, the Parameter is set to the value I am testing with, no need to pass the Parameters through the command line.

At least in Powershell 5.1 ISE when you press F5 to run a parameterized script, you will be asked to enter values for the parameters one by one.
When using the $PSDefaultParameterValues to populate the variables, you can reference the loaded script through the $psISE variable like
$PSDefaultParameterValues.add("$($psISE.CurrentFile.DisplayName):G_ARCHIVE","test")

I had this problem today. After messing around I discovered that the lower panel was running in Debug E.g. Prompt '[Dbg]: PS'
I stopped the debugging and re-issued my script with paramaeters.
Example: ./myScript.ps1 -ForceBuild $true
My results were that the script started and the breakpoints fired and allowed me to debug as expected.

Related

Change title of another window through PowerShell

I have a VB application, which starts several instances of a third party non-GUI application. To keep track of these multiple instances, I update their title, using the SetWindowText() function. This application however has the nasty habit of continuously updating the title, so each SetWindowText works only temporary. As soon as you click anywhere in the screen, the tile is changed back.
I found a way to update the title through PowerShell, using the following code:
$titletext = "My Title"
# Start a thread job to change the window title to $titletext
$null = Start-ThreadJob { param( $rawUI, $windowTitle )
Start-Sleep -s 2
if ( $rawUI.WindowTitle -ne $windowTitle ) {
$rawUI.WindowTitle = $windowTitle
}
}-ArgumentList $host.ui.RawUI, $titletext
& 'c:\Program Files\Application\Application.exe' '-id=userid -pass=password'
This works perfectly and the title change is permanent, so exactly what I want. The only problem is that everything is being logged in the Windows PowerShell log, including the parameters -id= and -pass=.
A solution would be if I can start application.exe through my VB application and do the rename through a PowerShell script, but I don't know if that is possible through a ThreadJob.
Is it possible to start a ThreadJob and rename another window, maybe through it's handle?
Changing the console title from inside that console is your best bet, which is what your PowerShell code does.
While it is possible to call the SetWindowText() API function to set another process' console-window title, this change isn't guaranteed to stay in effect, because any subsequent interaction with such a window causes the original window title to be restored (this behavior seems to be built into conhost.exe, the console host underlying regular console windows on Windows).
By contrast, setting the title of the console window associated with the current process, does stay in effect (unless overridden again later), which is what the SetConsoleWindow() WinAPI function does (which shell- and API-based mechanisms such as title in cmd.exe, and [Console]::Title / $hostUI.RawUI.WindowTitle in PowerShell presumably ultimately call).
Therefore, stick with your PowerShell approach and avoid the password-logging problem with the help of an environment variable, as detailed below.
Windows PowerShell's script-block logging - see about_Logging - logs the source code of code being created.
You can avoid argument values from being logged if you - instead of providing literal arguments - provide them indirectly, via variables that you set from outside PowerShell.
Therefore:
Make your VB.NET application (temporarily) set an environment variable that contains the password. (Perhaps needless to say, storing and passing plain-text passwords is best avoided).
In your PowerShell script, refer to that environment variable instead of passing a literal password - that way, the actual password will not be shown in the logs.
For example, assuming that your VB.NET application has created environment variable MYPWD containing the password, before launching the PowerShell script:
$titletext = "My Title"
# Start a thread job to change the window title to $titletext
$null = Start-ThreadJob { param( $rawUI, $windowTitle )
Start-Sleep -s 2
if ( $rawUI.WindowTitle -ne $windowTitle ) {
$rawUI.WindowTitle = $windowTitle
}
} -ArgumentList $host.ui.RawUI, $titletext
# Note:
# * Assumes that your VB.NET application has set env. var. "MYPWD".
# * The arguments must be passed *individually*, not inside a single string.
& 'c:\Program Files\Application\Application.exe' -id=userid "-pass=$env:MYPWD"

How to skip part of PowerShell script by starting script with flags or arguments

I am writing a script that presents the user with a menu of functions, but I also want to be able to run the script automatically from task scheduler which would mean I would need to skip the menu portion. Is there a way to do this with flags or arguments when starting the script (like "script.ps1 -auto" to skip the coding containing the menu, or just "script.ps1" to start)
I've performed internet searches for this, but have not yet found anything that I think is applicable. I'm not even sure if this is possible given the lack of information I've found (or not found).
script.ps1
script.ps1 -auto
Not to the point where error messages are applicable
You can use the [switch] parameter type in your param block.
param( [switch] $auto )
if ($auto) {
# here goes the code if the parameter auto is set
}
else {
}
See also this answer on SO, on how to handle command-line parameters with PowerShell.

How to initialize a multi-line array at the PowerShell prompt (not in a script)?

I'd like to initialize this array at the PowerShell prompt:
$Parameters = #{
SourcePath = 'D:\Website'
TargetPath = 'wwwroot'
ComputerName 'https://website:8172/msdeploy.axd?site=website'
Credential = $Credential
}
However, when I paste this command I'm prevented by a series of symbols from pressing Enter to get back to the prompt:
Pressing Enter only generates a new line with another set of symbols.
Is this possible to do at the prompt (not in a script)?
At the PowerShell command prompt, if the line-continuation prompt >>  keeps appearing when you press Enter, the implication is that the command is syntactically incomplete or broken.
Note: If module PSReadLine, which ships with W10, is imported, a command is recognized as complete right away; without it, an extra Enter keypress is needed.
Therefore, if you expect the command to be complete, yet >>  keeps appearing, the implication is that the command contains a syntax error, which is indeed what happened in your case, as you've discovered:
Line ComputerName 'https://website:8172/msdeploy.axd?site=website', meant to be a hashtable's key-value pair definition, was missing the = between key ComputerName and value 'https://website:8172/msdeploy.axd?site=website'.
I noticed as soon as I posted this that I was missing the = in the third element (ComputerName).
Fixing that solved the problem.

Referencing text after script is called within PS1 Script

Let's take the PowerShell statement below as an example:
powershell.exe c:\temp\windowsbroker.ps1 IIS
Is it possible to have it scripted within windowsbroker.ps1 to check for that IIS string, and if it's present to do a specific install script? The broker script would be intended to install different applications depending on what string followed it when it was called.
This may seem like an odd question, but I've been using CloudFormation to spin up application environments and I'm specifying an "ApplicationStack" parameter that will be referenced at the time when the powershell script is run so it knows which script to run to install the correct application during bootup.
What you're trying to do is called argument or parameter handling. In its simplest form PowerShell provides all arguments to a script in the automatic variable $args. That would allow you to check for an argument IIS like this:
if ($args -contains 'iis') {
# do something
}
or like this if you want the check to be case-sensitive (which I wouldn't recommend, since Windows and PowerShell usually aren't):
if ($args -ccontains 'IIS') {
# do something
}
However, since apparently you want to use the argument as a switch to trigger specific behavior of your script, there are better, more sophisticated ways of doing this. You could add a Param() section at the top of your script and check if the parameter was present in the arguments like this (for a list of things to install):
Param(
[Parameter()]
[string[]]$Install
)
$Install | ForEach-Object {
switch ($_) {
'IIS' {
# do something
}
...
}
}
or like this (for a single option):
Param(
[switch]$IIS
)
if ($IIS.IsPresent) {
# do something
}
You'd run the script like this:
powershell "c:\temp\windowsbroker.ps1" -Install "IIS",...
or like this respectively:
powershell "c:\temp\windowsbroker.ps1" -IIS
Usually I'd prefer switches over parameters with array arguments (unless you have a rather extensive list of options), because with the latter you have to worry about spelling of the array elements, whereas with switches you got a built-in spell check.
Using a Param() section will also automatically add a short usage description to your script:
PS C:\temp> Get-Help windowsbroker.ps1
windowsbroker.ps1 [-IIS]
You can further enhance this online help to your script via comment-based help.
Using parameters has a lot of other advantages on top of that (even though they probably aren't of that much use in your scenario). You can do parameter validation, make parameters mandatory, define default values, read values from the pipeline, make parameters depend on other parameters via parameter sets, and so on. See here and here for more information.
Yes, they are called positional parameters. You provide the parameters at the beginning of your script:
Param(
[string]$appToInstall
)
You could then write your script as follows:
switch ($appToInstall){
"IIS" {"Install IIS here"}
}

Execute a parameter passed into a powershell script as if it were a line in the script

I've got a wrapper powershell script that I'm hoping to use to automate a few things. It's pretty basic, and accepts a parameter that I want the script to run as if it were a line in the script. I absolutely cannot get it to work.
example:
param( [string[]] $p)
echo $p
# Adds the base cmdlets
Add-PSSnapin VMware.VimAutomation.Core
# Add the following if you want to do things with Update Manager
Add-PSSnapin VMware.VumAutomation
# This script adds some helper functions and sets the appearance. You can pick and choose parts of this file for a fully custom appearance.
. "C:\Program Files (x86)\VMware\Infrastructure\vSphere PowerCLI\Scripts\Initialize-VIToolkitEnvironment.ps1"
$p
In the example above, I want $p to execute as if it were a line in the script. I know this isn't secure, and that's probably where the problem lies.
Here is how I try running the script and passing in a parameter for $p:
D:\ps\test>powershell -command "D:\ps\test\powershell_wrapper.ps1" 'Suspend-VM servername -Verbose -Confirm:$False'
How can I get my parameter of 'Suspend-VM servername -Verbose -Confirm:$False' to run inside my script? If I just include the value in the script instead of pass it in as a parameter it runs without any issues...
You can basically approach this two ways, depending on what your needs really are and how you want to structure your code.
Approach #1 - Invoke-Expression
Invoke-Expression basically allows you to treat a string like an expression and evaluate it. Consider the following trivial example:
Invoke-Expression '{"Hello World"}'
That will evaluate the string as if it were an expression typed in directly, and place the string "Hello World" on the pipeline. You could use that to take your string parameter and run it on-the-fly in your script.
Approach #2 - Using a ScriptBlock
PowerShell has a special data type called a ScriptBlock, where you can bind a script to a variable, and then invoke that script as part of your code. Again, here is a trivial example:
function Test-SB([ScriptBlock]$sb) {
$sb.Invoke()
}
Test-SB -sb {"Hello World"}
This example creates a function with a single parameter $sb that is of type ScriptBlock. Notice the parameter is bound to the actual chunk of code {"Hello World"}? That code is assigned to the $sb parameter, and then a call to the .Invoke method actually executes the code. You could adapt your code to take in a ScriptBlock and invoke it as part of your script.
Approach #3 - Updating your profile
OK, so I said there were two ways to approach it. There is actually a third... sort of... You could add the VMWare cmdlets to your $profile so they are always present and you don't need your wrapper to load in those libraries. Granted, this is a pretty big hammer - but it might make sense if this is the environment you are constantly working in. You could also just create a shortcut to PowerShell that runs a .ps1 on startup that includes those libraries and hangs around (this is what MS did with the SharePoint admin shell and several others). Take a look at this TechNet page to get more info on the $profile and if it can help you out:
http://msdn.microsoft.com/en-us/library/windows/desktop/bb613488.aspx