How to capture value from a PS expression? - windows

I'm trying to put together an expression that returns a bare value, without a table or name of the expression included.
For example, I have this line from another solution:
gwmi win32_logicaldisk -Filter "DeviceID = 'C:'" |
Format-Table #{n="Size";e={[math]::Round($_.Size/1GB,2)}}
This returns:
Size
----
475.33
How can I grab just the 475.33?

Use Select-Object instead of Format-Table:
$DiskC = gwmi win32_logicaldisk -Filter "DeviceID = 'C:'" | Select-Object #{n="Size";e={[math]::Round($_.Size/1GB,2)}}
$DiskC.Size

Format-* cmdlets return objects whose sole purpose is to provide formatting instructions to PowerShell's output-formatting system - see this answer. In short: only ever use Format-* cmdlets to format data for display, never for subsequent programmatic processing.
gwmi is the built-in alias for the Get-WmiObject cmdlet, which is obsolete.
The CIM cmdlets (e.g., Get-CimInstance) superseded the WMI cmdlets in PowerShell v3 (released in September 2012). Therefore, the WMI cmdlets should be avoided, not least because PowerShell [Core] v6+, where all future effort will go, doesn't even have them anymore. For more information, see this answer.
If you simply want to calculate a value derived from (each of) your input object(s), use the ForEach-Object cmdlet.
Therefore:
Get-CimInstance win32_logicaldisk -Filter "DeviceID = 'C:'" | ForEach-Object {
[math]::Round($_.Size / 1gb, 2)
}
Or, more simply, using an expression:
[math]::Round(
(Get-CimInstance win32_logicaldisk -Filter "DeviceID = 'C:'").Size / 1gb, 2
)

If you use your first line:
gwmi win32_logicaldisk -Filter "DeviceID = 'C:'" | Format-Table #{n="Size";e={[math]::Round($_.Size/1GB,2)}}
But Pipe it trough Select-object instead of format table then pipe it again to select object expanding the property (or just .size)
gwmi win32_logicaldisk -Filter "DeviceID = 'C:'" | Select #{n="Size";e={[math]::Round($_.Size/1GB,2)}} | select -ExpandProperty Size

Related

Listed highest CPU usage windows

I can get list the processes but how would I get them to show by highest usage instead of alphabetically?
Wmic path win32_performatteddata_perfproc_process get Name,PercentProcessorTime
From powershell you don't need to make direct calls to wmic, Get-CimInstance is meant to easily query all instances of WMI and CIM classes and output objects which are easy to manipulate. Sorting objects in PowerShell can be done with Sort-Object.
Get-CimInstance Win32_PerfFormattedData_PerfProc_Process |
Sort-Object PercentPrivilegedTime -Descending |
Select-Object Name, PercentProcessorTime
You could even go one step further and group the objects by their name with the help of Group-Object:
Get-CimInstance Win32_PerfFormattedData_PerfProc_Process |
Group-Object { $_.Name -replace '#\d+$' } | ForEach-Object {
[pscustomobject]#{
Instances = $_.Count
Name = $_.Name
PercentProcessorTime = [Linq.Enumerable]::Sum([int[]] $_.Group.PercentProcessorTime)
}
} | Sort-Object PercentProcessorTime -Descending

multiple parameters in wmic path win32_process get

I want to run this command wmic path Win32_Process get ParentProcessId, commandLine, creationdate, executablepath, name, processId with several parameters,but powershell swear at the syntax if I try to write comma-separated. What do I need to fix?
By default, the wmi/cim cmdlets give you all the properties of a class on the object you receive, so you do not need to specify each one:
$wmi = Get-WmiObject -Class Win32_Process
$wmi | Get-Member -MemberType Property
$props = 'ParentProcessId', 'CommandLine', 'CreationDate', 'ExecutablePath', 'Name', 'ProcessId'
$wmi | Select-Object -Property $props
As a best practice: if powershell gives you a native abstraction (in this case, Get-WmiObject or Get-CimInstance), you should use it!
Commas indicate arrays. If you really want to wmic, you can use the magical "stop parsing" operator:
wmic --% path Win32_Process get ParentProcessId, commandLine, creationdate, executablepath, name, processId
Get-wmiobject or get-ciminstance will output objects though, that are easier to manipulate. Get-ciminstance even has tab completion on the classnames, and piping to select-object or where-object, you get tab completion on the properties.
get-ciminstance win32_process | select parentprocessId, commandLine, creationdate, executablepath, name, processId
get-ciminstance win32_process | where commandline -match chrome

How to get every disk in Windows and filter on DeviceType and then add the size of the disk next to it, separated by a comma?

