Newman with AzureDevOps + Powershell - powershell

Good morning everyone
I am trying integrate postman tests with AzureDevops Release pipeline.
I have two steps:
First step is to install newman
Second step is to run collection scripts with newman run comand
The second step looks like:
try
{
$testFiles = Get-ChildItem *.postman_collection.json -Recurse
$environmentFile = Get-ChildItem *staging.postman_environment.json -Recurse
Write-Host $testFiles.Count files to test
foreach ($f in $testFiles)
{
$environment = $environmentFile[0].FullName
Write-Host running file $f.FullName
Write-Host usting environment $environment
$collection = $f.FullName
$resultFile = "Results\" + $f.BaseName + ".xml"
Write-Host running $collection
Write-Host will create $resultFile
$(newman run $collection -e $environment -r junit --reporter-junit-export $resultFile)
}
}
catch
{
Write-Host "Exception occured"
Write-Host $_
}
Above step do not work as expected. In the release log I can see the both messages like:
Write-Host running $collection
Write-Host will create $resultFile
However the line
$(newman run $collection -e $environment -r junit --reporter-junit-export $resultFile)
is not being executed.
I did the same on my local machine and the command is working. However the bad thing is the try catch block is not working and only I can see as the result is :
2019-11-22T15:11:23.8332717Z ##[error]PowerShell exited with code '1'.
2019-11-22T15:11:23.8341270Z ##[debug]Processed: ##vso[task.logissue type=error]PowerShell exited with code '1'.
2019-11-22T15:11:23.8390876Z ##[debug]Processed: ##vso[task.complete result=Failed]Error detected
2019-11-22T15:11:23.8414283Z ##[debug]Leaving D:\a\_tasks\PowerShell_e213ff0f-5d5c-4791-802d-52ea3e7be1f1\2.151.2\powershell.ps1.
Do anyone know how to get real error or had experience with newman testing in AzureDevOps ?

When you run those above scripts in VSTS, please remove $() in the newman run line:
newman run $collection -e $environment -r junit --reporter-junit-export $resultFile
Then the script can be run very successfully.
I think you have known that for powershell command line, there will no result displayed in the powershell command line interface in the even if the newman run command has been ran succeed. So, there will no any directly message displayed in the log to let you know whether it is succeed. To confirm this in VSTS, you could check the agent cache if you are using private agent:

Related

Gitlab CI/CD validate powershell script

I am distributing several powershell scripts to various machines and want to make sure it is executable and does not contain any errors before uploading the new version to our distribution system.
How can I solve this with Gitlab .gitlab-ci.yml?
I have found a solution for my issue. This step in the build process checks the Powershell script and aborts the build if there are any errors.
gitlab-ci.yml
stages:
- validate
validate_script:
stage: validate
image:
name: "mcr.microsoft.com/powershell:ubuntu-20.04"
script:
- pwsh -File validate.ps1
tags:
- docker
validate.ps1
Write-Host "Install PSScriptAnalyzer"
Install-Module PSScriptAnalyzer -Scope CurrentUser -Confirm:$False -Force
Write-Host "Check mypowershell.ps1"
$result = Invoke-ScriptAnalyzer -Path 'src/mypowershell.ps1'
if ($result.Length -gt 0) {
Write-Host "Script Error"
Write-Host ($result | Format-Table | Out-String)
exit 1;
}
Not sure if this is what you are looking for or if it applies at all to .yml files as I'm not familiar with them, but for powershell script files you can use the creation of [scriptblock] to check for syntax and other compile errors. You can create a function with this code and pass in powershell script files to it which can take the code, try to compile it, and return any exceptions encountered.
# $code = Get-Content -Raw $file
$code = #"
while {
write-host "no condition set on while "
}
do {
Write-Host "no condition set on unitl "
} until ()
"#
try {
[ScriptBlock]::Create($code)
}
catch {
$_.Exception.innerexception.errors
}
Output
Extent ErrorId Message IncompleteInput
------ ------- ------- ---------------
MissingOpenParenthesisAfterKeyword Missing opening '(' after keyword 'while'. False
MissingExpressionAfterKeyword Missing expression after 'until' in loop. False
I also recommend looking into Pester for testing scripts. If you are unfamiliar it is a testing and mocking framework for Powershell code

