Home > Mobile >  How do I generate this report by importing the hostname to a list or csv?
How do I generate this report by importing the hostname to a list or csv?

Time:02-10

I had this script, but the format was in HTML and I cleaned up all the code and changed the commands that were "gwmi" to "Get-CimInstance" to have good practices. My goal is to transform this script that handles a hostname to a list of hostnames.

Can someone help me?

The idea I had would be to have a window that has a "browse" button to import the list, be it in txt or csv and for each hostname in that list it would do these commands and in the end it would export to an xlsx file (I tried with Export-Excel, but you need to download the module separately, and you need to trust the repository, authorize the import of the module for later use, so I would have to make this standalone, without any request, because I would convert this ps1 into an exe file) and the data, would need to be side by side with the headers, e.g. Hostname, Last User Logged, Type Of Chassis etc.

I would be very grateful if someone can help me, I've been building part 1 of this script for a few hours now, and now I need to go to part 2 (that is this process to create and export csv results) which is the part where I feel stuck.

Add-Type -AssemblyName Microsoft.VisualBasic
$ComputerName = [Microsoft.VisualBasic.Interaction]::InputBox("Insert the hostname Name", "Hardware Report")
If ($ComputerName -eq "") 
{
    break
}


#Check computer online in network, if is not online, the hostname will be skipped but necessary add in log entry which computers is offline
$ProgressPreference = 'SilentlyContinue'
try { 
    $ErrorActionPreference = "Stop";
    $TestComputerHost = Test-Connection $ComputerName -Count 1 -InformationAction Continue -WarningAction SilentlyContinue;
    } catch {
    #Hostname will be skipped
    } finally {
    $ProgressPreference = 'Continue'
    $ErrorActionPreference = "Continue"
    }

#Validade crucial service that is crucial for get remote data, and if is not possible to get this information, the hostname will be skipped
try {
$ErrorActionPreference = "Stop"
Get-CimInstance win32_operatingsystem -ComputerName $ComputerName | Out-Null }
catch [System.Runtime.InteropServices.COMException] 
    {
    #Hostname will be skipped
    }


#Validate if the WS Management service is enabled on the remote device
$ProgressPreference = 'SilentlyContinue'
$TestComputerHost = Test-NetConnection $ComputerName -Port 5985 -InformationLevel Quiet -WarningAction SilentlyContinue
If ($TestComputerHost -ne "False"){
    
}
$ProgressPreference = 'Continue'


#Function to create the Get-WUChassisType that is performed to find out if the Chassis of the equipment is Notebook or Desktop, and it is not configured to detect virtual machine
Function Get-WUChassisType {

    [CmdletBinding()]
    param (
    )
    Set-StrictMode -Version 'Latest'
    [int[]]$chassisType = try {
    $ErrorActionPreference = "Stop";
    Get-CimInstance Win32_SystemEnclosure -ComputerName $ComputerName | Select-Object -ExpandProperty ChassisTypes;
    } catch {
    #Here need to be blank result or skip this result but keep the rest results
    } finally {
    $ErrorActionPreference = "Continue";
    }
    switch ($chassisType) {
        { $_ -in 3, 4, 5, 6, 7, 15, 16 } {
            return 'Desktop'
        }
        { $_ -in 8, 9, 10, 11, 12, 14, 18, 21, 31, 32 } {
            return 'Notebook'
        }
        { $_ -in 30 } {
            return 'Tablet'
        }
        { $_ -in 17, 23 } {
            return 'Servidor'
        }
        Default {
        }
    }
}


#Function to get last logged user on remote computer
Function Get-LastUser {
try {
$ErrorActionPreference = "Stop"
Get-WmiObject Win32_LoggedOnUser -ComputerName $ComputerName | 
    Select Antecedent -Unique | 
    % { 
        $domain = $_.Antecedent.Split('"')[1]
        if($domain -eq "VLINET") {
            "{0}\{1}" -f $domain, $_.Antecedent.Split('"')[3]
        }
    } | Select-Object -First 1
    } catch [System.Runtime.InteropServices.COMException] 
    {
  
    }
    }


