microsoft / PSRule

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

Allow logging of per object result message #200

Closed LaurentDardenne closed 5 years ago

LaurentDardenne commented 5 years ago

There is a missing property on the RuleRecord object that would allow you to create a scan report for users. If I analyze a data file created by a user it can contain input errors, rules will therefore check the error cases. The user may not know the rules or have to interpret the result. I can of course create associations between a name of rules + a state AND an error message, but a dedicated field in the body of the rule would be simple I think.

$Path='C:\temp'
$File='Test.Rule.ps1'
@'
Rule -Name 'Test' {
  Recommend "$($TargetObject.Group) is not allowed."
  Write-Warning "$($TargetObject|Out-String)"
  ($TargetObject.Group -notin @('Group2','Group5'))
}
'@ > "$Path\$File"

Function New-ADUserData{
param(
      [Parameter(Mandatory=$True,position=0)]
    $Name,
      [Parameter(Mandatory=$True,position=1)]
    $Group
    )

  [pscustomobject]@{
    PSTypeName='ADUserData';
    Name=$Name;
    Group=$Group;
  }
}

$Result=1..5|
  Foreach-Object { 
    New-ADUserData -Name "Name$_" -Group "Group$_" 
  }|
  Invoke-PSRule -Path "$Path\$File"

$UserReport=$Result|Where-object {$_.Outcome -eq 'Fail'}
$UserReport
#    TargetName : Name2

# RuleName                            Outcome    Recommendation
# --------                            -------    --------------
# Test                                Fail       Group1 is not allowed .

#    TargetName : Name5

# RuleName                            Outcome    Recommendation
# --------                            -------    --------------
# Test                                Fail       Group1 is not allowed .

$UserReport=$UserReport|Select-Object -expandproperty Recommendation

$ofs="`r`n"
Write-Error "The datas file contains one or more error :`r`n $userReport"
# The datas file contains one or more error :
#  Group1 is not allowed.
# Group1 is not allowed.

Note that the misuse of the 'Recommendation' property always refers to the first object received in the pipeline.

Describe alternatives you've considered

Rule -Name 'Test' {
    Recommend "Use a different group name than the one specified."
    Report  "$($TargetObject.Group) is not allowed."
    ($TargetObject.Group -notin @('Group2','Group5'))
}
BernieWhite commented 5 years ago

@LaurentDardenne Just confirming that I've understood you correctly. You want to create an output message that is dynamic such as based on target object.

LaurentDardenne commented 5 years ago

Exact. String substitution from $TargetObject may be sufficient. And if possible allow message localization.

LaurentDardenne commented 5 years ago

After reflection, I miss in the result emitted by PSRule a property of type Object / PSObject and not only a string. The ToString() method of such an object could create a summary of the cause of the error from its various properties that would be populated in the body of the rule. Please tell me if I divert the use of PSRule (rule engine) which should only issue $true / $false in result.

I tried to use Pester as a rule engine but I had the same problem, the result of pester only concerns the success or failure of a test. What is his goal ;-)

BernieWhite commented 5 years ago

@LaurentDardenne PSRule will error if a non-boolean result is returned.

I have a plan for this, to be able to return condition messages.

But you could use Write-Information (which is supported) but it's not really the same.

BernieWhite commented 5 years ago

@LaurentDardenne Working through the implementation for this now.

Planning on:

LaurentDardenne commented 5 years ago

Thank you very much for these additions. I'm testing it soon.

LaurentDardenne commented 5 years ago

Maybe add in the documentation (about_PSRule_Keywords.md) that in the body of a rule, all occurrences of the Reason property are concatenated. For example :

@'
Rule -Name 'Test' {
  Recommend "Use a valid group."
  Reason "The group list contains errors."
  Write-Warning "$($TargetObject|Out-String)"
  Within Group -not 'Group2','Group5' -reason "The group '$($TargetObject.Group)' is not allowed."
  Match 'Group' '^Group(1|2|3)$' -reason "Naming convention error."
}
'@ > "$Path\$File"

$result|? {$_.outcome -eq 'fail'}|select TargetObject,Reason

TargetObject                Reason
------------                ------
@{Name=Name2; Group=Group2} {The group list contains errors., The group 'Group2' is not allowed.}
@{Name=Name4; Group=Group4} {The group list contains errors., Naming convention error.}
@{Name=Name5; Group=Group5} {The group list contains errors., The group 'Group5' is not allowed., Naming convention error.}

This makes it possible to check several rules on the same object and to obtain a single report. Bravo !

BernieWhite commented 5 years ago

@LaurentDardenne Yes I encountered this a little when I added the -OutputFormat Wide view. I added a GetReasonViewString() method to create a concatenated string. But your question makes me think there may be a better way.