Powershell script in columns instead of rows - powershell

I was trying to write a script which would use a text file with hostnames and needs to generate a file with the extra data. I can only manage to get it in rows instead of columns:
Model:
Bios Version:
TPM OEM Ver:
User logged In:
I would also like to get the emailaddress from the logged on user. I was thinking to use get-aduser. Could I add another foreach after the current code, using the column with the usernames? How would I do this?
All help is greatly appreciated!
My code is:
$output = foreach ($hostname in Get-Content C:\temp\hostnames.txt)
{
$computerinfo = get-ciminstance -computername $hostname Win32_ComputerSystem
$computerBIOS = get-ciminstance -computername $hostname Win32_BIOS
$tpm = Get-ciminstance -class Win32_Tpm -namespace root\CIMV2\Security\MicrosoftTpm -ComputerName $hostname
"Hostname: " + $computerinfo.name
"Model: " + $computerinfo.Model
"Bios Version: " + $computerBIOS.smbiosbiosversion
"TPM OEM Ver: " + $tpm.ManufacturerVersion
"User logged In: " + $computerinfo.UserName
}
$output | out-file 'C:\Temp\hostnames3.txt' -append

You should use the CSV file format instead of plain text for structured data. That makes it easier to use them for further steps if needed.
$output =
foreach ($hostname in Get-Content C:\temp\hostnames.txt) {
$computerinfo = Get-CimInstance -ComputerName $hostname -ClassName Win32_ComputerSystem
$computerBIOS = Get-CimInstance -ComputerName $hostname -ClassName Win32_BIOS
$tpm = Get-CimInstance -Namespace root\CIMV2\Security\MicrosoftTpm -ComputerName $hostname -ClassName Win32_Tpm
[PSCustomObject]#{
Hostname = $computerinfo.name
Model = $computerinfo.Model
Bios_Version = $computerBIOS.smbiosbiosversion
TPM_OEM_Ver = $tpm.ManufacturerVersion
User_logged_In = $computerinfo.UserName
}
}
$output |
Export-Csv -Path 'C:\Temp\hostnames3.csv' -NoTypeInformation
In my experience the UserName property of the CIM-Class Win32_ComputerSystem is unreliable to determine the logged on user. I usually use good old quser.exe like this:
$UserQuery = ( C:\Windows\System32\quser.exe /server:$hostname 2> null)
if ($UserQuery) {
$UserQuery[1].Trim() -match "^(\S+)\s+.*((\d{2}\.){2}\d{4}\s+\d{2}:\d{2})" | Out-Null
$LoggedOnUser = $Matches[1]
$LogonTime = Get-Date -Date $Matches[2]
}
Then you can use $LoggedOnUser and $LogonTime to include it in your output object if you like.
Of course you can include a additional AD query for more information about the logged on user.

Related

Powershell - Export csv for program name, version, installdate

