Export computer information to a CSV - powershell

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).

Related

Export-Csv Exporting List Properties Instead of Content

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

How to read input through a csv file and write the output in an output file in power shell script?

[This is the powershell script to get the selected services status of servers,where list of servers are given through input csv file and the status of those server should be stored in an output file.
-----------Below is the script----------
$Servers = Get-Content "C:\temp\input.csv"
$Log = #()
foreach($Server in $Servers)
{
$Services = Get-Service *HRRA*, "SQL Server Reporting Services" -ComputerName $COMPUTERNAME
foreach ($Service in $Services)
{
$properties = #(
#{n='ServiceName';e={$Service.Name}}
#{n='Status';e={$Service.Status}}
#{n='ServerName';e={$Server}}
)
$Log += "" | select $properties
}
}
$Log | Format-Table -AutoSize | Out-File "D:\temp\test.txt" -Force
------------------------------------New Script----------------------------------
$Computers = Import-Csv "C:\Temp\Input.csv"
#$mailboxdata = Get-Service *HRRA*,"SQL Server Reporting Services" -ComputerName $ComputerName| select machinename,name, status | sort machinename |
#format-table -AutoSize |Out-File "D:\Temp\RRR.txt"
#LogWrite "$ComputerName"
foreach($row in $computers)
{
{
Add-Content -Path D:\Temp\SSRS.txt -Value $mailboxdata }
Get-Content -Path D:\Temp\SSRS.txt
$ComputerName= $row.ComputerName;
$mailboxdata = Get-Service *HRRA*,"SQL Server Reporting Services" -ComputerName $ComputerName| select machinename,name, status | sort machinename |
format-table -AutoSize |Out-File "D:\Temp\SSR.txt"
$fromaddress = "Reporting.Services#accenture.com"
$toaddress = "aditi.m.singh#accenture.Com"
#$toaddress1 = "s.ab.balakrishnan#accenture.com"
$Subject = "SSRS Services Status"
$body = "Please find attached - test"
$attachment = "D:\Temp\SSR.txt"
$smtpserver = "AMRINT.SMTP.ACCENTURE.COM"
$message = new-object System.Net.Mail.MailMessage
$message.From = $fromaddress
$message.To.Add($toaddress)
#$message.To.Add($toaddress1)
$message.IsBodyHtml = $True
$message.Subject = $Subject
$attach = new-object Net.Mail.Attachment($attachment)
$message.Attachments.Add($attach)
$message.body = $body
$smtp = new-object Net.Mail.SmtpClient($smtpserver)
$smtp.Send($message)
}
If i am running the script with static value its giving me output for both the servers---Below is the script----
Get-Service *HRRA*,"SQL Server Reporting Services" -ComputerName VW118627, VW118623 | select name, status, machinename | sort machinename | format-table -AutoSize |
Out-File "D:\Temp\Report.txt"
Looking at the screenshot, I can see the input csv is really a Comma Separated Values file. For these, you use the Import-Csv cmdlet to retrieve an array of computer names from the file.
$outputFile = "D:\temp\test.csv"
# make sure the field name matches what is in the CSV header
$Servers = (Import-Csv -Path "C:\temp\input.csv").ComputerName
$Log = foreach($Server in $Servers) {
# add a test to see if we can reach the server
if (Test-Connection -ComputerName $Server -Count 1 -Quiet -ErrorAction SilentlyContinue) {
Get-Service -Name *HRRA*, "SQL Server Reporting Services" -ComputerName $Server |
Select-Object #{Name = 'MachineName'; Expression = {$Server}},
#{Name = 'ServiceName'; Expression = {$_.Name}},
#{Name = 'Status'; Expression = {$_.Status}}
}
else {
Write-Warning "Server '$Server' is unreachable"
}
}
# if you want the log sorted, do
$Log = $Log | Sort-Object MachineName
# output on screen
$Log | Format-Table -AutoSize
# output to CSV file
$Log | Export-Csv -Path $outputFile -Force -NoTypeInformation
# mail the output csv file using Send-MailMessage
# collect the parameters in a hashtable
$mailParams = #{
SmtpServer = "AMRINT.SMTP.ACCENTURE.COM"
From = "Reporting.Services#accenture.com"
To = "aditi.m.singh#accenture.com"
Subject = "SSRS Services Status"
Body = "Please find attached - test"
BodyAsHtml = $true
Attachments = $outputFile
# add other parameters if needed
# see https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/send-mailmessage
}
# use 'splatting' to send the email
# see https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_splatting
Send-MailMessage #mailParams
P.S. The Format-Table cmdlet is for displaying the object(s) on screen only.

