condition in powershell - disk usage - powershell

I made a powershell script that displays the disk size and free space in GB and percent.
How to make a condition here, so that after reaching 51%, a message is displayed that there is not enough space?
$props = #(
'DriveLetter'
#{
Name = 'SizeRemaining'
Expression = { "{0:N2} GB" -f ($_.SizeRemaining/ 1Gb) }
}
#{
Name = 'Size'
Expression = { "{0:N2} GB" -f ($_.Size / 1Gb) }
}
#{
Name = '% Free'
Expression = { "{0:P}" -f ($_.SizeRemaining / $_.Size) }
}
)
Get-Volume -DriveLetter C | Select-Object $props
if ( $_.SizeRemaining -lt 50 )
{
Write-Host "warning"
}
else {
Write-Host ("ok")
}

You could just add another property to your output with the warning message in it. For example, add a Status property like this:
$props = #(
'DriveLetter',
#{
Name = 'SizeRemaining'
Expression = { "{0:N2} GB" -f ($_.SizeRemaining/ 1Gb) }
},
#{
Name = 'Size'
Expression = { "{0:N2} GB" -f ($_.Size / 1Gb) }
},
#{
Name = '% Free'
Expression = { "{0:P}" -f ($_.SizeRemaining / $_.Size) }
},
#{
Name = 'Status'
Expression = { if(($_.SizeRemaining / $_.Size) -lt 0.5){"Low"}else{"OK"} }
}
)
So, you can just do this to see all the information at once:
Get-Volume -DriveLetter C | Select-Object $props | Format-Table -AutoSize
Which gives output like this:
DriveLetter SizeRemaining Size % Free Status
----------- ------------- ---- ------ ------
C 121.33 GB 464.14 GB 26.14% Warning
As it's just another property, you can access/manipulate it like any other property. For example, if you had lots of volumes on the system, you could just show the one with low space like this:
Get-Volume |
Select-Object $props |
Where-Object Status -eq 'Warning' |
Format-Table -AutoSize

Related

PowerShell Invoke Command, Script not returning some values from remote PC's

I'm new to scripting so please excuse me if my script is messy. This script pretty much does what I want it to do but for 2 fields it doesn't return the values.
If I run the commands without Invoke I get all the values I want but when I run this with the Invoke command on remote computers the OsHotFixes and CsProcessors return weird values of "Microsoft.PowerShell.Commands.HotFix" for each hotfix and "Microsoft.PowerShell.Commands.Processor" for the CsProcessors value. All other properties gave me the values I am looking for. I'm not sure why those 2 aren't returning correct values. If someone could point me in the right direction that would be awesome.
$c = Get-Content "myfilepath"
$e = "myfilepath"
$ScriptBlock = {
$ComputerInfo = Get-ComputerInfo -Property WindowsVersion, OsBuildNumber, OsHotFixes, CsModel, BiosSMBIOSBIOSVersion, WindowsProductName, CsProcessor, OsInstallDate, OsArchitecture, CsProcessors
$GPU = Get-WmiObject win32_VideoController | Select-Object "Name", "DeviceID", "DriverVersion"
$RAM = Get-CimInstance -ClassName CIM_PhysicalMemory | Select-Object "Manufacturer", "PartNumber", #{'Name'='Capacity (GB)'; 'Expression'={[math]::Truncate($_.capacity / 1GB)}}, "Speed"
$Storage = Get-WmiObject Win32_LogicalDisk | Where caption -eq "C:" | Foreach-object {write " $($_.caption) $('{0:N2}' -f ($_.Size/1gb)) GB total, $('{0:N2}' -f ($_.FreeSpace/1gb)) GB Free"}
$MyArray = #($ComputerInfo, $GPU, $RAM, $Storage)
$Properties =
#(
'WindowsVersion'
'OsBuildNumber'
'OsHotFixes'
'CsModel'
'BiosSMBIOSBIOSVersion'
'WindowsProductName'
'OsInstallDate'
'OsArchitecture'
'CsProcessors'
'Name'
'DeviceID'
'DriverVersion'
'Manufacturer'
'PartNumber'
'Capacity'
'Speed'
'Disk'
)
$MyArray | ForEach-Object {
:Inner ForEach( $Property in $Properties )
{
If($_.$Property)
{
[PSCustomObject][Ordered]#{
hostname = $env:COMPUTERNAME
WindowsVersion = $_.WindowsVersion
Build = $_.OsBuildNumber
Patches = $_.OsHotFixes
Motherboard = $_.CsModel
BiosVersion = $_.BiosSMBIOSBIOSVersion
WindowsProductName = $_.WindowsProductName
OsInstallDate = $_.OsInstallDate
OsArchitecture = $_.OsArchitecture
Processor = $_.CsProcessors
GPUName = $_.Name
DeviceID = $_.DeviceID
DriverVersion = $_.DriverVersion
RamManufacturer = $_.Manufacturer
PartNumber = $_.PartNumber
Capacity = $_.Capacity
Speed = $_.Speed
Disk = $Storage
}
Break Inner
}
}
}
}
Invoke-Command -ComputerName $c -ScriptBlock $ScriptBlock | Sort hostname | Export-Csv -append $e -NoTypeInformation
I've tried running just the lines from 4 - 8 locally and then Outputting the Array. This will show all correct values. However when this script runs with the PSCustomObject and Invoke command I don't get CsProcessors or OsHotFixes values.

