microsoft / PSRule

Validate infrastructure as code (IaC) and objects using PowerShell rules.
https://microsoft.github.io/PSRule/v2/
MIT License
385 stars 49 forks source link

Invoke-PsRule throw a System.InvalidCastException on non-boolean rule result #187

Closed LaurentDardenne closed 5 years ago

LaurentDardenne commented 5 years ago

Invoke-PsRule throw a System.InvalidCastException when a Rule send 2 objects into the pipeline.

To reproduce the error :

$Path='C:\temp'
$File='Test.Rule.ps1'
@'
Rule Test {
  'test' #or a return value of a method call
  $true
}
#same error with this code
# Rule Test2 {
#   $true
#   'test'

#No error with this code
# Rule Test2 {
#   $true
#   $true
#}
'@ >"$Path\$File"

'data'|Invoke-PSRule -Path "$Path\$File"
#Invoke-RuleBlock : Le cast spécifié n'est pas valide.
# Au caractère C:\....\Modules\psrule\0.7.0\PSRule.psm1:171 : 17
# +                 $pipeline.Process($InputObject);
# +                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#     + CategoryInfo          : NotSpecified: (:) [Invoke-RuleBlock], InvalidCastException
#     + FullyQualifiedErrorId : System.InvalidCastException,PSRule.Commands.InvokeRuleBlockCommand

The details of the exception :

$e=$error[0]
$e|select *

# PSMessageDetails      :
# Exception             : System.InvalidCastException: Le cast spécifié n'est pas valide.
#                            à PSRule.Rules.RuleConditionResult.Create(IEnumerable`1 value)
#                            à PSRule.Commands.InvokeRuleBlockCommand.ProcessRecord()
#                            à System.Management.Automation.CommandProcessor.ProcessRecord()
# TargetObject          :
# CategoryInfo          : NotSpecified: (:) [Invoke-RuleBlock], InvalidCastException
# FullyQualifiedErrorId : System.InvalidCastException,PSRule.Commands.InvokeRuleBlockCommand
# ErrorDetails          :
# InvocationInfo        : System.Management.Automation.InvocationInfo
# ScriptStackTrace      :
# PipelineIterationInfo : {}

$e.Exception|select *

# Message        : Le cast spécifié n'est pas valide.
# Data           : {}
# InnerException :
# TargetSite     : PSRule.Rules.RuleConditionResult Create(System.Collections.Generic.IEnumerable`1[System.Object])
# StackTrace     :    à PSRule.Rules.RuleConditionResult.Create(IEnumerable`1 value)
#                     à PSRule.Commands.InvokeRuleBlockCommand.ProcessRecord()
#                     à System.Management.Automation.CommandProcessor.ProcessRecord()
# HelpLink       :
# Source         : PSRule
# HResult        : -2147467262

Expected behaviour The documentation for the word Rule does not specify whether a rule should issue a single return value of type boolean ($true or $false) or multiple values of type boolean. In this case it is the last issued value that takes precedence.

BernieWhite commented 5 years ago

@LaurentDardenne :) you are testing my documentation, thanks keep it up.

Rule conditions can only return $True or $False, so the string test is not valid. Good catch.

When more then condition is defined if any return $False then the rule failed. If all return $True then the rule passed.

So, the following would always fail, regardless of the $True being the last condition.

Rule 'Test' {
$False
$True
}

As you have pointed out, the rule keyword documentation isn't clear. Thanks. I'll look to correct that.

The walkthrough is hopefully a little clearer.

LaurentDardenne commented 5 years ago

The walkthrough is hopefully a little clearer.

Rome wasn't built in a day :-)

Thanks.

LaurentDardenne commented 5 years ago

Thanks for this fix. I have just one remark.

With the version 0.7.0-B190633 :

$r='data'|Invoke-PSRule -Path "$Path\$File" -Outcome all
# Invoke-PSRule : An invalid rule result was returned for Test.Rule.ps1/Test. Conditions must return boolean $True or $False.
#...
#     + FullyQualifiedErrorId : PSRule.Runtime.InvalidRuleResult,Invoke-PSRule

$r|select *
# Outcome        : Fail
# OutcomeReason  : Processed
#...
# RuleId         : Test.Rule.ps1/Test
# RuleName       : Test

In this case I have a Powershell error, but the OutCome content is 'fail' What difference do you make here between 'Fail' and 'Error' ? Or under what conditions do I have the OutCome content at 'Error'?

BernieWhite commented 5 years ago

@LaurentDardenne Good call out. Agreed it should return the error status instead of fail. I think it is fair to say that if an error in rule or in engine occurs during an rule execution then the Error outcome should be returned.