Тестовые случаи для шаблонов ARM

В этой статье описываются тесты, которые выполняются с помощью набора средств тестирования для шаблонов Azure Resource Manager (шаблонов ARM). Здесь приводятся примеры, которые проходят и не проходят эти тесты с указанием имени каждого теста. Дополнительные сведения о выполнении тестов или какого-то определенного теста см. в разделе Параметры тестирования.

Использование правильной схемы

Имя теста: Схема DeploymentTemplate правильная

В шаблоне необходимо указать допустимое значение схемы.

Следующий пример не проходит тест, так как схема является недопустимой.

{
  "$schema": "https://schema.management.azure.com/schemas/2019-01-01/deploymentTemplate.json#",
}

Следующий пример отображает предупреждение, так как версия схемы 2015-01-01 является нерекомендуемой и не поддерживается.

{
  "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
}

Следующий пример проходит тест, используя правильную схему.

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
}

Свойству schema в шаблоне должна быть задана одна из следующих схем:

  • 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

Должны использоваться объявленные параметры

Имя теста: Должны быть указаны объявленные параметры

Этот тест находит параметры, которые не используются в шаблоне или имеют недопустимые выражения.

Чтобы сократить путаницу в шаблоне, удалите все параметры, которые определены, но не используются. Устранение неиспользуемых параметров упрощает развертывания шаблона, избавляя вас от необходимости вводить ненужные значения.

В Bicep используйте правило Linter — неиспользуемые параметры.

Следующий шаблон не проходит тест, так как в выражении со ссылкой на параметр отсутствует открывающая квадратная скобка ([).

"resources": [
  {
    "location": " parameters('location')]"
  }
]

Следующий пример содержит допустимое выражение и проходит тест.

"resources": [
  {
    "location": "[parameters('location')]"
  }
]

Защищенные параметры не могут иметь прописанные в коде значения по умолчанию

Имя теста: Строковые параметры безопасности не могут иметь значения по умолчанию

Не задавайте жестко запрограммированное значение по умолчанию для параметра безопасности в шаблоне. Защищенный параметр может иметь в качестве значения по умолчанию пустую строку или использовать в выражении функцию newGuid.

Типы secureString и secureObject используются для параметров с конфиденциальными значениями, например для паролей. Если параметр использует безопасный тип, значение параметра не заносится в журнал и не сохраняется в журнале развертывания. Это действие не позволит злоумышленнику обнаружить конфиденциальное значение.

Если вы предоставляете значение по умолчанию, оно может быть обнаружено любым пользователем, имеющим доступ к шаблону или журналу развертывания.

В Bicep используйте правило Linter — безопасный параметр по умолчанию.

Следующий пример не проходит тест.

"parameters": {
  "adminPassword": {
    "defaultValue": "HardcodedPassword",
    "type": "secureString"
  }
}

Пример ниже проходит тест.

"parameters": {
  "adminPassword": {
    "type": "secureString"
  }
}

Следующий пример проходит тест, так как в нем используется функция newGuid.

"parameters": {
  "secureParameter": {
    "type": "secureString",
    "defaultValue": "[newGuid()]"
  }
}

URL-адреса окружения не могут иметь прописанные в коде значения

Имя теста: DeploymentTemplate не должен содержать жестко заданный URI

Не следует прописывать в коде URL-адреса окружения в шаблоне. Вместо этого используйте функцию environment для динамического получения URL-адресов во время развертывания. Список заблокированных узлов URL-адресов см. в разделе Тестовый случай.

В Bicep используйте правило Linter — url-адрес жестко закодированных сред.

Следующий пример не проходит тест, так как URL-адрес прописан в коде.

"variables":{
  "AzureURL":"https://management.azure.com"
}

Тест также завершается ошибкой при использовании concat или uri.

"variables":{
  "AzureSchemaURL1": "[concat('https://','gallery.azure.com')]",
  "AzureSchemaURL2": "[uri('gallery.azure.com','test')]"
}

Следующий пример проходит тест.

"variables": {
  "AzureSchemaURL": "[environment().gallery]"
}

В расположении используется параметр

Имя теста: Расположение не должно быть жестко запрограммировано

Чтобы задать расположение ресурса, в шаблонах нужно указать параметр location с типом string. В основном шаблоне (azuredeploy.json или mainTemplate.json) этот параметр может по умолчанию содержать расположение группы ресурсов. В связанных или вложенных шаблонах параметр location не должен содержать расположение по умолчанию.

Пользователи шаблонов могут иметь ограниченный доступ к регионам, в которых они могут создавать ресурсы. Прописанное в коде расположение ресурса может привести к тому, что пользователь не сможет создать ресурс. Выражение "[resourceGroup().location]" будет блокировать пользователей, если группа ресурсов создана в недоступном для них регионе. Заблокированные пользователи не смогут использовать шаблон.

Если указать параметр location, в котором по умолчанию задано расположение группы ресурсов, пользователи смогут использовать значение по умолчанию, если это удобно, или указать другое расположение.

В Bicep используйте правило Linter — выражения расположения за пределами значений параметров по умолчанию.

Следующий пример не проходит тест, так как в шаблоне location имеет значение 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"
      }
    }
  ]
}