How can i format correctly the "Size" param at Get-Partition?

When i use get-partition i get something like that:
PartitionNumber Size Type
--------------- ---- ----
1 931.51 GB IFS
As you can see, the Size is correctly formatted with GB behind.
When I use:
get-partition | select Size
Size
----
104857600
i get the size in the wrong format.
is it possible to get it like the upper one without math functions?
If you just want to print the sizes expressed in GB with two decimal places, with suffix GB, do the following:
Get-Partition | ForEach-Object { '{0:N2} GB' -f ($_.Size / 1gb) }
If you also want a column header, use Format-Table with a calculated property as follows:
Format-Table #{ Name='Size'; Expression={ '{0:N2} GB' -f ($_.Size / 1gb) } }. You could use the same technique with Select-Object, but since for-display formatting is the goal here, that isn't necessary.
If you want to auto-scale the sizes (using simulated input objects to demonstrate, via PowerShell's number-literal suffixes such as kb, which are multiples of 1024):
#{ Size = 42 },
#{ Size = 42.1kb },
#{ Size = 42.2mb },
#{ Size = 42.3gb },
#{ Size = 42.4tb },
#{ Size = 42.4pb } |
ForEach-Object {
$decimalPlaces = 2
$scaledSize = switch ($_.Size) {
{ $_ -ge 1pb } { $_ / 1pb; $suffix='PB'; break }
{ $_ -ge 1tb } { $_ / 1tb; $suffix='TB'; break }
{ $_ -ge 1gb } { $_ / 1gb; $suffix='GB'; break }
{ $_ -ge 1mb } { $_ / 1mb; $suffix='MB'; break }
{ $_ -ge 1kb } { $_ / 1kb; $suffix='KB'; break }
default { $_; $suffix='B'; $decimalPlaces = 0 }
}
"{0:N${decimalPlaces}} $suffix" -f $scaledSize
}
Output:
42 B
42.10 KB
42.20 MB
42.30 GB
42.40 TB
42.40 PB
Note: The auto-scaling code above is essentially a more readable formulation of what PowerShell does in its default for-display formatting for the output objects emitted by Get-Partition, via their Microsoft.Management.Infrastructure.CimInstance#MSFT_Partition ETS type name; specifically, the code used is:
$size = $_.Size;
$postfixes = #( "B", "KB", "MB", "GB", "TB", "PB" )
for ($i=0; $size -ge 1024 -and $i -lt $postfixes.Length; $i++) { $size = $size / 1024; }
return "" + [System.Math]::Round($size,2) + " " + $postfixes[$i];
Is using math to round the number ok?
Get-Partition | Select DriveLetter, PartitionNumber, #{Label = "Size"; Expression = { [math]::round($_.Size / 1gb, 2) } }

Powershell script very slow when running with very big array of data

ive been dabbling with powershell for a while now and ive been trying to modify some data in an array.
Problem is that my source array is very large and this script takes hours to run. Maybe someone can help my optimize my script.
With a small source array the script runs just fine btw.
$array_metric_hour = #()
$array_metric_hour =
foreach ($resource in $resources) {
Write-Progress -Id 0 "Step $resource"
foreach ($hour in $Time_Array) {
Write-Progress -Id 1 -ParentId 0 "Step $resource - Substep" ($hour.timestamp+":00")
[pscustomobject] #{
resourceID = $resource
resourceName = $array_bill.resources.($resource).name
time = $hour.timestamp+":00"
Poweredon = ((($Array_combined | Where-Object {$_.resourceID -eq $resource -and $_.hour -eq $hour.timestamp}).poweredon | Measure-Object -Maximum).Maximum)
#Cpu_On = if (($Array_combined | Where-Object {$_.resourceID -eq $resource -and $_.hour -eq $hour.timestamp -and $_.poweredOn -eq "0,0"}).poweredon) {0} else {(($Array_combined | Where-Object {$_.resourceID -eq $resource -and $_.hour -eq $hour.timestamp -and $_.poweredOn -ne "0,0"}).provisionedCpu | Measure-Object -Maximum).Maximum}
Mem_GB_On = if (($Array_combined | Where-Object {$_.resourceID -eq $resource -and $_.hour -eq $hour.timestamp -and $_.poweredOn -eq "0,0"}).poweredon) {0} else {(($Array_combined | Where-Object {$_.resourceID -eq $resource -and $_.hour -eq $hour.timestamp -and $_.poweredOn -ne "0,0"}).provisionedMem_GB | Measure-Object -Maximum).Maximum}
hardware_Diskspace_GB = ((($Array_combined | Where-Object {$_.resourceID -eq $resource -and $_.hour -eq $hour.timestamp}).hardware_Diskspace_GB | Measure-Object -Maximum).Maximum)
#used_Diskspace_GB = ((($Array_combined | Where-Object {$_.resourceID -eq $resource -and $_.hour -eq $hour.timestamp}).used_Diskspace_GB | Measure-Object -Maximum).Maximum)
}
}
}
Some extra information that is required:
$Time_array has every full hour in a month, so 745 values in this case.
$array_combined exist of 98131 lines (5 minute interval with metrics during a month.
this array has the folowing items per interval.
resourceID
resourceName
timestamps
human_timestamp
hour
date
poweredOn
provisionedMem_GB
hardware_Diskspace_GB
used_Diskspace_GB
thanks for all the comments, next time ill try to supply all and correct information.
The suggestions of creating an extra filter was the winner for me. the scripts is 50 times faster in the current state and for now quick enough.
Added $filter1 and $filter2.
$array_metric_hour = #()
$array_metric_hour =
foreach ($resource in $resources) {
$filter1 = $Array_combined | Where-Object {$_.resourceID -eq $resource}
Write-Progress -Id 0 "Step $resource"
foreach ($hour in $Time_Array) {
$filter2 = $filter1 | Where-Object {$_.hour -eq $hour.timestamp}
Write-Progress -Id 1 -ParentId 0 "Step $resource - Substep" ($hour.timestamp+":00")
[pscustomobject] #{
resourceID = $resource
resourceName = $array_bill.resources.($resource).name
time = $hour.timestamp+":00"
Poweredon = if ($filter2 | where poweredOn -eq "0,0") {"0"} else {($filter2.poweredOn | Measure-Object -Maximum).Maximum}
Mem_GB_On = if ($filter2 | where poweredOn -eq "0,0") {0} else {(($filter2).provisionedMem_GB | Measure-Object -Average).Average}
hardware_Diskspace_GB = ((($filter2).hardware_Diskspace_GB | Measure-Object -Average).Average)
}
}
}
Since you're still not have provided much more of your code (e.g. where does "Array_combined" come from?), here are some important notes:
Don't use "Write-Progress" on every iteration! It has a very very huge impact on performace when using PS <=5.1, 6 and 7.
With the current "7.1" build I am using it works like a charm ("7.1.0-preview.7"). Have to look when they fixed it.
Avoid pipe'ing as much as you can when you want to have the best performance. Streaming data from one command to the other is compared to statements like "foreach {}" (NOT "Foreach-Object"!) really bad.
Here is an example for your template, even when there are some important steps missing:
# Progress bar definition
$progressActivity1 = 'Processing items'
$progressCounter1 = -1
$progressMax1 = #($resources).Count
$progressInterval1 = [math]::Ceiling($progressMax1 * 0.1) # each 10%
$progressId1 = 1
$progressParentId1 = 0
# *** use a list if your script adds objects several times.
# *** Note: "Arrays" are immutable and will be re-created each time you add something
$array_metric_hour = [System.Collections.Generic.List[psobject]]::new()
# *** good approach to add the result of a forEach-statement directly to variable. Performance is similar compared to adding objects to a list.
$array_metric_hour = foreach ($resource in $resources) {
# Progress bar counter & drawing (each 10%)
$progressCounter1++
If ($progressCounter1 % $progressInterval1 -eq 0) {
Write-Progress -Activity $progressActivity1 -PercentComplete($progressCounter1 / $progressMax1 * 100) -Id $progressId1 -ParentId $progressParentId1
}
# *** "Array_combined" is unknwon.... but according to the usage:
# !!! try to create a dictionary/hashtable of "Array_combined" with "resourceID" as key.
# !!! hash tables/dictionaries are much faster to access a particular item than arrays
# !!! access would be: $filter1 = $Array_combined[$resource]
$filter1 = $Array_combined | Where-Object { $_.resourceID -eq $resource }
# Progress bar definition
$progressActivity2 = 'Processing items'
$progressCounter2 = -1
$progressMax2 = #($Time_Array).Count
$progressInterval2 = [math]::Ceiling($progressMax2 * 0.1) # each 10%
$progressId2 = 2
$progressParentId2 = $progressId1
foreach ($hour in $Time_Array) {
# ??? don't know what $filter1 is about ...
# !!! replace that; use a hastable/dictionary
# !!! alternatively: use "foreach"-statement OR method ".where{}" which was introduced in PS 4.0
$filter2 = $filter1 | Where-Object { $_.hour -eq $hour.timestamp }
# Progress bar counter & drawing (each 10%)
$progressCounter2++
If ($progressCounter2 % $progressInterval2 -eq 0) {
Write-Progress -Activity $progressActivity2 -PercentComplete($progressCounter2 / $progressMax2 * 100) -Id $progressId2 -ParentId $progressParentId2
}
[pscustomobject] #{
resourceID = $resource
resourceName = $array_bill.resources.($resource).name
time = $hour.timestamp + ':00'
# ??? "Where-Object" could be replaced ... but don't know the background or data ....
# !!! replace "Measure-Object" with "[Linq.Enumerable]" methods if possible
Poweredon = if ($filter2 | Where-Object poweredOn -EQ '0,0') { '0' } else { ($filter2.poweredOn | Measure-Object -Maximum).Maximum }
# !!! same as above
Mem_GB_On = if ($filter2 | Where-Object poweredOn -EQ '0,0') { 0 } else { (($filter2).provisionedMem_GB | Measure-Object -Average).Average }
# !!! same as above
hardware_Diskspace_GB = ((($filter2).hardware_Diskspace_GB | Measure-Object -Average).Average)
}
}
}
# Progress completed
Write-Progress -Activity $progressActivity -Completed -Id $progressId1
Write-Progress -Activity $progressActivity -Completed -Id $progressId2

