Azure Pipeline: Pass System.Debug to a pwsh switch parameter - azure-devops

How do I parse a variable which is either the string "True" or non-existent into a boolean?
I'm trying to pass the value of the predefined azure pipeline variable "system.debug" into a pwsh script as the argument to a switch parameter. I've tried both of these approaches:
-isDebug:([boolean]'$(System.Debug)')
-isDebug:$$(System.Debug)
Both of these approaches work when the "Enable system diagnostics" option is checked. It gets a value of "True" which I then must parse. However, if not checked the System.Debug variable doesn't exist at all, not false or null. This makes my parse fail below:
$isDebug = $false
if($$(System.Debug)){
$isDebug = $$(System.Debug)
}
Error:
+ $isDebug = $$(System.Debug)
+ ~
Unexpected token '(' in expression or statement.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : UnexpectedToken

I was able to check if the environment variable exists with get-environmentVariable and if it does use the value, otherwise don't pass the switch parameter at all.
if([Environment]::GetEnvironmentVariable('SYSTEM_DEBUG') -ne $null){
write-host "SYSTEM_DEBUG: $(system.debug)"
./myscript.ps1 -isDebug:([boolean]$($env:SYSTEM_DEBUG))
}
else {
write-host "SYSTEM_DEBUG was not set"
./myscript.ps1
}

Related

String comparison not working in 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.

PowerShell - Add-Content- Unable to add multiple vars to a file

I'm trying to add an expression to a log file which contains Date,Time some data separated by ";". Unfortunately I get an error every time I change the position of the items in the -value brackets.
Whats seems to be wrong?
This is the code :
Add-Content -path C:\...\outlog.txt -Value($Date + ';' + $Time + ';Checked;' + $strFileName)
This is the error :
Cannot convert argument "1", with value: ";", for "op_Addition" to type "System.TimeSpan": "Cannot convert
value ";" to type "System.TimeSpan". Error: "String was not recognized as a valid TimeSpan.""
At C:\...\Untitled1.ps1:8 char:64
+ ... \outlog.txt -Value($($Date + ';' + $Time + ';'+ $str))
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodArgumentConversionInvalidCastArgument
Try this -
Add-Content -path C:\...\outlog.txt -Value("$Date; $Time; Checked; $strFileName")
If you look at get-help Add-Content -full, and look at the -value parameter, you will see -
-Value <Object[]>
Specifies the content to be added. Type a quoted string, such as "This data is for internal use only", or
specify an object that contains content, such as the DateTime object that Get-Date generates.
You cannot specify the contents of a file by typing its path, because the path is just a string, but you can
use a Get-Content command to get the content and pass it to the Value parameter.
Required? true
Position? 1
Default value None
Accept pipeline input? True (ByPropertyName, ByValue)
Accept wildcard characters? false
It says that it expects a quoted string or an object that contains content. It was missing in your case and hence the + operator was trying to add $date and time.

Pointer or reference variables

I want to use indirect reference variable.
I am setting this at Command Prompt
SET RiskScheduler=true
My code is like this
Write-Host "$Env:RiskScheduler" # prints true
I want to achieve the same should be printed with code like this
$name='RiskScheduler'
Write-host $name # prints RiskScheduler
Write-Host $Env:$name # gives error
The error I am getting is
Cannot process argument because the value of argument "path" is invalid.
Change the value of the "path" argument and run the operation again.
At D:\tmp\buildtools\udclient.6.2\ud_clean.PS1:37 char:17
+ Write-Host $Env: <<<< `$name`
+ CategoryInfo : InvalidArgument: (:) [], PSArgumentException
+ FullyQualifiedErrorId : Argument
I am looking for something that shall first evaluate $name and then evaluate $Env:(value of $name).
Can someone please suggest, what the correct syntax is?
Option 1
You can do this with:
$x = 'RiskScheduler'
Write-Host (Get-Item env:$x).Value
Which will output true in your case
If you have a list of variable names:
$varNames = #('COMPUTERNAME', 'SESSIONNAME', 'RiskScheduler')
ForEach($varName in $varNames) {
Write-Host (Get-Item env:$varName).Value
}
Which will output:
MyPcName
Console
true
You can find more information about this by entering Get-Help about_environment_variables into PowerShell:
Get-Item -Path Env:* | Get-Member
Displaying Environment Variables
You can use the cmdlets that contain the Item noun (the Item cmdlets) to
display and change the values of environment variables. Because
environment variables do not have child items, the output of Get-Item
and Get-ChildItem is the same.
When you refer to an environment variable, type the Env: drive name
followed by the name of the variable. For example, to display the value
of the COMPUTERNAME environment variable, type:
Get-Childitem Env:Computername
Option 2
Another option would be to use this function:
function Get-EnvVar($Name) {
$allVars = dir env:
foreach ($var in $allVars) {
If ($var.Name -eq $Name) {
return $var
}
}
}
The function iterates around all available environment variables and returns the one you are after (in this case, $Env:COMPUTERNAME).
You can then call
Write-Host $myvar.Value
to display the value of COMPUTERNAME
If you have an array of variable names you want the value of:
$varNames = #('COMPUTERNAME', 'SESSIONNAME', 'RiskScheduler')
ForEach($varName in $varNames) {
$var = Get-EnvVar -Name $varName
Write-Host $var.Value
}
Which outputs (for me) :
MyPcName
Console
true