Следующий пример использует параметр 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": {}
}

Следующий пример проходит этот тест, если шаблон используется как основной. Создайте параметр, который в качестве значения по умолчанию использует расположение группы ресурсов, но позволяет пользователям указать другое значение.

{
  "$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": {}
}

Примечание.

Если предыдущий пример используется в качестве связанного шаблона, он не пройдет тест. При использовании в качестве связанного шаблона удалите значение по умолчанию.

Ресурсы должны иметь расположение

Имя теста: Ресурсы должны иметь расположение

Для расположения ресурса должно быть задано выражение шаблона или global. В выражении шаблона обычно используется параметр location, описанный в разделе В расположении используется параметр.

В Bicep используйте правило Linter — не закодированные расположения.

Следующий пример не проходит тест, так как location не является выражением или 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": {}
}

Следующий пример проходит тест, так как ресурс location имеет значение 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": {}
}

Следующий пример также проходит тест, так как параметр location использует выражение. Ресурс location использует значение выражения.

{
  "$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": {}
}

Размер виртуальной машины должен быть параметром

Имя теста: Размер виртуальной машины должен быть параметром

Не следует прописывать в коде vmSize для объекта hardwareProfile. Этот тест завершается ошибкой, если hardwareProfile пропускается или содержит прописанное в коде значение. Предоставьте параметр, чтобы пользователи шаблона могли изменять размер развернутой виртуальной машины. См. дополнительные сведения о Microsoft.Compute virtualMachines.

Следующий пример не проходит тест, так как у объекта hardwareProfile параметр vmSize имеет прописанное в коде значение.

"resources": [
  {
    "type": "Microsoft.Compute/virtualMachines",
    "apiVersion": "2020-12-01",
    "name": "demoVM",
    "location": "[parameters('location')]",
    "properties": {
      "hardwareProfile": {
        "vmSize": "Standard_D2_v3"
      }
    }
  }
]

Этот пример проходит тест, если параметр задает значение для vmSize:

"parameters": {
  "vmSizeParameter": {
    "type": "string",
    "defaultValue": "Standard_D2_v3",
    "metadata": {
      "description": "Size for the virtual machine."
    }
  }
}

Затем hardwareProfile применяет выражение для vmSize, что указать ссылку на значение этого параметра.

"resources": [
  {
    "type": "Microsoft.Compute/virtualMachines",
    "apiVersion": "2020-12-01",
    "name": "demoVM",
    "location": "[parameters('location')]",
    "properties": {
      "hardwareProfile": {
        "vmSize": "[parameters('vmSizeParameter')]"
      }
    }
  }
]