Powershell if within foreach

I'm trying to retrieve some information about my hard drives using PowerShell (very new to PS btw).
When calculating the occupancy of the drives some drives are not mounted and will return a size of 0. So I need to introduce a condition where a drive with size 0 must not calculate occupancy.
$fmt = "{0,-5} | {1,-18} | {2,12:N2} | {3,21:N2} | {4,17:p} | {5,-15}"
$docc = { if ( $_.size -ne "0" ) { (($_.size-$_.freespace)/$_.size)} else {"0"} }
Get-WmiObject Win32_logicaldisk | foreach -begin {
$fmt -f "Unit","File System","Capacity(GO)", `
"Available Space(GO)","Occupancy","Observation" } {
$fmt -f $_.deviceID, $_.FileSystem, ($_.size/1GB), `
($_.freespace/1GB), $docc, ""}
The preceding code will only return the if as a string not interpret it (even without using a variable).
You need to execute the scriptblock contained in $docc - I use the call (&) operator below. The scriptblock doesn't have access to $_ either, so I pass it as a parameter.
$fmt = "{0,-5} | {1,-18} | {2,12:N2} | {3,21:N2} | {4,17:p} | {5,-15}"
$docc = { param($item); if ( $item.size -gt 0 ) {
(($item.size-$item.freespace)/$item.size)} else {"0"} }
Get-WmiObject Win32_logicaldisk | foreach -begin {
$fmt -f "Unit","File System","Capacity(GO)", `
"Available Space(GO)","Occupancy","Observation" } {
$fmt -f $_.deviceID, $_.FileSystem, ($_.size/1GB), `
($_.freespace/1GB), (& $docc $_), ""}
Hope this helps.

