Score:1

Update JSON Payload using Powershell and Nutanix API

cn flag

I'm currently writing a script that allow to update Nutanix VM Categories. Categories format is key:value and a VM may have no categories, 1 or multiple.

Process to do that is quite simple : first I create a request to get an existing VM informations (GET) (then retrieve its UUID).

In a second time, i create another request to update an existing VM (PUT) by using the UUID retrieved earlier, and changing what i need to change (update category but it should be add disks, nic etc...)

At the moment, request to get VM is simple and works smoothly but I got an issue while i need to use the second request to update the VM. Here we are :

Payload used for this request looks like this in JSON

{
  "spec": {
    "cluster_reference": {
      "kind": "cluster",
      "name": "my_cluster",
      "uuid": "0004f63d-6664-35ce-0126-88e9a456d3fc"
    },
    "name": "test-vm",
    "resources": {
      "num_threads_per_core": 1,
      "vnuma_config": {
        "num_vnuma_nodes": 0
      },
      "serial_port_list": [],
      "nic_list": [
        {
          "nic_type": "NORMAL_NIC",
          "uuid": "334e302a-db6a-48fb-b138-c3e3b2f1d8f6",
          "ip_endpoint_list": [
            {
              "ip": "10.230.172.84",
              "type": "ASSIGNED"
            }
          ],
          "vlan_mode": "ACCESS",
          "mac_address": "50:6b:8d:c5:6c:f0",
          "subnet_reference": {
            "kind": "subnet",
            "name": "vlanXXX",
            "uuid": "6de66666-05ec-46ac-6666-aea60e8e831f"
          },
          "is_connected": true,
          "trunked_vlan_list": []
        }
      ],
      "num_vcpus_per_socket": 1,
      "num_sockets": 1,
      "gpu_list": [],
      "is_agent_vm": false,
      "memory_size_mib": 4096,
      "boot_config": {
        "boot_device_order_list": [
          "CDROM",
          "DISK",
          "NETWORK"
        ],
        "boot_type": "LEGACY"
      },
      "hardware_clock_timezone": "UTC",
      "power_state_mechanism": {
        "guest_transition_config": {
          "should_fail_on_script_failure": false,
          "enable_script_exec": false
        },
        "mechanism": "HARD"
      },
      "power_state": "ON",
      "machine_type": "PC",
      "vga_console_enabled": true,
      "memory_overcommit_enabled": false,
      "disk_list": [
        {
          "uuid": "6f6ad0ba-5b81-487d-8c7b-6c5249a51f4b",
          "disk_size_bytes": 48318382080,
          "storage_config": {
            "storage_container_reference": {
              "kind": "storage_container",
              "uuid": "13611573-1364-4065-a03d-67a7da51c14d",
              "name": "SelfServiceContainer"
            }
          },
          "device_properties": {
            "disk_address": {
              "device_index": 0,
              "adapter_type": "SCSI"
            },
            "device_type": "DISK"
          },
          "data_source_reference": {
            "kind": "image",
            "uuid": "50a186ef-2538-43f3-9d6c-9150dd464ad7"
          },
          "disk_size_mib": 46080
        }
      ]
    },
    "description": "test sync categorie"
  },
  "api_version": "3.1",
  "metadata": {
    "last_update_time": "2023-06-29T14:25:48Z",
    "kind": "vm",
    "uuid": "6e1b8781-8a2a-4830-a15a-0af30daccf89",
    "project_reference": {
      "kind": "project",
      "name": "_internal",
      "uuid": "706f34c2-98be-4034-92e2-859be84040f3"
    },
    "creation_time": "2023-06-29T14:25:48Z",
    "spec_version": 3,
    "categories_mapping": {
      "AppType": [
        "Exchange"
      ],
      "AppTier": [
        "Default"
      ]
    },
    "entity_version": "2",
    "owner_reference": {
      "kind": "user",
      "name": "OWNER",
      "uuid": "6caba9b0-00a9-57ea-8ba0-10162c0b1dd4"
    },
    "categories": {
      "AppType": "Exchange",
      "AppTier": "Default"
    }
  }
}

And look like this once converted in Powershell (Note : we're only interested about metadata, you'll see why)

PS C:\Users\XXX> $payload.metadata

last_update_time   : 29/06/2023 14:25:48
kind               : vm
uuid               : 6e1b8781-8a2a-4830-a15a-0af30daccf89
project_reference  : @{kind=project; name=_internal; uuid=706f34c2-98be-4034-92e2-859be84040f3}
creation_time      : 29/06/2023 14:25:48
spec_version       : 3
categories_mapping : @{AppType=System.Object[]; AppTier=System.Object[]}
entity_version     : 2
owner_reference    : @{kind=user; name=owner; uuid=6caba9b0-00a9-57ea-8ba0-10162c0b1dd4}
categories         : @{AppType=Exchange; AppTier=Default}


PS C:\Users\XXX> $payload.metadata.categories

AppType  AppTier
-------  -------
Exchange Default

As you can see from a PS perspective, $payload and its properties are PSCustomObject, so i can't use method like .Add(), .Remove() etc... neither a +=

At the moment, i'm able to update a VM manually using the original JSON payload, and add category manually in the JSON format and test it through Postman. It works great.

The thing is, as you may have understand, i have to keep all the original payload from the GET method > achieve to add a new category > send a PUT request to fully update it without error.

My question is : from a PS perspective, how can I easily create and add a new key:value object and add it properly within categories by keeping the existing ? (= how to insert new value inside "categories": { "AppType": "Exchange", "AppTier": "Default" } )

PS: for references, here the Web Request i use to GET/PUT within PS. The PUT request is working but do nothing except PUT the same value as original :

#### -- HEADER
$Header_Prism = @{
    'Accept' = 'application/json'
    'Content-Type' = 'application/json'
    'Authorization' = $basicAuthValue
}

#### --- GET VM
$get_vm = Invoke-RestMethod -Uri "https://mynutanix.net/vms/6e1b8781-6666-4830-a15a-0af30daccf89"  `
-Method GET `
-Headers $Header_Prism


### --- PUT VM
$payload = $get_vm | select spec,api_version,metadata | convertTo-Json -depth 100


$put_vm = Invoke-RestMethod -Uri "https://mynutanix.net/vms/6e1b8781-6666-4830-a15a-0af30daccf89"  `
-Method PUT `
-Headers $Header_Prism `
-Body $payload

Score:1
cn flag

Finally found out a solution thanks to those inspiring StackOverflow posts :
https://stackoverflow.com/questions/48597871/how-to-add-an-object-in-array-on-parsed-json

https://stackoverflow.com/questions/23720045/powershell-how-to-add-something-on-parsed-json

So in my case (reminder : category = key:value) i create a value to update

$to_update = 'My_Value' 

then use this Add-Member method to add the key i need

$get_vm.metadata.categories | Add-Member -value $to_update -MemberType NoteProperty -Name 'My_Key'

Note : it seems proper to Nutanix but I have to increase spec_version parameters by 1

($get_vm.metadata.spec_version)++

And finally create the payload used in the web request

$payload = (($get_vm | select spec,api_version,metadata) | convertto-json -depth 100)

Then i'm able to launch the PUT web request successfully, and category is properly updated ! Now i'll try to improve my script efficiency, i know some stuff may be redundant or could be eased.

I sit in a Tesla and translated this thread with Ai:

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.