With the vSphere 5 licensing buzz from the past days and the incredibel number of hits on my Query vRAM post, I considered that a script to help you discover your memory overallocations might be useful.
The script uses the metric mem.usage.average to find out what amount of it’s allocated memory a guest is actually using. The script produces a report that will help you to determine which guests would be good candidates to lower their memory allocation.
The script
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
function Get-VMMemUse{ param( [string]$LocationName = "Datacenters", [switch]$Realtime = $true, [switch]$Day, [switch]$Week, [switch]$Month ) $metric = "mem.usage.average" $location = Get-Inventory -Name $locationName $vms = Get-VM -Location $location | Sort-Object -Property Name if($Day){ $timeFrame = "Day" $intervalSecs = (Get-StatInterval -Name "Past Day").SamplingPeriodSecs $start = (Get-Date).AddDays(-1) $stats = Get-Stat -Entity $vms -Stat $metric -Start $start -IntervalSecs $intervalSecs -EA SilentlyContinue } elseif($Week){ $timeFrame = "Week" $intervalSecs = (Get-StatInterval -Name "Past Week").SamplingPeriodSecs $start = (Get-Date).AddDays(-7) $stats = Get-Stat -Entity $vms -Stat $metric -Start $start -IntervalSecs $intervalSecs -EA SilentlyContinue } elseif($Month){ $timeFrame = "Month" $intervalSecs = (Get-StatInterval -Name "Past Month").SamplingPeriodSecs $start = (Get-Date).AddDays(-31) $stats = Get-Stat -Entity $vms -Stat $metric -Start $start -IntervalSecs $intervalSecs -EA SilentlyContinue } else{ $timeFrame = "Realtime" $stats = Get-Stat -Entity $vms -Stat $metric -Realtime -MaxSamples 1 -ErrorAction SilentlyContinue } $groups = $stats | Group-Object -Property EntityId foreach($vm in $vms){ $tgtVM = $groups | where {$_.Group[0].Entity.Name -eq $vm.Name} if($tgtVM){ $percentage = [math]::Round(($tgtVM.Group | Measure-Object -Property Value -Average).Average,1) $allocated = [math]::Round($vm.MemoryMB/1KB,1) $used = [math]::Round($vm.MemoryMB * $percentage / 100 / 1KB, 1) } else{ $percentage = "No data" $allocated = "" $used = "" } New-Object PSObject -Property @{ Name = $vm.Name TimeFrame = $timeFrame PowerState = $vm.PowerState MemConfiguredGB = $allocated MemAvgUsedPerc = $percentage MemAvgUsedGB = $used } } } |
Annotations
Line 3: By default the report contains all guests in your vCenter
Line 4: The default time option is Realtime.
Line 12-13: The function collects all the guests for which you want a report.
Line 15-36: The script retrieves the statistical data, taking into account the selected time interval.
Line 38: The statistical data is organised in groups based on the EntityId.
Line 39-50: A guest that was offline during the time interval will have no statistical data.
Line 51-58: The result is placed in the pipeline.
Sample usage
The use of the function is rather straightforward.
By default the function will report on all guests in your vCenter. With the LocationName parameter you specify a datacenter, a cluster, a resourcepool, a folder and even an ESX(i) host. The function will then return the information for all guests under that location.
I also decided to provide some switches to let you select the timeframe the function should report on.
Realtime
In this mode, which is also the deault mode, you get the most recent value for the mem.usage.average metric.
You call the function like this
1 |
Get-VMMemUse |
or like this
1 |
Get-VMMemUse -Realtime |
Both of these will report on all the guests in your vCenter.
If you want to limit the guests, you use the LocationName parameter.
For example, to only report on the guests in a datacenter called MyDC, you would do.
1 |
Get-VMMemUse -LocationName MyDC |
Day
If you want to report on the average memory usage of the past day, you do
1 |
Get-VMMemUse -LocationName MyDC -Day |
Week
Similarly, if you want a report over the past week, you do
1 |
Get-VMMemUse -LocationName MyDC -Week |
Month
And finally, to get the report calculated over a month’s worth of data you do
1 |
Get-VMMemUse -LocationName MyDC -Month |
Output
By default the report will be displayed on the console. It looks something like this.
But you can also redirect the output to, for example a CSV file.
1 |
Get-VMMemUse | Export-Csv "C:\report.csv" -NoTypeInformation -UseCulture |
Which shows a similar result but of course in a CSV file.
From this type of report it should be obvious for which guest lowering the memory allocation would be an option to consider.
Ravikanth
Hi Luc,
how can i retrieve the information for past 3 or 6 months
LucD
Hi Ravikanth,
The function only provides switches for the last day/week/month I’m afraid.
But if you have the statistical data available on your VCSA, you could adapt the value on the Start parameter of the Get-Stat cmdlet.
Instead of doing AddDays(-31), you could do AddDays(3 * -31).
FinnC
Hi LucD,
For the environment I am working in however I need to connect to multiple vCenters.
As you can imagine, there are thousands of VMs across these vCenters.
So far;
I have configured Powershell to connect to multiple vCenters at the same time using the below;
Set-PowerCLIConfiguration -ProxyPolicy NoProxy -DefaultVIServerMode Multiple -confirm:$false
I then listed all the vCenters in I want to connect to in a txt file and created a variable containing this txt file. See below;
$VCenterlist = Get-Content C:\Scripts\Scheduled_Tasks\VMware_Monitoring\Live_Monitoring_Scripts\VCenterlist.txt
I then connect to the vCenters using stored credentials using the below;
Foreach ($vcenter in $VCenterlist)
{
if( Connect-VIServer -server $vcenter -Protocol https -ErrorAction Ignore)
{
Write-Host “vCenter $vcenter Connected” -ForegroundColor Cyan
}
else
{
Write-Host “Failed to Connect vCenter $vcenter” -ForegroundColor Cyan
}
}
Once connected to the required vCenters I am then able to get the Average CPU Usage, Average Memory Usage and also the Average Network Usage using the below;
Get-VM | Where {$_.PowerState -eq “PoweredOn”} | Select Name, NumCpu, MemoryMB,
@{N="CPU Usage (Average), Mhz" ; E={[Math]::Round((($_ | Get-Stat -Stat cpu.usagemhz.average -Start (Get-Date).AddDays(-90) -IntervalMins 5 | Measure-Object Value -Average).Average),2)}},
@{N=”Memory Usage (Average), %” ; E={[Math]::Round((($_ | Get-Stat -Stat mem.usage.average -Start (Get-Date).AddDays(-90) -IntervalMins 5 | Measure-Object Value -Average).Average),2)}} ,
@{N="Network Usage (Average), KBps" ; E={[Math]::Round((($_ | Get-Stat -Stat net.usage.average -Start (Get-Date).AddDays(-90) -IntervalMins 5 | Measure-Object Value -Average).Average),2)}}
I then Export these results to CSV using the below;
Export-Csv -Path C:\Scripts\Scheduled_Tasks\VMware_Monitoring\Live_Monitoring_Scripts\VM_Average_Usage_Stats.csv
and convert it to a HTML file with the below;
$css = @"
h1, h5, th { text-align: center; font-family: Segoe UI; }
table { margin: auto; font-family: Segoe UI; box-shadow: 10px 10px 5px #888; border: thin ridge grey; }
th { background: #0046c3; color: #fff; max-width: 400px; padding: 5px 10px; }
td { font-size: 11px; padding: 5px 20px; color: #000; }
tr { background: #b8d1f3; }
tr:nth-child(even) { background: #dae5f4; }
tr:nth-child(odd) { background: #b8d1f3; }
"@
Import-CSV "C:\Scripts\Scheduled_Tasks\VMware_Monitoring\Live_Monitoring_Scripts\VM_Average_Usage_Stats.csv" -Delimiter ',' | ConvertTo-Html -Head $css -Body "Email ReportnGenerated on $(Get-Date)” | Out-File “C:\Scripts\Scheduled_Tasks\VMware_Monitoring\Live_Monitoring_Scripts\Server_Health_Report_VMware_6.7_Environment.html”
All of that works great, however, I would like to customize the script to only export the VMs which have an average memory utilization of below 10% (over the space of 3 months).
My reasoning for this is that I would like to minimize the VMs which are listed in the report. I only want to show the VMs which may need to be looked at.
At the moment the report list far to many VMs and is not really working as it takes time to go through it and find the VMs which need to be looked at.
Do you know of a way for me to get the desired result??
Below is the script I have so far;
#########################################################################
## Server Health Check ##
## This scripts check the server Avrg CPU, Memory & Network Utlization ##
#########################################################################
$VCenterlist = Get-Content C:\Scripts\Scheduled_Tasks\VMware_Monitoring\Live_Monitoring_Scripts\VCenterlist.txt
Foreach ($vcenter in $VCenterlist)
{
if( Connect-VIServer -server $vcenter -Protocol https -ErrorAction Ignore)
{
Write-Host “vCenter $vcenter Connected” -ForegroundColor Cyan
}
else
{
Write-Host “Failed to Connect vCenter $vcenter” -ForegroundColor Cyan
}
}
{
Get-VM | Where {$_.PowerState -eq “PoweredOn”} | Select Name, NumCpu, MemoryMB,
@{N="CPU Usage (Average), Mhz" ; E={[Math]::Round((($_ | Get-Stat -Stat cpu.usagemhz.average -Start (Get-Date).AddDays(-1) -IntervalMins 5 | Measure-Object Value -Average).Average),2)}},
@{N=”Memory Usage (Average), %” ; E={[Math]::Round((($_ | Get-Stat -Stat mem.usage.average -Start (Get-Date).AddDays(-1) -IntervalMins 5 | Measure-Object Value -Average).Average),2)}} ,
@{N="Network Usage (Average), KBps" ; E={[Math]::Round((($_ | Get-Stat -Stat net.usage.average -Start (Get-Date).AddDays(-1) -IntervalMins 5 | Measure-Object Value -Average).Average),2)}} |
Export-Csv -Path C:\Scripts\Scheduled_Tasks\VMware_Monitoring\Live_Monitoring_Scripts\VM_Average_Usage_Stats.csv
}
$css = @”
h1, h5, th { text-align: center; font-family: Segoe UI; }
table { margin: auto; font-family: Segoe UI; box-shadow: 10px 10px 5px #888; border: thin ridge grey; }
th { background: #0046c3; color: #fff; max-width: 400px; padding: 5px 10px; }
td { font-size: 11px; padding: 5px 20px; color: #000; }
tr { background: #b8d1f3; }
tr:nth-child(even) { background: #dae5f4; }
tr:nth-child(odd) { background: #b8d1f3; }
“@
Import-CSV “\\IECM1AP101.tycoelectronics.net\f$\Scripts\Scheduled_Tasks\TEIS_VMware_Monitoring\Live_Monitoring_Scripts\VM_Average_Usage_Stats.csv” -Delimiter ‘,’ | ConvertTo-Html -Head $css -Body “Email Report`nGenerated on $(Get-Date)” | Out-File “\\IECM1AP101.tycoelectronics.net\f$\Scripts\Scheduled_Tasks\TEIS_VMware_Monitoring\Live_Monitoring_Scripts\Server_Health_Report_TEIS_VMware_6.7_Environment.html”
LucD
You should be able to do that with a Where-clause.
Something like this
Get-VM | Where-Object { $_.PowerState -eq “PoweredOn” } |
Select-Object Name, NumCpu, MemoryMB,
@{N = "CPU Usage (Average), Mhz" ; E = {
[Math]::Round((($_ | Get-Stat -Stat cpu.usagemhz.average -Start (Get-Date).AddDays(-1) -IntervalMins 5 |
Measure-Object Value -Average).Average), 2)
} },
@{N = ”Memory Usage (Average), %” ; E = {
[Math]::Round((($_ | Get-Stat -Stat mem.usage.average -Start (Get-Date).AddDays(-1) -IntervalMins 5 |
Measure-Object Value -Average).Average), 2)
} } ,
@{N = "Network Usage (Average), KBps" ; E = {
[Math]::Round((($_ | Get-Stat -Stat net.usage.average -Start (Get-Date).AddDays(-1) -IntervalMins 5 |
Measure-Object Value -Average).Average), 2)
} } |
Where-Object {$_.'Memory Usage (Average), %' -lt 10} |
Export-Csv -Path C:\Scripts\Scheduled_Tasks\VMware_Monitoring\Live_Monitoring_Scripts\VM_Average_Usage_Stats.csv
Vikram Dhandapani
I think if you are trying to execute the script to pull out a historical report which is >30 days, you must use the time interval to be 120 minutes. If this is already noted, please ignore my suggestion
LucD
No, I don’t think you need to do that.
What is true is that due to aggregation there will only be data for 1 day intervals available.
But you can still specify another interval.
Ilnur
Result: Nodata for all VMs:((
TimeFrame Name PowerState MemAvgUsedGB MemAvgUsedPerc MemConfiguredGB
Month VM_Name_Test PoweredOff No data
i
m write in script my host name
function Get-VMMemUse{
param(
[string]$LocationName = "ESXHost_Name"
and run command:
Get-VMMemUse -Month | Export-Csv "C:\PowerCLI\Report\memOverlocations.csv" -NoTypeInformation -UseCulture
Whats wrong?
Sanyam
i want to calculate the maximum allocated size for the particular shared folder using powershell.
can you share a code to get the maximum allocated size to any particular shared folder
LucD
Hi Sanyam,
Can you be more specific, what kind of shared folder are you talking about ?
Is that a Samba or NFS share for example, and do you have any access to the sharing host ?
And please also defined what exactly you mean with “maximum allocated size”.
Amit
Hi Lucd,
Can we provide date range? Need help for this.
Thanks
Amit
Amit
Superb script
LucD
Thanks
steve
Hi LucD.
This is a very helpful script, can you include FolderName in the output.
Cheers
Steve
steve
anyone for the above?
Owen
I’m having difficulty running this script, which is a shame because it is exactly what i’m looking for!
Calling “Get-Inventory -Name MyVMName | Get-VM” returns:
Get-VM : The input object cannot be bound to any parameters for the command eit
her because the command does not take pipeline input or the input and its prope
rties do not match any of the parameters that take pipeline input.
At line:1 char:49
If I append Get-VMMemUse to the end of the script, then call the script using:
./memusescrupt.ps1 -LocationName MyDC -Day
It will run for hours and hours (we have 1000s) of VMs. Even specifying a folder or resourcepool with only a handful of VMs takes too long, which leads me to think that it is still running it against the entire DC.
Any thoughts?
LucD
Hi Owen,
When you just do ‘Get-Inventory -Name MyVMName’ it will return a VirtualMachine object. That is sufficient, no need to do a Get-VM after that.
The error message comes from the fact that the Get-VM cmdlet doesn’t have any parameter that accepts a VirtualMachine through the pipeline.
Did you define the parameters in the .ps1 file ?
In fact the .ps1 file should look something like this
param($location)
function Get-VMMemUse {
# My function code
}
Get-VMMemUse -LocationName $location -Day
Then you can do
./memusescrupt.ps1 -Location MyDC
Santhosh
Can someone help me modify the script to include the Peak Mem Usage.
Bill
Hi, I am trying to use this really nice script and I did change the name of it. That being said, I am trying to redirect the output to a csv file and get the following error:
Missing function body in function declaration.
At C:\Users\bseeley_c_p\Documents\Scripts\vm_mem_use.ps1:1 char:23
+ function Get-VMMemUse <<<< | Export-Csv"C:\report.csv" -NoTypeInformation
+ CategoryInfo : ParserError: (:) [], ParseException
+ FullyQualifiedErrorId : MissingFunctionBody
eulcedes
Very helpful script! BUT i’m getting the same error as Virag!
“Cannot index into a null array.
at *script location*:43 char:37
+ $tgtVM = $groups | where {$_.Group[ <<<< 0].Entity.Name -eq $vm.Name}
+ CategoryInfo : InvalidOperation: (0:Int32) [], RuntimeException
+ FullyQualifiedErrorId : NullArray"
This occurs with any time range, and if i try to have it display in-window OR export to file.
Any suggestions? Am I missing something easy here?
hermouet
HI,
this script can’t calcul balloning memory ? because when vm is very slow often time it’s because memory are swapped to storage no ?
for exe vm with 5gb of ram, and 4gb of dedicated, there is 1gb right on disk storage… so i want to see on all vm which server is used this “1gb” of swapped ram. i deduce it name is “balloning” exact ?
tks advance
LucD
@Hermouet, I think the answer is a bit more complex. Swapping is not always bad.
Have a look at Understanding Memory Management in VMware vSphere 5, it explains the memory management techniques that are available to the hypervisor.
Virag Jain
getting this error :
Cannot index into a null array.
+ $tgtVM = $groups | where {$_.Group[ <<<< 0].Entity.Name -eq $vm.Name}
+ CategoryInfo : InvalidOperation: (0:Int32) [], RuntimeException
+ FullyQualifiedErrorId : NullArray
Owen
Great script LucD – thanks!
Though it seems that no matter which vCenter and LocationName I specify, it reports back on every VM in the datacenter listing. Also need to butcher the script to force it to run for a specific timeframe, IE the -Month or -Week arguments don’t seem to work. Any ideas?
suresh
Hi, Can we get the Memory Usage report in Cluster level using Script .. like assigned memory to VMs and Actual VM’s usage memory and free memory in cluster level.
Thanks
Suresh
Roderick
Hi Luc,
Thanks for publishing the script!
Question: when I look at the MemAvgUsedGB and MemAvgUsedPerc results then I can’t help thinking that the numbers are so low.
For example: For a certain VM the MemAvgUsedGB is 0,4 and the configured memory is 2GB. When I lower the amount of memory for this VM to 1,5 GB the guest OS starts to swap and performance drops. This scenario can be reproduced with other VM’s.
Can you explain the MemAvgUsedPerc and MemAvgUsedGB this?
Grtz,
Roderick
LucD
@Roderick, that is indeed strange.
Question, did you run the report with the Realtime, Day, Week or Month switch ?
Maestriweb
An all round well written article..
Mr Major
Hi LucD!
Excellent script. Can you add peak utilization to your report’s results? We need to be able to see any deviation from the avgs…..
LucD
Check my answer to kfkernel below, that provides the peakuse values.
kfkernel
@LucD
Thank you works perfect!
kfkernel
Hi,
Thank you for yet another good script!
I wanted to also show the maximum % of used memory. I tried to add the following lines to the script.
…
$used = [math]::Round($vm.MemoryMB * $percentage / 100 / 1KB, 1)
$peakuse = $tgtVM.Group | sort-object -Descending
$peakuse = $peakuse[0].Value
}
and of course adding $peakuse to the new object. It produces some output but it seem to fail with identifying the maximum value. I cross checked one machine, in the csv file it reports 9.66 % PeakUsage. But when i look in vCenter 1 month graph peak is 22%
Any other ideas on how to get the Peak Usage?
LucD
@kfkernel, try using the following
if($tgtVM){
$percentage = [math]::Round(($tgtVM.Group | Measure-Object -Property Value -Average).Average,1)
$allocated = [math]::Round($vm.MemoryMB/1KB,1)
$used = [math]::Round($vm.MemoryMB * $percentage / 100 / 1KB, 1)
$peakuse = [math]::Round(@($tgtVM.Group | Sort-Object -Property Value -Descending)[0].Value,1)
}
You have to tell the Sort-Object cmdlet on which property to sort.
Harry
Great script, exactly what I was looking for, working like a charm.
Thanks
James
Did see one warning:
WARNING: ‘Entity’ property is obsolete. Use ‘EntityId’ instead.
But otherwise seemed to work well! thanks!! great script again!
LucD
@James, thanks.
What you see is a indeed a warning and it doesn’t break the script.
To use EntityId would have made the script slightly more complex, so I opted to leave the Entity in there for now.
Eugene
@LucD
Never got your email. Still having the same blank output issue 🙁
Ian
Hi,
Great script but just on question. How do I run it? I am connected to vCenter. I have copied the script to a text file and saved it as a .ps1 and bat. When I run Get-VMMemUse I get an error “The term ‘Get-VMMemUse’ is not recognized as the name of a cmdlet, function, script file.
Thanks,
LucD
@Ian, thanks.
The script is in fact a function and you have 2 ways of using it.
1) You save it in a .ps1 file and at the end you add the call to the function
function Get-VMMemUse{
...
}
Get-VMMemUse
You invoke the script from PowerCLI prompt:
PS> .\myfile.ps1
2) You save only the function in a .ps1 file.
To get the function into your PowerCLI session, you will have to dot-source the .ps1 file
PS> . ./myfile.ps1
That is a dot, followed by a space and then the .ps1 file
After that the function is know in your session. You can check with
PS> Get-Function
To call the function, you just call it from the PowerCLI prompt
PS> Get-VMMemUse
Matt
Hey there, nice script. Only vice I have is that, even if I specify day, month etc, it always appears to list realtime in the CSV output? is this only cosmetic, or is it ignoring the period flag?
LucD
@Matt, no it wasn’t cosmetic, it was a real bug 🙁
Thanks for discovering the flaw, the script has been corrected.
Conrad
Pretty Nice Script! Beats look at a bunch of graphs in vcenter
KevinS
@KevinS
Thanks Luc for the help with my interface between chair and keyboard
LucD
Thanks for the feedback Kevin, glad the problem is solved.
KevinS
when I run the script I get no output. This seems similar to other post.
Ron Davis
This may be a dumb question, but why are you looking at cpu.usage.average to determine memory usage?
LucD
@Ron, not dumb, that was a typo. It’s corrected now.
Sorry about that.
Eugene
I feel like it’s also user error on my part but still not having luck.
I am connected to vcenter and I did try both Get-VMMemUse -LocationName MyDC and .\Get-VMMemUse -LocationName MyDC (replacing MyDC with my actual DC name). Getting no output.
LucD
@Eugene, send you an email.
Let’s discuss the details offline.
John K
@LucD
Definitely user error on my part. The script works perfectly. Thanks!
LucD
@John, thanks for letting us know.
I hope the report allows you to find the memory gluttons in your environment 🙂
John K
I’m also getting no output when using my datacenter, cluster, or hostname for locationName; after connecting to my VC.
LucD
@John, can you share how you called the function ?
Does the following return a list of guests ?
Get-Inventory -Name anyname | Get-VM
Troy Clavell
Another great script! Does that mean I owe you more pints at VMworld?
LucD
@Troy, this goes on the same tab 😉
Eugene
Nothing happens when I try to run this.
LucD
@Eugene, could you give some more details ?
How did you call the function ? Did you use the LocationName ?
Were you connected to the vCenter ?