im trying to run a command against a list of computers to find out information on the programs on the computers. then i would like to export that information on a Csv in the following format:
Computer name, McAfee Agent, version, Installdate
i wanted to get the list of these computers and the programs version numbers and installdate.
this command works, but not for exporting:
Get-WmiObject win32_product -ComputerName SYSTEMName01 | Where name -Match "McAfee Agent" | Select Name, Version, Installdate
I tried using the following code here, but this isn't working because it populates the information in Powershell then gives me a:
"RPC server is unavailable" red text.
and the Exported CSV document only gives me one populated row of the Name of the program and its version # and install date. but it doesn't populate the computer name it looked into and it doesn't list the other computers in the .txt document.
$computers = Get-content -Path "C:\nice\List-of-systems.txt"
Get-WmiObject win32_product -ComputerName $computers | Where name -Match "McAfee Agent" | Select Name, Version, Installdate |
Export-Csv -Path "C:\nice\computers-mcafee-status.csv"
If you're gonna go through the trouble of just querying single information, might as well query the system info a long with it.
<#
SYNTAX
Get-SystemInfo [[-ComputerName] <string[]>] [<CommonParameters>]
EXAMPLE
Get-SystemInfo -ComputerName SystemOne, SystemTwo
'SystemOne', 'SystemTwo' | Get-SystemInfo
Get-content -Path "C:\nice\List-of-systems.txt" | Get-SystemInfo
Get-content -Path "C:\nice\List-of-systems.txt" | Get-SystemInfo | Export-CSV C:\List-Of-Systems.csv -NoTypeInformation
#>
Function Get-SystemInfo{
[CmdletBinding()]
Param(
[Parameter(Mandatory=$False,
ValueFromPipeLine=$True,
ValueFromPipelineByPropertyName=$true,
HelpMessage='Enter Computer Name')]
[Alias('CN','Name')]
[String[]]$ComputerName = $env:COMPUTERNAME)
Process{
try{
#$ExportPath = "C:\nice\computers-mcafee-status.csv"
#if(-Not(Test-Path -Path '$ExportPath')){
#New-Item '$ExportPath' -ItemType File }
foreach($Computer in $ComputerName){
$CIMSession = New-CimSession -ComputerName $Computer -ErrorAction Stop
#$OS = Get-CimInstance -CimSession $CIMSession -Namespace Root/CIMv2 -ClassName WIN32_OperatingSystem -ErrorAction SilentlyContinue
$CS = Get-CimInstance -CimSession $CIMSession -Namespace Root/CIMv2 -ClassName WIN32_ComputerSystem -Property Model,Manufacturer,Username -ErrorAction SilentlyContinue
$BS = Get-CimInstance -CimSession $CIMSession -Namespace Root/CIMv2 -ClassName WIN32_BIOS -ErrorAction SilentlyContinue
$MAC = Get-CimInstance -CimSession $CIMSession -Namespace Root/CIMv2 -ClassName WIN32_NetworkAdapterConfiguration | Where-Object -Property Description -Like "*Ethernet*"
$MA = Get-CimInstance -CimSession $CIMSession -Namespace Root/CIMv2 -ClassName WIN32_Product | Where-Object Name -Match "McAfee Agent" -ErrorAction SilentlyContinue
[PSCustomobject] #{
"Computer Name" = $Computer
"Status" = "Online"
"Manufacturer" = if($CS.Manufacturer){$CS.Manufacturer}Else{"Unable to get Manufacturer"}
"Model" = if($CS.Model){$CS.Model}Else{"Unable to get Model"}
"User Logged in" = if($cs.UserName){$cs.UserName}Else{"No user logged in"}
"Serial Number" = if($BS.SerialNumber){$BS.SerialNumber}Else{"Unable to get Serial #"}
"MAC Address" = if($MAC.MACAddress){$MAC.MACAddress}Else{"Unable to get MAC Address"}
"McAfee InstallDate" = if($MA.InstallDate){$MA.InstallDate}Else{"Unable to get InstallDate"}
"McAfee Version" = if($Ma.Version){$MA.Version}Else{"Unable to get Version"}
} #| Export-Csv -Path '$ExportPath' -Append -NoTypeInformation
}
} Catch [Microsoft.Management.Infrastructure.CimException] {
[PSCustomobject] #{
"Computer Name" = $Computer
"Status" = "Offline"
"Manufacturer" = $null
"Model" = $null
"User Logged in" = $null
"Serial Number" = $null
"MAC Address" = $null
"McAfee InstallDate" = $null
"McAfee Version" = $null
} #| Export-Csv -Path '$ExportPath' -Append -NoTypeInformation
} Finally {
if($CIMSession){
Get-CimSession | Remove-CimSession
}
}
}
}
The issue with what you're trying to do is, you're not including the computername to be exported along with the other information. You also need to -Append to the file. The errors you receive mean that you just couldn't connect to the remote machine.

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.

Create txt file from AD and query servers listed to get system information and export to csv

