Test cases for ARM templates
This article describes tests that are run with the template test toolkit for Azure Resource Manager templates (ARM templates). It provides examples that pass or fail the test and includes the name of each test. For more information about how to run tests or how to run a specific test, see Test parameters.
Use correct schema
Test name: DeploymentTemplate Schema Is Correct
In your template, you must specify a valid schema value.
The following example fails because the schema is invalid.
{
"$schema": "https://schema.management.azure.com/schemas/2019-01-01/deploymentTemplate.json#",
}
The following example displays a warning because schema version 2015-01-01
is deprecated and isn't maintained.
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
}
The following example passes using a valid schema.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
}
The template's schema
property must be set to one of the following schemas:
https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#
https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#
https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#
https://schema.management.azure.com/schemas/2019-08-01/tenantDeploymentTemplate.json#
https://schema.management.azure.com/schemas/2019-08-01/managementGroupDeploymentTemplate.json
Declared parameters must be used
Test name: Parameters Must Be Referenced
This test finds parameters that aren't used in the template or parameters that aren't used in a valid expression.
To reduce confusion in your template, delete any parameters that are defined but not used. Eliminating unused parameters simplifies template deployments because you don't have to provide unnecessary values.
In Bicep, use Linter rule - no unused parameters.
The following example fails because the expression that references a parameter is missing the leading square bracket ([
).
"resources": [
{
"location": " parameters('location')]"
}
]
The following example passes because the expression is valid.
"resources": [
{
"location": "[parameters('location')]"
}
]
Secure parameters can't have hard-coded default
Test name: Secure String Parameters Cannot Have Default
Don't provide a hard-coded default value for a secure parameter in your template. A secure parameter can have an empty string as a default value or use the newGuid function in an expression.
You use the types secureString
or secureObject
on parameters that contain sensitive values, like passwords. When a parameter uses a secure type, the value of the parameter isn't logged or stored in the deployment history. This action prevents a malicious user from discovering the sensitive value.
When you provide a default value, that value is discoverable by anyone who can access the template or the deployment history.
In Bicep, use Linter rule - secure parameter default.
The following example fails.
"parameters": {
"adminPassword": {
"defaultValue": "HardcodedPassword",
"type": "secureString"
}
}
The next example passes.
"parameters": {
"adminPassword": {
"type": "secureString"
}
}
The following example passes because the newGuid
function is used.
"parameters": {
"secureParameter": {
"type": "secureString",
"defaultValue": "[newGuid()]"
}
}
Environment URLs can't be hard-coded
Test name: DeploymentTemplate Must Not Contain Hardcoded Uri
Don't hard-code environment URLs in your template. Instead, use the environment function to dynamically get these URLs during deployment. For a list of the URL hosts that are blocked, see the test case.
In Bicep, use Linter rule - no hardcoded environment URL.
The following example fails because the URL is hard-coded.
"variables":{
"AzureURL":"https://management.azure.com"
}
The test also fails when used with concat or uri.
"variables":{
"AzureSchemaURL1": "[concat('https://','gallery.azure.com')]",
"AzureSchemaURL2": "[uri('gallery.azure.com','test')]"
}
The following example passes.
"variables": {
"AzureSchemaURL": "[environment().gallery]"
}
Location uses parameter
Test name: Location Should Not Be Hardcoded
To set a resource's location, your templates should have a parameter named location
with the type set to string
. In the main template, azuredeploy.json or mainTemplate.json, this parameter can default to the resource group location. In linked or nested templates, the location parameter shouldn't have a default location.
Template users may have limited access to regions where they can create resources. A hard-coded resource location might block users from creating a resource. The "[resourceGroup().location]"
expression could block users if the resource group was created in a region the user can't access. Users who are blocked are unable to use the template.
By providing a location
parameter that defaults to the resource group location, users can use the default value when convenient but also specify a different location.
In Bicep, use Linter rule - no location expressions outside of parameter default values.
The following example fails because the resource's location
is set to resourceGroup().location
.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {},
"variables": {},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2021-02-01",
"name": "storageaccount1",
"location": "[resourceGroup().location]",
"kind": "StorageV2",
"sku": {
"name": "Premium_LRS",
"tier": "Premium"
}
}
]
}
The next example uses a location
parameter but fails because the parameter defaults to a hard-coded location.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"location": {
"type": "string",
"defaultValue": "westus"
}
},
"variables": {},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2021-02-01",
"name": "storageaccount1",
"location": "[parameters('location')]",
"kind": "StorageV2",
"sku": {
"name": "Premium_LRS",
"tier": "Premium"
}
}
],
"outputs": {}
}
The following example passes when the template is used as the main template. Create a parameter that defaults to the resource group location but allows users to provide a different value.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "Location for the resources."
}
}
},
"variables": {},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2021-02-01",
"name": "storageaccount1",
"location": "[parameters('location')]",
"kind": "StorageV2",
"sku": {
"name": "Premium_LRS",
"tier": "Premium"
}
}
],
"outputs": {}
}
Note
If the preceding example is used as a linked template, the test fails. When used as a linked template, remove the default value.
Resources should have location
Test name: Resources Should Have Location
The location for a resource should be set to a template expression or global
. The template expression would typically use the location
parameter described in Location uses parameter.
In Bicep, use Linter rule - no hardcoded locations.
The following example fails because the location
isn't an expression or global
.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {},
"functions": [],
"variables": {},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2021-02-01",
"name": "storageaccount1",
"location": "westus",
"kind": "StorageV2",
"sku": {
"name": "Premium_LRS",
"tier": "Premium"
}
}
],
"outputs": {}
}
The following example passes because the resource location
is set to global
.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {},
"functions": [],
"variables": {},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2021-02-01",
"name": "storageaccount1",
"location": "global",
"kind": "StorageV2",
"sku": {
"name": "Premium_LRS",
"tier": "Premium"
}
}
],
"outputs": {}
}
The next example also passes because the location
parameter uses an expression. The resource location
uses the expression's value.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "Location for the resources."
}
}
},
"variables": {},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2021-02-01",
"name": "storageaccount1",
"location": "[parameters('location')]",
"kind": "StorageV2",
"sku": {
"name": "Premium_LRS",
"tier": "Premium"
}
}
],
"outputs": {}
}
VM size uses parameter
Test name: VM Size Should Be A Parameter
Don't hard-code the hardwareProfile
object's vmSize
. The test fails when the hardwareProfile
is omitted or contains a hard-coded value. Provide a parameter so users of your template can modify the size of the deployed virtual machine. For more information, see Microsoft.Compute virtualMachines.
The following example fails because the hardwareProfile
object's vmSize
is a hard-coded value.
"resources": [
{
"type": "Microsoft.Compute/virtualMachines",
"apiVersion": "2020-12-01",
"name": "demoVM",
"location": "[parameters('location')]",
"properties": {
"hardwareProfile": {
"vmSize": "Standard_D2_v3"
}
}
}
]
The example passes when a parameter specifies a value for vmSize
:
"parameters": {
"vmSizeParameter": {
"type": "string",
"defaultValue": "Standard_D2_v3",
"metadata": {
"description": "Size for the virtual machine."
}
}
}
Then, hardwareProfile
uses an expression for vmSize
to reference the parameter's value:
"resources": [
{
"type": "Microsoft.Compute/virtualMachines",
"apiVersion": "2020-12-01",
"name": "demoVM",
"location": "[parameters('location')]",
"properties": {
"hardwareProfile": {
"vmSize": "[parameters('vmSizeParameter')]"
}
}
}
]
Min and max values are numbers
Test name: Min And Max Value Are Numbers
When you define a parameter with minValue
and maxValue
, specify them as numbers. You must use minValue
and maxValue
as a pair or the test fails.
The following example fails because minValue
and maxValue
are strings.
"exampleParameter": {
"type": "int",
"minValue": "0",
"maxValue": "10"
}
The following example fails because only minValue
is used.
"exampleParameter": {
"type": "int",
"minValue": 0
}
The following example passes because minValue
and maxValue
are numbers.
"exampleParameter": {
"type": "int",
"minValue": 0,
"maxValue": 10
}
Artifacts parameter defined correctly
Test name: artifacts parameter
When you include parameters for _artifactsLocation
and _artifactsLocationSasToken
, use the correct defaults and types. The following conditions must be met to pass this test:
- If you provide one parameter, you must provide the other.
_artifactsLocation
must be astring
._artifactsLocation
must have a default value in the main template._artifactsLocation
can't have a default value in a nested template._artifactsLocation
must have either"[deployment().properties.templateLink.uri]"
or the raw repo URL for its default value._artifactsLocationSasToken
must be asecureString
._artifactsLocationSasToken
can only have an empty string for its default value._artifactsLocationSasToken
can't have a default value in a nested template.
In Bicep, use Linter rule - artifacts parameters.
Declared variables must be used
Test name: Variables Must Be Referenced
This test finds variables that aren't used in the template or aren't used in a valid expression. To reduce confusion in your template, delete any variables that are defined but not used.
Variables that use the copy
element to iterate values must be referenced. For more information, see Variable iteration in ARM templates.
In Bicep, use Linter rule - no unused variables.
The following example fails because the variable that uses the copy
element isn't referenced.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"itemCount": {
"type": "int",
"defaultValue": 5
}
},
"variables": {
"copy": [
{
"name": "stringArray",
"count": "[parameters('itemCount')]",
"input": "[concat('item', copyIndex('stringArray', 1))]"
}
]
},
"resources": [],
"outputs": {}
}
The following example fails because the expression that references a variable is missing the leading square bracket ([
).
"outputs": {
"outputVariable": {
"type": "string",
"value": " variables('varExample')]"
}
}
The following example passes because the variable is referenced in outputs
.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"itemCount": {
"type": "int",
"defaultValue": 5
}
},
"variables": {
"copy": [
{
"name": "stringArray",
"count": "[parameters('itemCount')]",
"input": "[concat('item', copyIndex('stringArray', 1))]"
}
]
},
"resources": [],
"outputs": {
"arrayResult": {
"type": "array",
"value": "[variables('stringArray')]"
}
}
}
The following example passes because the expression is valid.
"outputs": {
"outputVariable": {
"type": "string",
"value": "[variables('varExample')]"
}
}
Dynamic variable should not use concat
Test name: Dynamic Variable References Should Not Use Concat
Sometimes you need to dynamically construct a variable based on the value of another variable or parameter. Don't use the concat function when setting the value. Instead, use an object that includes the available options and dynamically get one of the properties from the object during deployment.
The following example passes. The currentImage
variable is dynamically set during deployment.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"osType": {
"type": "string",
"allowedValues": [
"Windows",
"Linux"
]
}
},
"variables": {
"imageOS": {
"Windows": {
"image": "Windows Image"
},
"Linux": {
"image": "Linux Image"
}
},
"currentImage": "[variables('imageOS')[parameters('osType')].image]"
},
"resources": [],
"outputs": {
"result": {
"type": "string",
"value": "[variables('currentImage')]"
}
}
}
Use recent API version
Test name: apiVersions Should Be Recent
The API version for each resource should use a recent version that's hard-coded as a string. The test evaluates the API version in your template against the resource provider's versions in the toolkit's cache. An API version that's less than two years old from the date the test was run is considered recent. Don't use a preview version when a more recent version is available.
A warning that an API version wasn't found only indicates the version isn't included in the toolkit's cache. Using the latest version of an API, which is recommended, can generate the warning.
Learn more about the toolkit cache.
In Bicep, use Linter rule - use recent API versions.
The following example fails because the API version is more than two years old.
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2019-06-01",
"name": "storageaccount1",
"location": "[parameters('location')]"
}
]
The following example fails because a preview version is used when a newer version is available.
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2020-08-01-preview",
"name": "storageaccount1",
"location": "[parameters('location')]"
}
]
The following example passes because it's a recent version that's not a preview version.
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2021-02-01",
"name": "storageaccount1",
"location": "[parameters('location')]"
}
]
Use hard-coded API version
Test name: Providers apiVersions Is Not Permitted
The API version for a resource type determines which properties are available. Provide a hard-coded API version in your template. Don't retrieve an API version that's determined during deployment because you won't know which properties are available.
The following example fails.
"resources": [
{
"type": "Microsoft.Compute/virtualMachines",
"apiVersion": "[providers('Microsoft.Compute', 'virtualMachines').apiVersions[0]]",
...
}
]
The following example passes.
"resources": [
{
"type": "Microsoft.Compute/virtualMachines",
"apiVersion": "2020-12-01",
...
}
]
Properties can't be empty
Test name: Template Should Not Contain Blanks
Don't hard-code properties to an empty value. Empty values include null and empty strings, objects, or arrays. If a property is set to an empty value, remove that property from your template. You can set a property to an empty value during deployment, such as through a parameter.
The template
property in a nested template can include empty properties. For more information about nested templates, see Microsoft.Resources deployments.
The following example fails because there are empty properties.
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2021-01-01",
"name": "storageaccount1",
"location": "[parameters('location')]",
"sku": {},
"kind": ""
}
]
The following example passes because the properties include values.
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2021-01-01",
"name": "storageaccount1",
"location": "[parameters('location')]",
"sku": {
"name": "Standard_LRS",
"tier": "Standard"
},
"kind": "Storage"
}
]
Use Resource ID functions
Test name: IDs Should Be Derived From ResourceIDs
When specifying a resource ID, use one of the resource ID functions. The allowed functions are:
Don't use the concat function to create a resource ID.
In Bicep, use Linter rule - use resource ID functions.
The following example fails.
"networkSecurityGroup": {
"id": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', resourceGroup().name, '/providers/Microsoft.Network/networkSecurityGroups/', variables('networkSecurityGroupName'))]"
}
The next example passes.
"networkSecurityGroup": {
"id": "[resourceId('Microsoft.Network/networkSecurityGroups', variables('networkSecurityGroupName'))]"
}
ResourceId function has correct parameters
Test name: ResourceIds should not contain
When generating resource IDs, don't use unnecessary functions for optional parameters. By default, the resourceId function uses the current subscription and resource group. You don't need to provide those values.
The following example fails because you don't need to provide the current subscription ID and resource group name.
"networkSecurityGroup": {
"id": "[resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.Network/networkSecurityGroups', variables('networkSecurityGroupName'))]"
}
The next example passes.
"networkSecurityGroup": {
"id": "[resourceId('Microsoft.Network/networkSecurityGroups', variables('networkSecurityGroupName'))]"
}
This test applies to:
For reference
and list*
, the test fails when you use concat
to construct the resource ID.
dependsOn best practices
Test name: DependsOn Best Practices
When setting the deployment dependencies, don't use the if function to test a condition. If one resource depends on a resource that's conditionally deployed, set the dependency as you would with any resource. When a conditional resource isn't deployed, Azure Resource Manager automatically removes it from the required dependencies.
The dependsOn
element can't begin with a concat function.
In Bicep, use Linter rule - no unnecessary dependsOn entries.
The following example fails because it contains an if
function.
"dependsOn": [
"[if(equals(parameters('newOrExisting'),'new'), variables('storageAccountName'), '')]"
]
The following example fails because it begins with concat
.
"dependsOn": [
"[concat(variables('storageAccountName'))]"
]
The following example passes.
"dependsOn": [
"[variables('storageAccountName')]"
]
Nested or linked deployments can't use debug
Test name: Deployment Resources Must Not Be Debug
When you define a nested or linked template with the Microsoft.Resources/deployments
resource type, you can enable debugging. Debugging is used when you need to test a template but can expose sensitive information. Before the template is used in production, turn off debugging. You can remove the debugSetting
object or change the detailLevel
property to none
.
The following example fails.
"debugSetting": {
"detailLevel": "requestContent"
}
The following example passes.
"debugSetting": {
"detailLevel": "none"
}
Admin user names can't be literal value
Test name: adminUsername Should Not Be A Literal
When setting an adminUserName
, don't use a literal value. Create a parameter for the user name and use an expression to reference the parameter's value.
In Bicep, use Linter rule - admin user name should not be literal.
The following example fails with a literal value.
"osProfile": {
"adminUserName": "myAdmin"
}
The following example passes with an expression.
"osProfile": {
"adminUsername": "[parameters('adminUsername')]"
}
Use latest VM image
Test name: VM Images Should Use Latest Version
This test is disabled, but the output shows that it passed. The best practice is to check your template for the following criteria:
If your template includes a virtual machine with an image, make sure it's using the latest version of the image.
In Bicep, use Linter rule - use stable VM image.
Use stable VM images
Test name: Virtual Machines Should Not Be Preview
Virtual machines shouldn't use preview images. The test checks the storageProfile
to verify that the imageReference
doesn't use a string that contains preview. And that preview isn't used in the imageReference
properties offer
, sku
, or version
.
For more information about the imageReference
property, see Microsoft.Compute virtualMachines and Microsoft.Compute virtualMachineScaleSets.
In Bicep, use Linter rule - use stable VM image.
The following example fails because imageReference
is a string that contains preview.
"properties": {
"storageProfile": {
"imageReference": "latest-preview"
}
}
The following example fails when preview is used in offer
, sku
, or version
.
"properties": {
"storageProfile": {
"imageReference": {
"publisher": "Canonical",
"offer": "UbuntuServer_preview",
"sku": "16.04-LTS-preview",
"version": "preview"
}
}
}
The following example passes.
"storageProfile": {
"imageReference": {
"publisher": "Canonical",
"offer": "UbuntuServer",
"sku": "16.04-LTS",
"version": "latest"
}
}
Don't use ManagedIdentity extension
Test name: ManagedIdentityExtension must not be used
Don't apply the ManagedIdentity
extension to a virtual machine. The extension was deprecated in 2019 and should no longer be used.
Outputs can't include secrets
Test name: Outputs Must Not Contain Secrets
Don't include any values in the outputs
section that potentially exposes secrets. For example, secure parameters of type secureString
or secureObject
, or list* functions such as listKeys
.
The output from a template is stored in the deployment history, so a malicious user could find that information.
In Bicep, use Linter rule - outputs should not contain secrets.
The following example fails because it includes a secure parameter in an output value.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"secureParam": {
"type": "secureString"
}
},
"functions": [],
"variables": {},
"resources": [],
"outputs": {
"badResult": {
"type": "string",
"value": "[concat('this is the value ', parameters('secureParam'))]"
}
}
}
The following example fails because it uses a list* function in the outputs.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"storageName": {
"type": "string"
}
},
"functions": [],
"variables": {},
"resources": [],
"outputs": {
"badResult": {
"type": "object",
"value": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('storageName')), '2021-02-01')]"
}
}
}
Use protectedSettings for commandToExecute secrets
Test name: CommandToExecute Must Use ProtectedSettings For Secrets
For resources with type CustomScript
, use the encrypted protectedSettings
when commandToExecute
includes secret data such as a password. For example, secret data can be used in secure parameters of type secureString
or secureObject
, list* functions such as listKeys
, or custom scripts.
Don't use secret data in the settings
object because it uses clear text. For more information, see Microsoft.Compute virtualMachines/extensions, Windows, or Linux.
In Bicep, use Linter rule - use protectedSettings for commandToExecute secrets.
The following example fails because settings
uses commandToExecute
with a secure parameter.
"parameters": {
"adminPassword": {
"type": "secureString"
}
}
...
"properties": {
"type": "CustomScript",
"settings": {
"commandToExecute": "[parameters('adminPassword')]"
}
}
The following example fails because settings
uses commandToExecute
with a listKeys
function.
"properties": {
"type": "CustomScript",
"settings": {
"commandToExecute": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('storageName')), '2021-02-01')]"
}
}
The following example passes because protectedSettings
uses commandToExecute
with a secure parameter.
"parameters": {
"adminPassword": {
"type": "secureString"
}
}
...
"properties": {
"type": "CustomScript",
"protectedSettings": {
"commandToExecute": "[parameters('adminPassword')]"
}
}
The following example passes because protectedSettings
uses commandToExecute
with a listKeys
function.
"properties": {
"type": "CustomScript",
"protectedSettings": {
"commandToExecute": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('storageName')), '2021-02-01')]"
}
}
Use recent API versions in reference functions
Test name: apiVersions Should Be Recent In Reference Functions
The API version used in a reference function must be recent and not a preview version. The test evaluates the API version in your template against the resource provider's versions in the toolkit's cache. An API version that's less than two years old from the date the test was run is considered recent.
A warning that an API version wasn't found only indicates the version isn't included in the toolkit's cache. Using the latest version of an API, which is recommended, can generate the warning.
Learn more about the toolkit cache.
The following example fails because the API version is more than two years old.
"outputs": {
"stgAcct": {
"type": "string",
"value": "[reference(resourceId(parameters('storageResourceGroup'), 'Microsoft.Storage/storageAccounts', parameters('storageAccountName')), '2019-06-01')]"
}
}
The following example fails because the API version is a preview version.
"outputs": {
"stgAcct": {
"type": "string",
"value": "[reference(resourceId(parameters('storageResourceGroup'), 'Microsoft.Storage/storageAccounts', parameters('storageAccountName')), '2020-08-01-preview')]"
}
}
The following example passes because the API version less than two years old and isn't a preview version.
"outputs": {
"stgAcct": {
"type": "string",
"value": "[reference(resourceId(parameters('storageResourceGroup'), 'Microsoft.Storage/storageAccounts', parameters('storageAccountName')), '2021-02-01')]"
}
}
Use type and name in resourceId functions
Test name: Resources Should Not Be Ambiguous
This test is disabled, but the output shows that it passed. The best practice is to check your template for the following criteria:
A resourceId must include a resource type and resource name. This test finds all the template's resourceId
functions and verifies that the resource is used in the template with the correct syntax. Otherwise the function is considered ambiguous.
For example, a resourceId
function is considered ambiguous:
- When a resource isn't found in the template and a resource group isn't specified.
- If a resource includes a condition and a resource group isn't specified.
- If a related resource contains some but not all of the name segments. For example, a child resource contains more than one name segment. For more information, see resourceId remarks.
Use inner scope for nested deployment secure parameters
Test name: Secure Params In Nested Deployments
Use the nested template's expressionEvaluationOptions
object with inner
scope to evaluate expressions that contain secure parameters of type secureString
or secureObject
or list* functions such as listKeys
. If the outer
scope is used, expressions are evaluated in clear text within the parent template's scope. The secure value is then visible to anyone with access to the deployment history. The default value of expressionEvaluationOptions
is outer
.
For more information about nested templates, see Microsoft.Resources deployments and Expression evaluation scope in nested templates.
In Bicep, use Linter rule - secure params in nested deploy.
The following example fails because expressionEvaluationOptions
uses outer
scope to evaluate secure parameters or list*
functions.
"resources": [
{
"type": "Microsoft.Resources/deployments",
"apiVersion": "2021-04-01",
"name": "nestedTemplate",
"properties": {
"expressionEvaluationOptions": {
"scope": "outer"
}
}
}
]
The following example passes because expressionEvaluationOptions
uses inner
scope to evaluate secure parameters or list*
functions.
"resources": [
{
"type": "Microsoft.Resources/deployments",
"apiVersion": "2021-04-01",
"name": "nestedTemplate",
"properties": {
"expressionEvaluationOptions": {
"scope": "inner"
}
}
}
]
Next steps
- To learn about running the test toolkit, see Use ARM template test toolkit.
- For a Learn module that covers using the test toolkit, see Preview changes and validate Azure resources by using what-if and the ARM template test toolkit.
- To test parameter files, see Test cases for parameter files.
- For createUiDefinition tests, see Test cases for createUiDefinition.json.
- To learn about tests for all files, see Test cases for all files.