ADSI query running well on some boxes and failing on others - powershell

I am working on a PowerShell script for a customer to pull information from Servers before they are decommissioned. I have had to jump through a couple of hoops, as they have 2008 and even some 2003 servers, which don't always support the same Get-CimInstance queries, but for the most part it is working as expected. One section I have as yet been unable to fix however is pulling a list of all local groups and their members, like so:
$localGroups = Invoke-Command -ScriptBlock {
[ADSI]$S = "WinNT://$($env:computername)"
$S.children.Where({$_.class -eq 'group'}) |
Select #{Name="Name";Expression={$_.name.value}},
#{Name="Members";Expression={
[ADSI]$group = "$($_.Parent)/$($_.Name),group"
$members = $Group.psbase.Invoke("Members")
($members | ForEach-Object {$_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null)}) -join ";"
}
}
} -ComputerName $sPC | Select PSComputername,Name,Members
This works just fine on Server 2012 and above, but fails on 2008 and below. The specific error states:
Method invocation failed because [System.DirectoryServices.DirectoryEntries] doesn't contain a method named 'Where'
So the OS does not like the $S.Children.Where() call. Just curious if anyone knows of another way to format it for use with 2008 and earlier. I could probably go the old WMI call route, but that takes forever to finish.

I ended up finding a (fairly) quick way of doing it, works on all OS flavors:
function get-groups {
param ($strcomputer)
$groups = gwmi win32_group -ComputerName $strcomputer
return $groups
}
function Get-LocalGroupMembers {
param(
[parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
[Alias("Name")]
[string]$ComputerName,
[string]$GroupName = "Administrators"
)
begin {}
process {
$ComputerName = $ComputerName.Replace("`$", '')
$arr = #()
$wmi = Get-WmiObject -ComputerName $ComputerName -Query "SELECT * FROM Win32_GroupUser WHERE GroupComponent=`"Win32_Group.Domain='$ComputerName',Name='$GroupName'`""
if ($wmi -ne $null) {
foreach ($item in $wmi) {
$arr += ($item.PartComponent.Substring($item.PartComponent.IndexOf(',') + 1).Replace('Name=', '').Replace("`"", ''))
}
}
$hash = #{ComputerName=$ComputerName;Members=$arr}
return $hash
}
end{}
}
$sPC = "Server2008R2"
$a = 1
$adapterInfo = Get-CimInstance -query "Select * From
Win32_NetworkAdapterConfiguration Where IPenabled = 'TRUE'" -ComputerName $sPC
$serialNumber = Get-CimInstance -query "Select * From Win32_Bios" -ComputerName $sPC
$shares = Get-CimInstance -query "Select * From Win32_Share" -ComputerName $sPC
$localGroups = get-groups $sPC
$myObject = New-Object -TypeName PSObject
$myObject | Add-Member -MemberType NoteProperty -Name ComputerName -Value $adapterInfo.PSComputerName
$myObject | Add-Member -MemberType NoteProperty -Name Description -Value $adapterInfo.Description
$adapterInfo | ForEach-Object {
$_.IPAddress |
ForEach-Object {
$myObject | Add-Member -MemberType NoteProperty -Name "IPAddress$($a)" -Value $_
$a++
}
}
$myObject | Add-Member -MemberType NoteProperty -Name SerialNumber -Value $serialNumber.SerialNumber
foreach ($share in $shares) {
if ($share.Name -ne "C$" -and
$share.Name -ne "D$" -and
$share.Name -ne "ADMIN$" -and
$share.Name -ne "IPC$") {
$myObject | Add-Member -MemberType NoteProperty -Name "Share $($share.Name)" -Value $share.Path
}
}
foreach ($group in $localGroups) {
$members = ((Get-LocalGroupMembers -ComputerName $sPC -GroupName $group.Name).Members) -join "; "
if ($members.Length -ne 0) {
$myObject | Add-Member -MemberType NoteProperty -Name "$($group.Name) Members" -Value $members
}
}
$myObject | Export-Csv -Path "<Path to Folder>\$($sPC).csv" -NoTypeInformation