Минимальное и максимальное значения являются числами

Имя теста: Минимальное и максимальное значения являются числами

Если вы указываете для параметра minValue и maxValue, они должны иметь числовые значения. minValue и maxValue всегда используются вместе, иначе тест завершается ошибкой.

Следующий пример не проходит тест, так как minValue и maxValue имеют строковые значения.

"exampleParameter": {
  "type": "int",
  "minValue": "0",
  "maxValue": "10"
}

Следующий пример не проходит тест, так как используется только minValue.

"exampleParameter": {
  "type": "int",
  "minValue": 0
}

Следующий пример проходит тест, так как minValue и maxValue имеют числовые значения.

"exampleParameter": {
  "type": "int",
  "minValue": 0,
  "maxValue": 10
}

Параметр артефактов определен правильно

Имя теста: Параметр артефактов

При включении параметров для _artifactsLocation и _artifactsLocationSasToken используйте правильные значения по умолчанию и типы. Для прохождения этого теста должны быть выполнены следующие условия.

  • Если вы предоставляете один параметр, необходимо указать и другой.
  • _artifactsLocation должен иметь тип string.
  • _artifactsLocation должен иметь значение по умолчанию в основном шаблоне.
  • _artifactsLocation не может иметь значение по умолчанию во вложенном шаблоне.
  • _artifactsLocation должен иметь значение по умолчанию "[deployment().properties.templateLink.uri]" или URL-адрес репозитория необработанных данных.
  • _artifactsLocationSasToken должен иметь тип secureString.
  • _artifactsLocationSasToken может иметь в качестве значения по умолчанию только пустую строку.
  • _artifactsLocationSasToken не может иметь значение по умолчанию во вложенном шаблоне.

В Bicep используйте правило Linter — параметры артефактов.

Необходимо использовать объявленные переменные

Имя теста: Переменные должны быть указаны по ссылке

Этот тест ищет переменные, которые не используются в шаблоне или используются в недопустимом выражении. Чтобы сократить путаницу в шаблоне, удалите все переменные, которые определены, но не используются.

Переменные, которые используют элемент copy для перебора значений, должны иметь ссылки. Дополнительные сведения см. в статье Итерация переменной в шаблонах ARM.

В Bicep используйте правило Linter — неиспользуемые переменные.

Следующий пример не проходит тест, так как в нем нет ссылок на переменную, которая использует элемент copy.

{
  "$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": {}
}

Следующий шаблон не проходит тест, так как в выражении со ссылкой на переменную отсутствует открывающая квадратная скобка ([).

"outputs": {
  "outputVariable": {
    "type": "string",
    "value": " variables('varExample')]"
  }
}

Следующий пример проходит тест, так как в 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')]"
    }
  }
}

Следующий пример содержит допустимое выражение и проходит тест.

"outputs": {
  "outputVariable": {
    "type": "string",
    "value": "[variables('varExample')]"
  }
}

Динамическая переменная не должна использовать Concat

Имя теста: Ссылки на динамические переменные не должны использовать Concat

Иногда необходимо динамически создать переменную на основе значения другой переменной или параметра. Не используйте функцию concat при задании значения. Вместо этого следует использовать объект, в котором есть доступные параметры, и динамически получать одно из свойств объекта во время развертывания.

Следующий пример проходит тест. Переменная currentImage задается динамически во время развертывания.

{
  "$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')]"
    }
  }
}

Использование последней версии API

Имя теста: Значением apiVersions должна быть последняя версия

Версия API для каждого ресурса должна указывать свежую версию, прописанную в коде в виде строки. Этот тест сравнивает версию API в приложении с версиями поставщика ресурсов в кэше набора средств. Свежей считается версия API, созданная менее чем за два года до даты выполнения теста. Не используйте предварительную версию, если доступна более свежая.

