This solution was so simple at first I thought you didn't understand the question, because it couldn't possible work. Then it worked like always, and after studying it for like 20 minutes I get it now. Thanks LucD!
I've added a few things just so that I can see the status of each stage, I may add a final stage to do some more checks. I still don't understand why some of the Copy-DatastoreItem -Item commands result in errors while others do not, I haven't been able to reproduce the error in my home lab.
Here is the final script:
#Permission required - Stage 1 RO + Datastore.browse
#STAGE1 - Search each datastore for all .vmx files, then search the VCenter inventory to see if there is a VM with that name in inventory. If there are none, add it to the list of suspected ungregistered VMs. ##
$Datastores = Get-Datastore | Where-Object {$_.name -notmatch "local" -and $_.name -notmatch "NFS"}
#$Datastores = Get-Datastore -name IOMEGA
$unregistered = @()
Write-Host "Starting stage 1 - Identify unregistered VMs."
ForEach ($datastore in $datastores) {
$psds = Get-Datastore -Name $datastore
New-PSDrive -Name TgtDS -Location $psds -PSProvider VimDatastore -Root '\' | Out-Null
$VMXS = Get-ChildItem -Path TgtDS: -Recurse -Filter *.vmx | Where-Object {$_.name -notmatch "vCLS-*" -and $_.name -notmatch "zerto*"}
#$VMXS = Get-ChildItem -Path TgtDS: -Recurse -Filter *.vmx | Where-Object {$_.name -eq "unregistered_IOMEGA1.vmx"}
Write-Host "Starting $datastore"
foreach ($VMX in $VMXS) {
try {
Get-VM -datastore $datastore -name $VMX.name.replace('.vmx','') -ErrorAction:Stop | Out-Null
}
catch {
$mismatch = Get-ChildItem -Path $VMX.PSParentpath -Filter *.lck | Where-Object {$_.name -contains "$($vmx.name).lck"}
$unregistered += [PSCustomObject] @{
Name = $vmx.Name
DatastoreFullPath = $vmx.DatastoreFullPath
LastWriteTime = $vmx.LastWriteTime
vCNameMismatch = $mismatch.Name
PSParentpath = $VMX.PSParentpath
FullName = $VMX.FullName
Result = "Stage1 Unregistered VM suspected."
}
}
}
Write-Host "Done"
Remove-PSDrive -Name TgtDS
}
$stage1 = $unregistered | select * | Where-Object {$_.name -ne $null -and $_.vCNameMismatch -eq $null}
$lckdetecteds = $unregistered | select * | Where-Object {$_.name -ne $null -and $_.vCNameMismatch -ne $null}
if ($stage1 -ne $null){
write-host "The following suspected unregistered VMs were found:"
$($stage1.name.Replace('.vmx',''))
}
if ($lckdetecteds -ne $null) {
write-host "The following VMs have .lck files associated with them, indicating they are presently running under a different name. Stage 2 will attempt to determine the name."
$($lckdetecteds.name.Replace('.vmx',''))
}
elseif ($stage1 -eq $null){
write-host "There were no unregistered VMs found."
}
#Stage 2 Permission required - low level DS operations
Write-Host "Starting Stage 2 - Attempting to determine the name VMs are running under within vCenter:"
$tempFile = New-TemporaryFile
foreach ($entry in $unregistered) {
if ($entry.vCNameMismatch -ne $null) {
Write-Host "Checking to see if $($entry.name.Replace('.vmx','')) is running on $global:DefaultVIServer under a different name."
try {
Copy-DatastoreItem -Item $entry.fullname -Destination $tempFile -ErrorAction:Stop | Out-Null
$VCVMName = Get-VM -Name (Get-Content -Path $tempFile | where { $_ -match 'displayName' }).Split('"')[1]
$VCname = (Get-Content -Path $tempFile | where { $_ -match 'displayName' }).Split('"')[1]
$RegisteredVCenter = $VCVMName.Uid.Substring($VCVMName.Uid.IndexOf('@') + 1).Split(":")[0]
$DSVMName = $entry.name
$DSpath = $entry.datastorefullpath
$result = "$VCVMName is POWERED-ON on $RegisteredVCenter. It is named $DSVMName on disk."
$entry.Result = $result
} catch {
$error[0]
$result = "ERROR downloading .vmx to local temp file."
$entry.Result = $result
}
Write-Host "$result"
}
}
Remove-Item -Path $tempFile
$unregistered
Original Message:
Sent: Jan 02, 2025 09:32 AM
From: LucD
Subject: Create a report of all unregistered VMs, then check them to see if they're running under different names
Try changing Stage 2 to something like this
#Stage 2 Permission required - low level DS operationsWrite-Host "Unregistered VMs have been identified. Now checking to see if they are running under different names."$tempFile = New-TemporaryFileforeach ($entry in $unregistered) { if ($entry.vCNameMismatch -ne $null) { Write-Host "Looking at $($entry.fullname)" try { Copy-DatastoreItem -Item $entry.fullname -Destination $tempFile -ErrorAction:Stop | Out-Null $VCVMName = Get-VM -Name (Get-Content -Path $tempFile | where { $_ -match 'displayName' }).Split('"')[1] $VCname = (Get-Content -Path $tempFile | where { $_ -match 'displayName' }).Split('"')[1] $RegisteredVCenter = $VCVMName.Uid.Substring($VCVMName.Uid.IndexOf('@') + 1).Split(":")[0] $DSVMName = $entry.name $DSpath = $entry.datastorefullpath $result = "$VCVMName was found running on vCenter $RegisteredVCenter however it is named $DSVMName on disk." $entry.Result = $result } catch { $error[0] $result = "ERROR" #NOT WORKING $entry.Result = $result } Write-Host "$result" }}Remove-Item -Path $tempFile$unregistered
------------------------------
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
Original Message:
Sent: Dec 29, 2024 12:03 PM
From: dbutch1976
Subject: Create a report of all unregistered VMs, then check them to see if they're running under different names
I've created a 2 stage script. This is because may need to break them up and run them with different permissions. Stage1 requires only read-only, stage2 required low level DS permissions. It's working, but I don't know how to update the existing $unregistered.result field prior to exporting it to csv:
I've added #NOT WORKING to the parts I need help with.
Thanks in advance!
#Permission required - Stage 1 RO + Datastore.browse$Datastores = Get-Datastore | Where-Object {$_.name -notmatch "local" -and $_.name -notmatch "NFS"}#$Datastores = Get-Datastore -name IOMEGA$unregistered = @()Write-Host "Starting stage 1 - Identify unregistered VMs."ForEach ($datastore in $datastores) { $psds = Get-Datastore -Name $datastore New-PSDrive -Name TgtDS -Location $psds -PSProvider VimDatastore -Root '\' | Out-Null $VMXS = Get-ChildItem -Path TgtDS: -Recurse -Filter *.vmx | Where-Object {$_.name -notmatch "vCLS-*" -and $_.name -notmatch "zerto*"} #$VMXS = Get-ChildItem -Path TgtDS: -Recurse -Filter *.vmx | Where-Object {$_.name -eq "unregistered_IOMEGA1.vmx"} Write-Host "Starting $datastore" foreach ($VMX in $VMXS) { try { Get-VM -datastore $datastore -name $VMX.name.replace('.vmx','') -ErrorAction:Stop | Out-Null } catch { $mismatch = Get-ChildItem -Path $VMX.PSParentpath -Filter *.lck | Where-Object {$_.name -contains "$($vmx.name).lck"} $unregistered += [PSCustomObject] @{ Name = $vmx.Name DatastoreFullPath = $vmx.DatastoreFullPath LastWriteTime = $vmx.LastWriteTime vCNameMismatch = $mismatch.Name PSParentpath = $VMX.PSParentpath FullName = $VMX.FullName Result = "Unregistered" } } } Write-Host "Done" Remove-PSDrive -Name TgtDS}$unregistered # | export-csv -Path C:\output\UnregisteredonlyVms3.csv -NoTypeInformation#Stage 2 Permission required - low level DS operationswrite-host "Unregistered VMs have been identified. Now checking to see if they are running under different names."$tempFile = New-TemporaryFile$lckdetecteds = $unregistered | select * | Where-Object {$_.vCNameMismatch -ne $null}ForEach ($lckdetected in $lckdetecteds) { Write-Host "Looking at $($lckdetected.fullname)" try { Copy-DatastoreItem -Item $lckdetected.fullname -Destination $tempFile -ErrorAction:Stop | Out-Null $VCVMName = Get-VM -Name (Get-Content -Path $tempFile | where { $_ -match 'displayName' }).Split('"')[1] $VCname = (Get-Content -Path $tempFile | where { $_ -match 'displayName' }).Split('"')[1] $RegisteredVCenter = $VCVMName.Uid.Substring($VCVMName.Uid.IndexOf('@') + 1).Split(":")[0] $DSVMName = $lckdetected.name $DSpath = $lckdetected.datastorefullpath $result = "$VCVMName was found running on vCenter $RegisteredVCenter however it is named $DSVMName on disk." #NOTWORKING $unregistered.Result = $result } catch { $error[0] $result = "ERROR" #NOT WORKING $unregistered.Result = $result } Write-Host "$result" Remove-Item -Path $tempFile}$unregistered