Invoke-ASCmd to return error/warning in PowerShell - powershell

Running the PowerShell in my deployment pipeline:
Write-Host "Invoking deployment script... This may take several minutes."
Invoke-ASCmd -Server:$SsasServer -InputFile $path\$bim.xmla | Out-File $path\$bim.xml
Write-Host "Please check $path\$bim.xml as this is output of this deployment"
I want to take the output file of the Invoke-ASCmd: $bim.xml and determine if a warning exist or error exist. It would be ideal to return a warning messages to the console in my azure pipeline.
Example Warning message:
<return xmlns="urn:schemas-microsoft-com:xml-analysis"><root xmlns="urn:schemas-microsoft-com:xml-analysis:empty"><Exception xmlns="urn:schemas-microsoft-com:xml-analysis:exception" /><Messages xmlns="urn:schemas-microsoft-com:xml-analysis:exception"><Error ErrorCode="-1055784777" Description="The JSON DDL request failed with the following error: Failed to execute XMLA. Error returned: 'The column 'ProcessDateKey' in table 'Financial Measures' has invalid bindings specified.
'.." Source="Microsoft SQL Server 2016 Analysis Services Managed Code Module" HelpFile="" /></Messages></root></return>
I am not sure if this is the correct way to handle this xml exactly. Here is my first draft:
[xml]$XmlDocument = Get-Content -Path $path\$bim.xml
if($XmlDocument.return.root.Messages.Error){
foreach ($errorMessage in $XmlDocument.return.root.Messages.Error ){
$message = $errorMessage.Description
Write-Error $message
}
exit 1
}

You cannot use if directly on the Error node, this could also be an Error collection. I recommend using a xPath query to find all the Error messages.
Also you can get directly the Xml output from Invoke-AsCmd into an xml variable:
[xml]$xmlResult = Invoke-ASCmd -Server:$SsasServer -InputFile $path\$bim.xmla
$errors = $xmlResult.return.root.Messages.SelectNodes("*[name() = 'Error']")
if($errors.Count -gt 0){
foreach ($errorMessage in $errors){
Write-Error $message.Description
}
exit 1
}

Related

How to perform conditional looping using powershell

I want to execute test cases using commands using PowerShell scripts.
How many time I want to execute those tests depend upon the $HowManyTimes variable.
It's possible that I get the success result before the $HowManyTimes
e.g As of now I set it to 3, but what if I get the successful result in the 2nd attempt itself.
I want to ignore that specific test case that got passed and proceed to execute the new test case.
I have copied the output of the command in $OutPut variable which looks like below
Not Imp Info
Errors, Failures and Warnings
Failed : UnitTestProject1.TestClass.AddTest()
Expected: 30
But was: 45
Not Imp Info
Test Run Summary
Overall result: Failed
Not Imp Info
Below if the script which I have written.
$XMLfile = 'Results.xml'
[XML]$Results = Get-Content $XMLfile
$HowManyTimes = 3 # How many time the script should run.
$TestCaseToExecute
$OutPut # Storing the result of command
$StopOperation = 0
for ($i=1; $i -le $HowManyTimes; $i++)
{
#I am reading the xml file which can have n test cases.
#For each loop on all the test case which are failed.
foreach($Result in $Results.'test-run'.'test-suite'.'test-suite'.'test-suite'.'test-suite'.'test-case' | Where-Object {$_.result -eq "Failed"})
{
Write-Host "Failed Test Case -- fullname :" ($Result).fullname
$TestCaseToExecute = ($Result).fullname
Write-Host " Executing ====>" $TestCaseToExecute
Write-Host "-----------------------------------------"
$OutPut = nunit3-console.exe UnitTestProject1\bin\Debug\UnitTestProject1.dll --test=$TestCaseToExecute | Out-String
if($OutPut -Like "*Overall result: Failed*"){
Write-Host "If Block"
continue
}else{
Write-Host "else block Stop : "
break
}
}
}
Please let me know what changes I need to make to achieve this.

PowerShell: Loop through IIS 8+ server's custom log fields to check for existence of required fields

