When using nested deployments in Azure, template expressions can be evaluated within the scope of the parent template or the scope of the nested template. If such a template expression evaluates a secure value of the parent template, it is possible to expose this value in the deployment history.

Why is this an issue?

Parameters with the type securestring and secureObject are designed to pass sensitive data to the resources being deployed. Secure parameters cannot be accessed after the deployment is completed: they can neither be logged nor used as an output.

When used in nested deployments, however, it is possible to embed secure parameters in such a way they can be visible afterward.

What is the potential impact?

If the nested deployment contains a secure parameter in this way, then the value of this parameter may be readable in the deployment history. This can lead to important credentials being leaked to unauthorized accounts.

How to fix it in ARM Templates

By setting properties.expressionEvaluationOptions.scope to Inner in the parent template, template evaluations are limited to the scope of the nested template. This makes it impossible to expose secure parameters defined in the parent template.

Code examples

Noncompliant code example

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "adminUsername": {
      "type": "securestring",
      "defaultValue": "[newGuid()]"
    }
  },
  "resources": [
    {
      "name": "example",
      "type": "Microsoft.Resources/deployments",
      "apiVersion": "2022-09-01",
      "properties": {
        "mode": "Incremental",
        "template": {
          "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
          "contentVersion": "1.0.0.0",
          "resources": [
            {
              "name": "example",
              "type": "Microsoft.Compute/virtualMachines",
              "apiVersion": "2022-11-01",
              "properties": {
                "osProfile": {
                  "adminUsername": "[parameters('adminUsername')]"
                }
              }
            }
          ]
        }
      }
    }
  ]
}

Compliant solution

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "resources": [
    {
      "name": "example",
      "type": "Microsoft.Resources/deployments",
      "apiVersion": "2022-09-01",
      "properties": {
        "expressionEvaluationOptions": {
          "scope": "Inner"
        },
        "mode": "Incremental",
        "template": {
          "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
          "contentVersion": "1.0.0.0",
          "parameters": {
            "adminUsername": {
              "type": "securestring",
              "defaultValue": "[newGuid()]"
            }
          },
          "resources": [
            {
              "name": "example",
              "type": "Microsoft.Compute/virtualMachines",
              "apiVersion": "2022-11-01",
              "properties": {
                "osProfile": {
                  "adminUsername": "[parameters('adminUsername')]"
                }
              }
            }
          ]
        }
      }
    }
  ]
}

How to fix it in Bicep

In Bicep, it is recommended to use modules instead of a Microsoft.Resources/deployments resource. Modules allow for reuse, improve readability by encapsulating different parts of a deployment and therefore reduce the risk for errors. They also do not leakage of secure parameters from a parent resource.

If it is not possible to use modules, this issue can be fixed by setting properties.expressionEvaluationOptions.scope to Inner in the Microsoft.Resources/deployments resource. By setting this property, template evaluations are limited to the scope of the nested template. This makes it impossible to expose secure parameters defined in the parent template.

Code examples

Noncompliant code example

@secure()
param adminUsername string = newGuid()

resource example 'Microsoft.Resources/deployments@2022-09-01' = {
  name: 'example-deployment'
  properties: {
    // Noncompliant: expressionEvaluationOptions is missing (defaults to 'Outer')
    mode: 'Incremental'
    template: {
      '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#'
      contentVersion: '1.0.0.0'
      resources: [
        {
          apiVersion: '2023-03-01'
          type: 'Microsoft.Compute/virtualMachines'
          name: 'example-vm'
          properties: {
            osProfile: {
              adminUsername: adminUsername
            }
          }
        }
      ]
    }
  }
}

Compliant solution

// main.bicep
module example 'vm.bicep' = {
  name: 'example-deployment'
}

// vm.bicep
@secure()
param adminUsername string = newGuid()

resource vmExample 'Microsoft.Compute/virtualMachines@2023-03-01' = {
  name: 'example-vm'
  properties: {
    osProfile: {
      adminUsername: adminUsername
    }
  }
}

Resources

Documentation

Standards