Server Inventory - Custom Objects

This topic contains 7 replies, has 3 voices, and was last updated by  Brian Clanton 1 week, 6 days ago.

  • Author
    Posts
  • #81379

    Brian Clanton
    Participant

    I have composed a basic script that will query a AD group and then grab BIOS info from a list of servers. I created a custom object that populates the object with a server's BIOS info which then will add the object to an array.

    Problem is, the array is never populated and I have an empty array at the end of it.

    Here is the script.

    $asp12Servers = (Get-ADGroupMember Group-asp12-Servers).name
    
    $BIOSReport = @()
    
    foreach ($server in $asp12Servers){
        
            $BIOSInfo = Get-WmiObject win32_bios -ComputerName $server -Credential $credential
    
            $object = New-Object PSObject
            Add-Member -InputObject $object -MemberType NoteProperty -Name Server -Value $server
            Add-Member -InputObject $object -MemberType NoteProperty -Name BIOS -Value $BIOSInfo.SMBIOSBIOSVersion
            Add-Member -InputObject $object -MemberType NoteProperty -Name Manufacturer -Value $BIOSInfo.Manufacturer
            Add-Member -InputObject $object -MemberType NoteProperty -Name SerialNumber -Value $BIOSInfo.SerialNumber
            Add-Member -InputObject $object -MemberType NoteProperty -Name Version -Value $BIOSInfo.Version 
       
        $BIOSReport += $object  
        
    }
        $BIOSReport

    When I check the $BIOSReport variable there is no data. When I add the $object variable just above where it says "$BIOSReport += $object to the script, I see that $object is in fact populating. However, when I type $BiosReport, there are no objects in the array.

    Snippet of $object output

    Server       : W2K8-C12-14
    BIOS         : 1.11.0
    Manufacturer : Dell Inc.
    SerialNumber : 1HR2NL1
    Version      : DELL   - 1
    
    Server       : W2K8-C12-15
    BIOS         : 1.4.6
    Manufacturer : Dell Inc.
    SerialNumber : 400HSW1
    Version      : DELL   - 1
    
    Server       : W2K8-C12-16
    BIOS         : 1.6.3
    Manufacturer : Dell Inc.
    SerialNumber : 5LCQXQ1
    Version      : DELL   - 1
    
    Server       : W12-R12-01S
    BIOS         : 2.4.2
    Manufacturer : Dell Inc.
    SerialNumber : 97Q4J02
    Version      : DELL   - 1
  • #81382

    Don Jones
    Keymaster

    Yeah, you really shouldn't do it that way :).

    https://devops-collective-inc.gitbooks.io/the-big-book-of-powershell-gotchas/content/manuscript/accumulating-output-in-a-function.html

    What you really want to do it write a function that emits each object, immediately, to the pipeline. If you want to use the objects for something else, you can pipe from the first function to a second function, and so on. The pipeline is literally designed to handle all of this for you, and does so faster and with less memory consumption than what you're attempting.

    • #81392

      Brian Clanton
      Participant

      One aspect I failed to mention and what seems to be significant is that I am running this code from a remote session to a domain controller. The script itself resides on the domain controller.

      Just for testing purposes, I ran the same code I showed above from the desktop of the DC as opposed to my remote PS session and actually got output to stick inside the $BiosREport Array.

      Snippet of output from local server DC without any changes.

      PS C:\Users\administrator.XXXXX\Documents> $BIOSReport
      
      
      Server       : W2K8-R12-01
      BIOS         : 2.4.2
      Manufacturer : Dell Inc.
      SerialNumber : 8JVLCY1
      Version      : DELL   - 1
      
      Server       : W2K8-R12-02
      BIOS         : 2.4.2
      Manufacturer : Dell Inc.
      SerialNumber : 88T09Y1
      Version      : DELL   - 1
      
      Server       : W2K8-R12-03
      BIOS         : 2.4.2
      Manufacturer : Dell Inc.
      SerialNumber : 1STGH02
      Version      : DELL   - 1

      To see if I could run this from a remote session, I followed the advice of the article you showed me and created the script as a function which outputs the Server info to the pipeline first and then to a variable. It is mandatory that I capture this as an array for eventually exporting to a csv or html file later, so after I call the function, I assign to a variable and then append that to an array. However, from a remote session I still receive no data in my $BIOSReport array. I tried both with a Return and Write-Object statement in my function (see below). However, when i run this directly from the server Desktop it is on, the $BioSReport array populates without issue.

      Am I following the instructions of the article correctly?

      From Remote PSSession, this script will not populate the BIOSInfo array. When I run it directly from the server, it will. I am authenticated to the server via PSSession with the same credentials that I am running from the server desktop. The script itself is on the server

      function Get-BiosInfo($Server){
              $BIOSInfo = Get-WmiObject win32_bios -ComputerName $server -Credential $credential
      
              $object = New-Object PSObject
              Add-Member -InputObject $object -MemberType NoteProperty -Name Server -Value $server
              Add-Member -InputObject $object -MemberType NoteProperty -Name BIOS -Value $BIOSInfo.SMBIOSBIOSVersion
              Add-Member -InputObject $object -MemberType NoteProperty -Name Manufacturer -Value $BIOSInfo.Manufacturer
              Add-Member -InputObject $object -MemberType NoteProperty -Name SerialNumber -Value $BIOSInfo.SerialNumber
              Add-Member -InputObject $object -MemberType NoteProperty -Name Version -Value $BIOSInfo.Version
      
         Return $object
         #Write-output $object
       }
      
      
      $asp12Servers = (Get-ADGroupMember Group-asp12-Servers).name 
      $BIOSReport = @()
      
      foreach ($Server in $asp12Servers){
          
          $ServerBIOS =  Get-BiosInfo ($Server) 
          $BIOSReport += $ServerBIOS
      }
  • #81394

    Dan Potter
    Participant

    Double hop isn't allowed.

    A couple personal preferences. pscustomobject formatted like this is a bit easier. Don't declare empty arrays and add to them, just put a variable in front of the foreach.

    
    function Get-BiosInfo{
    
    param($server)
    
            $BIOSInfo = Get-WmiObject win32_bios -ComputerName $server 
    
            [pscustomobject]@{
            Server = $server
            BIOS = $BIOSInfo.SMBIOSBIOSVersion
            Manufacturer = $BIOSInfo.Manufacturer
            SerialNumber = $BIOSInfo.SerialNumber
            Version = $BIOSInfo.Version
    
    		}
    
      
     }
    
    
    $asp12Servers = (Get-ADGroupMember Group-asp12-Servers).name 
    
    
    $biosinfo = foreach ($Server in $asp12Servers){
        
        Get-BiosInfo $Server 
        
    }
    
    
    
    $biosinfo
    
    
    
  • #81397

    Don Jones
    Keymaster

    Yeah, +1 on the double-hop. See, "Secrets of PowerShell Remoting" ebook. Remote to server is one hop, when it tries to go anyplace else, it can't. Your credential can't go any further than the 1st hop by default. And +1 to Dan's example.

  • #81407

    Brian Clanton
    Participant

    For my own edification and to make this a complete thread in case anyone else reads it, I found a way to get this info without having to go to the DC's desktop.

    I do a direct One to one PsSession from my Desktop PS to the DC and then run a script that will create a session to all of my terminal servers and then just run 'invoke-command' to the session variable.

    I wonder if one can definitively say that PSSession is a good work around for the limitations of the -Computername parameter when one has to overcome the 'double-hop' situation when gathering info or executing commands on multiple servers.

    From a direct PSsessoin on my desktop to the DC

    $TSSession = (Get-ADGroupMember Group-asp12-Servers).name | New-PSSession -Credential $credential
    
    $BIOSReport = Invoke-Command -Session $TSSession -ScriptBlock {Get-WmiObject win32_bios}
    
    $BIOSReport | Format-Table PSComputerName, SerialNumber, SMBIOSBIOSVersion, Manufacturer, SerialNumber

    Snippet of output

    PSComputerName SerialNumber SMBIOSBIOSVersion Manufacturer SerialNumber
    -------------- ------------ ----------------- ------------ ------------
    W2K8-R12-02    88T09Y1      2.4.2             Dell Inc.    88T09Y1     
    W2K8-R12-03    1STGH02      2.4.2             Dell Inc.    1STGH02     
    W2K8-R12-04    8JWKCY1      2.4.2             Dell Inc.    8JWKCY1     
    W2K8-R12-05    4W287Y1      2.4.2             Dell Inc.    4W287Y1     
    W2K8-R12-10    H5T71Y1      2.4.2             Dell Inc.    H5T71Y1     
    W2K8-R12-24    5V7MW12      2.4.2             Dell Inc.    5V7MW12     
    W2K8-R12-09    HZFRGS1      1.12.0            Dell Inc.    HZFRGS1     
    W2K8-C12-02    4W1B7Y1      2.4.2             Dell Inc.    4W1B7Y1     
    W2K8-R12-32    16T71Y1      2.4.2             Dell Inc.    16T71Y1     
    W2K8-R12-14    6DYRPN1      1.9.0             Dell Inc.    6DYRPN1     
    W12-R12-01S    97Q4J02      2.4.2             Dell Inc.    97Q4J02     
    W2K8-R12-16    JZFRGS1      1.9.0             Dell Inc.    JZFRGS1     
    W2K8-R12-11    3VDYFX1      2.4.2             Dell Inc.    3VDYFX1     
    W2K8-R12-31    400KSW1      2.4.2             Dell Inc.    400KSW1 
  • #81410

    Don Jones
    Keymaster

    A PSSession is Remoting; Remoting is exactly where the double-hop problem can engage. What you've done in this instance simply avoids creating a double hop.

    What you've done is create a Remoting session to one or more computers, and had each of those computers then run a local command. That's different and in many ways better than connecting to one remote computer, and having IT attempt to connect to multiple other computers. But this isn't a "limitation" of any -ComputerName parameter. It's entirely about the process you're following. You could have done the exact same thing by simply feeding your computer names to the -ComputerName parameter of Invoke-Command; all you've done is spun up the sessions in advance. Had you not, it would have implicitly created PSSessions for you anyway. Same thing.

    Invoke-Command -ComputerName (Get-ADGroupMember Group-asp12-Servers).name -ScriptBlock {Get-WmiObject win32_bios} |
    Format-Table
    

    As a note, if you'd used -AsJob on that Invoke-Command, you could have sat back and waited for it to finish, working on something else, and then retrieved all the results when it was done. Fun trick.

    I'll hope that you did indeed remove the PSSessions when you were done, and simply omitted that line here :).

  • #81416

    Brian Clanton
    Participant

    My company is starting to realize the benefits of the reporting and automation I can do for them and I would 'ideally' like to work directly from my desktop that has 4 huge monitors rather than have use our remoting tool to go to a server's desktop where I have to work on a virtual screen the size of a stamp.

    My machine is on a different domain than our Server environment, which is why I do a PSsessoin to a machine on that domain so my commands will recognize the hostnames on that same network. However, this brings up challenges like this where my script may require remote connections to multiple servers.

    My 'best idea' to work around the over head of rewriting code to overcome the 'double-hop' scenario is to write a small script that will gather IP addresses from the SErver Active Directory groups and then automatically copy those list of ip addresses in a text file to my local machine.

    My scripts/commands/functions that I would use would be based on those IP address lists and then remoting with whatever method I want as long as I submit the right credentials.

    I am not sure what other techs do in similar situations like this who do ALOT of powershell administration for their organizations.

You must be logged in to reply to this topic.