Chef: Two contradicting not_if powershell guard statements evaluating to true - powershell

So I am working with chef and need to chain some resources together if a precondition is true. I need to check if the version of a process is what I want it to be if so do things. I was seeing odd behavior and was noticing that my guard statements are not being evaludated in the way I would expect. So I made this tests
log 'log_version' do
message 'Peregrin Took'
level :error
guard_interpreter :powershell_script
not_if <<-EOH
(C:\\Program Files\\telegraf\\telegraf.exe --version) -Like '*1.7.2*'
EOH
end
log 'log_version' do
message 'Meriadoc Brandybuck'
level :error
guard_interpreter :powershell_script
not_if <<-EOH
(C:\\Program Files\\telegraf\\telegraf.exe --version) -NotLike '*1.7.2*'
EOH
end
And when I run this I get
Recipe: win-telegraf::telegraf
* log[log_version] action write[2018-07-24T07:31:42-07:00] INFO: Processing log[log_version] action write (win-telegraf::telegraf line 47)
[2018-07-24T07:31:42-07:00] INFO: Processing powershell_script[Guard resource] action run (dynamically defined)
[2018-07-24T07:31:43-07:00] ERROR: Peregrin Took
* log[log_version] action write[2018-07-24T07:31:43-07:00] INFO: Processing log[log_version] action write (win-telegraf::telegraf line 57)
[2018-07-24T07:31:43-07:00] INFO: Processing powershell_script[Guard resource] action run (dynamically defined)
[2018-07-24T07:31:43-07:00] ERROR: Meriadoc Brandybuck
Why are both of these statements logging? When I run these powershell snippets in the vm I get one returning true and the other returning false. So I would expect only one log line to write. But when I run both of them are writing.
After doing some reading it seems that the guard statement makes its decision off the $LASTEXITCODE and in the case of my statement both will have a $LASTEXITCODE of 0
So I have tried changing my guard statement to force an exit code of something other than 0
log 'log_version' do
message 'Peregrin Took'
level :error
guard_interpreter :powershell_script
not_if <<-EOH
if((C:\\Program Files\\telegraf\\telegraf.exe --version) -Like '*1.7.2*') { exit 1 }
EOH
end
log 'log_version opposite' do
message 'Meriadoc Brandybuck'
level :error
guard_interpreter :powershell_script
not_if <<-EOH
if((C:\\Program Files\\telegraf\\telegraf.exe --version) -NotLike '*1.7.2*') { exit 1 }
EOH
end
Though this has not changed results and I am still seeing both log resources executed.

So I got a working guard, the issue was with the space in Program Files and the --version being interpreted as Powershell code. by making my guard string like this
"(&'c:\\Program Files\\telegraf\\telegraf.exe' --version) -like '*#{node['windows']['telegraf']['version']}*'"
By single quoting the path the path was correctly interpreted by powershell, and I needed the & operator so that the string in the parentheses was interpreted like a commandlet.

Related

BTSKTask AddResource - How to raise an error in case the command fails

We are using the following command to deploy BizTalk assemblies via PowerShell:
BTSTask AddResource /ApplicationName:$App /Type:$BizTalkAssemblyType /Overwrite /Source:$Source /Options:GacOnAdd,GacOnInstall,GacOnImport
See: https://learn.microsoft.com/en-us/biztalk/core/addresource-command-biztalk-assembly
There are certain reasons this command can fail, e.g. an orchestration is not in the unenlisted state or one or more instances of the orchestration exists.
In this case the command does not raise an error so the script continues with an output like
Command failed with 1 errors, 0 warnings.
Because in this situation the assembly does not get deployed we would like to fail the PowerShell script e.g. by raising an error. How to achieve this?
You need to capture the output and check it for the failure, or rather, check for success and fail if it doesn't.
[array] $cmdOutput = BTSTask AddResource /ApplicationName:$App /Type:$BizTalkAssemblyType /Overwrite /Source:$Source /Options:"GacOnAdd,GacOnInstall,GacOnImport"
$line = $cmdOutput.Count-2
if ( $cmdOutput[$line] -eq "Command succeeded with 0 errors, 0 warnings.")
{
Write-Output "Deploy suceeded"
}
else
{
Throw "Deploy failed $cmdOutput"
}

Task Control Option - Custom Condition - run task when previous failed or timed out