Предупреждение о том, что версия API не найдена, указывает, что версия не включена в кэш набора инструментов. Предупреждение может возникать, если в соответствии с рекомендациями используется самая свежая версия API.

См. дополнительные сведения о кэше набора средств.

В Bicep используйте правило Linter. Используйте последние версии API.

Следующий пример не проходит тест, так как версия API в нем старше двух лет.

"resources": [
  {
    "type": "Microsoft.Storage/storageAccounts",
    "apiVersion": "2019-06-01",
    "name": "storageaccount1",
    "location": "[parameters('location')]"
  }
]

Следующий пример не проходит тест, так как используется предварительная версия при наличии более свежей.

"resources": [
  {
    "type": "Microsoft.Storage/storageAccounts",
    "apiVersion": "2020-08-01-preview",
    "name": "storageaccount1",
    "location": "[parameters('location')]"
  }
]

Следующий пример проходит тест, так как используется свежая версия, которая не является предварительной.

"resources": [
  {
    "type": "Microsoft.Storage/storageAccounts",
    "apiVersion": "2021-02-01",
    "name": "storageaccount1",
    "location": "[parameters('location')]"
  }
]

Использование прописанной в коде версии API

Имя теста: apiVersions поставщиков запрещены

Версия API для типа ресурсов определяет доступные свойства. Укажите в шаблоне жестко запрограммированную версию API. Не извлекайте версию API, которая определяется в процессе развертывания, так как не известно, какие свойства доступны.

Следующий пример не проходит тест.

"resources": [
  {
    "type": "Microsoft.Compute/virtualMachines",
    "apiVersion": "[providers('Microsoft.Compute', 'virtualMachines').apiVersions[0]]",
    ...
  }
]

Следующий пример проходит тест.

"resources": [
  {
    "type": "Microsoft.Compute/virtualMachines",
    "apiVersion": "2020-12-01",
    ...
  }
]

Свойства не могут быть пустыми

Имя теста: В шаблоне не должно быть пустых свойств

Не прописывайте в коде для свойств пустое значение. Пустыми являются значения NULL, пустые строки, объекты или массивы. Если свойству присвоено пустое значение, удалите это свойство из шаблона. Но вы можете присвоить свойству пустое значение во время развертывания, например с помощью параметра.

Свойство template во вложенном шаблоне может содержать пустые свойства. Дополнительные сведения о вложенных шаблонах см. в статье Развертывания Microsoft.Resources.

Следующий пример не проходит тест, так как в нем есть пустые свойства.

"resources": [
  {
    "type": "Microsoft.Storage/storageAccounts",
    "apiVersion": "2021-01-01",
    "name": "storageaccount1",
    "location": "[parameters('location')]",
    "sku": {},
    "kind": ""
  }
]

Следующий пример проходит тест, так как свойства в нем содержат значения.

"resources": [
  {
    "type": "Microsoft.Storage/storageAccounts",
    "apiVersion": "2021-01-01",
    "name": "storageaccount1",
    "location": "[parameters('location')]",
    "sku": {
      "name": "Standard_LRS",
      "tier": "Standard"
    },
    "kind": "Storage"
  }
]

Использование функций идентификатора ресурса

Имя теста: Идентификаторы должны быть производными от идентификаторов ресурсов

При указании идентификатора ресурса используйте одну из функций идентификатора ресурса. Допустимые функции:

Не используйте функцию concat для создания идентификатора ресурса.

В Bicep используйте правило Linter — используйте функции идентификатора ресурса.

Следующий пример не проходит тест.

"networkSecurityGroup": {
    "id": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', resourceGroup().name, '/providers/Microsoft.Network/networkSecurityGroups/', variables('networkSecurityGroupName'))]"
}

Пример ниже проходит тест.

"networkSecurityGroup": {
    "id": "[resourceId('Microsoft.Network/networkSecurityGroups', variables('networkSecurityGroupName'))]"
}

Функция ResourceId имеет правильные параметры

Имя теста: Идентификаторы ресурсов не должны содержать