Log output of ForEach loop

The code below gets computer info remotely. I couldn't get it send output to a log file. Also, how do I log all unqueried computers in a separate log file?
Code:
$ArrComputers = gc .\computernames.txt
Clear-Host
ForEach ($Computer in $ArrComputers)
{
$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 version
write-host "System Information for: " $computerSystem.Name -BackgroundColor DarkCyan
"-------------------------------------------------------"
"Model: " + $computerSystem.Model
"Serial Number: " + $computerBIOS.SerialNumber
"Version: " + $Version
""
"-------------------------------------------------------"
}
Logging is fairly straightforward. You just need to store output in a variable and then use Out-File cmdlet:
$ArrComputers = gc .\computernames.txt
$OutputLog = ".\output.log" # Main log
$NotRespondingLog = ".\notresponding.log" # Logging "unqueried" hosts
$ErrorActionPreference = "Stop" # Or add '-EA Stop' to Get-WmiObject queries
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
}
catch
{
$Computer | Out-File -FilePath $NotRespondingLog -Append -Encoding UTF8
continue
}
$Header = "System Information for: {0}" -f $computerSystem.Name
# Outputting and logging header.
write-host $Header -BackgroundColor DarkCyan
$Header | Out-File -FilePath $OutputLog -Append -Encoding UTF8
$Output = (#"
-------------------------------------------------------
Model: {0}
Serial Number: {1}
Version: {2}
-------------------------------------------------------
"#) -f $computerSystem.Model, $computerBIOS.SerialNumber, $Version
# Ouputting and logging WMI data
Write-Host $Output
$Output | Out-File -FilePath $OutputLog -Append -Encoding UTF8
}
In its current state, your code will give an error for each computer the Get-WmiObject command could not reach. I would consider using -ErrorAction SilentlyContinue -ErrorVariable Err at the end of the first Get-WmiObject command. This will stop the errors from coming to your screen and clogging your output. You can then condition the other two calls to Get-WmiObject to only happen if the ErrorVariable is empty. If it exists, log the name of the computer, and then output to a file.
The reason you are not able to log anything else to a file is because you are using Write-Host. I would consider using a PSObject to return information. This will allow you to see the output on the screen in an organized fashion while also allowing you to write output to a file.
Also, using the -ExpandProperty switch with Select-Object will keep you from returning a hashtable for the Version property.

Get Pc & Monitor Info remotely with one script

Code 1 gets PC info Remotely and Code 2 gets Monitor Info remotely. How do I incorporate Code 2 into Code 1? I would like the output to look like the PC info followed by Monitor Info. I tried to incorporate but got a lot of errors.
Code 1 logs all the information like computers queried and UN-queried.
Code 1: Courtesy of Alexander Obersht
$ArrComputers = gc .\computernames.txt
$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
}
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}
-------------------------------------------------------
"#) -f $computerSystem.Model, $computerBIOS.SerialNumber, $Version
Write-Host $Output
$Output | Out-File -FilePath $OutputLog -Append -Encoding UTF8
}
Code 2:
$users = gc .\computernames1.txt
gwmi WmiMonitorID -Namespace root\wmi -computername $users |
Select PSComputerName,
#{n="Model";e={[System.Text.Encoding]::ASCII.GetString($_.UserFriendlyName -ne 00)}},
#{n="Serial Number";e={[System.Text.Encoding]::ASCII.GetString($_.SerialNumberID -ne 00)}} |
Format-List | Out-File '.\report.csv'
Updated my solution from your previous question:
$ArrComputers = gc .\computernames.txt
$OutputLog = ".\output.log" # Main log
$NotRespondingLog = ".\notresponding.log" # Logging "unqueried" hosts
$ErrorActionPreference = "Stop" # Or add '-EA Stop' to Get-WmiObject queries
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 PSComputerName, `
#{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
# Outputting and logging header.
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 $computerSystem.Model, $computerBIOS.SerialNumber, $Version, `
$MonitorInfo.Model, $MonitorInfo."Serial Number"
# Ouputting and logging WMI data
Write-Host $Output
$Output | Out-File -FilePath $OutputLog -Append -Encoding UTF8
}

