Regex Match Multi-line Cisco Switch Config

Here’s the goal:

  • Using PowerShell, parse a multiple Cisco switch configs and:
  • Extract the hostname
  • Extract the name of all interfaces
  • If the interface description exists, extract
  • If the interface ip address exists, extract
  • If the ip helper exists, extract
Here's a sample string:

!
hostname SOME-HOSTNAME-01
!

!
interface Loopback0
ip address 192.168.1.1 255.255.255.255
ip ospf network point-to-point
!
interface FastEthernet0/0
description Some-Sample-DescriptionWithOdd/Char@s
ip address 192.168.1.2 255.255.255.0
ip helper-address 192.168.1.3
ip helper-address 192.168.1.4
duplex auto
speed auto
!
interface FastEthernet0/1
no ip address
shutdown
duplex auto
speed auto
!

Here’s the script sample:

$config = Get-Content -Path $configPath

$hostname = $config| Select-String -Pattern ‘(?smi)^hostname (?<hostname>\S*)’

$psobject = New-Object -TypeName PSObject

$psobject | Add-Member -MemberType NoteProperty -Name hostname -Value ($hostname.matches.groups | Where name -eq ‘hostname’).value

$interfaceStatements = $config| Select-String -Pattern ‘(?m)^interface[^!]*’ -AllMatches | Foreach {$.Matches} | Foreach {$.Value}

 

When I return the value of $interfaceStatements, I was expecting to see a multiline string that ends right before the ‘!’, but I’m only seeing the first line in each:

interface Loopback0
interface FastEthernet0/0
interface FastEthernet0/1

The problem is that by default Get-Content reads each line of an input file into a new element in an array, so your $config variable is actually an array with each line as an element. You can verify this by adding

$config.Count

after the Get-Content line.
With your example input, this returns 23. Your Select-String regex is working correctly, but it is only getting the lines of the input file one at a time as separate array elements, rather than the entire block of text all at once.

You can fix this by adding the -Raw parameter to Get-Content:

$config = Get-Content -Path $configPath -Raw

In my testing, this returned the expected output (with no other changes to the script):

interface Loopback0
ip address 192.168.1.1 255.255.255.255
ip ospf network point-to-point

interface FastEthernet0/0
description Some-Sample-DescriptionWithOdd/Char@s
ip address 192.168.1.2 255.255.255.0
ip helper-address 192.168.1.3
ip helper-address 192.168.1.4
duplex auto
speed auto

interface FastEthernet0/1
no ip address
shutdown
duplex auto
speed auto

Try something like this:

$txt = @"
!
hostname SOME-HOSTNAME-01
!
...
!
interface Loopback0
ip address 192.168.1.1 255.255.255.255
ip ospf network point-to-point
!
interface FastEthernet0/0
description Some-Sample-DescriptionWithOdd/Char@s
ip address 192.168.1.2 255.255.255.0
ip helper-address 192.168.1.3
ip helper-address 192.168.1.4
duplex auto
speed auto
!
interface FastEthernet0/1
no ip address
shutdown
duplex auto
speed auto
!
"@

#Emulate Get-Content
$config = $txt -split [environment]::NewLine

$hostname = $config | Select-String -Pattern '^hostname (?<HostName>.*)$'
$interface = $config | Select-String -Pattern '^interface (?<Interface>.*)$'

[pscustomobject]@{
    HostName = $hostname.Matches[0].Groups['HostName'].Value
    Interface = $interface.Matches | foreach {$_.Groups['Interface'].Value}
}

Output:

HostName         Interface                                    
--------         ---------                                    
SOME-HOSTNAME-01 {Loopback0, FastEthernet0/0, FastEthernet0/1}

To throw another solution out there:

$config = Get-Content -Path C:\Users\jneedle\Desktop\testnote.txt -Raw

$information = @()

$hostname = ($config| Select-String -Pattern "(?<=hostname ).+").Matches.Value

$interfaces = ($config | Select-String -Pattern "(?smi)^interface.+?(?=!)" -AllMatches).Matches.Value

Foreach($interface in $interfaces) {
$result = "" | Select HostName, Interface, Description, IPAddress, IPHelper
$result.HostName = $hostname
$result.Interface = ($interface | Select-String "(?<=interface.).+").Matches.Value
$result.Description = ($interface | Select-String "(?<=description.).+").Matches.Value
$result.IPAddress = ($interface | Select-String "(?<=ip address.).+").Matches.Value
$result.IPHelper = ($interface | Select-String -Pattern "(?<=ip helper.).+" -AllMatches).Matches.Value -join "`n"
$information += $result
}

Output when returning information:

HostName : SOME-HOSTNAME-01
Interface : Loopback0
Description : 
IPAddress : 192.168.1.1 255.255.255.255
IPHelper :

HostName : SOME-HOSTNAME-01
Interface : FastEthernet0/0
Description : Some-Sample-DescriptionWithOdd/Char@s
IPAddress : 192.168.1.2 255.255.255.0
IPHelper : address 192.168.1.3
address 192.168.1.4

HostName : SOME-HOSTNAME-01
Interface : FastEthernet0/1
Description : 
IPAddress : 
IPHelper :

Thank you all for the replies.

@Grokkit, that -Raw was exactly what I needed. I got it working now.

@Rob Simmers, that’s a great way to nest each of the properties of the interface

@JoshuaNeedle87, that’s about exactly what I ended up finishing up this morning, once that -Raw was in there.

Hope you guys have a great Christmas coming up!