PowerCli to get all VMs on certain Datastores?

From time to time, I get tasked with generating a report of production VMs with specs. This normally wouldn’t be an issue, but we have changed our server naming standard a few times, so we have machines with all sorts of names. The one thing in out environment that hasn’t changed is the datastore naming. We have our datastores split up between non-production and production, and each department has their own. So to get the production VMs, I can do a filter on the name of the datastores to get only the production one.

	Get-datastore | Where {$_.name -like '*PROD*' -or $_.name -like '*REPL*'} | Get-VM
Output of script to get VMs on certain datastores
Output of script to get VMs on certain datastores

The script results are good, but I don’t need to know if the servers are powered on for the report that I’ve been tasked with. There are also several other options that I need to collect on the VMs before I can call my report finished. I need to get the folder that the VM is in along with the VM cluster, and network adapter

	Get-datastore | Where {$_.name -like '*PROD*' -or $_.name -like '*REPL*'} | Get-VM | Select Name, @{N="vCPU";E={($_).NumCpu}}, @{N="Memory (GB)";E={($_).MemoryGB}}, @{N="Cluster";E={Get-Cluster -VM $_}}, @{N="Folder";E={$_.folder}}, @{N="Network";E={$_.Networkadapters.NetworkName}} | Format-Table

Now this is more like what I was tasked with collecting from vSphere. Now that I have the neccessary information, I need to export it to an CSV.

$report = @()
$VMs = Get-datastore | Where {$_.name -like '*PROD*' -or $_.name -like '*REPL*'} | Get-VM 
Foreach ($VM in $VMs){
	$line = $VM | Select Name, @{N="vCPU";E={($_).NumCpu}}, @{N="Memory (GB)";E={($_).MemoryGB}}, @{N="Cluster";E={Get-Cluster -VM $_}}, @{N="Folder";E={$_.folder}}, @{N="Network";E={$_.Networkadapters.NetworkName}}
	$report += $line
	}
	$report | Export-csv C:\scripts\logs\ProductionVMs.csv -NoTypeInformation -UseCulture
PowerCLI to get VMs based on Datastore report
Example report from the results of the Script to get VMs based on datastores

There are many different specs that can be collected with this script.  I also have a version that states whether or not the systems has RDMs attached. This is very helpful when needing to move the VM between cluster, as I would need to have the storage team to attach the RDMs to the new destination cluster.

$report = @()

$VMs = (Get-Datastore | Where {$_.Name -like '*REPL*' -or $_.Name -like '*PROD*'} | Get-VM).Name | sort

Foreach ($VM in $VMs){
    $line = Get-VM $VM | Select Name, @{N="vCPU";E={($_).NumCpu}}, @{N="Memory (GB)";E={($_).MemoryGB}}, @{N="Cluster";E={Get-Cluster -VM $_}}, @{N="Folder";E={$_.folder}}, @{N="Network";E={(Get-NetworkAdapter -VM $_).NetworkName}}, @{Expression={if (($_ | Get-HardDisk | Where {$_.DiskType -eq "RawPhysical"}) -eq $Null) {"No"} Else {"Yes"}}; Label="RDMs" }
    $report += $line
}
$report | Export-Csv C:\scripts\logs\ProductionVMs.csv -NoTypeInformation -UseCulture 

– Stuart

Find this script on Github