Is there an option to set the custom condition that will test if the previous task has failed OR timed out?
Currently, I'm using the Only when a previous task has failed which works when the task fails. If the task times out, then it is not considered an error and it is skipped.
I need a custom condition then, something like or(failed(), timedout()). Is it possible?
Context
We have this intermittent problem with the npm install task that we can't find a reason for but it is resolved with next job run, so we were searching for a retry functionality. Partial solution was to duplicate npm install and use the Control Option but it wasnt working for all "failure" cases. Solution gave by #Levi Lu-MSFT seems to be working for all our needs (it does retry) but sadly it doesnt solve the problem, 2nd line repeated task also fails.
Sample errors:
20741 error stack: 'Error: EPERM: operation not permitted, unlink \'C:\\agent2\\_work\\4\\s\\node_modules\\.staging\\typescript-4440ace9\\lib\\tsc.js\'',
20741 error errno: -4048,
20741 error code: 'EPERM',
20741 error syscall: 'unlink',
20741 error path: 'C:\\agent2\\_work\\4\\s\\node_modules\\.staging\\typescript-4440ace9\\lib\\tsc.js',
20741 error parent: 's' }
20742 error The operation was rejected by your operating system.
20742 error It's possible that the file was already in use (by a text editor or antivirus),
20742 error or that you lack permissions to access it.
or
21518 verbose stack SyntaxError: Unexpected end of JSON input while parsing near '...ter/doc/TypeScript%20'
21518 verbose stack at JSON.parse (<anonymous>)
21518 verbose stack at parseJson (C:\agent2\_work\_tool\node\8.17.0\x64\node_modules\npm\node_modules\json-parse-better-errors\index.js:7:17)
21518 verbose stack at consumeBody.call.then.buffer (C:\agent2\_work\_tool\node\8.17.0\x64\node_modules\npm\node_modules\node-fetch-npm\src\body.js:96:50)
21518 verbose stack at <anonymous>
21518 verbose stack at process._tickCallback (internal/process/next_tick.js:189:7)
21519 verbose cwd C:\agent2\_work\7\s
21520 verbose Windows_NT 10.0.14393
21521 verbose argv "C:\\agent2\\_work\\_tool\\node\\8.17.0\\x64\\node.exe" "C:\\agent2\\_work\\_tool\\node\\8.17.0\\x64\\node_modules\\npm\\bin\\npm-cli.js" "install"
21522 verbose node v8.17.0
21523 verbose npm v6.13.4
21524 error Unexpected end of JSON input while parsing near '...ter/doc/TypeScript%20'
21525 verbose exit [ 1, true ]
Sometimes also time's out
It is possible to add a custom condition. If you want the task to be executed when previous task failed or skipped, you can use custom condition not(succeeded())
However there is a problem with above custom condition, it does not work in the multiple tasks scenario.
For example, there are three tasks A,B,C. The expected behavior is Task C gets executed only when Task B failed. But the actual behavior is Task C will also get executed when Task A failed even if Task B succeeded. Check below screenshot.
The workaround for above problem is to add a script task to call azure devops restful api to get the status of Task B and set it to a variable using this expression echo "##vso[task.setvariable variable=taskStatus]taskStatus".
For below example, Add a powershell task (You need to set conditon for this task to Even if a previous task has failed, even if the build was canceled to always run this powershell task) before Task C to run below inline scripts:
$url = "$(System.TeamFoundationCollectionUri)$(System.TeamProject)/_apis/build/builds/$(Build.BuildId)/timeline?api-version=5.1"
$result = Invoke-RestMethod -Uri $url -Headers #{authorization = "Bearer $env:SYSTEM_ACCESSTOKEN"} -ContentType "application/json" -Method get
#Get the task B's task result
$taskResult = $result.records | where {$_.name -eq "B"} | select result
#set the Task B's taskResult to variable taskStatus
echo "##vso[task.setvariable variable=taskStatus]$($taskResult.result)"
In order above scripts can access the access token, you also need to click the Agent job and check option Allow scripts to access the OAuth token. Refer to below screenshot.
At last you can use custom condition and(not(canceled()), ne(variables.taskStatus, 'succeeded')) for Task C. Task C should be executed only when Task B not succeeded.
Although I failed to find a built-in function to detect if a build step is timed out, you can try to emulate this with the help of variables.
Consider the following YAML piece of pipeline declaration:
steps:
- script: |
echo Hello from the first task!
sleep 90
echo "##vso[task.setvariable variable=timedOut]false"
timeoutInMinutes: 1
displayName: 'A'
continueOnError: true
- script: echo Previous task has failed or timed out!
displayName: 'B'
condition: or(failed(), ne(variables.timedOut, 'false'))
The first task (A) is set to time out after 1 minute, but the script inside emulates the long-running task (sleep 90) for 1.5 minutes. As a result, the task times out and the timedOut variable is NOT set to false. Hence, the condition of the task B evaluates to true and it executes. The same happens if you replace sleep 90 with exit 1 to emulate the task A failure.
On the other hand, if task A succeeds, neither of the condition parts of task B evaluates to true, and the whole task B is skipped.
This is a very simplified example, but it demonstrates the idea which you can tweak further to satisfy the needs of your pipeline.

