none
Powershell: Getting IP v4 Address on a multihomed system

    Question

  • Hello,
    I started setting up a Powershell script as Shutdown script for the clients in the domain. It's last task is writing some inventory information into a file on a shared folder, which it does. The problem: I need to take note of the IP v4 IP addresses and export this information into a csv file. Current syntax to get the related data:

    $ip = get-wmiobject win32_networkadapterconfiguration -filter "ipenabled=true"
    $ip = $ip.ipaddress[0]

    works well for most machines here, but a few are running wired and wireless connection at the same time.
    Here the attempt addressing $ip.ipaddress[0] results in error "Cannot index into a null array." (Validated this by having a user disabling WLAN adapter and rerunning the script - no error any more.)

    The result in the log (since $ErrorActionPreference = 'SilentlyContinue' is set) looked pretty strange for the field IP address: \\PCNAME\root\cimv2:Win32_NetworkAdapterConfiguration.Index=7 \\PCNAME\root\cimv2:Win32_NetworkAdapterConfiguration.Index=11

    Now I hit a brain lock, so any help is welcome. The ideal scenario would be, that the script delivers the IP v4 addresses of each network adapter as a complete comma separated string (since the database, which needs to consume this value at a later step, has only one field for this property, i.e. 192.168.1.100,192.168.1.50).

    Best greetings from Germany
    Olaf

    Wednesday, November 17, 2010 2:43 PM

