I have powershell script with input parameters, but when I run it, some input is ignored and script just uses same parameters as in previous run.
I assume ps remembers some frequent parameters in cache, is there a way how to force it to always take new parameters?
Called with
-ExecutionPolicy bypass
-command xxx.ps1
-subject "Report: xxx"
-from "xxx#yyy.com"
-fordate "xxxx-xx-xx"
-path "xxx\"
-fileName "xxx.zip"
Parameter declaration in script
Param(
$subject,
$from,
[DateTime]$fordate,
$path,
$fileName
)
Thanks a lot
The problem was with one parameter that was split with space (for example 'report: xxx'), even though it was separated in commas, powershell only took the part before space (which was same for all cases - thats why i thought it didnt take new parameter).
I have fixed the problem with using -file instead of -command at the start (How to pass in a string with spaces into PowerShell?)
I think I will delete this question later because it is not the problem which was described in title/post (has zero value for future users i guess), but i still want to thank all people that commented or took a minute to think about it
Related
I have a script that inventories the installed software on a machine, based on certain criteria provided in arguments. It also needs to support being buried in a very deep folder structure (Architects just think like that and I need to support it, not control it). And since it now also supports AppX, which requires running elevated, which in turn requires the full path to the PS1 in the -file argument, I am running into issues with the overall length of the Target value in the shortcut.
So, I was looking at the usual run with the -verb argument approach, like this
If (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
$psArguments = #('-nologo', '-noExit', '-executionpolicy', 'bypass', '-verb', 'Runas', '-file',$PSCommandPath)
$pxArguments = #($myInvocation.BoundParameters.GetEnumerator().ForEach({"-$($_.Key)", "$($_.Value)"}))
$arguments = $psArguments + $pxArguments
Start-Process 'powershell.exe' -ArgumentList:$arguments -Verb:Runas
Stop-Process $PID
} else {
Write-Host 'Elevated'
# actual code to process
}
But I am having two issues there, one is critical, one less so.
On the critical side, I see a second blue PowerShell console open for a split second, but then it closes with no error or anything. I would have expected the -noExit argument in $psArguments to at least leave that window open for me, so I could read any errors. But I can't see any red, so that makes me think there is no error, and this usually happens when the -file argument doesn't point to an actual script. However, if I validate the arguments like so
foreach ($item in $arguments) {
Write-Host "$item"
}
Write-Host "$PSCommandPath $(Test-Path $PSCommandPath)"
The arguments all look good and Test-Path returns true.
So, what in this am I getting wrong, such that I don't get a new PowerShell console that actually works?
The second issue is that I have defined fonts and colors and console size in the shortcut, and I wonder if there is a single variable, perhaps $host, that I could then send to the second session populated with the values of the first. I suspect not, because I think the only types that can be used as arguments are strings and numbers.
DOH: I was on the right track, with that comment about it looking like the script wasn't found. There are SPACES in that path, so I need
$psArguments = #('-nologo', '-noExit', '-executionpolicy', 'bypass', '-file',"`"$PSCommandPath`"")
Now I just need to deal with resizing the console. I can live without fonts and colors, but the width and text wrapping I need to address.
I'd like to call this from another program, similar to calling it from the cmd prompt. I'd like to write it as is, like an unexpanded string to be called when an alert is triggered, but have it run inside a powershell runspace (when the alert is triggered) and expand the variable for $date, $date is always empty with what I've tried so far syntactically.
What is wrong with my syntax?
$omsaCommand = "omconfig system alertaction event=tempwarn execappath=`"Powershell.exe -NonInteractive -command `"$date = (Get-Date).ToShortDateString(); Set-Content -Path 'c:\omsaInfo\inventoryResults\temp.txt' -Value '$alertShortName - $date';`"`" ";
Invoke-Expression $omsaCommand;
For this, I couldn't find a syntax that allowed some variables to be compressed, others to be uncompressed, and fit within the framework of the parent application. The work around I came up with was to call
execappath="Powershell.exe -NonInteractive -file C:\omsaInfo\createAlertFile.ps1 -alertName $alertShortName "
which would allow me to run multiple commands without worrying about variable management
I'm struggling to call a second script in one of my PowerShell scripts at the moment using Invoke-Expression. It's currently producing an error:
"Parameter set cannot be resolved using the specified named parameters."
Annoyingly, it works fine for one switch (being -ServerDriveReport), but doesn't work for the other.
The first script (called DriveReport.ps1) is like:
[cmdletbinding()]
Param(
[Parameter(ParameterSetName="ServerDriveReport")]
[switch]$ServerDriveReport,
[Parameter(ParameterSetName="VMDriveReport")]
[switch]$VMDriveReport)
If($ServerDriveReport){
Invoke-Expression "& 'C:\Scripts\Drive Report\EmailDriveReport.ps1' -ServerDriveReport"}
If($VMDriveReport){
Invoke-Expression "& 'C:\Scripts\Drive Report\EmailDriveReport.ps1' -VMDriveReport"}
The "EmailDriveReport.ps1" script is like:
[cmdletbinding()]
Param(
[Parameter(ParameterSetName="ServerDriveReport")]
[switch]$ServerDriveReport,
[Parameter(ParameterSetName="VMDriveReport")]
[switch]$VMDriveReport)
If($ServerDriveReport){
# Send an email containing the server drive report}
If($VMDriveReport){
# Send an email contining the VM drive report}
When running "DriveReport.ps1 -ServerDriveReport" everything works as expected. But when running "DriveReport.ps1 -VMDriveReport", that's when I get the aforementioned error message.
Has anyone seen this before?
Any help would be greatly appreciated!
Without attempting to solve your immediate problem (which is not obvious to me from the code posted), consider using the automatic $PSBoundParameters variable via splatting to pass the parameters through to the 2nd script:
[cmdletbinding()]
Param(
[Parameter(ParameterSetName="ServerDriveReport")]
[switch]$ServerDriveReport,
[Parameter(ParameterSetName="VMDriveReport")]
[switch]$VMDriveReport)
)
& 'C:\Scripts\Drive Report\EmailDriveReport.ps1' #PSBoundParameters
Generally, Invoke-Expression should be avoided, because there are usually more robust solutions available and because it presents a security risk if invoked on untrusted strings.
Thanks for the help!
I managed to resolve this by carefully going over the script and finding out one of the Else statements was calling the file incorrectly. I've now changing this to:
& 'C:\Scripts\Drive Report\EmailDriveReport.ps1' as suggested.
My Powershell script needs to invoke an EXE with a very complicated set of arguments. I'm using Powershell 3.0, and must stick with that version. Alas, even the "magic" escaping operator (--%) isn't helping me. For example, using the Call operator, consider this:
& other.exe --% action /mode fast /path:"location with spaces" /fancyparam { /dothis /dothat:"arg with spaces" } /verbose
Now, if it were that simple, my script could easily work fine. But it isn't that simple. The arguments for "other.exe" can be different, depending on user selections earlier in my script. So instead, I need to build up those parameters ahead of time, perhaps like this:
$commandArgs = 'action /mode ' + $userMode + ' /path:"location with spaces" /fancyparam { /dothis /dothat:"' + $userArgs + " } /verbose'
Thus I would invoke this way:
& other.exe --% $commandArgs
...well, expect that --% means it just passes a raw string of $commandArgs instead. But without the --%, powershell auto-quotes the contents of $commandArgs, which really messes up the internal quotes (not to mention breaking the 'action' argument at the front that other.exe needs first). In other words, I've already tried embedding the --% inside my $commandArgs string, but the damage is already done by the time it would be parsed (and I don't think it even works that way).
NOTE that this example is only about 1/4 of my actual command I need to execute -- which includes many more user args, quotes and other funny characters that would drive me into escaping-hell in a hurry! I've also already been using the echoargs.exe tool, which is how I'm seeing the troubles I'm having. Oh, and I need all the spaces in my example, too (i.e. need spaces around the brace characters).
So after much searching for an answer, I turn to you for help. Thanks in advance.
Ok, probably weird to be answering my own question, but after spending another day on this problem yesterday, I might have realized the answer myself. At least, this is what I found that works. But I post here to get further feedback, in case I'm really doing something that isn't recommended ...using Invoke-Expression :-)
I had sorta realized early on, and some of you confirmed this in your responses, that the --% prevents all further expansions (including my $variables). And my problem is that I'm still needing to expand lots of things when trying to use the Call operator (&). My problem would be solved if my command line was all ready to go before using --%, so that's what I did.
I created a new string, composed of:
$fullCommand = '& "other.exe" --% ' + $commandArgs
(The EXE path actually has spaces in it, hence the quotes.) Then, with it all built up (including the --% where it needs to be), I invoke it as a new piece of script:
Invoke-Expression $fullCommand
So far, I'm having very good results. But I know in my search up to this point that Invoke-Expression sounded like this bad thing people shouldn't use. Thoughts, everyone?
The purpose of --% is to suppress argument processing, so there's no expansion of variables after that parameter on behalf of PowerShell. You can work around this by using environment variables, though:
$env:UserMode = 'foo'
$env:UserArgs = 'bar baz'
& other.exe --% action /mode %UserMode% /path:"location with spaces" /fancyparam { /dothis /dothat:"%userArgs%" } /verbose
I always recommend that people build their command line arguments into a variable, and then pass that variable into the -ArgumentList parameter of the Start-Process cmdlet.
$Program = '{0}\other.exe' -f $PSScriptRoot;
$UserMode = 'fast';
$Path = 'c:\location with\spaces';
$ArgWithSpaces = 'arg with spaces';
$ArgumentList = 'action /mode {0} /path:"{1}" /fancyparam { /dothis /dothat:"{2}" } /verbose' -f $UserMode, $Path, $ArgWithSpaces;
$Output = '{0}\{1}.log' -f $env:Temp, [System.Guid]::NewGuid().ToString();
Start-Process -Wait -FilePath $Program -ArgumentList $ArgumentList -RedirectStandardOutput $Output;
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