0

I'm working on a deployment script for VMs on a VMWare platform but I'm stuck.

Basically, my script does this:

Receive information from Excel and first runs some check. It does that for each row, which will represent VM information, before any VM is created.

When all VMs are validated, the first VM will be created, and then the next one, etc.

One of my functions will calculate the best available storage disk. It returns the first storage disk with the most available diskspace.

That function looks like this:

Function Get-AvailableStorage {
    $Threshold = "80" # GB
    $TotalFreeSpace = Get-Cluster -Name Management |
                      Get-Datastore |
                      where Name -notlike "*local*" |
                      sort FreeSpaceGB -Descending
    if ($SqlServices -notlike "NONE") {
        $VMSize = 30 + $VMStorage + 10
    } else {
        $VMSize = 30 + $VMStorage
    }

    foreach ($StorageDisk in $TotalFreeSpace) {
        [math]::floor($StorageDisk.FreeSpaceGB) | Set-Variable RoundedSpace
        $FreeAfter =  $RoundedSpace - $VMSize | sort -Descending
        if ($FreeAfter -lt $Threshold) {
            return $StoragePool = "VSAN"
        } else {
            return $StorageDisk.Name
        }
    }
}

The problem

When I have multiple VMs in my Excel, the storage disk is always the same, because the available diskspace is not being updated (because none of the VMs is being deployed yet).

I did some investigation on my own:

I have to figure out a way to update the column FreeSpaceGB but that is a ReadOnly property. I then though to push every item in another array which I created myself but that also doesn't work. Still Readonly property. Then I thought about using PSObject with an Add-Member, but I cannot get that working either (or I'm doing it wrong).

$ownarray= @()
$item = New-Object PSObject

$Global:TotalFreeSpaceManagement = Get-Cluster -Name Management |
    Get-Datastore |
    where Name -notlike "*local*" |
    sort FreeSpaceGB -Descending

foreach ($StorageDisk in $Global:TotalFreeSpaceManagement) {
    $item | Add-Member -Type NoteProperty -Name "$($StorageDisk.Name)" -Value "$($StorageDisk.FreeSpaceGB)" 
    $ownarray += $item
}

UPDATE

I use the hashtable like @Ansgar suggested. When I try it manually it's working perfectly, but in my script it's not. When I have multiple VMs in an array, the previous datastore is being used and the space that is left is UPDATED.

Example:

VM1 is 120GB and uses VM-105. That disk has 299GB left.
VM2 is 130GB and uses VM-105. Then the disk has 289GB left.

Both VMs are getting suggested VM-105 based on the most free space.

VM-105 should have 299 - 130 = 160GB left is the script was working correct but somehow the $FreeSpace is updated, or $max is overwritten, and I cannot figure how this happens.

Ansgar Wiechers
  • 193,178
  • 25
  • 254
  • 328
  • You need an arraylist. And you have to add in that one. Check my answer for the arraylist [ArrayList](http://stackoverflow.com/questions/41585856/multiarray-with-one-entry/41586375#41586375) – Ranadip Dutta Jan 11 '17 at 09:11
  • @RanadipDutta, Thanks but I still have the issue that I cannot update a value. When I do this: $array_list[1].FreeSpaceMB = '1' My error; 'FreeSpaceMB' is a ReadOnly property. or I don't understand fully how to update :), sorry for that but there is no SetValue method. –  Jan 11 '17 at 09:36
  • For help with updated code: show the updated code. I just amended a logic error in my code and tested again, and it does exactly what I expected it to do. – Ansgar Wiechers Jan 12 '17 at 10:32

1 Answers1

0

You need to keep track of the changes to the free space, while also maintaining the association between disk and the calculated remaining free space. To do that read your storage into a hashtable that associates disks with the available free space. Then use that hashtable for selecting a disk and updating the respective free space value when placing a VM.

$threshold = 80   # GB

# initialize global hashtable
$storage = @{}
Get-Cluster -Name Management | Get-Datastore | Where-Object {
    $_.Name -notlike '*local*'
} | ForEach-Object {
    $script:storage[$_.Name] = [Math]::Floor($_.FreeSpaceGB)
}

function Get-AvailableStorage([int]$VMSize) {
    # find disk that currently has the most free space
    $disk = ''
    $max  = 0
    foreach ($key in $script:storage.Keys) {
        $freespace = $script:storage[$key]   # just to shorten condition below
        if ($freespace -gt $max -and $threshold -lt ($freespace - $VMSize)) {
            $max  = $freespace
            $disk = $key
        }
    }

    # return storage and update global hashtable
    if (-not $disk) {
        return 'VSAN'   # fallback if no suitable disk was found
    } else {
        $script:storage[$disk] -= $VMSize
        return $disk
    }
}
Ansgar Wiechers
  • 193,178
  • 25
  • 254
  • 328
  • minor change about your anwer @Ansgar; I had to use double-quotes in the $threshold and the VMSize. Otherwise its not working correct –  Jan 11 '17 at 16:50
  • still figuring out the work of the hashtable. I can go -xxx in storage (so no storage left) but trying to figure out how that comes :) –  Jan 11 '17 at 18:41
  • I've updated my question because I now have another issue –  Jan 12 '17 at 05:36
  • The second condition in the loop that determines which disk to use was wrong. Must be `-lt`, not `-gt`. Updated my answer. – Ansgar Wiechers Jan 12 '17 at 10:26