Related

Assign local variable within scriptblock

I am trying to assign a local variable from within a scriptblock with no luck. The goal is log a status of each machine for the action taken with the data prior and the data after the change. I am not sure how to assign a local variable from within a script block. Any help is much appreciated.
$csvContents = #() # Create the empty array that will eventually be the CSV file
$Computers = Get-ADComputer -Filter '(OperatingSystem -like "Windows Server*") -and (Name -like "AD*")' | Sort-Object Name
foreach ($Computer in $Computers) {
$row = New-Object PSObject # Create an object to append to the array
$row | Add-Member -MemberType NoteProperty -Name "ComputerName" -Value NotSet
$row | Add-Member -MemberType NoteProperty -Name "PingStatus" -Value NotSet
$row | Add-Member -MemberType NoteProperty -Name "DNSChangeStatus" -Value NotSet
$row | Add-Member -MemberType NoteProperty -Name "BeforeChange" -Value NotSet
$row | Add-Member -MemberType NoteProperty -Name "AfterChange" -Value NotSet
#Write-Host "$($Computer.Name): " -ForegroundColor Yellow
$row.ComputerName = $Computer.Name
$rtn = Test-Connection -CN $Computer.dnshostname -Count 1 -BufferSize 16 -Quiet
if ($rtn -match 'True') {
Write-Host -ForegroundColor Green $Computer.DnsHostname
$row.PingStatus = 'Pingable'
Invoke-Command -ComputerName $Computer.Name -ScriptBlock {
$NewDnsServerSearchOrder = "10.93.108.225","10.93.108.134"
$Adapters = Get-WmiObject Win32_NetworkAdapterConfiguration | Where-Object {$_.DHCPEnabled -ne 'True' -and $_.DNSServerSearchOrder -eq "10.93.108.226"}
if ($Adapters -ne $null) {
# Show DNS servers before update
Write-Host "Before: " -ForegroundColor Green
$row.DNSChangeStatus = 'Change Needed'
$Adapters | ForEach-Object {
$_.DNSServerSearchOrder
$row.BeforeChange = $_.DNSServerSearchOrder
}
# Update DNS servers
$Adapters | ForEach-Object {$_.SetDNSServerSearchOrder($NewDnsServerSearchOrder)} | Out-Null
# Show DNS servers after update
$Adapters = Get-WmiObject Win32_NetworkAdapterConfiguration | Where-Object {$_.DHCPEnabled -ne 'True' -and $_.DNSServerSearchOrder -ne $null}
Write-Host "After: " -ForegroundColor Green
$Adapters | ForEach-Object {
$_.DNSServerSearchOrder
$row.AfterChange = $_.DNSServerSearchOrder
}
} else {
Write-Host "No DNS change needed " -ForegroundColor Yellow
$row.DNSChangeStatus = 'No DNS Change Needed'
}
}
} else {
Write-Host -ForegroundColor Red $Computer.DnsGostname
Write-Host -ForegroundColor Red "Host not pingable"
$row.PingStatus = 'Not Pingable'
}
$csvContents += $row # append the new data to the array
$row = $null
}
$csvContents | Export-Csv -Path C:\DNSChanges.csv
I can't tell exactly what you want to do so I'll guess it's "pass a variable to scriptblock being invoked on a remote machine". If this is the case, you can either add parameters to the scriptblock or use the $using: qualifier as in:
$using:row.DNSChangeStatus = 'Change Needed'
Note that you can't "return" anything that way so you'd have to actually return the modified object from Invoke-Command.
$row = Invoke-Command -ComputerName $Computer.Name -ScriptBlock {
and add
$row
as the last line of the scriptblock.

How can i get the disk details in csv format in windows server using powershell

Hi all I want to get details in below format
Hostname Drive_0 Drive_1 Drive_2
Name C: 99.899227142334 d: 99.899227142334 e: 99.899227142334
I can get this detail using below script but this works on PowerShell 3.0
how can I change to execute on PowerShell 2.0
$result = #()
$obj = new-object PSobject
$server = hostname
$obj |Add-Member -MemberType NoteProperty -Name "Hostname" -Value $server -ErrorAction SilentlyContinue
$z = Get-WmiObject -Class win32_Logicaldisk -Filter 'DriveType=3' | Select-Object -Property DeviceID, #{LABEL='TotalSize';EXPRESSION={$_.Size/1GB}}
$z3 = $z.DeviceID
$z4 = $z.TotalSize
$i = 0
foreach($z3 in $z3){
$z1 = $z.DeviceID[$i]
$z2 = $z.TotalSize[$i]
$zx = "$z1" + ": $z2"
$obj | Add-Member -MemberType NoteProperty -Name "Drive_$i" -Value $zx -ErrorAction SilentlyContinue
$i++
}
$result+=$obj
$result | Export-Csv "$env:userprofile\Desktop\Result.csv" -NoTypeInformation
You can change your code to the below. It's a bit neater and sorts out your loops and limits so you don't need to manage them
$result = #()
$obj = new-object PSobject
$server = hostname
$obj |Add-Member -MemberType NoteProperty -Name "Hostname" -Value $server -ErrorAction SilentlyContinue
$z = Get-WmiObject -Class win32_Logicaldisk -Filter 'DriveType=3' | Select-Object -Property DeviceID, #{LABEL='TotalSize';EXPRESSION={$_.Size/1GB}}
$i = 0
$z | % {
$z1 = $_.DeviceID
$z2 = $_.TotalSize
$zx = "$z1" + ": $z2"
$obj | Add-Member -MemberType NoteProperty -Name "Drive_$i" -Value $zx
$i++
}
$result+=$obj
$result | Export-Csv "$env:userprofile\Desktop\Result.csv" -NoTypeInformation
I also removed the -ErrorAction off the Add-Member as you should try and handle anything that crops up yourself, but add it back if you feel the need to.

Powershell script to see currently logged in users (domain and machine) + status (active, idle, away)

I am searching for a simple command to see logged on users on server.
I know this one :
Get-WmiObject -Class win32_computersystem
but this will not provide me the info I need.
It returns :
domain
Manufactureer
Model
Name (Machine name)
PrimaryOwnerName
TotalPhysicalMemory
I run Powershell 3.0 on a Windows 2012 server.
Also
Get-WmiObject Win32_LoggedOnUser -ComputerName $Computer | Select Antecedent -Unique
gives me not the exact answers I need.
I would love to see as well the idle time, or if they are active or away.
In search of this same solution, I found what I needed under a different question in stackoverflow:
Powershell-log-off-remote-session. The below one line will return a list of logged on users.
query user /server:$SERVER
Since we're in the PowerShell area, it's extra useful if we can return a proper PowerShell object ...
I personally like this method of parsing, for the terseness:
((quser) -replace '^>', '') -replace '\s{2,}', ',' | ConvertFrom-Csv
Note: this doesn't account for disconnected ("disc") users, but works well if you just want to get a quick list of users and don't care about the rest of the information. I just wanted a list and didn't care if they were currently disconnected.
If you do care about the rest of the data it's just a little more complex:
(((quser) -replace '^>', '') -replace '\s{2,}', ',').Trim() | ForEach-Object {
if ($_.Split(',').Count -eq 5) {
Write-Output ($_ -replace '(^[^,]+)', '$1,')
} else {
Write-Output $_
}
} | ConvertFrom-Csv
I take it a step farther and give you a very clean object on my blog.
I ended up making this into a module.
There's no "simple command" to do that. You can write a function, or take your choice of several that are available online in various code repositories. I use this:
function get-loggedonuser ($computername){
#mjolinor 3/17/10
$regexa = '.+Domain="(.+)",Name="(.+)"$'
$regexd = '.+LogonId="(\d+)"$'
$logontype = #{
"0"="Local System"
"2"="Interactive" #(Local logon)
"3"="Network" # (Remote logon)
"4"="Batch" # (Scheduled task)
"5"="Service" # (Service account logon)
"7"="Unlock" #(Screen saver)
"8"="NetworkCleartext" # (Cleartext network logon)
"9"="NewCredentials" #(RunAs using alternate credentials)
"10"="RemoteInteractive" #(RDP\TS\RemoteAssistance)
"11"="CachedInteractive" #(Local w\cached credentials)
}
$logon_sessions = #(gwmi win32_logonsession -ComputerName $computername)
$logon_users = #(gwmi win32_loggedonuser -ComputerName $computername)
$session_user = #{}
$logon_users |% {
$_.antecedent -match $regexa > $nul
$username = $matches[1] + "\" + $matches[2]
$_.dependent -match $regexd > $nul
$session = $matches[1]
$session_user[$session] += $username
}
$logon_sessions |%{
$starttime = [management.managementdatetimeconverter]::todatetime($_.starttime)
$loggedonuser = New-Object -TypeName psobject
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Session" -Value $_.logonid
$loggedonuser | Add-Member -MemberType NoteProperty -Name "User" -Value $session_user[$_.logonid]
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Type" -Value $logontype[$_.logontype.tostring()]
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Auth" -Value $_.authenticationpackage
$loggedonuser | Add-Member -MemberType NoteProperty -Name "StartTime" -Value $starttime
$loggedonuser
}
}
Maybe you can do something with
get-process -includeusername
If you want to find interactively logged on users, I found a great tip here :https://p0w3rsh3ll.wordpress.com/2012/02/03/get-logged-on-users/ (Win32_ComputerSystem did not help me)
$explorerprocesses = #(Get-WmiObject -Query "Select * FROM Win32_Process WHERE Name='explorer.exe'" -ErrorAction SilentlyContinue)
If ($explorerprocesses.Count -eq 0)
{
"No explorer process found / Nobody interactively logged on"
}
Else
{
ForEach ($i in $explorerprocesses)
{
$Username = $i.GetOwner().User
$Domain = $i.GetOwner().Domain
Write-Host "$Domain\$Username logged on since: $($i.ConvertToDateTime($i.CreationDate))"
}
}
Here is my Approach based on DarKalimHero's Suggestion by selecting only on Explorer.exe processes
Function Get-RdpSessions
{
param(
[string]$computername
)
$processinfo = Get-WmiObject -Query "select * from win32_process where name='explorer.exe'" -ComputerName $computername
$processinfo | ForEach-Object { $_.GetOwner().User } | Sort-Object -Unique | ForEach-Object { New-Object psobject -Property #{Computer=$computername;LoggedOn=$_} } | Select-Object Computer,LoggedOn
}
Another solution, also based on query user, but can handle variations in culture (as far as I can tell) and produces strongly-typed results (i.e. TimeSpan and DateTime values):
# Invoke "query user", it produces an output similar to this, but might be culture-dependant!
#
# USERNAME SESSIONNAME ID STATE IDLE TIME LOGON TIME
# >jantje rdp-tcp#55 2 Active . 3/29/2021 4:24 PM
# pietje 4 Disc 49+01:01 4/14/2021 9:26 AM
$result = (&query 'user' | Out-String -Stream)
# Take the header text and insert a '|' before the start of every HEADER - although defined as inserting a bar after
# every 2 or more spaces, or after the space at the start.
$fencedHeader = $result[0] -replace '(^\s|\s{2,})', '$1|'
# Now get the positions of all bars.
$fenceIndexes = ($fencedHeader | Select-String '\|' -AllMatches).Matches.Index
$timeSpanFormats = [string[]]#("d\+hh\:mm", "h\:mm", "m")
$entries = foreach($line in $result | Select-Object -Skip 1)
{
# Insert bars on the same positions, and then split the line into separate parts using these bars.
$fenceIndexes | ForEach-Object { $line = $line.Insert($_, "|") }
$parts = $line -split '\|' | ForEach-Object { $_.Trim() }
# Parse each part as a strongly typed value, using the UI Culture if needed.
[PSCustomObject] #{
IsCurrent = ($parts[0] -eq '>');
Username = $parts[1];
SessionName = $parts[2];
Id = [int]($parts[3]);
State = $parts[4];
IdleTime = $(if($parts[5] -ne '.') { [TimeSpan]::ParseExact($parts[5], $timeSpanFormats, [CultureInfo]::CurrentUICulture) } else { [TimeSpan]::Zero });
LogonTime = [DateTime]::ParseExact($parts[6], "g", [CultureInfo]::CurrentUICulture);
}
}
# Yields the following result:
#
# IsCurrent Username SessionName Id State IdleTime LogonTime
# --------- -------- ----------- -- ----- -------- ---------
# True jantje rdp-tcp#32 2 Active 00:00:00 3/29/2021 4:24:00 PM
# False pietje 4 Disc 48.11:06:00 4/14/2021 9:26:00 AM
$entries | Format-Table -AutoSize
Team!
I have pretty nice solution to get local session as [PSObject].
Function Get-LocalSession {
<#
.DESCRIPTION
Get local session. Pasre output of command - 'query session'.
#>
[OutputType([PSObject[]])]
[CmdletBinding()]
Param(
)
try {
#region functions
#endregion
$Result = #()
$Output = . query.exe 'session' | select-object -skip 1
#use regex to parse
$pattern = '^(?<This>.)(?<SessionName>[^\s]*)\s*(?<UserName>[a-z]\w*)?\s*(?<Id>[0-9]*)\s*(?<State>\w*)\s*((?<Type>\w*)\s*)?(?<Device>\w*)?'
foreach ( $line in $output ){
$match = [regex]::Matches( $line, $pattern )
if ( $match ){
$PSO = [PSCustomObject]#{
This = $match[0].groups['This'].Value
SessionName = $match[0].groups['SessionName'].Value
UserName = $match[0].groups['UserName'].Value
Id = $match[0].groups['Id'].Value
State = $match[0].groups['State'].Value
Type = $match[0].groups['Type'].Value
Device = $match[0].groups['Device'].Value
}
$Result += $PSO
}
Else {
write-host "Unable to process line [$line] in function [Get-LocalSession]!"
}
}
}
catch {
#Get-ErrorReporting -Trap $PSItem
write-host $PSItem
}
return $Result
}
#Run it
$SessionObject = Get-LocalSession
$SessionObject | format-table -autosize -property *
I have edited mjolinor script to remove duplicate records, and dummy account names such as system, network services,...etc
If you want to get all users
function get-loggedonuser ($computername){
$regexa = '.+Domain="(.+)",Name="(.+)"$'
$regexd = '.+LogonId="(\d+)"$'
$logontype = #{
"0"="Local System"
"2"="Interactive" #(Local logon)
"3"="Network" # (Remote logon)
"4"="Batch" # (Scheduled task)
"5"="Service" # (Service account logon)
"7"="Unlock" #(Screen saver)
"8"="NetworkCleartext" # (Cleartext network logon)
"9"="NewCredentials" #(RunAs using alternate credentials)
"10"="RemoteInteractive" #(RDP\TS\RemoteAssistance)
"11"="CachedInteractive" #(Local w\cached credentials)
}
$logon_sessions = #(gwmi win32_logonsession -ComputerName $computername)
$logon_users = #(gwmi win32_loggedonuser -ComputerName $computername)
$session_user = #{}
$logon_users |% {
$_.antecedent -match $regexa > $nul
$username = $matches[1] + "\" + $matches[2]
$_.dependent -match $regexd > $nul
$session = $matches[1]
$session_user[$session] += $username
}
$logon_sessions |%{
$starttime = [management.managementdatetimeconverter]::todatetime($_.starttime)
if ($session_user[$_.logonid] -notin $loggedonuser.user -and $session_user[$_.logonid] -notlike "*$*"){
$loggedonuser = New-Object -TypeName psobject
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Session" -Value $_.logonid
$loggedonuser | Add-Member -MemberType NoteProperty -Name "User" -Value $session_user[$_.logonid]
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Type" -Value $logontype[$_.logontype.tostring()]
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Auth" -Value $_.authenticationpackage
$loggedonuser | Add-Member -MemberType NoteProperty -Name "StartTime" -Value $starttime
$loggedonuser
}
}
}
if you want to have only domain users
function get-loggedonuser ($computername){
$HST= hostname
$regexa = '.+Domain="(.+)",Name="(.+)"$'
$regexd = '.+LogonId="(\d+)"$'
$logontype = #{
"0"="Local System"
"2"="Interactive" #(Local logon)
"3"="Network" # (Remote logon)
"4"="Batch" # (Scheduled task)
"5"="Service" # (Service account logon)
"7"="Unlock" #(Screen saver)
"8"="NetworkCleartext" # (Cleartext network logon)
"9"="NewCredentials" #(RunAs using alternate credentials)
"10"="RemoteInteractive" #(RDP\TS\RemoteAssistance)
"11"="CachedInteractive" #(Local w\cached credentials)
}
$logon_sessions = #(Get-WmiObject win32_logonsession -ComputerName $computername)
$logon_users = #(Get-WmiObject win32_loggedonuser -ComputerName $computername)
$session_user = #{}
$logon_users |ForEach-Object {
$_.antecedent -match $regexa > $nul
$username = $matches[1] + "\" + $matches[2]
$_.dependent -match $regexd > $nul
$session = $matches[1]
$session_user[$session] += $username
}
$logon_sessions |ForEach-Object{
if ($session_user[$_.logonid] -notin $loggedonuser.user -and $session_user[$_.logonid] -notlike "*$*" -and $session_user[$_.logonid] -notlike "*$HST*"){
$loggedonuser = New-Object -TypeName psobject
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Session" -Value $_.logonid
$loggedonuser | Add-Member -MemberType NoteProperty -Name "User" -Value $session_user[$_.logonid]
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Type" -Value $logontype[$_.logontype.tostring()]
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Auth" -Value $_.authenticationpackage
$loggedonuser | Add-Member -MemberType NoteProperty -Name "StartTime" -Value $starttime
$loggedonuser
}
}
}
This is what I just figured out and works out great!
Get-Process -IncludeUserName | Select-Object -Unique | Where-Object {$_.UserName -notlike 'NT AUTHORITY\SYSTEM' -and $_.UserName -notlike 'NT AUTHORITY\NETWORK SERVICE' -and $_.UserName -notlike 'NT AUTHORITY\LOCAL SERVICE'} | Format-Table -Wrap -AutoSize