PowerShell error 'can't call null-value expresssion' [duplicate]

I am simply trying to create a powershell script which calculates the md5 sum of an executable (a file).
My .ps1 script:
$answer = Read-Host "File name and extension (ie; file.exe)"
$someFilePath = "C:\Users\xxx\Downloads\$answer"
If (Test-Path $someFilePath){
$stream = [System.IO.File]::Open("$someFilePath",[System.IO.Filemode]::Open, [System.IO.FileAccess]::Read)
$hash = [System.BitConverter]::ToString($md5.ComputeHash($stream))
$hash
$stream.Close()
}
Else{
Write-Host "Sorry, file $answer doesn't seem to exist."
}
Upon running my script I receive the following error:
You cannot call a method on a null-valued expression.
At C:\Users\xxx\Downloads\md5sum.ps1:6 char:29
+ $hash = [System.BitConverter]::ToString($md5.Compute ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
To my understanding, this error means the script is attempting to do something, but another part of the script does not have any information to permit the first part of the script to work properly. In this case, $hash.
Get-ExecutionPolicy outputs Unrestricted.
What is causing this error?
What exactly is my null valued expression?
Any help is appreciated. I apologize if this is trivial and will continue my research.
References:
http://blogs.technet.com/b/heyscriptingguy/archive/2013/03/27/troubleshoot-the-invokemethodonnull-error-with-powershell.aspx
How to get an MD5 checksum in PowerShell
The simple answer for this one is that you have an undeclared (null) variable. In this case it is $md5. From the comment you put this needed to be declared elsewhere in your code
$md5 = new-object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider
The error was because you are trying to execute a method that does not exist.
PS C:\Users\Matt> $md5 | gm
TypeName: System.Security.Cryptography.MD5CryptoServiceProvider
Name MemberType Definition
---- ---------- ----------
Clear Method void Clear()
ComputeHash Method byte[] ComputeHash(System.IO.Stream inputStream), byte[] ComputeHash(byte[] buffer), byte[] ComputeHash(byte[] buffer, int offset, ...
The .ComputeHash() of $md5.ComputeHash() was the null valued expression. Typing in gibberish would create the same effect.
PS C:\Users\Matt> $bagel.MakeMeABagel()
You cannot call a method on a null-valued expression.
At line:1 char:1
+ $bagel.MakeMeABagel()
+ ~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
PowerShell by default allows this to happen as defined its StrictMode
When Set-StrictMode is off, uninitialized variables (Version 1) are assumed to have a value of 0 (zero) or $Null, depending on type. References to non-existent properties return $Null, and the results of function syntax that is not valid vary with the error. Unnamed variables are not permitted.

Passing default values in powershell to another script

Given:
setenv.ps1:
param([Parameter(Mandatory=$false)][ValidateSet(541,642,643,644,645,"tmp")]$version=645)
echo "[setenv] Version = $version"
dbupdate.ps1:
param($version)
. setenv $version
echo "[dbupdate] Version = $version"
Output:
PS C:\> dbupdate.ps1
c:\utils\setenv.ps1 : Cannot validate argument on parameter 'version'. The argument is null, empty, or an element of the argument collection contains a null value. Supply a collection that does not
contain any null values and then try the command again.
At c:\utils\dbupdate.ps1:3 char:10
+ . setenv $version
+ ~~~~~~~~
+ CategoryInfo : InvalidData: (:) [setenv.ps1], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,setenv.ps1
[dbupdate] Version =
I want to invoke dbupdate.ps1 without any arguments, which should tell setenv.ps1 use the default value for the $version argument. However, the default value is the implementation detail of the serenv.ps1 script - I do not want it to "leak" into the dbupdate.ps1.
How do I do it?
EDIT
Trying to follow the advice of Cookie Monster yields the following error:
c:\dayforce\utils\setenv.ps1 : Cannot validate argument on parameter 'version'. The argument "System.Collections.Hashtable"
does not belong to the set "541,642,643,644,645,tmp" specified by the ValidateSet attribute. Supply an argument that is in the
set and then try the command again.
At C:\dayforce\utils\dbupdate.ps1:9 char:10
+ . setenv $params
+ ~~~~~~~
+ CategoryInfo : InvalidData: (:) [setenv.ps1], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,setenv.ps1
This is a good use case for splatting your arguments. Particularly if you ever need to do it for more than a single parameter...
Modified dbupdate.ps1:
param($version)
#Build a hashtable containing the parameters and values you want to call
$params = #{}
if($version) { $params.version = $version }
. setenv #params
You could even use #PSBoundParameters, there's an example of this in the help system. Run Get-Help about_Splatting for more information!
Edit for clarification
Not sure why this was downvoted. Here is verification that this syntax works. Calling #params is required, not $params.
Lastly, here is one more example demonstrating splatting PSBoundParameters, even less code:
Get-Help about_Splatting or Googling "PowerShell splatting" will provide more examples if needed!
Your overriding the default value with a value of null. The validateset attribute seems to be causing that error. I tried adding allownull,allowemptystring and allowemptycollection and added a null value to the set but validateset still caused that error. As an alternative you could just do this -
if ($version) {
. .\setenv.ps1 $version
} else {
. .\setenv.ps1
}