header
header Register : : Login header
header
divider
menuleft
menuright
submenu
left

[August 25th, 2008] Check the home page regarding PowerShell related news from a brand new sponsor: Idera

Newbie: Best way gwmi from multiple objects to CSV
Last Post 17 Nov 2008 05:15 PM by Dods. 12 Replies.
Printer Friendly
Sort:
PrevPrev NextNext
You are not authorized to post a reply.
Author Messages
MattGUser is Offline
New Member
New Member
Posts:20
Avatar

--
11 Nov 2008 03:06 PM  

I am in the process of setting up a CMDB.  I want to feed it all of the relevant WMI data I can remotely gather from my servers.  Ultimately this is what I would like to do via Powershell:

  • Query AD for a list of servers to attempt to pull WMI data from
  • Iterate through each server and poll them for WMI data from different objects (ComputerSystem, OperatingSystem, etc)
  • Output a single line for each server to a CSV file using the names required for the CMDB

Once I have the file then I would have a scheduled job import the CSV file into the CMDB,  add any new records and merging any changes.

I have manually performed this process using the premium verison of LANSweeper with a custom report using joins between the tables on the ComputerName field,  converting the output to CSV and then importing it into the CMDB.

I have found the folliwng PS snippet (from: http://synjunkie.blogspot.com/2008/09/powershell-scripts-for-ad-management.html ):

gwmi -query "SELECT SystemName,Caption,VolumeName,Size,Freespace FROM win32_logicaldisk WHERE DriveType=3" -computername servanem | Select-Object SystemName,Caption,VolumeName,@{Name="Size(GB)"; Expression={"{0:N2}" -f ($_.Size/1GB)}},@{Name="Freespace(GB)"; Expression={"{0:N2}" -f ($_.Freespace/1GB)}}, @{n="% Free";e={"{0:P2}" -f ([long]$_.FreeSpace/[long]$_.Size)}} | sort "% Free" |export-csv -notype c:\Disk-GB.csv

Is it possible to do the SQL style joins via this Select statement so that I can output a single line at a time to the CSV,  or is there a better way to do this?

Thanks

-MattG

 

halr9000User is Offline
PowerShell MVP, Site Admin
Basic Member
Basic Member
Posts:334
Avatar

--
11 Nov 2008 09:05 PM  

Matt, I didn't get a chance to finish, but have a look at this. It may or may not work. :)

$outputFile = "c:\path\to\file.csv"

function New-ObjectWmiProperty ( $property ) {
	Process {
		$computer = $_
		switch ( $property ) {
			"SerialNumber" {
				$output = ( Get-WmiObject Win32_OperatingSystem -comp $computer.Name ).SerialNumber
				$computer | Add-Member -name SerialNumber -memberType NoteProperty `
					-value $output -passThru # passThru sends obj down pipeline
				}
			# repeat for each item to extract, using above template
		}
	}
}

# $ADComputers = Get-QADComputer # read help file for how to filter results here
$ADComputers = "." # for testing... can also do array e.g. "pc1", "pc2"
$output = $() # empty array
foreach ( $server in $ADComputers ) {
	$server | New-ObjectWmiProperty SerialNumber
	$server | New-ObjectWmiProperty AnotherKeyword
	$output += $server
} 
$output | Export-Csv $outputFile
Community Director, PowerShellCommunity.org
Co-host, PowerScripting Podcast
Author, TechProsaic
MattGUser is Offline
New Member
New Member
Posts:20
Avatar

--
12 Nov 2008 08:34 PM  

Hal,

Thanks for the reply.  I cannot figure out how to get the syntax of your suggestion to work?

Would something like this work:

#This retrieves the computer name of all computer accounts with NT or Server in their description where they are not disabled accounts:

$ADComputers = Get-QADComputer -ComputerRole 'Member' -ldapfilter '(&(!(userAccountControl:1.2.840.113556.1.4.803:=2))(|(operatingSystem=*Server*)(&(operatingSystem=*NT*))))'|select name

 

      foreach ( $server in $ADComputers ) {

$InstallDate = gwmi -query "SELECT InstallDate from win32_operatingsystem" -computername $server|select-object InstallDate
$Caption = gwmi -query "SELECT Caption from win32_operatingsystem" -computername $server|select-object Caption

#Need command here to concatenate the fields for output?

} 
$output | Export-Csv $outputFile

Thanks,
-MattG

MattGUser is Offline
New Member
New Member
Posts:20
Avatar

--
12 Nov 2008 11:32 PM  

I am thinking it might be best to just do a csv per query and then perform the CMDB imports with the various CSVs.

Here is an example of an OS specific CSV that was created with all enabled Windows Servers listed in AD:

Get-QADComputer -ComputerRole 'Member' -ldapfilter '(&(!(userAccountControl:1.2.840.113556.1.4.803:=2))(|(operatingSystem=*Server*)(&(operatingSystem=*NT*))))' | foreach {

$name = $_.name
$ping = gwmi win32_pingstatus -filter "address='$name'"

if($ping.statusCode -eq 0){
$srv = gwmi win32_operatingSystem -computer $name
$srv | select CSName,ServicePackMajorVersion,BuildNumber
}

} | export-csv c:\os.csv -noType


2 questions:
1. How do I change the column names for the exported csv data. My CMDB expects specific column headers.
2. When I run this command I get a handled of connect errors, how I log these error to a log. The errors come up in PS when I execute this command, but don't show the server that had an issue.

Thanks,
-MattG

MattGUser is Offline
New Member
New Member
Posts:20
Avatar

--
13 Nov 2008 04:19 PM  
I figured out how to format the column names to match the CMDB names:

Get-QADComputer -ldapfilter '(&(!(userAccountControl:1.2.840.113556.1.4.803:=2))(|(operatingSystem=*Server*)(&(operatingSystem=*NT*))))' | foreach {

$name = $_.name
$ping = gwmi win32_pingstatus -filter "address='$name'"

if($ping.statusCode -eq 0){
$srv = gwmi win32_operatingSystem -computer $name
$srv | select @{Name="Name";Expression={$_.Csname}},@{Name="SerialNo";Expression={$_.SerialNumber}},@{Name="BuildNo";Expression={$_.BuildNumber}}

} | export-csv c:\os.csv -noType


I made this a osinv.ps1 file and tried to execute it with:
powershell ./osinv

And it complains that Get-QADComputer "not recognized". If I modify the script to just do a GWMI against a specific computer the PS executes fine. If I execute the script PowerGUI script editor it works fine.

Why can't I execute this from the Windows Command Prompt and have it use the QAD snapin? Do I need to explicitly reference it in the PS1 script?

Thanks,
-MattG
halr9000User is Offline
PowerShell MVP, Site Admin
Basic Member
Basic Member
Posts:334
Avatar

--
13 Nov 2008 04:24 PM  
Matt, you do need to reference it in a scirpt if you have not done so in your profile. PowerGUI has its own snapin selection mechanism, and the shortcut you click on to start powershell probably is adding the Quest snapin as well. When you run powershell.exe, the only startup script which is run is your profile(s).

Include this line in your script, or add it to your $profile (try notepad $profile)

add-pssnapin Quest.ActiveRoles.ADManagement
Community Director, PowerShellCommunity.org
Co-host, PowerScripting Podcast
Author, TechProsaic
MattGUser is Offline
New Member
New Member
Posts:20
Avatar

--
13 Nov 2008 04:49 PM  

Hal,

Thanks.  That works.

-MattG

MattGUser is Offline
New Member
New Member
Posts:20
Avatar

--
13 Nov 2008 06:25 PM  
The previous script now works for a single domain. However, now I need to get it to work against an AD subdomain. I have already been able to get the script to run against either domain using the Connect-QADServer -Service SrvName command. However, I need to get the script to query for the both domains then loop through the combined list. My current thought would be to:
- Connect to the AD Srv and run the GETQADComoputers command and output just the Servername to a computers.txt file using out-file
-- Connect to the other AD Srv and run the GETQADComoputers command and append the output just the Servername to the same computers.txt file using out-file.

If this is the best way, how do I remove the heading that is outputed when running the command?
How could I modify my above For Each loop to read the computers.txt as it's input?

Thanks,
-Matt
MattGUser is Offline
New Member
New Member
Posts:20
Avatar

--
13 Nov 2008 06:40 PM  

I figured it out:

add-pssnapin Quest.ActiveRoles.ADManagement
 

Connect-QADService -Service DCSRV1
$AD = Get-QADComputer -ldapfilter '(&(!(userAccountControl:1.2.840.113556.1.4.803:=2))(|(operatingSystem=*Server*)(&(operatingSystem=*NT*))))'
 

Connect-QADService -Service DCSRV2
$AD += Get-QADComputer -ldapfilter '(&(!(userAccountControl:1.2.840.113556.1.4.803:=2))(|(operatingSystem=*Server*)(&(operatingSystem=*NT*))))'
 

$AD |foreach {
    
    $name = $_.name
    $ping = gwmi win32_pingstatus -filter "address='$name'"
    
    if($ping.statusCode -eq 0){
    $srv = gwmi win32_operatingSystem -computer $name
    $srv | select @{Name="Name";Expression={$_.Csname}},@{Name="os_serial";Expression={$_.SerialNumber}},@{Name="Os_Swproduct_Id";Expression={$_.Caption}},@{Name="ad_ou";Expression={$_.Caption}}
      }
  } | export-csv c:\newos.csv -noType

 

 

Thanks,

-MattG

halr9000User is Offline
PowerShell MVP, Site Admin
Basic Member
Basic Member
Posts:334
Avatar

--
13 Nov 2008 07:33 PM  
I was going to say something like that. :) Everything is an object, don't forget! The only time you should dump to a file is when you need that file for working with something outside of PowerShell, or perhaps for a separate powershell script running in a different session. And in those cases you may want to check out export-clixml.
Community Director, PowerShellCommunity.org
Co-host, PowerScripting Podcast
Author, TechProsaic
MattGUser is Offline
New Member
New Member
Posts:20
Avatar

--
14 Nov 2008 02:20 PM  

So here is my current code. I was able to assign each Select-Object filter to a variable which make my code easier to understand when I need to make changes. This code works.

Now I want to make it easier to include/exclude WMI fields from the query/output. Ideally the code would be setup so that the max fields are incldued in the code, and the user can comment out one line and the field would be excluded. I have tried to make the $field an array, but it complains that the "Name" field is already in use. I have tried to make the $fields strings, but that doesn't work when I try to use the variable down in the select statement. If I can figure out the mechanics of addind the appropriate variables to the Select statement, then I can figure out the IF THEN logic. Any suggestions? Thanks, -MattG

 

add-pssnapin add-pssnapin Quest.ActiveRoles.ADManagement
    

Connect-QADService -Service DCSRV1
    $AD = Get-QADComputer -ldapfilter '(&(!(userAccountControl:1.2.840.113556.1.4.803:=2))(|(operatingSystem=*Server*)(&(operatingSystem=*NT*))))'
    Connect-QADService -Service DCSRV2
    $AD += Get-QADComputer -ldapfilter '(&(!(userAccountControl:1.2.840.113556.1.4.803:=2))(|(operatingSystem=*Server*)(&(operatingSystem=*NT*))))'


$field1 = @{Name="name";Expression={$_.Csname}}
$field2 = @{Name="os_serial";Expression={$_.SerialNumber}}
$field3 = @{Name="Os_Swproduct_Id";Expression={$_.Caption}}
$field4 = @{Name="ad_ou";Expression={$_.Caption}}
$field5 = @{Name="os_patch_level";Expression= {$_.csdversion}}
$field6 = @{Name="installdate";Expression= {$_.ConvertToDateTime($_.installdate)}}
$field7 = @{Name="lastbootuptime";Expression= {$_.ConvertToDateTime($_.lastbootuptime)}}
$field8 = @{Name="othertypedescription";Expression= {$_.othertypedescription}}
$field9 = @{Name="os_organization";Expression= {$_.organization}}
$field10 = @{Name="os_registereduser";Expression= {$_.registereduser}}


$AD |foreach {
    
    $name = $_.name
    $ping = gwmi win32_pingstatus -filter "address='$name'"
    
    if($ping.statusCode -eq 0){
    $srv = gwmi win32_operatingSystem -computer $name
    
    $srv | select $field1,$field2,$field3,$field4,$field5,$field6,$field7,$field8,$field9,$field10
      }

    
  } | export-csv e:\easycmdb\import\csv\newos.csv -noType

ShayUser is Offline
Basic Member
Basic Member
Posts:271
Avatar

--
16 Nov 2008 09:51 PM  

Hi Matt

I have two suggestions, one is good and the other is even better IMO.

You can include all select's calculated properties as a collection and pass that collection to select object. $field1 = @{Name="name";Expression={$_.Csname}}
$field2 = @{Name="os_serial";Expression={$_.SerialNumber}}
(...)
$fields = $field1,$field2,$field3,$field4,$field5,$field6,$field7,$field8,$field9,$field10

... | select $fields

That way your users need to remove just the field(s) they don't want in the output, including the comma (see bolded text).

The second, and my favorite way... Teach your script users how to use the format-* cmdlets. You're already asking them to edit the script, so I'm sure that using the format-* cmdlets would be easier and much fun for them. You will need to remove the export-csv call so they can run the script and pipe (function) and pipe the results to format-table:
 

PS > .\script.ps1 | format-table $field1,$field6,$field7,$field10 

Here is my version of your script, keep in mind that the ldap query part (operatingSystem=*NT*) will capture the following values also, be sure to fine tune it:

Windows NT
Windows VistaT Enterprise

 
Also add -sizeLimit 0 to Get-QADUser to bypass the default 1000 objects limitation. 

$filter = '(&(!(userAccountControl:1.2.840.113556.1.4.803:=2))(|(operatingSystem=*Server*)(&(operatingSystem=*NT*))))'
$field1 = @{Name="name";Expression={$_.Csname}}
$field2 = @{Name="os_serial";Expression={$_.SerialNumber}}
$field3 = @{Name="Os_Swproduct_Id";Expression={$_.Caption}}
$field4 = @{Name="ad_ou";Expression={$_.Caption}}
$field5 = @{Name="os_patch_level";Expression= {$_.csdversion}}
$field6 = @{Name="installdate";Expression= {$_.ConvertToDateTime($_.installdate)}}
$field7 = @{Name="lastbootuptime";Expression= {$_.ConvertToDateTime($_.lastbootuptime)}}
$field8 = @{Name="othertypedescription";Expression= {$_.othertypedescription}}
$field9 = @{Name="os_organization";Expression= {$_.organization}}
$field10 = @{Name="os_registereduser";Expression= {$_.registereduser}}
$fields = $field1,$field2,$field3,$field4,$field5,$field6,$field7,$field8,$field9,$field10

"DCSRV1","DCSRV2" | foreach {
    $null = Connect-QADService -Service $_
    Get-QADComputer -ldap $filter -sizeLimit 0 | foreach {$_.name}  | where { (gwmi win32_pingstatus -filter "address='$_'").statusCode -eq 0 } | foreach {
    gwmi win32_operatingSystem -computer $_ | select $fields
} | export-csv e:\easycmdb\import\csv\newos.csv -noType

Shay Levy
Windows PowerShell MVP
http://blogs.microsoft.co.il/blogs/ScriptFanatic
DodsUser is Offline
New Member
New Member
Posts:12
Avatar

--
17 Nov 2008 05:15 PM  
Hello having some interest in this script and playing with it a bit I noticed that $field4 capturing the OU object would fail because the pipeline destroys that object early on.

To overcome this problem just amend / add to the above script with these changes.

$field4 = @{Name="ad_ou";Expression={$ad_ou}}


$QAD = @{}

Get-QADComputer -ldap $filter -sizeLimit 0 | % {$QAD."$($_.name)" = $_ ; $_.name} | where { (gwmi win32_pingstatus -filter "address='$_'").statusCode -eq 0 } |
foreach {
$ad_ou = $QAD.$_
gwmi win32_operatingSystem -computer $_ | select $fields
} | export-csv c:\newos.csv -noType


hope this works.
You are not authorized to post a reply.

Active Forums 4.1
right
   
footer Sponsored by Quest Software • SAPIEN Technologies • ShellTools, LLC • Microsoft Windows Server 2008 footer
footer