1

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

motorbass
  • 303
  • 2
  • 12

1 Answers1

1

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.

motorbass
  • 303
  • 2
  • 12