In vCloud API/GUI Throwdown part 2, I showed you how to modify VM disk sizes in vCloud Director while the VM is still powered on. However, we did *not* cover adding new disks to a VM.
Adding new harddisks is a little tricky because of the implementation of the object in the SDK.. The conversion from XML to a .NET object is not an easy thing, especially with the vCloud API's intricate nodes, elements, attributes, etc. Because of this, you can't create a representation of a harddisk as a .NET object. Trust me, I've tried.
That being said, we're working with a REST API, and Powershell LOVES REST APIs. Using the [System.Net.WebRequest] class, we can talk HTTP just like any web browser. That means we can use the 4 HTTP methods used in REST APIs: GET, POST, PUT, DELETE.
So, with a mix of PowerCLI and some Powershell .NET magic, I give you... New-CIHarddisk! I made this one an advanced function, so you can just pipe a vCloud VM to it, like so:
*Note the harddisk size is in bytes.
Here are a couple different ways to run the command:
# Passing VM via pipeline
Get-CIVM "MyVM" | New-CIHarddisk -size 10240
# Or just using a VM name
New-CIHarddisk -vm "MyVM" -size 10240
Here's what it looks like after you add more harddisks to a running VM...I may have had a little too much fun with this one:
So enjoy, but be careful!
function New-CIHarddisk
{[cmdletbinding()]param([Parameter (ParameterSetName="pipeline",ValueFromPipeline=$true,Mandatory=$true)]$vm,
[Parameter (Position=0, Mandatory=$true)]$size)Process{# Get indexes to create unique IDs for our new disk.if($vm -is [System.String]){$vm= get-civm $vm}$vmHardware=$vm.ExtensionData.GetVirtualHardwareSection()$scsiController=$vmHardware.item | where{$_.resourceType.value -eq "6"}$highaddress=($vmHardware.item | where{$_.resourcetype.value -eq "17"} | Sort-Object addressonParent)[-1].addressonParent.value
$addressOnParent=[int]$highaddress + 1
$highInstance=($vmHardware.item | where{$_.resourcetype.value -eq "17"} | Sort-Object instanceID)[-1].instanceId.value
$instanceId=[int]$highInstance + 1
$highElement=($vmHardware.item | where{$_.resourcetype.value -eq "17"} | Sort-Object elementName)[-1].elementName.value
$elementName=[int]$highElement.Split()[-1] + 1
# We need to get the raw XML via vCloud API to add the new disk.$hardwareContentType="application/vnd.vmware.vcloud.virtualHardwareSection+xml"$webclient=New-Object system.net.webclient
$webclient.Headers.Add("x-vcloud-authorization",$vmHardware.Client.SessionKey)$webclient.Headers.Add("accept",$hardwareContentType)[xml]$hardwareConfXML=$webclient.DownloadString($vmhardware.href)# Clone the XML node, assign our variables, and insert child into the XML$newDisk=($hardwareConfXML.VirtualHardwareSection.Item | where{$_.resourcetype -eq 17})[-1].clonenode(1)$newDisk.AddressOnParent ="$addressOnParent"$newDisk.InstanceId ="$instanceId"$newDisk.elementName ="Hard Disk $elementName"$newdisk.hostresource.capacity ="$size"$newDisk.parent ="$($scsiController.instanceID.value)"[void]$hardwareConfXML.VirtualHardwareSection.InsertAfter($newDisk,($hardwareConfXML.VirtualHardwareSection.Item | where{$_.resourcetype -eq 17})[-1])# PUT the data back through vCloud API$request=[System.Net.WebRequest]::Create($vmHardware.href);
$request.Headers.Add("x-vCloud-authorization",$vmHardware.Client.SessionKey)$request.Accept="application/vnd.vmware.vcloud.task+xml"$request.Method="PUT"$request.ContentType =$hardwareContentType$postData=$hardwareConfXML.virtualhardwaresection.outerxml
$xmlString=$postData.replace("
","")# adding strings to xml puts in silly newlines.[byte[]]$xmlEnc=[System.Text.Encoding]::UTF8.GetBytes($xmlString)$request.ContentLength =$xmlEnc.length
[System.IO.Stream]$requestStream=$request.GetRequestStream()$requestStream.write($xmlEnc, 0, $xmlEnc.Length)$requestStream.Close()$response=$request.GetResponse()$responseStream=$response.getResponseStream()$streamReader=new-object System.IO.StreamReader($responseStream)$result=$streamReader.ReadtoEnd()$streamReader.close()$response.close()}}