I'm trying to get write a script that will allow someone to select a VM folder and then it will scan the VM's in that folder to see if they need an upgrade or not. Then a window would come up with a list of the VM's that need to be upgraded. The user could then select the ones to upgrade and tools would be upgraded (w/o reboot). I've tried a few different things, even with a csv file, but it seems whenever I kind of get it to work it only takes one VM and upgrades it. I'm looking at the csv file and I see multiple machines in there. Oh I'm also trying to ignore powered off machines. Here's what I have for my code:
$vms = Get-Folder $myfolder | Get-VM
foreach ($vm in $vms) {
if ($vm.ExtensionData.Guest.ToolsVersionStatus2 -eq "guestToolsSupportedOld") {
$selectedvms = Get-VM -Name $vm.Name
$selectedvms | select Name | Export-CSV -Path .\toolsneedupgrade.csv -NoTypeInformation -UseCulture -Append
Write-Host "VM-->",$vm.Name, "needs to have tools upgraded"
}
}
Import-Csv -Path .\toolsneedupgrade.csv -UseCulture | Foreach-Object -Process { Get-VM -Name $_.Name
#Get-VM -Name $vm | #Out-GridView -Title 'Select the VMs you wish to upgrade tools on' -OutPutMode Multiple
}
foreach ($vm in $selectedvms) {
if ($vm.Guest.State -eq "Running")
{
Write-Host "VM -->",$vm.Name, "is powered on so tools will be updated"
Get-VM -Name $vm.Name | Update-Tools -NoReboot
}
elseif ($vm.Powerstate -eq "PoweredOff")
{
Write-Host "VM-->",$vm.Name, "is powered off, so ignoring"
}
And when I run it everything seems ok until I select the folder. It then outputs this:
VM--> VM1 needs to have tools upgraded
VM--> VM2 needs to have tools upgraded
Name PowerState Num CPUs MemoryGB
---- ---------- -------- --------
VM1 PoweredOff 4 24.000
VM2 PoweredOn 2 8.000
VM3 PoweredOn 2 8.000
VM1 PoweredOff 4 24.000
VM2 PoweredOn 2 8.000
VM --> VM2 is powered on so tools will be updated
Update-Tools : 12/6/2018 3:40:27 PM Update-Tools Operation is not valid due to the current state of the object.
At C:\Users\emcclure\Desktop\GenScripts\GenUpgradeVMTools.ps1:67 char:25
+ Get-VM -Name $vm.Name | Update-Tools -NoReboot
+ ~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Update-Tools], VimException
+ FullyQualifiedErrorId : Core_BaseCmdlet_UnknownError,VMware.VimAutomation.ViCore.Cmdlets.Commands.UpdateVmTools
It does wind up updating the VM tools on the machine, but doesn't get to the part where it sees the VM is powered off and moves on in the script. I'm sure it's something simple I'm missing, but I'm not sure what.
Hmm. So it goes away from what I had, which was a way for someone to choose a bunch of VM's and just have them update without having to press Y all the time. The multiple folder selection is nice, but one thing I do at the end of the scripts I have is disconnect from vCenter or loop them back to the start. I understand with someone selecting multiple folders they might not need to loop back to the beginning, unless there's something they forgot or the loop takes them back to connecting to vCenter.
This looks more and more like a moving target :smileygrin:
You had this very strange construct to select continue or exit, that is what the y/n is doing.
If you don't need it, just remove that part.
I normally leave out the connect and disconnect from a vSphere server in my code, since it is rather trivial.
If you want that, it's straightforward to add these at the start and end of the script.
If I understand you correctly (now), you want a script that allows the user to go back to the datacenter/folder/vm selection at any part of the script?
Where do you ask that question?
After every VM?
When all selected VMs are handled?
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
So the main goal of the thread was trying to get a script to update VM tools on selected VM's which I think we have accomplished. I'd have to dig around to find the page where I did the loop and question thing at the end. My scripts have that loop in them, so the user can go back to the datacenter selection as some vCenters we have contain multiple datacenters. This is typically after whatever main task the script does which is basically at the end. The updating of the tools I want to be automatic and not require any user input other than getting to the VM's to select. I have the disconnect in there since we also have multiple vCenters and I've seen it when I don't disconnect and run the script on another vCenter it can get the datacenters from the previous vCenter into the script, possibly causing confusion for whomever else is running it. I had the continue/exit part as I had requests to have that in the script, so someone wouldn't have to restart the script, it would just be done for them. I'm sure it's rather messy and I've only been dealing with PowerCLI/PowerShell for about 7 months, so I'm using this forum and whatever else I can find on google to figure things out and get the scripts created for the users. I'm always open to a better way of doing things, as long as I can loop a script back to the top with prompting the user for that and keeping the automated stuff automated.
What I often do when going to write a more complex script, with many possible paths, is to try and describe the flows in simple text.
This often allows you to have a better idea of what you actually want to do, and how to organise the flow in a script.
Does the following reflect what you want to achieve?
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
Hi LucD,
So I try to make all of my scripts as generic as possible. I eliminate any hard coding of vCenters, folders, etc unless the script is just meant for me. I do this so anybody can run the script on any vCenter and it will just work as designed without having to make any changes to it. As to what you said here's my answers:
Ok, let's see how this version fulfills your requirements.
$vcName = Read-Host -Prompt 'Enter the vCenter name'
Connect-VIServer -Server $vcName
while($true){
$endAnswer = 'D'
while($endAnswer -ne 'Q'){
if($endAnswer -eq 'D'){
$dc = Get-Datacenter | Select -ExpandProperty Name
if($dc.count -gt 1){
$dc = $dc | Out-GridView -OutputMode Single -Title 'Select one datacenter'
}
$endAnswer = 'F'
}
if($endAnswer -eq 'F'){
$folder = Get-Folder -Location $dc -Type VM | Select -ExpandProperty Name
if($folder.count -gt 1){
$folder = $folder | Out-GridView -OutputMode Multiple -Title 'Select one or more folders'
}
$endAnswer = 'V'
}
if($endAnswer -eq 'V'){
$vms = Get-VM -Location $folder |
where {$_.ExtensionData.Guest.ToolsVersionStatus2 -eq "guestToolsSupportedOld"} |
Select -ExpandProperty Name
if($vms.Count -eq 0){
Write-Host "No VMs found that require a VMware Tools update"
break
}
if($vms.count -gt 1){
$vms = $vms | Out-GridView -OutputMode Multiple -Title 'Select one or more folders'
}
}
if($vms.count -gt 0){
foreach($vm in Get-VM -Name $vms){
if($vm.PowerState -eq 'PoweredOn'){
if($vm.Guest.State -eq 'Running'){
Write-Host "VMware Tools on VM $($vm.Name) will be updated"
Get-VM -Name $vm.Name | Update-Tools -NoReboot
}
else{
Write-Host "VMware Tools are not running on VM $($vm.Name)"
}
}
else{
Write-Host "VM $($vm.Name) is not powered on"
}
}
}
else{
Write-Host "No VMs selected"
}
write-host "Please select an option"
Write-Host "D - Go back to the datacenter selection"
Write-Host "F - Go back to the folder selection"
Write-Host "V - Go back to the VM selection"
Write-Host "Q - Exit the script"
$endAnswer = ''
while('D','F','V','Q' -notcontains $endAnswer){
$endAnswer = (Read-Host -Prompt 'Your answer').ToUpper()
}
}
Disconnect-VIServer -Server $vcName -Confirm:$false
break
}
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
Hi LucD,
That works great. I think I'm going to use this new way to setup the scripts for looping as it seems to be easier and better for the users. Thanks so much.