Merge branch 'main' of https://gitea.ackcentuate.com/ackitsme/PowerShell
This commit is contained in:
commit
c43a68cf6f
1 file changed
+303
@@ -0,0 +1,303 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user