I'm writing an PowerShell script to collect some data of a computer. I'm almost done, but I don't know how to get size of all the disks on the computer. I know how to do it with a couple of If statements, but I want it to automatically detect the drives, not that I have to write a new If statement if a new disk is attached. The output I want is as follows: "A:,250GB". The "A:," bit works, but not the disk size bit.
This is the code I used and tweaked, but to no avail:
$Drives = Get-WmiObject Win32_logicaldisk| ?{$_.DriveType -eq 3} | ForEach-Object {$_.name}
ForEach ($Drivename in $Drives) {
$Drivenames = Get-WMIObject -Query "Select * From win32_logicaldisk Where DriveType = '3'" -computer localhost | Select-Object DeviceID
$Drive = [Math]::Round($Drivenames.Size / 1GB)
"$Drivenames,", $Drive | Out-File "C:\HDS\HDS_DRIVES.csv" -Append
}
In addition, [Math]::Round($Drivenames.Size / 1GB) throws me an error:
Method invocation failed because [System.Object[]] does not contain a method named 'op_Division'"
You can use Calculated Property with Select-Object to make it much more simple:
Get-WmiObject Win32_logicaldisk| ? {$_.DriveType -eq 3} |
Select-Object #{N="DeviceId";E={$_.DeviceId}},`
#{N="VolumeName";E={$_.VolumeName}},`
#{N="Size";E={[Math]::Round($_.Size / 1GB)}} |
Out-File "C:\HDS\HDS_DRIVES.csv" -Append
Note that you don't need to Invoke Get-WmiObject Twice like in your example.
Why it doesn't work?
The issue is that $Drivenames contains only DeviceID (as you used Select-Object to get only that property). Therefore you're getting an error where trying to round it (as rounding nothing is not supposed to work).
How to fix it?
You have to add Size and then access it using .PropertyName:
$DriveInfo = Get-WMIObject -Query "Select * From win32_logicaldisk Where DriveType = '3'" -computer localhost | Select-Object DeviceID, Size
$DriveInfo | ForEach-Object {
$DriveSize = [Math]::Round($_.Size / 1GB)
"$($_.DeviceID),$DriveSize" | Out-File "C:\HDS\HDS_DRIVES.csv" -Append
}
How can I make it more elegant
Also, take a look at #Avshalom's answer which uses calculated property.

Powershell 'Optimize-Volume' Output

I have written a system maintenance script which executes basic functions that retrieve statistics from a host, writes the output to a new PSObject, then finally combines the results and converts it all to a HTML web page.
I do not seem to be able to write the output of Optimize-Volume to the pipeline, I have to use -verbose - why is this? I would like to check the results of the Optimize-Volume cmdlet by looking for the following text which is generated at the end of the -verbose output, depending on the result:-
'It is recommended that you defragment this volume.'
'You do not need to defragment this volume.'
Here is the function:-
function Get-DefragInfo {
$getwmi = Get-WmiObject -Class Win32_volume -Filter "DriveType = 3" | Where-Object {$_.DriveLetter -cne $null} -ErrorAction SilentlyContinue
$letter = $getwmi.DriveLetter -replace ':'
foreach ($drive in $getwmi)
{
$analysis = Optimize-Volume -DriveLetter $letter -Analyze
if ($analysis -like 'It is recommended that you defragment this volume.')
{
$props =[ordered]#{‘Drive Letter’=$letter
'Defrag Recommended?'='Yes'}
}
elseif ($analysis -like 'You do not need to defragment this volume.')
{
$props =#{‘Drive Letter’=$letter
'Defrag Recommended?'='No'}
}
$obj = New-Object -TypeName PSObject -Property $props
Write-Output $obj
}
}
How do I capture the output I need?
Thanks in advance.
In PowerShell 3.0 and onward, you can use the stream redirection operator > to capture the Verbose ouput to a variable:
# Merge stream 4 (Verbose) into standard Output stream
$analysis = &{Optimize-Volume -DriveLetter $letter -Analyze -Verbose} 4>&1
# Check the "Message" property of the very last VerboseRecord in the output
if($analysis[-1].Message -like "*It is recommended*")
{
# defrag
}
else
{
# don't defrag
}
If we Get-Help Optimize-Volume -full we'll see the cmdlet has no output.
Some searching lead me to this Microsoft Scripting Guys article that pointed out using the following to check if Defrag is needed.
(gwmi -Class win32_volume -Filter "DriveLetter = 'C:'").DefragAnalysis()
Knowing this, we can easily make an IF Statement.
$DefragCheck = (gwmi -Class win32_volume -Filter "DriveLetter = 'C:'").DefragAnalysis() |
Select DefragRecommended
IF($DefragCheck){"Defrag recommended"}ELSE{"Defrag is not needed."}
It's helpful to pipe cmdlets to Get-Member in order to see if there are any options available. In the above example, we can pipe gwmi -Class win32_volume -Filter "DriveLetter = 'C:'" to Get-Member and find the DefragAnalysis method, which we use dotted notation to access (wrap the Get-WmiObject in () then use a . and the method name followed by (), it looks confusing until you try it a couple times!)
Thanks, I went for the verbose redirection option and it seems to be working well. My method is not the cleanest way of doing it I understand, but it works for me.
I like the second option also, I'm going to look at using this once the script is complete and functionality is proofed.
Thanks for your help once again.

Trying to display SMS_Client methods for SCCM using VBScript

This is the script I have in Powershell;
$wmi = get-wmiobject -Namespace root\ccm -class sms_client -list | gm
The line above works exactly how I want.
The line below gives me completely different results because I removed the -list switch:
$wmi = get-wmiobject -Namespace root\ccm -class sms_client | gm
My question is how can I transpose the first command to VBScript. I want to be able to call the "TriggerSchedule" method.
Something like this should work (schedule ID taken from the documentation):
Set wmi = GetObject("winmgmts://./root/ccm")
scheduleID = "{00000000-0000-0000-0000-000000000001}"
For Each client In wmi.ExecQuery("SELECT * FROM SMS_Client")
client.TriggerSchedule(scheduleID)
Next
Untested, though, because I don't have SCCM at hand here.
Late to the party but try this in PowerShell:
GWMI CCM_Scheduler_ScheduledMessage -namespace root\ccm\policy\machine\actualconfig |
select-object ScheduledMessageID, TargetEndPoint |
where-object {$_.TargetEndPoint -ne "direct:execmgr"}
Returns the following, pay attention to the values which are similar to 00000000-0000-0000-0000-000000000116
ScheduledMessageID TargetEndPoint
------------------ --------------
{F83F662D-3DE6-4696-B064-701B2D86DADA} direct:UpdatesDeploymentAgent
{00000000-0000-0000-0000-000000000116} direct:StateMessageManager

Resources