PowerCLI

 View Only
Expand all | Collapse all

VMTools & Hardware upgrade script using PoshRSJob - Powershell

  • 1.  VMTools & Hardware upgrade script using PoshRSJob - Powershell

    Posted Jan 04, 2023 01:16 PM

    Hi,

    I have written two scripts to handle updating VMTools and Hardware on virtual machines from a csv file.

    The main script imports the csv file and starts a Job for each VM run against the JobScript. This jobs are set to a max of 10 at any one time.

    The Job script has a list of steps it takes

    • Get a View of the VM
    • Reboot VM - to ensure no windows updates pending
    • Power Off VM
    • Take Snapshot
    • Power On VM
    • Perform VMTools upgrade with no reboot
    • Reboot VM
    • Power Off VM
    • Upgrade Hardware of VM
    • Power On VM

    There are lots of checks built into each step to ensure they complete before moving on.

    the problem I am having is that not all the commands issued against the VM's are running in vCenter. So some VM's will fail due to a missed power On/Off or a missed snapshot. The built in checks will catch the failure of a command to run but I can't tell why they didn't run.

    The missed commands are also not consistent, sometimes it will complete all on 10 VM's other times only 8 will complete.

    Main Script

    ###################################
    ### vmtools & HW upgrade Script ###
    ###################################
    
    ### Setup parameters/variables for the script ###
    
    [cmdletbinding()]
    param(
    ### Setup Credentials parameter which will be stored in a secure way
    [parameter(ValueFromPipelineByPropertyName=$true,ValueFromPipeline=$true,Mandatory=$false)]
    [PSCredential]$UserCredential,
    
    ### Change this to the relevant vCenter you are connecting to ###
    [object] $VCs = "XXXXXXXXXXXXXXXXXX", 
    
    ### Job sub script path ###
    [string] $Subdir = "C:\Scripts\vmtoolsHW\vmtoolsHWsubv5.ps1",
    
    ### Initialze the log file location ###
    [string] $LogFile = "C:\Scripts\Master_vmtools&HW-" + (Get-Date).tostring("dd-MM-yyyy") + ".txt", 
    
    ### Set the maximum number of background jobs that the script will run, The max allowed is 10 ###
    [int] $MaxJobs = 10,
    
    ### Set Hardware version you want to upgrade to ###
    [string] $VMHW = "vmx-19"
    )
    
    ### Import required modules
    Import-Module -Name VMware.PowerCLI -ErrorAction SilentlyContinue | Out-Null
    Import-Module -Name VMware.VimAutomation.Core -ErrorAction SilentlyContinue | Out-Null
    Import-Module -Name PoshRSJob -ErrorAction SilentlyContinue | Out-Null
    
    ### Setting change to allow linked vCenters to connect all at once ###
    set-powercliconfiguration -defaultviservermode multiple
    
    ### Import Array ###
    $VMNames = Import-Csv "C:\Scripts\vm_upgrades.csv" | Select -ExpandProperty "Name" # create array variable without a header
    
    ####################### Main Code ############################
    
    
    Write-Output "$(Get-Date) ===============  vmtools & HW upgrade Script  ======================="  | Tee-Object -FilePath $LogFile -Append
    
    Write-Output $VMNames | Out-File $LogFile -Append
    
    ### ensure no prior vCenter connections exist ###
    Disconnect-VIServer -Server * -Confirm:$false -Force | Out-Null
    
    ### Get login credentials to vCenter ###
    $UserCredential = Get-Credential -message "Enter credentials for VCenter in format domain\username"
    
    Connect-VIServer -Server $VCs -Credential $UserCredential -WarningAction SilentlyContinue | out-null
    Write-Output "$(Get-Date) ===============  connecting to $VCs  =================================" |  Tee-Object -FilePath $LogFile -Append
    
    ### code to kick off the VMtools & HW job for each vm ###
    Write-Output "$(Get-Date) - starting upgrade Jobs now `n" | Tee-Object -FilePath $LogFile -Append
    
    $VMNames | ForEach-Object {Start-RSJob -Name $_ -FilePath $Subdir -ArgumentList @($_, $UserCredential, $VCs, $VMHW) -Throttle $MaxJobs -batch "upgrades_$($_.name)" } | Wait-RSJob -ShowProgress 
    
    Write-Output "$(Get-Date) - All Jobs completed `n`n"  | Tee-Object -FilePath $LogFile -Append
    
    ### Add all the Job results to the Log file ###
    Get-RSJob | Tee-Object -FilePath $LogFile -Append
    
    Sleep 5
    
    ### remove RSJobs & disconnect-vCenter ###
    Get-RSJob | Remove-RSJob -Force
    Disconnect-VIServer -Server * -Confirm:$false -Force | Out-Null
    
    ### End Log File ###
    Write-Output "$(Get-Date) ===============  End of vmtools & HW upgrade Script  ======================="  | Tee-Object -FilePath $LogFile -Append
    
    ### Clear all variables ###
    Remove-Variable * -ErrorAction SilentlyContinue

     

    JobScript

    #######################################
    ### vmtools & HW upgrade Job Script ###
    #######################################
    
    ### define required parameters for Sub script ###
    [cmdletbinding()]
    param(
    [string] $VM,
    [PSCredential]$UserCredential,
    [object] $VCs,
    [string] $VMHW 
    )
    
    ### define Log File location ###
    $LogFile = "C:\Scripts\VMLogs\vmtools&HW-"+ $VM +"-" + (Get-Date).tostring("dd-MM-yyyy") + ".txt" ##Initialze the individual log file for each VM
    
    ### Power on VM ###
    Function PowerOn-VM
    {
        ### define required parameters for Power On function ###
       param(
       [Parameter(Mandatory=$true, Position=0)]
       [object] $VM,
       [Parameter(Mandatory=$true, Position=1)]
       [object] $VMView
       )
    
       ### Issue Start command to $VM ### 
       Start-VM -VM $VM -Confirm:$false| Out-Null
    
       sleep 5 # pause the script for 5 seconds to allow time for the start VM command to commence
    
       Write-Output "$(Get-Date) - Step 3.4.1 - Power on VM command issued on $VM, $VM is starting. `r`n" | Out-File -FilePath $LogFile -Append   
    
       $timerPON = 0 # Setup timer variable and set to 0, it will be used as an escape from the loop checking VM is powered on
    
       ### 5 minute loop after Power On command has been issued, will keep checking VM until VM is powered on or timer gets to 30 ### 
       do
       {
        $VMView.UpdateViewData("Summary.Runtime.PowerState") # update specfic VM info - PowerState
        $powerstate = $VMView.Summary.Runtime.PowerState # Set $powerstate variable to new value from $VMView
    
        $vmview.UpdateViewData("Summary.Guest.ToolsStatus") # update specfic VM info - ToolsStatus
        $ToolsStatus = $vmview.Summary.Guest.ToolsStatus # Set $ToolsStatus variable to new value from $VMView
    
        $timerPON = ++$timerPON # Add 1 to the timer variable
     
        Write-Output "$(Get-Date) - Step 3.4.2 - $VM is starting, powerstate is $powerstate and toolsstatus is $ToolsStatus. `r`n" | Out-File -FilePath $LogFile -Append
        
        sleep 10 # Sleep loop for 10 seconds
     
        }until(($powerstate -match "PoweredOn") -and (($ToolsStatus -match "toolsOld") -or ($ToolsStatus -match "toolsOk") -or ($ToolsStatus -match "toolsNotInstalled")) -or ($timerPON -eq 30))
     
        ### Check Power status after checking loop, If powered ON and Tools  return normal to log file, If VM is not powered off exit script ###
    
        if (($powerstate -match "PoweredOn") -and (($ToolsStatus -match "toolsOk") -or ($ToolsStatus -match "toolsOld") -or ($ToolsStatus -match "toolsNotInstalled"))) # Loop will run until conditions are met.
        {
          Write-Output "$(Get-Date) - Step 3.4.3 - $VM is started and has ToolsStatus - $ToolsStatus. `r`n" | Out-File -FilePath $LogFile -Append
        }
    
        ### Else Loop - If VM didn't power on show Error message and exit script ###
        else
        {
          Write-Output "$(Get-Date) - Step 3.4.4 - PowerOn Error detected on $vm, exiting Job. `r`n" | Out-File -FilePath $LogFile -Append
          Exit # Exit Sub script due to Error
        }
        return $VMView # Return $VMView 
    }
    
    ### Shutdown VM ###
    function poweroff-VM
    {
       ### define required parameters for Power Off function ###
       param(
       [Parameter(Mandatory=$true, Position=0)]
       [object] $VM,
       [Parameter(Mandatory=$true, Position=1)]
       [object] $VMView
       )
    
       ### Issue Guest Shutdown command to $VM ### 
       Shutdown-VMGuest -VM $VM -Confirm:$false | Out-Null 
    
       sleep 5 # pause the script for 5 seconds to allow time for the guest shutdown command to commence
    
       Write-Output "$(Get-Date) - Step 3.2.1 - Shutdown command issued to $VM. `r`n" | Out-File -FilePath $LogFile -Append
    
       $timerPOFF = 0 # Setup timer variable and set to 0, it will be used as an escape from the loop checking VM is powered down
    
       ### 5 minute loop after shutdown Guest OS command has been issued, will keep checking VM until VM is powered off or timer gets to 30 ### 
       do
       {
    
          $VMView.UpdateViewData("Summary.Runtime.PowerState") # update specfic VM info - PowerState
          $powerstate = $VMView.Summary.Runtime.PowerState # Set $powerstate variable to new value from $VMView
    
          $vmview.UpdateViewData("Summary.Guest.ToolsStatus") # update specfic VM info - ToolsStatus
          $ToolsStatus = $vmview.Summary.Guest.ToolsStatus # Set $ToolsStatus variable to new value from $VMView
    
          $timerPOFF = ++$timerPOFF # Add 1 to the timer variable
    
          Write-Output "$(Get-Date) - Step 3.2.2 - Loop checking $VM is stopping: powerstate = $powerstate , toolsStatus = $ToolsStatus. `r`n" | Out-File -FilePath $LogFile -Append
          
          sleep 10 # Sleep loop for 10 seconds
    
       }until(($powerstate -match "PoweredOff") -or ($timerPON -eq 30)) # Loop will run until either condition is met.
    
       ### Check Power status after checking loop, If powered off return normal to log file, If VM is not powered off exit script ###
     
       if (($powerstate -match "PoweredOff") -and (($ToolsStatus -match "toolsNotRunning") -or ($ToolsStatus -match "toolsNotInstalled")))
       {
          Write-Output "$(Get-Date) - Step 3.2.3 - $vm is powered-off. `r`n" | Out-File -FilePath $LogFile -Append
       }
    
       ### Else Loop - If VM didn't power down show Error message and exit script ###
       else
       {
    	  Write-Output "$(Get-Date) - Step 3.2.4 - PowerOff Error detected on $vm. `r`n"| Out-File -FilePath $LogFile -Append
          Exit # Exit Sub script due to Error
       }
       return $VMView # Return $VMView  
    
    }
    
    function Reboot-VM
    {
        ### define required parameters for Reboot VM function ###
        param(
        [Parameter(Mandatory=$true, Position=0)]
        [object] $vm,
        [Parameter(Mandatory=$true, Position=1)]
        [object] $VMView
        )
    
        $powerstate = $VMView.Summary.Runtime.PowerState # Set $powerstate variable to new value from $VMView
    
        ### Initial If loop to ensure VM is in PoweredOn state before anything is done on VM ###
        If($powerstate -ne "PoweredOn")
        {
          Write-Output "$(Get-Date) - Step 1.0 - $vm has power state - $powerstate `r`n Upgrade of VMTools and Hardware will not be attempted. `r`n Exiting Job. `r`n" | Out-File -FilePath $LogFile -Append
          Exit # Exit Sub script due to Error
        }
    
        ### Else Loop - If VM is PoweredOn start reboot ###
        else
        {
            Write-Output "$(Get-Date) - Step 1.1 - Issuing a Reboot Guest OS command to $vm. `r`n" | Out-File -FilePath $LogFile -Append
    
            ### command to restart VM guest OS ###
            Get-VM $vm | Restart-VMGuest -Confirm:$false | Out-Null
    
            sleep 5 # pause the script for 5 seconds to allow time for the reboot guest OS command to commence
    
            $timerPReboot = 0 # Setup timer variable and set to 0, it will be used as an escape from the loop checking VM Rebooted
        
        
            ### 20 minute loop after Reboot Guest OS command has been issued, will keep checking VM until VM is powered on or timer gets to 120 ### 
            do
            {
                sleep 10 # Sleep loop for 10 seconds
    
                $VMView.UpdateViewData("Summary.Runtime.PowerState") # update specfic VM info - PowerState
                $powerstate = $VMView.Summary.Runtime.PowerState # Set $powerstate variable to new value from $VMView
    
                $vmview.UpdateViewData("Summary.Guest.ToolsStatus") # update specfic VM info - ToolsStatus
                $ToolsStatus = $vmview.Summary.Guest.ToolsStatus # Set $ToolsStatus variable to new value from $VMView
    
                $timerPReboot = ++$timerPReboot # Add 1 to the timer variable
    
                Write-Output "$(Get-Date) - Step 1.2 - Loop checking $vm is rebooting: powerstate = $powerstate , toolsStatus = $ToolsStatus. `r`n" | Out-File -FilePath $LogFile -Append
     
            }until((($powerstate -match "PoweredOn") -and (($ToolsStatus -match "toolsOk") -or ($ToolsStatus -match "toolsOld") -or ($ToolsStatus -match "toolsNotInstalled"))) -or ($timerPReboot -eq 120)) # Loop will run until a condition is met.
    
            ### Check Power status after check loop, If powered ON and Tools return normal to log file, If VM is not rebooted exit script ###
            if (($powerstate -match "PoweredOn") -and (($ToolsStatus -match "toolsOk") -or ($ToolsStatus -match "toolsOld") -or ($ToolsStatus -match "toolsNotInstalled")))
            {
                Write-Output "$(Get-Date) - Step 1.3 - $vm is started and has ToolsStatus $ToolsStatus. `r`n" | Out-File -FilePath $LogFile -Append
            }
       
            ### Else Loop - If VM didn't Reboot show Error message and exit script ###
            else
            {
                Write-Output "$(Get-Date) - Step 1.4 - PowerOn Error detected on $vm, exiting Job. `r`n" | Out-File -FilePath $LogFile -Append
                Exit # Exit Sub script due to Error
            }
       }
        return $VMView # Return $VMView
    }
    
    ### Take a Snapshot ### 
    function Make-Snapshot
    {
      ### define required parameters for Make-Snapshot function ###
      param(
      [Parameter(Mandatory=$true, Position=0)]
      [object] $VM
      )  
      
      $SnapCheck = @{}
      $SnapName = "VMTools&HWPreUpgradeSnap"
    
      ### Create Snapshot on $VM with below -Name and -Description ###
      New-Snapshot -VM $vm -Name $SnapName -Description "Snapshot taken before Tools and Hardware was updated" -Memory:$False -Quiesce:$False -Confirm:$false | Out-Null
      Write-Output " $(Get-Date) - Step 2.0.0 - Snapshot has been taken on $vm"| Out-File -FilePath $LogFile -Append
    
      sleep 5
      
      ### Ensure Snapshot is present on VM before proceeding, exit script if snap not taken
      $SnapCheck = Get-Snapshot -VM $VM -Name $SnapName 
      if($Snapcheck.Name -ne $SnapName)
      {
        Write-Output "$(Get-Date) - Step 2.0.1 - There was a problem taking a Snapshot on $VM, exiting script.`r`n"  | Out-File -FilePath $LogFile -Append
        PowerON-VM $VM $VMView
        Exit # Exit Sub script due to Error
      }   
    }
    
    ### upgrade VMTools of VM ###
    function Upgrade-vmtools-version
    {
        ### define required parameters for Upgrade-vmtools-version function ###
        param(
        [Parameter(Mandatory=$true, Position=0)]
        [object] $VM,
        [Parameter(Mandatory=$true, Position=1)]
        [object] $VMView
        )
    
        $family = $VMView.Guest.GuestFamily # Set $family variable to the Guest OS Family of $VM from the $VMView Hashtable
    
        $VMView.UpdateViewData("Summary.Guest.ToolsStatus") # update specfic VM info - ToolsStatus   
        $VMToolsStatus = $VMView.Summary.Guest.ToolsStatus # Set $VMToolsStatus variable to new value from $VMView
    
    
        $timerToolsUpgrade = 0 # Setup timer variable and set to 0, it will be used as an escape from the loop checking VMTools upgrade
     
        Write-Output "$(Get-Date) - Step 2.1 - Starting VMTools Check/Upgrade on $VM , VMTools Status - $VMToolsStatus & Guest OS - $family. `r`n" | Out-File -FilePath $LogFile -Append
    
    
        ### Inital If loop will check VMTools status, if the Tools are up to date it will skip the upgrade. ###
        if($VMToolsStatus -eq "toolsOK")
        {
            Write-Output "$(Get-Date) - Step 2.2 - The VM tools up to date -  $VMToolsStatus on $VM. `r`n" | Out-File -FilePath $LogFile -Append
        }
    
        ### ElseIF Loop - Will perform a VMTools upgrade on $VM ###
        elseif(($family -eq "windowsGuest") -and ($VMToolsStatus -ne "toolsNotInstalled") -and ($VMToolsStatus -ne "toolsNotRunning"))
        {       
            ### Perform VMTools upgrade with a surpressed reboot ###
            Write-Output "$(Get-Date) - Step 2.3 - The VM tools are $VMToolsStatus on $VM. Starting update/install now, This will take at few minutes.`r`n" | Out-File -FilePath $LogFile -Append
            
            sleep 1 # Pause for 1 second
    
            Get-VMGuest $vm | Update-Tools -NoReboot # issue a VMTools upgrade command to $VM, an advanced install will occur with no reboot
    
            ### Loop to check Tools upgrade status ###
            do
            {           
                sleep 10 # Sleep loop for 10 seconds        
    
                $vmview.UpdateViewData("Summary.Guest.ToolsStatus") # update specfic VM info - ToolsStatus
                $VMToolsStatus = $vmview.Summary.Guest.ToolsStatus # Set $VMToolsStatus variable to new value from $VMView
    
                $timerToolsUpgrade = ++$timerToolsUpgrade # Add 1 to the timer variable
    
                Write-Output "$(Get-Date) - Step 2.4 - Checking VMTools status on $VM now - $vmtoolsstatus. `r`n" | Out-File -FilePath $LogFile -Append
    
            }until(($VMToolsStatus -eq "toolsOK") -or ($timerToolsUpgrade -eq 30)) # Loop will run until a condition is met.      
    
            ### If Loop - If Tools upgrade was successful and $VMToolsStatus is now toolsOK reboot the $VM ###
            If($VMToolsStatus -eq "toolsOK")
            {
                Write-Output "$(Get-Date) - Step 2.5 - Issuing reboot Guest OS command to $VM. `r`n" | Out-File -FilePath $LogFile -Append
                Reboot-VM $VM $VMView # Call Reboot-VM function and pass $VM & $VMView parameters
            }
    
            ### Else loop - If the $VMToolsStatus didn't come back as toolsOK display error and stop script ###
            else
            {
                Write-Output "$(Get-Date) - Step 2.6 - There was a problem while trying to run the vm tools upgrade on $vm. `r`n Tools status - $vmToolsStatus `r`n Guest OS - $family `r`n no upgrade will be performed. `r`n" | Out-File -FilePath $LogFile -Append
                Exit # Exit Sub script due to Error
            } 
        }
    
        ### Else loop - If the $VMToolsStatus is ToolsNotrunning display error and stop script ###
        else
        {
            Write-Output "$(Get-Date) - Step 2.7 - There was a problem while trying to run the vm tools upgrade on $vm. `r`n Tools status - $vmToolsStatus `r`n Guest OS - $family `r`n no upgrade will be performed. `r`n" | Out-File -FilePath $LogFile -Append
            Exit # Exit Sub script due to Error
        }
        return $VMView # Return $VMView
    }
    
    ### Check & upgrade Hardware version of VM ###
    function Upgrade-hardware-version
    {
      ### define required parameters for Upgrade-vmtools-version function ###
        param(
        [Parameter(Mandatory=$true, Position=0)]
        [object] $VM,
        [Parameter(Mandatory=$true, Position=1)]
        [object] $VMView,
        [Parameter(Mandatory=$true, Position=2)]
        [object] $VMHW
        )
    
        $VMHardware = $VMView.Config.Version # Set $VMHardware to the current Hardware version of the VM
    
        $VMView.UpdateViewData("Summary.Guest.ToolsStatus") # update specfic VM info - ToolsStatus
        $VMToolsStatus = $VMView.Summary.Guest.ToolsStatus # Set $VMToolsStatus variable to new value from $VMView
    
        sleep 1
    
        ### Inital If loop will check VMTools status & Hardware version, if the Tools are old or Hardware version is up to date it will skip the upgrade. ###
        if(($vmHardware -eq "vmx-19") -or ($VMToolsStatus -eq "toolsOld"))
        {
            Write-Output "$(Get-Date) - Step 3.1 - The VMTools version is $VMToolsStatus on $VM & The hardware version is $VMHardware on $VM. an Hardware upgrade will not be attempted `r`n" | Out-File -FilePath $LogFile -Append
        }
    
        ### ElseIF Loop - Will perform a Hardware upgrade on $VM ###
        elseif(($VMToolsStatus -eq "toolsOK") -and ($VMHardware -ne "vmx-19"))
        {
    
            PowerOff-VM $vm $VMView
    
            $VMView.UpdateViewData("Summary.Runtime.PowerState") # update specfic VM info - PowerState
            $PowerOffVM = $VMView.Summary.Runtime.PowerState # Set $powerstate variable to new value from $VMView
    
            sleep 1
    
            ### If loop - If VM is powered down perform Hardware upgrade and power on VM ###
            if($PowerOffVM -eq "PoweredOff")
            {
                
                Write-Output "$(Get-Date) - Step 3.3 - Starting upgrade of hardware version on $VM. `r`n" | Out-File -FilePath $LogFile -Append
    
                ### perform Hardware version upgrade ###
                Get-View ($VMView.UpgradeVM_Task($VMHW)) | Out-Null # Upgrade the Hardware version on the VM to vmx-19
                
                sleep 2 # Sleep for 2 seconds to allow the Hardware upgrade time to complete
    
                $VMView.UpdateViewData("Config.Version") # update specfic VM info - Version
                $VMHardware = $VMView.Config.Version # Set $VMHardware to the current Hardware version of the VM
    
                PowerOn-VM $VM $VMView # Run PowerOn-VM function
                Write-Output "$(Get-Date) - Step 3.4 - $vm Hardware has been upgraded to $VMHardware. `r`n" | Out-File -FilePath $LogFile -Append
    
            }
    
            ### Else loop - If VM doesn't power down display error and stop script ###
            else
            {
                Write-Output "$(Get-Date) - Step 3.5 - There is something wrong with the hardware level or the tools of $VM. Skipping $VM. `r`n" | Out-File -FilePath $LogFile -Append
                Exit # Exit Sub script due to Error
            }
        }
        return $VMView # Return $VMView
    }
    
    ############################################### Main Code ###################################################################
    
    
    ### Start Log File ###
    Write-Output "$(Get-Date) ===============  vmtools & HW upgrade starting on $vm  ======================= `r`n"  | Out-File -FilePath $LogFile -Append
    
    ### Connecting to vCenters ###
    Connect-VIServer -Server $VCs -Credential $UserCredential -WarningAction SilentlyContinue | out-null # Login to all vCenters listed in the $VCs array
    Write-Output "$(Get-Date) =====  connecting to all listed vCenters - $VCs ===== `r`n" | Out-File $LogFile -Append
    
    ### Get initial view of $vm ###
    $VMView=@{} # Setup $VMView Hashtable
    $VMView = get-view -ViewType VirtualMachine -filter @{"Name"=$vm} # Get View of VM which contains all Status, parameters and configuration settings. Set the output to the $VMView Hashtable
    
    ### Run Reboot VM function- This is done first incase windows patches are pending ###
    Reboot-VM $VM $VMView
    sleep 5
    
    ### Create Snapshot on VM ###
    Write-Output "$(Get-Date) - Taking Snapshot on $vm pre upgrade work. `r`n" | Out-File -FilePath $LogFile -Append
    
    Poweroff-VM $VM $VMView
    Make-Snapshot $VM
    sleep 3
    PowerOn-VM $VM $VMView
    
    ### Run Upgrade-VMtools-version function - this will check the VMTools version on the VM and upgrade if needed ###
    Upgrade-vmtools-version $VM $VMView
    
    ### Run Upgrade-hardware-version function - this will check the Hardware version on the VM and upgrade if needed ###
    Upgrade-hardware-version $VM $VMView $VMHW
     
    ### End Log File ###
    Write-Output "$(Get-Date) ===== vmtools & HW upgrade Completed on $vm ===== `r`n"  | Out-File -FilePath $LogFile -Append

    Any ideas, or help would be much appreciated

    Thanks



  • 2.  RE: VMTools & Hardware upgrade script using PoshRSJob - Powershell

    Posted Jan 04, 2023 01:45 PM

    That is quite an impressive script.

    The first thing I would suggest changing is relying on the Start-Sleep cmdlet to make sure a Start-VM/Stop-VM/New-Snapshot/... has been completed.
    A better method imho is to check the Events (Get-VIEvent) for the completion of such actions.
    The VMPoweredOnEvent, VMPoweredOffEvent, ExtendedEvent (with msg com.vmware.dp.events.snapshotdone), ... are the ones to look for.

    Besides your own log you create, you might want to use a Transcript to monitor what exactly is happening in each job.
    Add a Start-Transcript with an unique filename at the start of the job script.






  • 3.  RE: VMTools & Hardware upgrade script using PoshRSJob - Powershell

    Posted Jan 04, 2023 03:32 PM

    Thanks for the quick reply,

    I will add the transcript to the top of the Job script as you suggested, this will give me a better idea of what is/isn't happening.

    As for the other change you mentioned, I am a little confused.

    I had already been using some sleep commands instead of Start-sleep, is there a difference between one and the other?

    And for the Get-VIEvent suggestion would this be in place of the do until loops I have, or would it be as well as those?



  • 4.  RE: VMTools & Hardware upgrade script using PoshRSJob - Powershell
    Best Answer

    Posted Jan 04, 2023 05:21 PM

    Sleep is an alias for Start-Sleep, they are the same.

    What I normally do is run Get-VEvent over the interval that starts when I launched the cmdlet, like for example Stop-VM, until I see an event that corresponds with the completion of the cmdlet (in this example the VMPoweredoffEvent).
    But there are alternatives, like doing the Get-VM in a loop, and waiting till the PowerState property changes



  • 5.  RE: VMTools & Hardware upgrade script using PoshRSJob - Powershell

    Posted Feb 21, 2023 04:40 PM

    I was able to get this running with your suggestion of get-vievent and also using the error stream of powershell to stop any of the jobs if an unexpected result is returned from any of the commands.



  • 6.  RE: VMTools & Hardware upgrade script using PoshRSJob - Powershell

    Posted Jan 22, 2024 10:08 AM

    Hi LucD,

    There's a new requirement, though with existing operational stuff on VMTools+Compatibility upgrade. However, there's a small user input required in the whole process. One of our environment is at version 5.5 with few clusters running several VM's. They're being shut down, migrated to 7.0.3 vCenter using Zerto. However, the process followed there was to power down servers based on vLAN's one-by-one on the source vCenter and post Zerto migrations, the VM's will need to be upgraded with vmtools and/or compatibility or even both based on server owner requirements. However, the sequencing they want to follow was to first perform tools upgrade and then only the compatibility if both upgrades are allowed to be selected. Post all this, they're looking for a clean HTML report file that we can look to showcase the success & obviously if any failures. I've got some tweaking's with the existing script they gave and I believe I messed it up somewhere where, on clicking the user input to update tools / compatibility or both, nothing happens. I'm attaching the script here and the likely flow for your reference. Would you please take a look and help me with the script? Sorry, this is little urgent...!!!

    Below is the complete script :

    Start-Transcript -OutputDirectory C:\TEMP\ScriptLogs\transcriptlog
    Add-Type -AssemblyName System.Windows.Forms
    #Import Module
    Write-Host "Importing PowerCLI module for VMware" -ForegroundColor Green
    Import-Module VMware.PowerCLI
    #Connect Source vCenter
    Write-Host "Provide creadentials to connect to Source vCenter" -ForegroundColor Green
    Connect-VIServer ddc1-vb01vmvc01
    #Get VM List
    Write-Host "Validating list of VM's inputted..." -ForegroundColor Green
    $vmlist = get-content C:\Users\eaf6ggj\desktop\VMList.txt
    $Datetime = (get-date).ToString("dd-MM-yyy-hh-mm-ss")
    #VM Status
    foreach($vm in $vmlist){
    # Shutdown vm
    if (get-vm $vm | where {$_.ExtensionData.Guest.ToolsStatus -eq "toolsOK"})
    { get-vm $vm | Shutdown-VMGuest -Confirm:$false
    Write-Host "Gracefully shutting down $vm" -ForegroundColor Green
    sleep 10
    get-vm $vm | where {$_.Powerstate -ne "PoweredOff"} |Stop-VM -Confirm:$false
    }
    # Poweroff VM
    else {write-host "VMware tools not running Forcefully powered off $vm" -ForegroundColor Orange
    get-vm $vm | where {$_.Powerstate -ne "PoweredOff"} |Stop-VM -Confirm:$false
    }}
    $ButtonClicked = [System.Windows.Forms.MessageBox]::Show("VM's at Source vCenter shutdown completely. If Zerto moves are complete, please click 'Ok' to connect to target vCenter for further action. Else, wait for Zerto move to complete", 'Warning', 'Ok', 'Warning')
    If ($ButtonClicked -eq 'Ok') {Write-Host "Ok was chosen."}
    #Connect Tartget vCenter
    Write-Host "Provide credentials to connect to target vCenter" -ForegroundColor Green
    Connect-VIServer ddc1-vc02.dm.mckesson.com
    $vmtools = New-Object System.Management.Automation.Host.ChoiceDescription 'Upgrade VMware Tools', 'Upgrade VMware Tools'
    $compatibility = New-Object System.Management.Automation.Host.ChoiceDescription 'Upgrade VM Compatibility', 'Upgrade VM Compatibility'
    $both = New-Object System.Management.Automation.Host.ChoiceDescription 'Upgrade both VM Tools and Compatibility', 'Upgrade both VM Tools and Compatibility'
    $options = [System.Management.Automation.Host.ChoiceDescription[]]($vmtools,$compatibility,$both)
    $title = 'Choose any one upgrade option'
    $message = "What do you want to do?"
    $result = $host.ui.PromptForChoice($title, $message, $options, 0)
    if ($result -eq 0) {
    $choice = "VMware Tools Upgrade"
    }
    elseif ($result -eq 1) {
    $choice = "VM Compatibility Upgrade"
    }
    elseif ($result -eq 2){
    $choice = "Upgradation of both VM Tools & H/W Compatibility"
    }
    else {
    $choice = "Try again"
    }
    "Performing : {0}" -f $choice
    #VM Status
    #Switch ($options){
    foreach($vm in $vmlist){
    # Shutdown vm
    #if (get-vm $vm | where {$_.ExtensionData.Guest.ToolsStatus -eq "toolsOK"})
    #{ get-vm $vm | Shutdown-VMGuest -Confirm:$false
    #Write-Host "Gracefully shutting down $vm" -ForegroundColor Green
    #sleep 10
    #get-vm $vm | where {$_.Powerstate -ne "PoweredOff"} |Stop-VM -Confirm:$false
    #}
    # Poweroff VM
    #else {write-host "VMware tools not running Forcefully powered off $vm" -ForegroundColor Orange
    #get-vm $vm | where {$_.Powerstate -ne "PoweredOff"} |Stop-VM -Confirm:$false
    #}
    # Take snpshot
    #Write-host "Taking snapshot for $vm"
    #New-Snapshot -VM $vm -Name BeforeVMTools
    # Power On VM
    #Start-VM -VM $vm -Confirm:$false
    #Write-Host "PoweredOn the VM $vm" -ForegroundColor yellow
    #sleep 20
    # Upgrading VM tools
    Write-Host "Starting VMware tools upgrade" -ForegroundColor Yellow
    if (get-vm $vm | where {$_.ExtensionData.Guest.ToolsStatus -ne "toolsNotInstalled"})
    {
    Get-VMGuest $vm | Update-Tools -NoReboot -Async
    Write-Host "upgrading VMware Tools for $vm" -ForegroundColor Green
    }
    else {
    Write-Host "Cannot update VM tools on $vm" -ForegroundColor Orange
    $vm | Out-File C:\TEMP\ScriptLogs\failedvms_"$datetime".txt
    }
    sleep 15
    #shutdown vm
    if (get-vm $vm | where {$_.ExtensionData.Guest.ToolsStatus -ne "toolsNotInstalled"})
    {get-vm $vm | Shutdown-VMGuest -Confirm:$false
    Write-Host "Gracefully shutting down $vm" -ForegroundColor Green
    sleep 15
    get-vm $vm | where {$_.Powerstate -ne "PoweredOff"} |Stop-VM -Confirm:$false
    }
    # Poweroff VM
    else {
    write-host "VMware tools not running Forcefully powered off $vm" -ForegroundColor Orange
    get-vm $vm | where {$_.Powerstate -ne "PoweredOff"} |Stop-VM -Confirm:$false
    }
    #upgrade vm hardware
    Write-Host "Upgrading VM Hardware Version for $vm" -ForegroundColor Yellow
    get-vm $vm | Set-VM -HardwareVersion vmx-19 -Confirm:$false # this is applicable for 7.0.3
    #poweron vms
    Write-Host "VM Hardware version updated and Power on $vm"
    Start-VM -VM $vm -Confirm:$false
    }
    Sleep 15
    #VM Status
    $report = @()
    $report = foreach($vm in $vmlist){
    if ($guestos = (Get-View -ViewType VirtualMachine -Filter @{"Name" = "$vm"}).Config.GuestFullName -like "*windows*"){
    #$dnsname = (Get-VM $vm).Guest.HostName
    write-host "Tools checking for $vm"
    get-service -name "VMTools" -ComputerName $vm | Where ($_.Status -ne 'Running') | start-service
    sleep 10
    }
    else {
    write-host "$vm is non windows" -ForegroundColor Orange
    $vm | out-file C:\TEMP\ScriptLogs\nonwindows_"$datetime".txt
    }
    Get-VM $vm | Select Name, PowerState,Hardwareversion,
    @{N='VMware Tools State';E={$_.ExtensionData.Guest.ToolsStatus}},
    @{N='VMware Tools Version';E={$_.ExtensionData.Guest.ToolsVersion}}
    }
    #return $options}
    $report | Export-csv C:\TEMP\ScriptLogs\Report_"$datetime".csv -NoTypeInformation -UseCulture
    Stop-Transcript
    Write-Host "Script completed" -ForegroundColor Green

    Interactive options based executionWorkflow



  • 7.  RE: VMTools & Hardware upgrade script using PoshRSJob - Powershell

    Posted Jan 22, 2024 12:10 PM

    What is in the transcript log?



  • 8.  RE: VMTools & Hardware upgrade script using PoshRSJob - Powershell

    Posted Jan 22, 2024 12:27 PM

    It just collects on-screen information and save it to the logfile . We want this script to handle exceptions which we’re planning to incorporate in the coming versions…!



  • 9.  RE: VMTools & Hardware upgrade script using PoshRSJob - Powershell

    Posted Jan 22, 2024 12:55 PM

    I just want to know where the script gets to.
    The screenshots don't tell me anything



  • 10.  RE: VMTools & Hardware upgrade script using PoshRSJob - Powershell

    Posted Jan 22, 2024 12:59 PM

    It gets stored at “C:\TEMP\ScriptLogs\transcriptlog” folder



  • 11.  RE: VMTools & Hardware upgrade script using PoshRSJob - Powershell

    Posted Jan 23, 2024 12:48 PM

    Hi Luc, 

    Just wanted to circle up if any way forward for this one? Thanks!



  • 12.  RE: VMTools & Hardware upgrade script using PoshRSJob - Powershell

    Posted Jan 30, 2024 03:17 PM

    Hi Neil_orourke,
    I wanted to try this script in my own test environment today, but I couldn't run it. It doesn't give any errors. Is this the final version? There are two sections: Main script and Job script, right? Any chance of sending your final revised version? Thanks in advance.



  • 13.  RE: VMTools & Hardware upgrade script using PoshRSJob - Powershell

    Posted Feb 01, 2024 01:14 PM

    Hi Enigma,

    I have iterated on the above script with the suggestions from LucD, the newer version currently runs 15 vmtools/hardware upgrades asynchronously. I have tested this in my lab 8 times successfully. hopefully it works for you too.

    Make sure to put in your directories in parameter blocks for both script files.

    vmtoolsHWMain_v7

     

    ###########################################
    ### vmtools & HW upgrade wrapper Script ###
    ###########################################
    
    ### Setup parameters/variables for the script ###
    
    [cmdletbinding()]
    param(
    ### Setup Credentials parameter which will be stored in a secure way
    [parameter(ValueFromPipelineByPropertyName=$true,ValueFromPipeline=$true,Mandatory=$false)]
    [PSCredential]$UserCredential,
    
    ### Create Array variables ###
    [object] $VMarray,
    [object] $jobs,
    
    ### Create String variables ###
    [string] $vms,
    [string] $VMName,
    [string] $form_date,
    
    ### Change this to the relevant vCenter you are connecting to ###
    [object] $VCs = @('********************'), #put vCenter addresses here
    
    ### Directory of where the script and files are located ###
    [string] $dir = "**********",
    
    ### Job sub script path ###
    [string] $Subdir = "*********",
    
    ### Initialze the log file location ###
    [string] $LogFile = "*********" + (Get-Date).tostring("dd-MM-yyyy") + ".txt", 
    
    ### Set the maximum number of background jobs that the script will run, The max allowed is 10 ###
    [int] $MaxJobs = 15,
    
    ### Set Hardware version you want to upgrade to ###
    [string] $VMHW = "vmx-19"
    )
    
    ### clear error stream ###
    $Error.Clear()
    
    #$DebugPreference="SilentlyContinue"
    
    ### Setup environment for script ###
    Import-Module -Name VMware.PowerCLI -ErrorAction SilentlyContinue | Out-Null
    Import-Module -Name VMware.VimAutomation.Core -ErrorAction SilentlyContinue | Out-Null
    Import-Module -Name PoshRSJob -ErrorAction SilentlyContinue | Out-Null
    
    set-powercliconfiguration -InvalidCertificateAction Ignore -defaultviservermode multiple -Confirm:$false
    #set-powercliconfiguration -InvalidCertificateAction Ignore -defaultviservermode Single -Confirm:$false
    
    
    ### Import Array ###
    $VMarray = Import-Csv "C:\Scripts\vmtoolsHW\vm_upgrades.csv" ## Change this to the location of your csv file
    $VMNames = Import-Csv "C:\Scripts\vmtoolsHW\vm_upgrades.csv" | Select -ExpandProperty "Name" # create array variable without a header
    
    ### Script starting time ###
    $form_date = Get-Date -Format "HH_mm_MM_dd_yyyy"
    
    ### Get VMTools, Hardware & Network status of Array VM's ### 
    function UpgradeCheck
    {
        ### Setup mandatory parameters to be passed to function ###
        param(
        [Parameter(Mandatory=$true, Position=0)]
        [object] $VMarray
        )
    
        Write-Output "$(Get-Date) - Collecting VM's in Array information on $VCs" | Tee-Object $LogFile -Append
    
        $vms = @() # Create the $vms Hashtable
        ### For Loop - Get VMs details ###
        ForEach ($item in $VMarray)
        {
            $VMName = $item.Name
            
            ### Variable getvm is required, for some reason the $vm cannot be used to query the host and the IP-address ###
            $getvm = Get-VM $VMName
            $vmview = Get-VM $VMName | Get-View
    
            ### Get required info from all the VM's ###
            $vmnic = Get-NetworkAdapter -VM $VMName
            $nicmac = Get-NetworkAdapter -VM $VMName | ForEach-Object {$_.MacAddress}
            $nictype = Get-NetworkAdapter -VM $VMName | ForEach-Object {$_.Type}
            $nicname = Get-NetworkAdapter -VM $VMName | ForEach-Object {$_.NetworkName}
            $VMInfo = "" | Select VMName,NICCount,IPAddress,MacAddress,NICType,NetworkName,GuestRunningOS,PowerState,ToolsVersion,ToolsStatus,ToolsRunningStatus,HWLevel,VMHost # Create all the headers for the columns in the Hashtable
            $VMInfo.VMName = $vmview.Name
            $VMInfo.NICCount = $vmview.Guest.Net.Count
            $VMInfo.IPAddress = [String]$getvm.Guest.IPAddress
            $VMInfo.MacAddress = [String]$nicmac
            $VMInfo.NICType = [String]$nictype
            $VMInfo.NetworkName = [String]$nicname
            $VMInfo.GuestRunningOS = $vmview.Guest.GuestFullname
            $VMInfo.PowerState = $getvm.PowerState
            $VMInfo.ToolsVersion = $vmview.Guest.ToolsVersion
            $VMInfo.ToolsStatus = $vmview.Guest.ToolsStatus
            $VMInfo.ToolsRunningStatus = $vmview.Guest.ToolsRunningStatus
            $VMInfo.HWLevel = $vmview.Config.Version
            $VMInfo.VMHost = $getvm.VMHost
            $vms += $VMInfo # Add all info gathered on VM to $vms Hashtable
        }
    
        ### Get time & Date for export ###
        $form_date = Get-Date -Format "HH_mm_MM_dd_yyyy"
    
        ### Format table and export to csv ###
        $dir_new = $dir 
        try{New-Item -Path $dir_new -ItemType Directory -ErrorAction Ignore -WarningAction Ignore | Out-Null}catch{} # ensure export path is available
        $csv = $dir_new + "\VMUpgradeReport-" + $form_date + ".csv" # create file name for exported report
        $vms | Export-Csv -Path $csv # export $vms array containing all VM info to $csv path
        Write-Output "$(Get-Date) - vmtools, Hardware and Network of VMs Report exported to: $csv" | Tee-Object -FilePath $LogFile -Append
    }
    
    ############################################### Main Code ###################################################################
    
    ### Start Log File ###
    Write-Output "$(Get-Date) ===============  vmtools & HW upgrade Script  ======================="  | Tee-Object -FilePath $LogFile -Append
    
    Write-Output $VMarray | Out-File $LogFile -Append
    
    ### Define form_date variable ###
    $form_date = Get-Date -Format "HH_mm_MM_dd_yyyy"
    
    ### ensure no prior vCenter connections exist ###
    Disconnect-VIServer -Server * -Confirm:$false -Force | Out-Null
    
    ### Get login credentials to vCenter ###
    $UserCredential = Get-Credential -message "Enter credentials for VCenter in format domain\username"
    
    #Connect-VIServer -Server $VCs -Credential $UserCredential -AllLinked -WarningAction SilentlyContinue | out-null
    Connect-VIServer -Server $VCs -Credential $UserCredential -WarningAction SilentlyContinue | out-null # Lab connection only
    Write-Output "$(Get-Date) ===============  connecting to $VCs  =================================" |  Tee-Object -FilePath $LogFile -Append
    
    ### create HW list of VM's pre upgrade ###
    Write-Output "$(Get-Date) - Pre Upgrade - Getting VM configuration and Status info for $VMarray `n" | Tee-Object -FilePath $LogFile -Append
    UpgradeCheck $VMarray
    
    ### code to kick off the VMtools & HW job for each vm ###
    Write-Output "$(Get-Date) - starting upgrade Jobs now `n" | Tee-Object -FilePath $LogFile -Append
    $VMNames | ForEach-Object {Start-RSJob -Name $_ -FilePath $Subdir -ArgumentList @($_, $UserCredential, $VCs, $VMHW) -Throttle $MaxJobs -batch "upgrades_$($_.name)" } | Wait-RSJob -ShowProgress # A Job will be queued up for each vm in the csv file we imported, the number of jobs that will run at once is based on $MaxJobs. A Progress bar will show until all Jobs have finished.
    
    Write-Output "$(Get-Date) - All Jobs completed `n`n"  | Tee-Object -FilePath $LogFile -Append
    
    ### Add all the Job results to the Log file ###
    Get-RSJob |Tee-Object -FilePath $LogFile -Append
    
    sleep 30
    
    ###create HW list of VM's post upgrade
    Write-Output "$(Get-Date) - Post Upgrade - Getting VM configuration and Status info for $VMarray `n" | Tee-Object -FilePath $LogFile -Append
    UpgradeCheck $VMarray
    
    Sleep 5
    
    ### remove RSJobs & disconnect-vCenter ###
    Get-RSJob | Remove-RSJob -Force | Tee-Object -FilePath $LogFile -Append
    Disconnect-VIServer -Server * -Confirm:$false -Force | Out-Null
    
    ### End Log File ###
    Write-Output "$(Get-Date) ===============  End of vmtools & HW upgrade Script  ======================="  | Tee-Object -FilePath $LogFile -Append
    
    ### Clear all variables ###
    Remove-Variable * -ErrorAction SilentlyContinue 

     

     

     

    vmtoolsHWsubv3_5

     

    #######################################
    ### vmtools & HW upgrade Sub Script ###
    #######################################
    
    #######################
    ### Version changes ###
    #######################
    
    # v3.1 - added guestoperationsready check to the Power on and Reboot functions
    # v3.2 - starting using debug stream to capture steps, added if loop in each function to exit job if Event error still present or timer = on any of the VM commands, modified the do until loop to use $Error instead
    # v3.3 - Added individual time variable for Task error checking within Do until loops in functions, changed error message write to include $VM 
    # v3.4 - Clean up script, remove debug outputs, increase sleep during reboot command loop, 
    # v3.5 - Changed operator of command do until loop to -contains and removed * from condition
    
    ### define required parameters for Sub script ###
    [cmdletbinding()]
    param(
    [string] $VM,
    [PSCredential]$UserCredential,
    [object] $VCs,
    [string] $VMHW 
    )
    ### Global variables ###
    [string] $Logfile = "********-"+ $VM +"-" + (Get-Date).tostring("dd-MM-yyyy") + ".txt" # Logfile path
    [object] $VMView # VM view info
    [int] $timer = 1 # timer for loop monitoring and breaks
    
    #$DebugPreference="Continue"
    
    ### Power on VM ###
    Function PowerOn-VM
    {
        ### define required parameters for Power On function ###
       param(
       [Parameter(Mandatory=$true, Position=0)]
       [object] $VM,
       [Parameter(Mandatory=$true, Position=1)]
       [object] $VMView,   
       [Parameter(Mandatory=$true, Position=2)]
       [int] $timer,
    
       ### Function individual variables ###
       [Parameter(Mandatory=$false)]
       [object] $EventInfo,
       [Parameter(Mandatory=$false)]
       [object] $LoopErrorInfo,
       [Parameter(Mandatory=$false)]
       [string] $ToolsStatus,
       [Parameter(Mandatory=$false)]
       [string] $ToolsRunningStatus,
       [Parameter(Mandatory=$false)]
       [string] $PowerState,
       [Parameter(Mandatory=$false)]
       [string] $GuestOperations,
       [Parameter(Mandatory=$false)]
       [string] $PowerOnTime,
       [Parameter(Mandatory=$false)]
       [string] $PowerOnErrorTime
       )
    
       ### Function start time - will be used to check task has been Implemented ###
       $PowerOnTime = Get-Date -UFormat "%d/%m/%Y %R"
       $timer = 1  
    
       ### Issue Start command to $VM - Do Until loop will ensure the command has run ### 
       do{        
            $PowerOnErrorTime = Get-Date -UFormat "%d/%m/%Y %R"
            $LoopErrorInfo = $null
    
            write-Output "$(Get-Date) - PowerOn-VM Function Step 1 - Checking Power on task ran against $VM. `r`n" | Tee-Object -FilePath $Logfile -Append
    
            $EventInfo = Get-VIEvent -Entity $VM -Types Info -start $PowerOnTime | Where-Object {$_.FullFormattedMessage -Like "Task: Power On virtual machine"} 
            $LoopErrorInfo = Get-VIEvent -Entity $VM -Types Error -start $PowerOnErrorTime 
    
            If(($EventInfo -eq $null) -or ($LoopErrorInfo -ne $null))
            {
                Start-VM -VM $VM -Confirm:$false -RunAsync | Wait-Task
            }
        
            Start-Sleep -s 20 
    
            $EventInfo = Get-VIEvent -Entity $VM -Types Info -start $PowerOnTime | Where-Object {$_.FullFormattedMessage -Like "Task: Power On virtual machine"} 
            $LoopErrorInfo = Get-VIEvent -Entity $VM -Types Error -start $PowerOnErrorTime 
    
            write-output "$(Get-Date) - $VM - Power on Error: `r`n" | Tee-Object -FilePath $Logfile  -Append
            write-Output $LoopErrorInfo | Tee-Object -FilePath $Logfile -Append
            Write-Output "$(Get-Date) - $VM - Power on task = Created Time : $($EventInfo.CreatedTime) , $($EventInfo.FullFormattedMessage). Power On command attempt = $timer  `r`n" | Tee-Object -FilePath $Logfile -Append        
            
            $timer = ++$timer # Add 1 to the timer variable  
    
            Start-Sleep 2
    
        }until((($EventInfo.FullFormattedMessage -contains "Task: Power On virtual machine") -and ($LoopErrorInfo -eq $null)) -or ($timer -eq 5))
    
       If(($LoopErrorInfo -ne $null) -or ($timer -eq 5)) 
       {
            Write-Output "$(Get-Date) - PowerOn-VM Function Step 1.1 - Power on VM command failed on $VM, exiting Job. `r`n" | Tee-Object -FilePath $Logfile -Append
            $StepMapArray += "$(Get-Date) - Power On VM Command run - Status = False" | Out-Null
            Write-Error "VMHW Error on $VM = True"  
       }          
    
       Write-Output "$(Get-Date) - PowerOn-VM Function Step 2 - Power on VM command issued on $VM, $VM is starting. `r`n" | Tee-Object -FilePath $Logfile -Append   
       $timer = 1 # Reset timer variable and set to 0, it will be used as an escape from the loop checking VM is powered on
    
       ### Do until loop after Power On command has been issued, to wait until VM has Power Status of PowerOn & Tools Status is appearing, will keep checking VM until VM is powered on or timer gets to 30 ### 
    
       do
       {
            $VMView.UpdateViewData("Summary.Runtime.PowerState") # update specfic VM info - PowerState
            $Powerstate = $VMView.Summary.Runtime.PowerState # Set $Powerstate variable to new value from $VMView
    
            $VMView.UpdateViewData("Summary.Guest.ToolsStatus") # update specfic VM info - ToolsStatus
            $VMView.UpdateViewData("Summary.Guest.ToolsRunningStatus") # update specfic VM info - ToolsStatus
            $ToolsStatus = $VMView.Summary.Guest.ToolsStatus # Set $ToolsStatus variable to new value from $VMView
            $ToolsRunningStatus = $VMView.Summary.Guest.ToolsRunningStatus # Set $ToolsRunningStatus variable to new value from $VMView
    
            $VMView.UpdateViewData("Guest.GuestOperationsReady") # update specfic VM info - Guest Operations Ready
            $GuestOperations = $VMView.Guest.GuestOperationsReady # Set $Powerstate variable to new value from $VMView
    
            $timer = ++$timer # Add 1 to the timer variable
     
            Write-Output "$(Get-Date) - PowerOn-VM Function Step 2 - $VM is starting, powerstate is $Powerstate and toolsstatus is $ToolsStatus. `r`n" | Tee-Object -FilePath $Logfile -Append
        
            Start-Sleep -s 10 # Sleep loop for 10 seconds
     
        }until(($Powerstate -match "PoweredOn") -and ((($ToolsStatus -match "toolsOk") -and ($ToolsRunningStatus -match "guestToolsRunning") -and ($GuestOperations -match "True")) -or (($ToolsStatus -match "toolsOld") -and ($ToolsRunningStatus -match "guestToolsRunning") -and ($GuestOperations -match "True")) -or ($ToolsStatus -match "toolsNotInstalled")) -or ($timer -eq 61))
     
        ### Check Power status after checking loop, If powered ON and Tools  return normal to log file, If VM is not powered off exit script ###
        if (($Powerstate -match "PoweredOn") -and (($ToolsStatus -match "toolsOk") -or ($ToolsStatus -match "toolsOld") -or ($ToolsStatus -match "toolsNotInstalled"))) # Loop will run until conditions are met.
        {
          Write-Output "$(Get-Date) - PowerOn-VM Function Step 3 - $VM is started and has ToolsStatus - $ToolsStatus. `r`n" | Tee-Object -FilePath $Logfile -Append
        }
        ### Else Loop - If VM didn't power on show Error message and exit script ###
        else
        {
          Write-Output "$(Get-Date) - PowerOn-VM Function Step 4 - PowerOn Error detected on $VM, exiting Job. `r`n" | Tee-Object -FilePath $Logfile -Append
          Write-Error "VMHW Error on $VM = True"
        }
    
        ### Result variables to $null so function is starting from scratch on next call of function ###
        $PowerOnTime = $null; $timer = $null; $EventInfo = $null; $LoopErrorInfo = $null
    
    }
    
    ### Shutdown VM ###
    function poweroff-VM
    {
        ### define required parameters for Power Off function ##
        param(
        [Parameter(Mandatory=$true, Position=0)]
        [object] $VM,
        [Parameter(Mandatory=$true, Position=1)]
        [object] $VMView,   
        [Parameter(Mandatory=$true, Position=2)]
        [int] $timer,
    
        ### Function individual variables ###
        [Parameter(Mandatory=$false)]
        [object] $EventInfo,
        [Parameter(Mandatory=$false)]
        [object] $LoopErrorInfo,
        [Parameter(Mandatory=$false)]
        [string] $ToolsStatus,
        [Parameter(Mandatory=$false)]
        [string] $ToolsRunningStatus,
        [Parameter(Mandatory=$false)]
        [string] $PowerState,
        [Parameter(Mandatory=$false)]
        [string] $PowerOffTime,
        [Parameter(Mandatory=$false)]
        [string] $PowerOffErrorTime
        )
    
       ### Function start time - will be used to check task has been Implemented ###
       $PowerOffTime = Get-Date -UFormat "%d/%m/%Y %R"
       $timer = 1 
    
       ### Issue Guest OS Shutdown command to $VM - Do Until loop will ensure the command has run ### 
       do
       {
          $PowerOffErrorTime = Get-Date -UFormat "%d/%m/%Y %R"
          
          $LoopErrorInfo = $null
          
          Write-Output "$(Get-Date) - PowerOff-VM Function Step 1 - Checking Power Off task ran against $VM. `r`n" | Tee-Object -FilePath $Logfile -Append
         
          $EventInfo = Get-VIEvent -Entity $VM -Types Info -start $PowerOffTime | Where-Object {$_.FullFormattedMessage -like "*Task: Initiate guest OS shutdown*"} 
          $LoopErrorInfo = Get-VIEvent -Entity $VM -Types Error -start $PowerOffErrorTime 
    
          If(($EventInfo-eq $null) -or ($LoopErrorInfo -ne $null))
          {
            Shutdown-VMGuest -VM $VM -Confirm:$false
          }
          
          Start-Sleep -s 20 # Sleep loop for 20 seconds
    
          $EventInfo = Get-VIEvent -Entity $VM -Types Info -start $PowerOffTime | Where-Object {$_.FullFormattedMessage -like "*Task: Initiate guest OS shutdown*"} 
          $LoopErrorInfo = Get-VIEvent -Entity $VM -Types Error -start $PowerOffErrorTime 
    
          Write-Output "$(Get-Date) - $VM - Power off task = Created Time : $($EventInfo.CreatedTime) , $($EventInfo.FullFormattedMessage). Power Off command attempt = $timer  `r`n" | Tee-Object -FilePath $Logfile -Append
          write-output "$(Get-Date) - $VM - Power Off Error: `r`n" | Tee-Object -FilePath $Logfile  -Append
          write-Output $LoopErrorInfo | Tee-Object -FilePath $Logfile -Append
          
          $timer = ++$timer # Add 1 to the timer variable
    
          Start-Sleep 2
    
       }until((($EventInfo.FullFormattedMessage -contains "Task: Initiate guest OS shutdown") -and ($LoopErrorInfo -eq $null)) -or ($timer -eq 5))
    
       If(($LoopErrorInfo -ne $null) -or ($timer -eq 5)) 
       {
            Write-Output "$(Get-Date) - PowerOff-VM Function Step 1.1 - Power off VM command failed on $VM, exiting Job. `r`n" | Tee-Object -FilePath $Logfile -Append
            Write-Error "VMHW Error on $VM = True"
       }
    
       Write-Output "$(Get-Date) - PowerOff-VM Function Step 2 - Shutdown command issued to $VM. `r`n" | Tee-Object -FilePath $Logfile -Append
       $timer = 0 # Setup timer variable and set to 0, it will be used as an escape from the loop checking VM is powered down
    
       ### 5 minute loop after shutdown Guest OS command has been issued, will keep checking VM until VM is powered off or timer gets to 30 ### 
       do
       {
    
          $VMView.UpdateViewData("Summary.Runtime.PowerState") # update specfic VM info - PowerState
          $Powerstate = $VMView.Summary.Runtime.PowerState # Set $Powerstate variable to new value from $VMView
    
          $VMView.UpdateViewData("Summary.Guest.ToolsRunningStatus") # update specfic VM info - ToolsStatus
          $ToolsRunningStatus = $VMView.Summary.Guest.ToolsRunningStatus # Set $ToolsStatus variable to new value from $VMView
    
          $timer = ++$timer # Add 1 to the timer variable
    
          Write-Output "$(Get-Date) - PowerOff-VM Function Step 3 - Loop checking $VM is stopping: powerstate = $Powerstate , toolsRunningStatus = $ToolsRunningStatus. `r`n" | Tee-Object -FilePath $Logfile -Append
          
          Start-Sleep -s 10 # Sleep loop for 10 seconds
    
       }until(($Powerstate -match "PoweredOff") -or ($timer -eq 60)) # Loop will run until either condition is met.
    
       ### Check Power status after checking loop, If powered off return normal to log file, If VM is not powered off exit script ###
     
       if (($Powerstate -match "PoweredOff") -and (($ToolsRunningStatus -match "toolsNotRunning") -or ($ToolsStatus -match "toolsNotInstalled")))
       {
          Write-Output "$(Get-Date) - PowerOff-VM Function Step 4 - $VM is powered-off. `r`n" | Tee-Object -FilePath $Logfile -Append
       }
    
       ### Else Loop - If VM didn't power down show Error message and exit script ###
       else
       {
    	  Write-Output "$(Get-Date) - PowerOff-VM Function Step 5 - PowerOff Error detected on $VM. `r`n"| Tee-Object -FilePath $Logfile -Append
          Write-Error "VMHW Error on $VM = True"
       }
    
       ### Result variables to $null so function is starting from scratch on next call of function ###
       $PowerOffTime = $null; $timer = $null; $EventInfo = $null; $LoopErrorInfo = $null
    
    }
    
    function Reboot-VM
    {
        ### define required parameters for Reboot VM function ###
        param(
       [Parameter(Mandatory=$true, Position=0)]
       [object] $VM,
       [Parameter(Mandatory=$true, Position=1)]
       [object] $VMView,   
       [Parameter(Mandatory=$true, Position=2)]
       [int] $timer,
    
    
       ### Function individual variables ###
       [Parameter(Mandatory=$false)]
       [object] $EventInfo,
       [Parameter(Mandatory=$false)]
       [object] $LoopErrorInfo,
       [Parameter(Mandatory=$false)]
       [string] $ToolsStatus,
       [Parameter(Mandatory=$false)]
       [string] $ToolsRunningStatus,
       [Parameter(Mandatory=$false)]
       [string] $PowerState,
       [Parameter(Mandatory=$false)]
       [string] $GuestOperations,
       [Parameter(Mandatory=$false)]
       [string] $RebootTime,
       [Parameter(Mandatory=$false)]
       [string] $RebootErrorTime
       )
       $Powerstate = $VMView.Summary.Runtime.PowerState # Set $Powerstate variable to new value from $VMView
    
        ### Function start time - will be used to check task has been Implemented ###
        $RebootTime = Get-Date -UFormat "%d/%m/%Y %R"
        $timer = 1   
    
        ### Initial If loop to ensure VM is in PoweredOn state before anything is done on VM ###
        If($Powerstate -ne "PoweredOn")
        {
          Write-Output "$(Get-Date) - Step 1.0 - $VM has power state - $Powerstate `r`n Upgrade of VMTools and Hardware will not be attempted. `r`n Exiting Job. `r`n" | Tee-Object -FilePath $Logfile -Append
          Write-Error "VMHW Error on $VM = True" 
        }
    
        ### Else Loop - If VM is PoweredOn start reboot ###
        else
        {
            Write-Output "$(Get-Date) - Reboot-VM Function Step 1 - Issuing a Reboot Guest OS command to $VM. `r`n" | Tee-Object -FilePath $Logfile -Append        
            
            ### Issue restart VM guest OS command to $VM - Do Until loop will ensure the command has run ### 
            do
            {
                $RebootErrorTime = Get-Date -UFormat "%d/%m/%Y %R"            
                $LoopErrorInfo = $null
    
                Write-Output "$(Get-Date) - Reboot-VM Function Step 2 - Checking Reboot Guest OS task ran against $VM. `r`n" | Tee-Object -FilePath $Logfile -Append
    
                $EventInfo = Get-VIEvent -Entity $VM -start $RebootTime | Where-Object {$_.FullFormattedMessage -like "*Task: Initiate guest OS reboot*"} 
                $LoopErrorInfo = Get-VIEvent -Entity $VM -Types Error -start $RebootErrorTime
    
                If(($EventInfo -eq $null) -or ($LoopErrorInfo -ne $null))
                {
                    Get-VM $VM | Restart-VMGuest -Confirm:$false
                }
                
                Start-Sleep -s 45 # Sleep loop for 45 seconds
                
                $EventInfo = Get-VIEvent -Entity $VM -start $RebootTime | Where-Object {$_.FullFormattedMessage -contains "Task: Initiate guest OS reboot"} 
                $LoopErrorInfo = Get-VIEvent -Entity $VM -Types Error -start $RebootErrorTime 
    
                write-output "$(Get-Date) - $VM - Reboot Error: `r`n" | Tee-Object -FilePath $Logfile  -Append
                write-Output $LoopErrorInfo | Tee-Object -FilePath $Logfile -Append
                Write-Output "$(Get-Date) - $VM - Reboot task = Created Time : $($EventInfo.CreatedTime) , $($EventInfo.FullFormattedMessage). Reboot command attempt = $timer  `r`n" | Tee-Object -FilePath $Logfile -Append
    
                $timer = ++$timer # Add 1 to the timer variable
    
                Start-Sleep 2
    
            }until((($EventInfo.FullFormattedMessage -contains "Task: Initiate guest OS reboot") -and ($LoopErrorInfo -eq $null)) -or ($timer -eq 5))
    
            If(($LoopErrorInfo -ne $null) -or ($timer -eq 5)) 
            {
                Write-Output "$(Get-Date) - Reboot-VM Function Step 2.1 - Reboot VM command failed on $VM, exiting Job. `r`n" | Tee-Object -FilePath $Logfile -Append
                Write-Error "VMHW Error on $VM = True"  
            }
    
            Start-Sleep -s 30 # pause the script for 5 seconds to allow time for the reboot guest OS command to commence
    
            $timer = 0 # Reset timer variable  to 0, it will be used as an escape from the loop checking VM Rebooted
            
            ### 20 minute loop after Reboot Guest OS command has been issued, will keep checking VM until VM is powered on or timer gets to 120 ### 
            do
            {
                Start-Sleep -s 10 # Sleep loop for 10 seconds
    
                $VMView.UpdateViewData("Summary.Runtime.PowerState") # update specfic VM info - PowerState
                $Powerstate = $VMView.Summary.Runtime.PowerState # Set $Powerstate variable to new value from $VMView
                $VMView.UpdateViewData("Summary.Guest.ToolsStatus") # update specfic VM info - ToolsStatus
                $ToolsStatus = $VMView.Summary.Guest.ToolsStatus # Set $ToolsStatus variable to new value from $VMView
                $VMView.UpdateViewData("Summary.Guest.ToolsRunningStatus") # update specfic VM info - ToolsStatus
                $ToolsRunningStatus = $VMView.Summary.Guest.ToolsRunningStatus # Set $ToolsRunningStatus variable to new value from $VMView
    
                $VMView.UpdateViewData("Guest.GuestOperationsReady") # update specfic VM info - Guest Operations Ready
                $GuestOperations = $VMView.Guest.GuestOperationsReady # Set $Powerstate variable to new value from $VMView
    
                $timer = ++$timer # Add 1 to the timer variable
    
                Write-Output "$(Get-Date) - Reboot-VM Function Step 3 - Loop checking $VM is rebooting: powerstate = $Powerstate , toolsStatus = $ToolsStatus. `r`n" | Tee-Object -FilePath $Logfile -Append
     
            }until((($ToolsStatus -match "toolsOk") -and ($ToolsRunningStatus -match "guestToolsRunning") -and ($GuestOperations -match "True")) -or (($ToolsStatus -match "toolsOld") -and ($ToolsRunningStatus -match "guestToolsRunning") -and ($GuestOperations -match "True")) -or (($ToolsStatus -match "toolsnotinstalled") -and ($Powerstate -match "PoweredOn"))-or ($timer -eq 121)) # Loop will run until a condition is met.
    
            ### Check Power status after check loop, If powered ON and Tools return normal to log file, If VM is not rebooted exit script ###
            if (($Powerstate -match "PoweredOn") -and (($ToolsStatus -match "toolsOk") -or ($ToolsStatus -match "toolsOld") -or ($ToolsStatus -match "toolsNotInstalled")))
            {
                Write-Output "$(Get-Date) - Reboot-VM Function Step 4 - $VM is started and has ToolsStatus $ToolsStatus. `r`n" | Tee-Object -FilePath $Logfile -Append
                $StepMapArray += "$(Get-Date) - Reboot command successful - Status = True" | Out-Null
            }
       
            ### Else Loop - If VM didn't Reboot show Error message and exit script ###
            else
            {
                Write-Output "$(Get-Date) - Reboot-VM Function Step 5 - PowerOn Error detected on $VM, exiting Job. `r`n" | Tee-Object -FilePath $Logfile -Append
                Write-Error "VMHW Error on $VM = True"
            }
       }
    
       Start-Sleep -s 20
    
       ### Result variables to $null so function is starting from scratch on next call of function ###
       $RebootTime = $null; $timer = $null; $EventInfo = $null; $LoopErrorInfo = $null
    
    }
    
    ### Take a Snapshot ### 
    function Make-Snapshot
    {
      ### define required parameters for Make-Snapshot function ###
      param(
      [Parameter(Mandatory=$true, Position=0)]
      [object] $VM,
      [Parameter(Mandatory=$true, Position=1)]
      [int] $timer,
    
      ### function specific parameters ###
      [Parameter(Mandatory=$false)]
      [string] $SnapTime,
      [Parameter(Mandatory=$false)]
      [string] $SnapErrorTime
      )  
    
      ### Function Variable values ###
      $SnapName = "VMTools&HWPreUpgradeSnap"
      $SnapTime = Get-Date -UFormat "%d/%m/%Y %R"
      $timer = 1 
    
      ### Create Snapshot on $VM with below -Name and -Description ###
      
      Write-Output "$(Get-Date) - Make-Snapshot Function Step 1 - Taking Snapshot on $VM pre upgrade work. `r`n" | Tee-Object -FilePath $Logfile -Append
    
      ### perform Hardware version upgrade ###
      do
      {
            $SnapErrorTime = Get-Date -UFormat "%d/%m/%Y %R"
            $LoopErrorInfo = $null
    
            Write-Output "$(Get-Date) - Make-Snapshot Function Step 2 - Checking Snapshot command runs on $VM. SnapTime = $SnapTime `r`n" | Tee-Object -FilePath $Logfile -Append
    
            $EventInfo = Get-VIEvent -Entity $VM -Type Info -start $SnapTime | Where-Object {$_.FullFormattedMessage -Like "*Task: Create virtual machine snapshot*"} 
            $LoopErrorInfo = Get-VIEvent -Entity $VM -Types Error -start $SnapErrorTime 
            
            If(($EventInfo -eq $null) -or ($LoopErrorInfo -ne $null))
            {
                $SnapshotVM = New-Snapshot -VM $VM -Name $SnapName -Description "Snapshot taken before Tools and Hardware was updated" -Memory:$False -Quiesce:$False -Confirm:$false -RunAsync
            }
    
            Start-Sleep -s 20 # Sleep loop for 20 seconds
    
            $EventInfo = Get-VIEvent -Entity $VM -Type Info -start $SnapTime | Where-Object {$_.FullFormattedMessage -Like "*Task: Create virtual machine snapshot*"} 
            $LoopErrorInfo = Get-VIEvent -Entity $VM -Types Error -start $SnapErrorTime  
    
            write-output "$(Get-Date) - $VM - Snap Error: `r`n" | Tee-Object -FilePath $Logfile  -Append
            write-Output $LoopErrorInfo | Tee-Object -FilePath $Logfile -Append
    
            Write-Output "$(Get-Date) - $VM - Snap task = Created Time : $($EventInfo.CreatedTime) , $($EventInfo.FullFormattedMessage). Snapshot command attempt = $timer  `r`n" | Tee-Object -FilePath $Logfile -Append
                
            $timer = ++$timer # Add 1 to the timer variable
    
            Start-Sleep 2
    
      }until((($EventInfo.FullFormattedMessage -contains "Task: Create virtual machine snapshot") -and ($LoopErrorInfo -eq $null)) -or ($timer -eq 5))
    
      If(($LoopErrorInfo -ne $null) -or ($timer -eq 5)) 
      {
            Write-Output "$(Get-Date) - Snap Function Step 2.1 - Snapshot of VM command failed on $VM, exiting Job. `r`n" | Tee-Object -FilePath $Logfile -Append
            Write-Error "VMHW Error on $VM = True" 
      }
    
      Write-Output " $(Get-Date) - Make-Snapshot Function Step 3 - Snapshot command issued on $VM"| Tee-Object -FilePath $Logfile -Append
    
      Start-Sleep -s 30
      
      ### Ensure Snapshot is present on VM before proceeding, exit script if snap not taken
      $SnapCheck = Get-Snapshot -VM $VM -Name $SnapName 
      if($Snapcheck.Name -ne $SnapName)
      {
        Write-Output "$(Get-Date) - Make-Snapshot Function Step 4 - There was a problem taking a Snapshot on $VM, exiting script.`r`n"  | Tee-Object -FilePath $Logfile -Append
        PowerOn-VM $VM        
        Write-Error "VMHW Error on $VM = True"  
      }
    
      Write-Output " $(Get-Date) - Make-Snapshot Function Step 3 - Snapshot successful on $VM"| Tee-Object -FilePath $Logfile -Append
    
       ### Result variables to $null so function is starting from scratch on next call of function ###
       $SnapTime = $null; $timer = $null; $EventInfo = $null; $LoopErrorInfo = $null   
    }
    
    ### upgrade VMTools of VM ###
    function Upgrade-vmtools-version
    {
        ### define required parameters for Upgrade-vmtools-version function ###
        param(
       [Parameter(Mandatory=$true, Position=0)]
       [object] $VM,
       [Parameter(Mandatory=$true, Position=1)]
       [object] $VMView,   
       [Parameter(Mandatory=$true, Position=2)]
       [int] $timer,
    
       ### Function individual variables ###
       [Parameter(Mandatory=$false)]
       [object] $EventInfo,
       [Parameter(Mandatory=$false)]
       [object] $LoopErrorInfo,
       [Parameter(Mandatory=$false)]
       [string] $ToolsStatus,
       [Parameter(Mandatory=$false)]
       [string] $ToolsRunningStatus,
       [Parameter(Mandatory=$false)]
       [string] $PowerState,
       [Parameter(Mandatory=$false)]
       [string] $family,
       [Parameter(Mandatory=$false)]
       [string] $ToolsTime,
       [Parameter(Mandatory=$false)]
       [string] $ToolsErrorTime
        )
    
        ### Function variable values ###
        $ToolsTime = Get-Date -UFormat "%d/%m/%Y %R"
        $timer = 1
    
        $family = $VMView.Guest.GuestFamily # Set $family variable to the Guest OS Family of $VM from the $VMView Hashtable
    
        $VMView.UpdateViewData("Summary.Guest.ToolsStatus") # update specfic VM info - ToolsStatus   
        $ToolsStatus = $VMView.Summary.Guest.ToolsStatus # Set $VMToolsStatus variable to new value from $VMView
     
        Write-Output "$(Get-Date) - Upgrade-vmtools-version Function Step 1 - Starting VMTools Check/Upgrade on $VM , VMTools Status - $ToolsStatus & Guest OS - $family. `r`n" | Tee-Object -FilePath $Logfile -Append
    
        ### Inital If loop will check VMTools status, if the Tools are up to date it will skip the upgrade. ###
        if($ToolsStatus -eq "toolsOK")
        {
            Write-Output "$(Get-Date) - Upgrade-vmtools-version Function Step 2 - The VM tools up to date -  $ToolsStatus on $VM. `r`n" | Tee-Object -FilePath $Logfile -Append
    
        }
    
        ### ElseIF Loop - Will perform a VMTools upgrade on $VM ###
        elseif(($family -eq "windowsGuest") -and ($ToolsStatus -ne "toolsNotInstalled") -and ($ToolsStatus -ne "toolsNotRunning"))
        {       
            ### Perform VMTools upgrade with a surpressed reboot ###
            Write-Output "$(Get-Date) - Upgrade-vmtools-version Function Step 3 - The VM tools are $ToolsStatus on $VM. Starting update/install now, This will take at few minutes.`r`n" | Tee-Object -FilePath $Logfile -Append
            
            Start-Sleep 1 # Pause for 1 second
            ### Issue VMTools Upgrade to $VM - Do Until loop will ensure the command has run ### 
            do
            {
                $ToolsErrorTime = Get-Date -UFormat "%d/%m/%Y %R"
                $LoopErrorInfo = $null
                
                Write-Output "$(Get-Date) - Upgrade-vmtools-version Function Step 4 - Checking VMTools upgrade command runs $VM. `r`n" | Tee-Object -FilePath $Logfile -Append
    
                $EventInfo = Get-VIEvent -Entity $VM -Type Info -start $ToolsTime | Where-Object {$_.FullFormattedMessage -Like "*Task: Initiated VMware Tools install or upgrade*"} 
                $LoopErrorInfo = Get-VIEvent -Entity $VM -Type Error -start $ToolsErrorTime
              
                If(($EventInfo -eq $null) -or ($LoopErrorInfo -ne $null))
                {
                $UpdateToolsVM = Get-VMGuest $VM | Update-Tools -NoReboot -RunAsync | Wait-Task
                }
    
                Start-Sleep -s 20 # Sleep loop for 10 seconds
                
                $EventInfo = Get-VIEvent -Entity $VM -Type Info -start $ToolsTime | Where-Object {$_.FullFormattedMessage -Like "Task: Initiated VMware Tools install or upgrade"} 
                $LoopErrorInfo = Get-VIEvent -Entity $VM -Type Error -start $ToolsErrorTime
    
                write-Output "$(Get-Date) - $VM - Tools ugrade Error: `r`n" | Tee-Object -FilePath $Logfile  -Append
                write-Output $LoopErrorInfo | Tee-Object -FilePath $Logfile -Append
                Write-Output "$(Get-Date) - $VM - Tools Upgrade task = Created Time : $($EventInfo.CreatedTime) , $($EventInfo.FullFormattedMessage). VMtools upgrade attempt = $timer  `r`n" | Tee-Object -FilePath $Logfile -Append
               
                $timer = ++$timer # Add 1 to the timer variable
    
                Start-Sleep 2
    
            }until((($EventInfo.FullFormattedMessage -contains "Task: Initiated VMware Tools install or upgrade") -and ($LoopErrorInfo -eq $null)) -or ($timer -eq 5))
            
            If(($LoopErrorInfo -ne $null) -or ($timer -eq 5)) 
            {
                Write-Output "$(Get-Date) - Upgrade-vmtools-version Function Step 4.1 - attempt to upgrade vmtools failed on $VM, exiting Job. `r`n" | Tee-Object -FilePath $Logfile -Append
                Write-Error "VMHW Error on $VM = True"
            }
    
            $timer = 0 # reset timer for upgrade status loop
    
            ### Loop to check Tools upgrade status ###
            do
            {           
                Start-Sleep 10 # Sleep loop for 10 seconds        
    
                $VMView.UpdateViewData("Summary.Guest.ToolsStatus") # update specfic VM info - ToolsStatus
                $ToolsStatus = $VMView.Summary.Guest.ToolsStatus # Set $VMToolsStatus variable to new value from $VMView
    
                $timer = ++$timer # Add 1 to the timer variable
    
                Write-Output "$(Get-Date) - Upgrade-vmtools-version Function Step 5 - Checking VMTools status on $VM now - $ToolsStatus. `r`n" | Tee-Object -FilePath $Logfile -Append
    
            }until(($ToolsStatus -eq "toolsOK") -or ($timer -eq 61)) # Loop will run until a condition is met.      
    
            ### If Loop - If Tools upgrade was successful and $VMToolsStatus is now toolsOK reboot the $VM ###
            If($ToolsStatus -eq "toolsOK")
            {
                Write-Output "$(Get-Date) - Upgrade-vmtools-version Function Step 6 - Issuing reboot Guest OS command to $VM. `r`n" | Tee-Object -FilePath $Logfile -Append
                Reboot-VM $VM $VMView $timer # Call Reboot-VM function and pass $VM & $VMView parameters          
            }
    
            ### Else loop - If the $VMToolsStatus didn't come back as toolsOK display error and stop script ###
            else
            {
                Write-Output "$(Get-Date) - Upgrade-vmtools-version Function Step 7 - There was a problem while trying to run the vm tools upgrade on $VM. `r`n Tools status - $VMToolsStatus `r`n Guest OS - $family `r`n no upgrade will be performed. `r`n" | Tee-Object -FilePath $Logfile -Append
                Write-Error "VMHW Error on $VM = True"
            } 
        }
    
        ### Else loop - If the $VMToolsStatus is ToolsNotrunning display error and stop script ###
        else
        {
            Write-Output "$(Get-Date) - Upgrade-vmtools-version Function Step 8 - There was a problem while trying to run the vm tools upgrade on $VM. `r`n Tools status - $ToolsStatus `r`n Guest OS - $family `r`n no upgrade will be performed. `r`n" | Tee-Object -FilePath $Logfile -Append
            Write-Error "VMHW Error on $VM = True"
        }
    
        ### Result variables to $null so function is starting from scratch on next call of function ###
        $ToolsTime = $null; $timer = $null; $EventInfo = $null; $LoopErrorInfo = $null
    
    }
    
    ### Check & upgrade Hardware version of VM ###
    function Upgrade-hardware-version
    {
      ### define required parameters for Upgrade-vmtools-version function ###
        param(
       [Parameter(Mandatory=$true, Position=0)]
       [object] $VM,
       [Parameter(Mandatory=$true, Position=1)]
       [object] $VMView,   
       [Parameter(Mandatory=$true, Position=2)]
       [int] $timer,
       [Parameter(Mandatory=$true, Position=3)]
       [string] $VMHW,
    
       ### Function individual variables ###
       [Parameter(Mandatory=$false)]
       [object] $EventInfo,
       [Parameter(Mandatory=$false)]
       [object] $LoopErrorInfo,
       [Parameter(Mandatory=$false)]
       [string] $ToolsStatus,
       [Parameter(Mandatory=$false)]
       [string] $ToolsRunningStatus,
       [Parameter(Mandatory=$false)]
       [string] $PowerState,
       [Parameter(Mandatory=$false)]
       [string] $HWTime,
       [Parameter(Mandatory=$false)]
       [string] $HWErrorTime
        )
    
        ### Function variable values set ###
        $HWTime = Get-Date -UFormat "%d/%m/%Y %R"
        
        $VMHardware = $VMView.Config.Version # Set $VMHardware to the current Hardware version of the VM
        $VMView.UpdateViewData("Summary.Guest.ToolsStatus") # update specfic VM info - ToolsStatus
        $ToolsStatus = $VMView.Summary.Guest.ToolsStatus # Set $VMToolsStatus variable to new value from $VMView
    
        write-output "$(Get-Date) - $VM - HW ugrade function starting, Tools status = $ToolsStatus & Hardware version = $VMHardware `r`n" | Tee-Object -FilePath $Logfile  -Append
    
        sleep 1
    
        ### Inital If loop will check VMTools status & Hardware version, if the Tools are old or Hardware version is up to date it will skip the upgrade. ###
        if(($VMHardware -eq "vmx-19") -or ($ToolsStatus -eq "toolsOld"))
        {
            Write-Output "$(Get-Date) - Upgrade-hardware-version Function Step 1 - The VMTools version is $ToolsStatus on $VM & The hardware version is $VMHardware on $VM. an Hardware upgrade will not be attempted `r`n" | Tee-Object -FilePath $Logfile -Append
        }
    
        ### ElseIF Loop - Will perform a Hardware upgrade on $VM ###
        elseif(($ToolsStatus -eq "toolsOK") -and ($VMHardware -ne "vmx-19"))
        {
    
            PowerOff-VM $VM $VMView $timer
            $timer = 1  
    
            $VMView.UpdateViewData("Summary.Runtime.PowerState") # update specfic VM info - PowerState
            $Powerstate = $VMView.Summary.Runtime.PowerState # Set $Powerstate variable to new value from $VMView
    
            ### If loop - If VM is powered down perform Hardware upgrade and power on VM ###
            if($Powerstate -eq "PoweredOff")
            {
                
                Write-Output "$(Get-Date) - Upgrade-hardware-version Function Step 2 - Starting upgrade of hardware version on $VM. `r`n" | Tee-Object -FilePath $Logfile -Append
    
                ### perform Hardware version upgrade ###
                do
                {
                    $HWErrorTime = Get-Date -UFormat "%d/%m/%Y %R"
                    $LoopErrorInfo = $null
                    
                    Write-Output "$(Get-Date) - Upgrade-hardware-version Function Step 3 - Checking HW upgrade command runs $VM. `r`n" | Tee-Object -FilePath $Logfile -Append
    
                    $EventInfo = Get-VIEvent -Entity $VM -Type Info -start $HWTime | Where-Object {$_.FullFormattedMessage -Like "*Task: Upgrade VM compatibility*"} 
                    $LoopErrorInfo = Get-VIEvent -Entity $VM -Type Error -start $HWErrorTime
    
                    If(($EventInfo -eq $null) -or ($LoopErrorInfo -ne $null))
                    {
                        Get-View ($VMView.UpgradeVM_Task($VMHW))
                    }
    
                    Start-Sleep -s 20 # Sleep loop for 20 seconds
    
                    $EventInfo = Get-VIEvent -Entity $VM -Type Info -start $HWTime | Where-Object {$_.FullFormattedMessage -Like "*Task: Upgrade VM compatibility*"} 
                    $LoopErrorInfo = Get-VIEvent -Entity $VM -Type Error -start $HWErrorTime 
    
    
                    write-output "$(Get-Date) - $VM - HW ugrade Error: `r`n" | Tee-Object -FilePath $Logfile  -Append
                    write-Output $LoopErrorInfo | Tee-Object -FilePath $Logfile -Append
                    Write-Output "$(Get-Date) - $VM - HW Upgrade task =  Created Time : $($EventInfo.CreatedTime) , $($EventInfo.FullFormattedMessage). HW upgrade command attempt = $timer  `r`n" | Tee-Object -FilePath $Logfile -Append
                        
                    $timer = ++$timer # Add 1 to the timer variable
    
                    Start-Sleep 2
    
                }until((($EventInfo.FullFormattedMessage -contains "Task: Upgrade VM compatibility") -and ($LoopErrorInfo -eq $null)) -or ($timer -eq 5))
    
                If(($LoopErrorInfo -ne $null) -or ($timer -eq 5)) 
                {
                    Write-Output "$(Get-Date) - upgrade-hardware-function Step 3.1 - Attempt to issue upgrade command failed on $VM, exiting Job. `r`n" | Tee-Object -FilePath $Logfile -Append
                    Write-Error "VMHW Error on $VM = True"
                }
    
                sleep 15 # Sleep for 15 seconds to allow the Hardware upgrade time to complete
    
                $VMView.UpdateViewData("Config.Version") # update specfic VM info - Version
                $VMHardware = $VMView.Config.Version # Set $VMHardware to the current Hardware version of the VM
    
                PowerOn-VM $VM $VMView $timer # Run PowerOn-VM function
                Write-Output "$(Get-Date) - Upgrade-hardware-version Function Step 4 - $VM Hardware has been upgraded to $VMHardware. `r`n" | Tee-Object -FilePath $Logfile -Append                       
            }
    
            ### Else loop - If VM doesn't power down display error and stop script ###
            else
            {
                Write-Output "$(Get-Date) - Upgrade-hardware-version Function Step 5 - There is something wrong with the hardware level or the tools of $VM. Skipping $VM. `r`n" | Tee-Object -FilePath $Logfile -Append
                Write-Error "VMHW Error on $VM = True"
            }
        }
    
        Write-Error "VMHW upgrades completed on $VM = True"
    
        ### Result variables to $null so function is starting from scratch on next call of function ###
        $HWTime = $null; $timer = $null; $EventInfo = $null; $LoopErrorInfo = $null
    
    }
    
    ############################################### Main Code ###################################################################
    
    
    ### Start Log File ###
    Write-Output "$(Get-Date) ===============  vmtools & HW upgrade starting on $VM  ======================= `r`n"  | Tee-Object -FilePath $Logfile -Append
    
    ### Random wait to unsequence jobs ###
    $RandomSleep = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 | Get-Random
    Write-Output "$(Get-Date) Random Sleep value = $RandomSleep `r`n" | Tee-Object -FilePath $Logfile -Append
    Start-Sleep -s $RandomSleep
    
    ### Connecting to vCenters ###
    Write-Output "$(Get-Date) =====  connecting to all listed vCenters - $VCs ===== `r`n" | Tee-Object $Logfile -Append
    
    Connect-VIServer -Server $VCs -Credential $UserCredential -WarningAction SilentlyContinue #| Tee-Object $Logfile -Append # Login to all vCenters listed in the $VCs array
    
    ### Get initial view of $VM ###
    $VMView = get-view -ViewType VirtualMachine -filter @{"Name"=$VM} # Get View of VM which contains all Status, parameters and configuration settings. Set the output to the $VMView Hashtable
    
    ### Do Until Loop - run each step of the upgrade process, break out of process if an error occurs ###
    Do
    {
        Reboot-VM $VM $VMView $timer # Run Reboot-VM function - perform a Reboot guest OS 
    
        Poweroff-VM $VM $VMView $timer # Run Poweroff-VM function - perform a guest OS shutdown
    
        Make-Snapshot $VM $timer # Run Make-Snapshot function - Create snapshot on $VM
    
        PowerOn-VM $VM $VMView $timer # Run PowerOn-VM function - Post snapshot power on of VM pre upgrades
        
        Upgrade-vmtools-version $VM $VMView $timer # Run Upgrade-VMtools-version function - this will check the VMTools version on the VM and upgrade if needed 
    
        Upgrade-hardware-version $VM $VMView $timer $VMHW # Run Upgrade-hardware-version function - this will check the Hardware version on the VM and upgrade if needed    
    
    }Until (( $Error -like "*VMHW Error on $VM = True*") -or ($Error -like "*VMHW upgrades completed on $VM = True*")) #Loop will exit if $Error contains either constraint
    
    ### Write all Job errors to VM Log file ###
    Write-Output "$(Get-Date) ===== $VM Job Errors ===== `r`n"  | Tee-Object -FilePath $Logfile -Append
    $Error | Tee-Object -FilePath $Logfile -Append
    
    ### End Log File ###
    Write-Output "$(Get-Date) ===== vmtools & HW upgrade Completed on $VM ===== `r`n"  | Tee-Object -FilePath $Logfile -Append

     

     

     



  • 14.  RE: VMTools & Hardware upgrade script using PoshRSJob - Powershell

    Posted Feb 09, 2024 03:04 PM

    Hello again Neil_orourke,
    Thank you for your support and comments here. I found my mistake. We have virtual servers in the environment as VTEST1, VTEST2, VTEST3. There are also ISTVTEST1.copy, ISTVTEST2.copy, ISTVTEST3.copy servers where these are replicated and replicated from the opposite site. The script saw them as more than one VM and wrote the hardware version and vmwaretools versions twice from the view. I debugged this and realized it, although it was difficult. I changed the names of these test servers and put a check so that the server names do not conflict like this. Everything is working properly. I ran it for 3 VMs at the same time and it's fine.

    Enigma06, from VB-->MTE