Note: All features that have yet to be implemented have been flagged with an asterisk [*].
Template Analyzer built-in rules are authored in JSON. Each rule contains metadata about what's being evaluated (such as id, description, help information, severity), along with the specifics of the evaluation itself. Files consisting of multiple rules should contain an array of rule objects.
Here are the fields that make up a rule definition.
{
"id": "Rule id",
"name": "A human-readable identifier"
"shortDescription": "Brief description of what the rule is evaluating",
"fullDescription": "Detailed description of what the rule is evaluating",
"recommendation": "Guidance describing what should be done to fix the issue if a template violates the rule",
"helpUri": "URI to find more detailed information about the rule and how to fix a template",
"severity" : "Integer value between 1 and 3, with 1 being high and 3 being low, designating the importance of the rule",
"evaluation": { … } // The evaluation logic of the rule. More details below.
}
Property Name | Description | Is required for contributing a built-in rule |
Is required in schema |
Default Value |
---|---|---|---|---|
id | The id should look like TA-NNNNNN , with NNNNNN being the next unused number according to the rule ids already defined. |
yes | yes | - |
name | A human-readable identifier, more details here. | yes | yes | - |
shortDescription | Brief description of what the rule is evaluating, more details here. | yes | yes | - |
fullDescription | Detailed description of what the rule is evaluating, more details here. | yes | yes | - |
recommendation | The recommendation should provide clear but concise guidance on how to modify a template if the rule fails.If some details are somewhat complex, or the rule takes a bit more to understand, add those details to a guide accessible at the URI in helpUri . |
yes | no | none |
helpUri | The helpUri is optional, but it is good practice to include. For built-in rules, this will point to a guide in the GitHub repository. |
yes | no | none |
severity | The severity is optional. If no severity is provided, it defaults to a severity of 2. |
yes | no | 2 |
The Evaluation
is comprised of the following basic properties.
{
"path": "JSON path to property to evaluate in template",
"resourceType": "(optional) The Azure resource type this evaluation applies to",
"where": {
// Evaluation Object
}
"<operator>": // One of several kinds of operators, defined below
}
Evaluation of the templates is performed on the JSON representation of the template. Therefore, Evaluation
s operate on the JSON properties of the template. Specifying the template property is done by specifying a JSON path for the path
key. This path can contain wildcards ('*') to select multiple paths to evaluate - see Wildcard Behavior for details.
Since most rules apply only to specific types of Azure resources, the resourceType
property gives rule authors a shorthand to only evaluate those types of resources. If resourceType
is specified, the path specified in path
becomes relative to the resource selected in the template.
When resourceType
is specified, it must be the fully-qualified type name (for example, Microsoft.Sql/servers/auditingSettings, instead of simply auditingSettings as might be specified in a child resource of Microsoft.Sql/servers).
When looking for the specified resource type, Template Analyzer will look for the "resources" array property at the current scopes, and if found, compare the "type" property of each resource against the string specified for resourceType. The search will also include looking at child resources - i.e. a "resources" array property defined within a resource. This will occur if a type-parent of the specified resourceType is found in the resources (e.g. if searching for type Microsoft.Sql/servers/auditingSettings, resources defined within a resource of type Microsoft.Sql/servers will also be searched).
Documentation on where
is provided below in Where Conditions.
There are two kinds of operators: value operators and structured operators. Value operators evaluate a single value, whereas structured operators are used to nest and combine multiple Evaluation
s, each containing their own operator.
These operators evaluate a specific JSON property in the template. All operators are valid properties in the Evaluation
, but only one operator can be present in the top level of the Evaluation
. If multiple operators are necessary, a structured operator can be used to combine or nest the operators. Each operator must be accompanied by a path
in the Evaluation
. The type of value each operator expects is defined with each operator. Most types are self-descriptive; for the date
type, the following ISO 8601 formats are currently accepted:
- yyyy-MM-dd
- yyyy-MM-ddThh:mm:ssK
- yyyy-MM-ddThh:mmK
- yyyy-MM-dd hh:mm:ssK
More information on the format identifiers can be found here.
The examples given with the operators below will be in the context of the following JSON:
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"parameters": { },
"variables": { },
"resources": [
{
"type": "Microsoft.Compute/virtualMachines",
"name": "myVmResource",
"apiVersion": "2020-06-01",
"properties": {
"networkProfile": {
"networkInterfaces": [
{
"id": "[resourceId('Microsoft.Network/networkInterfaces', 'myNic')]"
}
]
},
"osProfile": {
"computerName": "myVm",
"adminUsername": "myusername",
"adminPassword": null,
}
}
}
],
"outputs": {
"numberOfResourcesDeployed": {
"type": "integer",
"value": 1
},
"customOutput": {
"type": "string",
"value": "A custom output string"
}
}
}
Type: Boolean
Evaluates a JSON path to determine if the specified path exists in the template and compares that result to the value associated with the property in the rule. Results in true
if the existence of the path is the same as the expected value in the rule; false
otherwise.
Example:
{
"resourceType": "Microsoft.Compute/virtualMachines",
"path": "properties.osProfile.linuxConfiguration",
"exists": false // Evaluates to `true` because the path "properties.osProfile.linuxConfiguration" is not defined in the JSON, which is what the rule expects.
}
Type: Boolean
Evaluates a JSON path to determine if the specified path has any value in the template other than null
or empty string and compares that result to the value associated with the property in the rule. Results in true
if the value of the path matches the expectation in the rule; false
otherwise.
Example:
{
"resourceType": "Microsoft.Compute/virtualMachines",
"path": "properties.osProfile.adminPassword",
"hasValue": false // Evaluates to `true` because the path "properties.osProfile.adminPassword" is defined in the JSON, but its value is `null` (does not have a value), which is what the rule expects.
}
Type: Any basic JSON value (integer, float, string, bool, null)
Tests the template value of the path
to determine if it is equal to the value specified in the rule.
- If the type of the value of
equals
does not match the type of the value atpath
, this evaluates tofalse
(except for integer and float, which can be compared with one another). Otherwise, this behaves as expected for the given type. - Evaluations on string types are case-insensitive.
Example:
{
"resourceType": "Microsoft.Compute/virtualMachines",
"path": "name",
"equals": "MyVMResource" // Evaluates to `true` because the value of the path "name" is a string and case-insensitively matches the value in the rule.
}
Type: Any basic JSON value (integer, float, string, bool, null)
The logical inverse of equals
. Evaluations on incompatible types results in true
.
Example:
{
"resourceType": "Microsoft.Compute/virtualMachines",
"path": "properties.osProfile.adminPassword",
"notEquals": "password" // Evaluates to `true` because the value of the path "properties.osProfile.adminPassword" is `null`, which does not match the value in the rule.
}
Type: number (integer, float), date
Compares the template value of the path
against the value specified in the rule. Evaluates to true
if the template value is less than the value in the template; false
otherwise.
Example:
{
"resourceType": "Microsoft.Compute/virtualMachines",
"path": "outputs.numberOfResourcesDeployed.value",
"less": 1 // Evaluates to `false` because the value of the path "outputs.numberOfResourcesDeployed.value" (1) is not less than 1.
}
Type: number (integer, float), date
Compares the template value of the path
against the value specified in the rule. Evaluates to true
if the template value is less than or equal to the value in the template; false
otherwise.
Example:
{
"resourceType": "Microsoft.Compute/virtualMachines",
"path": "outputs.numberOfResourcesDeployed.value",
"lessOrEquals": 1 // Evaluates to `true` because the value of the path "outputs.numberOfResourcesDeployed.value" (1) is equal to 1.
}
Type: number (integer, float), date
Compares the template value of the path
against the value specified in the rule. Evaluates to true
if the template value is greater than the value in the template; false
otherwise.
Example:
{
"resourceType": "Microsoft.Compute/virtualMachines",
"path": "outputs.numberOfResourcesDeployed.value",
"greater": 0 // Evaluates to `true` because the value of the path "outputs.numberOfResourcesDeployed.value" (1) is greater than 0.
}
Type: number (integer, float), date
Compares the template value of the path
against the value specified in the rule. Evaluates to true
if the template value is greater than or equal to the value in the template; false
otherwise.
Example:
{
"resourceType": "Microsoft.Compute/virtualMachines",
"path": "outputs.numberOfResourcesDeployed.value",
"greaterOrEquals": 2 // Evaluates to `false` because the value of the path "outputs.numberOfResourcesDeployed.value" (1) is not greater than or equal to 1.
}
Type: string
Runs the regular expression in the specified value against the value of the path
in the template. All regular expressions are case-insensitive. Evaluates to true
if the regular expression is a match; false
otherwise. If the value in the template is not a string, this evaluates to false
.
Note: regex
replaces many common string-type conditions. Examples:
- Contains "value" -->
"regex": "value"
- Starts with "begin" -->
"regex": "^begin"
- Ends with "end" -->
"regex": "end$"
Example:
{
"resourceType": "Microsoft.Compute/virtualMachines",
"path": "properties.osProfile.adminUsername",
"regex": "admin" // Evaluates to `false` because "admin" is not contained in the value of the path "properties.osProfile.adminUsername".
}
Type: mixed-type array of basic JSON values (integer, float, string, bool, null)
Evaluates the value of the path
in the template using the equals
operator for each value specified in the array. If any results in true
, in
will evaluate to true; false
otherwise.
Example:
{
"resourceType": "Microsoft.Compute/virtualMachines",
"path": "apiVersion",
"in": [
"2020-06-01",
"2019-12-01",
"2019-07-01",
"2019-03-01"
] // Evaluates to `true` because the value of "apiVersion" in the template ("2020-06-01") is equal to one of the values in the array specified for `in`.
}
These operators build up a structure of child Evaluation
s, and therefore contain additional operators inside them. These operators are not required to include a path
. If resourceType
or path
are specified, that becomes the scope for all Evaluation
s nested inside the operator. More information on Scopes can be found below.
Type: array of Evaluation
s
Performs a logical 'or' operation on the array of Evaluation
s. Evaluates to true
if the result of any Evaluation
in the array is true
; evaluates to false
otherwise.
Example:
{
"anyOf": [
{
"resourceType": "Microsoft.Compute/virtualMachines",
"path": "properties.osProfile.adminPassword",
"hasValue": false // Evaluates to `false`
},
{
"resourceType": "Microsoft.Compute/virtualMachines",
"path": "properties.osProfile.adminUsername",
"regex": "username" // Evaluates to `true`
}
] // Evaluates to `true` because one of the expressions contained in "anyOf" resulted in `true`
}
Type: array of Evaluation
s
Performs a logical 'and' operation on the array of Evaluation
s. Evaluates to true
if the result of all Evaluation
s in the array is true
; evaluates to false
otherwise.
Example:
{
"allOf": [
{
"resourceType": "Microsoft.Compute/virtualMachines",
"path": "properties.osProfile.adminPassword",
"hasValue": false // Evaluates to `false`
},
{
"resourceType": "Microsoft.Compute/virtualMachines",
"path": "properties.osProfile.adminUsername",
"regex": "username" // Evaluates to `true`
}
] // Evaluates to `false` because not all the expressions contained in "allOf" resulted in `true`
}
Type: Evaluation
Performs a logical 'not' operation on the Evaluation
. Evaluates to true
if the result of the Evaluation
it contains is false
; evaluates to false
otherwise.
Example:
{
"not": {
"resourceType": "Microsoft.Compute/virtualMachines",
"path": "properties.osProfile.adminUsername",
"regex": "admin" // Evaluates to `false`
} // Evaluates to `true` because it's the logical 'not' of the sub-evaluation that is `false`
}
Evaluate [*]
Type: Evaluation
NOTE: evaluate
is not yet supported. As a workaround, replace it with an allOf
or anyOf
operator containing a single Evaluation
. See the examples in Where Conditions for what this would look like.
Wraps a single Evaluation
. The result of the operator is exactly the result of the Evaluation
it contains.
This operator is most commonly useful in combination with a where
condition, where resourceType
or path
may need to be scoped down multiple times.
Example:
{
"evaluate": {
"resourceType": "Microsoft.Compute/virtualMachines",
"path": "properties.osProfile.adminPassword",
"hasValue": false // Evaluates to `true`
} // Evaluates to the same as the inner evaluation (`true`)
}
Each Evaluation
has a path scope which is inherited by child Evaluation
s. If a path
and/or resourceType
is specified in a Structured Evaluation
, the path
and resourceType
of each child Evaluation
start at the path determined in the parent. Therefore, each path
continues from the path
specified in the parent.
For example, here's a simple illustration:
{
"resourceType": "Microsoft.Compute/virtualMachines",
"allOf": [
{
"path": "properties.osProfile.adminPassword", // Continues from resource selected in parent scope
"hasValue": false
}
]
}
The full path used by the 'hasValue' Evaluator would be resources[*].properties.osProfile.adminPassword (limited to resources where type equals "Microsoft.Compute/virtualMachines").
First, resourceType
is used to select resources within the resources[] array. Then, only those resources with the given type are considered. Further, the path
specified with hasValue
continues from the path in the parent scope, appending .properties.osProfile.adminPassword to the resources selection.
Rule authors may wish to define a rule that is dependent on other properties. For example, there may be desire for a rule of the form: "If a property in a resource equals some value, then assert another property is a certain value."
Conditions like this can be defined using the where
property, which has a value of type Evaluation
(similar to Structured Operators).
In the Evaluation
in which where
is defined, resourceType
and path
are evaluated first, and then the resulting scopes are evaluated by where
. Since where
is an Evaluation
, the scope can be further narrowed by specifying resourceType
or path
inside it.
Multiple scopes may be evaluated by where
as a result of:
- multiple resources matching the
resourceType
specification - wildcards being present in
path
and matching multiple paths in the template JSON
For each scope evaluated by where
, only the scopes in which where
evaluates to true
are evaluated by the operator that is a sibling of where
; if the where
evaluates to false
for a given scope, evaluation of the operator will be skipped for that scope.
Examples:
{
"resourceType": "Microsoft.Compute/virtualMachines",
"where": {
"path": "apiVersion",
"regex": "^2019-.*" // "Microsoft.Compute/virtualMachines" resources where the value of "apiVersion" matches this regex...
},
"allOf": [
{
"path": "properties.osProfile.computerName",
"hasValue": true // ...must have a value for "properties.osProfile.computerName".
}
]
}
In the simple example above, the allOf
operator would be skipped, because there is no resource of type "Microsoft.Compute/virtualMachines" where apiVersion starts with 2019. The entire example would therefore not return any result.
{
"resourceType": "Microsoft.Compute/virtualMachines",
"where": {
"path": "name",
"equals": "myVmResource" // "Microsoft.Compute/virtualMachines" resources where the value of "name" is "myVmResource"...
},
"allOf": [
{
"path": "properties.osProfile.computerName",
"hasValue": true // ...must have a value for "properties.osProfile.computerName".
}
]
}
In contrast to the first example, the allOf
operator in the example above would be evaluated, because the resource of type "Microsoft.Compute/virtualMachines" defines its "name" property to be "myVmResource".
NOTE: In both examples above, "path": "properties.osProfile.computerName"
is specified inside the allOf
operators. This is important because of how scopes are determined. If it was instead specified outside the operator (as a sibling to where
), it would narrow the outer scope to that path. That path would then be passed into where
, resulting in "path": "apiVersion"
and "path": "name"
(inside where
in the examples) being appended to properties.osProfile.computerName in the outer scope, which is not the intent.
The path
in an Evaluation
can specify the '*' character as a wildcard. '*' can be used to match any full property name or as the index into an array (selecting all elements of the array). When a wildcard is used, zero or more paths in the template will be found that match path
. If zero paths are found, the operator in the Evaluation
is skipped, as there is nothing to evaluate. If two or more paths are found, the operator evaluates each path individually and treats each path as its own result; then, if the operator evaluating the path(s) is contained within an allOf
or anyOf
, the results will be combined according to those operators - otherwise, they will be reported individually.
When using a wildcard for a property name, '*' must replace the entire name of a property (such as property.* or property.*.otherProperty), being the only character between the periods. Wildcards for partial property names (e.g. property.*id) are not supported. When using a wildcard as an index into an array (such as property[*]), '*' must be the only character between the '[]' characters.
Examples:
{
"resourceType": "Microsoft.Compute/virtualMachines",
"path": "properties.osProfile.*" // Returns all child properties of osProfile:
// resources[0].properties.osProfile.computerName
// resources[0].properties.osProfile.adminUsername
// resources[0].properties.osProfile.adminPassword
}
{
"resourceType": "Microsoft.Compute/virtualMachines",
"path": "properties.networkProfile.networkInterfaces[*]" // Returns all elements in networkInterfaces array (only one element is defined in the array):
// resources[0].properties.networkProfile.networkInterfaces[0]
}
{
"path": "resources[*]" // Returns all resources (only one resource is defined):
// resources[0]
}
{
"path": "outputs.*" // Returns all outputs:
// outputs.numberOfResourcesDeployed
// outputs.customOutput
}
{
"resourceType": "Microsoft.Compute/virtualMachines",
"path": "tags.*" // Returns all child properties of 'tags' - no paths returned, as no tags are defined in the virtual machine resource
}