При создании идентификаторов ресурсов не используйте ненужные функции для необязательных параметров. По умолчанию функция resourceId использует текущую подписку и группу ресурсов. Указывать эти значения не нужно.

Следующий пример не проходит тест, так как не нужно указывать идентификатор текущей подписки и имя группы ресурсов.

"networkSecurityGroup": {
    "id": "[resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.Network/networkSecurityGroups', variables('networkSecurityGroupName'))]"
}

Пример ниже проходит тест.

"networkSecurityGroup": {
    "id": "[resourceId('Microsoft.Network/networkSecurityGroups', variables('networkSecurityGroupName'))]"
}

Этот тест применяется к:

Для reference и list* тест завершается ошибкой при использовании concat для создания идентификатора ресурса.

Рекомендации по dependsOn

Имя теста: Рекомендации по DependsOn

При задании зависимостей развертывания не используйте функцию if для проверки условия. Если один ресурс зависит от ресурса, который развертывается условно, установите зависимость так же, как и для любого другого ресурса. Если условный ресурс не развернут, Azure Resource Manager автоматически удаляет его из числа необходимых зависимостей.

Элемент dependsOn не может начинаться с функции concat.

В Bicep используйте правило Linter — ненужные записи не зависят от элементов.

Следующий пример не проходит тест, так как содержит функцию if.

"dependsOn": [
  "[if(equals(parameters('newOrExisting'),'new'), variables('storageAccountName'), '')]"
]

Следующий пример не проходит тест, так как начинается с concat.

"dependsOn": [
  "[concat(variables('storageAccountName'))]"
]

Следующий пример проходит тест.

"dependsOn": [
  "[variables('storageAccountName')]"
]

Вложенные или связанные развертывания не могут использовать отладку

Имя теста: Ресурсы развертывания не должны быть отладкой

Если вы определяете вложенный или связанный шаблон с типом ресурса Microsoft.Resources/deployments, для него можно включить отладку. Отладка помогает выполнить тестирование шаблона, если в нем есть конфиденциальная информация. Перед развертыванием шаблона в рабочей среде отключите отладку. Вы можете просто удалить объект debugSetting или указать для его свойства detailLevel значение none.

Следующий пример не проходит тест.

"debugSetting": {
  "detailLevel": "requestContent"
}

Следующий пример проходит тест.

"debugSetting": {
  "detailLevel": "none"
}

Имя пользователя администратора не может быть литеральным значением

Имя теста: Значение adminUsername не должен быть литералом

Если вы задаете значение adminUserName, не используйте литеральное значение. Создайте параметр для имени пользователя и укажите выражение со ссылкой на значение этого параметра.

В Bicep используйте правило Linter— имя пользователя администратора не должно быть литеральным.

Следующий пример не проходит тест, так как содержит литеральное значение.

"osProfile":  {
  "adminUserName": "myAdmin"
}

Следующий пример проходит тест так как используется выражение.

"osProfile": {
  "adminUsername": "[parameters('adminUsername')]"
}

Использование последней версии образа виртуальной машины

Имя теста: Должна использоваться последняя версия образа виртуальной машины

Этот тест отключен, но в выходных данных отображается как пройденный. Мы рекомендуем проверить шаблон на соответствие следующим критериям:

Если в шаблоне есть виртуальная машина с образом, она должна использовать последнюю версию образа.

В Bicep используйте правило Linter — используйте стабильный образ виртуальной машины.

Использование стабильных образов виртуальных машин

Имя теста: Нельзя использовать предварительные версии виртуальных машин

Виртуальные машины не должны использовать предварительные образы. Этот тест проверяет в storageProfile, не использует ли imageReference строковое значение с подстрокой preview и не используется ли подстрока preview в свойствах imageReference: offer, sku или version.

Дополнительные сведения о свойстве imageReference см. в документации по Microsoft.Compute virtualMachines и Microsoft.Compute virtualMachineScaleSets.

