Configure options via environment variables #691 (#703)

This commit is contained in:
Bernie White 2021-04-30 23:45:11 -07:00 коммит произвёл GitHub
Родитель 93333d85ba
Коммит 2a9ef35711
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
22 изменённых файлов: 1329 добавлений и 185 удалений

21
.vscode/tasks.json поставляемый
Просмотреть файл

@ -18,6 +18,20 @@
"panel": "dedicated"
}
},
{
"label": "Run Pester test group",
"detail": "Runs a specific group for Pester tests.",
"type": "shell",
"command": "Invoke-Build Test -AssertStyle Client -TestGroup '${input:pesterTestGroup}'",
"group": "test",
"problemMatcher": [
"$pester"
],
"presentation": {
"clear": true,
"panel": "dedicated"
}
},
{
"label": "Analyze repository",
"detail": "Run repository analysis.",
@ -95,5 +109,12 @@
"panel": "dedicated"
}
}
],
"inputs": [
{
"id": "pesterTestGroup",
"type": "promptString",
"description": "A group to use for Pester tests."
}
]
}

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

@ -10,6 +10,12 @@ See [upgrade notes][upgrade-notes] for helpful information when upgrading from p
## Unreleased
What's changed since pre-release v1.3.0-B2104042:
- Engine features:
- Options can be configured with environment variables. [#691](https://github.com/microsoft/PSRule/issues/691)
- See [about_PSRule_Options] for details.
## v1.3.0-B2104042 (pre-release)
What's changed since pre-release v1.3.0-B2104030:

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

@ -147,6 +147,8 @@ spec: { }
### Example Baseline.Rule.yaml
```yaml
# Example Baseline.Rule.yaml
---
# Synopsis: This is an example baseline
apiVersion: github.com/microsoft/PSRule/v1
@ -183,3 +185,10 @@ spec:
configuration:
key1: value1
```
## KEYWORDS
- Options
- PSRule
- Baseline
- Binding

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

@ -118,7 +118,30 @@ If more than one of these files exist, the following order will be used to find
- `psrule.yml`
We recommend only using lowercase characters as shown above.
This is because not all operation systems treat case in the same way.
This is because not all operating systems treat case in the same way.
Most options can be set using environment variables.
When configuring environment variables we recommend that all capital letters are used.
This is because environment variables are case-sensitive on some operating systems.
PSRule environment variables use a consistent naming pattern of `PSRULE_<PARENT>_<NAME>`.
Where `<PARENT>` is the parent class and `<NAME>` is the specific option.
For example:
- `Execution.InconclusiveWarning` is configured by `PSRULE_EXECUTION_INCONCLUSIVEWARNING`.
- `Input.TargetType` is configured by `PSRULE_INPUT_TARGETTYPE`.
- `Output.Format` is configured by `PSRULE_OUTPUT_FORMAT`.
When setting environment variables:
- Enum values are set by string.
For example `PSRULE_OUTPUT_FORMAT` could be set to `Yaml`.
Enum values are case-insensitive.
- Boolean values are set by `true`, `false`, `1`, or `0`.
For example `PSRULE_EXECUTION_INCONCLUSIVEWARNING` could be set to `false`.
Boolean values are case-insensitive.
- String array values can specify multiple items by using a semi-colon separator.
For example `PSRULE_INPUT_TARGETTYPE` could be set to `virtualMachine;virtualNetwork`.
### Binding.Field
@ -197,6 +220,24 @@ binding:
ignoreCase: false
```
```bash
# Bash: Using environment variable
export PSRULE_BINDING_IGNORECASE=false
```
```yaml
# GitHub Actions: Using environment variable
env:
PSRULE_BINDING_IGNORECASE: false
```
```yaml
# Azure Pipelines: Using environment variable
variables:
- name: PSRULE_BINDING_IGNORECASE
value: false
```
### Binding.NameSeparator
When an object is passed from the pipeline, PSRule assigns the object a _TargetName_.
@ -234,6 +275,24 @@ binding:
nameSeparator: '::'
```
```bash
# Bash: Using environment variable
export PSRULE_BINDING_NAMESEPARATOR='::'
```
```yaml
# GitHub Actions: Using environment variable
env:
PSRULE_BINDING_NAMESEPARATOR: '::'
```
```yaml
# Azure Pipelines: Using environment variable
variables:
- name: PSRULE_BINDING_NAMESEPARATOR
value: '::'
```
### Binding.PreferTargetInfo
Some built-in objects within PSRule perform automatic binding of TargetName and TargetType.
@ -267,6 +326,24 @@ binding:
preferTargetInfo: true
```
```bash
# Bash: Using environment variable
export PSRULE_BINDING_PREFERTARGETINFO=false
```
```yaml
# GitHub Actions: Using environment variable
env:
PSRULE_BINDING_PREFERTARGETINFO: false
```
```yaml
# Azure Pipelines: Using environment variable
variables:
- name: PSRULE_BINDING_PREFERTARGETINFO
value: false
```
### Binding.TargetName
When an object is passed from the pipeline, PSRule assigns the object a _TargetName_.
@ -315,6 +392,24 @@ binding:
- AlternateName
```
```bash
# Bash: Using environment variable
export PSRULE_BINDING_TARGETNAME='ResourceName;AlternateName'
```
```yaml
# GitHub Actions: Using environment variable
env:
PSRULE_BINDING_TARGETNAME: 'ResourceName;AlternateName'
```
```yaml
# Azure Pipelines: Using environment variable
variables:
- name: PSRULE_BINDING_TARGETNAME
value: 'ResourceName;AlternateName'
```
To specify a custom binding function use:
```powershell
@ -376,6 +471,24 @@ binding:
- kind
```
```bash
# Bash: Using environment variable
export PSRULE_BINDING_TARGETTYPE='ResourceType;kind'
```
```yaml
# GitHub Actions: Using environment variable
env:
PSRULE_BINDING_TARGETTYPE: 'ResourceType;kind'
```
```yaml
# Azure Pipelines: Using environment variable
variables:
- name: PSRULE_BINDING_TARGETTYPE
value: 'ResourceType;kind'
```
To specify a custom binding function use:
```powershell
@ -433,9 +546,28 @@ binding:
useQualifiedName: true
```
```bash
# Bash: Using environment variable
export PSRULE_BINDING_USEQUALIFIEDNAME=false
```
```yaml
# GitHub Actions: Using environment variable
env:
PSRULE_BINDING_USEQUALIFIEDNAME: false
```
```yaml
# Azure Pipelines: Using environment variable
variables:
- name: PSRULE_BINDING_USEQUALIFIEDNAME
value: false
```
### Configuration
Configures a set of baseline configuration values that can be used in rule definitions instead of using hard coded values.
Configures a set of baseline configuration values that can be used in rule definitions.
Configuration values can be overridden at different scopes.
This option can be specified using:
@ -450,6 +582,27 @@ configuration:
LOCAL_APPSERVICEMININSTANCECOUNT: 2
```
Configuration values can be specified using environment variables.
To specify a configuration value, prefix the configuration value with `PSRULE_CONFIGURATION_`.
```bash
# Bash: Using environment variable
export PSRULE_CONFIGURATION_LOCAL_APPSERVICEMININSTANCECOUNT=2
```
```yaml
# GitHub Actions: Using environment variable
env:
PSRULE_CONFIGURATION_LOCAL_APPSERVICEMININSTANCECOUNT: '2'
```
```yaml
# Azure Pipelines: Using environment variable
variables:
- name: PSRULE_CONFIGURATION_LOCAL_APPSERVICEMININSTANCECOUNT
value: '2'
```
### Convention.Include
Specifies conventions to execute when the pipeline run.
@ -480,6 +633,24 @@ convention:
- 'Convention2'
```
```bash
# Bash: Using environment variable
export PSRULE_CONVENTION_INCLUDE='Convention1;Convention2'
```
```yaml
# GitHub Actions: Using environment variable
env:
PSRULE_CONVENTION_INCLUDE: 'Convention1;Convention2'
```
```yaml
# Azure Pipelines: Using environment variable
variables:
- name: PSRULE_CONVENTION_INCLUDE
value: 'Convention1;Convention2'
```
### Execution.LanguageMode
Unless PowerShell has been constrained, full language features of PowerShell are available to use within rule definitions.
@ -505,6 +676,24 @@ execution:
languageMode: ConstrainedLanguage
```
```bash
# Bash: Using environment variable
export PSRULE_EXECUTION_LANGUAGEMODE=ConstrainedLanguage
```
```yaml
# GitHub Actions: Using environment variable
env:
PSRULE_EXECUTION_LANGUAGEMODE: ConstrainedLanguage
```
```yaml
# Azure Pipelines: Using environment variable
variables:
- name: PSRULE_EXECUTION_LANGUAGEMODE
value: ConstrainedLanguage
```
### Execution.InconclusiveWarning
When defining rules, it is possible not return a valid `$True` or `$False` result within the definition script block.
@ -543,6 +732,24 @@ execution:
inconclusiveWarning: false
```
```bash
# Bash: Using environment variable
export PSRULE_EXECUTION_INCONCLUSIVEWARNING=false
```
```yaml
# GitHub Actions: Using environment variable
env:
PSRULE_EXECUTION_INCONCLUSIVEWARNING: false
```
```yaml
# Azure Pipelines: Using environment variable
variables:
- name: PSRULE_EXECUTION_INCONCLUSIVEWARNING
value: false
```
### Execution.NotProcessedWarning
When evaluating rules, it is possible to incorrectly select a path with rules that use pre-conditions that do not accept the pipeline object.
@ -576,6 +783,24 @@ execution:
notProcessedWarning: false
```
```bash
# Bash: Using environment variable
export PSRULE_EXECUTION_NOTPROCESSEDWARNING=false
```
```yaml
# GitHub Actions: Using environment variable
env:
PSRULE_EXECUTION_NOTPROCESSEDWARNING: false
```
```yaml
# Azure Pipelines: Using environment variable
variables:
- name: PSRULE_EXECUTION_NOTPROCESSEDWARNING
value: false
```
### Input.Format
Configures the input format for when a string is passed in as a target object.
@ -645,6 +870,24 @@ input:
format: Yaml
```
```bash
# Bash: Using environment variable
export PSRULE_INPUT_FORMAT=Yaml
```
```yaml
# GitHub Actions: Using environment variable
env:
PSRULE_INPUT_FORMAT: Yaml
```
```yaml
# Azure Pipelines: Using environment variable
variables:
- name: PSRULE_INPUT_FORMAT
value: Yaml
```
### Input.ObjectPath
The object path to a property to use instead of the pipeline object.
@ -680,6 +923,24 @@ input:
objectPath: items
```
```bash
# Bash: Using environment variable
export PSRULE_INPUT_OBJECTPATH=items
```
```yaml
# GitHub Actions: Using environment variable
env:
PSRULE_INPUT_OBJECTPATH: items
```
```yaml
# Azure Pipelines: Using environment variable
variables:
- name: PSRULE_INPUT_OBJECTPATH
value: items
```
### Input.PathIgnore
Ignores input files that match the path spec when using `-InputPath`.
@ -710,6 +971,24 @@ input:
- '*.Designer.cs'
```
```bash
# Bash: Using environment variable
export PSRULE_INPUT_PATHIGNORE=*.Designer.cs
```
```yaml
# GitHub Actions: Using environment variable
env:
PSRULE_INPUT_PATHIGNORE: '*.Designer.cs'
```
```yaml
# Azure Pipelines: Using environment variable
variables:
- name: PSRULE_INPUT_PATHIGNORE
value: '*.Designer.cs'
```
### Input.TargetType
Filters input objects by TargetType.
@ -728,17 +1007,17 @@ This option can be specified using:
```powershell
# PowerShell: Using the InputTargetType parameter
$option = New-PSRuleOption -InputTargetType 'virtualMachine';
$option = New-PSRuleOption -InputTargetType 'virtualMachine', 'virtualNetwork';
```
```powershell
# PowerShell: Using the Input.TargetType hashtable key
$option = New-PSRuleOption -Option @{ 'Input.TargetType' = 'virtualMachine' };
$option = New-PSRuleOption -Option @{ 'Input.TargetType' = 'virtualMachine', 'virtualNetwork' };
```
```powershell
# PowerShell: Using the InputTargetType parameter to set YAML
Set-PSRuleOption -InputTargetType 'virtualMachine';
Set-PSRuleOption -InputTargetType 'virtualMachine', 'virtualNetwork';
```
```yaml
@ -748,6 +1027,24 @@ input:
- virtualMachine
```
```bash
# Bash: Using environment variable
export PSRULE_INPUT_TARGETTYPE=virtualMachine;virtualNetwork
```
```yaml
# GitHub Actions: Using environment variable
env:
PSRULE_INPUT_TARGETTYPE: virtualMachine;virtualNetwork
```
```yaml
# Azure Pipelines: Using environment variable
variables:
- name: PSRULE_INPUT_TARGETTYPE
value: virtualMachine;virtualNetwork
```
### Logging.LimitDebug
Limits debug messages to a list of named debug scopes.
@ -942,6 +1239,24 @@ output:
as: Summary
```
```bash
# Bash: Using environment variable
export PSRULE_OUTPUT_AS=Summary
```
```yaml
# GitHub Actions: Using environment variable
env:
PSRULE_OUTPUT_AS: Summary
```
```yaml
# Azure Pipelines: Using environment variable
variables:
- name: PSRULE_OUTPUT_AS
value: Summary
```
### Output.Culture
Specified the name of one or more cultures to use for generating output.
@ -976,6 +1291,24 @@ output:
culture: [ 'en-AU', 'en-US' ]
```
```bash
# Bash: Using environment variable
export PSRULE_OUTPUT_CULTURE=en-AU;en-US
```
```yaml
# GitHub Actions: Using environment variable
env:
PSRULE_OUTPUT_CULTURE: en-AU;en-US
```
```yaml
# Azure Pipelines: Using environment variable
variables:
- name: PSRULE_OUTPUT_CULTURE
value: en-AU;en-US
```
### Output.Encoding
Configures the encoding used when output is written to file.
@ -1013,6 +1346,24 @@ output:
encoding: UTF8
```
```bash
# Bash: Using environment variable
export PSRULE_OUTPUT_ENCODING=UTF8
```
```yaml
# GitHub Actions: Using environment variable
env:
PSRULE_OUTPUT_ENCODING: UTF8
```
```yaml
# Azure Pipelines: Using environment variable
variables:
- name: PSRULE_OUTPUT_ENCODING
value: UTF8
```
### Output.Format
Configures the format that results will be presented in.
@ -1059,6 +1410,24 @@ output:
format: Yaml
```
```bash
# Bash: Using environment variable
export PSRULE_OUTPUT_FORMAT=Yaml
```
```yaml
# GitHub Actions: Using environment variable
env:
PSRULE_OUTPUT_FORMAT: Yaml
```
```yaml
# Azure Pipelines: Using environment variable
variables:
- name: PSRULE_OUTPUT_FORMAT
value: Yaml
```
### Output.Outcome
Filters output to include results with the specified outcome.
@ -1095,6 +1464,24 @@ output:
outcome: 'Fail'
```
```bash
# Bash: Using environment variable
export PSRULE_OUTPUT_OUTCOME=Fail
```
```yaml
# GitHub Actions: Using environment variable
env:
PSRULE_OUTPUT_OUTCOME: Fail
```
```yaml
# Azure Pipelines: Using environment variable
variables:
- name: PSRULE_OUTPUT_OUTCOME
value: Fail
```
### Output.Path
Specifies the output file path to write results.
@ -1127,6 +1514,24 @@ output:
path: 'out/results.yaml'
```
```bash
# Bash: Using environment variable
export PSRULE_OUTPUT_PATH=out/results.yaml
```
```yaml
# GitHub Actions: Using environment variable
env:
PSRULE_OUTPUT_PATH: out/results.yaml
```
```yaml
# Azure Pipelines: Using environment variable
variables:
- name: PSRULE_OUTPUT_PATH
value: out/results.yaml
```
### Output.Style
Configures the style that results will be presented in.
@ -1165,6 +1570,24 @@ output:
style: AzurePipelines
```
```bash
# Bash: Using environment variable
export PSRULE_OUTPUT_STYLE=AzurePipelines
```
```yaml
# GitHub Actions: Using environment variable
env:
PSRULE_OUTPUT_STYLE: AzurePipelines
```
```yaml
# Azure Pipelines: Using environment variable
variables:
- name: PSRULE_OUTPUT_STYLE
value: AzurePipelines
```
### Requires
Specifies module version constraints for running PSRule.
@ -1191,6 +1614,32 @@ requires:
PSRule.Rules.Azure: '>=1.0.0' # Require v1.0.0 or greater.
```
This option can be configured using environment variables.
To specify a module version constraint, prefix the module name with `PSRULE_REQUIRES_`.
When the module name includes a dot (`.`) use an underscore (`_`) instead.
```bash
# Bash: Using environment variable
export PSRULE_REQUIRES_PSRULE='>=1.0.0'
export PSRULE_REQUIRES_PSRULE_RULES_AZURE='>=1.0.0'
```
```yaml
# GitHub Actions: Using environment variable
env:
PSRULE_REQUIRES_PSRULE: '>=1.0.0'
PSRULE_REQUIRES_PSRULE_RULES_AZURE: '>=1.0.0'
```
```yaml
# Azure Pipelines: Using environment variable
variables:
- name: PSRULE_REQUIRES_PSRULE
value: '>=1.0.0'
- name: PSRULE_REQUIRES_PSRULE_RULES_AZURE
value: '>=1.0.0'
```
### Rule.Include
The name of specific rules to evaluate.
@ -1209,8 +1658,26 @@ $option = New-PSRuleOption -Option @{ 'Rule.Include' = 'Rule1','Rule2' };
# YAML: Using the rule/include property
rule:
include:
- rule1
- rule2
- Rule1
- Rule2
```
```bash
# Bash: Using environment variable
export PSRULE_RULE_INCLUDE='Rule1;Rule2'
```
```yaml
# GitHub Actions: Using environment variable
env:
PSRULE_RULE_INCLUDE: 'Rule1;Rule2'
```
```yaml
# Azure Pipelines: Using environment variable
variables:
- name: PSRULE_RULE_INCLUDE
value: 'Rule1;Rule2'
```
### Rule.Exclude
@ -1229,8 +1696,26 @@ $option = New-PSRuleOption -Option @{ 'Rule.Exclude' = 'Rule3','Rule4' };
# YAML: Using the rule/exclude property
rule:
exclude:
- rule3
- rule4
- Rule3
- Rule4
```
```bash
# Bash: Using environment variable
export PSRULE_RULE_EXCLUDE='Rule3;Rule4'
```
```yaml
# GitHub Actions: Using environment variable
env:
PSRULE_RULE_EXCLUDE: 'Rule3;Rule4'
```
```yaml
# Azure Pipelines: Using environment variable
variables:
- name: PSRULE_RULE_EXCLUDE
value: 'Rule3;Rule4'
```
### Rule.Tag

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

@ -117,13 +117,14 @@ Similarly, when including baselines within a module use the `.Rule.yaml` suffix.
## Defining a module configuration
A module configuration that sets options defaults can be optionally packaged with a module.
A module configuration that sets options defaults and can be optionally packaged with a module.
To set a module configuration, define a `ModuleConfig` resource within an included `.Rule.yaml` file.
A module configuration `.Rule.yaml` file must be distributed within the module directory structure.
PSRule only supports a single `ModuleConfig` resource.
The name of the `ModuleConfig` must match the name of the module.
Additional `ModuleConfig` resources or with an alternative name are ignored.
PSRule does not support module configurations distributed outside of a module.
```yaml
---
@ -154,6 +155,7 @@ The following options are allowed within a `ModuleConfig`:
- `Binding.Field`
- `Binding.IgnoreCase`
- `Binding.NameSeparator`
- `Binding.PreferTargetInfo`
- `Binding.TargetName`
- `Binding.TargetType`
- `Binding.UseQualifiedName`

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

@ -22,7 +22,10 @@ param (
[String]$ArtifactPath = (Join-Path -Path $PWD -ChildPath out/modules),
[Parameter(Mandatory = $False)]
[String]$AssertStyle = 'AzurePipelines'
[String]$AssertStyle = 'AzurePipelines',
[Parameter(Mandatory = $False)]
[String]$TestGroup = $Null
)
Write-Host -Object "[Pipeline] -- PowerShell v$($PSVersionTable.PSVersion.ToString())" -ForegroundColor Green;
@ -274,6 +277,10 @@ task TestModule Pester, PSScriptAnalyzer, {
$Null = New-Item -Path reports -ItemType Directory -Force;
}
if ($Null -ne $TestGroup) {
$pesterParams['Tags'] = $TestGroup;
}
$results = Invoke-Pester @pesterParams;
# Throw an error if pester tests failed

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

@ -2,9 +2,9 @@
// Licensed under the MIT License.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace PSRule
{
@ -32,7 +32,33 @@ namespace PSRule
public static bool TryPopBool(this IDictionary<string, object> dictionary, string key, out bool value)
{
value = default;
return dictionary.TryGetValue(key, out object v) && dictionary.Remove(key) && bool.TryParse(v.ToString(), out value);
return TryPopValue(dictionary, key, out object v) && bool.TryParse(v.ToString(), out value);
}
[DebuggerStepThrough]
public static bool TryPopEnum<TEnum>(this IDictionary<string, object> dictionary, string key, out TEnum value) where TEnum : struct
{
value = default;
return TryPopValue(dictionary, key, out object v) && Enum.TryParse(v.ToString(), ignoreCase: true, result: out value);
}
[DebuggerStepThrough]
public static bool TryPopString(this IDictionary<string, object> dictionary, string key, out string value)
{
value = default;
if (TryPopValue(dictionary, key, out object v) && v is string svalue)
{
value = svalue;
return true;
}
return false;
}
[DebuggerStepThrough]
public static bool TryPopStringArray(this IDictionary<string, object> dictionary, string key, out string[] value)
{
value = default;
return TryPopValue(dictionary, key, out object v) && TryStringArray(v, out value);
}
[DebuggerStepThrough]
@ -87,5 +113,16 @@ namespace PSRule
if (!dictionary.ContainsKey(kv.Key))
dictionary.Add(kv.Key, kv.Value);
}
[DebuggerStepThrough]
private static bool TryStringArray(object o, out string[] value)
{
value = default;
if (o == null)
return false;
value = o.GetType().IsArray ? ((object[])o).OfType<string>().ToArray() : new string[] { o.ToString() };
return true;
}
}
}

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

@ -1,27 +1,26 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Security;
namespace PSRule
{
internal static class EnvironmentHelper
internal sealed class EnvironmentHelper
{
private const char UNDERSCORE = '_';
private readonly static char[] STRINGARRAY_SEPARATOR = new char[] { ';' };
internal static bool TryString(string key, out string value)
public static readonly EnvironmentHelper Default = new EnvironmentHelper();
internal bool TryString(string key, out string value)
{
value = null;
var variable = System.Environment.GetEnvironmentVariable(key);
if (string.IsNullOrEmpty(variable))
return false;
value = variable;
return true;
return TryVariable(key, out value) && !string.IsNullOrEmpty(value);
}
internal static bool TrySecureString(string key, out SecureString value)
internal bool TrySecureString(string key, out SecureString value)
{
value = null;
if (!TryString(key, out string variable))
@ -31,27 +30,68 @@ namespace PSRule
return true;
}
internal static bool TryInt(string key, out int value)
internal bool TryInt(string key, out int value)
{
var variable = System.Environment.GetEnvironmentVariable(key);
if (!int.TryParse(variable, out value))
return false;
return true;
value = default;
return TryVariable(key, out string variable) && int.TryParse(variable, out value);
}
internal static bool TryBool(string key, out bool value)
internal bool TryBool(string key, out bool value)
{
var variable = System.Environment.GetEnvironmentVariable(key);
if (!bool.TryParse(variable, out value))
return false;
return true;
value = default;
return TryVariable(key, out string variable) && TryParseBool(variable, out value);
}
private static string CombineKey(string prefix, string key)
internal bool TryEnum<TEnum>(string key, out TEnum value) where TEnum : struct
{
return string.IsNullOrEmpty(prefix) ? key : string.Concat(prefix, UNDERSCORE, key);
value = default;
if (!TryVariable(key, out string variable))
return false;
return Enum.TryParse(variable, ignoreCase: true, out value);
}
internal bool TryStringArray(string key, out string[] value)
{
value = default;
if (!TryVariable(key, out string variable))
return false;
value = variable.Split(STRINGARRAY_SEPARATOR, options: StringSplitOptions.RemoveEmptyEntries);
return value != null;
}
private bool TryVariable(string key, out string variable)
{
variable = Environment.GetEnvironmentVariable(key);
return variable != null;
}
private static bool TryParseBool(string variable, out bool value)
{
if (bool.TryParse(variable, out value))
return true;
if (int.TryParse(variable, out int ivalue))
{
value = ivalue > 0;
return true;
}
return false;
}
internal IEnumerable<KeyValuePair<string, object>> WithPrefix(string prefix)
{
var env = Environment.GetEnvironmentVariables();
var enumerator = env.GetEnumerator();
while (enumerator.MoveNext())
{
var key = enumerator.Key.ToString();
if (key.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
{
yield return new KeyValuePair<string, object>(key, enumerator.Value);
}
}
}
}
}

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

@ -10,15 +10,15 @@ namespace PSRule
public static string GetHeadRef(string path)
{
// Try PSRule
if (EnvironmentHelper.TryString("PSRULE_GITREF", out string value))
if (EnvironmentHelper.Default.TryString("PSRULE_GITREF", out string value))
return value;
// Try Azure Pipelines
if (EnvironmentHelper.TryString("BUILD_SOURCEBRANCH", out value))
if (EnvironmentHelper.Default.TryString("BUILD_SOURCEBRANCH", out value))
return value;
// Try GitHub Actions
if (EnvironmentHelper.TryString("GITHUB_REF", out value))
if (EnvironmentHelper.Default.TryString("GITHUB_REF", out value))
return value;
// Try .git/HEAD

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

@ -108,6 +108,9 @@ namespace PSRule
return GetEnumerator();
}
/// <summary>
/// Load options from a hashtable.
/// </summary>
protected void Load(Hashtable hashtable)
{
if (hashtable == null)
@ -117,6 +120,29 @@ namespace PSRule
_Map.Add(entry.Key.ToString(), (TValue)entry.Value);
}
/// <summary>
/// Load options from environment variables.
/// </summary>
internal void Load(string prefix, EnvironmentHelper env, Func<string, string> format = null)
{
if (env == null)
throw new ArgumentNullException(nameof(env));
foreach (var variable in env.WithPrefix(prefix))
{
if (TryKeyPrefix(variable.Key, prefix, out string suffix))
{
if (format != null)
suffix = format(suffix);
_Map[suffix] = (TValue)variable.Value;
}
}
}
/// <summary>
/// Load options from a dictionary.
/// </summary>
protected void Load(string prefix, IDictionary<string, object> dictionary)
{
if (dictionary == null)
@ -133,6 +159,9 @@ namespace PSRule
}
}
/// <summary>
/// Try a key prefix.
/// </summary>
private static bool TryKeyPrefix(string key, string prefix, out string suffix)
{
suffix = key;

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

@ -65,6 +65,40 @@ namespace PSRule.Configuration
return new BaselineRef(value);
}
internal static void Load(IBaselineSpec option, EnvironmentHelper env)
{
// Binding.Field - currently not supported
if (env.TryBool("PSRULE_BINDING_IGNORECASE", out bool ignoreCase))
option.Binding.IgnoreCase = ignoreCase;
if (env.TryString("PSRULE_BINDING_NAMESEPARATOR", out string nameSeparator))
option.Binding.NameSeparator = nameSeparator;
if (env.TryBool("PSRULE_BINDING_PREFERTARGETINFO", out bool preferTargetInfo))
option.Binding.PreferTargetInfo = preferTargetInfo;
if (env.TryStringArray("PSRULE_BINDING_TARGETNAME", out string[] targetName))
option.Binding.TargetName = targetName;
if (env.TryStringArray("PSRULE_BINDING_TARGETTYPE", out string[] targetType))
option.Binding.TargetType = targetType;
if (env.TryBool("PSRULE_BINDING_USEQUALIFIEDNAME", out bool useQualifiedName))
option.Binding.UseQualifiedName = useQualifiedName;
if (env.TryStringArray("PSRULE_RULE_INCLUDE", out string[] include))
option.Rule.Include = include;
if (env.TryStringArray("PSRULE_RULE_EXCLUDE", out string[] exclude))
option.Rule.Exclude = exclude;
// Rule.Tag - currently not supported
// Process configuration values
option.Configuration.Load(env);
}
/// <summary>
/// Load matching values
/// </summary>
@ -75,46 +109,29 @@ namespace PSRule.Configuration
if (properties.TryPopValue("Binding.Field", out Hashtable map))
option.Binding.Field = new FieldMap(map);
if (properties.TryPopBool("Binding.IgnoreCase", out bool bvalue))
option.Binding.IgnoreCase = bvalue;
if (properties.TryPopBool("Binding.IgnoreCase", out bool ignoreCase))
option.Binding.IgnoreCase = ignoreCase;
if (properties.TryPopBool("Binding.PreferTargetInfo", out bvalue))
option.Binding.PreferTargetInfo = bvalue;
if (properties.TryPopString("Binding.NameSeparator", out string nameSeparator))
option.Binding.NameSeparator = nameSeparator;
if (properties.TryPopValue("Binding.NameSeparator", out object value))
option.Binding.NameSeparator = value.ToString();
if (properties.TryPopBool("Binding.PreferTargetInfo", out bool preferTargetInfo))
option.Binding.PreferTargetInfo = preferTargetInfo;
if (properties.TryPopValue("Binding.TargetName", out value))
{
if (value.GetType().IsArray)
option.Binding.TargetName = ((object[])value).OfType<string>().ToArray();
else
option.Binding.TargetName = new string[] { value.ToString() };
}
if (properties.TryPopValue("Binding.targettype", out value))
{
if (value.GetType().IsArray)
option.Binding.TargetType = ((object[])value).OfType<string>().ToArray();
else
option.Binding.TargetType = new string[] { value.ToString() };
}
if (properties.TryPopValue("Binding.UseQualifiedName", out bvalue))
option.Binding.UseQualifiedName = bvalue;
if (properties.TryPopStringArray("Binding.TargetName", out string[] targetName))
option.Binding.TargetName = targetName;
if (properties.TryPopStringArray("Binding.TargetType", out string[] targetType))
option.Binding.TargetType = targetType;
if (properties.TryPopValue("Binding.UseQualifiedName", out bool useQualifiedName))
option.Binding.UseQualifiedName = useQualifiedName;
if (properties.TryPopStringArray("Rule.Include", out string[] include))
option.Rule.Include = include;
if (properties.TryPopStringArray("Rule.Exclude", out string[] exclude))
option.Rule.Exclude = exclude;
if (properties.TryPopValue("Rule.Include", out value))
{
if (value.GetType().IsArray)
option.Rule.Include = ((object[])value).OfType<string>().ToArray();
else
option.Rule.Include = new string[] { value.ToString() };
}
if (properties.TryPopValue("Rule.Exclude", out value))
{
if (value.GetType().IsArray)
option.Rule.Exclude = ((object[])value).OfType<string>().ToArray();
else
option.Rule.Exclude = new string[] { value.ToString() };
}
if (properties.TryPopValue("Rule.Tag", out Hashtable tag))
option.Rule.Tag = tag;

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

@ -11,7 +11,8 @@ namespace PSRule.Configuration
/// </summary>
public sealed class ConfigurationOption : KeyMapDictionary<object>
{
private const string KEYMAP_PREFIX = "Configuration.";
private const string ENVIRONMENT_PREFIX = "PSRULE_CONFIGURATION_";
private const string DICTIONARY_PREFIX = "Configuration.";
public ConfigurationOption()
: base() { }
@ -34,9 +35,14 @@ namespace PSRule.Configuration
return result;
}
internal void Load(EnvironmentHelper env)
{
base.Load(ENVIRONMENT_PREFIX, env);
}
internal void Load(IDictionary<string, object> dictionary)
{
base.Load(KEYMAP_PREFIX, dictionary);
base.Load(DICTIONARY_PREFIX, dictionary);
}
}
}

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

@ -2,6 +2,7 @@
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.ComponentModel;
namespace PSRule.Configuration
@ -61,5 +62,17 @@ namespace PSRule.Configuration
[DefaultValue(null)]
public string[] Include { get; set; }
internal void Load(EnvironmentHelper env)
{
if (env.TryStringArray("PSRULE_CONVENTION_INCLUDE", out string[] include))
Include = include;
}
internal void Load(Dictionary<string, object> index)
{
if (index.TryPopStringArray("Convention.Include", out string[] include))
Include = include;
}
}
}

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

@ -2,6 +2,7 @@
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.ComponentModel;
namespace PSRule.Configuration
@ -80,5 +81,29 @@ namespace PSRule.Configuration
[DefaultValue(null)]
public bool? NotProcessedWarning { get; set; }
internal void Load(EnvironmentHelper env)
{
if (env.TryEnum("PSRULE_EXECUTION_LANGUAGEMODE", out LanguageMode languageMode))
LanguageMode = languageMode;
if (env.TryBool("PSRULE_EXECUTION_INCONCLUSIVEWARNING", out bool bvalue))
InconclusiveWarning = bvalue;
if (env.TryBool("PSRULE_EXECUTION_NOTPROCESSEDWARNING", out bvalue))
NotProcessedWarning = bvalue;
}
internal void Load(Dictionary<string, object> index)
{
if (index.TryPopEnum("Execution.LanguageMode", out LanguageMode languageMode))
LanguageMode = languageMode;
if (index.TryPopBool("Execution.InconclusiveWarning", out bool bvalue))
InconclusiveWarning = bvalue;
if (index.TryPopBool("Execution.NotProcessedWarning", out bvalue))
NotProcessedWarning = bvalue;
}
}
}

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

@ -2,6 +2,7 @@
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.ComponentModel;
namespace PSRule.Configuration
@ -105,5 +106,35 @@ namespace PSRule.Configuration
/// </summary>
[DefaultValue(null)]
public string[] TargetType { get; set; }
internal void Load(EnvironmentHelper env)
{
if (env.TryEnum("PSRULE_INPUT_FORMAT", out InputFormat format))
Format = format;
if (env.TryString("PSRULE_INPUT_OBJECTPATH", out string objectPath))
ObjectPath = objectPath;
if (env.TryStringArray("PSRULE_INPUT_PATHIGNORE", out string[] pathIgnore))
PathIgnore = pathIgnore;
if (env.TryStringArray("PSRULE_INPUT_TARGETTYPE", out string[] targetType))
TargetType = targetType;
}
internal void Load(Dictionary<string, object> index)
{
if (index.TryPopEnum("Input.Format", out InputFormat format))
Format = format;
if (index.TryPopString("Input.ObjectPath", out string objectPath))
ObjectPath = objectPath;
if (index.TryPopStringArray("Input.PathIgnore", out string[] pathIgnore))
PathIgnore = pathIgnore;
if (index.TryPopStringArray("Input.TargetType", out string[] targetType))
TargetType = targetType;
}
}
}

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

@ -2,6 +2,7 @@
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.ComponentModel;
namespace PSRule.Configuration
@ -103,5 +104,35 @@ namespace PSRule.Configuration
/// </summary>
[DefaultValue(null)]
public OutcomeLogStream? RulePass { get; set; }
internal void Load(EnvironmentHelper env)
{
if (env.TryStringArray("PSRULE_LOGGING_LIMITDEBUG", out string[] limitDebug))
LimitDebug = limitDebug;
if (env.TryStringArray("PSRULE_LOGGING_LIMITVERBOSE", out string[] limitVerbose))
LimitVerbose = limitVerbose;
if (env.TryEnum("PSRULE_LOGGING_RULEFAIL", out OutcomeLogStream ruleFail))
RuleFail = ruleFail;
if (env.TryEnum("PSRULE_LOGGING_RULEPASS", out OutcomeLogStream rulePass))
RulePass = rulePass;
}
internal void Load(Dictionary<string, object> index)
{
if (index.TryPopStringArray("Logging.LimitDebug", out string[] limitDebug))
LimitDebug = limitDebug;
if (index.TryPopStringArray("Logging.LimitVerbose", out string[] limitVerbose))
LimitVerbose = limitVerbose;
if (index.TryPopEnum("Logging.RuleFail", out OutcomeLogStream ruleFail))
RuleFail = ruleFail;
if (index.TryPopEnum("Logging.RulePass", out OutcomeLogStream rulePass))
RulePass = rulePass;
}
}
}

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

@ -3,6 +3,7 @@
using PSRule.Rules;
using System;
using System.Collections.Generic;
using System.ComponentModel;
namespace PSRule.Configuration
@ -131,5 +132,53 @@ namespace PSRule.Configuration
[DefaultValue(null)]
public OutputStyle? Style { get; set; }
internal void Load(EnvironmentHelper env)
{
if (env.TryEnum("PSRULE_OUTPUT_AS", out ResultFormat value))
As = value;
if (env.TryStringArray("PSRULE_OUTPUT_CULTURE", out string[] culture))
Culture = culture;
if (env.TryEnum("PSRULE_OUTPUT_ENCODING", out OutputEncoding encoding))
Encoding = encoding;
if (env.TryEnum("PSRULE_OUTPUT_FORMAT", out OutputFormat format))
Format = format;
if (env.TryEnum("PSRULE_OUTPUT_OUTCOME", out RuleOutcome outcome))
Outcome = outcome;
if (env.TryString("PSRULE_OUTPUT_PATH", out string path))
Path = path;
if (env.TryEnum("PSRULE_OUTPUT_STYLE", out OutputStyle style))
Style = style;
}
internal void Load(Dictionary<string, object> index)
{
if (index.TryPopEnum("Output.As", out ResultFormat value))
As = value;
if (index.TryPopStringArray("Output.Culture", out string[] culture))
Culture = culture;
if (index.TryPopEnum("Output.Encoding", out OutputEncoding encoding))
Encoding = encoding;
if (index.TryPopEnum("Output.Format", out OutputFormat format))
Format = format;
if (index.TryPopEnum("Output.Outcome", out RuleOutcome outcome))
Outcome = outcome;
if (index.TryPopString("Output.Path", out string path))
Path = path;
if (index.TryPopEnum("Output.Style", out OutputStyle style))
Style = style;
}
}
}

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

@ -4,14 +4,12 @@
using Newtonsoft.Json;
using PSRule.Definitions;
using PSRule.Resources;
using PSRule.Rules;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Management.Automation;
using System.Threading;
using YamlDotNet.Serialization;
@ -207,7 +205,7 @@ namespace PSRule.Configuration
if (!File.Exists(filePath))
throw new FileNotFoundException(PSRuleResources.OptionsNotFound, filePath);
return FromYaml(path: filePath, yaml: File.ReadAllText(filePath));
return FromEnvironment(FromYaml(path: filePath, yaml: File.ReadAllText(filePath)));
}
/// <summary>
@ -227,7 +225,7 @@ namespace PSRule.Configuration
if (!File.Exists(filePath))
return new PSRuleOption();
return FromYaml(path: filePath, yaml: File.ReadAllText(filePath));
return FromEnvironment(FromYaml(path: filePath, yaml: File.ReadAllText(filePath)));
}
/// <summary>
@ -255,11 +253,47 @@ namespace PSRule.Configuration
.WithTypeConverter(new FieldMapYamlTypeConverter())
.WithTypeConverter(new SuppressionRuleYamlTypeConverter())
.Build();
var option = d.Deserialize<PSRuleOption>(yaml) ?? new PSRuleOption();
option.SourcePath = path;
return option;
}
private static PSRuleOption FromEnvironment(PSRuleOption option)
{
if (option == null)
option = new PSRuleOption();
// Start loading matching values
var env = EnvironmentHelper.Default;
option.Convention.Load(env);
option.Execution.Load(env);
option.Input.Load(env);
option.Logging.Load(env);
option.Output.Load(env);
option.Requires.Load(env);
BaselineOption.Load(option, env);
return option;
}
public static PSRuleOption FromHashtable(Hashtable hashtable)
{
var option = new PSRuleOption();
if (hashtable == null)
return option;
// Start loading matching values
var index = BuildIndex(hashtable);
option.Convention.Load(index);
option.Execution.Load(index);
option.Input.Load(index);
option.Logging.Load(index);
option.Output.Load(index);
option.Requires.Load(index);
BaselineOption.Load(option, index);
return option;
}
/// <summary>
/// Set working path from PowerShell host environment.
/// </summary>
@ -277,11 +311,13 @@ namespace PSRule.Configuration
_GetWorkingPath = () => executionContext.SessionState.Path.CurrentFileSystemLocation.Path;
}
[DebuggerStepThrough]
public static void UseCurrentCulture()
{
UseCurrentCulture(Thread.CurrentThread.CurrentCulture);
}
[DebuggerStepThrough]
public static void UseCurrentCulture(string culture)
{
UseCurrentCulture(CultureInfo.CreateSpecificCulture(culture));
@ -308,94 +344,7 @@ namespace PSRule.Configuration
/// <param name="hashtable"></param>
public static implicit operator PSRuleOption(Hashtable hashtable)
{
var option = new PSRuleOption();
if (hashtable == null)
return option;
// Build index to allow mapping
var index = BuildIndex(hashtable);
// Start loading matching values
if (index.TryPopValue("Convention.Include", out object value))
{
option.Convention.Include = AsStringArray(value);
}
if (index.TryPopValue("execution.languagemode", out value))
{
option.Execution.LanguageMode = (LanguageMode)Enum.Parse(typeof(LanguageMode), (string)value);
}
if (index.TryPopBool("execution.inconclusivewarning", out bool bvalue))
{
option.Execution.InconclusiveWarning = bvalue;
}
if (index.TryPopBool("execution.notprocessedwarning", out bvalue))
{
option.Execution.NotProcessedWarning = bvalue;
}
if (index.TryPopValue("input.format", out value))
{
option.Input.Format = (InputFormat)Enum.Parse(typeof(InputFormat), (string)value);
}
if (index.TryPopValue("input.objectpath", out value))
{
option.Input.ObjectPath = (string)value;
}
if (index.TryPopValue("input.pathignore", out value))
{
option.Input.PathIgnore = AsStringArray(value);
}
if (index.TryPopValue("input.targettype", out value))
{
option.Input.TargetType = AsStringArray(value);
}
if (index.TryPopValue("logging.limitdebug", out value))
{
option.Logging.LimitDebug = AsStringArray(value);
}
if (index.TryPopValue("logging.limitverbose", out value))
{
option.Logging.LimitVerbose = AsStringArray(value);
}
if (index.TryPopValue("logging.rulefail", out value))
{
option.Logging.RuleFail = (OutcomeLogStream)Enum.Parse(typeof(OutcomeLogStream), (string)value);
}
if (index.TryPopValue("logging.rulepass", out value))
{
option.Logging.RulePass = (OutcomeLogStream)Enum.Parse(typeof(OutcomeLogStream), (string)value);
}
if (index.TryPopValue("output.as", out value))
{
option.Output.As = (ResultFormat)Enum.Parse(typeof(ResultFormat), (string)value);
}
if (index.TryPopValue("output.culture", out value))
{
option.Output.Culture = AsStringArray(value);
}
if (index.TryPopValue("output.encoding", out value))
{
option.Output.Encoding = (OutputEncoding)Enum.Parse(typeof(OutputEncoding), (string)value);
}
if (index.TryPopValue("output.format", out value))
{
option.Output.Format = (OutputFormat)Enum.Parse(typeof(OutputFormat), (string)value);
}
if (index.TryPopValue("output.outcome", out value))
{
option.Output.Outcome = (RuleOutcome)Enum.Parse(typeof(RuleOutcome), (string)value);
}
if (index.TryPopValue("output.path", out value))
{
option.Output.Path = (string)value;
}
if (index.TryPopValue("output.style", out value))
{
option.Output.Style = (OutputStyle)Enum.Parse(typeof(OutputStyle), (string)value);
}
option.Requires.Load(index);
BaselineOption.Load(option, index);
return option;
return FromHashtable(hashtable);
}
/// <summary>
@ -404,8 +353,7 @@ namespace PSRule.Configuration
/// <param name="path">A file or directory to read options from.</param>
public static implicit operator PSRuleOption(string path)
{
var option = FromFile(path);
return option;
return FromFile(path);
}
public override bool Equals(object obj)
@ -494,6 +442,9 @@ namespace PSRule.Configuration
return string.Concat(rootedPath, Path.DirectorySeparatorChar);
}
/// <summary>
/// Build index to allow mapping values.
/// </summary>
[DebuggerStepThrough]
internal static Dictionary<string, object> BuildIndex(Hashtable hashtable)
{
@ -539,15 +490,6 @@ namespace PSRule.Configuration
return s.Serialize(this);
}
[DebuggerStepThrough]
private static string[] AsStringArray(object value)
{
if (value == null)
return null;
return value.GetType().IsArray ? ((object[])value).OfType<string>().ToArray() : new string[] { value.ToString() };
}
[DebuggerStepThrough]
private static bool IsSeparator(char c)
{

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

@ -7,7 +7,10 @@ namespace PSRule.Configuration
{
public sealed class RequiresOption : KeyMapDictionary<string>
{
private const string KEYMAP_PREFIX = "Requires.";
private const string ENVIRONMENT_PREFIX = "PSRULE_REQUIRES_";
private const string DICTIONARY_PREFIX = "Requires.";
private const char UNDERSCORE = '_';
private const char DOT = '.';
public RequiresOption()
: base() { }
@ -15,9 +18,28 @@ namespace PSRule.Configuration
internal RequiresOption(RequiresOption option)
: base(option) { }
/// <summary>
/// Load Requires option from environment variables.
/// </summary>
internal void Load(EnvironmentHelper env)
{
base.Load(ENVIRONMENT_PREFIX, env, ConvertUnderscore);
}
/// <summary>
/// Load Requires option from a dictionary.
/// </summary>
internal void Load(IDictionary<string, object> dictionary)
{
base.Load(KEYMAP_PREFIX, dictionary);
base.Load(DICTIONARY_PREFIX, dictionary);
}
/// <summary>
/// Convert module names with underscores to dots.
/// </summary>
private string ConvertUnderscore(string key)
{
return key.Replace(UNDERSCORE, DOT);
}
}
}

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

@ -62,12 +62,21 @@ namespace PSRule.Configuration
return result;
}
/// <summary>
/// A set of rules to include for execution.
/// </summary>
[DefaultValue(null)]
public string[] Include { get; set; }
/// <summary>
/// A set of rules to exclude for execution.
/// </summary>
[DefaultValue(null)]
public string[] Exclude { get; set; }
/// <summary>
/// A set of rule tags to include for execution.
/// </summary>
[DefaultValue(null)]
public Hashtable Tag { get; set; }
}

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

@ -86,6 +86,23 @@ Describe 'New-PSRuleOption' -Tag 'Option','New-PSRuleOption' {
$option = New-PSRuleOption -Option (Join-Path -Path $here -ChildPath 'PSRule.Tests3.yml');
$option.Rule.Include | Should -BeIn 'rule1';
}
It 'from Environment' {
try {
# With single item
$Env:PSRULE_RULE_INCLUDE = 'rule1';
$option = New-PSRuleOption;
$option.Rule.Include | Should -BeIn 'rule1';
# With array
$Env:PSRULE_RULE_INCLUDE = 'rule1;rule2';
$option = New-PSRuleOption;
$option.Rule.Include | Should -BeIn 'rule1', 'rule2';
}
finally {
Remove-Item 'Env:PSRULE_RULE_INCLUDE' -Force;
}
}
}
Context 'Read Rule.Exclude' {
@ -117,6 +134,23 @@ Describe 'New-PSRuleOption' -Tag 'Option','New-PSRuleOption' {
$option = New-PSRuleOption -Option (Join-Path -Path $here -ChildPath 'PSRule.Tests3.yml');
$option.Rule.Exclude | Should -BeIn 'rule3';
}
It 'from Environment' {
try {
# With single item
$Env:PSRULE_RULE_EXCLUDE = 'rule3';
$option = New-PSRuleOption;
$option.Rule.Exclude | Should -BeIn 'rule3';
# With array
$Env:PSRULE_RULE_EXCLUDE = 'rule3;rule4';
$option = New-PSRuleOption;
$option.Rule.Exclude | Should -BeIn 'rule3', 'rule4';
}
finally {
Remove-Item 'Env:PSRULE_RULE_EXCLUDE' -Force;
}
}
}
Context 'Read Rule.Tag' {
@ -141,7 +175,7 @@ Describe 'New-PSRuleOption' -Tag 'Option','New-PSRuleOption' {
}
}
Context 'Read Baseline.Configuration' {
Context 'Read Configuration' {
It 'from default' {
$option = New-PSRuleOption -Default;
$option.Configuration.Count | Should -Be 0;
@ -160,6 +194,20 @@ Describe 'New-PSRuleOption' -Tag 'Option','New-PSRuleOption' {
$option.Configuration.option2 | Should -Be 2;
$option.Configuration.option3 | Should -BeIn 'option3a', 'option3b';
}
It 'from Environment' {
try {
$Env:PSRULE_CONFIGURATION_OPTION1 = 'value1';
$Env:PSRULE_CONFIGURATION_OPTION2_NAME = 'value2';
$option = New-PSRuleOption;
$option.Configuration.option1 | Should -Be 'value1';
$option.Configuration.option2_name | Should -Be 'value2';
}
finally {
Remove-Item 'Env:PSRULE_CONFIGURATION_OPTION1' -Force;
Remove-Item 'Env:PSRULE_CONFIGURATION_OPTION2_NAME' -Force;
}
}
}
Context 'Read Binding.Field' {
@ -205,6 +253,23 @@ Describe 'New-PSRuleOption' -Tag 'Option','New-PSRuleOption' {
$option.Binding.IgnoreCase | Should -Be $False;
}
It 'from Environment' {
try {
# With bool
$Env:PSRULE_BINDING_IGNORECASE = 'false';
$option = New-PSRuleOption;
$option.Binding.IgnoreCase | Should -Be $False;
# With int
$Env:PSRULE_BINDING_IGNORECASE = '0';
$option = New-PSRuleOption;
$option.Binding.IgnoreCase | Should -Be $False;
}
finally {
Remove-Item 'Env:PSRULE_BINDING_IGNORECASE' -Force;
}
}
It 'from parameter' {
$option = New-PSRuleOption -BindingIgnoreCase $False -Path $emptyOptionsFilePath;
$option.Binding.IgnoreCase | Should -Be $False;
@ -227,6 +292,17 @@ Describe 'New-PSRuleOption' -Tag 'Option','New-PSRuleOption' {
$option.Binding.NameSeparator | Should -Be '::';
}
It 'from Environment' {
try {
$Env:PSRULE_BINDING_NAMESEPARATOR = '::';
$option = New-PSRuleOption;
$option.Binding.NameSeparator | Should -Be '::';
}
finally {
Remove-Item 'Env:PSRULE_BINDING_NAMESEPARATOR' -Force;
}
}
It 'from parameter' {
$option = New-PSRuleOption -BindingNameSeparator 'zz' -Path $emptyOptionsFilePath;
$option.Binding.NameSeparator | Should -Be 'zz';
@ -249,6 +325,23 @@ Describe 'New-PSRuleOption' -Tag 'Option','New-PSRuleOption' {
$option.Binding.PreferTargetInfo | Should -Be $True;
}
It 'from Environment' {
try {
# With bool
$Env:PSRULE_BINDING_PREFERTARGETINFO = 'true';
$option = New-PSRuleOption;
$option.Binding.PreferTargetInfo | Should -Be $True;
# With int
$Env:PSRULE_BINDING_PREFERTARGETINFO = '1';
$option = New-PSRuleOption;
$option.Binding.PreferTargetInfo | Should -Be $True;
}
finally {
Remove-Item 'Env:PSRULE_BINDING_PREFERTARGETINFO' -Force;
}
}
It 'from parameter' {
$option = New-PSRuleOption -BindingPreferTargetInfo $True -Path $emptyOptionsFilePath;
$option.Binding.PreferTargetInfo | Should -Be $True;
@ -286,6 +379,23 @@ Describe 'New-PSRuleOption' -Tag 'Option','New-PSRuleOption' {
$option.Binding.TargetName | Should -BeIn 'ResourceName';
}
It 'from Environment' {
try {
# With single item
$Env:PSRULE_BINDING_TARGETNAME = 'ResourceName';
$option = New-PSRuleOption;
$option.Binding.TargetName | Should -BeIn 'ResourceName';
# With array
$Env:PSRULE_BINDING_TARGETNAME = 'ResourceName;AlternateName';
$option = New-PSRuleOption;
$option.Binding.TargetName | Should -BeIn 'ResourceName', 'AlternateName';
}
finally {
Remove-Item 'Env:PSRULE_BINDING_TARGETNAME' -Force;
}
}
It 'from parameter' {
$option = New-PSRuleOption -TargetName 'ResourceName', 'AlternateName' -Path $emptyOptionsFilePath;
$option.Binding.TargetName | Should -BeIn 'ResourceName', 'AlternateName';
@ -323,6 +433,23 @@ Describe 'New-PSRuleOption' -Tag 'Option','New-PSRuleOption' {
$option.Binding.TargetType | Should -BeIn 'ResourceType';
}
It 'from Environment' {
try {
# With single item
$Env:PSRULE_BINDING_TARGETTYPE = 'ResourceType';
$option = New-PSRuleOption;
$option.Binding.TargetType | Should -BeIn 'ResourceType';
# With array
$Env:PSRULE_BINDING_TARGETTYPE = 'ResourceType;Kind';
$option = New-PSRuleOption;
$option.Binding.TargetType | Should -BeIn 'ResourceType', 'Kind';
}
finally {
Remove-Item 'Env:PSRULE_BINDING_TARGETTYPE' -Force;
}
}
It 'from parameter' {
$option = New-PSRuleOption -TargetType 'ResourceType', 'Kind' -Path $emptyOptionsFilePath;
$option.Binding.TargetType | Should -BeIn 'ResourceType', 'Kind';
@ -345,6 +472,23 @@ Describe 'New-PSRuleOption' -Tag 'Option','New-PSRuleOption' {
$option.Binding.UseQualifiedName | Should -Be $True;
}
It 'from Environment' {
try {
# With bool
$Env:PSRULE_BINDING_USEQUALIFIEDNAME = 'true';
$option = New-PSRuleOption;
$option.Binding.UseQualifiedName | Should -Be $True;
# With int
$Env:PSRULE_BINDING_USEQUALIFIEDNAME = '1';
$option = New-PSRuleOption;
$option.Binding.UseQualifiedName | Should -Be $True;
}
finally {
Remove-Item 'Env:PSRULE_BINDING_USEQUALIFIEDNAME' -Force;
}
}
It 'from parameter' {
$option = New-PSRuleOption -BindingUseQualifiedName $True -Path $emptyOptionsFilePath;
$option.Binding.UseQualifiedName | Should -Be $True;
@ -382,6 +526,23 @@ Describe 'New-PSRuleOption' -Tag 'Option','New-PSRuleOption' {
$option.Convention.Include | Should -BeIn 'Convention3';
}
It 'from Environment' {
try {
# With single item
$Env:PSRULE_CONVENTION_INCLUDE = 'Convention1';
$option = New-PSRuleOption;
$option.Convention.Include | Should -Be 'Convention1';
# With array
$Env:PSRULE_CONVENTION_INCLUDE = 'Convention1;Convention2';
$option = New-PSRuleOption;
$option.Convention.Include | Should -Be 'Convention1', 'Convention2';
}
finally {
Remove-Item 'Env:PSRULE_CONVENTION_INCLUDE' -Force;
}
}
It 'from parameter' {
$option = New-PSRuleOption -Convention 'Convention1', 'Convention2' -Path $emptyOptionsFilePath;
$option.Convention.Include | Should -BeIn 'Convention1', 'Convention2';
@ -403,6 +564,17 @@ Describe 'New-PSRuleOption' -Tag 'Option','New-PSRuleOption' {
$option = New-PSRuleOption -Option (Join-Path -Path $here -ChildPath 'PSRule.Tests.yml');
$option.Execution.LanguageMode | Should -Be 'ConstrainedLanguage';
}
It 'from Environment' {
try {
$Env:PSRULE_EXECUTION_LANGUAGEMODE = 'ConstrainedLanguage';
$option = New-PSRuleOption;
$option.Execution.LanguageMode | Should -Be 'ConstrainedLanguage';
}
finally {
Remove-Item 'Env:PSRULE_EXECUTION_LANGUAGEMODE' -Force;
}
}
}
Context 'Read Execution.InconclusiveWarning' {
@ -421,6 +593,23 @@ Describe 'New-PSRuleOption' -Tag 'Option','New-PSRuleOption' {
$option.Execution.InconclusiveWarning | Should -Be $False;
}
It 'from Environment' {
try {
# With bool
$Env:PSRULE_EXECUTION_INCONCLUSIVEWARNING = 'false';
$option = New-PSRuleOption;
$option.Execution.InconclusiveWarning | Should -Be $False;
# With int
$Env:PSRULE_EXECUTION_INCONCLUSIVEWARNING = '0';
$option = New-PSRuleOption;
$option.Execution.InconclusiveWarning | Should -Be $False;
}
finally {
Remove-Item 'Env:PSRULE_EXECUTION_INCONCLUSIVEWARNING' -Force;
}
}
It 'from parameter' {
$option = New-PSRuleOption -InconclusiveWarning $False -Path $emptyOptionsFilePath;
$option.Execution.InconclusiveWarning | Should -Be $False;
@ -443,6 +632,23 @@ Describe 'New-PSRuleOption' -Tag 'Option','New-PSRuleOption' {
$option.Execution.NotProcessedWarning | Should -Be $False;
}
It 'from Environment' {
try {
# With bool
$Env:PSRULE_EXECUTION_NOTPROCESSEDWARNING = 'false';
$option = New-PSRuleOption;
$option.Execution.NotProcessedWarning | Should -Be $False;
# With int
$Env:PSRULE_EXECUTION_NOTPROCESSEDWARNING = '0';
$option = New-PSRuleOption;
$option.Execution.NotProcessedWarning | Should -Be $False;
}
finally {
Remove-Item 'Env:PSRULE_EXECUTION_NOTPROCESSEDWARNING' -Force;
}
}
It 'from parameter' {
$option = New-PSRuleOption -NotProcessedWarning $False -Path $emptyOptionsFilePath;
$option.Execution.NotProcessedWarning | Should -Be $False;
@ -465,6 +671,17 @@ Describe 'New-PSRuleOption' -Tag 'Option','New-PSRuleOption' {
$option.Input.Format | Should -Be 'Yaml';
}
It 'from Environment' {
try {
$Env:PSRULE_INPUT_FORMAT = 'Yaml';
$option = New-PSRuleOption;
$option.Input.Format | Should -Be 'Yaml';
}
finally {
Remove-Item 'Env:PSRULE_INPUT_FORMAT' -Force;
}
}
It 'from parameter' {
$option = New-PSRuleOption -Format 'Yaml' -Path $emptyOptionsFilePath;
$option.Input.Format | Should -Be 'Yaml';
@ -487,6 +704,17 @@ Describe 'New-PSRuleOption' -Tag 'Option','New-PSRuleOption' {
$option.Input.ObjectPath | Should -Be 'items';
}
It 'from Environment' {
try {
$Env:PSRULE_INPUT_OBJECTPATH = 'items';
$option = New-PSRuleOption;
$option.Input.ObjectPath | Should -Be 'items';
}
finally {
Remove-Item 'Env:PSRULE_INPUT_OBJECTPATH' -Force;
}
}
It 'from parameter' {
$option = New-PSRuleOption -ObjectPath 'items' -Path $emptyOptionsFilePath;
$option.Input.ObjectPath | Should -Be 'items';
@ -509,6 +737,23 @@ Describe 'New-PSRuleOption' -Tag 'Option','New-PSRuleOption' {
$option.Input.PathIgnore | Should -Be '*.Designer.cs';
}
It 'from Environment' {
try {
# With single item
$Env:PSRULE_INPUT_PATHIGNORE = 'ignore.cs';
$option = New-PSRuleOption;
$option.Input.PathIgnore | Should -Be 'ignore.cs';
# With array
$Env:PSRULE_INPUT_PATHIGNORE = 'ignore.cs;*.Designer.cs';
$option = New-PSRuleOption;
$option.Input.PathIgnore | Should -Be 'ignore.cs', '*.Designer.cs';
}
finally {
Remove-Item 'Env:PSRULE_INPUT_PATHIGNORE' -Force;
}
}
It 'from parameter' {
$option = New-PSRuleOption -InputPathIgnore 'ignore.cs' -Path $emptyOptionsFilePath;
$option.Input.PathIgnore | Should -Be 'ignore.cs';
@ -546,6 +791,23 @@ Describe 'New-PSRuleOption' -Tag 'Option','New-PSRuleOption' {
$option.Input.TargetType | Should -BeIn 'virtualMachine';
}
It 'from Environment' {
try {
# With single item
$Env:PSRULE_INPUT_TARGETTYPE = 'virtualMachine';
$option = New-PSRuleOption;
$option.Input.TargetType | Should -Be 'virtualMachine';
# With array
$Env:PSRULE_INPUT_TARGETTYPE = 'virtualMachine;virtualNetwork';
$option = New-PSRuleOption;
$option.Input.TargetType | Should -Be 'virtualMachine', 'virtualNetwork';
}
finally {
Remove-Item 'Env:PSRULE_INPUT_TARGETTYPE' -Force;
}
}
It 'from parameter' {
$option = New-PSRuleOption -InputTargetType 'virtualMachine', 'virtualNetwork' -Path $emptyOptionsFilePath;
$option.Input.TargetType | Should -BeIn 'virtualMachine', 'virtualNetwork';
@ -656,6 +918,17 @@ Describe 'New-PSRuleOption' -Tag 'Option','New-PSRuleOption' {
$option.Output.As | Should -Be 'Summary';
}
It 'from Environment' {
try {
$Env:PSRULE_OUTPUT_AS = 'Summary';
$option = New-PSRuleOption;
$option.Output.As | Should -Be 'Summary';
}
finally {
Remove-Item 'Env:PSRULE_OUTPUT_AS' -Force;
}
}
It 'from parameter' {
$option = New-PSRuleOption -OutputAs 'Summary' -Path $emptyOptionsFilePath;
$option.Output.As | Should -Be 'Summary';
@ -692,6 +965,23 @@ Describe 'New-PSRuleOption' -Tag 'Option','New-PSRuleOption' {
$option.Output.Culture | Should -BeIn 'en-CC', 'en-DD';
}
It 'from Environment' {
try {
# Single
$Env:PSRULE_OUTPUT_CULTURE = 'en-AA';
$option = New-PSRuleOption;
$option.Output.Culture | Should -BeIn 'en-AA';
# Array
$Env:PSRULE_OUTPUT_CULTURE = 'en-AA;en-BB';
$option = New-PSRuleOption;
$option.Output.Culture | Should -BeIn 'en-AA', 'en-BB';
}
finally {
Remove-Item 'Env:PSRULE_OUTPUT_CULTURE' -Force;
}
}
It 'from parameter' {
# Single
$option = New-PSRuleOption -OutputCulture 'en-XX' -Path $emptyOptionsFilePath;
@ -721,6 +1011,17 @@ Describe 'New-PSRuleOption' -Tag 'Option','New-PSRuleOption' {
$option.Output.Encoding | Should -Be 'UTF7';
}
It 'from Environment' {
try {
$Env:PSRULE_OUTPUT_ENCODING = 'UTF7';
$option = New-PSRuleOption;
$option.Output.Encoding | Should -Be 'UTF7';
}
finally {
Remove-Item 'Env:PSRULE_OUTPUT_ENCODING' -Force;
}
}
It 'from parameter' {
$option = New-PSRuleOption -OutputEncoding 'UTF7' -Path $emptyOptionsFilePath;
$option.Output.Encoding | Should -Be 'UTF7';
@ -743,6 +1044,17 @@ Describe 'New-PSRuleOption' -Tag 'Option','New-PSRuleOption' {
$option.Output.Format | Should -Be 'Json';
}
It 'from Environment' {
try {
$Env:PSRULE_OUTPUT_FORMAT = 'Yaml';
$option = New-PSRuleOption;
$option.Output.Format | Should -Be 'Yaml';
}
finally {
Remove-Item 'Env:PSRULE_OUTPUT_FORMAT' -Force;
}
}
It 'from parameter' {
$option = New-PSRuleOption -OutputFormat 'Yaml' -Path $emptyOptionsFilePath;
$option.Output.Format | Should -Be 'Yaml';
@ -765,6 +1077,17 @@ Describe 'New-PSRuleOption' -Tag 'Option','New-PSRuleOption' {
$option.Output.Outcome | Should -Be 'Pass';
}
It 'from Environment' {
try {
$Env:PSRULE_OUTPUT_OUTCOME = 'Fail';
$option = New-PSRuleOption;
$option.Output.Outcome | Should -Be 'Fail';
}
finally {
Remove-Item 'Env:PSRULE_OUTPUT_OUTCOME' -Force;
}
}
It 'from parameter' {
$option = New-PSRuleOption -OutputOutcome 'Fail' -Path $emptyOptionsFilePath;
$option.Output.Outcome | Should -Be 'Fail';
@ -787,6 +1110,17 @@ Describe 'New-PSRuleOption' -Tag 'Option','New-PSRuleOption' {
$option.Output.Path | Should -Be 'out/OutputPath.txt';
}
It 'from Environment' {
try {
$Env:PSRULE_OUTPUT_PATH = 'out/OutputPath.txt';
$option = New-PSRuleOption;
$option.Output.Path | Should -Be 'out/OutputPath.txt';
}
finally {
Remove-Item 'Env:PSRULE_OUTPUT_PATH' -Force;
}
}
It 'from parameter' {
$option = New-PSRuleOption -OutputPath 'out/OutputPath.txt' -Path $emptyOptionsFilePath;
$option.Output.Path | Should -Be 'out/OutputPath.txt';
@ -809,6 +1143,17 @@ Describe 'New-PSRuleOption' -Tag 'Option','New-PSRuleOption' {
$option.Output.Style | Should -Be 'GitHubActions';
}
It 'from Environment' {
try {
$Env:PSRULE_OUTPUT_STYLE = 'AzurePipelines';
$option = New-PSRuleOption;
$option.Output.Style | Should -Be 'AzurePipelines';
}
finally {
Remove-Item 'Env:PSRULE_OUTPUT_STYLE' -Force;
}
}
It 'from parameter' {
$option = New-PSRuleOption -OutputStyle 'AzurePipelines' -Path $emptyOptionsFilePath;
$option.Output.Style | Should -Be 'AzurePipelines';
@ -830,6 +1175,20 @@ Describe 'New-PSRuleOption' -Tag 'Option','New-PSRuleOption' {
$option = New-PSRuleOption -Option (Join-Path -Path $here -ChildPath 'PSRule.Tests6.yml');
$option.Requires.PSRule | Should -Be '>=0.18.0';
}
It 'from Environment' {
try {
$Env:PSRULE_REQUIRES_PSRULE = '^0.1.0';
$Env:PSRULE_REQUIRES_PSRULE_RULES_AZURE = '^0.2.0';
$option = New-PSRuleOption;
$option.Requires.PSRule | Should -Be '^0.1.0';
$option.Requires.'PSRule.Rules.Azure' | Should -Be '^0.2.0';
}
finally {
Remove-Item 'Env:PSRULE_REQUIRES_PSRULE' -Force;
Remove-Item 'Env:PSRULE_REQUIRES_PSRULE_RULES_AZURE' -Force;
}
}
}
Context 'Read Suppression' {

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

@ -87,6 +87,8 @@ namespace PSRule
Assert.Null(builder.Build());
}
#region Helper methods
private static Source[] GetSource()
{
var builder = new SourcePipelineBuilder(null, null);
@ -106,5 +108,7 @@ namespace PSRule
{
return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName);
}
#endregion Helper methods
}
}