I'm trying to create a list of computer systems in AD, which I would like to save to a text file, and then using the text file retrieve the system information, i.e make, model, manufacturer, serial number etc.
Rather than try and tackle this all in one go I thought I'd do the query system information first, but the first problem is I can read contents from text file, but it only displays the information from first server then stops and secondly I've set it to (or tried to set it to) export to CSV, but it creates the CSV file but no information on the CSV file. Also at some point I'm going to need to sort headers too.
New-Item -ItemType Directory -Force -Path C:\Computer_Details
set-location C:\Computer_Details
$results = ForEach ($Computersystem in $Computer)
{
$Computer = Get-Content -path C:\computers.txt
$computerSystem = Get-CimInstance CIM_ComputerSystem
$computerBIOS = Get-CimInstance CIM_BIOSElement
$computerOS = Get-CimInstance CIM_OperatingSystem
$computerCPU = Get-CimInstance CIM_Processor
$computerHDD = Get-CimInstance Win32_LogicalDisk -Filter "DeviceID = 'C:'"}
Write-Host "System Information for: " $computerSystem.Name -BackgroundColor DarkCyan
"Manufacturer: " + $computerSystem.Manufacturer
"Model: " + $computerSystem.Model
"Serial Number: " + $computerBIOS.SerialNumber
"CPU: " + $computerCPU.Name
"HDD Capacity: " + "{0:N2}" -f ($computerHDD.Size/1GB) + "GB"
"HDD Space: " + "{0:P2}" -f ($computerHDD.FreeSpace/$computerHDD.Size) + " Free (" + "{0:N2}" -f ($computerHDD.FreeSpace/1GB) + "GB)"
"RAM: " + "{0:N2}" -f ($computerSystem.TotalPhysicalMemory/1GB) + "GB"
"Operating System: " + $computerOS.caption + ", Service Pack: " + $computerOS.ServicePackMajorVersion
"User logged In: " + $computerSystem.UserName
"Last Reboot: " + $computerOS.LastBootUpTime
$results | Export-Csv ComputerDetails.csv
Any help would be greatly appreciated and probably should mention I'm fairly new to PowerShell, but guessing you'll work that out reading the above :)
You need to define $Computer outside the foreach loop. In addition to that, you'd want to gather all the system information strings per computer inside the loop:
New-Item -ItemType Directory -Force -Path C:\Computer_Details
set-location C:\Computer_Details
# Get computer names from file
$Computers = Get-Content -path C:\computers.txt
# Loop over each computer name
$results = foreach($Computer in $Computers)
{
# Set up a remote session to the machine in question
try{
$ComputerSession = New-CimSession -ComputerName $Computer
# Fetch all the information from it
$computerSystem = Get-CimInstance CIM_ComputerSystem -CimSession $ComputerSession
$computerBIOS = Get-CimInstance CIM_BIOSElement -CimSession $ComputerSession
$computerOS = Get-CimInstance CIM_OperatingSystem -CimSession $ComputerSession
$computerCPU = Get-CimInstance CIM_Processor -CimSession $ComputerSession
$computerHDD = Get-CimInstance Win32_LogicalDisk -Filter "DeviceID = 'C:'" -CimSession $ComputerSession
$Sucess = $true
} catch {
Write-Warning "Unable to fetch information from $Computer"
$Success = $false
}
if($Sucess){
# Create a new object containing all the details you want
New-Object psobject -Property #{
"ComputerName" = $Computer
"Manufacturer" = $computerSystem.Manufacturer
"Model" = $computerSystem.Model
"Serial Number" = $computerBIOS.SerialNumber
"CPU" = $computerCPU.Name
"HDD Capacity" = "{0:N2}GB" -f ($computerHDD.Size/1GB)
"HDD Space" = "{0:P2}" -f ($computerHDD.FreeSpace/$computerHDD.Size)
"HDD Free" = "{0:N2}GB" -f ($computerHDD.FreeSpace/1GB)
"RAM" = "{0:N2}GB" -f ($computerSystem.TotalPhysicalMemory/1GB)
"Operating System" = "{0}, Service Pack: {1}" -f $computerOS.caption,$computerOS.ServicePackMajorVersion
"User logged In" = $computerSystem.UserName
"Last Reboot" = $computerOS.LastBootUpTime
}
} else {
New-Object psobject -Property #{
"ComputerName" = $Computer
}
}
}
# $results is now an array of the objects created above, export it to a CSV file!
$results | Export-Csv ComputerDetails.csv
You should not use Write-Host as a method to produce data, it's only good for debugging purposes. Instead output a single string inside your foreach-loop as a result, this way it will be properly gathered in your results variable. Next, you want to iterate through more than a single computer, but instead you get the only file there is, c:\computers.txt and more, you don't query Get-CIMInstance against any remote computer identified by its name. To resolve: First, get the computer name out of computers.txt content, one by one, then execute a series of remote requests to CIM instances on that computer by providing -ComputerName as an argument to Get-CIMInstance. And finally, collect the output as a single string (this is preferred to simplify further parsing, if there is any) and use it as an output of your script block.
$computers=get-content -path C:\computers.txt
$results = ForEach ($Computer in $Computers)
{
try {
# remote computer might not be accessible!
$computerSystem = Get-CimInstance CIM_ComputerSystem -computername $computer
$computerBIOS = Get-CimInstance CIM_BIOSElement -computername $computer
$computerOS = Get-CimInstance CIM_OperatingSystem -computername $computer
$computerCPU = Get-CimInstance CIM_Processor -computername $computer
$computerHDD = Get-CimInstance Win32_LogicalDisk -computername $computer -Filter "DeviceID = 'C:'"}
# once you've collected the data, prepare output string
$output="System Information for: $($computerSystem.Name)`r`n"
$output+="Manufacturer: $($computerSystem.Manufacturer)`r`n"
$output+="Model: $($computerSystem.Model)`r`n"
$output+="Serial Number: $($computerBIOS.SerialNumber)`r`n"
$output+="CPU: $($computerCPU.Name)`r`n"
$output+="HDD Capacity: $("{0:N2}" -f ($computerHDD.Size/1GB)) GB`r`n"
$output+="HDD Space: $("{0:P2}" -f ($computerHDD.FreeSpace/$computerHDD.Size)) Free ($({0:N2}" -f ($computerHDD.FreeSpace/1GB)) GB)"
$output+="RAM: $({0:N2}" -f ($computerSystem.TotalPhysicalMemory/1GB)) GB`r`n"
$output+="Operating System: $($computerOS.caption), Service Pack: $($computerOS.ServicePackMajorVersion)`r`n"
$output+="User logged In: $($computerSystem.UserName)`r`n"
$output+="Last Reboot: $($computerOS.LastBootUpTime)`r`n"
} catch {
$output="$computer is not accessible!"
}
# once built, output the string into the variable
$output
}
$results | Out-File ComputerDetails.txt -Encoding UTF8
Note, I have used a "$(expression)" construction everywhere in the strings, it simplifies syntax of building a single string out of many expressions' results.