Obtaining Required columns in HTML output in Powershell

I obtained the BIOS data and saved it in HTML file but i don't want all the data to be in the file and i also need to format it so that it could suite my requirement.
Code:
$bios = get-wmiobject -class "Win32_BIOS" -namespace "root\CIMV2"
[string]$body = $bios | ConvertTo-Html -As List| Out-File c:/d/d/Test.htm
So based on the discussion in the comments, I think you are looking for something like this:
function GetCompInfoWork
{
param (
[string]$computername,[string]$logfile
)
$os = Get-WmiObject win32_operatingsystem -ComputerName $computername
$bios = Get-WmiObject win32_bios -ComputerName $computername
$disk = Get-WmiObject win32_logicalDisk -Filter "DeviceID= 'C:'" `
-computername $computername
$obj = New-Object -TypeName PSObject
$obj | Add-Member -MemberType NoteProperty `
-Name ComputerName -Value ($os.csname)
$obj | Add-Member -MemberType NoteProperty `
-Name Manufacturer -Value ($bios.manufacturer)
$obj | Add-Member -MemberType NoteProperty `
-Name SysDriveFree -Value ($disk.freespace / 1GB -as [int])
$obj | Add-Member -MemberType NoteProperty `
-Name SerialNumber -Value ($bios.serialnumber)
$obj | Add-Member -MemberType NoteProperty `
-Name Version -Value ($bios.version)
Write-Output $obj
}
function Get-CompInfo
{
param ([string[]]$computername,[string]$logfile )
BEGIN
{
$usedParamater = $False
if ($PSBoundParameters.ContainsKey('computername')) {
$usedParamater = $True
}
}
PROCESS {
if ($usedParamater)
{
foreach ($computer in $computername)
{
Getcompinfowork -computername $computer `
-logfile $logfile
}
}
else
{
Getcompinfowork -computername $_ `
-logfile $logfile
}
}
END {}
}
Get-CompInfo -computername localhost | ConvertTo-Html | Out-File C:\Output.html
Try this:
$freeSpace = (gwmi Win32_logicaldisk -Filter "DeviceID = 'C:'").FreeSpace
gwmi Win32_BIOS `
| select PSComputerName,Manufacturer,SerialNumber,Version,
#{n="AvailableSpace";e={[int]($freeSpace / 1MB)}} `
| ConvertTo-Html `
| Out-File "C:/d/d/Test.htm"

How to get name and in a sequence in Powershell

When I run the below code I get an error with Model Number in which the heading is not being displayed and one more thing is i am unable to get all the three of them in a sequence.
$arrComputers = get-Content -Path "C:\Desktop\Computers.txt"
$e=$arrComputers | ForEach-Object {Get-WMIObject -Class Win32_BIOS -ComputerName $_ } |Select PSComputerName, Version,Manufacturer,Status,BIOSVersion,SerialNumber |ConvertTo-Html -fragment
$e2=$arrComputers |ForEach-Object { get-wmiobject -class Win32_logicaldisk -Filter "DeviceID = 'C:'" -ComputerName $_ } | select freeSpace,size | ConvertTo-Html -fragment
$e3=$arrComputers |ForEach-Object { get-wmiobject -class "Win32_ComputerSystem" -ComputerName $_ } | select Model| ConvertTo-Html -fragment
ConvertTo-HTML -Body "$e $e2 $e3" -Title "List of Computers" | Out-File C:\Users\Desktop\gf.html
Its a lot easier to make all of the WMI calls into a single object. It is much easier to handle formatting. I think I got everything that you were wanting:
function GetCompInfoWork
{
param (
[string]$computername,[string]$logfile
)
$pc = Get-WmiObject Win32_ComputerSystem -ComputerName $computername
$bios = Get-WmiObject win32_bios -ComputerName $computername
$disk = Get-WmiObject win32_logicalDisk -Filter "DeviceID= 'C:'" `
-computername $computername
$obj = New-Object -TypeName PSObject
$obj | Add-Member -MemberType NoteProperty `
-Name PSCompName -Value ($bios.PSComputerName)
$obj | Add-Member -MemberType NoteProperty `
-Name Version -Value ($bios.version)
$obj | Add-Member -MemberType NoteProperty `
-Name Manufacturer -Value ($bios.manufacturer)
$obj | Add-Member -MemberType NoteProperty `
-Name Status -Value ($bois.status)
$obj | Add-Member -MemberType NoteProperty `
-Name SerialNumber -Value ($bios.serialnumber)
$obj | Add-Member -MemberType NoteProperty `
-Name DiskSize -Value ($disk.size / 1GB -as [int])
$obj | Add-Member -MemberType NoteProperty `
-Name SysDriveFree -Value ($disk.freespace / 1GB -as [int])
$obj | Add-Member -MemberType NoteProperty `
-Name ComputerName -Value ($pc.model)
Write-Output $obj
}
function Get-CompInfo
{
param ([string[]]$computername,[string]$logfile )
BEGIN
{
$usedParamater = $False
if ($PSBoundParameters.ContainsKey('computername')) {
$usedParamater = $True
}
}
PROCESS {
if ($usedParamater)
{
foreach ($computer in $computername)
{
Getcompinfowork -computername $computer `
-logfile $logfile
}
}
else
{
Getcompinfowork -computername $_ `
-logfile $logfile
}
}
END {}
}
Get-Content C:\Users\Kev\Desktop\computers.txt| Get-CompInfo | ConvertTo-Html | Out-File C:\Users\kev\Desktop\Output.html