24 thoughts on “PowerCli to get all VMs on certain Datastores?”

    1. Joy,
      I have modified the script to capture the data that i believe that you are looking for.

      $report = @()
      $datastores = Get-datastore | Where {$_.name -like '*PROD*' -or $_.name -like '*REPL*'}
          Foreach ($Datastore in $datastores){
              $VMs = $datastore | Get-VM
                  Foreach ($VM in $VMs){
                      $Line = "" | Select Name, vCPU, 'Memory(GB)', TotalHDD, Cluster, Folder, Datastore, 'Datastore Capacity'
                      $line.Name                 = $VM.name
                      $line.vCPU                 = $vm.NumCpu
                      $line.'Memory(GB)'         = $vm.MemoryGB
                      $line.TotalHDD             = ($VM | Get-HardDisk | Measure capacityGB -sum).sum
                      $line.Cluster              = Get-Cluster -VM $VM.name
                      $line.Folder               = $VM.Folder
                      $line.Datastore            = $datastore.name
                      $line.'Datastore Capacity' = $Datastore.CapacityGB
                      $report                   += $line
                  }
          }
      $report | ft -AutoSize
      

      I hope this helps.

      -Stuart

      1. Hi Stuart, This script has been great help.

        I was wondering if you can advise how to run the above and also display if there are RDMs attached?

        Thank You!

        1. Vik,
          I have the exact script you might be looking for, and its the one that I use the most when collecting data. Let me know how this works for you.

          -Stuart

          $report = @()
          $VMs = (Get-Datastore | Where {$_.Name -like '*REPL*' -or $_.Name -like '*PROD*'} | Get-VM).Name | sort
          foreach ($vm in $VMs){
              $line = Get-VM $VM | Select Name,NumCpu, MemoryGB, @{N="OS Version";E={$_.ExtensionData.config.GuestFullName}}, @{N="Used Disk Space(GB)";E={[math]::ROUND($_.UsedSpaceGB,2)}},ToolsVersion, Version, @{N="Cluster";E={Get-Cluster -VM $_}}, @{N="Folder";E={$_.folder}}, @{N="ESX Host";E={Get-VMHost -VM $_}}, @{N="Network";E={(Get-NetworkAdapter -VM $_).NetworkName}}, @{N="DataStore";E={($_ | Get-Datastore).name}}, @{Expression={if (($_ | Get-HardDisk | Where {$_.DiskType -eq "RawPhysical"}) -eq $Null) {"No"} Else {"Yes"}}; Label="RDMs" }, notes
              $report += $line
          }
          $report | Export-Csv C:\scripts\logs\ProductionVMs.csv -NoTypeInformation -UseCulture
          
        2. Hi Stuart,

          This is excellent, if there could be a line added for the size of the datastore, it would be perfect!

          Thank you for your speedy response. Great help!

          Vik

          1. Vik,
            This should do it. I added 3 extra columns, Datastore name, DS freespaceGB, and DS capacityGB.

            -Stuart

            report = @()
            $VMs = (Get-Datastore | Where {$_.Name -like '*REPL*' -or $_.Name -like '*PROD*'} | Get-VM).Name | sort
            foreach ($vm in $VMs){
                $DataStore = Get-DataStore -VM $VM
                $line = Get-VM $VM | Select Name,NumCpu, MemoryGB, @{N="DataStore";E={$Datastore.Name}}, @{N="DS FreeSpaceGB"; E={$Datastore.FreeSpaceGB}}, @{N="DS CapacityGB"; E={$dataStore.CapacityGB}}, @{N="OS Version";E={$_.ExtensionData.config.GuestFullName}}, @{N="Used Disk Space(GB)";E={[math]::ROUND($_.UsedSpaceGB,2)}},ToolsVersion, Version, @{N="Cluster";E={Get-Cluster -VM $_}}, @{N="Folder";E={$_.folder}}, @{N="ESX Host";E={Get-VMHost -VM $_}}, @{N="Network";E={(Get-NetworkAdapter -VM $_).NetworkName}}, @{N="DataStore";E={($_ | Get-Datastore).name}}, @{Expression={if (($_ | Get-HardDisk | Where {$_.DiskType -eq "RawPhysical"}) -eq $Null) {"No"} Else {"Yes"}}; Label="RDMs" }, notes
                $report += $line
            }
            $report | Export-Csv C:\scripts\logs\ProductionVMs.csv -NoTypeInformation -UseCulture
            
  1. Awesome script! I was wondering if this can be modified to list the datastore name and tell me how many VMs are on that datastore and the free space left? I am trying to create a spreadsheet that can easily tell someone which datastore to use when building a VM. We like to put 15 VMs per datastore and keep the free space above 500G out of the 2T provisioned for each datastore. We have a lot of FC datastores and it can take some time to click on each one in vCenter to see how many VMs are on it and what the free space is. Thanks!

    1. Patrick,
      I believe that this is what you are looking for.

      $DatastoreReport = @()
      $Datastores = Get-datastore
      Foreach ($Datastore in $datastores){
          $DSLine             = "" | Select Name, NumofVMs, FreeSpaceGB
          $DSView             = $Datastore | Get-View
          $DSLine.Name        =  $DSView.Name
          $DSLine.NumofVMs    = ($DSView.vm).count
          $DSLine.FreeSpaceGB = [math]::Round(($DSView.Info.FreeSpace / 1024GB),2)
          $DatastoreReport   += $DSLine
      }
      $date = get-date -Format MM"-"dd"-"yyyy
      $DatastoreReport | Sort -Property FreeSpaceGB -Descending | Export-CSV C:\scripts\Logs\DataStore_Report_$date.csv -NoTypeInformation -UseCulture
      

      -Stuart

      1. Stuart,
        I was not expecting a reply so soon. You are a gentleman and a scholar!!! This is exactly what I have been needing for a very long time!!!
        I modified the script a tad and copied a line from another script I found online to look for a specific name. We have other datastores in vCenter but I only care about the FC ones for this spreadsheet. Not sure if I need the -or part but it works.

        Thanks again!!!

        $DatastoreReport = @()
        $Datastores = Get-datastore | Where {$_.name -like ‘*D1C01*’ -or $_.name -like ‘*REPL*’}
        Foreach ($Datastore in $datastores){
            $DSLine             = "" | Select Name, NumofVMs, FreeSpaceGB
            $DSView             = $Datastore | Get-View
            $DSLine.Name        =  $DSView.Name
            $DSLine.NumofVMs    = ($DSView.vm).count
            $DSLine.FreeSpaceGB = [math]::Round(($DSView.Info.FreeSpace / 1024GB),2)
            $DatastoreReport   += $DSLine
        }
        $date = get-date -Format MM"-"dd"-"yyyy
        $DatastoreReport | Sort -Property FreeSpaceGB -Descending | Export-CSV C:\Scripts\Logs\DataStore_Report_$date.csv -NoTypeInformation -UseCulture
        

        -Patrick

        1. Patrick,
          Thanks for the kind words. The ‘-or’ part of the script does additional sorting based on the names of the datastores. Its not necessary if your datastores don’t include *REPL* in the name.

          -Stuart

          1. Hi Stuart,
            If I wanted to include the total capacity of the datastores now, what would that line in the script look like?

          2. Hello Patrick,
            Happy Holidays! I have rewrote the script to add the capacity of the datastores along with the free space to the output of the script.

            $report = @()
            $DataStores = Get-datastore | Where {$_.name -like '*PROD*' -or $_.name -like '*REPL*'}
            foreach ($DataStore in $DataStores){
                $VMs = $DataStore | Get-VM
                Foreach ($VM in $VMs){
            	    $line                  = "" | Select Name, vCPU, MemoryGB, Cluster, Folder, Network, DataStore, "DS FreeSpaceGB", "DS CapacityGB"
                    $line.Name             = $VM.Name
                    $line.vCPU             = $VM.NumCpu
                    $line.MemoryGB         = $VM.MemoryGB
                    $line.Cluster          = Get-Cluster -VM $VM
                    $line.Folder           = $VM.folder
                    $line.Network          = $VM.Networkadapters.NetworkName
                    $line.DataStore        = $DataStore.Name
                    $line."DS CapacityGB"  = $DataStore.CapacityGB
                    $line."DS FreeSpaceGB" = [math]::Round($($DataStore.FreeSpaceGB),2)
            	    $report += $line
                }
            }
            $report | Export-csv C:\scripts\logs\ProductionVMs.csv -NoTypeInformation -UseCulture
            

            I hope this is what you are looking for.

            -Stuart

    1. Bill,
      This should be what you are looking for. Basically you would need to add the following to the $line row to also gather the Datastore name that the VM is on.
      @{N=”Datastore”;E={($_ | Get-datastore).Name}}

      Below is the script that has the line already inserted for reference.

      $report = @()
      $VMs = Get-datastore | Where {$_.name -like '*PROD*' -or $_.name -like '*REPL*'} | Get-VM 
      Foreach ($VM in $VMs){
      	$line = $VM | Select Name, @{N="vCPU";E={($_).NumCpu}}, @{N="Memory (GB)";E={($_).MemoryGB}}, @{N="Datastore";E={($_ | Get-datastore).Name}}, @{N="Cluster";E={Get-Cluster -VM $_}}, @{N="Folder";E={$_.folder}}, @{N="Network";E={$_.Networkadapters.NetworkName}}
      	$report += $line
      	}
      	$report | Export-csv C:\scripts\logs\ProductionVMs.csv -NoTypeInformation -UseCulture
      

      -Stuart

  2. This thread is great, thanks everyone for all the contribution! I’m a bash guy so I’m still learning powercli but is there a way to be prompted to enter the datastore name rather then having a static filter? I’ve been tweaking the scripts above and I want something like below:

    $esxDS = Read-host “Enter DS”
    $report = @()
    $DataStores = Get-datastore | Where {$_.name -like ‘$esxDS’ -or $_.name -like ‘*$esxDS*’}
    foreach ($DataStore in $DataStores){
    $VMs = $DataStore | Get-VM
    Foreach ($VM in $VMs){
    $line = “” | Select Name, Cluster, DataStore, “DS FreeSpaceGB”, “DS CapacityGB”
    $line.Name = $VM.Name
    $line.Cluster = Get-Cluster -VM $VM
    $line.DataStore = $DataStore.Name
    $line.”DS CapacityGB” = $DataStore.CapacityGB
    $line.”DS FreeSpaceGB” = [math]::Round($($DataStore.FreeSpaceGB),2)
    $report += $line
    }
    }
    $report | ft -AutoSize

    1. Chris,
      I have put together what I think you are looking for.

      $DataStore = Read-host “Enter Datastore Name”
      $report = @()
      $DS = Get-datastore -Name $Datastore
      $VMs = $DS | Get-VM
      Foreach ($VM in $VMs){
      $line = “” | Select Name, Cluster, DataStore, “DS FreeSpaceGB”, “DS CapacityGB”
      $line.Name = $VM.Name
      $line.Cluster = Get-Cluster -VM $VM
      $line.DataStore = $DS.Name
      $line.”DS CapacityGB” = $DS.CapacityGB
      $line.”DS FreeSpaceGB” = [math]::Round($($DS.FreeSpaceGB),2)
      $report += $line
      }
      
      $report | ft -AutoSize
      
  3. Hi Stuart
    I need help to create powercli Script , where I need the Datastore names Which does not have any VMS running .will above Script work for that ?

    DatastoreReport = @()
    $Datastores = Get-datastore
    Foreach ($Datastore in $datastores){
    $DSLine = “” | Select Name, NumofVMs, FreeSpaceGB
    $DSView = $Datastore | Get-View
    $DSLine.Name = $DSView.Name
    $DSLine.NumofVMs = ($DSView.vm).count
    $DSLine.FreeSpaceGB = [math]::Round(($DSView.Info.FreeSpace / 1024GB),2)
    $DatastoreReport += $DSLine
    }
    $date = get-date -Format MM”-“dd”-“yyyy
    $DatastoreReport | Sort -Property FreeSpaceGB -Descending | Export-CSV C:\scripts\Logs\DataStore

    1. Akash,
      The following script will help in finding your answer.

      Get-datastore | Select Name, @{N="Num of VMs"; E={($_.ExtensionData.VM).count}} | Sort -Property "Num of VMs"

      Please note, that just because there isn’t any VMs on that datastore, there still could be a VMDK file from another VM.

      -Stuart

  4. Stuart,

    I must say, you’ve great knowledge in PS scripting. I’m new into PS scripting.
    We’ve have VMC on AWS where we’ve 3 clusters and 3 DS in each cluster for workloads (with below names).
    Name
    —-
    WorkloadDatastore (2)
    WorkloadDatastore
    WorkloadDatastore (1)

    I’m looking to achieve below tasks from powercli using scripts. I need your help on this.
    – How to get the list all VMs those are assigned a specific SPBM storge policy (RAID5 or RAID6 or vSAN Default Storage Policy? We’ve created 2 policies named RAID5 & RAID6.
    – How to list VMs from specific cluster/datastore assigned to specific Storage policy?
    – Once I list down the VMs those are running with “vSAN Default Storage Policy”, how to assign RAID5 or RAID6 policy to them from a text files contains the VM list?

    1. Arunabh,
      This one is a bit tough for me as I don’t have access to VMC, but the commands should be the same.
      1. List VM assigned to specific SPBM – Get-VM | Get-SpbmEntityConfiguration
      This will list the VMs with the assigned storage policy.
      2. I’m really not sure on this one. I’m still looking into this. I don’t use storage policies.
      3. I found a script to apply the storage settings, but please use with caution

      $VMlist = get-datastore vsandatastore | get-vm
      foreach ($VM in $VMlist)
      {
        # First do the VM Home objects:
        Get-VM -Name $VM | Set-SpbmEntityConfiguration
        # Then all their VMDK's
        Get-VM -Name $VM | Get-Harddisk | Set-SpbmEntityConfiguration
      }
      

      https://communities.vmware.com/thread/543587
      Set-SpbmEntityConfiguration

      1. Thanks Stuart for sparing time to look into it.
        I’ll try this script with few test VMs.
        Get-VM | Get-SpbmEntityConfiguration command works and list all the VMs with their assigned storage policies, but I was looking to list the VMs those have assigned with a specific policy. For example, I’ve created multiple policies according to their roles (i.e. app, db etc). One of them is “SP-RAID6-VM-Policy”. I want to list the VMs those are assigned with this policy.

        PowerCLI C:\> Get-SpbmStoragePolicy -Name *RAID*

        Name Description AnyOfRuleSets CommonRule
        —- ———– ————- ———-
        SP-RAID5-VM-DB-Po… Storage policy used for Databases … {(VSAN.hostFailuresToTolera… {}
        SP-RAID1-Thick-Ga… Storage policy for Storage gateway… {(VSAN.hostFailuresToTolera… {}
        SP-RAID5-Thick-Ga… Storage policy for Storage gateway… {(VSAN.hostFailuresToTolera… {}
        SP-RAID6-VM-Policy Storage policy used for All VMs ex… {(VSAN.hostFailuresToTolera… {}
        RAID5-with-disk-s… RAID5 with 2 disk stipes {(VSAN.hostFailuresToTolera… {}

        Please let me know if you find anything. I would also respond back once I’ll get some fix. Thank you.

        1. Arunabh,
          Try the following to see if that will get the results you are looking for.

          Get-VM | Get-SpbmEntityConfiguration | Where {$_.Name -eq "SP-RAID6-VM-Policy"}
          
          1. Hi Stuart,

            Thanks.
            Sorry for the late response. I’ve tried above script but it waited for approx 2 mins and then back to prompt. There were no output on the screen.

  5. The Get-SpbmEntityConfiguration property is Entity versus Name. The below one-liner should work for you, Arunabh.

    Get-SpbmEntityConfiguration | Where-Object {$_.Entity -match ‘SP-RAID6-VM-Policy}

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.