using powershell to gather logged on user names on remote computers?

$MachineList = Get-Content -Path "E:\ps\comp list\Test Computers.txt"; # One system name per line
foreach ($Machine in $MachineList)
{
($Machine + ": " + #(Get-WmiObject -ComputerName $Machine -Namespace root\cimv2 -Class Win32_ComputerSystem -erroraction silentlycontinue)[0].UserName);
Write-Output ($Machine + ": " + #(Get-WmiObject -ComputerName $Machine -Namespace root\cimv2 -Class Win32_ComputerSystem -erroraction silentlycontinue)[0].UserName) | Out-File "E:\ps\comp output\Test Computers.txt" -Append
}
Update: Here's the working script, thanks all for the help! :) It pulls in a list of computers prints to the screen and then also writes them to a file.
I found this powershell code and it works but I'd like it to display the machine name in front of the username when it displays. How can I get it to do that?
So like -
MachineName: Username
I'm a powershell newb... any help would be appreciated! Thanks! :)
Try this
$MachineList = Get-Content -Path c:\ListOfMachines.txt; # One system name per line
foreach ($Machine in $MachineList){
($Machine + ": " + #(Get-WmiObject -ComputerName $Machine -Namespace root\cimv2 -Class Win32_ComputerSystem)[0].UserName);
}
Try this:
Get-Content -Path c:\ListOfMachines.txt | % {
Get-WmiObject -ComputerName $_ -Namespace root\cimv2 -Class Win32_ComputerSystem -erroraction silentlycontinue | % {
"$($_.Name): $($_.username)"
}
}

How to make script run remotelly on computers?

I have written a script that is to collect hardware and software information from a forrest/domain. I've read several posts about running a PS-script from a computer on a server, but I want to do the opposite.
How do you know that a script is "remotely accesible".
I've seen this command beeing used:
Invoke-Command -ComputerName {serverName} –ScriptBlock { commands }
Is there any other alternatives than computername? I'm thinking that this is not exclussive as several computers can have the same name..
This is my script:
try{
$ConfirmPreference="none"
$error.clear()
$erroractionpreference = "silentlycontinue"
#Gets the date of this day, used as name for XML-file later.
$a = get-date -uformat "%Y_%m_%d"
#Saves computername to compname variable(HELPER)
$compname = gc env:computername
#Gets the path to directory for all saved files and folders
$scriptpath = Split-Path -parent $myinvocation.MyCommand.Definition
#PC Serial Number, is used as name for directory containing XML files for this computer.
$serialnr = gwmi win32_bios | select -Expand serialnumber
#Creates a folder with the name of the computers hardware serialnumber if it does not exist.
if(!(Test-Path -path $scriptpath\$serialnr)) {
New-item -path $scriptpath -name $serialnr -type directory
}
#Username
$username = gc env:username
#System Info
gwmi -computer $compname Win32_ComputerSystem | ForEach {$siname = $_.Name; $simanufacturer = $_.Manufacturer; $simodel = $_.Model}
#Graphic card
$gpuname = gwmi win32_VideoController | select -Expand Name
#Processor Info
gwmi -computer $compname Win32_Processor | ForEach-Object {$cpuname = $_.Name; $cpumanufacturer = $_.Manufacturer; $cpucores = $_.NumberOfCores; $cpuaddresswidth = $_.AddressWidth}
#Memory
$totalmem = 0
$memsticks = gwmi -Class win32_physicalmemory
foreach ($stick in $memsticks) { $totalmem += $stick.capacity }
$totalmem = [String]$($totalmem / 1gb) + " GB"
#Drive
$totalspace = 0
$totalsize = gwmi -Class win32_logicaldisk
foreach($size in $totalsize) { $totalspace += $size.size }
$totalspace = "{0:N2}" -f ($totalspace/1Gb) + " GB"
#Install time for windows OS
$utctime = get-wmiobject win32_OperatingSystem | select-object -expandproperty installDate
$installtime = [System.Management.ManagementDateTimeConverter]::ToDateTime($utctime);
$installtime = Get-Date $installtime -uformat "%d/%m/%Y %X"
#--------#
#XML-form
#--------#
try{
$erroractionpreference = "stop"
$template = "<computer version='1.0'>
<hardware>
<serialnumber>$serialnr</serialnumber>
<systeminfo>
<name>$siname</name>
<manufacturer>$simanufacturer</manufacturer>
<model>$simodel</model>
</systeminfo>
<drive>
<size>$totalspace</size>
</drive>
<memory>
<size>$totalmem</size>
</memory>
<gpu>
<name>$gpuname</name>
</gpu>
<cpu>
<name>$cpuname</name>
<manufacturer>$cpumanufacturer</manufacturer>
<id>cpuid</id>
<numberofcores>$cpucores</numberofcores>
<addresswidth>$cpuaddresswidth</addresswidth>
</cpu>
</hardware>
<software>
<user>
<name>$username</name>
</user>
<osinfo>
<caption></caption>
<installdate>$installtime</installdate>
<servicepack></servicepack>
</osinfo>
</software>
</computer>"
$template | out-File -force $ScriptPath\$serialnr\$a.xml
$systemroot = [System.Environment]::SystemDirectory
$xml = New-Object xml
$xml.Load("$ScriptPath\$serialnr\$a.xml")
}catch{
}
#OSInfo, software
$newosinfo = (#($xml.computer.software.osinfo)[0])
Get-WmiObject -computer $compname Win32_OperatingSystem |
ForEach-Object {
$newosinfo = $newosinfo.clone()
[String] $bitversion = $_.osarchitecture
$newosinfo.caption = [String]$_.caption + "" + $_.osarchitecture
$newosinfo.servicepack = $_.csdversion
$xml.computer.software.AppendChild($newosinfo) > $null
}
$xml.computer.software.osinfo | where-object {$_.caption -eq ""} | foreach-object {$xml.computer.software.RemoveChild($_)}
#-------Save and get content--------
$xml.Save("$scriptpath\$serialnr\$a.xml")
#$new = Get-Content $scriptpath\$serialnr\$a.xml
#-----------------------------------
if(!$?){
"An error has occured"
}
}catch{
[system.exception]
"Error in script: system exception"
}finally{
}
For the -ComputerName parameter, you can use NETBIOS name, IP address, or fully-qualified domain name. For more details, see Invoke-Command on TechNet.
Seems like your script is "only" saving the data on the machine it is running, you will probably want it to return something in order to be useful with Invoke-Command.