зеркало из https://github.com/microsoft/PSRule.git
Pre-release module update 02 (#4)
* Read from file instead of using parser * Handle multi-condition rules and inconclusive results * Add additional contextual help
This commit is contained in:
Родитель
49be453c14
Коммит
656e31aaa2
|
@ -0,0 +1,105 @@
|
|||
# PSRule_Options
|
||||
|
||||
## about_PSRule_Options
|
||||
|
||||
## SHORT DESCRIPTION
|
||||
|
||||
Describes additional options that can be used during rule execution.
|
||||
|
||||
## LONG DESCRIPTION
|
||||
|
||||
PSRule lets you use options when calling `Invoke-PSRule` to change how rules are executed. This topic describes what options are available, when to and how to use them.
|
||||
|
||||
Options can be used by:
|
||||
|
||||
- Using the `-Option` parameter of `Invoke-PSRule` with an object created with `New-PSRuleOption`
|
||||
- Using the `-Option` parameter of `Invoke-PSRule` with a hash table
|
||||
- Using the `-Option` parameter of `Invoke-PSRule` with a YAML file
|
||||
- Configuring the default options file `psrule.yml`
|
||||
|
||||
As mentioned above, a options object can be created with `New-PSRuleOption` see cmdlet help for syntax and examples.
|
||||
|
||||
When using a hash table, `@{}`, one or more options can be specified with the `-Option` parameter using a dotted notation.
|
||||
|
||||
For example:
|
||||
|
||||
```powershell
|
||||
$option = @{ 'execution.languageMode' = 'ConstrainedLanguage' };
|
||||
Invoke-PSRule -Path . -Option $option;
|
||||
```
|
||||
|
||||
`execution.languageMode` is an example of an option that can be used. Please see the following sections for other options can be used.
|
||||
|
||||
Another option is to use an external file, formatted as YAML, instead of having to create an options object manually each time. This YAML file can be used with `Invoke-PSRule` to quickly execute rules in a repeatable way.
|
||||
|
||||
YAML properties are specified using lower camel case, for example:
|
||||
|
||||
```yaml
|
||||
execution:
|
||||
languageMode: ConstrainedLanguage
|
||||
```
|
||||
|
||||
By default PSRule will automatically look for a file named `psrule.yml` in the current working directory. Alternatively, you can specify a YAML file in the `-Option` parameter.
|
||||
|
||||
For example:
|
||||
|
||||
```powershell
|
||||
Invoke-PSRule -Path . -Option '.\myconfig.yml'.
|
||||
```
|
||||
|
||||
### Language mode
|
||||
|
||||
Unless PowerShell has been constrained, full language features of PowerShell are available to use within rule definitions. In locked down environments, a reduced set of language features may be desired.
|
||||
|
||||
When PSRule is executed in an environment configured for Device Guard, only constrained language features are available.
|
||||
|
||||
The following language modes are available for use in PSRule:
|
||||
|
||||
- FullLanguage
|
||||
- ConstrainedLanguage
|
||||
|
||||
This option can be specified using:
|
||||
|
||||
```powershell
|
||||
# PowerShell: Using the Execution.LanguageMode hash table key
|
||||
$option = New-PSRuleOption -Option @{ 'Execution.LanguageMode' = 'ConstrainedLanguage' }
|
||||
```
|
||||
|
||||
```yaml
|
||||
# psrule.yml: Using the execution/languageMode YAML property
|
||||
execution:
|
||||
languageMode: ConstrainedLanguage
|
||||
```
|
||||
|
||||
## EXAMPLES
|
||||
|
||||
### Example PSRule.yml
|
||||
|
||||
```yaml
|
||||
# Set execution options
|
||||
execution:
|
||||
languageMode: ConstrainedLanguage
|
||||
```
|
||||
|
||||
### Default PSRule.yml
|
||||
|
||||
```yaml
|
||||
# These are the default options.
|
||||
# Only properties that differ from the default values need to be specified.
|
||||
execution:
|
||||
languageMode: FullLanguage
|
||||
```
|
||||
|
||||
## NOTE
|
||||
|
||||
An online version of this document is available at https://github.com/BernieWhite/PSRule/blob/master/docs/concepts/PSRule/en-US/about_PSRule_Options.md.
|
||||
|
||||
## SEE ALSO
|
||||
|
||||
- [Invoke-PSRule](https://github.com/BernieWhite/PSRule/blob/master/docs/commands/PSRule/en-US/Invoke-PSRule.md)
|
||||
- [New-PSRuleOption](https://github.com/BernieWhite/PSRule/blob/master/docs/commands/PSRule/en-US/New-PSRuleOption.md)
|
||||
|
||||
## KEYWORDS
|
||||
|
||||
- Options
|
||||
- PSRule
|
|
@ -0,0 +1,58 @@
|
|||
# PSRule_Variables
|
||||
|
||||
## about_PSRule_Variables
|
||||
|
||||
## SHORT DESCRIPTION
|
||||
|
||||
Describes the automatic variables that can be used within PSRule rule definitions.
|
||||
|
||||
## LONG DESCRIPTION
|
||||
|
||||
PSRule lets you define rules using PowerShell blocks. A rule is defined within script files by using the `rule` keyword.
|
||||
|
||||
Within a rule definition, PSRule exposes a number of automatic variables that can be read to assist with rule execution. Overwriting these variables or variable properties is not supported.
|
||||
|
||||
The following variables are available for use:
|
||||
|
||||
- [$Rule](#rule)
|
||||
- [$TargetObject](#targetobject)
|
||||
|
||||
### Rule
|
||||
|
||||
An object representing the current object model of the rule during execution.
|
||||
|
||||
The following section properties are available for public read access:
|
||||
|
||||
- `RuleName` - The name of the rule.
|
||||
- `TargetObject` - The object currently being processed on the pipeline.
|
||||
|
||||
Syntax:
|
||||
|
||||
```powershell
|
||||
$Rule
|
||||
```
|
||||
|
||||
### TargetObject
|
||||
|
||||
The value of the pipeline object currently being processed. `$TargetObject` is set by using the `-InputObject` parameter of `Invoke-PSRule`.
|
||||
|
||||
When more than one input object is set, each object will be processed sequentially.
|
||||
|
||||
Syntax:
|
||||
|
||||
```powershell
|
||||
$TargetObject
|
||||
```
|
||||
|
||||
## NOTE
|
||||
|
||||
An online version of this document is available at https://github.com/BernieWhite/PSRule/blob/master/docs/concepts/PSRule/en-US/about_PSRule_Variables.md.
|
||||
|
||||
## SEE ALSO
|
||||
|
||||
- [Invoke-PSRule](https://github.com/BernieWhite/PSRule/blob/master/docs/commands/PSRule/en-US/Invoke-PSRule.md)
|
||||
|
||||
## KEYWORDS
|
||||
|
||||
- Rule
|
||||
- TargetObject
|
|
@ -2,8 +2,11 @@
|
|||
|
||||
namespace PSRule.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// The AllOf keyword.
|
||||
/// </summary>
|
||||
[Cmdlet(VerbsLifecycle.Assert, RuleLanguageNouns.AllOf)]
|
||||
internal sealed class AssertAllOfCommand : InternalLanguageCommand
|
||||
internal sealed class AssertAllOfCommand : RuleKeyword
|
||||
{
|
||||
[Parameter(Mandatory = true, Position = 0)]
|
||||
public ScriptBlock Body { get; set; }
|
||||
|
|
|
@ -2,8 +2,11 @@
|
|||
|
||||
namespace PSRule.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// The AnyOf keyword.
|
||||
/// </summary>
|
||||
[Cmdlet(VerbsLifecycle.Assert, RuleLanguageNouns.AnyOf)]
|
||||
internal sealed class AssertAnyOfCommand : InternalLanguageCommand
|
||||
internal sealed class AssertAnyOfCommand : RuleKeyword
|
||||
{
|
||||
[Parameter(Mandatory = true, Position = 0)]
|
||||
public ScriptBlock Body { get; set; }
|
||||
|
|
|
@ -2,8 +2,11 @@
|
|||
|
||||
namespace PSRule.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// The Exists keyword.
|
||||
/// </summary>
|
||||
[Cmdlet(VerbsLifecycle.Assert, RuleLanguageNouns.Exists)]
|
||||
internal sealed class AssertExistsCommand : InternalLanguageCommand
|
||||
internal sealed class AssertExistsCommand : RuleKeyword
|
||||
{
|
||||
[Parameter(Mandatory = true, Position = 0)]
|
||||
public string[] Field { get; set; }
|
||||
|
|
|
@ -3,8 +3,11 @@ using System.Text.RegularExpressions;
|
|||
|
||||
namespace PSRule.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// The Match keyword.
|
||||
/// </summary>
|
||||
[Cmdlet(VerbsLifecycle.Assert, RuleLanguageNouns.Match)]
|
||||
internal sealed class AssertMatchCommand : InternalLanguageCommand
|
||||
internal sealed class AssertMatchCommand : RuleKeyword
|
||||
{
|
||||
private Regex[] _Expressions;
|
||||
|
||||
|
|
|
@ -3,8 +3,11 @@ using System.Management.Automation;
|
|||
|
||||
namespace PSRule.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// The TypeOf keyword.
|
||||
/// </summary>
|
||||
[Cmdlet(VerbsLifecycle.Assert, RuleLanguageNouns.TypeOf)]
|
||||
internal sealed class AssertTypeOfCommand : InternalLanguageCommand
|
||||
internal sealed class AssertTypeOfCommand : RuleKeyword
|
||||
{
|
||||
[Parameter(Mandatory = true, Position = 0)]
|
||||
public string[] TypeName { get; set; }
|
||||
|
|
|
@ -3,8 +3,11 @@ using System.Management.Automation;
|
|||
|
||||
namespace PSRule.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// The Within keyword.
|
||||
/// </summary>
|
||||
[Cmdlet(VerbsLifecycle.Assert, RuleLanguageNouns.Within)]
|
||||
internal sealed class AssertWithinCommand : InternalLanguageCommand
|
||||
internal sealed class AssertWithinCommand : RuleKeyword
|
||||
{
|
||||
[Parameter(Mandatory = true, Position = 0)]
|
||||
public string Field { get; set; }
|
||||
|
|
|
@ -6,11 +6,14 @@ using System.Management.Automation;
|
|||
|
||||
namespace PSRule.Commands
|
||||
{
|
||||
public abstract class LanguageBlockCommand : PSCmdlet
|
||||
/// <summary>
|
||||
/// A base class for language blocks.
|
||||
/// </summary>
|
||||
internal abstract class LanguageBlock : PSCmdlet
|
||||
{
|
||||
protected BlockMetadata GetMetadata(ScriptBlock body)
|
||||
protected BlockMetadata GetMetadata(string path, int lineNumber, int offset)
|
||||
{
|
||||
return HostHelper.GetCommentMeta(body.File, body.Ast.Parent.Parent.Extent.StartOffset);
|
||||
return HostHelper.GetCommentMeta(path, lineNumber, offset);
|
||||
}
|
||||
|
||||
protected TagSet GetTag(Hashtable hashtable)
|
|
@ -4,8 +4,11 @@ using System.Management.Automation;
|
|||
|
||||
namespace PSRule.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// A Rule language block.
|
||||
/// </summary>
|
||||
[Cmdlet(VerbsCommon.New, RuleLanguageNouns.RuleDefinition)]
|
||||
internal sealed class NewRuleDefinitionCommand : LanguageBlockCommand
|
||||
internal sealed class NewRuleDefinitionCommand : LanguageBlock
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the deployment.
|
||||
|
@ -17,7 +20,7 @@ namespace PSRule.Commands
|
|||
/// The definition of the deployment.
|
||||
/// </summary>
|
||||
[Parameter(Mandatory = false, Position = 1)]
|
||||
public ScriptBlock Body { get; set; }
|
||||
public RuleCondition Body { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A set of tags with additional metadata for the rule.
|
||||
|
@ -39,7 +42,7 @@ namespace PSRule.Commands
|
|||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
var metadata = GetMetadata(Body);
|
||||
var metadata = GetMetadata(MyInvocation.ScriptName, MyInvocation.ScriptLineNumber, MyInvocation.OffsetInLine);
|
||||
var tag = GetTag(Tag);
|
||||
|
||||
WriteVerbose($"[PSRule][R][{Name}]::BEGIN");
|
||||
|
|
|
@ -2,11 +2,13 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Management.Automation;
|
||||
using System.Reflection;
|
||||
|
||||
namespace PSRule.Commands
|
||||
{
|
||||
public abstract class InternalLanguageCommand : PSCmdlet
|
||||
/// <summary>
|
||||
/// A base class for Rule keywords.
|
||||
/// </summary>
|
||||
internal abstract class RuleKeyword : PSCmdlet
|
||||
{
|
||||
|
||||
protected RuleResult GetResult()
|
|
@ -1,6 +1,6 @@
|
|||
namespace PSRule.Commands
|
||||
{
|
||||
public static class RuleLanguageNouns
|
||||
internal static class RuleLanguageNouns
|
||||
{
|
||||
public const string Exists = "Exists";
|
||||
public const string Match = "Match";
|
||||
|
|
|
@ -2,8 +2,11 @@
|
|||
|
||||
namespace PSRule.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// The Hint keyword.
|
||||
/// </summary>
|
||||
[Cmdlet(VerbsCommon.Set, RuleLanguageNouns.RuleHint)]
|
||||
internal sealed class SetPSRuleHintCommand : InternalLanguageCommand
|
||||
internal sealed class SetPSRuleHintCommand : RuleKeyword
|
||||
{
|
||||
[Parameter(Mandatory = false, Position = 0)]
|
||||
public string Message { get; set; }
|
||||
|
|
|
@ -4,29 +4,10 @@ using System.Management.Automation.Runspaces;
|
|||
|
||||
namespace PSRule.Host
|
||||
{
|
||||
public sealed class EnvironmentVariable : PSVariable
|
||||
{
|
||||
public EnvironmentVariable(string name)
|
||||
: base(name, null, ScopedItemOptions.ReadOnly)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override object Value
|
||||
{
|
||||
get
|
||||
{
|
||||
//return LanguageContext._Environment;
|
||||
return string.Empty;
|
||||
}
|
||||
set
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class RuleVariable : PSVariable
|
||||
/// <summary>
|
||||
/// A dynamic variable used during Rule execution.
|
||||
/// </summary>
|
||||
internal sealed class RuleVariable : PSVariable
|
||||
{
|
||||
public RuleVariable(string name)
|
||||
: base(name, null, ScopedItemOptions.ReadOnly)
|
||||
|
@ -40,14 +21,13 @@ namespace PSRule.Host
|
|||
{
|
||||
return LanguageContext._Rule;
|
||||
}
|
||||
set
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class TargetObjectVariable : PSVariable
|
||||
/// <summary>
|
||||
/// A dynamic variable used during Rule execution.
|
||||
/// </summary>
|
||||
internal sealed class TargetObjectVariable : PSVariable
|
||||
{
|
||||
public TargetObjectVariable(string name)
|
||||
: base (name, null, ScopedItemOptions.ReadOnly)
|
||||
|
@ -61,16 +41,14 @@ namespace PSRule.Host
|
|||
{
|
||||
return LanguageContext._Rule?.TargetObject;
|
||||
}
|
||||
set
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static class HostState
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Define language commands.
|
||||
/// </summary>
|
||||
internal static SessionStateCmdletEntry[] BuiltInCmdlets = new SessionStateCmdletEntry[]
|
||||
{
|
||||
new SessionStateCmdletEntry("New-RuleDefinition", typeof(NewRuleDefinitionCommand), null),
|
||||
|
@ -83,24 +61,9 @@ namespace PSRule.Host
|
|||
new SessionStateCmdletEntry("Assert-AnyOf", typeof(AssertAnyOfCommand), null),
|
||||
};
|
||||
|
||||
public static InitialSessionState CreateDefault()
|
||||
{
|
||||
var state = InitialSessionState.CreateDefault();
|
||||
|
||||
state.Commands.Add(BuiltInCmdlets);
|
||||
state.Commands.Add(BuiltInAliases);
|
||||
state.Commands.Add(GetSpec);
|
||||
|
||||
state.Variables.Add(new SessionStateVariableEntry("LanguageContext", new LanguageContext(), string.Empty, ScopedItemOptions.ReadOnly));
|
||||
|
||||
//ResourceCommandBinder.Bind(state);
|
||||
|
||||
#if !NET472
|
||||
state.ExecutionPolicy = Microsoft.PowerShell.ExecutionPolicy.RemoteSigned;
|
||||
#endif
|
||||
return state;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Define language aliases.
|
||||
/// </summary>
|
||||
internal static SessionStateAliasEntry[] BuiltInAliases
|
||||
{
|
||||
get
|
||||
|
@ -121,15 +84,23 @@ namespace PSRule.Host
|
|||
}
|
||||
}
|
||||
|
||||
internal static SessionStateFunctionEntry[] GetSpec
|
||||
/// <summary>
|
||||
/// Create a default session state.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static InitialSessionState CreateDefault()
|
||||
{
|
||||
get
|
||||
{
|
||||
return new SessionStateFunctionEntry[]
|
||||
{
|
||||
new SessionStateFunctionEntry("DOK.TestSpec", "")
|
||||
};
|
||||
}
|
||||
var state = InitialSessionState.CreateDefault();
|
||||
|
||||
// Add in language elements
|
||||
state.Commands.Add(BuiltInCmdlets);
|
||||
state.Commands.Add(BuiltInAliases);
|
||||
|
||||
#if !NET472
|
||||
// Set execution policy
|
||||
state.ExecutionPolicy = Microsoft.PowerShell.ExecutionPolicy.RemoteSigned;
|
||||
#endif
|
||||
return state;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ namespace PSRule.Host
|
|||
{
|
||||
return ToRule(GetLanguageBlock(option, context, scriptPaths), filter).Values.ToArray();
|
||||
}
|
||||
|
||||
public static IDictionary<string, RuleBlock> GetRuleBlock(PSRuleOption option, LanguageContext context, string[] scriptPaths, RuleFilter filter)
|
||||
{
|
||||
return ToRuleBlock(GetLanguageBlock(option, context, scriptPaths), filter);
|
||||
|
@ -28,36 +29,23 @@ namespace PSRule.Host
|
|||
/// <param name="path"></param>
|
||||
/// <param name="start"></param>
|
||||
/// <returns></returns>
|
||||
public static BlockMetadata GetCommentMeta(string path, int start)
|
||||
public static BlockMetadata GetCommentMeta(string path, int lineNumber, int offset)
|
||||
{
|
||||
var scriptBlockContent = File.ReadAllText(path, Encoding.UTF8);
|
||||
|
||||
var errors = new Collection<PSParseError>();
|
||||
|
||||
var tokens = PSParser.Tokenize(scriptBlockContent, out errors);
|
||||
|
||||
if (tokens.Count == 0)
|
||||
if (lineNumber == 1)
|
||||
{
|
||||
return new BlockMetadata();
|
||||
}
|
||||
|
||||
var i = 0;
|
||||
var lines = File.ReadAllLines(path, Encoding.UTF8);
|
||||
|
||||
var i = lineNumber - 1;
|
||||
var comments = new List<string>();
|
||||
|
||||
for (var t = tokens[0]; i < tokens.Count && t.Start < start; i++)
|
||||
for (; i >= 0; i--)
|
||||
{
|
||||
t = tokens[i];
|
||||
|
||||
if (t.Start == start)
|
||||
if (lines[i].Contains("#"))
|
||||
{
|
||||
// If tokens was a new line back track so that all comments are found
|
||||
if (i > 1 && tokens[i - 1].Type == PSTokenType.NewLine)
|
||||
{
|
||||
for (var j = i - 2; j >= 0 && tokens[j].Type == PSTokenType.Comment; j--)
|
||||
{
|
||||
comments.Insert(0, tokens[j].Content);
|
||||
}
|
||||
}
|
||||
comments.Insert(0, lines[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -98,7 +86,6 @@ namespace PSRule.Host
|
|||
runspace.ThreadOptions = PSThreadOptions.UseCurrentThread;
|
||||
|
||||
runspace.Open();
|
||||
runspace.SessionStateProxy.PSVariable.Set(new EnvironmentVariable("Environment"));
|
||||
runspace.SessionStateProxy.PSVariable.Set(new RuleVariable("Rule"));
|
||||
runspace.SessionStateProxy.PSVariable.Set(new TargetObjectVariable("TargetObject"));
|
||||
|
||||
|
@ -161,24 +148,17 @@ namespace PSRule.Host
|
|||
|
||||
result.Status = RuleResultOutcome.InProgress;
|
||||
|
||||
var scriptBlock = block.Body;
|
||||
var invokeResults = scriptBlock.Invoke();
|
||||
var invokeResults = block.Body.Invoke();
|
||||
|
||||
foreach (var ir in invokeResults)
|
||||
{
|
||||
if (ir.BaseObject is bool)
|
||||
{
|
||||
var success = (bool)ir.BaseObject;
|
||||
|
||||
result.Success = success;
|
||||
result.Status = success ? RuleResultOutcome.Passed : RuleResultOutcome.Failed;
|
||||
}
|
||||
}
|
||||
|
||||
if (result.Status == RuleResultOutcome.InProgress)
|
||||
if (invokeResults == null)
|
||||
{
|
||||
result.Status = RuleResultOutcome.Inconclusive;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Success = invokeResults.Success;
|
||||
result.Status = result.Success ? RuleResultOutcome.Passed : RuleResultOutcome.Failed;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -1,50 +1,11 @@
|
|||
using PSRule.Rules;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Management.Automation;
|
||||
|
||||
namespace PSRule.Host
|
||||
{
|
||||
public sealed class LanguageContext
|
||||
internal sealed class LanguageContext
|
||||
{
|
||||
[ThreadStatic]
|
||||
internal static RuleResult _Rule;
|
||||
|
||||
public LanguageContext()
|
||||
{
|
||||
Functions = new Dictionary<string, ScriptBlock>();
|
||||
Variables = new Dictionary<string, PSVariable>();
|
||||
}
|
||||
|
||||
public LanguageContext(IDictionary<string, ScriptBlock> functions, IDictionary<string, PSVariable> variables)
|
||||
{
|
||||
Functions = new Dictionary<string, ScriptBlock>(functions, System.StringComparer.OrdinalIgnoreCase);
|
||||
Variables = new Dictionary<string, PSVariable>(variables, System.StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public Dictionary<string, ScriptBlock> Functions { get; private set; }
|
||||
|
||||
public Dictionary<string, PSVariable> Variables { get; private set; }
|
||||
|
||||
public LanguageContext New()
|
||||
{
|
||||
var context = new LanguageContext(Functions, Variables);
|
||||
context.SetVariable("LanguageContext", context);
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
public Collection<PSObject> InvokeWithContext(ScriptBlock scriptBlock, params object[] args)
|
||||
{
|
||||
return scriptBlock.InvokeWithContext(Functions, Variables.Values.ToList(), args);
|
||||
}
|
||||
|
||||
public void SetVariable(string name, object value)
|
||||
{
|
||||
var psVar = new PSVariable(name, value, ScopedItemOptions.ReadOnly);
|
||||
Variables[name] = psVar;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace PSRule.Pipeline
|
|||
|
||||
public IEnumerable<Rule> Process()
|
||||
{
|
||||
return HostHelper.GetRule(_Option, _Context, _Path, _Filter);
|
||||
return HostHelper.GetRule(_Option, null, _Path, _Filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,6 @@ namespace PSRule.Pipeline
|
|||
{
|
||||
public interface IRulePipeline
|
||||
{
|
||||
IEnumerable<Rule> Process(LanguageContext context, string[] path, RuleFilter filter);
|
||||
IEnumerable<Rule> Process(string[] path, RuleFilter filter);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ namespace PSRule.Pipeline
|
|||
: base(option, path, filter)
|
||||
{
|
||||
_Outcome = outcome;
|
||||
_RuleBlock = HostHelper.GetRuleBlock(_Option, _Context, _Path, _Filter);
|
||||
_RuleBlock = HostHelper.GetRuleBlock(_Option, null, _Path, _Filter);
|
||||
}
|
||||
|
||||
public IEnumerable<RuleResult> Process(PSObject o)
|
||||
|
|
|
@ -9,14 +9,12 @@ namespace PSRule.Pipeline
|
|||
protected readonly PSRuleOption _Option;
|
||||
protected readonly string[] _Path;
|
||||
protected readonly RuleFilter _Filter;
|
||||
protected readonly LanguageContext _Context;
|
||||
|
||||
internal RulePipeline(PSRuleOption option, string[] path, RuleFilter filter)
|
||||
{
|
||||
_Option = option;
|
||||
_Path = path;
|
||||
_Filter = filter;
|
||||
_Context = new LanguageContext();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using Newtonsoft.Json;
|
||||
using System.Management.Automation;
|
||||
|
||||
namespace PSRule.Rules
|
||||
{
|
||||
|
@ -18,9 +17,6 @@ namespace PSRule.Rules
|
|||
[JsonProperty(PropertyName = "description")]
|
||||
public string Description { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public ScriptBlock Body { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "tag")]
|
||||
public TagSet Tag { get; set; }
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
using PSRule.Host;
|
||||
using System.Management.Automation;
|
||||
|
||||
namespace PSRule.Rules
|
||||
{
|
||||
public delegate bool RulePrecondition();
|
||||
|
||||
public delegate RuleConditionResult RuleCondition();
|
||||
|
||||
/// <summary>
|
||||
/// Define an instance of a deployment block. Each deployment block has a unique name.
|
||||
/// </summary>
|
||||
|
@ -27,9 +28,12 @@ namespace PSRule.Rules
|
|||
/// </summary>
|
||||
public string Description { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A precondition that if set, must evaluate as true before the main rule body will be processed.
|
||||
/// </summary>
|
||||
public RulePrecondition If { get; set; }
|
||||
|
||||
public ScriptBlock Body { get; set; }
|
||||
public RuleCondition Body { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Other deployments that must completed successfully before calling this deployment.
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
namespace PSRule.Rules
|
||||
{
|
||||
public sealed class RuleConditionResult
|
||||
{
|
||||
private readonly bool _value;
|
||||
|
||||
public RuleConditionResult(bool value)
|
||||
{
|
||||
_value = value;
|
||||
}
|
||||
|
||||
public RuleConditionResult(object[] value)
|
||||
{
|
||||
var totalCount = 0;
|
||||
var successCount = 0;
|
||||
|
||||
foreach (var v in value)
|
||||
{
|
||||
totalCount++;
|
||||
|
||||
if (v is bool && (bool)v)
|
||||
{
|
||||
successCount++;
|
||||
}
|
||||
}
|
||||
|
||||
_value = successCount == totalCount;
|
||||
}
|
||||
|
||||
public bool Success => _value;
|
||||
|
||||
public static explicit operator RuleConditionResult(bool value) => new RuleConditionResult(value);
|
||||
|
||||
public static explicit operator RuleConditionResult(object[] value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new RuleConditionResult(value);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,6 +4,9 @@ using System.Collections.Generic;
|
|||
|
||||
namespace PSRule.Rules
|
||||
{
|
||||
/// <summary>
|
||||
/// A filter to include or exclude rules from being processed by name or tag.
|
||||
/// </summary>
|
||||
public sealed class RuleFilter
|
||||
{
|
||||
private HashSet<string> _RequiredName;
|
||||
|
|
|
@ -6,6 +6,7 @@ Rule 'FromFile1' -Tag @{ category = "group1"; test = "Test1" } {
|
|||
|
||||
# Successful
|
||||
$True;
|
||||
$True;
|
||||
}
|
||||
|
||||
# Description: Test rule 2
|
||||
|
@ -15,6 +16,8 @@ Rule 'FromFile2' -Tag @{ category = "group1"; test = "Test2" } {
|
|||
|
||||
# Failed
|
||||
$False;
|
||||
$True;
|
||||
$True;
|
||||
}
|
||||
|
||||
# Description: Test rule 3
|
||||
|
|
|
@ -30,18 +30,24 @@ Describe 'Invoke-PSRule' {
|
|||
Value = 1
|
||||
}
|
||||
|
||||
It 'Return success' {
|
||||
It 'Return passed' {
|
||||
$result = $testObject | Invoke-PSRule -Path (Join-Path -Path $here -ChildPath 'FromFile.Rule.ps1') -Name 'FromFile1';
|
||||
$result | Should -Not -BeNullOrEmpty;
|
||||
$result.Success | Should -Be $True;
|
||||
$result.TargetName | Should -Be 'TestTarget1'
|
||||
$result.TargetName | Should -Be 'TestTarget1';
|
||||
}
|
||||
|
||||
It 'Return failure' {
|
||||
$result = $testObject | Invoke-PSRule -Path (Join-Path -Path $here -ChildPath 'FromFile.Rule.ps1') -Name 'FromFile2';
|
||||
$result | Should -Not -BeNullOrEmpty;
|
||||
$result.Success | Should -Be $False;
|
||||
$result.TargetName | Should -Be 'TestTarget2'
|
||||
$result.TargetName | Should -Be 'TestTarget2';
|
||||
}
|
||||
|
||||
It 'Returns inconclusive' {
|
||||
$result = $testObject | Invoke-PSRule -Path (Join-Path -Path $here -ChildPath 'FromFile.Rule.ps1') -Name 'FromFile3' -Status All;
|
||||
$result | Should -Not -BeNullOrEmpty;
|
||||
$result.Status | Should -Be 'Inconclusive';
|
||||
}
|
||||
|
||||
It 'Processes rules preconditions' {
|
||||
|
@ -91,13 +97,20 @@ Describe 'Get-PSRule' {
|
|||
$result = Get-PSRule -Path (Join-Path -Path $here -ChildPath 'FromFile.Rule.ps1') -Name 'FromFile1', 'FromFile3';
|
||||
$result | Should -Not -BeNullOrEmpty;
|
||||
$result.Count | Should -Be 2;
|
||||
$result.Name | Should -BeIn @('FromFile1', 'FromFile3')
|
||||
$result.Name | Should -BeIn @('FromFile1', 'FromFile3');
|
||||
}
|
||||
|
||||
It 'Filters by tag' {
|
||||
$result = Get-PSRule -Path (Join-Path -Path $here -ChildPath 'FromFile.Rule.ps1') -Tag @{ Test = "Test1" };
|
||||
$result | Should -Not -BeNullOrEmpty;
|
||||
$result.Name | Should -Be 'FromFile1'
|
||||
$result.Name | Should -Be 'FromFile1';
|
||||
}
|
||||
|
||||
It 'Reads metadata' {
|
||||
$result = Get-PSRule -Path (Join-Path -Path $here -ChildPath 'FromFile.Rule.ps1') -Name 'FromFile1';
|
||||
$result | Should -Not -BeNullOrEmpty;
|
||||
$result.Name | Should -Be 'FromFile1';
|
||||
$result.Description | Should -Be 'Test rule 1'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#
|
||||
|
||||
# Description: App Service Plan has multiple instances
|
||||
Rule 'appServicePlan.MinInstanceCount' -If { $TargetObject.ResourceType -eq 'Microsoft.Web/serverfarms' } {
|
||||
Rule 'appServicePlan.MinInstanceCount' -If { ResourceType 'Microsoft.Web/serverfarms' } {
|
||||
|
||||
Hint 'Use at least two (2) instances' -TargetName $TargetObject.ResourceName
|
||||
|
||||
|
@ -11,7 +11,7 @@ Rule 'appServicePlan.MinInstanceCount' -If { $TargetObject.ResourceType -eq 'Mic
|
|||
}
|
||||
|
||||
# Description: Use at least a Standard App Service Plan
|
||||
Rule 'appServicePlan.MinPlan' -If { $TargetObject.ResourceType -eq 'Microsoft.Web/serverfarms' } {
|
||||
Rule 'appServicePlan.MinPlan' -If { ResourceType 'Microsoft.Web/serverfarms' } {
|
||||
|
||||
Hint 'Use a Standard or high plans for production services' -TargetName $TargetObject.ResourceName
|
||||
|
||||
|
@ -21,7 +21,7 @@ Rule 'appServicePlan.MinPlan' -If { $TargetObject.ResourceType -eq 'Microsoft.We
|
|||
}
|
||||
|
||||
# Description: Disable client affinity for stateless services
|
||||
Rule 'appServiceApp.ARRAfinity' -If { $TargetObject.ResourceType -eq 'Microsoft.Web/sites' } {
|
||||
Rule 'appServiceApp.ARRAfinity' -If { ResourceType 'Microsoft.Web/sites' } {
|
||||
|
||||
Hint 'Disable ARR affinity when not required' -TargetName $TargetObject.ResourceName
|
||||
|
||||
|
@ -29,7 +29,7 @@ Rule 'appServiceApp.ARRAfinity' -If { $TargetObject.ResourceType -eq 'Microsoft.
|
|||
}
|
||||
|
||||
# Description: Use HTTPS only
|
||||
Rule 'appServiceApp.UseHTTPS' -If { $TargetObject.ResourceType -eq 'Microsoft.Web/sites' } {
|
||||
Rule 'appServiceApp.UseHTTPS' -If { ResourceType 'Microsoft.Web/sites' } {
|
||||
|
||||
Hint 'Disable HTTP when not required' -TargetName $TargetObject.ResourceName
|
||||
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
#
|
||||
# Helper functions for rules
|
||||
#
|
||||
|
||||
# Add a custom function to filter by resource type
|
||||
function global:ResourceType {
|
||||
param (
|
||||
[String]$ResourceType
|
||||
)
|
||||
|
||||
process {
|
||||
return $TargetObject.ResourceType -eq $ResourceType;
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@
|
|||
#
|
||||
|
||||
# Description: Configure storage accounts to only access encrypted traffic i.e. HTTPS/SMB
|
||||
Rule 'storageAccounts.UseHttps' -If { $TargetObject.ResourceType -eq 'Microsoft.Storage/storageAccounts' } {
|
||||
Rule 'storageAccounts.UseHttps' -If { ResourceType 'Microsoft.Storage/storageAccounts' } {
|
||||
|
||||
Hint 'Secure access should only allow secure traffic' -TargetName $TargetObject.ResourceName
|
||||
|
||||
|
@ -11,7 +11,7 @@ Rule 'storageAccounts.UseHttps' -If { $TargetObject.ResourceType -eq 'Microsoft.
|
|||
}
|
||||
|
||||
# Description: Use at-rest storage encryption
|
||||
Rule 'storageAccounts.UseEncryption' -If { $TargetObject.ResourceType -eq 'Microsoft.Storage/storageAccounts' } {
|
||||
Rule 'storageAccounts.UseEncryption' -If { ResourceType 'Microsoft.Storage/storageAccounts' } {
|
||||
|
||||
Hint 'Storage accounts should have encryption enabled' -TargetName $TargetObject.ResourceName
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче