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:
Bernie White 2018-11-30 00:10:02 +11:00 коммит произвёл GitHub
Родитель 49be453c14
Коммит 656e31aaa2
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
29 изменённых файлов: 352 добавлений и 173 удалений

Просмотреть файл

@ -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