Sub-selectors#
Abstract
This topic covers sub-selectors which are a PSRule language feature specific to YAML and JSON expressions. They are useful for filtering out objects that you do not want to evaluate. Sub-selectors don't apply to script expressions because PowerShell already has rich support for filtering.
Experimental
Sub-selectors are a work in progress and subject to change. We hope to add broader support, and more detailed documentation in the future. Join or start a disucssion to let us know how we can improve this feature going forward.
Sub-selectors cover two (2) main scenarios:
- Pre-conditions — you want to filtering out objects before a rule is run.
- Object filtering — you want to limit a condition to specific elements in a list of items.
Pre-conditions#
PSRule can process many different types of objects. Rules however, are normally written to test a specific property or type of object. So it is important that rules only run on objects that you want to evaluate. Pre-condition sub-selectors are one way you can determine if a rule should be run.
To use a sub-selector as a pre-condition, use the where
property, directly under the spec
.
The expressions in the sub-selector follow the same form that you can use in rules.
For example:
---
# Synopsis: A rule with a sub-selector precondition.
apiVersion: github.com/microsoft/PSRule/v1
kind: Rule
metadata:
name: Yaml.Subselector.Precondition
spec:
where:
field: 'kind'
equals: 'api'
condition:
field: resources
count: 10
{
// Synopsis: A rule with a sub-selector precondition.
"apiVersion": "github.com/microsoft/PSRule/v1",
"kind": "Rule",
"metadata": {
"name": "Json.Subselector.Precondition"
},
"spec": {
"where": {
"field": "kind",
"equals": "api"
},
"condition": {
"field": "resources",
"count": 10
}
}
}
In the example:
- The
where
property is the start of a sub-selector. - The sub-selector checks if the
kind
property equalsapi
.
The rule does not run if the:
- The object does not have a
kind
property. OR - The value of the
kind
property is notapi
.
Tip
Other types of pre-conditions also exist that allow you to filter based on type or by a shared selector.
Object filter#
When you are evaluating an object, you can use sub-selectors to limit the condition. This is helpful when dealing with properties that are a list of items. Properties that contain a list of items may contain a sub-set of items that you want to evaluate.
For example, the object may look like this:
name: app1
type: Microsoft.Web/sites
resources:
- name: web
type: Microsoft.Web/sites/config
properties:
detailedErrorLoggingEnabled: true
{
"name": "app1",
"type": "Microsoft.Web/sites",
"resources": [
{
"name": "web",
"type": "Microsoft.Web/sites/config",
"properties": {
"detailedErrorLoggingEnabled": true
}
}
]
}
A rule to test if any sub-resources with the detailedErrorLoggingEnabled
set to true
exist might look like this:
---
# Synopsis: A rule with a sub-selector filter.
apiVersion: github.com/microsoft/PSRule/v1
kind: Rule
metadata:
name: Yaml.Subselector.Filter
spec:
condition:
field: resources
where:
type: '.'
equals: 'Microsoft.Web/sites/config'
allOf:
- field: properties.detailedErrorLoggingEnabled
equals: true
{
// Synopsis: A rule with a sub-selector filter.
"apiVersion": "github.com/microsoft/PSRule/v1",
"kind": "Rule",
"metadata": {
"name": "Json.Subselector.Filter"
},
"spec": {
"condition": {
"field": "resources",
"where": {
"type": ".",
"equals": "Microsoft.Web/sites/config"
},
"allOf": [
{
"field": "properties.detailedErrorLoggingEnabled",
"equals": true
}
]
}
}
}
In the example:
- If the array property
resources
exists, any items with a type ofMicrosoft.Web/sites/config
are evaluated.- Each item must have the
properties.detailedErrorLoggingEnabled
property set totrue
to pass. - Items without the
properties.detailedErrorLoggingEnabled
property fail. - Items with the
properties.detailedErrorLoggingEnabled
property set to a value other thentrue
fail.
- Each item must have the
- If the
resources
property does not exist, the rule fails. - If the
resources
property exists but has 0 items of typeMicrosoft.Web/sites/config
, the rule fails. - If the
resources
property exists and has any items of typeMicrosoft.Web/sites/config
but any fail, the rule fails. - If the
resources
property exists and has any items of typeMicrosoft.Web/sites/config
and all pass, the rule passes.
When there are no results#
Given the example, is important to understand what happens by default if:
- The
resources
property doesn't exist. OR - The
resources
property doesn't contain any items that match the sub-selector condition.
In either of these two cases, the sub-selector will return false
and fail the rule.
The rule fails because there is no secondary conditions that could be used instead.
If this was not the desired behavior, you could:
- Use a pre-condition to avoid running the rule.
- Group the sub-selector into a
anyOf
, and provide a secondary condition. - Use a quantifier to determine how many items must match sub-selector and match the
allOf
/anyOf
operator.
For example:
---
# Synopsis: A rule with a sub-selector filter.
apiVersion: github.com/microsoft/PSRule/v1
kind: Rule
metadata:
name: Yaml.Subselector.FilterOr
spec:
condition:
anyOf:
- field: resources
where:
type: '.'
equals: 'Microsoft.Web/sites/config'
allOf:
- field: properties.detailedErrorLoggingEnabled
equals: true
- field: resources
exists: false
{
// Synopsis: A rule with a sub-selector filter.
"apiVersion": "github.com/microsoft/PSRule/v1",
"kind": "Rule",
"metadata": {
"name": "Json.Subselector.FilterOr"
},
"spec": {
"condition": {
"anyOf": [
{
"field": "resources",
"where": {
"type": ".",
"equals": "Microsoft.Web/sites/config"
},
"allOf": [
{
"field": "properties.detailedErrorLoggingEnabled",
"equals": true
}
]
},
{
"field": "resources",
"exists": false
}
]
}
}
}
In the example:
- If the array property
resources
exists, any items with a type ofMicrosoft.Web/sites/config
are evaluated.- Each item must have the
properties.detailedErrorLoggingEnabled
property set totrue
to pass. - Items without the
properties.detailedErrorLoggingEnabled
property fail. - Items with the
properties.detailedErrorLoggingEnabled
property set to a value other thentrue
fail.
- Each item must have the
- If the
resources
property does not exist, the rule passes. - If the
resources
property exists but has 0 items of typeMicrosoft.Web/sites/config
, the rule fails. - If the
resources
property exists and has any items of typeMicrosoft.Web/sites/config
but any fail, the rule fails. - If the
resources
property exists and has any items of typeMicrosoft.Web/sites/config
and all pass, the rule passes.
Using a quantifier with sub-selectors#
When iterating over a list of items, you may want to determine how many items must match. A quantifier determines how many items in the list match. Matching items must be:
- Selected by the sub-selector.
- Match the condition of the operator.
Supported quantifiers are:
count
— The number of items must equal then the specified value.less
— The number of items must less then the specified value.lessOrEqual
— The number of items must less or equal to the specified value.greater
— The number of items must greater then the specified value.greaterOrEqual
— The number of items must greater or equal to the specified value.
For example:
---
# Synopsis: A rule with a sub-selector quantifier.
apiVersion: github.com/microsoft/PSRule/v1
kind: Rule
metadata:
name: Yaml.Subselector.Quantifier
spec:
condition:
field: resources
where:
type: '.'
equals: 'Microsoft.Web/sites/config'
greaterOrEqual: 1
allOf:
- field: properties.detailedErrorLoggingEnabled
equals: true
{
// Synopsis: A rule with a sub-selector quantifier.
"apiVersion": "github.com/microsoft/PSRule/v1",
"kind": "Rule",
"metadata": {
"name": "Json.Subselector.Quantifier"
},
"spec": {
"condition": {
"field": "resources",
"where": {
"type": ".",
"equals": "Microsoft.Web/sites/config"
},
"greaterOrEqual": 1,
"allOf": [
{
"field": "properties.detailedErrorLoggingEnabled",
"equals": true
}
]
}
}
}
In the example:
- If the array property
resources
exists, any items with a type ofMicrosoft.Web/sites/config
are evaluated.- Each item must have the
properties.detailedErrorLoggingEnabled
property set totrue
to pass. - The number of items that pass must be greater or equal to
1
.
- Each item must have the
- If the
resources
property does not exist or is empty, the number of items is0
which fails greater or equal to1
.