How to return a single integer from this WMIObject in PowerShell (Temperature) - powershell

$temps = Get-CIMInstance MSAcpi_ThermalZoneTemperature -Namespace "root/wmi"
$temps | Select-Object -Property InstanceName,#{n="TempF";e={(($_.currenttemperature /10 -273.15) *1.8 +32)}}
I cannot seem to select-object -ExpandProperty like usual to get just the TempF result:
InstanceName TempF
------------ -----
ACPI\ThermalZone\THM__0 77.09
Just want the 77.09
Bonus points if can display in Celsius :)

If using powershell 3 or above you can call the property by using the member name of the object.
Example:
$temps.TempF Would return your result of 77.09
$tempt = Get-CimInstance MSAcpi_ThermalZoneTemperature -Namespace "root/wmi"
$temps = ($tempt | Select-Object -Property InstanceName,#{n="TempF";e={(($_.currenttemperature /10 -273.15) *1.8 +32)}},#{n="TempC";e={($_.currenttemperature /10 -273.15)}})
I have also added Celcius to the object/variable $temps.
You can get either Fahrenheit $temps.tempF or Celsius $temps.tempC

$tempt = Get-CimInstance MSAcpi_ThermalZoneTemperature -Namespace "root/wmi"
$tempF = ($tempt | Select-Object -Property InstanceName,#{n="TempF";e={(($_.currenttemperature /10 -273.15) *1.8 +32)}})|Select-Object -ExpandProperty TempF
Had an epiphany and figured it out.

Related

How to exclude part of an output in Powershell?

i am writing a script that accepts the device ID as an argument to check the used percentage of a disk. Here is my code.
$device_id = $args[0]
Get-WmiObject -Class Win32_LogicalDisk |
Select-Object -Property DeviceID,
#{label='UsedPercentage'; expression={[Math]::Round((($_.Size - $_.FreeSpace)/$_.Size) * 100, 2)}} |
findstr $device_id
Here is my output. i am passing an argument to see usage of the device by device ID.
PS D:\Development\Powershell> .\disk-usage.ps1 D:
D: 57.69
What i want to do is to just output that number. How do i do this?
There's no need to use findstr to filter the output. Instead, use the parameter argument to filter your WMI query:
$device_id = $args[0]
# use argument to filter WMI query
Get-WmiObject -Class Win32_LogicalDisk -Filter "DeviceID = '$device_id'" |ForEach-Object {
# output the free space calculation, nothing else
[Math]::Round((($_.Size - $_.FreeSpace)/$_.Size) * 100, 2)
}
You can add the 'used percentage' as a property to the WMI object you get back from your query:
$deviceID = args[0]
$diskUsage = Get-WmiObject -Query "SELECT FreeSpace, Size FROM Win32_LogicalDisk WHERE DeviceID = '$deviceID'" |
Add-Member -MemberType ScriptProperty -Name 'UsedPercentage' -Value {[Math]::Round((($this.Size - $this.FreeSpace)/$this.Size) * 100, 2)} -PassThru
Now, $diskUsage is a WMI object with Size, FreeSpace and UsedPercentage properties (as well as some WMI metadata properties you can ignore). You can output the value of any of them by refering to the one you want:
$diskUsage.UsedPercentage
15.3
Or show them in a neat table:
$diskUsage | Format-Table Size, FreeSpace, UsedPercentage -AutoSize
Size FreeSpace UsedPercentage
---- --------- --------------
1013310287872 858247196672 15.3

Add values to PS object from other PS command

I created the following object:
$PSOhash = #{
ConnectedNode = $ConnectedNode
ConnectedNodeDeviceNumber = $ConnectedNodeDeviceNumber
Serialnumber = $Serialnumber
ProductId = $ProductId
}
$ClusterNodeSSDs = New-Object PSObject -Property $PSOhash
and want to add values from the following command into it:
$SSDModel = "xyz123"
$ClusterNode = "Node1"
gwmi -Namespace root\wmi ClusPortDeviceInformation| select ConnectedNode,ConnectedNodeDeviceNumber, Serialnumber, ProductId | sort ConnectedNodeDeviceNumber | where {($_.ConnectedNode -eq $ClusterNode) -and ($_.ProductId -match "$SSDModel")}
which returns the proper informations, but need them as properties in the object for further processing.
If you want to add a set of property-value pairs to an already existing PSObject ($MyObject in this example) that currently does not have those properties, you can use the Add-Member command for this:
$PSOhash = #{
ConnectedNode = $ConnectedNode
ConnectedNodeDeviceNumber = $ConnectedNodeDeviceNumber
Serialnumber = $Serialnumber
ProductId = $ProductId
}
$MyObject = $MyObject | Add-Member -NotePropertyMembers $PSOHash
Explanation:
The -NotePropertyMembers parameter allows you do add a hash table of property-value pairs to a custom object.
Optionally, you can use a combination of Add-Member's -NotePropertyValue and -NotePropertyName to add properties one at a time.
If you want to update one object's property values with property values (same property names) from another object, you can just use direct assignment and the member access operator (.).
$SSDModel = "xyz123"
$ClusterNode = "Node1"
$WmiObjects = Get-WmiObject -Namespace root\wmi ClusPortDeviceInformation |
Select-Object ConnectedNode,ConnectedNodeDeviceNumber, Serialnumber, ProductId |
Sort-Object ConnectedNodeDeviceNumber |
where {($_.ConnectedNode -eq $ClusterNode) -and ($_.ProductId -match "$SSDModel")}
$ClusterNodeSSDs = foreach ($WmiObject in $WmiObjects) {
$PSOhash = #{
ConnectedNode = $WmiObject.ConnectedNode
ConnectedNodeDeviceNumber = $WmiObject.ConnectedNodeDeviceNumber
Serialnumber = $WmiObject.Serialnumber
ProductId = $WmiObject.ProductId
}
[pscustomobject]$PSOhash
}
Explanation:
Note the use of the foreach loop here because the Get-WmiObject will likely return a collection. So you will need to iterate all of them to create custom objects. However, it just seems that you can just use the Get-WmiObject | Select-Object output to perform the same thing.

