Trouble formatting valid JObject for .WithParameters() when deploying new Azure VM from ARM Template

人盡茶涼 提交于 2020-02-29 05:49:24

问题


Currently I'm having trouble with deploying an Azure VM from an ARM template using an Azure Function which is written in C#, whilst using a JObject, from the Newjonsoft.Json,Linq library, to provide parameters for the new VM.

The JObject.FromObject() method formulates parameters in the format "{"paramName": "paramValue"}", however I believe that it needs to be formulated as "{"paramName": { "value": "paramValue"}. I'm not sure if 'contentVersion' and '$schema' ARM Template parameters also need to be specified for this to work.

So far I have tried to formulate the object using a dynamic variable, which is then converted to string and parsed using JObject.Parse() method, however this only works to produce the same result as described before.

Azure Function code sample (not all code):

using Microsoft.Azure.Management.Fluent;
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;
using Microsoft.WindowsAzure.Storage.Table;
using System.Threading.Tasks;
using System;
using Microsoft.Rest.Azure;
using Newtonsoft.Json.Linq;

// Authenticate with Azure

IAzure azure = await 
    Authentication.AuthenticateWithAzure(azureVmDeploymentRequest.SubscriptionId);

// Get current datetime
string Datetime = DateTime.Now.ToString("yyyy-MM-ddHHmmss");

log.LogInformation("Initiating VM ARM Template Deployment");
var parameters = azureVmDeploymentRequest.ToArmParameters(
        subscriptionId: azureVmDeploymentRequest.SubscriptionId,
        imageReferencePublisher: azureVmDeploymentRequest.ImageReferencePublisher
    );

// AzNewVmRequestArmParametersMain is a custom object containing the 
// parameters needed for the ARM template, constructed with GET SET

var parametersMain = new AzNewVmRequestArmParametersMain
{
    parameters = parameters
};

var jParameters = JObject.FromObject(parameters);

// Deploy VM from ARM template if request is valid
var vmArmTemplateParams = new ARMTemplateDeploymentRequest
{
    DeploymentName = "vmDeployTfLCP-" + Datetime,
    ParametersObject = jParameters,
    ResourceGroupName = azureVmDeploymentRequest.ResourceGroupName,
    TemplateUri = Environment.GetEnvironmentVariable("VM_ARMTEMPLATE_URI"),
    SasToken = Environment.GetEnvironmentVariable("STORAGE_ACCOUNT_SASTOKEN")
};

ARM Template Deployment class code sample (not all code):

using Microsoft.Azure.Management.Fluent;
using System.Threading.Tasks;
using Microsoft.Azure.Management.ResourceManager.Fluent;
using System;
using Microsoft.Extensions.Logging;
using Microsoft.Rest.Azure;


// Formulate ARM template URI
var ArmTemplatePath = ARMTemplateDeploymentRequest.TemplateUri + ARMTemplateDeploymentRequest.SasToken;

deployment = azure.Deployments.Define(ARMTemplateDeploymentRequest.DeploymentName)
    .WithExistingResourceGroup(ARMTemplateDeploymentRequest.ResourceGroupName)
    .WithTemplateLink(ArmTemplatePath, "1.0.0.0")
    .WithParameters(ARMTemplateDeploymentRequest.ParametersObject)
    .WithMode(Microsoft.Azure.Management.ResourceManager.Fluent.Models.DeploymentMode.Incremental)
    .Create();

As an expected result, i'm expecting the code to simply initiate an ARM template deployment to a Azure Resource Group, however currently it is failing with the following message:

'The request content was invalid and could not be deserialized: 'Error converting value "parameterValue" to type 'Microsoft.WindowsAzure.ResourceStack.Frontdoor.Data.Definitions.DeploymentParameterDefinition'. Path 'properties.parameters.vNetResourceGroup', line 8, position 48.'.'


回答1:


According to my test, if you want to formulate the object using a dynamic variable, we need to create a new JObject. For example My template.json