В Bicep используйте правило Linter — используйте стабильный образ виртуальной машины.

Следующий пример не проходит тест, так как imageReference является строкой и содержит preview.

"properties": {
  "storageProfile": {
    "imageReference": "latest-preview"
  }
}

Следующий пример не проходит тест, так как содержит preview в значениях offer, sku или version.

"properties": {
  "storageProfile": {
    "imageReference": {
      "publisher": "Canonical",
      "offer": "UbuntuServer_preview",
      "sku": "16.04-LTS-preview",
      "version": "preview"
    }
  }
}

Следующий пример проходит тест.

"storageProfile": {
  "imageReference": {
    "publisher": "Canonical",
    "offer": "UbuntuServer",
    "sku": "16.04-LTS",
    "version": "latest"
  }
}

Не использовать расширение ManagedIdentity

Имя теста: Нельзя использовать расширение ManagedIdentity

Не применяйте расширение ManagedIdentity к виртуальной машине. Расширение было признано устаревшим в 2019 г. и больше не должно использоваться.

Выходные данные не могут содержать секреты

Имя теста: Выходные данные не должны содержать секреты

Не включайте в раздел outputs значения, которые могут предоставлять секреты. Например, защитите все параметры с типом secureString или secureObject, а также функции list*, например listKeys.

Выходные данные шаблона хранятся в журнале развертывания, поэтому злоумышленник может найти эту информацию.

В Bicep используйте правило Linter. Выходные данные не должны содержать секреты.

Следующий пример не проходит тест, так как в его выходных данных содержится защищенный параметр.

{
  "$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'))]"
    }
  }
}

Следующий пример не проходит тест, поскольку в его выходных данных используется функция list*.

{
  "$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')]"
    }
  }
}

Использование protectedSettings для секретов commandToExecute

Имя теста: CommandToExecute должны использовать ProtectedSettings для секретов

Для ресурсов с типом CustomScript используйте зашифрованное значение protectedSettings, если commandToExecute содержит пароли или другие секретные данные. Секретные данные могут содержаться в параметрах с типом secureString или secureObject, функциях list* (например, listKeys) и пользовательских скриптах.

Не используйте секретные данные в объекте settings, так как он использует открытый текст. Дополнительные сведения см. в разделах документации по Microsoft.Compute virtualMachines/extensions для Windows и Linux.

В Bicep используйте правило Linter — используйте защищенные наборыSettings для секретов commandToExecute.

Следующий пример не проходит тест, так как settings использует commandToExecute для защищенного параметра.

"parameters": {
  "adminPassword": {
    "type": "secureString"
  }
}
...
"properties": {
  "type": "CustomScript",
  "settings": {
    "commandToExecute": "[parameters('adminPassword')]"
  }
}

Следующий пример не проходит тест, так как settings использует commandToExecute с функцией listKeys.

"properties": {
  "type": "CustomScript",
  "settings": {
    "commandToExecute": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('storageName')), '2021-02-01')]"
  }
}

Следующий пример проходит тест, так как protectedSettings использует commandToExecute для защищенного параметра.

"parameters": {
  "adminPassword": {
    "type": "secureString"
  }
}
...
"properties": {
  "type": "CustomScript",
  "protectedSettings": {
    "commandToExecute": "[parameters('adminPassword')]"
  }
}

Следующий пример проходит тест, так как protectedSettings использует commandToExecute с функцией listKeys.

"properties": {
  "type": "CustomScript",
  "protectedSettings": {
    "commandToExecute": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('storageName')), '2021-02-01')]"
  }
}

Использование свежих версий API в ссылочных функциях

Имя теста: apiVersions Should Be Recent In Reference Functions (В ссылочных функциях должны быть свежие версии API)

Версия API, используемая в ссылочной функции, должна быть свежей и не должна быть предварительной. Этот тест сравнивает версию API в приложении с версиями поставщика ресурсов в кэше набора средств. Свежей считается версия API, созданная менее чем за два года до даты выполнения теста.

