<#
.SYNOPSIS
    Function to process a VMware change request
.DESCRIPTION
    A longer description of the function, its purpose, common use cases, etc.
.NOTES
    Information or caveats about the function e.g. 'This function is not supported in Linux'
.LINK
    Specify a URI to a help page, this will show when Get-Help -Online is used.
.EXAMPLE
    Test-MyTestFunction -Verbose
    Explanation of the function or its result. You can include multiple examples with additional .EXAMPLE lines
#>

function Set-ITDVMwareVMViaSnowTask {
    [CmdletBinding()]
    param (
        [string]
        $SCTaskNum,

        [switch]
        $Override,

        [switch]
        $CloseTask
    )
    
    begin {
        $FreePercentThreshold = 0.20    # 20% free space required before disk expansions are automated
    }
    
    process {
        # get current user, SCTask, Ritm
        $assignTo = Get-ITDServiceNowUser -Username $Env:USERNAME
        $SCTask = Get-ITDServiceNowRecord -ItemType 'Catalog Task' -Number $SCTaskNum
        $Ritm = Get-ITDServiceNowRecord -ItemType 'Request Item' -SysId ($SCTask.request_item.value) -IncludeVariableSet
        $RitmNum = $Ritm.number.value

        # Get Hostname and CMDB object
        [string]$SCTaskDescriptionHostname = $SCTask.description.display_value.split(' ')[-1]
        $TaskCmdb = @()

        Write-Verbose -Message ("Gathering VariableSet data from $RitmNum")
        $MatchFound = $false
        ForEach ($Row in $Ritm.VariableSet) {
            $TempCi = Get-ITDServiceNowRecord -Table cmdb_ci -SysId ($Row.host_name_ref) -ErrorAction Stop
            If ($SCTaskDescriptionHostname -eq $TempCi.name.display_value) {
                $Ci = $TempCi
                $MatchFound = $true
            }
        }

        If ($MatchFound -eq $false) {
            Write-Error -Message "ComputerName $ComputerName was not found in VariableSet for $RitmNum" -ErrorAction Stop
        }

        $FQDN = $Ci.fqdn.display_value

        If ( @($Ci).count -gt 1 ) {
            Write-Error -Message "More than one CMDB object found that matches the hostname in this task's description" -ErrorAction Stop
        }
        
        # update SCTask description and assignment for humans
        Update-ITDServiceNowRecord -ItemType 'Catalog Task' -Number $SCTaskNum -Values @{assigned_to = $assignTo.name }

        # search for VMware VM
        try {
            $VM = Get-VM -Name $Ci.fqdn.display_value | Where-Object { $_.ExtensionData.summary.config.ManagedBy.Type -ne "placeholderVm" }
        }
        catch {
            Write-Error "Error on VM lookup.  Are you connected to vCenter?" -ErrorAction Stop
        }

        switch ( @($VM).count ) {
            { 0 -or $null } {
                # no matches
                Write-Error -Message "Zero VM matches found, ending script" -ErrorAction Stop
            }
            { $_ -ne 1 } {
                # more than one match
                Write-Error -Message "Multiple VM matches found, ending script" -ErrorAction Stop
            }
            { 1 } {
                # exactly one match, gather request information, and populate variables
                Write-Verbose -Message ("VM: " + $VM.Name)
                [int]$CPU = ($Ritm.VariableSet | Where-Object { $_.host_name_ref -EQ $Ci.sys_id.value } ).Processors
                [int]$MemoryGB = ($Ritm.VariableSet | Where-Object { $_.host_name_ref -EQ $Ci.sys_id.value } ).memory_gb
                [int]$Disk1 = ($Ritm.VariableSet | Where-Object { $_.host_name_ref -EQ $Ci.sys_id.value } ).disk_1_os
                [int]$Disk2 = ($Ritm.VariableSet | Where-Object { $_.host_name_ref -EQ $Ci.sys_id.value } ).disk_2_swap_disk

                3..16 | ForEach-Object {
                    $DiskNum = $_
                    Write-Verbose "Populating variable Disk$DiskNum"
                    ###Set-Variable -Name "Disk$DiskNum" -Value ([int](($Ritm.CustomVariable | Where-Object Name -Like "disk_$DiskNum*").Value))
                    Set-Variable -Name "Disk$DiskNum" -Value ([int]( ($Ritm.VariableSet | Where-Object { $_.host_name_ref -EQ $Ci.sys_id.value } )."disk_$DiskNum"))
                }

                # CPU modification
                If ($CPU -ne 0 -and $CPU -ne $VM.NumCpu) {
                    Write-Verbose -Message ($VM.Name + " attempt changing CPU from " + $VM.NumCPU + " to " + $CPU)
                    try {
                        $OldCpu = $VM.NumCpu
                        $VM | Set-VM -NumCpu $CPU  -Confirm:$false -ErrorAction Stop
                        $CommentsForWorkNotes += "CPU was updated from $OldCpu to $CPU.  `n"
                    }
                    catch {
                        
                    }
                }

                # MemoryGB modification
                If ($MemoryGB -ne 0 -and $MemoryGB -ne $VM.MemoryGB) {
                    Write-Verbose -Message ($VM.Name + " attempt changing MemoryGB to " + $MemoryGB)
                    try {
                        $OldMemoryGB = $VM.MemoryGB
                        $VM | Set-VM -MemoryGB $MemoryGB -Confirm:$false -ErrorAction Stop
                        $CommentsForWorkNotes += "MemoryGB was updated from $OldMemoryGB to $MemoryGB.  `n"
                    }
                    catch {

                    }
                }

                # Disk modification loop
                $VMDisks = $VM | Get-HardDisk
                1..16 | ForEach-Object {
                    Write-Verbose "Start Loop for Disk $_"
                    $HardDisk = $null
                    $DiskGBNewValue = $null
                    $DiskVarName = $null

                    $DiskNum = $_
                    $DiskGBNewValue = (Get-Variable -Name Disk$DiskNum).Value

                    switch ($DiskNum){
                        1 {
                            $DiskVarName = "Disk1"
                        }
                        2 {
                            $DiskVarName = "Disk2"
                        }
                        Default {
                            $DiskVarName = ("Disk" + $DiskNum)
                        }
                    }
                    
                    If ( (Get-Variable -Name $DiskVarName).Value -ne 0 ) {
                        Write-Verbose -Message ("DiskNum: $DiskNum, DiskGBNewValue: $DiskGBNewValue")

                        $HardDisk = $VM | Get-HardDisk -Name "Hard disk $DiskNum" -ErrorAction SilentlyContinue
                        If ($HardDisk) {
                            $Datastore = $HardDisk | Get-Datastore
                            $DiskGBOldValue = $HardDisk.CapacityGB
                            $HardDiskIncreaseGB = $DiskGBNewValue - $DiskGBOldValue
                            If ($HardDiskIncreaseGB -ge 500) {
                                # manual intervention
                            }
                            $FreePercentBefore = ($Datastore.FreeSpaceGB) / $Datastore.CapacityGB
                            $FreePercentAfter = ($Datastore.FreeSpaceGB - $HardDiskIncreaseGB) / $Datastore.CapacityGB
                    
                            Write-Verbose -Message ("Datastore " + $Datastore.Name + " free space will lower from " + [math]::round($FreePercentBefore, 4) + " to " + [math]::round($FreePercentAfter, 4) + "") -Verbose
                            Write-Verbose -Message ("Override is " + $Override)
                            If ( ($FreePercentAfter -gt $FreePercentThreshold) -or ($Override -eq $true)) {
                                try {
                                    Write-Verbose -Message ("Hard disk $DiskNum :  Increasing from " + $HardDisk.CapacityGB + "GB to " + $DiskGBNewValue + "GB") -Verbose
                                    
                                    $VM | Get-HardDisk -Name "Hard disk $DiskNum" | Set-HardDisk -CapacityGB $DiskGBNewValue -Confirm:$false
                                    $CommentsForWorkNotes += "Hard disk $DiskNum CapacityGB was modified from $DiskGBOldValue GB to $DiskGBNewValue GB.  `n"
                                    $DiskChanged = $true
                                }
                                catch {
                                    Write-Error -Message "Disk $DiskNum expansion failed" -ErrorAction Stop
                                }
                            }
                            Else {
                                try {
                                    Write-Error -Message ("Hard disk $DiskNum failed.  " + $FreePercentThreshold * 100 + "% free space is required for automated disk expansions.  " + $Datastore.Name + " would be " + [math]::round($FreePercentAfter, 4) + ".") -ErrorAction Stop
                                }
                                catch {
                                    Write-Error -Message ("Hard disk $DiskNum failed.  " + $FreePercentThreshold * 100 + "% free space is required for automated disk expansions.  " + $Datastore.Name + " would be " + [math]::round($FreePercentAfter, 4) + ".") -ErrorAction Stop
                                }
                            }
                        }
                        Else {
                            Write-Verbose "Hard disk $DiskNum was not found.  New disk will attempt to be created." -Verbose
                            # get licensing and storage format of existing disks
                            $VMTag = Get-TagAssignment -Entity $VM -Category LicensingRestrictions
                            If (@($VMDisks | Where-Object StorageFormat -Like "*Thick*").count -gt 0) {
                                $StorageFormat = 'EagerZeroedThick'
                            }
                            Else {
                                $StorageFormat = 'Thin'
                            }
                            
                            # if not SQL, validate available space and create if possible.  if VM is SQL, stop                            
                            If ($VMTag.Tag.Name -notlike "*SQL*") {
                                # place new disk with disk 1 and validate datastore free space
                                $Datastore = $VM | Get-HardDisk -Name "Hard disk 1" | Get-Datastore
                                $DiskGBOldValue = 0
                                $HardDiskIncreaseGB = $DiskGBNewValue - $DiskGBOldValue
                                If ($HardDiskIncreaseGB -ge 500) {
                                    # manual intervention ?
                                }
                                $FreePercentBefore = ($Datastore.FreeSpaceGB) / $Datastore.CapacityGB
                                $FreePercentAfter = ($Datastore.FreeSpaceGB - $HardDiskIncreaseGB) / $Datastore.CapacityGB

                                #create the disk, with decided storageformat and persistent persistence on hard disk 1 datastore
                                If ($FreePercentAfter -gt $FreePercentThreshold) {
                                    try {
                                        Write-Warning -Message ("Hard disk $DiskNum :  Creating new disk " + $DiskGBNewValue + " GB")
                                        #Write-Warning -Message ("Datastore " + $Datastore.Name + " free space will lower from " + [math]::round($FreePercentBefore,4)*100 + "% to " + [math]::round($FreePercentAfter,4)*100 + "%")
                                        Write-Warning -Message ("Datastore " + $Datastore.Name + " free space will lower from " + [math]::round($FreePercentBefore, 4) + " to " + [math]::round($FreePercentAfter, 4) + "")
                                        $VM | New-HardDisk -CapacityGB $DiskGBNewValue -StorageFormat $StorageFormat
                                        $CommentsForWorkNotes += "Hard disk $DiskNum was created with $DiskGBNewValue GB.  "
                                        $DiskChanged = $true
                                    }
                                    catch {
                                    
                                    }
                                }
                                Else {
                                    Write-Error -Message ("Hard disk $_ failed.  " + $FreePercentThreshold * 100 + "% free space is required for automated disk expansions.  " + $Datastore.Name + " would be " + [math]::round($FreePercentAfter, 4) + ".")
                                    $DiskChanged = $false
                                    $DiskError = $true
                                }
                            }
                            Else {
                                # require human review
                                Write-Error -Message ($HostName + " has SQL licensing, create the new disk manually due to SQL Server best practices.  Once complete, rerun to validate size and close task.") -ErrorAction Stop
                            }
                        }
                    }
                }

                # update sctask and ritm, reassign if disk changed
                $CommentsToAdd = "VMware VM " + $VM.Name + " was modified: `n" + $CommentsForWorkNotes
                Update-ITDServiceNowRecord -ItemType 'Catalog Task' -Number $SCTaskNum -Values @{comments = $CommentsToAdd }
                Update-ITDServiceNowRecord -ItemType 'Request Item' -Number $Ritm.number.value -Values @{comments = $CommentsToAdd }

                If ($CloseTask) {
                    Update-ITDServiceNowRecord -ItemType 'Catalog Task' -Number $SCTaskNum -Values @{close_notes = $CommentsToAdd; state = "Closed Complete" }
                }
            }
        }
    }

    end {
        
    }
}