Files
PowerShell/vSphere Tools/VMFolderTools.ps1
T
2026-09-15 11:50:36 -07:00

303 lines
9.5 KiB
PowerShell

param (
[string]$vCenter = "",
[string]$datafile = "",
[switch]$backup = $false,
[switch]$restore = $false,
[switch]$savecred = $false,
[switch]$help = $false
)
$fileVersion = "2.0.0"
$fileDate = "2025-09-29"
function ConnectVcenter {
try {
if ((Test-Path -Path "$env:USERPROFILE\VMFolderTools-${vCenter}.cred" -PathType Leaf) -and (-not $savecred)) {
Write-Host "Using stored credentials for $vCenter."
$cred = Import-CliXML -path "$env:USERPROFILE\VMFolderTools-${vCenter}.cred"
} else {
$cred = Get-Credential -Message "Enter credentials for vCenter $vCenter"
}
Write-Host "Connecting to vCenter server $vCenter..." -NoNewLine
$vcServer = connect-viserver $vCenter -credential $cred -ErrorAction Stop
Write-Host " Success" -ForegroundColor Green
} catch {
Write-Host " Failure" -ForegroundColor Red
Write-Host "ERROR: Invalid credentials"
exit
}
if ($savecred) {
Write-Host "Storing credentials for $vCenter."
$cred | Export-CliXML -Path "$env:USERPROFILE\VMFolderTools-${vCenter}.cred"
}
}
function BackupFolderStructure {
New-VIProperty -Name 'FolderPath' -ObjectType 'VirtualMachine' -Value {
param($vm)
function Get-ParentName{
param($object)
if($object.Folder){
$blue = Get-ParentName $object.Folder
$name = $object.Folder.Name
}
elseif($object.Parent -and $object.Parent.GetType().Name -like "Folder*"){
$blue = Get-ParentName $object.Parent
$name = $object.Parent.Name
}
elseif($object.ParentFolder){
$blue = Get-ParentName $object.ParentFolder
$name = $object.ParentFolder.Name
}
if("vm","Datacenters" -notcontains $name){
$blue + "/" + $name
}
else{
$blue
}
}
(Get-ParentName $vm).Remove(0,1)
} -Force | Out-Null
ConnectVcenter
while ([string]::IsNullOrEmpty($datacenter)) {
Write-Host "`n`nSelect datacenter:" -ForegroundColor Yellow
$datacenters = Get-Datacenter -Server $vcServer | sort-object -property Name
$counter = 1
foreach ($dc in $datacenters) {
$dcName = $dc.Name
Write-host -NoNewLine "${counter}" -ForegroundColor Yellow
Write-Host ": $dcName" -ForegroundColor Green
++$counter
}
try {
$dcNum = [int](Read-Host "`nSelection")
} catch {
$dcNum = 0
}
if (($dcNum -ne 0) -and ($dcNum -le $datacenters.Length)) {
$datacenter = $datacenters[$dcnum - 1]
}
}
$dcName = $datacenter.Name
$clusterlist = Get-Datacenter $datacenter | Get-Cluster | sort-object -property Name
Write-Host "--------------------------------------------------" -ForegroundColor White
while($true) {
Write-Host "`n`nSelect cluster to inventory:" -ForegroundColor Yellow
$counter = 1
for($counter = 1; $counter -le $clusterlist.Length; ++$counter) {
$cluster = $clusterlist[$counter-1]
$clusterName = $cluster.Name
Write-host -NoNewLine "${counter}" -ForegroundColor Yellow
Write-Host ": $clusterName" -ForegroundColor Green
}
Write-Host -NoNewLine "$counter" -ForegroundColor Yellow
Write-Host ": All Clusters" -ForegroundColor Green
try {
$clusterNum = [int](Read-Host "`nSelection")
} catch {
$clusterNum = 0
}
if (($clusterNum -ne 0) -and ($clusterNum -le $clusterlist.Length + 1)) {
$clusters = @()
if ($clusterNum -eq $counter) {
$clusters = $clusterlist
break
} else {
$cluster = $clusterlist[$clusterNum - 1]
$clusters += $cluster
break
}
}
}
Write-Host "`n`nProcessing..." -ForegroundColor Yellow
$vmdata = [PSCustomObject]@{"vCenter" = $vCenter}
$vmdata | Add-Member -MemberType NoteProperty -Name "Clusters" -Value @()
foreach ($cluster in $clusters) {
$clusterName = $cluster.Name
Write-Host "Gathering folder inventory for cluster $clusterName"
$myhosts = $cluster | get-VMHost
if ($myhosts.Count -lt 1) {
continue
}
$mydatacenter = ($myhosts[0] | get-datacenter).Name
$vmlist = Get-VM -Location (Get-Cluster $cluster) | Select Name, @{Label="VMHost";Expression={$_.VMHost.Name}}, FolderPath
$thiscluster = [PSCustomObject]@{Name="$clusterName";Datacenter="$mydatacenter"}
$thiscluster | Add-Member -MemberType NoteProperty -Name "VMs" -Value $vmlist
$vmdata.Clusters += $thiscluster
}
$jsonOutput = $vmdata | ConvertTo-Json -Depth 4 #-Compress
$cwd = (Get-Location).Path
$jsonOutput | Out-File -FilePath "$cwd\$clusterName-Folder.json"
Write-Host "`nProcessing complete." -ForegroundColor Yellow
Write-Host "Files have been placed into the folder $cwd."
disconnect-viserver $vCenter -Confirm:$false
}
function RestoreFolderStructure {
ConnectVcenter
$newClusters = Get-Cluster -Server $vcServer | Sort-Object -Property Name
$newClusters
$data = Get-Content $datafile | Out-String | ConvertFrom-Json
$oldvc = $data.vCenter
$oldClusters = $data.Clusters
while($true) {
Write-Host "`n`nSelect source cluster from ${oldvc}:" -ForegroundColor Yellow
$counter = 1
for($counter = 1; $counter -le $oldClusters.Length; ++$counter) {
$cluster = $oldClusters[$counter-1]
$clusterName = $cluster.Name
Write-host -NoNewLine "${counter}" -ForegroundColor Yellow
Write-Host ": $clusterName" -ForegroundColor Green
}
try {
$clusterNum = [int](Read-Host "`nSelection")
} catch {
$clusterNum = 0
}
if (($clusterNum -ne 0) -and ($clusterNum -le $oldClusters.Length)) {
$oldcluster = $oldClusters[$clusterNum - 1]
break
}
}
$oldClusterName = $oldcluster.Name
$oldDC = $oldcluster.Datacenter
$newClusters
while($true) {
Write-Host "`n`nSelect destination cluster from ${vCenter}:" -ForegroundColor Yellow
$counter = 1
for($counter = 1; $counter -le $newClusters.Length; ++$counter) {
$cluster = $newClusters[$counter-1]
$clusterName = $cluster.Name
Write-host -NoNewLine "${counter}" -ForegroundColor Yellow
Write-Host ": $clusterName" -ForegroundColor Green
}
try {
$clusterNum = [int](Read-Host "`nSelection")
} catch {
$clusterNum = 0
}
if (($clusterNum -ne 0) -and ($clusterNum -le $newClusters.Length)) {
$newcluster = $newClusters[$clusterNum - 1]
break
}
}
$newClusterName = $newcluster.Name
$newDC = $newcluster | Get-Datacenter
Write-Host "`n"
$clusterData = $oldcluster.VMs
$clusterData | %{
$location = "vm"
$folderPath = $_.FolderPath
if ($folderpath -eq "") {
$location = Get-Folder -Name "vm" -Server $vCenterenter -ErrorAction SilentlyContinue | Select-Object -First 1
$folderpath = "/"
} else {
$_.FolderPath.TrimStart('/').Split('/') | %{
$tgtFolder = Get-Folder -Name $_ -Server $vCenterenter -Location $location -ErrorAction SilentlyContinue | Select-Object -First 1
if(!$tgtFolder){
$location = New-Folder -Name $_ -Server $vCenterenter -Location $location
}
else{
$location = $tgtFolder
}
}
}
try {
$vmName = $_.Name
$thisVM = Get-VM $vmName -Server $vCenterenter -ErrorAction SilentlyContinue
if ($thisVM -and (($thisVM.Folder | Select-Object -First 1).Name -ne $location.Name)) {
Write-Host -NoNewLine "Moving VM $vmName to folder $folderPath..."
$result = Move-VM -VM $thisVM -InventoryLocation $location -Confirm:$false
Write-Host " Success" -ForegroundColor Green
} else {
If (($thisVM.Folder | Select-Object -First 1).Name -eq $location.Name) {
Write-Host "VM $vmName does not need to be moved."
} else {
Write-Host "VM $vmName cannot be found in this Datacenter"
}
}
} catch {
Write-Host "Failure" -ForegroundColor Red
Write-Host "ERROR: Unable to move VM $vmName"
}
}
disconnect-viserver $vCenter -Confirm:$false
}
Write-Host "--------------------------------------------------" -ForegroundColor White
Write-Host "vSphere VM Folder Structure Backup/Restore Utility" -ForegroundColor Green
Write-Host "Version ${FileVersion} (${FileDate})" -ForegroundColor Green
Write-Host "Written by Charlie Silverman" -ForegroundColor Green
Write-Host "--------------------------------------------------" -ForegroundColor White
if ($backup) { $cmdoption = "backup" }
if ($restore) { $cmdoption = "restore" }
if ($help) { $cmdoption = "backup" }
switch($cmdoption) {
"help" {
Write-Host "Command Syntax:" -ForegroundColor Green
Write-Host 'VMFolderTools.ps1 -oldvc="VCURI" [-$vCenter="VCURI"] -backup/-restore/-help [-savecred]' -ForegroundColor Green
Write-Host ""
Write-Host "Command line options:" -ForegroundColor Green
Write-Host "---------------------" -ForegroundColor Green
Write-Host "-$vCenter - Specify the URI for the appropriate vCenter server" -ForegroundColor Green
Write-Host "-backup - Backup folder structure to CSV" -ForegroundColor Green
Write-Host "-restore - Restore folder structure from CSV" -ForegroundColor Green
Write-Host "-datafile - Specify the path to a file created with a backup process" -ForegroundColor Green
Write-Host "-savecred - Save Credentials. If credentials were previously saved and this option is not selected," -ForegroundColor Green
Write-Host " they will be used." -ForegroundColor Green
Write-Host "-help - Show this help information." -ForegroundColor Green
exit
}
"backup" {
if ($vCenter -eq "") {
Write-Host "ERROR: vCenter not specified." -ForegroundColor Red
exit
}
BackupFolderStructure
}
"restore" {
if ($vCenter -eq "") {
Write-Host "ERROR: vCenter not specified." -ForegroundColor Red
exit
}
if ($datafile -eq "") {
Write-Host "ERROR: Data file not specified." -ForegroundColor Red
exit
} else {
if (-not (Test-Path -Path $datafile)) {
Write-Host "ERROR: Data file specified (${datafile}) does not exist." -ForegroundColor Red
exit
}
}
RestoreFolderStructure
}
default {
Write-Host "ERROR: Command option not specified." -ForegroundColor Red
}
}