Powershell: List VM Name, Hard Drive Name and Datastore Name

I recently had a request for a script that listed virtual machines hard drives and datastore locations. This is a fairly quick and easy script. The only problem is the formatting. Since some VM’s have more than one HD the formatting is a little odd. Instead of use the Export-CSV cmdlet I decided to use the Out-File cmdlet and save the results to a text file. It looks a bit cleaner to me.

$VMs = Get-vm

foreach ($vm in $VMs){
$ds = Get-HardDisk -VM $vm | Select-Object name,filename
$vm.Name,$ds | Format-Table | Out-File -Append -FilePath "c:\filename.txt}

Here’s a preview of the output:

Powershell: Clone Multiple Virtual Machines Simultaneously

One of my roles is to stand up environments for development or testing projects. I receive requests on a weekly basis for anywhere from 1-10 virtual machine environments. If I were to use the typical method of vSphere to clone a set of 10 it may take 4-5 hours. The thing about this script is in order to clone multiple VM’s the specs need to be the same, meaning vCPU, RAM and hard drives. You also need to determine the names you want to use for the virtual machines and then write them down in a text file called servers.txt. I’ll break down the code to show you exactly what it’s doing.

$VM_Location = "Example"
$servers = (Get-Content servers.txt)

Line 1. Change this variable to suite your environment. This is the inventory location. Where your virtual machine will be stored in the inventory list.
Line 2. Read in the server names from the servers.txt file – Store in the same location as this script.

function clone_vms {
	$template_name = (Read-Host "Enter the template name").Trim()
	$ESX_host = (Read-Host "Enter the ESX host").Trim()
	$datastore = (Read-Host "Enter the datastore location for the c:\ drive")
	foreach($server in $servers)
	{
		New-VM -Location $VM_Location -VMHost $ESX_host -Name $server -Template $template_name -Datastore $datastore
	}
}

Line 1. Simply creating a function to clone the virtual machines. Makes the code a bit more organized and cleaner.
Line 2. Prompting the user for the name of the template. This needs to be exact.
Line 3. Prompting the user for the name of the esx host to place these virtual machines on.
Line 4. Prompting the user for the name of the datastore for the c:\ drive.
Line 5. Simple foreach statement which says for each server in the servers.txt file do the following block of code {xxxxx}
Line 7. New-VM is the cmdlet used to create a new virtual machine. Here we define the inventory location with the $VM_Location variable. We also define the ESX Host where we want to place this vm, the name of the VM, the template to use and the datastore location. Just a tip, if you want to run multiple clones at the same time use the -RunAsync at the end of line 6, inside the two }}. Be warned though if you run more than 4-5 at a time this will severely impact the performance of vCenter. Right now once one vm clone is finished it automatically starts the next one.

function change_vcpu{
	$vCPU = Read-Host "Enter the number of vCPU's"
	foreach($server in $servers)
	{
		Set-VM $server -NumCpu $vCPU -Confirm:$false -RunAsync
	}
}

Line 4. Set-VM cmdlet is used to change things like vCPU. We define the $server we want to change the vCPU on based on the foreach loop. We also need to let the cmdlet know how many vCPU we are changing to. The -Confirm:$false switch is telling the cmdlet to not prompt me for permission to change this. The -RunAsync switch says change all servers at the same time.

function add_harddrive {
	$size = (Read-Host "Enter size in KB").Trim()
	$datastore = (Read-Host "Enter DataStore Location").Trim()
		foreach($server in $servers)
		{
			New-HardDisk -VM $server -CapacityKB $size -ThinProvisioned -DataStore $datastore
		}
}

Line 6. The New-HardDisk cmdlet gives us the ability to add hard drives to a VM. The -VM switch lets the cmdlet know which VM we are adding a hard disk to. The -CapacityKB switch tells the cmdlet how large of a hard drive we are creating. I’ll talk more about the -ThinProvisioned switch in another blog.

function change_ram_amount {
	$ram = Read-Host "Enter amount of RAM in MB"
	foreach($server in $servers)
		{
			Set-VM $server -MemoryMB $ram -Confirm:$false -RunAsync
		}
}
function power_on_vms {
	foreach ($server in $servers) 
	{
		Start-VM $server -RunAsync
	}
Write-Host "$server has been powered on"
}

clone_vms

$change_vcpu = (Read-Host "Do you want to change the vCPU count [y] or [n]").Trim()
if($change_vcpu -eq "y") {change_vcpu}

$add_harddrive = (Read-Host "Do you want to add a harddrive [y] or [n]")
if($add_Harddrive -eq "y") {add_harddrive}

