Powershell output not formatting properly - powershell

I'm using this script to get some basic info from virtual machines on our HyperV cluster:
#Establish global variables and MasterList array
$VMList = Get-VM
$MasterList = #()
#Loop through VMs and get Name, Processor count, assigned memory, add to MasterList
foreach($vm in $VMList) {
$ALLVHD = Get-VHD $vm.harddrives.path -ComputerName $vm.computername
$MyObject = New-Object PSObject -Property #{
Name = ($vm).VMName
ProcessorCount = (Get-VMProcessor $vm).Count
AssignedMemory = ($vm).MemoryAssigned
DiskType = $VHD.VhdType
'Total(GB)' = [math]::Round($VHD.Size/1GB)
'Used(GB)' = [math]::Round($VHD.FileSize/1GB)
'Free(GB)' = [math]::Round($VHD.Size/1GB- $VHD.FileSize/1GB)
}
$MasterList += $MyObject
}
$MasterList | Out-GridView
It mostly works, but there are several problems. The column order is wrong, it outputs DiskType,Name,AssignedMemory,Free(GB),ProcessorCount,Used(GB),Total(GB) and I have no idea why because that's now how it's ordered in the code. Also, the Free,Used, and Total amounts are 71, 29, and 100 for all items when that is incorrect.
If any Powershell experts can help me with this, it would be much appreciated.

I figured it out, thanks for the suggestions
#Establish global variables and MasterList array
$VMList = Get-VM
$MasterList = #()
#Loop through all VMs on node
foreach($vm in $VMList) {
$ALLVHD = Get-VHD $vm.HardDrives.path -ComputerName $vm.computername
foreach($VHD in $ALLVHD){
$MyObject = New-Object PSObject -Property #{
Name = $vm.Name
DiskType = $VHD.VhdType
Path = $VHD.Path
'Total(GB)' = [math]::Round($VHD.Size/1GB)
'Used(GB)' = [math]::Round($VHD.FileSize/1GB)
'Free(GB)' = [math]::Round($VHD.Size/1GB- $VHD.FileSize/1GB)
ProcessorCount = (Get-VMProcessor $vm).Count
AssignedMemory = ($vm).MemoryAssigned
}
#Add information to MasterList array
$Masterlist += $MyObject
}
}
#Change this line to print output however you want
$MasterList | select Name,DiskType,Path,'Total(GB)','Used(GB)','Free(GB)',ProcessorCount,#{Expression={$_.AssignedMemory/1GB};Label="AssignedMemory(GB)"}

Related

Optimize for each variable powershell script