PowerShell property expression increases execution time by 4-5 times

Scroll down for TL;DR
I need to get the following properties for every process as quickly as possible, ideally 5 seconds, maximum 10 seconds: ID, Name, Description, Path, Company, Username, Session ID, StartTime, Memory, CPU (percentage, not time)
To get this data, I put together the following snippet which (I think) is functionally perfect:
$ProcessCPU = Get-WmiObject Win32_PerfFormattedData_PerfProc_Process | Select-Object IDProcess, PercentProcessorTime
$Processes = Get-Process -IncludeUserName |
Select-Object `
#{Name='Id';Expression={[int]$_.Id}},
#{Name='Name';Expression={[string]$_.Name}},
#{Name='Description';Expression={[string]$_.Description}},
#{Name='Path';Expression={[string]$_.Path}},
#{Name='Company';Expression={[string]$_.Company}},
#{Name='Username';Expression={[string]$_.UserName}},
#{Name='SessionId';Expression={[string]$_.SessionId}},
#{Name='StartTime';Expression={[string](($_.StartTime).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"))}},
#{Name='MemoryMB';Expression={[int]([math]::Round($_.WorkingSet/1MB,2))}},
#{Name='CPUPercent';Expression={
[int]($ProcessCPU | ?{'IDProcess' -eq $_.Id}).PercentProcessorTime
}}
The issue is that its taking 18-22 seconds to execute, caused by this line (which adds about 16 seconds):
#{Name='CPUPercent';Expression={
[int]($ProcessCPU | ?{'IDProcess' -eq $_.Id}).PercentProcessorTime
}}
PS C:\Windows\system32> Measure-Command -Expression {
$ProcessCPU = Get-WmiObject Win32_PerfFormattedData_PerfProc_Process | Select-Object IDProcess, PercentProcessorTime
$Processes = Get-Process -IncludeUserName |
Select-Object `
#{Name='Id';Expression={[int]$_.Id}},
#{Name='Name';Expression={[string]$_.Name}},
#{Name='Description';Expression={[string]$_.Description}},
#{Name='Path';Expression={[string]$_.Path}},
#{Name='Company';Expression={[string]$_.Company}},
#{Name='Username';Expression={[string]$_.UserName}},
#{Name='SessionId';Expression={[string]$_.SessionId}},
#{Name='StartTime';Expression={[string](($_.StartTime).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"))}},
#{Name='MemoryMB';Expression={[int]([math]::Round($_.WorkingSet/1MB,2))}},
#{Name='CPUPercent';Expression={
[int]($ProcessCPU | ?{'IDProcess' -eq $_.Id}).PercentProcessorTime
}}
}
TotalSeconds : 19.061206
When I remove the slow property expression noted above and keep the WMI query, execution takes about 4.5 seconds:
Measure-Command -Expression {
$ProcessCPU = Get-WmiObject Win32_PerfFormattedData_PerfProc_Process | Select-Object IDProcess, PercentProcessorTime
$Processes = Get-Process -IncludeUserName |
Select-Object `
#{Name='Id';Expression={[int]$_.Id}},
#{Name='Name';Expression={[string]$_.Name}},
#{Name='Description';Expression={[string]$_.Description}},
#{Name='Path';Expression={[string]$_.Path}},
#{Name='Company';Expression={[string]$_.Company}},
#{Name='Username';Expression={[string]$_.UserName}},
#{Name='SessionId';Expression={[string]$_.SessionId}},
#{Name='StartTime';Expression={[string](($_.StartTime).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"))}},
#{Name='MemoryMB';Expression={[int]([math]::Round($_.WorkingSet/1MB,2))}}
}
TotalSeconds : 4.5202906
I thought that by getting all of the required data in a single query and referring back to the $ProcessCPU array would be fast - but I appreciate I'm iterating through each of the 250 arrays stored in $Processes.
TL;DR:
Is there a more performant method of joining two objects on a common property rather than using iteration as I have above? I.E. $ProcessCPU.IDProcess on $Processes.Id?
I tried the following block to test $Output = $ProcessCPU + $Processes | Group-Object -Property Id, it executed in just 3 seconds, but the output wasn't acceptable:
PS C:\Windows\system32> Measure-Command -Expression {
$ProcessCPU = Get-WmiObject Win32_PerfFormattedData_PerfProc_Process | Select-Object #{Name='Id';Expression={[int]$_.IDProcess}}, PercentProcessorTime
$Processes = Get-Process -IncludeUserName |
Select-Object `
#{Name='Id';Expression={[int]$_.Id}},
#{Name='Name';Expression={[string]$_.Name}},
#{Name='Description';Expression={[string]$_.Description}},
#{Name='Path';Expression={[string]$_.Path}},
#{Name='Company';Expression={[string]$_.Company}},
#{Name='Username';Expression={[string]$_.UserName}},
#{Name='SessionId';Expression={[string]$_.SessionId}},
#{Name='StartTime';Expression={[string](($_.StartTime).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"))}},
#{Name='MemoryMB';Expression={[int]([math]::Round($_.WorkingSet/1MB,2))}}
$Output = $ProcessCPU + $Processes | Group-Object -Property Id
}
TotalSeconds : 2.9656969
Use CIM to build up a hashtable that maps process IDs (PIDs) to their CPU percentages first.
Then make the calculated property passed to Select-Object consult that hashtable for efficient lookups:
Get-CimInstance Win32_PerfFormattedData_PerfProc_Process |
ForEach-Object -Begin { $htCpuPctg=#{} } `
-Process { $htCpuPctg[$_.IdProcess] = $_.PercentProcessorTime } #`
Get-Process -IncludeUserName |
Select-Object Id,
Name,
Description,
Path,
Company,
UserName,
SessionId,
#{Name='StartTime';Expression={[string](($_.StartTime).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"))}},
#{Name='MemoryMB';Expression={[int]([math]::Round($_.WorkingSet/1MB,2))}},
#{Name='CPUPercent';Expression={ $htCpuPctg[[uint32] $_.Id] }}
Note:
Get-CimInstance rather than Get-WimObject is used, because the CIM cmdlets superseded the WMI cmdlets in PowerShell v3 (released in September 2012). Therefore, the WMI cmdlets should be avoided, not least because PowerShell Core, where all future effort will go, doesn't even have them anymore. For more information, see this answer.
There is usually no need to use calculated properties such as #{Name='Id';Expression={[int]$_.Id}} to simply extract a property as-is - just use the property's name - Id - as a Select-Object -Property argument (but you've since clarified that you're using calculated properties because you want explicit control over the property's data type for sending data to an IoT Gateway via JSON).
Note that CIM reports PIDs as [uint32]-typed values, whereas Get-Process uses [int] values - hence the need to cast to [uint32] in the hashtable lookup.

Full output hidden on console

I don't get full output of the following code I made.
For Example:
DriveSpace : {174, 0, 98, 171...}
Notice the ellipses (...) after 171. It is skipping the rest of the output after that. You can run the following script to see my output.
#Start of script
$cpu = gwmi -Class Win32_Processor | Select-Object NumberOfCores,NumberOfLogicalProcessors
$memory = gwmi -class win32_physicalmemory | Select-Object {[math]::truncate($_.capacity / 1GB)}
$HostDescription= gwmi -Class Win32_OperatingSystem
$fqdn = "$env:computername.$env:userdnsdomain"
$OS = (gwmi Win32_OperatingSystem)
$OSarchitecture = (gwmi Win32_OperatingSystem)
$disk = gwmi Win32_LogicalDisk | Select-Object DeviceID, volumeName, {[math]::truncate($_.size / 1GB)}
$timezone = [System.TimeZone]::CurrentTimeZone
$fire = netsh advfirewall show publicprofile | select-string state
$firematch = $fire -match "off"
$slmgrResult = cscript c:\windows\system32\slmgr.vbs /dli | Select-string "License Status"
$activation = $slmgrResult -match "Licensed"
$apps = gp HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* |Select DisplayName, DisplayVersion, Publisher, InstallDate
$network = Get-WmiObject win32_networkadapterconfiguration -filter "ipenabled = 'True'" | select-object IPAddress, DefaultIPGateway, DNSDomain, IPSubnet
$props = #{
NumberOfCores = $cpu.NumberOfCores
NumberOfLogicalProcessors = $cpu.NumberOfLogicalProcessors
Memory = $memory.{[math]::truncate($_.capacity / 1GB)}
HostDescription = $HostDescription.Description
FQDN = "$env:computername.$env:userdnsdomain"
OS = (gwmi Win32_OperatingSystem).Name
OSarchitecture = $OSarchitecture.OSArchitecture
DriveLetters = $disk.DeviceID
DriveLabels = $disk.volumeName
DriveSpace = $disk.{[math]::truncate($_.size / 1GB)}
timezone = [System.TimeZone]::CurrentTimeZone.StandardName
FirewallDisabled = $firematch
Licensed = $activation
Applications = $apps
IPAddress_Gateway_DNSDomain_subnet = $network.IPAddress, $network.DefaultIPGateway, $network.DNSDomain, $network.IPSubnet
}
New-Object PSObject -Property $props
#End of script
This is not an official answer as I think the OP needs to be clear on what output he is expecting. This is a start nonetheless
While I have not found official documentation to support this you are just seeing how PowerShell handles console output. Consider the following example which is a collection of varying sizes of arrays.
[pscustomobject]#{
data = "1","2","3","4","5"
}
Would produce the following list style output.
data : {1, 2, 3, 4...}
Notice the fifth element of the 5 property now has the ellipses. The data is still there. It has just been truncated on the console to make the output more terse and easier to read. In this case it seems folly to do so but with some objects complicated output PowerShell has to draw the line somewhere.
Prevent the ellipses
As PetSerAL pointed out you can just use the following line of code before your output.
$FormatEnumerationLimit=-1
If you look at about_Preference_Variables you will see that, by default, this is set to 4. That would support the output you are seeing. Set that value to something higher or -1 and see if it helps.
Other Potential Issues
Like in my comments I want to draw attention to the variable you created called $disk. The output is below. Note this is from my own machine and wont match yours. Still, you should get the picture
DeviceID volumeName [math]::truncate($_.size / 1GB)
-------- ---------- -------------------------------
C: 111
D: Data 499
E: Multimedia 1362
F: 0
G: CentOS 7 x86_64 3
M: Media 2794
Z: 0
Without any other information I can only assume that you want a series of free space values to display. Given that we could break those results out of the array by casting them to string. Also want to update the line that populates the variable.
$disk = gwmi Win32_LogicalDisk | Select-Object DeviceID, volumeName, #{Label="Size(GB)";Expression={[math]::truncate($_.size / 1GB)}}
Gives us the following in $disk`
DeviceID volumeName Size(GB)
-------- ---------- --------
C: 111
D: Data 499
E: Multimedia 1362
F: 0
G: CentOS 7 x86_64 3
M: Media 2794
Z: 0
Then when you build your hashtable you can cast the array to a single space delimited string like this:
DriveSpace = [string]($disk."Size(GB)")
Fairly sure there will be more questions to come from this but it is at least a start. Welcome to SO. It is always a good idea when possible to show us desired output in cases like this so we know what you are trying to achieve. Even if you think it is obvious.
Side notes
You have other properties other that $disk that might have the same issues like Applications which is a complex object. If you do have issues with those as well solving this one might get you in the right direction.
You have many calls to gwmi Win32_OperatingSystem. You should save the results of that into a variable that you can refer to whenever you need it. Right now you are losing time calling it and getting the same results. For example:
$wmiOS = gwmi Win32_OperatingSystem
This is the default formatting of Powershell at work, as provided by Out-Default. It is truncating the DriveSpace array to display in a table in your console, but the information is still there. For example, if you type:
$props.DriveSpace
... you will see the full array displayed. The default formatting behaves differently when it's handling a simple array as opposed to when it's handling a complex object like the $props one you've created.
See also:
How Powershell Outputting and Formatting REALLY works

Convert GUID string to octetBytes using PowerShell

I have a powershell script which outputs all Exchange 2003 mailboxes by size.
$computers = "vexch01","vexch02"
foreach ($computer in $computers) {
Get-Wmiobject -namespace root\MicrosoftExchangeV2 -class Exchange_Mailbox -computer $computer | sort-object -desc Size | select-object MailboxDisplayName,StoreName,#{Name="Size/Mb";Expression={[math]::round(($_.Size / 1024),2)}}, MailboxGUID | Export-Csv -notype -Path $computer.csv
}
Currently this outputs the MailboxGUID as a string type GUID (e.g. {21EC2020-3AEA-1069-A2DD-08002B30309D}). I want to look up users in AD by this, but AD stores them in octetBytes format.
I have found some powershell functions which will do the conversion but only when the curly braces are removed. The Guid.ToString method should supply this, but I can't get it to work in the above.
However, if I could figure out how to do that, the Guid.ToByteArray method might get me even closer.
Has anyone cracked this?
Update: the answers so far helped me write a function that converts the mailboxguid into the correct format for searching via LDAP. However, I now cannot get this working in the script. This is my updated script:
function ConvertGuidToLdapSearchString(
[parameter(mandatory=$true, position=0)]$Guid
)
{
$guid_object = [System.Guid]$Guid
($guid_object.ToByteArray() | foreach { '\' + $_.ToString('x2') }) -join ''
}
# Gets data through WMI from specified Exchange mailbox servers
$servers = "vexch01","vexch02"
foreach ($server in $servers) {
Get-Wmiobject -namespace root\MicrosoftExchangeV2 -class Exchange_Mailbox -computer $computer | sort-object -desc Size | select-object MailboxDisplayName,StoreName,#{Name="Size/Mb";Expression={[math]::round(($_.Size / 1024),2)}}, #{Name="LDAP Guid";Expression={ConvertGuidToLdapSearchString(MailboxGUID)}} | Export-Csv -notype -Path $server.csv
}
I'm not sure why using the function in the select-object with #{Name="LDAP Guid";Expression={ConvertGuidToLdapSearchString(MailboxGUID)}} doesn't work.
Is there another way of using this function in select-object that will give the string?
In conjunction with Andy Schneider's answer, you may find this function useful:
function Convert-GuidToLdapSearchString(
[parameter(mandatory=$true, position=0)][guid]$Guid
)
{
($Guid.ToByteArray() | foreach { '\' + $_.ToString('x2') }) -join ''
}
(I thought I had a more clever way to do this by adding a ScriptProperty to System.Guid, but I seem to have learned that you can't effectively add members to structs.)
I'm not sure I understand what you are trying to accomplish based on your comment, but I think you may have just left out a $_. Here is a somewhat contrived example that creates an object with a property that is a GUID, then uses select and Convert-GuidToLdapSearchString to convert the format. I hope it helps.
$o = New-Object PSObject -Property #{ GUID = $([Guid]::NewGuid()) }
$o
$o | select #{ Name='SearchString'; Expression={ Convert-GuidToLdapSearchString $_.GUID } }
This is not at all how I had imagined the function being used. I expected you would use it to create an LDAP search clause such as:
$searchString = Convert-GuidToLdapSearchString '{9e76c48b-e764-4f0c-8857-77659108a41e}'
$searcher = [adsisearcher]"(msExchMailboxGuid=$searchString)"
$searcher.FindAll()
Are you casting the string to a GUID ?
$guid = [System.Guid]"{21EC2020-3AEA-1069-A2DD-08002B30309D}"
$guid.ToString()
$guid.ToByteArray()