Capture errors within ForEach-Object -Parallel in Powershell 7

I am trying to execute Powershell script (7.0) file using Powershell 7-Preview which iterates through all the databases, update them using DACPAC file and execute other SQL Server scripts in all the DBs.
It works fine when there is no errors, however, in case of an error while executing Dacpac file
It gives below error and stops executing the script further.
##[error] Could not deploy package.
##[error]PowerShell exited with code '1'.
Any pointer on how we can catch the errors gracefully in PowerShell within the Parallel statement and let script to be continued for other databases? Try-Catch block does not seem to be working here.
I am new to PowerShell. This PowerShell script is a part of DevOps release pipeline.
#.. Variables are defined here ..#
[string[]]$DatabaseNames = Invoke-Sqlcmd #GetDatabasesParams | select -expand name
[int]$ErrorCount = 0
$DatabaseNames | ForEach-Object -Parallel {
try
{
echo "$_ - Updating database using DACPAC."
dir -Path "C:\Program Files (x86)\Microsoft Visual Studio*" -Filter "SqlPackage.exe" -Recurse -ErrorAction SilentlyContinue | %{$_.FullName} {$using:SqlPackagePath /Action:Publish /tu:$using:DatabaseUsername /tp:$using:DatabasePassword /tsn:$using:ServerInstance /tdn:"$_" /sf:using:$DacpacLocation /p:BlockOnPossibleDataLoss=False}
echo "$_ - Updating the scripts."
$OngoingChangesScriptParams = #{
"Database" = "$_"
"ServerInstance" = "$ServerInstance"
"Username" = "$DatabaseUsername"
"Password" = "$DatabasePassword"
"InputFile" ="$SystemWorkingDir\$OngoingChangesScriptLocation"
"OutputSqlErrors" = 1
"QueryTimeout" = 9999
"ConnectionTimeout" = 9999
}
Invoke-Sqlcmd #OngoingChangesScriptParams
}
catch {
$ErrorCount++
echo "Internal Error. The remaining databases will still be processed."
echo $_.Exception|Format-List -force
}
}```
Logs-
2020-09-17T19:21:59.3602523Z *** The column [dbo].[FileJob].[GMTOffset] on table [dbo].[FileJob] must be added, but the column has no default value and does not allow NULL values. If the table contains data, the ALTER script will not work. To avoid this issue you must either: add a default value to the column, mark it as allowing NULL values, or enable the generation of smart-defaults as a deployment option.
2020-09-17T19:21:59.4253722Z Updating database (Start)
2020-09-17T19:21:59.4274293Z An error occurred while the batch was being executed.
2020-09-17T19:21:59.4280337Z Updating database (Failed)
2020-09-17T19:21:59.4330894Z ##[error]*** Could not deploy package.
2020-09-17T19:22:00.3399607Z ##[error]PowerShell exited with code '1'.
2020-09-17T19:22:00.7303341Z ##[section]Finishing: Update All Companies

azure cli not stopping on error in PS script

Boiled down to the minimum I have a Powershell script that looks like this:
$ErrorActionPreference='Stop'
az group deployment create -g ....
# Error in az group
# More az cli commands
Even though there is an error in the az group deployment create, it continues to execute beyond the error. How do I stop the script from executing on error?
Normally, the first thing to try is to wrap everything in a try...catch block.
try {
$ErrorActionPreference='Stop'
az group deployment create -g ....
# Error in az group
# More az cli commands
}
catch {
Write-Host "ERROR: $Error"
}
Aaaaand it doesn't work.
This is when you scratch your head and realize that we are dealing with Azure CLI commands and not Azure PowerShell. They are not native PowerShell commands which would honor $ErrorActionPreference, instead, (as bad as it sounds), we have to treat each Azure CLI command independently as if we were running individual programs (in the back end, the Azure CLI is basically aliases which run python commands. Ironically most of Azure PowerShell commands are just PowerShell wrappers around Azure CLI commands ;-)).
Knowing that the Azure CLI will not throw a terminating error, instead, we have to treat it like a program, and look at the return code (stored in the variable $LASTEXITCODE) to see if it was successful or not. Once we evaluate that, we can then throw an error:
az group deployment create -g ....
if($LASTEXITCODE){
Write-Host "ERROR: in Az Group"
Throw "ERROR: in Az Group"
}
This then can be implemented into a try...catch block to stop the subsequent commands from running:
try {
az group deployment create -g ....
if($LASTEXITCODE){
Write-Host "ERROR: in Az Group"
Throw "ERROR: in Az Group"
}
# Error in az group
# More az cli commands
}
catch {
Write-Host "ERROR: $Error"
}
Unfortunately this means you have to evaluate $LASTEXITCODE every single time you execute an Azure CLI command.
You may use the automatic variable $?. This contains the result of the last execution, i.e. True if succeded or False if it failed: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_automatic_variables?view=powershell-5.1#section-1
Your code would look something like this:
az group deployment create -g ....
if(!$?){
Write-Error "Your error message"
# Handle your error
}
Unfortunately, and same to #HAL9256 answer, you will need to add this code every single time you execute azure-cli
Update - PS Version 7 error trapping for Az CLI (or kubectl)
This behavior changed significantly in PowerShell v7:
https://github.com/PowerShell/PowerShell/issues/4002
https://github.com/PowerShell/PowerShell/pull/13361
I ended up using the following solution instead which is consistent with PowerShell v5/7. It has the advantage of accepting pipeline input for commands like kubectl where stdio can be used for applying configuration etc.
I used scriptblocks since this integrates nicely into existing scripts without breaking syntax:
# no error
# Invoke-Cli {cmd /c echo hi}
# throws terminating error
# (with psv7 you can now call Get-Error to see details)
# Invoke-Cli {az bad}
# outputs a json object
# Invoke-Cli {az account show} -AsJson
# applies input from pipeline to kubernetes cluster
# Read-Host "Enter some json" | Invoke-Cli {kubectl apply -f -}
function Invoke-Cli([scriptblock]$script, [switch]$AsJson)
{
$ErrorActionPreference = "Continue"
$jsonOutputArg = if ($AsJson)
{
"--output json"
}
$scriptBlock = [scriptblock]::Create("$script $jsonOutputArg 2>&1")
if ($MyInvocation.ExpectingInput)
{
Write-Verbose "Invoking with input: $script"
$output = $input | Invoke-Command $scriptBlock 2>&1
}
else
{
Write-Verbose "Invoking: $script"
$output = Invoke-Command $scriptBlock
}
if ($LASTEXITCODE)
{
Write-Error "$Output" -ErrorAction Stop
}
else
{
if ($AsJson)
{
return $output | ConvertFrom-Json
}
else
{
return $output
}
}
}
Handling command shell errors in PowerShell <= v5
Use $ErrorActionPreference = 'Stop' and append 2>&1 to the end of the statement.
# this displays regular output:
az account show
# this also works as normal:
az account show 2>&1
# this demonstrates that regular output is unaffected / still works:
az account show -o json 2>&1 | ConvertFrom-Json
# this displays an error as normal console output (but unfortunately ignores $ErrorActionPreference):
az gibberish
# this throws a terminating error like the OP is asking:
$ErrorActionPreference = 'Stop'
az gibberish 2>&1
Background
PowerShell native and non-native streams, while similar, do not function identically. PowerShell offers extended functionality with streams and concepts that are not present in the Windows command shell (such as Write-Warning or Write-Progress).
Due to the way PowerShell handles Windows command shell output, the error stream from a non-native PowerShell process is unable (by itself) to throw a terminating error in PowerShell. It will appear in the PowerShell runspace as regular output, even though in the context of Windows command shell, it is indeed writing to the error stream.
This can be demonstrated:
# error is displayed but appears as normal text
cmd /c "asdf"
# nothing is displayed since the stdio error stream is redirected to nul
cmd /c "asdf 2>nul"
# error is displayed on the PowerShell error stream as red text
(cmd /c asdf) 2>&1
# error is displayed as red text, and the script will terminate at this line
$ErrorActionPreference = 'Stop'
(cmd /c asdf) 2>&1
Explanation of 2>&1 workaround
Unless otherwise specified, PowerShell will redirect Windows command shell stdio errors to the console output stream by default. This happens outside the scope of PowerShell. The redirection applies before any errors reach the PowerShell error stream, making $ErrorActionPreference irrelevant.
The behavior changes when explicitly specified to redirect the Windows command shell error stream to any other location in PowerShell context. As a result, PowerShell is forced to remove the stdio error redirection, and the output becomes visible to the PowerShell error stream.
Once the output is on the PowerShell error stream, the $ErrorActionPreference setting will determine the outcome of how error messages are handled.
Further info on redirection and PowerShell streams
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_redirection?view=powershell-7.2
The sample from HAL9256 didn't work for me either, but I did find a workaround:
az group deployment create -g ....
if($error){
Write-Host "ERROR: in Az Group"
#throw error or take corrective action
$error.clear() # optional - clear the error
}
# More az cli commands
I used this to try to deploy a keyvault, but in my environment, soft delete is enabled, so the old key vault is kept for a number of days. If the deployment failed, I'd run a key vault purge and then try the deployment again.
Building from HAL9256's and sam's answers, add this one-liner after each az cli or Powershell az command in a Powershell script to ensure that the catch block is hit when an error occurs:
if($error){ throw $error }
Then for the catch block, clear the error, e.g.
catch {
$ErrorMessage = $_.Exception.Message
$error.Clear()
Write-Warning "-- Operation Failed: $ErrorMessage"
}

How to read the logs from TFS Build/Release and change the task status accordingly?

I am using TFS(on premises 2015)automated build and release for one of my project. In release definition, I have an ALM task and I can see the TFS release log returning "completed successfully: Y (or N) " in the log based on the task completion status in ALM and the ALM task always shows a success. Is there any way that I can read the this "completed successfully: N" from the logs and fail the ALM release task itself as an indication of failure?
Thanks in advance for any help!
Well you're not giving much help here. With or having a better idea of what your script does... But you could do something like
(At the end of your command)
Command -errorvariable fail
If ($fail -ne $null){
$success = $fail
} Else {
$success = $true
}
You could also pipe the error variable into the file at the next line if it's a txt log.
Command -ev fail
$fail | out-file log.txt -append
Or
Command -ev fail
If ($fail -ne $null) {
Write-output "the command failed at $variable" | out-file log.txt -append
}
$variable would be the variable used for your loop or whatever to identify the current task.

PowerShell on remote target shows incomplete verbose logs

I wrote the PowerShell script to run the SQL scripts on target database server using RoundhousE.
Example code in PowerShell script:
$outsideTransactionDir = (Join-Path $migrateDir "OutsideTransaction")
Write-Host $(Get-Item $outsideTransactionDir).FullName
if ((Test-Path $outsideTransactionDir) -eq $true)
{
Write-Verbose "Running RoundhousE (Outside Transaction)" -Verbose
Write-Host "Running RoundhousE (Outside Transaction)"
ExecuteRoundhousE $(Get-Item $outsideTransactionDir).FullName $([String]::Format('-cs "{0}" -ni -ct 500 -rb "runBeforeUp" –-debug --env PROD --vf "db_version.xml" {1}', $settings.roundhouse.connectionstring, $r))
}
Write-Verbose "Running RoundhousE" -Verbose
Write-Host "Running RoundhousE"
ExecuteRoundhousE $(Get-Item $migrateDir).FullName $([String]::Format('-cs "{0}" --trx -ni -ct 500 -rb "runBeforeUp" -sp "StoredProcedure" -fu "UserDefinedFunction" -vw "View" -ix "Index" –-debug --env PROD --vf "db_version.xml" {1}', $settings.roundhouse.connectionstring, $r))
After executing the above lines of code through the Run PowerShell on Target Server task at VSTS release. Internally one text file will be created in specified server, it contains logs (changes) related to database SQL scripts execution, and also same logs will be displayed in VSTS release logs under specified task. But the issue is total logs will not be displayed In VSTS release logs only few logs will be displayed in VSTS release logs.