Formatting Output File

I am creating a script to get the uptime / last reboot of a group of PCs read in from a text file.
I can output the results to file, but it keeps repeating the column header which I do not want. Basically what I would like is to output the first line for the headers and then each line after for the data the data underneath. The code below repeats the column header to the output file and there are a lot of spaces between the lines. Would an output to CSV be easier to handle?
Here is my code. The first Out-File command is to overwrite the file if it exists, essentially clearing the file.
$computers = Get-Content "c:\temp\ps\pc.txt"
out-file -FilePath "C:\temp\ps\output.txt"
foreach ($computer in $computers)
{
$Computerobj = "" | select ComputerName, Uptime, LastReboot
$wmi = Get-WmiObject -ComputerName $computer -Query "SELECT LastBootUpTime FROM Win32_OperatingSystem"
$now = Get-Date
$boottime = $wmi.ConvertToDateTime($wmi.LastBootUpTime)
$uptime = $now - $boottime
$d =$uptime.days
$h =$uptime.hours
$m =$uptime.Minutes
$s = $uptime.Seconds
$Computerobj.ComputerName = $computer
$Computerobj.Uptime = "$d Days $h Hours $m Min $s Sec"
$Computerobj.LastReboot = $boottime
$Computerobj
out-file -FilePath "C:\temp\ps\output.txt" -in $computerobj -append
}
A pipeline with ForEach-Object and Export-Csv would be a better approach:
$now = Get-Date
Get-Content -Path 'C:\temp\ps\pc.txt' | ForEach-Object {
$wmi = Get-WmiObject -ComputerName $_ -Class Win32_OperatingSystem
$boottime = $wmi.ConvertToDateTime($wmi.LastBootUpTime)
$uptime = $now - $boottime
New-Object -Type PSObject -Property #{
'ComputerName' = $_
'Uptime' = '{0} Days {1} Hours {2} Min {3} Sec' -f $uptime.Days,
$uptime.Hours, $uptime.Minutes, $uptime.Seconds
'LastReboot' = $boottime
}
} | Export-Csv -Path 'C:\temp\ps\output.txt' -NoType
If you need the data both in a file and on the console, you could use ConvertTo-Csv and Tee-Object:
Get-Content 'c:\temp\ps\pc.txt' | ForEach-Object {
...
} | ConvertTo-Csv -NoType | Tee-Object -FilePath 'C:\temp\ps\output.txt'
Try this:
$computers = Get-Content "c:\temp\ps\pc.txt"
#Create a report variable as an array to hold all our data
$report = #();
#No longer necessary
#out-file -FilePath "C:\temp\ps\output.txt"
foreach ($computer in $computers)
{
$Computerobj = "" | select ComputerName, Uptime, LastReboot
$wmi = Get-WmiObject -ComputerName $computer -Query "SELECT LastBootUpTime FROM Win32_OperatingSystem"
$now = Get-Date
$boottime = $wmi.ConvertToDateTime($wmi.LastBootUpTime)
$uptime = $now - $boottime
$d =$uptime.days
$h =$uptime.hours
$m =$uptime.Minutes
$s = $uptime.Seconds
$Computerobj.ComputerName = $computer
$Computerobj.Uptime = "$d Days $h Hours $m Min $s Sec"
$Computerobj.LastReboot = $boottime
#Add the computer to the report array
$report += $Computerobj
}
#Uncomment this if you need to see the report as well as write it to a file
#Write-Output $report
out-file -FilePath "C:\temp\ps\output.txt" -in $report
Now you can manipulate the report as a whole, so you can even add things at the end like $report = $report | Sort-Object -Property ComputerName to sort the report by computer names, or filter it with Where-Object.