Mule PowerShell Connector - The variable '$<variable-name>' cannot be retrieved because it has not been set

Can't figure out why my variable in my PowerShell script keeps saying the variable is null (Error: The variable '$' cannot be retrieved because it has not been set.)
Mule Flow = HTTP --> Set Payload --> PowerShell --> Logger --> End
~ MULE XML Code Snippet - Using PowerShell Connector 3.x version ~
<powershell:execute config-ref="PowerShell" doc:name="PowerShell" scriptFile="classpath:powershell-script.ps1">
<powershell:parameters>
<powershell:parameter key="variable1">#[groovy: payload.variable1 ?: '']</powershell:parameter>
<powershell:parameter key="variable2">#[groovy: payload.variable2 ?: '']</powershell:parameter>
<powershell:parameter key="variable3">#[groovy: payload.variable3 ?: '']</powershell:parameter>
<powershell:parameters>
<powershell:execute>
~ PowerShell Code ~
Set-StrictMode -Version Latest
Param($variable1, $variable2, $variable3)
#Create NEW AD accounts
Function Start-Commands
{
Write-Output "Start-Commands"
Write-Output "Parameters-: $variable1 - $variable2 - $variable3"
}
Start-Commands
~ Console Output ~
Root Exception stack trace:
org.mule.modules.powershell.PowershellException: The message could not be sent. An exception has been thrown: [{"Exception":"System.Management.Automation.RuntimeException: The variable '$flowVarManager' cannot be retrieved because it has not been set.\r\n
You've since stated that Mule as the environment from which PowerShell is invoked is incidental to the problem.
The symptom - error The variable '<name>' cannot be retrieved because it has not been set. - indeed implies that a variable is being accessed that has never been set (initialized), and this kind of error is only raised if Set-StrictMode -Version 1 or higher is in effect.
-Version 1 still allows unset variables inside expandable strings (e.g., "$noSuchVar"), but -Version 2 and above does not.
It's fair to assume that you're not on PowerShell v1 (where -Version Latest would imply -Version 1), so any reference to an unset variable encountered during execution would trigger the error.
However, the parameter variables that PowerShell implicitly manages (as part of the param(...) block) are not subject to Set-StrictMode checking, as the following example demonstrates:
PS> Set-StrictMode -Version Latest; & { param($paramVar1) "[$paramVar1]" }
[]
Note: & { ... } uses &, the call operator, to execute a script block { ... }, but such a script block behaves just as a script would.
As you can see, even though no argument was passed to parameter -paramVar1 - i.e., no value was bound to the underlying $paramVar1 parameter variable - accessing $paramVar1 did not cause an error, evaluated to $null, which in the context of string interpolation becomes the empty string.
Contrast this with referencing a truly unset variable:
PS> Set-StrictMode -Version Latest; & { param($paramVar1) "[$somveVar]" }
The variable '$somveVar' cannot be retrieved because it has not been set.
Because $someVar wasn't ever set (and isn't a parameter variable), it triggered the error.
Therefore, we can observe the following about the code printed in the question as of this writing, reproduced here:
# NOTE: This code is syntactically invalid due to the placement
# of the Set-StrictMode call.
Set-StrictMode -Version Latest
Param($variable1, $variable2, $variable3)
#Create NEW AD accounts
Function Start-Commands
{
Write-Output "Start-Commands"
Write-Output "Parameters-: $variable1 - $variable2 - $variable3"
}
Start-Commands
Firstly, the Set-StrictMode call cannot be placed above the param(...) block, as the latter must be the first statement in a script.
Secondly, given that the error message complains about a variable named $flowVarManager and given that the code makes no reference whatsoever to that variable, the quoted code alone cannot be the source of the problem.
To fix the issue I removed Set-StrictMode -Version Latest
Based off my research I found this article -> https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/set-strictmode?view=powershell-6
The Set-StrictMode cmdlet configures strict mode for the current scope and all child scopes, and turns it on and off. When strict mode is on, Windows PowerShell generates a terminating error when the content of an expression, script, or script block violates basic best-practice coding rules.
When Set-StrictMode is ON it is interfering with the passing of the parameter values from the MS PowerShell Connector - Mule 3 to the PS script. Upon removing the line of code, the parameters were getting set with values.

Detecting error after a powershell statement is executed

I am using powershell to run sqlplus and I would like PowerShell to detect if there are error after the script was run and to perform some action instead of me looking at the result file.
& 'sqlplus' 'system/myOraclePassword' '#Test' | out-file 'result.txt';
Normally in DOS, there is %errorlevel% when the command encounters error and I wonder if there is similar stuff in PowerShell?
Of course, I can read the log file myself but sometimes, thing got too routine and I may forget.
My Test.sql:
select level from dual
connect by level<5;
select 10/0 from dual;
quit;
There is clearly a division by zero error. The result.txt captures it but I would like powershell to detect it as well
SQL*Plus: Release 12.1.0.2.0 Production on Thu Apr 27 16:24:30 2017
Copyright (c) 1982, 2014, Oracle. All rights reserved.
Last Successful login time: Thu Apr 27 2017 16:17:34 -04:00
Connected to:
Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production
With the Partitioning, OLAP, Advanced Analytics and Real Application Testing options
LEVEL
----------
1
2
3
4
select 10/0 from dual
*
ERROR at line 1:
ORA-01476: divisor is equal to zero
Does the powershell statement return an errorlevel after the statement is executed like DOS?
I have tried:
& 'sqlplus' 'system/myOraclePassword' '#Test' | out-file 'result.txt';
if (errorlevel 1)
{ write-host error;
}
else
{ write-host ok;
}
But that has caused syntax error?
errorlevel : The term 'errorlevel' 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.
What is a proper way to check error in powershell?
UPDATE
I used this:
if ($LASTEXITCODE -ne 0 )
{
write-host error;
}
else
{
write-host ok;
}
Since you are invoking an executable, you probably want to check for the $LASTEXITCODE variable or the return value of sqlplus. In PowerShell each variable has a $ prefix.

How do I get topshelf to return error code when command fails?

I am trying to start my service with powershell but currently it fails. I don't know why it fails but that is not the point here. When trying to start the host all I don't get the correct exit code so my automatic deploy fails silently.
What I'm trying to do is:
$cmd = "$folder" + "\MyService.exe"
try
{
& $cmd stop
& $cmd uninstall
& $cmd install
& $cmd start
}
catch
{
Write-Host "Error: Update of service failed"
exit 1
}
The start command fails with the following messge:
Topshelf.Hosts.StartHost Error: 0 : The service failed to start., System.InvalidOperationException: Cannot start service MyService on computer '.'. ---> System.ComponentModel.Win32Exception: The service cannot be started, either because it is disabled or because it has no enabled devices associated with it
--- End of inner exception stack trace ---
at System.ServiceProcess.ServiceController.Start(String[] args)
at System.ServiceProcess.ServiceController.Start()
at Topshelf.Runtime.Windows.WindowsHostEnvironment.StartService(String serviceName)
at Topshelf.Hosts.StartHost.Run()
and I never get into the catch statement of my powershell script.
UPDATE:
Note that I am asking for how to get the method to the catch statement and not the solution to the actual exception. I have solved the actual exception but I want better feedback in the future if it fails, and that is want the catch statement to be executed which it isn't in case of error.
try/catch in PowerShell doesn't work with exe.
After myservice.exe calls you need to check the automatic variable $LastExitCode.
Try something like this:
$out = & $cmd start
if ($LastExitCode -ne 0) # if exe returns 0 on success, if not change the condition accordingly
{
"ERROR: $out"
return # to exit script or do something else.
}