$change_ram = (Read-Host "Do you want to change the amount of RAM [y] or [n]")
if($change_ram -eq "y") {change_ram_amount}

power_on_vms

I’ve pretty much gone through all of the cmdlets used, all of the switches used, etc. The last section is changing the RAM amount with the Set-VM cmdlet and then defining the function power_on_vms which does exactly what it says. The next chunk is what actually makes the script run.

Lets take line 18-19 as an example. Line 18 is asking the user whether or not they want to change the RAM amount on the virtual machines and storing the response in a variable called $change_vcpu. Line 19 is a conditional statement saying if $change_vcpu is equal to “y” then call the change_vcpu function. If the user types anything other than “y” the conditional statement is not equal and moves on to the next one which is prompting to add a hard drive. The same procedure is followed through the rest.

What I do is run a set of 10 virtual machines, specify what I need to and move on to something else. Once I see in vCenter that the virtual machines are all done being created, I check my console again to modify the vCPU, RAM and hard drive specs.

Powershell: Identify all VMs using a particular NIC

Can you imagine going through hundreds or thousands of VM’s looking for a specific NIC. To do that manually would have taken hours if not days. I was asked if I had a way to identify which VM’s are running the “vmxnet3” adapter. I wrote up this little script which gave us the answers we were looking for in about 20-30 minutes.

#Identify all VMs with a specific NIC name

$VMs = (Get-VM)

#Change the adapter_name variable to match the NIC name you are looking for
$adapter_name = "vmxnet3"

#Change the network_path variable to point to your text file that will store the results
$network_path = "c:\powershell scripts\network_adapter.txt"

ForEach ($VM in $VMs){
	$VM_Name = $VM.name
	$Get_Adapter = Get-NetworkAdapter $VM
	$Get_Adapter_Type = $Get_Adapter.Type
	if($Get_Adapter_Type -eq $adapter_name)
		{$VM_name, $Get_Adapter_type | Out-File -FilePath $network_path -append}}

Note: This script took quite a while to run. I’ve modified it to run much much more efficiently. This script will finish in about 5 minutes for 1,500+ vm’s.

$VMs = Get-VM | Select-Object -Property Name,@{N="Adapter Type";E={(Get-NetworkAdapter -VM $_).Type}} | 
`Export-Csv -NoTypeInformation -Path network_adapters.csv

The only thing about this script is right now it will only report the NIC type for vm’s with 1 NIC. If the VM has two NIC’s it won’t report a type. Also, it will report all NIC types. You will need to open the CSV file and apply a filter based on the type you’re looking for.

Powershell: Count the number of VMs with memory limits and determine the difference

Count the number of VMs with memory limits and determine the difference between configured memory and memory limit. Output the data to two different text files in order to manipulate in different ways.

I am in the process of building several very large dashboards in order for managers and executive managers to be able to quickly determine the state of our, VMware, NetApp and database environments. While writing this script I decided to include the number of “trimmed” virtual machines or virtual machines with memory limits. The reason I did this is because a lot of people don’t understand the difference between how physical memory on a single server is managed vs memory on a hypervisor. After taking a look at CapacityIQ I quickly realized the majority of our VMs were oversized. Half of the memory and cpu weren’t even being utilized nor had never been utilized. Because VMware doesn’t have the capability to reconfigure the RAM without resetting a server we decided to set limits. This doesn’t require a reboot and is easy to change if the ESX server decides to balloon the VM or force it to swap for one reason or another. It’s also a good statistic for management to see and be able to dissect. Here is the script I wrote to determine how many VMs have limits and what the difference between the configured memory and the limit is. This way we’re able to tell how much unused memory we’ve actually recovered.

#Get VM Objects
$VMs = Get-VM

#Initialize variables to 0
$count = 0
$Difference = 0
$Recovered_Memory = 0
$Total = 0

#Get limit data for each virtual machine
Write-Host "Collecting VM limit information..."
foreach($vm in $VMs){
	$result = (Get-VMResourceConfiguration -VM $vm)
		if($result.MemLimitMB -ne '-1')
		{
			$Count += "1"
			$vm_memory = $vm.MemoryMB
			$vm_limit = $result.MemLimitMB
			$Difference = ($vm_memory - $vm_limit)
			$Recovered_memory += $Difference
		}
}
Write-Host "Generating results files..."

$final = ($Recovered_Memory/1024)
"{0:N2}" -f $final | Out-File -FilePath recovered_memory.txt
$count | Out-File -FilePath vms_with_limits.txt