{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
      "adminUsername": { "type": "string" },
      "adminPassword": { "type": "securestring" }
    },
    "variables": {
      "vnetID": "[resourceId('Microsoft.Network/virtualNetworks','myVNet123456')]", 
      "subnetRef": "[concat(variables('vnetID'),'/subnets/mySubnet')]"
    },
    "resources": [
      {
        "apiVersion": "2016-03-30",
        "type": "Microsoft.Network/publicIPAddresses",
        "name": "myPublicIPAddress123456",
        "location": "[resourceGroup().location]",
        "properties": {
          "publicIPAllocationMethod": "Dynamic",
          "dnsSettings": {
            "domainNameLabel": "myresourcegroupdns15896"
          }
        }
      },
      {
        "apiVersion": "2016-03-30",
        "type": "Microsoft.Network/virtualNetworks",
        "name": "myVNet123456",
        "location": "[resourceGroup().location]",
        "properties": {
          "addressSpace": { "addressPrefixes": [ "10.0.0.0/16" ] },
          "subnets": [
            {
              "name": "mySubnet",
              "properties": { "addressPrefix": "10.0.0.0/24" }
            }
          ]
        }
      },
      {
        "apiVersion": "2016-03-30",
        "type": "Microsoft.Network/networkInterfaces",
        "name": "myNic562354",
        "location": "[resourceGroup().location]",
        "dependsOn": [
          "[resourceId('Microsoft.Network/publicIPAddresses/', 'myPublicIPAddress123456')]",
          "[resourceId('Microsoft.Network/virtualNetworks/', 'myVNet')]"
        ],
        "properties": {
          "ipConfigurations": [
            {
              "name": "ipconfig1",
              "properties": {
                "privateIPAllocationMethod": "Dynamic",
                "publicIPAddress": { "id": "[resourceId('Microsoft.Network/publicIPAddresses','myPublicIPAddress123456')]" },
                "subnet": { "id": "[variables('subnetRef')]" }
              }
            }
          ]
        }
      },
      {
        "apiVersion": "2016-04-30-preview",
        "type": "Microsoft.Compute/virtualMachines",
        "name": "myVM",
        "location": "[resourceGroup().location]",
        "dependsOn": [
          "[resourceId('Microsoft.Network/networkInterfaces/', 'myNic562354')]"
        ],
        "properties": {
          "hardwareProfile": { "vmSize": "Standard_DS1" },
          "osProfile": {
            "computerName": "myVM",
            "adminUsername": "[parameters('adminUsername')]",
            "adminPassword": "[parameters('adminPassword')]"
          },
          "storageProfile": {
            "imageReference": {
              "publisher": "MicrosoftWindowsServer",
              "offer": "WindowsServer",
              "sku": "2012-R2-Datacenter",
              "version": "latest"
            },
            "osDisk": {
              "name": "myManagedOSDisk",
              "caching": "ReadWrite",
              "createOption": "FromImage"
            }
          },
          "networkProfile": {
            "networkInterfaces": [
              {
                "id": "[resourceId('Microsoft.Network/networkInterfaces','myNic562354')]"
              }
            ]
          }
        }
      }
    ]
  }

My code

JObject parametesObjectv1 = new JObject(
                    new JProperty("adminUsername",
                        new JObject(
                            new JProperty("value", "azureuser")
                        )
                    ),
                    new JProperty("adminPassword",
                        new JObject(
                            new JProperty("value", "Azure12345678")
                        )
                    )
                );



       var credentials = SdkContext.AzureCredentialsFactory.FromServicePrincipal(clientId, clientSecret, tenantId, AzureEnvironment.AzureGlobalCloud);
        var azure = Azure
                    .Configure()
                    .WithLogLevel(HttpLoggingDelegatingHandler.Level.Basic)
                    .Authenticate(credentials)
                    .WithSubscription(subscriptionId);
        if (azure.ResourceGroups.Contain(resourceGroupName) == false)
        {
            var resourceGroup = azure.ResourceGroups.Define(resourceGroupName)
                            .WithRegion(resourceGroupLocation)
                            .Create();
        }
        Console.WriteLine("start");
        var deployment = azure.Deployments.Define(deploymentName)
                    .WithExistingResourceGroup(resourceGroupName)
                    .WithTemplateLink(pathToTemplateFile, "1.0.0.0")
                    .WithParameters(parametesObjectv1)
                    .WithMode(Microsoft.Azure.Management.ResourceManager.Fluent.Models.DeploymentMode.Incremental)
                    .Create();

Besdes, if you do not want to use dynamic variable, you can directly provide the url of your parameter.json and template.json to create resource

var deployment = azure.Deployments.Define(deploymentName)
                    .WithExistingResourceGroup(resourceGroupName)
                    .WithTemplateLink(pathToTemplateFile, "1.0.0.0")
                    .WithParametersLink(pathToJsonFile, "1.0.0.0")
                    .WithMode(Microsoft.Azure.Management.ResourceManager.Fluent.Models.DeploymentMode.Incremental)
                    .Create();



回答2:


I was able to solve the problem by constructing parameter type based classes, and formulated a method to map out the parameter values to a ARM Parameter type.

ARM Template parameter classes extract:

namespace FuncApp.MSAzure.ARMTemplates.ARMParaneterTypes
{
    public class ParameterValueString
    {
        public string Value { get; set; }
    }

    public class ParameterValueArray
    {
        public string[] Value { get; set; }
    }

    public class ParameterBoolValue
    {
        public bool Value { get; set; }
    }
}

Mapping Class method extract:

   public static AzNewVmRequestArmParameters ToArmParameters(
            this AzNewVmRequest requestContent,
            string adminUsername,
            string adminPassword
        )
    {

            return new AzNewVmRequestArmParameters
            {
                location = new ParameterValueString {
                    Value = requestContent.Location
                },
                adminUsername = new ParameterValueString
                {
                    Value = adminUsername
                },
                adminPassword = new ParameterValueString
                {
                    Value = adminPassword
                },
            };
        }

'AzNewVmRequestArmParameters' Model class extract:

namespace FuncApp.MSAzure.VirtualMachines
{
    public class AzNewVmRequestArmParameters
    {

        public ParameterValueString location { get; set; }

        public ParameterValueString adminUsername { get; set; }

        public ParameterValueString adminPassword { get; set; }

    }
}

With these, i'm able to run the following code below (simplified) to formulate a valid jObject variable with the parameters that can be ready by the API:

var parameters = azureVmDeploymentRequest.ToArmParameters(
   adminUsername: "azurevmadmin",
   adminPassword: "P@ssword123!"
);
var jParameters = JObject.FromObject(parameters);


来源:https://stackoverflow.com/questions/57434148/trouble-formatting-valid-jobject-for-withparameters-when-deploying-new-azure

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!