I'm trying to loop through a list of servers (IIS 8.5 mostly) and for each server see if certain custom field names exist for logs. I'm executing these commands from my local machine, using the "invoke-command" cmdlet. Currently, it just returns to the cursor instead of writing out if there is a Match or No Match.
srv.csv file contains:
- Host,IP
- srv1,10.0.0.2
- srv2,10.0.0.3
log_stig.csv file contains:
- LogFieldName,SourceType
- Connection,RequestHeader
- User-Agent,RequestHeader
- Content-Type,ResponseHeader
Assigns the values in srv.csv to $IISServers variable
try{
$IISServers = Import-Csv C:\Users\snappy\ps_script_resources\srv.csv
}
catch{
"The file 'srv.csv' is not available"
}
Assigns the values in log_stig.csv to $STIG_CustomFields variable
try{
$STIG_CustomFields = Import-Csv C:\Users\snappy\ps_script_resources\log_stig.csv
}catch{
"The file 'log_stig.csv' is not available"
}
Loop through each server and then loop through each customfield name checking if they exist on the server.
ForEach($IISServer in $IISServers){
$IISServerName = $($IISServer.Host)
try{
invoke-command -ComputerName $IISServerName -ScriptBlock{
$SiteLogFileCustom = Get-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' -
filter "system.applicationHost/sites/site/logFile/customFields" -Name 'Collection'
ForEach ($STIG_CustomField in $STIG_CustomFields){
write-output $STIG_CustomField.LogFieldName
write-output $SiteLogFileCustom.logFieldName
if($STIG_CustomField.LogFieldName -match $SiteLogFileCustom.logFieldName){
write-output "Match"
}
else{
write-output "No Match"
}
write-output "This ran"
}
}
}
catch{
"Invoke failed for "
}
}
The current code when I run from my local machine only returns to the cursor, I would expect that for every match, I would see either a Match or No Match message in my console. Any recommendations welcomed

Catching Cascading Errors in PowerShell