#Name of remote computer
$Name = 'Hostname'   $ComputerName

#Get last logged user (by function)
$LastLoggedUser = ((Get-LastUser).Split('\')[1])

#Last Boot Time
$LastBoot = (Get-CimInstance win32_operatingsystem -ComputerName $ComputerName |Select-Object CSName, LastBootUpTime | Select -ExpandProperty LastBootUpTime).tostring("dd/MM/yyyy hh:mm:ss")

#Chassis Type Of Computer
$ChassisType = Get-WUChassisType

#Operating System
$OS = (Get-CimInstance win32_operatingsystem -ComputerName $ComputerName).caption

#System Installed On
$SystemInstalledOn = ((Get-CimInstance Win32_OperatingSystem -ComputerName $ComputerName).InstallDate).tostring("dd/MM/yyyy hh:mm:ss")

#Processor
$Processor = (Get-CimInstance win32_processor -ComputerName $ComputerName -filter "deviceid='CPU0'").Name

#Disk
$Disk = (Get-CimInstance Win32_LogicalDisk -ComputerName $ComputerName | Select-Object @{Name="Size"; Expression={"$([math]::round($_.Size / 1GB,2))GB"}}).Size

#Ram Memory
$Ram = (Get-CimInstance Win32_PhysicalMemory -ComputerName $ComputerName | Select-Object @{Name="Capacity"; Expression={"$([math]::round($_.Capacity / 1GB,2))GB"}}).Capacity

#Serial Number
$SerialNumber = Get-CimInstance win32_bios -ComputerName $ComputerName | Select-Object -ExpandProperty SerialNumber

#Manufacturer
$Manufacturer = Get-CimInstance win32_ComputerSystemProduct -ComputerName $ComputerName | Select-Object -ExpandProperty Vendor

#Model
$Model = Get-CimInstance win32_ComputerSystemProduct -ComputerName $ComputerName | Select-Object -ExpandProperty Name

#Export to CSV
$Name   $LastLoggedUser   $ChassisType   $LastBoot   $OS   $SystemInstalledOn   $Processor   $Disk   $Ram   $SerialNumber   $Manufacturer   $Model | Export-Csv

#Dialog box to information finish script
[Microsoft.VisualBasic.Interaction]::MsgBox("Report is finished", "OKOnly,SystemModal,Information", "Success") | Out-Null```

CodePudding user response:

Here is an example of how I'd make an initial pass at changing your function with the primary changes being:

Multiple calls were being made to the same class, remotely. This is really inefficient. The calls were consolidated to store the result of the first call in a single variable and then reference the variable's properties for the additional information.

Your script is setting preferences, globally, a lot. All PowerShell cmdlets allow you to set the ErrorActionPreference inline, so setting it globally back and forth is unnecessary.

By prestaging an output object ($temp) and emitting it where the code quits processing, you can see the results of partial communications failures (e.g. pingable but WinRM off, etc.).

Your chassis function shouldn't have worked. Since chassisTypes returns an array, you can't use the -in operator to check if an array exists in an array. I used some code from another SO article that shows some pretty cool PowerShell on how to make this value report the chassis values accurately.

The CIM instance of Win32_LoggedOnUser returns the 'domain' and 'name' properties directly so you don't need to string-parse the values using splits and array references.

Examples of using it in the desired states you specified in the question are at the bottom (accepting multiple computer names and accepting them from the contents of a file (not with a popup, but I hope you'll see how not using GUIs will be more helpful)).

For the amount of independent remote calls you're making, if you identify that the code runs slower than you'd like, you might think about adapting it to use Invoke-Command. This would pass all of your code to the remote machine once, process it on the remote machine, and just return the output object. In my experience, this dramatically reduces the execution time of the script (e.g. I was able to pull information from thousands of servers spanning the globe in about 15 minutes using Invoke-Command, whereas individual remote calls took 12 hours or more).

Function Generate-ComputerHwReport {
    param(
        [Parameter(Mandatory=$true)] [string[]] [ValidateNotNullOrEmpty()] $ComputerNames ## This lines requires the passed in value to be an array of strings
    )
    
        ## Modification -- Looping through the array to check all computers passed in
        foreach ($ComputerName in $ComputerNames) {
    
            $temp = [pscustomobject] @{
                TestPing     = $false
                TestWinRM    = $false
                TestWSMan    = $false
                Hostname     = $ComputerName
                LastUser     = ''
                LastBootTime = ''
                ChassisType  = ''
                OS           = ''
                InstallDate  = ''
                Processor    = ''
                Disk         = ''
                Ram          = ''
                SerialNumber = ''
                Manufacturer = ''
                Model        = ''
                ErrorLog     = ''
            }  
    
            #Check computer online in network, if is not online, the hostname will be skipped but necessary add in log entry which computers is offline
            if (Test-Connection $ComputerName -Count 1 -Quiet) {
                $temp.TestPing = $true
            } else {
                $temp
                continue
            }
    
            #Validade crucial service that is crucial for get remote data, and if is not possible to get this information, the hostname will be skipped
            try {
                $Win32_OS = Get-CimInstance win32_operatingsystem -ComputerName $ComputerName -ErrorAction Stop
                $temp.TestWinRM = $true
                $temp.LastBootTime = $Win32_OS.LastBootUpTime.ToString("dd/MM/yyyy hh:mm:ss")
                $temp.OS = $Win32_OS.Caption
                $temp.InstallDate = $Win32_OS.InstallDate.ToString("dd/MM/yyyy hh:mm:ss")
            } catch {
                #Hostname will be skipped
                $temp
                continue
            }
    
    
            #Validate if the WS Management service is enabled on the remote device
            if ((Test-NetConnection $ComputerName -Port 5985).TcpTestSucceeded) {
                $temp.TestWSMan = $true   
            } else {
                $temp
                continue
            }
    
    
            #Function to create the Get-WUChassisType that is performed to find out if the Chassis of the equipment is Notebook or Desktop, and it is not configured to detect virtual machine
    
            ## https://stackoverflow.com/questions/55184682/powershell-getting-chassis-types-info
            $ChassisTypes = @{
                Name = 'ChassisType'
                Expression = {
                    # property is an array, so process all values
                    $result = foreach($value in $_.ChassisTypes)
                    {
                        switch([int]$value)
                        {
                            1          {'Other'}
                            2          {'Unknown'}
                            3          {'Desktop'}
                            4          {'Low Profile Desktop'}
                            5          {'Pizza Box'}
                            6          {'Mini Tower'}
                            7          {'Tower'}
                            8          {'Portable'}
                            9          {'Laptop'}
                            10         {'Notebook'}
                            11         {'Hand Held'}
                            12         {'Docking Station'}
                            13         {'All in One'}
                            14         {'Sub Notebook'}
                            15         {'Space-Saving'}
                            16         {'Lunch Box'}
                            17         {'Main System Chassis'}
                            18         {'Expansion Chassis'}
                            19         {'SubChassis'}
                            20         {'Bus Expansion Chassis'}
                            21         {'Peripheral Chassis'}
                            22         {'Storage Chassis'}
                            23         {'Rack Mount Chassis'}
                            24         {'Sealed-Case PC'}
                            default    {"$value"}
                        }
    
                    }
                    $result
                }
            }
    
            $temp.ChassisType = (Get-CimInstance -ClassName Win32_SystemEnclosure -ComputerName $ComputerName | Select-Object -Property $ChassisTypes).ChassisType
    
    
            #Function to get last logged user on remote computer
            try {
                $t = Get-CimInstance win32_loggedonuser -ComputerName $ComputerName -ErrorAction Stop | Select Antecedent -Unique
    
                $temp.LastUser = "{0}\{1}" -f $t.Antecedent.Domain, $t.Antecedent.Name
            } catch {
                $temp.ErrorLog  = $_
            }
    
    
            #Processor
            $temp.Processor = (Get-CimInstance win32_processor -ComputerName $ComputerName -filter "deviceid='CPU0'").Name
    
            #Disk
            $temp.Disk = (Get-CimInstance Win32_LogicalDisk -ComputerName $ComputerName | Select-Object @{Name="Size"; Expression={"$([math]::round($_.Size / 1GB,2))GB"}}).Size
    
            #Ram Memory
            $temp.Ram = (Get-CimInstance Win32_PhysicalMemory -ComputerName $ComputerName | Select-Object @{Name="Capacity"; Expression={"$([math]::round($_.Capacity / 1GB,2))GB"}}).Capacity
    
            #Serial Number
            $temp.SerialNumber = Get-CimInstance win32_bios -ComputerName $ComputerName | Select-Object -ExpandProperty SerialNumber
    
            #Manufacturer
            $temp.Manufacturer = Get-CimInstance win32_ComputerSystemProduct -ComputerName $ComputerName | Select-Object -ExpandProperty Vendor
    
            #Model
            $temp.Model = Get-CimInstance win32_ComputerSystemProduct -ComputerName $ComputerName | Select-Object -ExpandProperty Name
    
            $temp
        }
    }

    ## Output to console
    Generate-ComputerHwReport -ComputerNames localhost, pc2
    
    ## Output to console reading in the computer names from a file
    Generate-ComputerHwReport -ComputerNames (gc listofcomputernames.txt)
    
    ## Output to CSV reading in the computer names from a file
    Generate-ComputerHwReport -ComputerNames (gc listofcomputernames.txt) | Export-Csv -NoTypeInformation ComputerHwReport.csv

CodePudding user response:

edit: rewrote it a little for you, try the below. Note that the input csv expects a header called "ComputerName" and a list of computer names underneath that.

I copied the csv-to-excel part at the bottom from here

#Function to let user select a file then return the filepath.
Function Get-FileName($initialDirectory){
    [System.Reflection.Assembly]::LoadWithPartialName(“System.windows.forms”) | Out-Null
    $OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
    $OpenFileDialog.initialDirectory = $initialDirectory
    $OpenFileDialog.filter = “CSV Exports (*.csv)| *.csv”
    $OpenFileDialog.ShowDialog() | Out-Null
    $OpenFileDialog.FileName
}

$filepath = Get-FileName -initialDirectory $PSScriptRoot
$csvdata = Import-Csv -Path $filepath

#Function to create the Get-WUChassisType that is performed to find out if the Chassis of the equipment is Notebook or Desktop, and it is not configured to detect virtual machine
Function Get-WUChassisType {
    [CmdletBinding()]
    param ($ComputerName=$null)
    Set-StrictMode -Version 'Latest'
    $chassisTypes = Get-CimInstance Win32_SystemEnclosure -ComputerName $ComputerName | Select -ExpandProperty ChassisTypes
    switch ($chassisTypes) {
        { $_ -in 3, 4, 5, 6, 7, 15, 16 } {
            return 'Desktop'
        }
        { $_ -in 8, 9, 10, 11, 12, 14, 18, 21, 31, 32 } {
            return 'Notebook'
        }
        { $_ -in 30 } {
            return 'Tablet'
        }
        { $_ -in 17, 23 } {
            return 'Servidor'
        }
        Default {
            return ''
        }
    }
}

#Function to get last logged user on remote computer
Function Get-LastUser($ComputerName){
try {
Get-WmiObject Win32_LoggedOnUser -ComputerName $ComputerName | 
    Select Antecedent -Unique | 
    % { 
        $domain = $_.Antecedent.Split('"')[1]
        if($domain -eq "VLINET") {
            "{0}\{1}" -f $domain, $_.Antecedent.Split('"')[3]
        }
    } | Select-Object -First 1
    } catch [System.Runtime.InteropServices.COMException]{
  
    }
}

#We'll append all our individual pc resuls into this array
$exportObj = @()

#Check computer online in network, if is not online, the hostname will be skipped but necessary add in log entry which computers is offline
foreach ($row in $csvdata){
    if(Test-Connection $row.ComputerName -Count 1){
        $ciminfo = Get-CimInstance win32_operatingsystem -ComputerName $row.ComputerName -ErrorAction SilentlyContinue |Select-Object CSName, LastBootUpTime, Caption, InstallDate
        $sysinfo = Get-CimInstance win32_ComputerSystemProduct -ComputerName $row.ComputerName -ErrorAction SilentlyContinue | Select-Object Vendor, Name
        if ($ciminfo -and $sysinfo){
            $objPcResult = New-Object PSObject -Property @{
                Name              = 'Hostname: '   $row.ComputerName;
                ChassisType       = Get-WUChassisType -ComputerName $row.ComputerName;
                LastLoggedUser    = ((Get-LastUser -ComputerName $row.ComputerName).Split('\')[1]);
                LastBoot          = $ciminfo.LastBootUpTime.tostring("dd/MM/yyyy hh:mm:ss");
                OS                = $ciminfo.Caption;
                SystemInstalledOn = $ciminfo.InstallDate.tostring("dd/MM/yyyy hh:mm:ss");
                Processor         = (Get-CimInstance win32_processor -ComputerName $row.ComputerName -filter "deviceid='CPU0'").Name;
                Disk              = (Get-CimInstance Win32_LogicalDisk -ComputerName $row.ComputerName | Select-Object @{Name="Size"; Expression={"$([math]::round($_.Size / 1GB,2))GB"}}).Size;
                Ram               = (Get-CimInstance Win32_PhysicalMemory -ComputerName $row.ComputerName | Select-Object @{Name="Capacity"; Expression={"$([math]::round($_.Capacity / 1GB,2))GB"}}).Capacity;
                SerialNumber      = Get-CimInstance win32_bios -ComputerName $row.ComputerName | Select-Object -ExpandProperty SerialNumber;
                Manufacturer      = $sysinfo.Vendor;
                Model             = $sysinfo.Name;
            }

            #Add each PC results as a new row in our array
            $exportObj  = $objPcResult
        }
    }
}


#Setup our temp variables to save our collected data as a temporary CSV, so we can import it into Excel to save as an XLSX.
$tempcsv = "c:\temp\temp.csv" #Location of the source file
$xlsx = "c:\temp\output.xlsx" #Desired location of output
$delimiter = "," #Specify the delimiter used in the file

#Temp export our csv - to be converted to xlsx
$exportObj | Export-Csv -Path $tempcsv -NoTypeInformation

### Create a new Excel Workbook with one empty sheet
$excel = New-Object -ComObject excel.application 
$excel.Visible = $false
$workbook = $excel.Workbooks.Add(1)
$worksheet = $workbook.worksheets.Item(1)

# Build the QueryTables.Add command and reformat the data
$TxtConnector = ("TEXT;"   $tempcsv)
$Connector = $worksheet.QueryTables.add($TxtConnector,$worksheet.Range("A1"))
$query = $worksheet.QueryTables.item($Connector.name)
$query.TextFileOtherDelimiter = $delimiter
$query.TextFileParseType  = 1
$query.TextFileColumnDataTypes = ,1 * $worksheet.Cells.Columns.Count
$query.AdjustColumnWidth = 1

# Execute & delete the import query
$query.Refresh()
$query.Delete()

# Save & close the Workbook as XLSX.
$Workbook.SaveAs($xlsx,51)
$excel.Quit()
  • Related