Powershell issues with Start-Process of same script elevated - powershell

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.

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"

Powershell syntax problem trying to write a non-expanded string with variables, but have Powershell expand and use string variables

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

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.

Powershell ignores new parameters

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

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