$allResources = #()
$subscriptions=Get-AzSubscription
ForEach ($vsub in $subscriptions){
Select-AzSubscription $vsub.SubscriptionID
Write-Host
Write-Host "Working on "$vsub
Write-Host
$allResources += $allResources |Select-Object $vsub.SubscriptionID,$vsub.Name
$result=#()
$webapps = Get-AzWebApp
foreach($webapp in $webapps){
$Tier = (Get-AzResource -ResourceId $webapp.ServerFarmId).Sku.Tier
$SKU = (Get-AzAppServicePlan -ResourceGroupName $webapp.ResourceGroup).Sku.Size
$AppServiceName = (Get-AzAppServicePlan -ResourceGroupName $webapp.ResourceGroup).Name
$obj = [PSCustomObject]#{
TenantId = $vsub.TenantId
SubscriptionName = $vsub.Name
WebappName = $webapp.Name
ResourceGroup = $webapp.ResourceGroup
Hostname = $WebApp.DefaultHostName
PricingTier = $Tier
SKU = ($SKU -join ',')
AppServiceName = ($AppServiceName -join ',')
#State = $webapp.State
#Location = $webapp.Location
#AppType = $webapp.Kind
}
$result += $obj
$result | Export-Csv -Path "E:\webapps_filter.csv" -Append -NoTypeInformation
$input = 'E:\webapps_filter.csv'
$inputCsv = Import-Csv $input | Sort-Object * -Unique
$inputCsv | Export-Csv "E:\webapps.csv" -NoTypeInformation}}
Right now I am using the above script to fetch all the required data of web apps from all the subscriptions. Currently, the script is taking time to execute, I need to optimize it and also the script gives a duplicate output so in last have added the filter to sort out it by unique entry.
It seems you are adding stuff to an array variable $allResources you don't use, so get rid of that.
Instead of the costly (time/memory) $result += $obj, better let PowerShell collect the objects using $result = foreach(..)
You are appending a temporary file inside the foreach loop on each iteration, then import this file and filter it on all properties to become unique.
again, inside the loop you are exporting this uniqified data to a CSV file on every iteration
You are calling upon cmdlet Get-AzAppServicePlan twice to get different properties. Use it only once would save time
as aside, you should not use a self-defined variable called $input as this is an Automatic variable
Try:
$subscriptions = Get-AzSubscription
$result = foreach ($vsub in $subscriptions){
Select-AzSubscription $vsub.SubscriptionID
Write-Host
Write-Host "Working on $($vsub.Name)"
Write-Host
foreach($webapp in (Get-AzWebApp)){
$Tier = (Get-AzResource -ResourceId $webapp.ServerFarmId).Sku.Tier
$Plan = Get-AzAppServicePlan -ResourceGroupName $webapp.ResourceGroup
# output the object so it gets collected in $result
[PSCustomObject]#{
TenantId = $vsub.TenantId
SubscriptionName = $vsub.Name
SubscriptionID = $vsub.SubscriptionID
WebappName = $webapp.Name
ResourceGroup = $webapp.ResourceGroup
Hostname = $webapp.DefaultHostName
PricingTier = $Tier
SKU = #($Plan.Sku.Size) -join ','
AppServiceName = #($Plan.Name) -join ','
#State = $webapp.State
#Location = $webapp.Location
#AppType = $webapp.Kind
}
}
}
# sort unique and export the file
$result | Sort-Object * -Unique | Export-Csv -Path "E:\webapps.csv" -NoTypeInformation

Powershell - Where-Object in forEach Loop

