使用Python部署Azure Key Vault ARM模板时参数文件报错求助
问题场景
使用ARM模板部署Azure Key Vault,基础模板基于官方的Key Vault模块。无参数时部署成功,但添加参数文件后触发InvalidTemplate错误,提示参数文件中的$schema、contentVersion、parameters不是原模板支持的参数,无法定位问题。
参数文件内容
{ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", "contentVersion": "1.0.0.0", "parameters": { "vaultName": { "value": <key vault name> } } }
错误详情
(InvalidTemplate) 部署模板验证失败:'参数文件中的模板参数'$schema, contentVersion, parameters'无效;这些参数不存在于原始模板中,因此无法在部署时提供。此模板仅支持以下参数:'vaultName, location, enabledForDeployment, enabledForDiskEncryption, enabledForTemplateDeployment, enablePurgeProtection, enableRbacAuthorization, enableSoftDelete, softDeleteRetentionInDays, tenantId, networkRuleBypassOptions, NetworkRuleAction, ipRules, accessPolicies, virtualNetworkRules, skuName, tags'。
#代码: InvalidTemplate
#消息: 部署模板验证失败:'参数文件中的模板参数'$schema, contentVersion, parameters'无效;这些参数不存在于原始模板中,因此无法在部署时提供。此模板仅支持以下参数:'vaultName, location, enabledForDeployment, enabledForDiskEncryption, enabledForTemplateDeployment, enablePurgeProtection, enableRbacAuthorization, enableSoftDelete, softDeleteRetentionInDays, tenantId, networkRuleBypassOptions, NetworkRuleAction, ipRules, accessPolicies, virtualNetworkRules, skuName, tags'。
附加信息:类型: TemplateViolation
信息: {
"lineNumber": 0,
"linePosition": 0,
"path": ""
}
对应的Python代码
import os from azure.identity import DefaultAzureCredential from azure.mgmt.resource import ResourceManagementClient from azure.mgmt.resource.resources.models import DeploymentMode from azure.mgmt.resource.resources.models import Deployment from azure.mgmt.resource.resources.models import DeploymentProperties from miscellaneous.logger import Logger from msrestazure.azure_cloud import get_cloud_from_metadata_endpoint from uuid6 import uuid7 class AzureConnection(object): def __init__(self, subscriptionId, resourceGroup): self.logger = Logger("Azure Connection") self.logger.info("Retrieving the list of available endpoint") endpoints = get_cloud_from_metadata_endpoint(os.environ.get("ARM_ENDPOINT")) self.subscriptionId = subscriptionId self.resourceGroup = resourceGroup self.credentials = DefaultAzureCredential() self.logger.info("Creating a client for deploying resources on subscription {}".format(self.subscriptionId)) self.client = ResourceManagementClient(self.credentials, self.subscriptionId, base_url=endpoints.endpoints.resource_manager) self.logger.success("Client was successfully created") def deploy(self, template): resources = "" for resource in template.get("resources"): resources += "\n\t {}".format(resource.get("type")) self.logger.info("The following resources: {}\nwill be deployed".format(resources)) deploymentProperties = DeploymentProperties( mode=DeploymentMode.incremental, template=template ) self.logger.info("Attempting deploy operation") try: deployment_async_operation = self.client.deployments.begin_create_or_update( self.resourceGroup, uuid7(), Deployment(properties=deploymentProperties) ) except: self.logger.error("The resources could not be deployed"); self.logger.success("Resources were successfully deployed") def deployWithParameters(self, template, parameters): resources = "" for resource in template.get("resources"): resources += "\n\t {}".format(resource.get("type")) self.logger.info("The following resources: {}\nwill be deployed".format(resources)) parameters = {k: {"value": v} for k, v in parameters.items()} deploymentProperties = DeploymentProperties( mode=DeploymentMode.incremental, template=template, parameters=parameters ) self.logger.info("Attempting deploy operation") deployment_async_operation = self.client.deployments.begin_create_or_update( self.resourceGroup, uuid7(), Deployment(properties=deploymentProperties) ) from dotenv import load_dotenv load_dotenv() azureConnection = AzureConnection(os.environ.get("AZURE_SUBSCRIPTION_ID"), os.environ.get("AZURE_RESOURCE_GROUP")) with open((os.path.dirname(__file__), "templates", <fileName>), "r") as file: template = json.load(file) with open((os.path.dirname(__file__), "parameters", <fileName>), "r") as file: json = json.load(file) deployment = azureConnection.deployWithParameters(template, parameter)
核心问题与修复方案
问题根源
代码中直接将整个参数文件的JSON对象传递给部署方法,而ARM部署API要求仅传递参数文件内parameters字段下的实际参数内容。参数文件的顶层字段($schema、contentVersion、parameters)是参数文件的元数据,并非模板定义的参数,被误传后导致验证失败。
具体修复步骤
修正参数读取逻辑
读取参数文件后,提取其中的parameters字段内容,而非传递整个文件对象,同时修复变量名冲突和路径拼接错误:import json # 确保导入json模块 # ... # 读取模板文件 with open(os.path.join(os.path.dirname(__file__), "templates", "<fileName>"), "r") as file: template = json.load(file) # 读取参数文件并提取实际参数 with open(os.path.join(os.path.dirname(__file__), "parameters", "<fileName>"), "r") as file: param_file_content = json.load(file) actual_parameters = param_file_content["parameters"] # 执行部署 deployment = azureConnection.deployWithParameters(template, actual_parameters)移除冗余的参数格式化代码
参数文件中的参数已经是{"value": xxx}的格式,deployWithParameters方法内的parameters = {k: {"value": v} for k, v in parameters.items()}会导致格式嵌套错误,直接删除该行代码。修复文件路径错误
原代码用元组拼接路径会触发类型错误,统一使用os.path.join拼接文件路径。
验证逻辑
修正后,传递给部署API的参数将只包含模板支持的字段(如vaultName),格式与模板定义完全匹配,不会再将参数文件的元数据当作模板参数传递,即可解决InvalidTemplate错误。
内容的提问来源于stack exchange,提问作者delucaezequiel