Предупреждение о том, что версия API не найдена, указывает, что версия не включена в кэш набора инструментов. Предупреждение может возникать, если в соответствии с рекомендациями используется самая свежая версия API.

См. дополнительные сведения о кэше набора средств.

Следующий пример не проходит тест, так как версия API в нем старше двух лет.

"outputs": {
  "stgAcct": {
    "type": "string",
    "value": "[reference(resourceId(parameters('storageResourceGroup'), 'Microsoft.Storage/storageAccounts', parameters('storageAccountName')), '2019-06-01')]"
  }
}

Следующий пример не проходит тест, так как используется API предварительной версии.

"outputs": {
  "stgAcct": {
    "type": "string",
    "value": "[reference(resourceId(parameters('storageResourceGroup'), 'Microsoft.Storage/storageAccounts', parameters('storageAccountName')), '2020-08-01-preview')]"
  }
}

Следующий пример проходит тест, так как версия API не старше двух лет и не является предварительной версией.

"outputs": {
  "stgAcct": {
    "type": "string",
    "value": "[reference(resourceId(parameters('storageResourceGroup'), 'Microsoft.Storage/storageAccounts', parameters('storageAccountName')), '2021-02-01')]"
  }
}

Использование типа и имени в функциях resourceId

Имя теста: Resources Should Not Be Ambiguous (Ресурсы не должны быть неоднозначными)

Этот тест отключен, но в выходных данных отображается как пройденный. Мы рекомендуем проверить шаблон на соответствие следующим критериям:

Значение resourceId должно содержать тип и имя ресурса. Этот тест ищет все функции resourceId в шаблоне и проверяет, что ресурс в шаблоне используется с правильным синтаксисом. В противном случае функция считается неоднозначной.

Например, функция resourceId будет неоднозначной в следующих случаях:

  • если ресурс не обнаружен в шаблоне и группа ресурсов не указана;
  • если ресурс содержит условие и группа ресурсов не указана;
  • если связанный ресурс содержит только часть сегментов имени. Например, дочерний ресурс содержит более одного сегмента имени. См. дополнительные сведения в примечаниях о resourceId.

Использование внутренней области действия для защищенных параметров во вложенных развертываниях

Имя теста: Secure Params In Nested Deployments (Защищенные параметры во вложенных развертываниях)

Используйте объект expressionEvaluationOptions во вложенном шаблоне с областью действия inner для оценки выражений, которые содержат защищенные параметры с типами secureString и secureObject или функции list*, например listKeys. Если используется область действия outer, выражения оцениваются в формате обычного текста в области действия родительского шаблона. Защищенный параметр в этом случае становится виден всем, кто имеет доступ к журналу развертывания. Значение expressionEvaluationOptions по умолчанию — outer.

Дополнительные сведения о вложенных шаблонах см. в статьях Развертывания Microsoft.Resources и Область вычислений выражения во вложенных шаблонах.

В Bicep используйте правило Linter — безопасные парамы в вложенном развертывании.

Следующий пример не проходит тест, так как expressionEvaluationOptions использует область действия outer для оценки защищенных параметров или функций list*.

"resources": [
  {
    "type": "Microsoft.Resources/deployments",
    "apiVersion": "2021-04-01",
    "name": "nestedTemplate",
    "properties": {
      "expressionEvaluationOptions": {
        "scope": "outer"
      }
    }
  }
]

Следующий пример проходит тест, так как expressionEvaluationOptions использует область действия inner для оценки защищенных параметров или функций list*.

"resources": [
  {
    "type": "Microsoft.Resources/deployments",
    "apiVersion": "2021-04-01",
    "name": "nestedTemplate",
    "properties": {
      "expressionEvaluationOptions": {
        "scope": "inner"
      }
    }
  }
]

Следующие шаги