I need some help for executing a little script and filtering results ...
I checkmy VM diskspace with the following script
Get-VM | ForEach-Object {
$VM = $_
$_.Guest.Disks | ForEach-Object {
$Report = "" | Select-Object -Property VM,Path,Capacity,FreeSpace,PercentageFreeSpace
$Report.VM = $VM.Name
$Report.Path = $_.Path
$Report.Capacity = $_.Capacity
$Report.FreeSpace = $_.FreeSpace
if ($_.Capacity) {$Report.PercentageFreeSpace = [math]::Round(100*($_.FreeSpace/$_.Capacity))}
$report
}
}
But I'd like to add a filter that my report only show me PercentageFreeSPace lesser than 20.
I try to add a where-object condition to my report with no success...
Can somebody help me please ? I m a beginner in PS...
Thanks in advance,
Best regards
For future questions you should format your script to be more readable.
I believe I have done it quite well below:
[EDIT] Seems that something gone wrong with displaying your question on my laptop. Just after my answer, the formatting is almost the same as provided below.
Get-VM | ForEach-Object {
$VM = $_
$_.Guest.Disks | ForEach-Object {
$Report = "" | Select-Object -Property VM,Path,Capacity,FreeSpace,PercentageFreeSpace $report
$Report.VM = $VM.Name
$Report.Path = $_.Path
$Report.Capacity = $_.Capacity
$Report.FreeSpace = $_.FreeSpace
if ($_.Capacity) {
$Report.PercentageFreeSpace = [math]::Round(100*($_.FreeSpace/$_.Capacity))
}
$report
}
}
To be honest I do not understand why you are using pipe at line 4.
Regarding your question, you should puy Where-Object clause before you will go to second for each loop.
First off, try and avoid multiple nested pipes to foreach-object, use foreach instead. That is, don't do
Get-VM | ForEach-Object {
$VM = $_
$_.Guest.Disks | ForEach-Object {
but
foreach($vm in Get-VM) {
foreach($disk in $vm.Guest.disks) {
This makes it easy to see what objects are handled later, and there isn't need to save current object on the pipeline into a temp variable (which you do by $VM = $_).
Also, objects can be initialized via a hash table instead of using select-object. Like so,
$pctLimit = 20 # Variable sets percentage limit instead of a magic number
foreach($vm in Get-VM) {
foreach($disk in $vm.Guest.Disks) {
# Report object is created via hash table
$Report = new-object PSObject -Property #{
VM = $VM.Name
Path = $disk.Path
Capacity = $disk.Capacity
FreeSpace = $disk.FreeSpace
ZeroDisk = ($disk.Capacity -gt 0) # NB: calculated property
}
if (-not $Report.ZeroDisk) { # Process only non-zero disks
$Report.PercentageFreeSpace = [math]::Round(100*($Report.FreeSpace/$Report.Capacity))
if($Report.PercentageFreeSpace -lt $pctLimit) {
$report
} else {
# Disk had more than $pctLimit free space. Now what?
}
} else {
# Disk had zero capacity. Now what?
}
}
}
FIrst of all thanks for your help
I manage to filter with the following
Get-VM | ForEach-Object {
$VM = $_
$_.Guest.Disks | ForEach-Object {
$Report = "" | Select-Object -Property VM,Path,Capacity,FreeSpace,PercentageFreeSpace
$Report.VM = $VM.Name
$Report.Path = $_.Path
$Report.Capacity = $_.Capacity
$Report.FreeSpace = $_.FreeSpace
if ($_.Capacity) {$Report.PercentageFreeSpace = [math]::Round(100*($_.FreeSpace/$_.Capacity))}
if( $_.Capacity -and $Report.PercentageFreeSpace -lt 30 ) {
$Report
}
}
}
`
Thanks,
Best regards

How to get all instances of a class?

My knowledge of classes is relatively new. But I want to output all objects/instances of a class with powershell. Is this even possible? Here is an example of how I create two objects of the class Computer.
Class Computer {
[String]$Name
[String]$Description
[String]$Type
}
$NewComputer = New-Object 'Computer'
$NewComputer.Name = 'ultra1'
$NewComputer.Description = 'Lenovo Yoga 900'
$NewComputer.Type = 'Ultrabook'
$NewComputer = New-Object 'Computer'
$NewComputer.Name = 'ultra2'
$NewComputer.Description = 'Lenovo Yoga X1'
$NewComputer.Type = 'Ultrabook'
Now I want to output both objects, how can I do this?
Judging from your comment "if there is a possibility to get the objects of a class without putting them into a collection", I think what you want to do is to create new Computer objects using your class and later on use these objects as separate variables.
For easier creation, I'd suggest you add a constructor to the class, so you can create the objects in a single line:
Class Computer {
[String]$Name
[String]$Description
[String]$Type
# add a constructor
Computer(
[string]$n,
[string]$d,
[string]$t
){
$this.Name = $n
$this.Description = $d
$this.Type = $t
}
}
# now create the computer objects
[Computer]$pcUltra1 = [Computer]::new('ultra1','Lenovo Yoga 900','Ultrabook')
[Computer]$pcUltra2 = [Computer]::new('ultra2','Lenovo Yoga X1','Ultrabook')
# show what you have now
$pcUltra1
$pcUltra2
Output:
Name Description Type
---- ----------- ----
ultra1 Lenovo Yoga 900 Ultrabook
ultra2 Lenovo Yoga X1 Ultrabook
Hope that helps
Maybe this will help
Class Computer {
[String]$Name
[String]$Description
[String]$Type
}
# a collection of computers
$computers =#()
$NewComputer = New-Object 'Computer'
$NewComputer.Name = ‘ultra1’
$NewComputer.Description = ‘Lenovo Yoga 900’
$NewComputer.Type = ‘Ultrabook’
# append a computer to teh collection
$computers += $NewComputer
$NewComputer = New-Object 'Computer'
$NewComputer.Name = ‘ultra2’
$NewComputer.Description = ‘Lenovo Yoga X1’
$NewComputer.Type = ‘Ultrabook’
# append a computer to teh collection
$computers += $NewComputer
# this outputs each of the computers
$computers
# or you can format the data in a table
$computers | Format-Table -AutoSize
# or in a list
$computers | Format-List *
# or as json
$computers | ConvertTo-Json
Assuming you haven't re-bound any names, you can do the following:
Get-Variable | Select-Object -ExpandProperty Value |
Where-Object { $_.GetType().Name -eq 'Computer' }

Creating a dynamic hashtable in Powershell

I want to create an overview of the local computer in Powershell and output it in JSON via a hash table. Now this can have several hard disks and it must be created dynamically in the hash table.
My Code:
$name = (Get-WmiObject -Class Win32_ComputerSystem -Property
Name).Name #{foreach ($Disk in $Disk) { $stats.Add("$platten", $Disk[0].VolumeName) }
stats = #{ $name= #{
CPUusage = $CPU
RAMusage = $ram
disknames = $disknames[1]
SSDsum = $ssdsum
HDDsum = $hddsum
Disksum = $disksum
}
$disk1 = #{
}
$disk2 = #{
}
$disk3 = #{
}
}}
Now I ask the hard drives and saves them in an Hash table. Then the foreach loop should go through each disk and enter the data into the other hash table.
And here comes the Error, i try to put it into the Hashtable and it did not works..
Your question is very unclear end incomplete. However, I think this might help you on your way:
$ComuterSystem = Get-CimInstance -ClassName Win32_ComputerSystem
$Result = foreach ($Computer in $ComuterSystem) {
$LogicalDisk = Get-CimInstance -ClassName win32_logicaldisk -ComputerName $ComuterSystem.Name
# Create a new hashtable for each computer
$diskHash = #{}
# Foreach disk of that computer add it to the hashtable
foreach ($disk in ($LogicalDisk.Where({$_.DeviceID}))) {
$diskHash.Add($disk.DeviceID, $disk.Size)
}
[PSCustomObject]#{
Name = $Computer.Name
Model = $Computer.Model
Manufacturer = $Computer.Manufacturer
# Easiest is to simply store all data:
LogicalDisk = $LogicalDisk
# Or store the hashtable with your key value pair
Disks = $diskHash
# Or store a selection of what you need
Selection = $LogicalDisk | Select-Object DeviceID, VolumeName, Size, FreeSpace
}
}
$Result
$Result.Disks
$Result.LogicalDisk
$Result.Selection
if you create a hashtable it is typically of fixed size
initialize the variable $disknames like:
$disknames = New-Object System.Collections.ArrayList
then you can add entries like you tried to

PowerCLI- get VM cd-mount in 1 table

I try to get all the vm which have a mounted cd in one table, but the output I get is a line-by line like:
vm1
\vm1_isopath\
vm2
\vm2_isopath\
can it possible to get all the information in 1 table with 2 columns, of VM name, and ISOPath ?
my code is:
$VMs=Get-VM
ForEach ( $vm in $VMs)
{
$VMmount=Get-CDDrive -VM $vm
if ($VMmount.IsoPath)
{
$vm | select Name
$VMmount.IsoPath
}
}
thank you.
I extended your code to:
$VMs=Get-VM
$vmInfos = #()
ForEach ( $vm in $VMs)
{
$VMmount=Get-CDDrive -VM $vm
if ($VMmount.IsoPath)
{
# Store needed info in hashtable
$info = #{}
$info.name = ($vm | select -ExpandProperty Name)
$info.IsoPath = $VMmount.IsoPath
# Convert hashtable to custom object
$object = New-Object –TypeName PSObject –Prop $info
# Add to array
$vmInfos += $object
}
}
# Dump collected infos
$vmInfos | format-table -autosize
Hope that helps