I'm working with a wrapper script that calls a function which queries some databases inside specified servers and inserts metadata to a specific database on a separate server. I use the $error.count variable to determine if the script was successful or not. I anticipate some permission/extraction errors to happen and want these to be caught and ignored (no increase in the $error.count variable but having a warning written in the job log). I can confirm one permission error is happening and is being properly caught. The $error.count variable is not increased but a warning is printed from the catch showing the database that could not be accessed.
My problem occurs after the extraction/insertion function is finished running. Immediately after this function returns to the wrapper script, I have the $error.count variable print again. This time, it returns a 1 as if the error previously caught cascades into the wrapper script. As I mentioned previously, I do not want this to be included in the error count. I'm not sure how or why the $error.count is increased from this function.
Should I use a different variable to determine if the script "failed" or not? Is there some underlying reason why the $error.count would increase outside of the function that has the error while not increasing after the error is caught? Any guidance on this issue would be appreciated.
Code for reference:
Wrapper function:
$errorCount = $error.count
Write-Warning ("$errorCount Before function")
Extraction/Insertion_Function -serverList $serverList -insertionDB $insertionDB -ErrorAction SilentlyContinue
$errorCount = $error.count
Write-Warning ("$errorCount After function")
} catch {
Write-Error "Error caught by wrapper: $_"
}
Extraction/Insertion_Function:
ForEach ($db in $dbList) {
Write-Warning "$errorCount database
.
.
.
try {
$totalProperties = Get-ServerDBMetadata -DBConnectionString ($connStr) -DatabaseName $dbName -EA SilentlyContinue
} catch {
Write-Warning "Unable to extract metadata from $dbname in $server"
}
}
I then have the error count printing out inside the loop that extracts/inserts the metadata from each database to the insertion database, as well as in the loop for each server that contains the databases:
WARNING: 0 Before function
WARNING: 0 database
.
.
.
WARNING: 0 database
WARNING: Unable to extract metadata from *database* in *server*
WARNING: 0 database
.
.
.
WARNING: 0 database
**WARNING: 1 After function**
The error (permission issue) is caught inside the function but cascades to my wrapper script. I want this particular error to be ignored while NOT ignoring other, more serious errors (like being unable to connect to the server I'm inserting the metadata into) so placing -EA Ignore on the driver function inside the wrapper script is out of the question.
Replace SilentlyContinue with Ignore to ignore the error and not have it increase the count.
Extraction/Insertion_Function -serverList $serverList -insertionDB $insertionDB -ErrorAction Ignore
To catch it inside the function, use -ErrorAction Stop as in thepip3r's answer as try/catch statements only catch terminating errors.
Your primary problem with the try-catch not catching the error (even though you don't supply all of the code) is that your cmdlet explicitly calls -ErrorAction SilentlyContinue. Try/Catch blocks REQUIRE the use of terminating errors so in the case of your function/cmdlet, you need to change to -ErrorAction Stop for try/catch to appropriately handle an error from that function/cmdlet.
This needs to be updated for any other function/cmdlet in the code we can't see.
Edit described in comments below:
$n = New-Object PSObject -property #{
'Test1' = ''
'Test2' = ''
'Test3' = ''
}
try {
get-process someprocess -ErrorAction Stop
$n.Test1 = $true
} catch {
$n.Test1 = $false
}
try {
Get-WmiObject win32_computersystem -ErrorAction Stop
$n.Test2 = $true
} catch {
$n.Test2 = $false
}
try {
Get-Content somefile.ext -ErrorAction Stop
$n.Test3 = $true
} catch {
$n.Test3 = $false
}
if ($n.Test1 -and $n.Test2 -and $n.Test3) {
## All procedures completed successfully -- do something magical
} else {
## At least one test procedure failed.
}
Logging the remaining errors since the last log:
While ($Global:ErrorCount -lt $Error.Count) {
$Err = $Error[$Error.Count - ++$Global:ErrorCount]
$ErrLine = "Error at $($Err.InvocationInfo.ScriptLineNumber),$($Err.InvocationInfo.OffsetInLine): $Err"
Write-Host $ErrLine -ForegroundColor Red # Log this
}

Powershell job unexpectedly returning 'System.Management.Automation.PSObject' object instead of System.Object

I run a maintenance Powershell script which remotely checks Windows server event logs for various entries and then takes appropriate corrective/alerting actions.
The script runs every 5 minutes, but will occasionally run too long due to Get-WinEvent calls timing out with an RPC unavailable error while attempting to query unreachable/unresponsive servers.
To avoid this issue, I am working on wrapping the Get-WinEvent calls in Jobs so that I can apply a configurable timeout to them.
For Get-WinEvent jobs finding multiple events, Receive-Job properly returns a 'System.Object[]' array containing 'System.Diagnostics.Eventing.Reader.EventLogRecord' objects. If only a single event is found, Receive-Job returns a 'System.Management.Automation.PSObject' object instead.
Without the Job-related code, a Get-WinEvent call finding one event returns a non-array 'System.Diagnostics.Eventing.Reader.EventLogRecord' object which can easily be wrapped with an array for downstream consumption.
Anyone have a better way to add a timeout to a remote Get-WinEvent call or an explanation/fix for the 'System.Management.Automation.PSObject' being returned instead of a non-array 'System.Diagnostics.Eventing.Reader.EventLogRecord' object?
The function and some sample calls are shown below:
Function CollectRemoteEvents($the_server,$event_log,$events_to_find,$event_label,$search_start,$search_timeout,$max_event_count){
Try{
$job_info = Start-Job -name GetEvents -scriptblock {param($server,$logname,$eventID,$StartTime,$MaxEvents) Get-WinEvent -ComputerName $server -FilterHashtable #{"logname"=$logname;"id"=$eventID;StartTime=$StartTime} -MaxEvents $MaxEvents} -Arg $the_server,$event_log,$events_to_find,$search_start,$max_event_count
#if the provided timeout value is greater than 0, use it
if($search_timeout -gt 0){
#if the job takes a while, tell it to timeout after ## seconds
$wait_result = Wait-Job -id $job_info.id -timeout $search_timeout
}Else{
#if the timeout was specified as 0, let the job run to completion
$wait_result = Wait-Job -id $job_info.id
}
$current_job_state = Get-Job -id ($job_info.id)
#check if the job has completed before time runs out
if($current_job_state.State -eq "Completed"){
#capture the job object
$job = Get-Job -id ($job_info.id)
#retrieve the output of the job; if the job raises errors, exceptions will be populated into the $joberror variable
#NOTE: the $ is *intentionally* left out of the 'joberror' variable name in the command below
$job_result = $job | Receive-Job -ErrorVariable joberror -ErrorAction Stop
If($joberror -ne "" -And $joberror -ne $null){
#if joberror is not empty, the job failed; log it
# write-host "JobError: '$joberror'" #used for debugging, this would log to file in a production capacity
}Else{
# write-host $job_result.gettype() #used for debugging
return ,$job_result
}
}else{
#the search timed out
# write-host "The event log search timed out." #used for debugging, this would log to file in a production capacity
return $null
}
}Catch [Exception]{
If($_.FullyQualifiedErrorID -eq "NoMatchingEventsFound,Microsoft.PowerShell.Commands.GetWinEventCommand"){
#No logon timeout events were registered since $search_start
write-host "$the_server : No $event_label events were found."
return #()
}Elseif($_.FullyQualifiedErrorID -eq "ParameterArgumentValidationError,Microsoft.PowerShell.Commands.GetWinEventCommand"){
#"argument validation error", exit the function with a return value indicating failure
write-host "$the_server : Event log retrieval failed, can't check for $event_label events (Argument validation error);"
return $null
}Elseif($_.FullyQualifiedErrorID -eq "System.Diagnostics.Eventing.Reader.EventLogException,Microsoft.PowerShell.Commands.GetWinEventCommand"){
#"the RPC server is unavailable", exit the function with a return value indicating failure
write-host "$the_server : Event log retrieval failed, can't check for $event_label events (RPC server unavailable);"
return $null
}Else{
#if the server logs cannot be retrieved, exit the function with a return value indicating failure
write-host "$the_server : Event log retrieval failed, can't check for $event_label events (Check access/permissions)($($_.FullyQualifiedErrorID));"
return $null
}
}
}
$server_name = "localhost"
$system_event_ID = #(6013)
$app_event_ID = #(1033)
$timeout_check_timespan = (Get-Date).AddMonths(-2)
$WinEvent_timeout = 10 #how long to let the Job run before timing out
$returns_array = CollectRemoteEvents $server_name 'System' $system_event_ID "Label One" $timeout_check_timespan $WinEvent_timeout 5
$returns_non_array = CollectRemoteEvents $server_name 'Application' $app_event_ID "Label Two" $timeout_check_timespan $WinEvent_timeout 1
write-host ""
write-host $returns_array
write-host $returns_array.count
write-host ""
write-host $returns_non_array
write-host $returns_non_array.count
The comma on the main return line is attempt to force an array to be returned (see: Count property of array in PowerShell with pscustomobjects )
I have also tried instantiating an array and then adding the result set to it:
$var = #()
$var += $results
return $var
casting the result set as an array:
return [Array]($results)
and returning the result set as part of an array:
return #($results)
I believe that this is a different issue than the one covered in the 'Function return value in Powershell' proposed solution - in my issue the problem of the object types is present before the function returns.
Uncommenting the following line for debugging purposes
# write-host $job_result.gettype() #used for debugging
Results in the following output being printed:
System.Object[]
System.Management.Automation.PSObject
The System.Object[] line is returned by a Job running a Get-WinEvent query that finds multiple events.
The 'System.Management.Automation.PSObject' line is returned by a Job running a Get-WinEvent query that finds a single event
After lots of googling based upon a suggestion from a Reddit user, it appears that you effectively have to double-wrap the single-object return content to have it end up as an array:
#this *does not* work
return #(#($job_result))
#This works
return , #($job_result)

Error Message from Powershell Script mail to EmailAddress

I have constructed an script for our Dataloader in Salesforce:
**# The Function for the Check Error File**
function CheckErrorFile {
Param ([string]$perrorfilepath)
$csvlines = (Get-Content $perrorfilepath | Measure-Object -line).Lines
# header is always 1 row so we look for > 1 rows
if ($csvlines -gt 1)
{
$errorCount = $csvLines - 1
$errorMessage = [string]::Format("** errors ** {0} failed rows in {1}", $errorCount, $perrorfilepath)
# this will print red error message but not stop execution
write-error $errorMessage
}
}
**# set up locations/variables for data loader**
$dataloader = "C:\Program Files (x86)\salesforce.com\Data Loader\bin\process.bat"
$dlconfig = "U:\Projekte\Salesforce\Dataloader-Test-Infor-Files\Example-Migration-SF-Test\DataLoaderConfig"
set-location "C:\Program Files (x86)\salesforce.com\Data Loader\bin"
$dlLogFolder = "U:\Projekte\Salesforce\Dataloader-Test-Infor-Files\Example-Migration-SF-Test\Logs\"
**# execute the data loader**
& $dataloader $dlconfig testsf-delete
**# check for errors in the error.csv file**
CheckErrorFile ("{0}error_testsf.csv" -f $dlLogFolder)
So far so good it think
But what i want to is that the Error Code which comes Back from CheckErrorFile Command should sent to an Email Address.
So i had think about 2 Considerations:
when if error sent to mail
pack the error output in a variable and sent to mail adress
But i'm not so good at Powershell that i know how i have to integrate this in my script.
I found a site with an send trigger when a eventlog has changed but I'm not sure if i can use this for my purposes and how i have to integrate this:
Powershell Email when Eventlog is Changed
May someone could help
thnks