Answers

  • Or you can do it this way without having to get twice the same object out which one would be just used for iteration :(

    $colItems = Get-WmiObject Win32_NetworkAdapterConfiguration -Namespace "root\CIMV2" -ComputerName $strComputer | where{$_.IPEnabled -eq "True"}

    foreach($objItem in $colItems) {

    if($objItem.IPAddress.Length -gt 1)

    {

     write-Host "IP Address:" ([array]($objItem.IPAddress[0]))
    }

    else

    {

     Write-Host "IP Address:" $objItem.IPAddress

    }

    • Proposed as answer by Singh_Varun1 Thursday, June 28, 2012 10:44 PM
    • Edited by Singh_Varun1 Thursday, June 28, 2012 10:56 PM syntax for greater than was not accurate
    • Marked as answer by Olaf EngelkeMVP Friday, June 29, 2012 11:25 AM
    Thursday, June 28, 2012 10:43 PM
  • Looks nice, but this delivers also the unwanted IP v.6 address.

    Best greetings from Germany
    Olaf

    So would all of the other solutions if the didn't stop enumerating the addresses.  It is possible to have more than one IPv4 address on an interface.

    Just post-filter for the address type required.

    Get-WmiObject Win32_NetworkAdapterConfiguration `
                  -ComputerName $strComputer `
                  -filter 'IPEnabled="True"' |
       Select -expand IPAddress | ?{$_ -notmatch ':'}

    PowersHell mkes all of this very easy. Of course you might want to add some other items like system name and adapther labe so the structure would need to be expanded but the method and flow would remain the same.

    The following will get all systems in a file into a nice report.

    $computers=Get-Content computers.txt
    Get-WmiObject Win32_NetworkAdapterConfiguration `
                  -ComputerName $computers `
                  -filter 'IPEnabled="True"' |
       %{
          $server=$_.DNSHostName
          $adapter=$_.Caption
          $_.IPAddress | %{
             New-Object PSObject -Property @{Server=$server;Adapter=$adapter;IPAddress=$_}
          }
        } | ?{$_ -notmatch ':'}

    Here is the same method fully flushed out.  It was weasy to grow because the base code lends itself to enhancements.  The onlly piece to add is to process errors and generate records for systems that do not respond.

    (unfortunately teh code colorizer breaks whenwe use line extensions so bear with the color issues.)

    function Get-SystemAddresses{
         [CmdLetBinding()]
         Param(
              [string[]]$computers=$env:computername,
              [string]$filter='XXXX' # dummy value
         )
         Get-WmiObject Win32_NetworkAdapterConfiguration `
                  -ComputerName $computers `
                  -ErrorAction 0 `
                  -filter 'IPEnabled="True"' |
             ForEach-Object{
                  $server=$_.DNSHostName
                  $adapter=$_.Caption
                  Write-Verbose "Processing system:$server"
                  $_.IPAddress | 
                       ForEach-Object{
                            Write-Verbose "Adding new address:$_"
                            New-Object PSObject -Property @{Server=$server;Adapter=$adapter;IPAddress=$_}
                       }
             } | 
             Where-Object{$_ -notmatch $filter}
    }
    $computers=Get-Content computers.txt
    Get-SystemAddresses -computers $computers -filter ':' -v


    ¯\_(ツ)_/¯



    • Edited by jrv Friday, June 29, 2012 8:37 AM
    • Marked as answer by Olaf EngelkeMVP Friday, June 29, 2012 11:26 AM
    Friday, June 29, 2012 8:13 AM
  • When there is more than one Win32_NetworkAdapterConfiguration instance, PowerShell returns the results as an array, so you need to iterate it:

     

    $ipCol = get-wmiobject win32_networkadapterconfiguration -filter "ipenabled=true"
    
    $ipCol.Length
    
    foreach($ip in $ipCol)
    {
      $ip.ipaddress[0]
    }
    
    

    Uros Calakovic
    Wednesday, November 17, 2010 3:49 PM
    Moderator

All replies

  • When there is more than one Win32_NetworkAdapterConfiguration instance, PowerShell returns the results as an array, so you need to iterate it:

     

    $ipCol = get-wmiobject win32_networkadapterconfiguration -filter "ipenabled=true"
    
    $ipCol.Length
    
    foreach($ip in $ipCol)
    {
      $ip.ipaddress[0]
    }
    
    

    Uros Calakovic
    Wednesday, November 17, 2010 3:49 PM
    Moderator
  • Hm, just lost my last posting and your posting is no longer marked as answer!? (And I cannot post from within IE 9 Beta to the forums.)

    With your help I got the script to run properly also in these strange configurations with following code:

    $lan = get-wmiobject win32_networkadapterconfiguration -filter "ipenabled=true"
    $lancount =  get-wmiobject win32_networkadapterconfiguration -filter "ipenabled=true"
    $ip=""
    foreach($lan in $lancount){$ip = $ip + "," + [string]($lan.ipaddress[0])}
    $ip = $ip.substring(1)

    Thanks a lot!

    Best greetings from Germany
    Olaf

     

    Thursday, November 18, 2010 11:11 AM
  • Or you can do it this way without having to get twice the same object out which one would be just used for iteration :(

    $colItems = Get-WmiObject Win32_NetworkAdapterConfiguration -Namespace "root\CIMV2" -ComputerName $strComputer | where{$_.IPEnabled -eq "True"}

    foreach($objItem in $colItems) {

    if($objItem.IPAddress.Length -gt 1)

    {

     write-Host "IP Address:" ([array]($objItem.IPAddress[0]))
    }

    else

    {

     Write-Host "IP Address:" $objItem.IPAddress

    }

    • Proposed as answer by Singh_Varun1 Thursday, June 28, 2012 10:44 PM
    • Edited by Singh_Varun1 Thursday, June 28, 2012 10:56 PM syntax for greater than was not accurate
    • Marked as answer by Olaf EngelkeMVP Friday, June 29, 2012 11:25 AM
    Thursday, June 28, 2012 10:43 PM
  • Singh - closed topic but...

    You mssed that one by a bit:

    Get-WmiObject Win32_NetworkAdapterConfiguration `
                  -ComputerName $strComputer `
                  -filter 'IPEnabled="True"' |
       Select -expand IPAddress

    This will do it all on one line with no inter-logic.

    If you have a question start a new topic.

    Remember - PowerShell is not a linear system.


    ¯\_(ツ)_/¯

    Friday, June 29, 2012 12:13 AM
  • Looks nice, but this delivers also the unwanted IP v.6 address.

    Best greetings from Germany
    Olaf

    Friday, June 29, 2012 7:57 AM
  • Looks nice, but this delivers also the unwanted IP v.6 address.

    Best greetings from Germany
    Olaf

    So would all of the other solutions if the didn't stop enumerating the addresses.  It is possible to have more than one IPv4 address on an interface.

    Just post-filter for the address type required.

    Get-WmiObject Win32_NetworkAdapterConfiguration `
                  -ComputerName $strComputer `
                  -filter 'IPEnabled="True"' |
       Select -expand IPAddress | ?{$_ -notmatch ':'}

    PowersHell mkes all of this very easy. Of course you might want to add some other items like system name and adapther labe so the structure would need to be expanded but the method and flow would remain the same.

    The following will get all systems in a file into a nice report.

    $computers=Get-Content computers.txt
    Get-WmiObject Win32_NetworkAdapterConfiguration `
                  -ComputerName $computers `
                  -filter 'IPEnabled="True"' |
       %{
          $server=$_.DNSHostName
          $adapter=$_.Caption
          $_.IPAddress | %{
             New-Object PSObject -Property @{Server=$server;Adapter=$adapter;IPAddress=$_}
          }
        } | ?{$_ -notmatch ':'}

    Here is the same method fully flushed out.  It was weasy to grow because the base code lends itself to enhancements.  The onlly piece to add is to process errors and generate records for systems that do not respond.

    (unfortunately teh code colorizer breaks whenwe use line extensions so bear with the color issues.)

    function Get-SystemAddresses{
         [CmdLetBinding()]
         Param(
              [string[]]$computers=$env:computername,
              [string]$filter='XXXX' # dummy value
         )
         Get-WmiObject Win32_NetworkAdapterConfiguration `
                  -ComputerName $computers `
                  -ErrorAction 0 `
                  -filter 'IPEnabled="True"' |
             ForEach-Object{
                  $server=$_.DNSHostName
                  $adapter=$_.Caption
                  Write-Verbose "Processing system:$server"
                  $_.IPAddress | 
                       ForEach-Object{
                            Write-Verbose "Adding new address:$_"
                            New-Object PSObject -Property @{Server=$server;Adapter=$adapter;IPAddress=$_}
                       }
             } | 
             Where-Object{$_ -notmatch $filter}
    }
    $computers=Get-Content computers.txt
    Get-SystemAddresses -computers $computers -filter ':' -v


    ¯\_(ツ)_/¯



    • Edited by jrv Friday, June 29, 2012 8:37 AM
    • Marked as answer by Olaf EngelkeMVP Friday, June 29, 2012 11:26 AM
    Friday, June 29, 2012 8:13 AM
  • Thanks, looks really pretty and gives the proper results.

    The only benefit of the first solution was, that I could easily integrate the ; as delimiter for the database import, if needed with more than one address.

    Best greetings from Germany
    Olaf

    Friday, June 29, 2012 8:43 AM
  • Thanks, looks really pretty and gives the proper results.

    The only benefit of the first solution was, that I could easily integrate the ; as delimiter for the database import, if needed with more than one address.

    Best greetings from Germany
    Olaf

    So why can't you do that with this version. It willexport to a delimited file just perfectly.

    Get-SystemAddresses -computers $computers -filter ':' -v | Export-Csv data.cav -NoType

    That is one of the reasons to do this correctly.  It can be ealy built upon such as:

    Get-SystemAddresses -computers $computers -filter ':' -v | ConvertTo-Html | Out-File report.htm
    .\report.htm

    We can even add color and images to the report.

    We can save th object and later restore it:

    Get-SystemAddresses -computers $computers -filter ':' | Export-CliXml objects.xml
    $addresses=Import-CliXml objects.xml

    Want a plain text report?

    Get-SystemAddresses -computers $computers -filter ':' | Format-Table -group server -auto

    All of this is possible because we used PowerSHell as it was intended and doin't just dump arbitrary text to teh screem.  Note that the Write-Host output i snot usable in any way except as a message to the screen.

    Always keep everything as objects until the very end then convert to what yuo need.


    ¯\_(ツ)_/¯


    • Edited by jrv Friday, June 29, 2012 9:26 AM
    Friday, June 29, 2012 9:24 AM
  • Hi,

    the IP address is only a part of the data to be exported.

    The last 2 lines of the script look as follows:

    $allinfo=[string]$computer.name+"`t"+[string]$computer.model+"`t"+[string]$serial+"`t"+[string]$processor+"`t"+[string]$cpuspeed+"`t"+[string]$physmem+"`t"+[string]$disksize+"`t"+[string]$os+"`t"+[string]$ip+"`t"+[string]$ou+"`t"+[string]$lastuser+"`t"+$team+"`t"+[string]$timestamp+"`t Inventory Script`t1"
    write-output $allinfo | format-table | out-file $logfile -encoding ASCII

    (This file from each computer will later be merged to a table containing the data from all computers and then used to update an Access database. With export-csv there were some issues - however cannot remember those any more, but I think it had to do with some computers having different sets of information, so a delimiter of some kind - in this case tab - was needed for proper alignment of columns after merging.)

    Best greetings from Germany
    Olaf

    Friday, June 29, 2012 9:57 AM
  • Why use tabs when a CSV is importable into all databases.

    Besides you are adding things that have nothing to do with the original question. The technique posted can do what you are looking for very easily.

    The original question states that the output is to a CSV file.  Your last post does not show any use of a CSV file.

    The original post says nothing about any other items except the IPAddress.

    YOu have a number of issues to deal with.  The IPAddress is an array of addresses.  An Adapter can have zero or more addresses.  The addreses can be a mix of IPv4 and IPv6. 

    I have adapters with as many as 5 addresses so they can communicate ( for diagnostic purposes) with multiple subnets.

    None of what you have posted in information that is available on the adapter configuration.  Are you sure you are not referring to a different post?

    In any case all of that can be collected in exactly the same way and returned as an object that can be exported to a database.


    ¯\_(ツ)_/¯

    Friday, June 29, 2012 10:42 AM
  • Why use tabs?

    Because there have been some fields returned content with commas or similar usually used delimiters, depending from the hardware. And tabs are one of the supported delimiters for easy Access import.

    The original question states that the output is to a CSV file.

    What is a CSV file? Its a text file, with the extension .CSV. So $logfile in ASCII encoding (other format made diffiulties with informing monitor WMI information into Access) is only the collection of fields, which I have for one computer. Another script merges them with the header and the files resulting from all other computers into the final CSV file.

    The original post says nothing about any other items. 

    Thats also not true. I wrote "It's last task is writing some inventory information into a file on a shared folder". And in this context I run into a problem with the IP addresses as part of this inventory collection, which has been solved by applying the original answerers proposal (more than 1.5 years ago). But sure, seeing a more elegant way as you and Singh provided, I am willing to adapt this method - but again run into the semicolon issue (and I don't want to change the format again, since other scripts and the database import now rely on this ...

    I have no need for any secondary or 3rd IP addresses on the same adapter, the primary IP address is well enough (it's client machines we are logging with the shutdown script for asset change tracking, so in our environment this is not an issue).

    Also you may know for sure, that designing a script for some purpose is a think in developement, so later informations or needs may be there, which were not available in the beginning.

    Best greetings from Germany
    Olaf


    Friday, June 29, 2012 11:24 AM
  • #1 - A CSV file is not a text file with delimiters.  Iti s a very special file that has a specific structure that allows fields ot have commas and ohte elements that would not normallybe allowed.  Access can import CSV files and, in fact, is a perferred methos.

    #2 The promary IP can be had in the same code by making one small change. The method Singh produced will not produce output because it uses write-host.

    $ipaddress=(Get-WmiObject Win32_NetworkAdapterConfiguration -filter 'IPEnabled="True"').IPAddress[0]

    Will always get the first IPAddress and does not throw an exception when there is only one address.  It will not work if there is more than one adapter so you will need to specify the adapter index which is availble on the adapter object.

    $ipaddress=(Get-WmiObject Win32_NetworkAdapterConfiguration -filter 'Index=8' AND IPEnabled="True").IPAddress[0]

    This one will always get only the primary address.  It will always work as long as you target only configurations that have IP enabled.

    Whenever possible use the filter an d not the 'Where' construct as it is more efficient and more accurate. If you do not use IPEnabled then yuo will get null addresses and that will cause you an issue.  There is absolutely no reason to get an exception doing this unless you are doing something incorrectly.


    ¯\_(ツ)_/¯

    Friday, June 29, 2012 12:00 PM