1
votes

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.'.'

2
Can you give a sample git repo ? then i can take a lookHariHaran
Can you post the expected JSON of the params? And also the one you are getting here?bit

2 Answers

0
votes

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();
0
votes

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);