How to sum multiple items in an object in PowerShell?

I have:
$report.gettype().name
Object[]
echo $report
Item Average
-- -------
orange 0.294117647058824
orange -0.901960784313726
orange -0.901960784313726
grape 9.91335740072202
grape 0
pear 3.48736462093863
pear -0.0324909747292419
pear -0.0324909747292419
apple 12.1261261261261
apple -0.0045045045045045
I want to create a variable, $total, (such as a hash table) which contains the sum of the 'Average' column for each item, for example,
echo $total
orange -1.5097
grape 9.913
pear 3.423
apple 12.116
Right now I'm thinking of looping through the $report, but it's hell ugly, and I am looking for something more elegant than the following starting point (incomplete):
$tmpPrev = ""
foreach($r in $report){
$tmp = $r.item
$subtotal = 0
if($tmp <> $tmpPrev){
$subtotal += $r.average
}
How could I do this?
Cmdlets Group-Object and Measure-Object help to solve the task in a PowerShell-ish way:
Code:
# Demo input
$report = #(
New-Object psobject -Property #{ Item = 'orange'; Average = 1 }
New-Object psobject -Property #{ Item = 'orange'; Average = 2 }
New-Object psobject -Property #{ Item = 'grape'; Average = 3 }
New-Object psobject -Property #{ Item = 'grape'; Average = 4 }
)
# Process: group by 'Item' then sum 'Average' for each group
# and create output objects on the fly
$report | Group-Object Item | %{
New-Object psobject -Property #{
Item = $_.Name
Sum = ($_.Group | Measure-Object Average -Sum).Sum
}
}
Output:
Sum Item
--- ----
3 orange
7 grape
I've got a more command-line solution.
Given $report
$groupreport = $report | Group-Object -Property item -AsHashTable
is
Name Value
---- -----
grape {#{Item=grape; Average=9.91335740072202}, #{Item=grape; Average=0}}
orange {#{Item=orange; Average=0.294117647058824}, #{Item=orange; Average=-0.901960784313726...
apple {#{Item=apple; Average=12.1261261261261}, #{Item=apple; Average=-0.0045045045045045}}
pear {#{Item=pear; Average=3.48736462093863}, #{Item=pear; Average=-0.0324909747292419}, #...
then
$tab=#{}
$groupreport.keys | % {$tab += #{$_ = ($groupreport[$_] | measure-object -Property average -sum)}}
gives
PS> $tab["grape"]
Count : 2
Average :
Sum : 9,91335740072202
Maximum :
Minimum :
Property : Average
PS> $tab["grape"].sum
9,91335740072202
It seems short and usable.
Summary
$groupreport = $report | Group-Object -Property item -AsHashTable
$tab = #{}
$groupreport.keys | % {$tab += #{$_ = ($groupreport[$_] | measure-object -Property average -sum)}}
$tab.keys | % {write-host $_ `t $tab[$_].sum}
I don't know if you can get rid of looping. What about:
$report | % {$averages = #{}} {
if ($averages[$_.item]) {
$averages[$_.item] += $_.average
}
else {
$averages[$_.item] = $_.average
}
} {$averages}