I'm attempting to export a list to a CSV file. The output should look something like this:
Computer Name
BIOS
Operating System
Version
Disk Number
Size (GB)
Partition Type
DEVICE1
UEFI
Microsoft Windows 10 Pro
10.0.19042.0
0
954
GPT
DEVICE1
UEFI
Microsoft Windows 10 Pro
10.0.19042.0
1
119
GPT
etc...
However, the output in the CSV file looks like this:
Count
Length
LongLength
Rank
SyncRoot
IsReadOnly
IsFixedSize
IsSynchronized
2
2
2
1
System.Object[]
FALSE
TRUE
FALSE
Relevant Code
$Result = New-Object System.Collections.Generic.List[System.Object]
#Irrelevant Code...
foreach($Device in $Devices){
#More irrelevant code...
Write-Host "`tCompiling device information... " -NoNewline
try{
$Cmd = Invoke-Command -Session $Session -ScriptBlock{
Get-Disk | Foreach-Object{
[PSCustomObject]#{
"Computer Name" = $env:COMPUTERNAME
"BIOS" = if (Test-Path HKLM:\System\CurrentControlSet\control\SecureBoot\State) {"UEFI"} else {"Legacy"}
"Operating System" = Get-WmiObject -Class Win32_OperatingSystem | Select-Object -ExpandProperty Caption
"Version" = [Environment]::OSVersion | Select-Object -ExpandProperty Version
"Disk Number" = $_.number
"Size (GB)" = [int]($_.size/1GB)
"Partition Type" = $_.PartitionStyle
}
}
}
$Cmd = $Cmd | Select * -ExcludeProperty PSComputerName, RunspaceId
$Result.Add($Cmd)
Write-Host "Success" -ForegroundColor Green
}catch{
Write-Host "Failed" -ForegroundColor Red
$Result.Add(
[PSCustomObject]#{
"Computer Name" = $Device
"BIOS" = $QueryErrorMsg
"Operating System" = $QueryErrorMsg
"Version" = $QueryErrorMsg
"Disk Number" = $QueryErrorMsg
"Size (GB)" = $QueryErrorMsg
"Partition Type" = $QueryErrorMsg
}
)
Continue
}finally{
Remove-PSSession $Session
Write-Host "`tConnection terminated."
}
$Count++
}
if($DisplayResults){
$Result | ft -a
}
if($ExportToCsv){
$Result | Export-Csv -Path ($OutputDirectory + "\DiskPartitionAudit $((Get-Date).ToString('MM-dd-yyyy hh-mm-ss tt')).csv") -NoTypeInformation
}
Any ideas how I can fix this? Thanks!
The issue is you are adding all your collected records in $cmd as a single object to $result. Instead, use AddRange(). I also changed the select-object as I was getting PSComputerName and PSShowComputerName in my csvs
$Result = New-Object System.Collections.Generic.List[System.Object]
#Irrelevant Code...
foreach($Device in $Devices){
#More irrelevant code...
Write-Host "`tCompiling device information... " -NoNewline
try{
$Cmd = Invoke-Command -Session $Session -ScriptBlock{
Get-Disk | Foreach-Object{
[PSCustomObject]#{
"Computer Name" = $env:COMPUTERNAME
"BIOS" = if (Test-Path HKLM:\System\CurrentControlSet\control\SecureBoot\State) {"UEFI"} else {"Legacy"}
"Operating System" = Get-WmiObject -Class Win32_OperatingSystem | Select-Object -ExpandProperty Caption
"Version" = [Environment]::OSVersion | Select-Object -ExpandProperty Version
"Disk Number" = $_.number
"Size (GB)" = [int]($_.size/1GB)
"Partition Type" = $_.PartitionStyle
}
}
} -HideComputerName
$Result.AddRange($Cmd)
Write-Host "Success" -ForegroundColor Green
}catch{
Write-Host "Failed" -ForegroundColor Red
$Result.Add(
[PSCustomObject]#{
"Computer Name" = $Device
"BIOS" = $QueryErrorMsg
"Operating System" = $QueryErrorMsg
"Version" = $QueryErrorMsg
"Disk Number" = $QueryErrorMsg
"Size (GB)" = $QueryErrorMsg
"Partition Type" = $QueryErrorMsg
}
)
Continue
}finally{
Remove-PSSession $Session
Write-Host "`tConnection terminated."
}
$Count++
}
if($DisplayResults){
$Result | ft -a
}
if($ExportToCsv){
$Result | Select * -ExcludeProperty RunspaceId,PSComputerName,PSShowComputerName |
Export-Csv -Path ($OutputDirectory + "\DiskPartitionAudit $((Get-Date).ToString('MM-dd-yyyy hh-mm-ss tt')).csv") -NoTypeInformation
}
You need to use "Select-Object" with the list of attributes:
$Result | Select-Object -Property "Computer Name", "BIOS", "Operating System", "Version", "Disk Number", "Size (GB)", "Partition Type" | Export-Csv -Path ($OutputDirectory + "\DiskPartitionAudit $((Get-Date).ToString('MM-dd-yyyy hh-mm-ss tt')).csv") -NoTypeInformation
Related
I ran into such a problem, I don't know how best to solve it.
We have this script, determining the load on the CPU.
$pc = 'cmd'
try {$Connection = Test-Connection -ComputerName $pc -Count 2 -EA Stop}
catch {Write-Host "NO network connection to $PC" -fo Red; break}
try
{
$option = New-CimSessionOption -Protocol Dcom
$session = New-CimSession -ComputerName $pc -SessionOption $option -EA Stop
}
catch
{
try
{
$option = New-CimSessionOption -Protocol Wsman
$session = New-CimSession -ComputerName $pc -SessionOption $option -EA Stop
}
Catch {Write-Host "NOT connect to CimSession on $PC" -fo Red; break}
}
$ComputerSystem = Get-CimInstance -ClassName Win32_ComputerSystem -CimSession $session
$CpuCores = $ComputerSystem.NumberOfLogicalProcessors
Write-Host 'Please wait for CPU Usage data to be collect...' -fo Yellow
if (Get-Counter -ListSet * -ComputerName $pc | ? Paths -Match "\\Процесс\(\*\)"){$CounterPath = "\Процесс(*)\% загруженности процессора"}
else {$CounterPath = "\Process(*)\% Processor Time"}
$CPU_Usage_Counter = (Get-Counter -Counter $CounterPath -SampleInterval 1 -MaxSamples 5 -ComputerName $pc -EA SilentlyContinue).CounterSamples | ? CookedValue -ne 0 | group InstanceName
$CPU_Usage_Data = $CPU_Usage_Counter | select Name,#{N='CPU_Usage_%';E={[math]::Round(($_.Group | %{$_.CookedValue} | measure -Average).Average/$CpuCores,2)}}
$CPU_Usage_Data | ? Name -ne '_total' | sort 'CPU_Usage_%' -Des | ft -a
Write-Host "NumberOfLogicalProcessors: $CpuCores"
$check = Write-Host "CpuUsageProcess" ( [math]::Round((100 - (($CPU_Usage_Data | ? Name -eq 'idle').'CPU_Usage_%')),2) ) -fo Yellow
#####---------------------------------------------------------------------------------------------------------------------------------------------------
$status = (($check).CpuUsageProcess -gt 5)
if($status)
{
#Break
Write-Host -ForegroundColor Green 'if!'
}
else
{
Write-Host -ForegroundColor Red 'else!'
}
After execution, you need to extract the result of the "CpuUsageProcess" value and use it in the if / else condition
As Theo points out, Write-Host never emits any output - it simply causes PowerShell to write the input directly to the screen buffer.
Calculate the value(s) separately before calling Write-Host:
$check = [math]::Round((100 - (($CPU_Usage_Data | ? Name -eq 'idle').'CPU_Usage_%')),2)
Write-Host "CpuUsageProcess $check" -fo Yellow
if($check -gt 5){
# ...
}
The below script was working fine to search and check particular patches of installation information in the list of servers (Domain Controllers).
Clear-Host
$hotFixesID = #('KB4571694','KB4056890')
$computerList = Get-ADComputer -LDAPFilter "(&(objectCategory=computer)(userAccountControl:1.2.840.113556.1.4.803:=8192))" | Where-Object { Test-Connection $_.Name -Count 1 -Quiet } | Select-Object -ExpandProperty Name
$properties = 'PSComputerName', 'HotFixID', 'Installed', 'Description', 'InstalledBy', 'InstalledOn', 'Exception'
$computerList | ForEach-Object {
Write-Host "Processing Server $($_) ..." -ForegroundColor Cyan
Foreach ($hotFixID in $hotFixesID) {
Write-Host "`tProcessing Server $($_) Hotfix ID $($hotFixID)..." -ForegroundColor Yellow
$out = $exception = $null
Try {
$out = Get-WmiObject -Query "Select * From Win32_QuickFixEngineering Where HotFixID='$($hotFixID)'" -ComputerName $_ -ErrorAction Stop | Select-Object -Property $properties
}
Catch {
$exception = $_.Exception.Message
}
If ($out) {
$out.Installed = $true
}
Else {
$out = '' | Select-Object -Property $properties
$out.PSComputerName = $_
$out.HotFixID = $hotFixID
If ($exception) {
$out.Exception = $exception
}
Else {
$out.Installed = $false
}
}
$out
}
} | OGV
How can I fix the minor issue where the Installed Hotfix result is duplicated?
Error:
The property 'Installed' cannot be found on this object. Verify that the property exists and can be set.
At line:17 char:13
+ $out.Installed = $true
+ ~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : PropertyAssignmentException
OK I found the script to be cumbersome and hard to debug. I've rewritten it with more consideration of what to output when the call doesn't fail but the KB is not installed.
$computerlist = Get-ADComputer -LDAPFilter "(&(objectCategory=computer)(userAccountControl:1.2.840.113556.1.4.803:=8192))" | Select-Object -ExpandProperty Name
$computerList | ForEach-Object {
Write-Host "Processing Server $_ ..." -ForegroundColor Cyan
Foreach ($hotFixID in $hotFixesID) {
Write-Host "`tProcessing Server $_ Hotfix ID $hotFixID..." -ForegroundColor Yellow
Try
{
$result = Get-WmiObject -Query "Select * From Win32_QuickFixEngineering Where HotFixID='$hotFixID'" -ComputerName $_ -ErrorAction Stop
$exception = "No error"
if($result)
{
$installed = $true
$description = $result.description
$installedBy = $result.installedby
$installedOn = $result.installedon
}
else
{
$installed = $false
$description = "N/A"
$installedBy = "N/A"
$installedOn = "N/A"
}
}
Catch {
$exception = $_.Exception.Message
}
[PSCustomObject]#{
PSComputerName = $_
HotFixID = $hotFixID
Installed = $installed
Description = $description
InstalledBy = $installedBy
InstalledOn = $installedon
Exception = $exception
}
}
} | Out-GridView
I have a question regarding Null values.
The script below works fine, but will return errors when nothing is in my Z (optical drive). Obviously if nothing is in the optical drive it is basically null, but I have not found a way around that as of yet. I know it's probably a simple fix and am hoping someone here can help.
Function GetDisk
{
Param(
$ComputerName = $env:COMPUTERNAME,
[Switch]$PassThru
)
# ...
}
Function Get-ColorSplat
{
# Create color Splats
$C1 = #{ForegroundColor="Green"}
$C2 = #{ForegroundColor="Yellow"}
$C3 = #{ForegroundColor="Red"}
# Create color constants in the previous scope.
New-Variable -Name "Good" -Value $C1 -Scope 1
New-Variable -Name "Problem" -Value $C2 -Scope 1
New-Variable -Name "Bad" -Value $C3 -Scope 1
} # End: Get-ColorSplat
Function Write-ColorOutput
{
Param($DiskInfo)
Write-Host""
Write-Host -NoNewLine "OS Version: "
Get-CimInstance Win32_OperatingSystem | Select-Object Caption | ForEach{ $_.Caption }
Write-Host""
# Display the headers.
Write-host "SystemName DeviceID VolumeName Size(GB) FreeSpace(GB) %FreeSpace(GB) Date"
Write-host "---------- -------- ----------- -------- ------------ -------------- ----------------"
# Display the data.
ForEach ($D in $DiskInfo)
{
$PSComputerName = $D.PSComputerName.PadRight(6)
$DeviceID = $D.DeviceID.ToString().PadRight(6).Remove(5)
$VolumeName = $D.VolumeName.ToString().PadRight(20).Remove(19)
$SizeGB = $D.SizeGB.ToString().PadRight(6).Remove(5)
$FSGB = $D.FreeSpaceGB.ToString().PadRight(6).Remove(5)
$PFS = $D.PercentFS.ToString().PadRight(7).Remove(6)
$Date = $D.Date.ToString().PadRight(20).Remove(19)
If ($D.PercentFS -ge 50)
{ Write-Host "$PSComputerName $($DeviceID) $($VolumeName)$($SizeGB) $($FSGB) $($PFS) $($Date)" #Good }
ElseIf (($D.PercentFS -lt 50) -and ($D.PercentFS -GE 40))
{ Write-Host "$PSComputerName $($DeviceID) $($VolumeName)$($SizeGB) $($FSGB) $($PFS) $($Date)" #Problem }
Else
{ Write-Host "$PSComputerName $($DeviceID) $($VolumeName)$($SizeGB) $($FSGB) $($PFS) $($Date)" #Bad }
}
}
# Get the color splats
Get-ColorSplat
$DiskInfo = Get-WMIObject win32_logicalDisk -ComputerName $env:computername | Select-Object PSComputerName, DeviceID, Size, VolumeName,
#{name="SizeGB"; expression={"{0:N2}" -f ($_.Size/1gb)}},
#{Name="FreeSpaceGB"; expression={"{0:N2}" -f ($_.FreeSpace/1gb)}},
#{Name="PercentFS"; expression={"{0:N2}%" -f(($_.FreeSpace/$_.size)*100)}},
#{name="Date"; expression={$(Get-Date -Format "g")}}
#$ErrorActionPreference= 'silentlycontinue'
If (!$PassThru) {
Write-ColorOutput -DiskInfo $DiskInfo
}
Else {
Write-Output $DiskInfo
}
}
You had to problems several values of $D were returning $null but you aren't checking their value (e.g. $D.VolumeName).
The second problem is that you are creating the global variables multiple times (e.g. New-Variable -Name "Good").
I suggest you refactor so that variable creation is in "Get-ColorSplat" if you are going to call it several times.
Edit #1 fixed padding in output table
Function GetDisk
{
Param(
$ComputerName = $env:COMPUTERNAME,
[Switch]$PassThru
)
# ...
}
Function Get-ColorSplat
{
# Create color Splats
$C1 = #{ForegroundColor="Green"}
$C2 = #{ForegroundColor="Yellow"}
$C3 = #{ForegroundColor="Red"}
# Create color constants in the previous scope.
if(Get-Variable Good -Scope Global -ErrorAction SilentlyContinue){}
else{New-Variable -Name "Good" -Value $C1 -Scope 1}
if(Get-Variable Problem -Scope Global -ErrorAction SilentlyContinue){}
else{New-Variable -Name "Problem" -Value $C2 -Scope 1}
if(Get-Variable Bad -Scope Global -ErrorAction SilentlyContinue){}
else{New-Variable -Name "Bad" -Value $C3 -Scope 1}
} # End: Get-ColorSplat
Function Write-ColorOutput
{
Param($DiskInfo)
Write-Host""
Write-Host -NoNewLine "OS Version: "
Get-CimInstance Win32_OperatingSystem | Select-Object Caption | ForEach{ $_.Caption }
Write-Host""
# Display the headers.
Write-host "SystemName DeviceID VolumeName Size(GB) FreeSpace(GB) %FreeSpace(GB) Date"
Write-host "---------- -------- ----------- -------- ------------ -------------- ----------------"
# Display the data.
ForEach ($D in $DiskInfo)
{
$PSComputerName = $D.PSComputerName.PadRight(6)
$DeviceID = $D.DeviceID.ToString().PadRight(6).Remove(5)
$Dname = $D.VolumeName
if($Dname){$VolumeName = $D.VolumeName.ToString().PadRight(20).Remove(19)}
else{$VolumeName = ' '}
$SizeGB = $D.SizeGB.ToString().PadRight(6).Remove(5)
$FSGB = $D.FreeSpaceGB.ToString().PadRight(6).Remove(5)
$PercentFS = $D.PercentFS
if($PercentFS){$PFS = $D.PercentFS.ToString().PadRight(7).Remove(6)}
else{$PFS = ' '}
$Date = $D.Date.ToString().PadRight(20).Remove(19)
If ($D.PercentFS -ge 50)
{ Write-Host "$PSComputerName $($DeviceID) $($VolumeName)$($SizeGB) $($FSGB) $($PFS) $($Date)" #Good }
ElseIf (($D.PercentFS -lt 50) -and ($D.PercentFS -GE 40))
{ Write-Host "$PSComputerName $($DeviceID) $($VolumeName)$($SizeGB) $($FSGB) $($PFS) $($Date)" #Problem }
Else
{ Write-Host "$PSComputerName $($DeviceID) $($VolumeName)$($SizeGB) $($FSGB) $($PFS) $($Date)" #Bad }
}
}
# Get the color splats
Get-ColorSplat
$DiskInfo = Get-WMIObject win32_logicalDisk -ComputerName $env:computername | Select-Object PSComputerName, DeviceID, Size, VolumeName,
#{name="SizeGB"; expression={"{0:N2}" -f ($_.Size/1gb)}},
#{Name="FreeSpaceGB"; expression={"{0:N2}" -f ($_.FreeSpace/1gb)}},
#{Name="PercentFS"; expression={"{0:N2}%" -f(($_.FreeSpace/$_.size)*100)}},
#{name="Date"; expression={$(Get-Date -Format "g")}}
#$ErrorActionPreference= 'silentlycontinue'
If (!$PassThru) {Write-ColorOutput -DiskInfo $DiskInfo}
Else {Write-Output $DiskInfo}
Got it figured out - thanks to all that commented and helped me.
Function GetDisk
{
Param(
$ComputerName = $env:COMPUTERNAME,
[Switch]$PassThru
)
Function Write-ColorOutput
{
Param($DiskInfo)
Write-Host""
Write-Host -NoNewLine "OS Version: "
Get-CimInstance Win32_OperatingSystem | Select-Object Caption | ForEach{ $_.Caption }
Get-CimInstance Win32_OperatingSystem | Select-Object CSName, InstallDate, BuildNumber, OSArchitecture, BootDevice | Format-Table -AutoSize | Out-String | Write-Host -ForegroundColor Green
Get-PhysicalDisk | Select-Object $env:computername, FriendlyName, SerialNumber, MediaType, OperationalStatus, HealthStatus | Format-Table -AutoSize | Out-String | Write-Host -ForegroundColor Green
Write-Host""
# Display the headers.
Write-host "SystemName DeviceID VolumeName Size(GB) FreeSpace(GB) %FreeSpace(GB) Date"
Write-host "---------- -------- ----------- -------- ------------ -------------- ----------------"
# Display the data.
ForEach ($D in $DiskInfo)
{
$PSComputerName = $D.PSComputerName.PadRight(6)
$DeviceID = $D.DeviceID.ToString().PadRight(6).Remove(5)
$Dname = $D.VolumeName
if($Dname){$VolumeName = $D.VolumeName.ToString().PadRight(20).Remove(19)} else{$VolumeName = ' '}
$SizeGB = $D.SizeGB.ToString().PadRight(6).Remove(5)
$FSGB = $D.FreeSpaceGB.ToString().PadRight(6).Remove(5)
$PercentFS = $D.PercentFS
if($PercentFS){$PFS = $D.PercentFS.ToString().PadRight(7).Remove(6)} else{$PFS = ' '}
$Date = $D.Date.ToString().PadRight(20).Remove(19)
If ($D.PercentFS -ge 50)
{Write-Host "$PSComputerName $($DeviceID) $($VolumeName)$($SizeGB) $($FSGB) $($PFS) $($Date)" -ForegroundColor Green}
ElseIf (($D.PercentFS -lt 50) -and ($D.PercentFS -GE 40))
{Write-Host "$PSComputerName $($DeviceID) $($VolumeName)$($SizeGB) $($FSGB) $($PFS) $($Date)" -ForegroundColor Yellow}
Else
{Write-Host "$PSComputerName $($DeviceID) $($VolumeName)$($SizeGB) $($FSGB) $($PFS) $($Date)" -ForegroundColor Red}
}
Get-PhysicalDisk | Get-StorageReliabilityCounter | Select-Object $env:computername, PowerOnHours, Temperature, Wear, ReadErrorsTotal, WriteErrorsTotal, FlushLatencyMax, WriteLatencyMax | Format-Table -AutoSize | Out-String | Write-Host -ForegroundColor Green
Disk | Select-Object | Format-Table -AutoSize | Out-String | Write-Host -ForegroundColor Green
}
$DiskInfo = Get-WMIObject win32_logicalDisk -ComputerName $env:computername | Select-Object PSComputerName, DeviceID, Size, VolumeName,
#{name="SizeGB"; expression={"{0:N2}" -f ($_.Size/1gb)}},
#{Name="FreeSpaceGB"; expression={"{0:N2}" -f ($_.FreeSpace/1gb)}},
#{Name="PercentFS"; expression={"{0:N2}%" -f(($_.FreeSpace/$_.size)*100)}},
#{name="Date"; expression={$(Get-Date -Format "g")}}
#$ErrorActionPreference= 'silentlycontinue'
If (!$PassThru) {Write-ColorOutput -DiskInfo $DiskInfo} Else {Write-Output $DiskInfo}
Write-Host""
Write-Host""
}
My PowerShell script:
Param([string]$Computers) #Must supply a comma seperated list of servers
$Threshold = 20 #Only show CPU over this number
$NoP = 20 #Number of processes to list
$NoRS = 4 #Number of result sets
If (! $Computers) {
Write-Host "Connection to server failed - please specify a server name." -ForegroundColor Red
Break
} Else {
$ComputerList = $Computers -Split " "#,[StringSplitOptions]'RemoveEmptyEntries')
}
$Credential = $host.ui.PromptForCredential("Need credentials", "Please enter your user name and password.", "", "NetBiosUserName")
If (! $Credential) {
Write-Host "Authentication failed - please verify your username and password." -ForegroundColor Red
Break
}
$UserName = $Credential.Username
$Password = $Credential.GetNetworkCredential().Password
$CurrentDomain = "LDAP://" + ([ADSI]"").distinguishedName
$Domain = New-Object System.DirectoryServices.DirectoryEntry($CurrentDomain,$UserName,$Password)
If ($Domain.Name -eq $null){
Write-Host "Authentication failed - please verify your username and password." -ForegroundColor Red
Break
}
ForEach ($ComputerName In $ComputerList) {
$LoadPercentage = $Processors.LoadPercentage
If (!$LoadPercentage) {$LoadPercentage = 0}
Write-Host "Server: $ComputerName (CPU load $LoadPercentage%)" -NoNewline
$Processors = Get-WmiObject win32_processor -ComputerName $ComputerName -Credential $Credential
$i = 1
$TopProcess = #()
$PercentComplete = 0
Do{
$PercentComplete = [Math]::Floor($i/$NoRS*100)
Write-Progress -Activity $ComputerName -Status "$PercentComplete% Complete:" -PercentComplete $PercentComplete
$ProcessList = gwmi Win32_PerfFormattedData_PerfProc_Process -ComputerName $ComputerName -Credential $Credential |
Select IDProcess,Name,PercentProcessorTime |
Where {$_.Name -ne "_Total" -and $_.Name -ne "Idle"} |
Sort PercentProcessorTime -Descending |
Select -First $NoP
ForEach ($Process In $ProcessList) {
$row = New-Object PSObject -Property #{
Id = $Process.IDProcess
Name = $Process.Name
User = (gwmi Win32_Process -ComputerName $ComputerName -Credential $Credential | Where {$_.ProcessId -eq $Process.IDProcess}).GetOwner().User
CPU = $Process.PercentProcessorTime/$Processors.NumberOfLogicalProcessors -f {P}
Description = (gwmi Win32_Process -ComputerName $ComputerName -Credential $Credential | Where {$_.ProcessId -eq $Process.IDProcess}).Description
}
$TopProcess += $row
}
$i++
} While ($i -lt $NoRS + 1)
Write-Progress -Activity $ComputerName -Completed
$Group = $TopProcess | Where {$_.CPU -gt $Threshold} | Group 'ID' | Where Count -eq $NoRS
If (!$Group) {
Write-Host " has no processes persistently above $Threshold percent CPU usage."
} Else {
$Processes = #()
ForEach ($Groupee In $Group) {
$Ungroup = $Groupee | Select -ExpandProperty Group
$CPU = 0
ForEach ($ugr in $Ungroup) {
$CPU += $ugr.CPU
}
$row = new-object PSObject -Property #{
Id = $Ungroup.Id | Select -First 1
Name = $Ungroup.Name | Select -First 1
CPU = $CPU/$NoRS
User = $Ungroup.User | Select -First 1
Description = $Ungroup.Description | Select -First 1
}
$Processes += $row
}
$Processes | Format-Table #{Expression={$_.User};Label="User Name";width=25},#{Expression={$_.CPU};Label="CPU";width=5},#{Expression={$_.Id};Label="ID";width=8},#{Expression={$_.Description};Label="Description";width=48}
}
}
intermittantly gives the following error:
You cannot call a method on a null-valued expression. At C:\Users\Jasons1\CPUusage.ps1:41 char:4
$row = new-object PSObject -Property #{
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CategoryInfo : InvalidOperation: (:) [], RuntimeException
FullyQualifiedErrorId : InvokeMethodOnNull
which I fail to understand as it is within a loop and should either work or get skipped as there is a test for null.
Pretty sure that your issues are stemming from this line:
User = (gwmi Win32_Process -ComputerName $ComputerName -Credential $Credential | Where {$_.ProcessId -eq $Process.IDProcess}).GetOwner().User
Specifically from .GetOwner(). Your where clause must not be finding a matching process for that item while it is in the loop. I realize there is not much time elapsed but WMI queries are not the fastest things out there.
What is happening is likely a result of a process queried earlier in $ProcessList = gwmi Win32_PerfFormattedData_PerfProc_Process and then later when you are using gwmi Win32_Process the list of processes changed. You need to account for this as well. Time has elapsed and threads do not live forever.
$queryResult = gwmi Win32_Process -ComputerName $ComputerName -Credential $Credential | Where {$_.ProcessId -eq $Process.IDProcess}
$owner = if($queryResult){$queryResult.GetOwner().User}else{"Process DNE"}
#...
User = $owner
Not very pretty but accounts for the potential of a null return from the wmi query.
The code below outputs information like:
System Information for: Localhost
Model : {0}
Serial Number : {1}
Version : {2}
Monitor Model : {3}
Monitor Serial : {4}
How do I export to CSV and have the formatting in Excel like:
Name, Model, Serial Number, Version, Monitor Model, Monitor serial
I would like each value in its own cell.
Code 1:
$ArrComputers = "localhost"
$OutputLog = ".\output.log"
$NotRespondingLog = ".\notresponding.log"
$ErrorActionPreference = "Stop"
Clear-Host
ForEach ($Computer in $ArrComputers) {
try {
$computerSystem = get-wmiobject Win32_ComputerSystem -Computer $Computer
$computerBIOS = get-wmiobject Win32_BIOS -Computer $Computer
$Version = Get-WmiObject -Namespace "Root\CIMv2" `
-Query "Select * from Win32_ComputerSystemProduct" `
-computer $computer | select -ExpandProperty version
$MonitorInfo = gwmi WmiMonitorID -Namespace root\wmi -computername $computer |
Select -last 1 #{n="Model"; e={[System.Text.Encoding]::ASCII.GetString($_.UserFriendlyName -ne 00)}},
#{n="Serial Number";e={[System.Text.Encoding]::ASCII.GetString($_.SerialNumberID -ne 00)}}
} catch {
$Computer | Out-File -FilePath $NotRespondingLog -Append -Encoding UTF8
continue
}
$Header = "System Information for: {0}" -f $computerSystem.Name
write-host $Header -BackgroundColor DarkCyan
$Header | Out-File -FilePath $OutputLog -Append -Encoding UTF8
$Output = (#"
-------------------------------------------------------
Model : {0}
Serial Number : {1}
Version : {2}
Monitor Model : {3}
Monitor Serial : {4}
-------------------------------------------------------
"#) -f -join $computerSystem.Model, $computerBIOS.SerialNumber, $Version, `
$MonitorInfo.Model, $MonitorInfo."Serial Number"
Write-Host $Output
$Output | Out-File -FilePath $OutputLog -Append -Encoding UTF8
}
Drop the format string and simply export the data to a CSV:
$data = ForEach ($Computer in $ArrComputers) {
try {
...
} catch {
...
}
$props = [ordered]#{
'Name' = $computerSystem.Name
'Model' = $computerSystem.Model
'Serial Number' = $computerBIOS.SerialNumber
'Version' = $Version
'Monitor Model' = $MonitorInfo.Model
'Monitor Serial' = $MonitorInfo."Serial Number"
}
New-Object -Type PSCustomObject -Property $props
}
$data | Export-Csv 'C:\path\to\output.csv' -NoType
The New-Object statement is required, because Export-Csv exports the properties of a list of objects as the fields of the CSV file.
Beware that Excel is rather particular about what it accepts as CSV. The file must must be comma-separated (regardless of what field separator is configured in your system's regional settings).