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

Expose rule dependency information from Get-PSRule #210

Closed LaurentDardenne closed 5 years ago

LaurentDardenne commented 5 years ago

I try to build, in a script, a rule dependent on a second rule which is located in a module:

$Path='C:\temp'
$File='Main.Rule.ps1'
@'
Write-warning "call Main"
Rule Test -dependsOn 'M1.Rule2' {
    Write-warning $Rule.RuleName
    $true
}
'@ > "$Path\$File"
#import-module 'C:\Prive\PSRule\TestModule\TestModule.psd1'
$env:PSModulePath +=";C:\Prive\PSRule"
#'object'|Invoke-PSRule -path "$Path\$File" -Module 'C:\Prive\PSRule\TestModule\TestModule.psd1'
'object'|Invoke-PSRule -path "$Path\$File" -Module TestModule -verbose
# COMMENTAIRES : [Invoke-PSRule] BEGIN::
# COMMENTAIRES : [New-PSRuleOption] BEGIN::
# COMMENTAIRES : Attempting to read: C:\Prive\PSRule\rules
# COMMENTAIRES : [New-PSRuleOption] END::
# COMMENTAIRES : [PSRule][D] -- Scanning for source files: C:\temp\Main.Rule.ps1
# COMMENTAIRES : [PSRule][D] -- Found 1 PSRule module(s)
# COMMENTAIRES : [PSRule][D] -- Scanning for source files in module: TestModule
# COMMENTAIRES : [PSRule][D] -- Discovering rules in: C:\temp\Main.Rule.ps1
# AVERTISSEMENT : call Main
# COMMENTAIRES : [PSRule][D] -- Found Test in C:\temp\Main.Rule.ps1
# COMMENTAIRES : [PSRule][D] -- Discovering rules in: C:\Prive\PSRule\TestModule\rules\Test.Rule.ps1
# COMMENTAIRES : [PSRule][D] -- Found M1.Rule1 in C:\Prive\PSRule\TestModule\rules\Test.Rule.ps1
# COMMENTAIRES : [PSRule][D] -- Found M1.Rule2 in C:\Prive\PSRule\TestModule\rules\Test.Rule.ps1

# The dependency 'Main.Rule.ps1/M1.Rule2' for 'Main.Rule.ps1/Test' could not be found.
# Check that the rule is defined in a .Rule.ps1 file within the search path.

# Au caractère C:\Prive\Modules\psrule\0.7.0\PSRule.psm1:168 : 13
# +             throw $_.Exception.GetBaseException();
# +             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#     + CategoryInfo          : OperationStopped: (:) [], RuleRuntimeException
#     + FullyQualifiedErrorId : The dependency 'Main.Rule.ps1/M1.Rule2' for 'Main.Rule.ps1/Test' could not be found. 
#        Check that the rule is defined in a .Rule.ps1 file within the search path.

According to the exception message, should a rule dependency be in the same module / script? If yes, the next line :

Invoke-PSRule -path "$Path\$File" -Module TestModule

does not create a 'single set' of rules, but merely follows the execution of rules defined in several places, is that it?

BernieWhite commented 5 years ago

@LaurentDardenne Modules are designed to be discrete. So something like this should be able to be done with a fully qualified rule Id. In your DependsOn use <modules>/<file>/<ruleName> so in this case -DependsOn 'TestModule/Test.Rule.ps1/M1.Rule1' should work, but untested.

That said, I have been thinking about:

  1. making cross referencing rules more intuitive.
  2. merging rule sets together like you are trying to do. So this is related to #170

In point 2 the idea was to allow a new rule set to incorporate rules from an existing rule set, as a new combined rules set.

LaurentDardenne commented 5 years ago

The following syntax works (bravo !):

Rule Test -dependsOn 'TestModule/Test.Rule.ps1/M1.Rule2' 

but not this one

Rule Test -dependsOn 'TestModule\Test.Rule.ps1\M1.Rule2' 

In addition, the module must be loaded BEFORE the Invoke-PSRule call, the -Module parameter is only a reference on a loaded module. This variable does not affect behavior.

$PSModuleAutoLoadingPreference='All'
BernieWhite commented 5 years ago

@LaurentDardenne Yes that is correct.

I have been thinking on these two points for a little while.

Packaging rules in module was not originally possible, so / made more sense then. Now that packaging rules is possible it makes sense to rework how RuleId is used to make it consistent with the fully qualified referencing that exists in PowerShell for cmdlets.

i.e. ModuleName\RuleName so TestModule\M1.Rule2.

This would be a breaking change that would drop supports for duplicate rule names in the same path.

Also agreed, -Module should auto-load modules based on the preference variable and currently doesn't.

Let me add these features for tracking separately.

BernieWhite commented 5 years ago

@LaurentDardenne Also curious on your intended usage of these features. If you can provide any more context, that would help.

LaurentDardenne commented 5 years ago

Also curious on your intended usage of these features. If you can provide any more context, that would help.

I'm looking at how PSRule works. I worked on a technical validation automaton of package standards before publishing them via SCCM. The use would have been to add or remove rules by configuration or type of package, some rules may be common, others specific. In short, have a parametric rule engine.

I do not know if reuse rules would use -Tag would be enough, if it is better to create many modules with few rules or insert dependencies. For now I'm thinking about how to use PSRule :-)

LaurentDardenne commented 5 years ago

I have another question about rule dependencies, The definition can carry this information, but the class PSRule.Rules.Rule does not offer them. How to find them to create a graph of dependencies ?

BernieWhite commented 5 years ago

@LaurentDardenne

I do not know if reuse rules would use -Tag would be enough, if it is better to create many modules with few rules or insert dependencies.

You could consider using the baseline configuration options in descrete config files config1.yaml, config2.yaml ...

Then Invoke-PSRule -Option .\config1.yaml

In the future I'd like to expand the capabilities to allow named baselines to be defined and shipped in modules. Currently that is not possible, but separate PSRule options configuration files can be used.


How to find them to create a graph of dependencies?

Currently the dependency graph is built internally but not exposed. It should be fairly easy to expose this through to the PSRule.Rules.Rule class if it is important.

LaurentDardenne commented 5 years ago

You could consider using the baseline configuration options in descrete config files

I have not yet taken the time to look at this feature, thank you.

It should be fairly easy to expose this through to the PSRule.Rules.Rule class if it is important.

It would be useful I think, but may not be used by everyone.

A simple Get-Graph cmdlet can suffice to create the data needed for this type of rendering or DGML

BernieWhite commented 5 years ago
BernieWhite commented 5 years ago

@LaurentDardenne I'll release this soon.

Building a dependency graph with mermaid.

function Get-Graph {
    "graph TD;"
    Get-PSRule .\tests\PSRule.Tests\TestModule2\ -Name M2.Rule2 -IncludeDependencies | ForEach-Object {
        foreach ($dep in $_.DependsOn) {
            [String]::Concat($_.RuleId, "-->", $dep, ";")
        }
    }
}

Link to diagram

LaurentDardenne commented 5 years ago

Thank you.