String comparison not working in powershell - powershell

I am trying an if else condition in powershell using string comparison. I tried as per documentation using -eq operator. But getting below error. Here "Build.Reason" is a predefined variable. Not sure why its looking for cmdlet name for variable.
Write-Host "$(Build.Reason)"
if ($(Build.Reason) -eq "Manual" ) {
$temp = "https://url/api/qualitygates/project_status?&pullRequest=$(Build.Reason)"
Write-Host "Manual"
} else {
Write-Host "CI"
}
Error
"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -NoLogo -NoProfile -NonInteractive -ExecutionPolicy Unrestricted -Command ". 'D:\a\_temp\d7af16d6-ce3e-4dec-a636-9447962fdac4.ps1'"
Manual
Manual : The term 'Manual' is not recognized as the name of a cmdlet, function, script file, or operable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At D:\a\_temp\d7af16d6-ce3e-4dec-a636-9447962fdac4.ps1:7 char:5
+ if (Manual -eq "Manual" ) {
+ ~~~~~~
+ CategoryInfo : ObjectNotFound: (Manual:String) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : CommandNotFoundException

It looks like $(Build.Reason) is a macro-style value provide by a CI system (it is not a PowerShell construct), which is expanded to become a literal part of the code before PowerShell sees it.
Therefore, if this value is to be treated as a string in the resulting PowerShell code, you need to quote it; e.g.:
if ("$(Build.Reason)" -eq "Manual") { # ...
Note that if there's a chance that $(Build.Reason) expands to a value with embedded " characters, they would have to be escaped as `". Similarly, if the value contains embedded $ chars., single-quoting should be used, which may then require escaping embedded single quotes as ''.
If this escaping cannot be performed at the source, you can use a verbatim here-string:
if (#'
$(Build.Reason)
'# -eq 'Manual') { # ...
Important: The closing '# must always be at the very beginning of the line.

Related

How to pass a variable to new console window in Powershell

I want to pass a variable to a new console, but I don't know how.
$server = "server_name"
Start-Process Powershell {
$host.ui.RawUI.WindowTitle = “Get-Process”
Invoke-Command -ComputerName $server -ScriptBlock {
Get-Process
}
cmd /c pause
}
Error message:
Invoke-Command : Cannot validate argument on parameter 'ComputerName'. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again
Start-Process only accepts (one or more) strings as arguments, not a script block ({ ... }).
While a script block is accepted, it is simply stringified, which results in its verbatim content being passed as the argument (sans { and }), which means that $server is retained as-is (not expanded), and the child process that runs your command doesn't have a variable by that name defined, causing Invoke-Command to fail due not receiving a value for -ComputerName.
Therefore, in order to incorporate variable values from the caller's scope, you must use string interpolation, using an expandable (double-quoted) string ("...") that encodes all arguments:[1]
$server = "server_name"
# Parameters -FilePath and -ArgumentList are positionally implied.
# For the resulting powershell.exe call, -Command is implied.
Start-Process powershell "
$host.ui.RawUI.WindowTitle = 'Get-Process'
Invoke-Command -ComputerName $server -ScriptBlock { Get-Process }
pause
"
A computer name ($server, in your case) doesn't contain spaces, but any variable values that do would require embedded enclosing quoting inside the overall "..." string, such as \`"$someVar\`" (`" escapes an " inside a "..." string in PowerShell, and the \ is additionally needed to escape the resulting verbatim " for the PowerShell CLI, powershell.exe).
For full robustness, additionally enclose the entire string value (implied -Command argument) in embedded "..." quoting ("`"...`"").
You can make this a bit easier by using the here-string form of an expandable string (#"<newline>...<newline>"#), inside of which you don't need to escape " chars.
Example of a fully robust call:
$someVar = 'A value with spaces'
Start-Process powershell #"
-NoProfile -Command "
# Echo the value of $someVar
Write-Output \"$someVar\"
pause
"
"#
Note the use of -NoProfile before -Command, which suppresses loading of the profile files, which can speed up the call and makes for a more predictable execution environment.
[1] Technically, -ArgumentList accepts an array of arguments, and while passing the pass-through arguments individually may be conceptually preferable, a long-standing bug unfortunately makes it better to encode all arguments in a single string - see this answer.
if you want to use param-
info
param([type]$p1 = , [type]$p2 = , ...)
or:
info
param(
[string]$server
)
Write-Host $a
./Test.ps1 "your server name"

using powershell invoke-expression to run code output

I have been doing a lot of reading on invoke-expression (also known as iex) and I'm having trouble getting it to work for me.
My understanding is, it will run any powershell code you give to it. However, when I run my tests on it, it does not run the code.
Example:
## testcode.ps1
$myvar = "i am here"
if ($myvar -ne $null) {
"($myvar) variable is Full"
} else {
"($myvar) variable is Empty"
}
Now, if I cat(gc) this file and I pass it to iex, it outputs a bunch of errors. Same thing happens when I save the code into a variable and then feed the variable to iex. Neither works.
Despite the fact that I've tried numerous examples, I feel there's something minor I'm doing wrong that I'm hoping someone can point out for me.
I'm new to Windows scripting, so please bear with me. These are the results of the tests I performed:
First Test:
PS C:\Users\J> gc C:\Users\J\testcode.ps1 | iex
Invoke-Expression : Cannot bind argument to parameter 'Command' because it is an empty string.
At line:1 char:31
+ cat C:\Users\J\testcode.ps1 | iex
+ ~~~
+ CategoryInfo : InvalidData: (:PSObject) [Invoke-Expression], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorEmptyStringNotAllowed,Microsoft.PowerShell.Commands.InvokeExpressionCommand
iex : At line:1 char:23
+ if ($myvar -ne $null) {
+ ~
Missing closing '}' in statement block or type definition.
At line:1 char:31
+ cat C:\Users\J\testcode.ps1 | iex
+ ~~~
+ CategoryInfo : ParserError: (:) [Invoke-Expression], ParseException
+ FullyQualifiedErrorId : MissingEndCurlyBrace,Microsoft.PowerShell.Commands.InvokeExpressionCommand
Second Test:
PS C:\Users\J> $scriptBlock = gc C:\Users\J\testcode.ps1
PS C:\Users\J>
PS C:\Users\J> iex -Command "$scriptBlock"
iex : At line:1 char:23
+ $myvar = "i am here" if ($myvar -ne $null) { "($myvar) variable ...
+ ~~
Unexpected token 'if' in expression or statement.
At line:1 char:1
+ iex -Command "$scriptBlock"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ParserError: (:) [Invoke-Expression], ParseException
+ FullyQualifiedErrorId : UnexpectedToken,Microsoft.PowerShell.Commands.InvokeExpressionCommand
PS C:\Users\J>
I'm aware that I can just run the file containing the code. However, I need help figuring out how iex works and what it is I'm doing wrong.
Please kindly advise.
First things first:
Invoke-Expression should generally be avoided and used only as a last resort, due to its security risks. In short: avoid it, if possible, given that superior alternatives are usually available. If there truly is no alternative, only ever use it on input you either provided yourself or fully trust - see this answer.
For the record: in the case at hand, the superior alternative is to directly invoke the script file:
# Prepend `& `, if the script file path is quoted or references a variable.
C:\Users\J\testcode.ps1
Invoke-Expression (iex) accepts multiple strings via the pipeline, and evaluates each individually, as a self-contained script.
Therefore, you must provide the contents of your script as a whole, as a single string, which is what Get-Content's (gc's) -Raw switch does[1]:
Get-Content -Raw C:\Users\J\testcode.ps1 | Invoke-Expression
Alternatively, pass the script-file contents as an argument:
Invoke-Expression (Get-Content -Raw C:\Users\J\testcode.ps1)
Note that passing the string to evaluate as an argument truly only accepts a single string, so the command would fail without -Raw.
[1] By default, the Get-Content cmdlet reads a file line by line, passing each line through the pipeline as it is being read.
$myvar = "I'm Here"
#Using Invoke-Expression - Accepts a STRING as Input
$SBCode = 'if ($Null -ne $myvar) {"($myvar) variable is Full"}' +
'else {"`$myvar variable is Empty"}'
Clear-Host
"Before Invoke-Expression `$myvar = $myvar"
$Result = Invoke-Expression $SBCode
"Invoke-Expression Returns: $Result"
#Using Invoke-Command - Accepts Script Block as Input
$SBCode = {
if ($myvar -ne $null) {
"($myvar) variable is Full"
}
else {
"`$myvar variable is Empty"
}
} #End $SBCode Script Block
"Before Invoke-Command `$myvar = $myvar"
$Result = Invoke-Command -ScriptBlock $SBCode
"Invoke-Command Returns: $Result"
Results:
Before Invoke-Expression $myvar = I'm Here
Invoke-Expression Returns: (I'm Here) variable is Full
Before Invoke-Command $myvar = I'm Here
Invoke-Command Returns: (I'm Here) variable is Full
# After changing $MyVar = $Null
Before Invoke-Expression $myvar =
Invoke-Expression Returns: $myvar variable is Empty
Before Invoke-Command $myvar =
Invoke-Command Returns: $myvar variable is Empty
HTH
You can use out-string to convert output into string.
cat C:\Users\J\testcode.ps1 | out-string | Invoke-Expression

How to assign and reference environment variables containing square brackets in Powershell

When the PSDrive is not specified, the following works:
${[foo]}="bar"
echo ${[foo]}
But the following does not work
$env:${[foo]}="bar"
At line:1 char:1
+ $env:${[foo]}="bar"
+ ~~~~~
Variable reference is not valid. ':' was not followed by a valid variable name character. Consider using ${} to delimit the name.
At line:1 char:6
+ $env:${[foo]}="bar"
+ ~~~~~~~~~~~~~~
Unexpected token '${[foo]}="bar"' in expression or statement.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : InvalidVariableReferenceWithDrive
${env:[foo]}="bar"
Cannot find path 'env:[foo]' because it does not exist.
At line:1 char:1
+ ${env:[foo]}="bar"
+ ~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (env:[foo]:String) [], ItemNotFoundException
+ FullyQualifiedErrorId : PathNotFound
The following works, though I am curious if there's short hand syntax for it:
Set-Item -LiteralPath env:${[foo]} -Value "bar"
Get-Item -LiteralPath env:${[foo]} | % {$_.Value}
However the following does not work:
Set-Item -LiteralPath env:${[foo]2} -Value "bar"
Set-Item : Cannot process argument because the value of argument "name" is null. Change the value of argument "name" to a non-null value.
At line:1 char:1
+ Set-Item -LiteralPath env:${[foo]2} -Value "bar"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:String) [Set-Item], PSArgumentNullException
+ FullyQualifiedErrorId : SetItemNullName,Microsoft.PowerShell.Commands.SetItemCommand
Written as of PowerShell Core 6.2.0
The reason is that PowerShell treats the following:
${<drive>:<name>}
as if you had specified:
Get-Content -Path <drive>:<name> # or, with assignment, Set-Content -Path ...
This notation - though often used with the Env: drive (e.g., $env:Path) - is little-known as a general paradigm named namespace variable notation, which is explained in this answer.
The problem is the use of -Path rather than -LiteralPath, because -Path interprets its argument as a wildcard expression.
Therefore, the [foo] in ${env:[foo]} - rather than being used as-is - is interpreted as a wildcard expression that matches a single character that is either f or o ([foo] is a character set or range ([...]) that matches any one of the (distinct) characters inside - see about_Wildcards).
On assigning to ${env:[foo]}, the logic of Set-Content -Path requires that a wildcard-based path resolve to something existing, even though you're generally not required to explicitly create environment variables; e.g., ${env:NoSuchVarExistsYet} = 'new' works just fine.
Workaround:
Use double(!)-`-escaping of the wildcard metacharacters:
# Namespace variable notation only works with if you
# double(!)-backtick-escape the wildcard metacharacters:
# Assign to / implicitly create env. var '[foo]'
${env:``[foo``]} = 'bar'
# Get its value.
${env:``[foo``]}
Note:
Escaping shouldn't be required at all, because there is no good reason to treat paths that conceptually identify a given, known item as wildcard expressions - see GitHub issue #9225.
That double `-escaping is needed is an added quirk - see GitHub issue #7999.
Another workaround - one that doesn't involve escaping - is to use
Set-Content -LiteralPath env:[foo] bar and Get-Content -LiteralPath env:[foo], but that is both verbose and slow.
As for the other syntax variations you tried:
$env:${[foo]}="bar"
Since your variable reference isn't {...}-enclosed as a whole (except for the initial $), the token that follows the : is only allowed to contain characters that do not require escaping - and $, { and } all violate that rule.
{...}-enclosing the entire path - ${env:[foo]} - solves the syntax problem, but runs into the problem detailed above.
Set-Item -LiteralPath env:${[foo]} -Value "bar"
This does not work in general, because string expansion is applied beforehand here - it is as if you had passed "env:${[foo]}": the reference to a (regular) variable named ${[foo]} is expanded (replaced with its value) and in effect appended to literal env:, before handing the result to Set-Item.
If such a regular variable doesn't exist, what Set-Item sees is just env: (because non-existent variables default to $null, which becomes the empty string in a string context), which causes an error due to the lack of variable name.
By contrast, the following would set an environment variable named unrelated instead:
# Create a regular variable literally named '[foo]'.
${[foo]} = 'unrelated'
# !! The following sets env:unrelated, i.e., env. var 'unrelated',
# !! due to the string expansion that is performed on the -LiteralPath
# !! argument up front.
Set-Item -LiteralPath env:${[foo]} bar
$env:unrelated # -> 'bar'
The same applies to Get-Item -LiteralPath env:${[foo]} and
Set-Item -LiteralPath env:${[foo]2} -Value "bar".

Powershell scripting help needed

I am very new to powershell scripting. Trying to learn it from web. Now I am trying to do a script and facing some problem, so need some help ans suggestions from you people. I am giving description what I tried to do:
first of all I have declared 2 variables, then I used if statement to see if the variables are empty then it will show a warning message and it will ask for the inputs from the user and after that it will show the value of the variables. but it is giving some errors.
$workingdirectory = args[0]
$directoryname = args[1]
if ("$WorkingDirectory" -eq "")
{
Write-Warning "Parameter Required"
$WorkingDirectory = Read-Host "Enter the absolute path to working directory "
}
if ("$DirectoryName" -eq "")
{
Write-Warning "Paramater Required"
$DirectoryName = Read-Host "Enter a directory name to search for in $WorkingDirectory "
}
Write-Host "$WorkingDIrectory"
write-host "$DirectoryName"
When I run it, it is showing the following errors:
ARGS[0] : The term 'ARGS[0]' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the
name, or if a path was included, verify that the path is correct and try again.
At C:\LAB_5-submission\mmbillah1_Lab_testdir.ps1:24 char:21
+ $WorkingDirectory = ARGS[0]
+ ~~~~~~~
+ CategoryInfo : ObjectNotFound: (ARGS[0]:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
ARGS[1] : The term 'ARGS[1]' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the
name, or if a path was included, verify that the path is correct and try again.
At C:\LAB_5-submission\mmbillah1_Lab_testdir.ps1:25 char:18
+ $DirectoryName = ARGS[1]
+ ~~~~~~~
+ CategoryInfo : ObjectNotFound: (ARGS[1]:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
I want to run like this: .\scriptname.ps1, if I use this then it will show the warning and ask for the two variables input.
and if I run this .\scriptname.ps1 C:\users\masum then it will ask for the second variable value only.
The root of your problem is simply syntax it should be $args[0] and $args[1] if you intend to use what you have now. What I would strongly suggest instead is to create parameters for your script, and then test if those parameters are valid, and if they aren't to prompt for them.
Parameters can be defined in a Param() block as such:
Param(
$workingdirectory,
$directoryname
)
That is very simple, but for your needs it works. You can add types to make sure the right kinds of things are passed as the parameter, and add tests to make sure the parameters are valid, but that goes beyond what we're doing here.
Then you would check to make sure that there is a value for each, and I would recommend making sure that the path is valid. Something like:
While([string]::IsNullOrEmpty($workingdirectory) -or !(Test-Path $workingdirectory)){
$workingdirectory = Read-Host "Enter a valid working directory"
}
That checks if the $workingdirectory variable is empty or just blank spaces, and if it actually has a value it will check to make sure it's a valid directory. If it is blank or the path isn't valid it prompts the user to enter a valid path. You would need to repeat that for the $directoryname variable.
So you would end up with something like:
Param(
$workingdirectory,
$directoryname
)
While([string]::IsNullOrEmpty($workingdirectory) -or !(Test-Path $workingdirectory)){
$workingdirectory = Read-Host "Enter a valid working directory"
}
While([string]::IsNullOrEmpty($directoryname) -or !(Test-Path $directoryname)){
$directoryname= Read-Host "Enter a valid target directory"
}
Write-Host $workingdirectory
Write-Host $directoryname

PS 4.0 Parameters with Space - Catch Error

I'm executing script in batch and passing 5 parameters separated by a comma via Java. PS 4.0 accepts parameters without the "".
[CmdletBinding()]
Param (
[Parameter(Position = 0)]
[string[]] $inpParms = $(throw 'Failure : This is required.')
)
I invoke the script like
myScript.ps1 user,pwd,Server Name, DLName,Action
It errors out due to the space in "Server Name". Throws error which spits back the parameters:
A positional parameter cannot be found that accepts argument 'System.Object[]'.
At line:1 char:1
+ .\myScript.ps1 user,pwd,Server Name, DLName,Create
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [ExchangeDL.ps1], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,ExchangeDL.ps1
Is there a way to trap this error so I don't spit sensitive info in logs?
While you don't have to quote arguments that don't have shell metacharacters in them, a space is a metacharacter, so you have two choices:
`-quote the metacharacters individually:
./myScript.ps1 user, pwd, Server` Name, DLName, Create
Or use "..." (interpolating) or '...' (literal) to enclose entire arguments as needed:
./myScript.ps1 user, pwd, 'Server Name', DLName, Create
Generally, though, your script will be more maintainable if you define individual parameters rather than a single array.
If you want to catch an incorrect invocation attempt, use a Try / Catch statement:
Try {
./myScript.ps1 user, pwd, Server Name, DLName, Action
} Catch {
Throw "Invocation of ./myScript.ps1 failed."
}
By not using the information from the statement-terminating error that the incorrect invocation produced (accessible as $_ in the Catch block) in the Throw statement, the original command line is not leaked.