This is somewhat of a follow-up question to the post here to which a satisfactory solution was discovered, but I now must go deeper. My first post was about changing the instanceUuid (also known as vc.uuid in the VMX file) from PowerCLI, and it is possible as outlined thanks to the idea from LucD. However, I now need some ideas on a solution that is able to backup and restore that instanceUuid. Here's a scenario as to why this is necessary:
The backup portion could be accomplished by just reading the instanceUuid out and saving to a file, but there needs to be a way to more or less manually edit the VMX file in situ to restore the given instanceUuid to what it previously was. As discussed in the previous thread, it's not possible to use VMware.Vim.VirtualMachineConfigSpec because the API will not permit an arbitrary instanceUuid to be set, only a new one to be generated by setting the parameter to null. The VM is always in a powered off state, so reloading of that VMX should not be necessary.
Is there PowerCLI code that anyone can think of that could directly edit a VMX without going through the API? Any other ideas are certainly welcome, but it would seem we are reduced to this only. Thanks to all who can offer thoughts or ideas.
There was an error in the vCenter calculated property as well.
Try this new version
####Set variables####
$vcenters = 'llvc02.domain.com'
$username = 'myuser@domain.com'
$password = 'password'
$foldername = 'MyFolder'
$UuidFile = 'D:\uuids.csv'
#####################
$spec = New-Object VMware.Vim.VirtualMachineConfigSpec
$spec.instanceUuid = ''
$report = @()
foreach ($vcenter in $vcenters){
Connect-VIServer $vcenter -User $username -Password $password -Force
Get-Folder -Name $foldername -Server $vcenter | Get-Template | Set-Template -ToVM
foreach ($vm in (Get-Folder $foldername | Get-VM )){
$vm.Extensiondata.ReconfigVM($spec)
$vm = Get-VM -Name $vm.Name
$newinstanceUuid = $vm.extensiondata.config.InstanceUUID
Write-Output "New vCenter ID for $($vm.Name) is $($newinstanceUuid)."
$report += $vm | Select-Object Name,
@{Name="InstanceUUID"; Expression={$_.extensiondata.config.InstanceUUID}},
@{Name="vCenter"; Expression={$_.Uid.Split('@')[1].Split(':')[0]}}
}
Get-Folder -Name $foldername -Server $vcenter | Get-VM | Set-VM -ToTemplate -Confirm:$false | Out-Null
}
$report | Export-Csv -Path $UuidFile -NoTypeInformation -UseCulture
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
You can handle the VMX file as a regular file, search in it and change content.
See for example Re: how can modify/change key in VMX file
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
Thanks, Luc, for pointing me in that direction. I will look to that as a model for what I'm trying to accomplish and will let you know. It appears it'll do the trick.
LucD is it possible to edit a VMX file in place rather than downloading it via a PS drive and uploading it after replacing content?
And second question is about writing out values for each VM to a CSV. Here's what I have so far:
foreach ($vmname in (Get-Folder $foldername | Get-VM )){
$spec = New-Object VMware.Vim.VirtualMachineConfigSpec
$spec.instanceUuid = ''
$vm = Get-VM -Name $vmName
$vm.Extensiondata.ReconfigVM_Task($spec)
$newinstanceUuid = (Get-VM $vmname).extensiondata.config.InstanceUUID
Write-Output "New vCenter ID for $vmname is $newinstanceUuid."
$vmname | Select-Object Name,@{Name="InstanceUUID"; Expression={$_.extensiondata.config.InstanceUUID},@{Name="vCenter"; Expression={$_.Uid.Substring($_.Uid.IndexOf('@')+1).Split(":")[0]}}} | Export-Csv -Append -Path $UuidFile -NoTypeInformation}
The last line of the foreach loop is contiguous. I need to write out to a CSV file for later parsing the VM name, InstanceUuid, and vCenter to which it belongs. The Name and InstanceUuid work fine. When I add the expression that derives the vCenter name I get the error
Select-Object : The "Expression" key has a type, System.Object[], that is not valid; expected types are {System.String, System.Management.Automation.ScriptBlock}.
I take it this is not a valid object. There may be an easier way, so could you suggest how I might accomplish this simple task? Also, if you can offer any critique of the code that would make it more efficient and all-around "better" then please do so.
1. Afaik you can't edit a .vmx file in place
2. I made a couple of changes to the script
$spec = New-Object VMware.Vim.VirtualMachineConfigSpec
$spec.instanceUuid = ''
$report = foreach ($vm in (Get-Folder $foldername | Get-VM )){
$vm.Extensiondata.ReconfigVM_Task($spec)
$vm.ExtensionData.UpdateViewData('Config.InstanceUUID')
$newinstanceUuid = $vm.extensiondata.config.InstanceUUID
Write-Output "New vCenter ID for $vmname is $newinstanceUuid."
$vm | Select-Object Name,
@{Name="InstanceUUID"; Expression={$_.extensiondata.config.InstanceUUID},
@{Name="vCenter"; Expression={$_.Uid.Uid.Split('@')[1].Split(':')[0]}}
}
$report | Export-Csv -Append -Path $UuidFile -NoTypeInformation -UseCulture
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
Thanks for your help, Luc. I will have to use the copy-replace mechanism for changing strings in the VMX. Once replaced, do you know if it needs to be reloaded for vCenter to parse any changes? Or any other methods invoked for the changes made to take effect.
About your script changes, that does look better and I've learned quite a bit from the few changes. However, I'm not having success incorporating that into what I had before. I know it's something simple.
####Set variables####
$vcenters = 'llvc02.domain.com'
$username = 'myuser@domain.com'
$password = 'password'
$foldername = 'MyFolder'
$UuidFile = 'D:\uuids.csv'
#####################
foreach ($vcenter in $vcenters){
Connect-VIServer $vcenter -User $username -Password $password -Force
Get-Folder -Name $foldername -Server $vcenter | Get-Template | Set-Template -ToVM
$spec = New-Object VMware.Vim.VirtualMachineConfigSpec
$spec.instanceUuid = ''
$report = foreach ($vm in (Get-Folder $foldername | Get-VM )){
$vm.Extensiondata.ReconfigVM_Task($spec)
$vm.ExtensionData.UpdateViewData('Config.InstanceUUID')
$newinstanceUuid = $vm.extensiondata.config.InstanceUUID
Write-Output "New vCenter ID for $vm is $newinstanceUuid."
$vm | Select-Object Name,@{Name="InstanceUUID"; Expression={$_.extensiondata.config.InstanceUUID}},@{Name="vCenter"; Expression={$_.Uid.Uid.Split('@')[1].Split(':')[0]}}
$report | Export-Csv -Append -Path $UuidFile -NoTypeInformation -UseCulture}
}
Get-Folder -Name $foldername -Server $vcenter | Get-VM | Set-VM -ToTemplate -Confirm:$false | Out-Null
Specifically, the information as expected is not getting written to the CSV file, and Write-Output is not having any effect.
Also, how do you preserve the color formatting when pasting here?
See KB1026043 for reloading a VMX file.
You will need an SSH to the hosting ESXi node for that.
The reloadVirtualMachineFromPath method unfortunately requires the VM to be powered off.
Try like this.
The $report array needs to be outside the ForEach loop, else it's still empty when you export.
####Set variables####
$vcenters = 'llvc02.domain.com'
$username = 'myuser@domain.com'
$password = 'password'
$foldername = 'MyFolder'
$UuidFile = 'D:\uuids.csv'
#####################
$spec = New-Object VMware.Vim.VirtualMachineConfigSpec
$spec.instanceUuid = ''
foreach ($vcenter in $vcenters){
Connect-VIServer $vcenter -User $username -Password $password -Force
Get-Folder -Name $foldername -Server $vcenter | Get-Template | Set-Template -ToVM
$report = foreach ($vm in (Get-Folder $foldername | Get-VM )){
$vm.Extensiondata.ReconfigVM_Task($spec)
$vm.ExtensionData.UpdateViewData('Config.InstanceUUID')
$newinstanceUuid = $vm.extensiondata.config.InstanceUUID
Write-Output "New vCenter ID for $($vm.Name) is $($newinstanceUuid)."
$vm | Select-Object Name,
@{Name="InstanceUUID"; Expression={$_.extensiondata.config.InstanceUUID}},
@{Name="vCenter"; Expression={$_.Uid.Uid.Split('@')[1].Split(':')[0]}}
}
$report | Export-Csv -Append -Path $UuidFile -NoTypeInformation -UseCulture
}
Get-Folder -Name $foldername -Server $vcenter | Get-VM | Set-VM -ToTemplate -Confirm:$false | Out-Null
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
1.) I had to put the last Get-Folder line inside the foreach loop as the last step. This way, it can iterate on all the folders when there are multiple vCenters.
$spec = New-Object VMware.Vim.VirtualMachineConfigSpec
$spec.instanceUuid = ''
foreach ($vcenter in $vcenters){
Connect-VIServer $vcenter -User $username -Password $password -Force
Get-Folder -Name $foldername -Server $vcenter | Get-Template | Set-Template -ToVM
$report = foreach ($vm in (Get-Folder $foldername | Get-VM )){
$vm.Extensiondata.ReconfigVM_Task($spec)
$vm.ExtensionData.UpdateViewData('Config.InstanceUUID')
$newinstanceUuid = $vm.extensiondata.config.InstanceUUID
Write-Output "New vCenter ID for $($vm.Name) is $($newinstanceUuid)."
$vm | Select-Object Name,
@{Name="InstanceUUID"; Expression={$_.extensiondata.config.InstanceUUID}},
@{Name="vCenter"; Expression={$_.Uid.Uid.Split('@')[1].Split(':')[0]}}
}
$report | Export-Csv -Append -Path $UuidFile -NoTypeInformation -UseCulture
Get-Folder -Name $foldername -Server $vcenter | Get-VM | Set-VM -ToTemplate -Confirm:$false | Out-Null
}
2.) I still am seeing no console output from Write-Output.
3.) More importantly, the uuids.csv file generated from the $report array doesn't contain any values:
"Type","Value"
"Task","task-170794"
,
,
"Task","task-170795"
,
,
Also, these VMs will be powered off, so the reloadVirtualMachineFromPath method should work. I'll need to figure that out in just a bit.
Ok, I think I know what happens, when you call a methood with the _Task suffix, that is the same as running a cmdlet with the RunAsync switch.
It runs in the background, and the script continues.
Change line
$vm.Extensiondata.ReconfigVM_Task($spec)
into
$vm.Extensiondata.ReconfigVM($spec)
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
The reload is quite straightforward, something like this
$vm = Get-VM -Name MyVM
$vm.ExtensionData.reloadVirtualMachineFromPath($vm.ExtensionData.Config.Files.VmPathName)
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
Still no Write-Output, still no data in CSV, only this now:
"Length"
"74"
"71"
Can you try leaving out the Write-Output line altogether?
Just comment it out
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
Ah, ok that does produce output in the file, but no vCenter associated with each of the VMs:
"Name","InstanceUUID","vCenter"
"CentOS 7.2","5005c055-e3e1-26ec-6f3e-67bbe9534a6b",
"W2K12R2","5005a95a-eea6-8005-2a8a-dc4afafd4df1",
There was an error in the vCenter calculated property as well.
Try this new version
####Set variables####
$vcenters = 'llvc02.domain.com'
$username = 'myuser@domain.com'
$password = 'password'
$foldername = 'MyFolder'
$UuidFile = 'D:\uuids.csv'
#####################
$spec = New-Object VMware.Vim.VirtualMachineConfigSpec
$spec.instanceUuid = ''
$report = @()
foreach ($vcenter in $vcenters){
Connect-VIServer $vcenter -User $username -Password $password -Force
Get-Folder -Name $foldername -Server $vcenter | Get-Template | Set-Template -ToVM
foreach ($vm in (Get-Folder $foldername | Get-VM )){
$vm.Extensiondata.ReconfigVM($spec)
$vm = Get-VM -Name $vm.Name
$newinstanceUuid = $vm.extensiondata.config.InstanceUUID
Write-Output "New vCenter ID for $($vm.Name) is $($newinstanceUuid)."
$report += $vm | Select-Object Name,
@{Name="InstanceUUID"; Expression={$_.extensiondata.config.InstanceUUID}},
@{Name="vCenter"; Expression={$_.Uid.Split('@')[1].Split(':')[0]}}
}
Get-Folder -Name $foldername -Server $vcenter | Get-VM | Set-VM -ToTemplate -Confirm:$false | Out-Null
}
$report | Export-Csv -Path $UuidFile -NoTypeInformation -UseCulture
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
Ahh, yes! That's it! I see what you did there. You've created a variable called $report and initialized it as an array. The second foreach is largely as it was, except each time you add content into $report you're appending to the array. Finally, $report is output piped to CSV. This is very educational to see, so thank you.
What is an ideal way to suppress much of the output that's produced from the loops calling Get-VM? I'm speaking of blocks like this:
PowerState : PoweredOff
Version : v11
Notes :
Guest : CentOS 7.2:CentOS 4/5 or later (64-bit)
NumCpu : 1
CoresPerSocket : 1
MemoryMB : 2048
MemoryGB : 2
VMHostId : HostSystem-host-29
VMHost : host01.domain.com
VApp :
FolderId : Folder-group-v16621
Folder : My Folder
ResourcePoolId : ResourcePool-resgroup-27
ResourcePool : Resources
PersistentId : 5005c055-e3e1-26ec-6f3e-67bbe9534a6b
UsedSpaceGB : 2.7035106951370835304260253906
ProvisionedSpaceGB : 10.890099778771400451660156250
DatastoreIdList : {Datastore-datastore-34}
HARestartPriority : ClusterRestartPriority
HAIsolationResponse : AsSpecifiedByCluster
DrsAutomationLevel : AsSpecifiedByCluster
VMSwapfilePolicy : Inherit
VMResourceConfiguration : CpuShares:Normal/1000 MemShares:Normal/20480
GuestId : centos64Guest
Name : CentOS 7.2-vRA
CustomFields : {[SRM-com.vmware.vcDr:::protected, ]}
ExtensionData : VMware.Vim.VirtualMachine
Id : VirtualMachine-vm-16623
Uid : /VIServer=domain\user@vc.domain.com:443/VirtualMachine=VirtualMachine-vm-16623/
Client : VMware.VimAutomation.ViCore.Impl.V1.VimClient
Piping it to Out-Null doesn't work since it throws the data into a black hole. I only want to silence this information while still being able to print an errors that would occur.
Also, can you share your secret as to how you're pasting Powershell with color markup like what ISE displays? Is it via an Add-On for the ISE? I think I saw reference to one out there.
PowerShell output is split in streams, you cna redirect individual streams
For example, the following will suppress regular output (stream 1), but still show errors (and all the other streams).
Test by using a name for a VM that exist and one that doesn't exist
Get-VM -Name xyz 1> $null
Yes, you can use an ISE plugin, one example is the one from Falchion
And there is also one for the VSC, see Copy as HTML
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
Thanks, I'll try one of those extensions. It makes it so much easier to read when pasted here.
Every place I try and specify 1> $null it affects the running of the script because each instance of Get-VM is used further down. Either the Write-Output contains no IDs, doesn't run at all, and the output uuids.csv file is empty. I tried the occurrence in lines 17 and 19 with foul luck. Is this just not going to be possible the way the script is constructed?
My suggestions was for line 26 where you had the Out-Null, replace that with the stream 1 redirection.
The other lines shouldn't be producing any output on the console
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference