Jenkins (Pipeline) Boolean Parameter in Powershell confusing - powershell

I have a parametrized Jenkins Pipeline Script, where I pass a Boolean Parameter "isModuleUpdate".
When I use this parameter in my Pipeline Script I get confusing results.
My Script:
Write-Host ">>> isModuleUpdate as String: $Env:isModuleUpdate"
Write-Host ">>> isModuleUpdate as Variable: " $Env:isModuleUpdate
if ($Env:isModuleUpdate) {
Write-Host ">>> ModuleUpdate is checked!"
}
When I run my Script, the Result is:
>>> isModuleUpdate as String: false
>>> isModuleUpdate as Variable: false
>>> ModuleUpdate is checked!
What is the sexiest way to check this variable corectly?

From about_Environment_Variables:
Environment variables, unlike other types of variables in PowerShell, are always stored as a string and can't be empty.
Your if statement evaluates to true, because your string variable is not empty. In other words, it contains the string false and not a boolean value. Do a proper string comparison instead:
if ($Env:isModuleUpdate -like 'true') {...

I recall having issues with checking booleans in PowerShell as well. Ultimately, -eq $true worked:
if ($Env:isModuleUpdate -eq $true) {

Related

Can Ansible's win_powershell thoroughly process a boolean

win_powershell seems to choke when I attempt to pass a Boolean variable through it to register a tested discrete state on a remote guest/target (for later consumption). For numerous attempts to provide compliant statements, I get errors such as ...
was not followed by a valid variable name character. Consider using ${} to delimit the name.
Last close parenthesis is an invalid character
Cannot convert string to bool
Colon is unexpected token
$True is unexpected token
Unable to convert to dictionary
Must either be a JSON string or in the key=value form
My goal was to use the win_powershell module to execute a powershell script ...
$IsInstalled = ((gp HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*).DisplayName -Match "Microsoft SQL Server").Length -gt 0
... on a remote host and consume the IsInstalled variable later in the Playbook. (the p/s statement executes flawlessy when invoked directly).
My task ended up looking like the below with all of the various/unsuccessful attempts commented out. Notice that I resorted to the simplest of tests in the end which infers that processing a boolean via win_powershell is not do-able. I'm hoping somebody can prove me wrong and/or confirm that I need to get such resolve by processing a string variable instead.
BTW (for the innocent by-stander) there are no hits for the word bool in the Ansible documentation for win_powershell. However, the description for parameters includes the word dictionary and term key=value pairs. If these descriptions are restraints, it should be (I wish it were) stated as such (more emphatic).
Key points observed along the way, …
An = operator always produces a string; use json syntax for other variable types; a : (colon) for other types such as boolean.
Consider using ${} to delimit the variable name (but how?)
Enclose any boolean equates with single ‘
I found some other blogs/posts but nothing in layman (json newbie) terms (that I could understand).
Anybody have a solution to make a Boolean work as sought or should I (just) try to process the needed state as a string variable? Check out these blogs …
powershell - Variable reference is not valid. ':' was not followed by a valid variable name character
How can you use an object's property in a double-quoted string?
Here's my task efforts (with failed statements being commented) ...
- name: Test for installed SQL Server
ansible.windows.win_powershell:
script: |
[CmdletBinding()]
param (
[bool]$IsInstalled
)
# $IsInstalled = ((gp HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*).DisplayName -Match "Microsoft SQL Server").Length -gt 0
# $IsInstalled: ((gp HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*).DisplayName -Match "Microsoft SQL Server").Length -gt 0
# $IsInstalled: ${ ((gp HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*).DisplayName -Match "Microsoft SQL Server").Length -gt 0 }
# ${IsInstalled}: ((gp HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*).DisplayName -Match "Microsoft SQL Server").Length -gt 0
# if ( ((gp HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*).DisplayName -Match "Microsoft SQL Server").Length -gt 0 ) # last close paren is invalid character
# if ( '((gp HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*).DisplayName -Match "Microsoft SQL Server").Length -gt 0' ) # cannot convert string to bool
if ( $False ) # a sanity check ...
# { $IsInstalled: $True }
# { ${ $IsInstalled}: $True }
# { ${IsInstalled}: $True } # colon is unexpected token
# { ${IsInstalled} = $True } # equals outputs a string; cannot convert to boolean (i knew that but what the hey)
# { ${ $IsInstalled: } $True } # unexpected token $True
{ '$IsInstalled: $True' }
else
# { $IsInstalled: $False }
# { ${ $IsInstalled}: $False }
# { ${IsInstalled}: $False }
# { ${IsInstalled} = $False }
# { ${ $IsInstalled: } $False }
{ '$IsInstalled: $False' }
parameters:
# IsInstalled: $True # cannot convert string to bool
# 'IsInstalled: $True' # is of type System.String ... unable to convert to dict ... must either be a JSON string or in the key=value form"
}
register: SQLtest
I was expecting the first (commented) statement to work; done. That is, to register SQLtest.IsInstalled as a boolean state for later consumption.
Here's an exact solution that did work; i couldn't get the templating to work, ...
- name: Test for installed SQL Server
ansible.windows.win_powershell:
script: |
((gp HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*).DisplayName -Match "Microsoft SQL Server 2019 ").Length -gt 0
register: SQLtest
... as consumed later in a when conditional ...
when: not SQLtest.output
It's always simple (once you know how) ...

Powershell Understanding "continue"

So in the spirit of this short but sweet tutorial I'm trying to filter out disabled user and only work on the "Enabled" users with this code. (FYI Search-ADAccount needs elevated)
$EXPusers = (Search-ADAccount -AccountExpired -UsersOnly)
foreach($user in $EXPusers){
$UENB = $user.Enabled
$UENB # Sanity Check
if($UENB -eq "False"){
continue
}
# All of this is functioning
# disable user
# Logoff user
# Send email
}
In my lab $EXPusers just resolves to one user that is disabled or Enabled=False. So what happens is no matter what I set $UENB equal to it keeps sending mail. Seems to me that if it's "False" it should skip that iteration and not process the rest of the foreach statement and move to the next user, in this case do nothing.
What am I missing?
The reason why it's failing is because you're comparing a boolean (the Enabled Property of an ADAccount instance is a bool) with a string. It's important to note that, in PowerShell a string that is not empty will always be $true and, since in your comparison the string is in the right hand side (RHS) of the comparison, PowerShell attempts type coercion as the same type of the left hand side (LHS), so the string 'false' is converted to a boolean during the comparison which results being $true.
In about Comparison Operators documentation on the Equality operators section we can read the following:
The equality operator can compare objects of different types. It is important to understand that the value is on the right-hand side of the comparison can be converted to the type of the left-hand side value for comparison.
A simple demo:
[bool] 'false' # => $true
$false -eq 'false' # => $false
'false' -eq $false # => $true
The last comparison results in $true because the boolean $false in the RHS is converted to string and, in PowerShell, [string] $false results in the literal string false.
In conclusion, by simply changing your if condition your code would work properly:
if($false -eq $UENB) {
continue
}
The other alternative would be to use the logical -not operator:
if(-not $UENB) {
continue
}

What does the special character "!" mean in PowerShell?

What does the special character ! mean in PowerShell?
Or a site which lists all special characters and meaning.
Example:
$string = blah
!$String
(Returns $false)
PowerShell uses the ! character as an alias for the logical -not operator:
$true
!$true
$false
!$false
True
False
False
True
PowerShell interprets everything that is empty, $Null, or 0 to the Boolean $False. Bool can only have $True or $False.
By casting the value to a Boolean you can see what PowerShell interprets for each value:
[bool]0 # False
[bool]1 # True
[bool]"" # False
[bool]"test" # True
[bool]$null # False
The locical NOT operation turns each Boolean into its opposite:
!$True # Is $False
!$False # Is $True
![bool]0 # True
![bool]1 # False
![bool]"" # True
![bool]"test" # False
![bool]$null # True
You were assigning a string to a variable and then checking whether it is empty or not.
$string = blah
!$String # $String is not $Null or Empty so it is $True
# But the !(NOT) operation turns it to $False
Conditionals and loops in programming languages only work with Boolean values.
So when getting user input you can use this to check whether the user has input text, or not, and react on it:
$UserName = Read-Host -Prompt "Whats your Name Sir?"
If ($UserName) {
Write-Output "Happy Birthday $UserName"
}
Else {
Write-Output "I can't congratulate you as I don't know your name :("
}
The ! (exclamation mark) character in PowerShell is a shortcut to the -not operator ('not equal').
For example:
$a = $null;
if(!$a) {
Write-Host '$a is null'
}
Output.
$a is null
The symbol ! is an alias for -Not, which is a unary (one-argument) operator that casts its argument to a Boolean value and then returns the logical opposite of that value. (Most of the time, spelling out -Not is a better choice for readability.)
Boolean is a data type with only two possible values: true and false. In PowerShell the type is known as Bool and the two values are written $True and $False. The -Not/! operator just flips the value to its opposite: -Not $True is $False and ! $False is $True.
But in PowerShell, as in many other programming languages, you can apply Boolean operators (which also include -And and -Or) to non-Boolean values; those values just get automatically cast to Bool before being operated on, as if you had put a [Bool] in front of them.
Values that become $True when so cast are called "truthy"; values that become $False are called "falsy" (sometimes spelled "falsey"). Different programming languages have different conventions for what goes in each category; for instance, an empty array is falsy in Perl and Python, but truthy in Ruby.
In PowerShell, both the null value $Null and the empty string are falsy. So in your code example, !$String would be true if the string is either null or empty (since it would boolify to false and then get flipped to true by the !). Such an expression would likely show up in, for example, an If statement that sets a variable to a default value if it has not already been set to a different one by earlier code.
Besides the empty string and $Null, other falsy values in PowerShell include the number 0 and the empty array. However, an empty HashTable is truthy. You can see these correspondences by casting the values to Boolean, either with [Bool] or the shortcut !!. (! casts the value to Boolean but then flips it; the second ! flips it back. I recommend using !! at the prompt for quicker typing, but spelling out [Bool] for better self-documentation in production code.) Here are some examples:
PS /> [Bool]$Null
False
PS /> !!''
False
PS /> !!0
False
PS /> !!#()
False
PS /> !!#{}
True

How to distinguish between empty argument and zero-value argument in Powershell?

I want to be able to pass a single int through to a powershell script, and be able to tell when no variable is passed. My understanding was that the following should identify whether an argument is null or not:
if (!$args[0]) { Write-Host "Null" }
else { Write-Host "Not null" }
This works fine until I try to pass 0 as an int. If I use 0 as an argument, Powershell treats it as null. Whats the correct way to be able to distinguish between the argument being empty or having a zero value?
You can just test $args variable or $args.count to see how many vars are passed to the script.
Another thing $args[0] -eq $null is different from $args[0] -eq 0 and from !$args[0].
If users like me come from Google and want to know how to treat empty command line parameters, here is a possible solution:
if (!$args) { Write-Host "Null" }
This checks the $args array. If you want to check the first element of the array (i.e. the first cmdline parameter), use the solution from the OP:
if (!$args[0]) { Write-Host "Null" }
If the variable is declared in param() as an integer then its value will be '0' even if no value is specified for the argument. To prevent that you have to declare it as nullable:
param([AllowNull()][System.Nullable[int]]$Variable)
This will allow you to validate with If ($Variable -eq $null) {}

Conditional logic no longer working as expected?

Its been a long day and I think I'm going mad. I wanted to test for a file and generate an email if none existed. Here it is pared down to its most minimal:
> IF('False' -eq (Test-Path D:\Scripts\SFTP\Import\*)){ECHO "SEND EMAIL"}
> SEND EMAIL
__________________________________________________________________________
> IF((Test-Path D:\Scripts\SFTP\Import\*) -eq 'False'){ECHO "SEND EMAIL"}
>
Why doesn't the second command work?
I've tried running the Test-Path outside of the 'IF' statement into a variable and then testing against that, again it doesn't work.
If I simply run the 'Test-Path' command I get a boolean 'False' as expected. I've used conditional logic in this way before and its worked.
What am I doing wrong?
The reason is this. In the first one you have a string as the first operand of the comparison. This forces PS to coerce the second operand to a string if possible. In this case that means calling the .ToString() method of the boolean which would return the 'False' string (if the boolean is actually false of course). In the second case though, you are presenting a boolean as the first operand, so the string is being coerced to a boolean. Obviously it is not working. To avoid these issues, use the builtin $false (or $true) variable. Personally I would suggest just negating the Test-Path. Here are a couple of ways that should work:
if( -NOT (Test-Path D:\Scripts\SFTP\Import\*)){
if( (Test-Path D:\Scripts\SFTP\Import\*) -eq $false){
For the coercing rules of powershell
'False' -eq (Test-Path D:\Scripts\SFTP\Import\*)
the second value of comparision is evaluated as [string]
here
(Test-Path D:\Scripts\SFTP\Import\*) -eq 'False'
the second value of comparison can't be evaluated as [Bool] then it fails.
For bool comparin is optima use the automatic variable $false and $true