Getting Netstat output - code fails on Server 2008 R2 - powershell

I am quite new to PowerShell, and scripting in general. I have been asked to generate a list of all listening TCP ports on a large set of servers over a period of time, returning a big csv file that can be imported and searched. Unfortunately some of them are still running Server 2008R2 (yeah, yeah I know...) so using Get-NetTCPConnection is out of the question. I pretty much have to try and run NetStat and make use of the output from that. I found a wonderful script written by Adam Bertram in 2015 called Get-LocalPort.ps1 which coverts the output to proper Powershell objects and appears to be ideal, however it doesn't run on Server 2008R2 either. It produces the error Method invocation failed because [System.Object[]] doesn't contain a method named 'Trim'. which I believe is coming from the line $Netstat = (netstat -anb | where {$_ -and ($_ -ne 'Active Connections')}).Trim() | Select-Object -Skip 1 | foreach {$_ -replace '\s{2,}','|'} I don't understand why that line works on newer versions but not on 2008R2. Can anyone help me tweak this so it runs on older versions of Powershell? Thanks very much.
The whole script is as follows:
<#
.SYNOPSIS
This parses the native netstat.exe's output using the command line "netstat -anb" to find
all of the network ports in use on a local machine and all associated processes and services
.NOTES
Created on: 2/15/2015
Created by: Adam Bertram
Filename: Get-LocalPort.ps1
.EXAMPLE
PS> Get-LocalPort.ps1
This example will find all network ports in uses on the local computer with associated
processes and services
.EXAMPLE
PS> Get-LocalPort.ps1 | Where-Object {$_.ProcessOwner -eq 'svchost.exe'}
This example will find all network ports in use on the local computer that were opened
by the svchost.exe process.
.EXAMPLE
PS> Get-LocalPort.ps1 | Where-Object {$_.IPVersion -eq 'IPv4'}
This example will find all network ports in use on the local computer using IPv4 only.
#>
[CmdletBinding()]
param ()
begin {
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
}
process {
try {
## Capture the output of the native netstat.exe utility
## Remove the top row from the result and trim off any leading or trailing spaces from each line
## Replace all instances of more than 1 space with a pipe symbol. This allows easier parsing of
## the fields
$Netstat = (netstat -anb | where {$_ -and ($_ -ne 'Active Connections')}).Trim() | Select-Object -Skip 1 | foreach {$_ -replace '\s{2,}','|'}
$i = 0
foreach ($Line in $Netstat) {
## Create the hashtable to conver to object later
$Out = #{
'Protocol' = ''
'State' = ''
'IPVersion' = ''
'LocalAddress' = ''
'LocalPort' = ''
'RemoteAddress' = ''
'RemotePort' = ''
'ProcessOwner' = ''
'Service' = ''
}
## If the line is a port
if ($Line -cmatch '^[A-Z]{3}\|') {
$Cols = $Line.Split('|')
$Out.Protocol = $Cols[0]
## Some ports don't have a state. If they do, there's always 4 fields in the line
if ($Cols.Count -eq 4) {
$Out.State = $Cols[3]
}
## All port lines that start with a [ are IPv6
if ($Cols[1].StartsWith('[')) {
$Out.IPVersion = 'IPv6'
$Out.LocalAddress = $Cols[1].Split(']')[0].TrimStart('[')
$Out.LocalPort = $Cols[1].Split(']')[1].TrimStart(':')
if ($Cols[2] -eq '*:*') {
$Out.RemoteAddress = '*'
$Out.RemotePort = '*'
} else {
$Out.RemoteAddress = $Cols[2].Split(']')[0].TrimStart('[')
$Out.RemotePort = $Cols[2].Split(']')[1].TrimStart(':')
}
} else {
$Out.IPVersion = 'IPv4'
$Out.LocalAddress = $Cols[1].Split(':')[0]
$Out.LocalPort = $Cols[1].Split(':')[1]
$Out.RemoteAddress = $Cols[2].Split(':')[0]
$Out.RemotePort = $Cols[2].Split(':')[1]
}
## Because the process owner and service are on separate lines than the port line and the number of lines between them is variable
## this craziness was necessary. This line starts parsing the netstat output at the current port line and searches for all
## lines after that that are NOT a port line and finds the first one. This is how many lines there are until the next port
## is defined.
$LinesUntilNextPortNum = ($Netstat | Select-Object -Skip $i | Select-String -Pattern '^[A-Z]{3}\|' -NotMatch | Select-Object -First 1).LineNumber
## Add the current line to the number of lines until the next port definition to find the associated process owner and service name
$NextPortLineNum = $i + $LinesUntilNextPortNum
## This would contain the process owner and service name
$PortAttribs = $Netstat[($i+1)..$NextPortLineNum]
## The process owner is always enclosed in brackets of, if it can't find the owner, starts with 'Can'
$Out.ProcessOwner = $PortAttribs -match '^\[.*\.exe\]|Can'
if ($Out.ProcessOwner) {
## Get rid of the brackets and pick the first index because this is an array
$Out.ProcessOwner = ($Out.ProcessOwner -replace '\[|\]','')[0]
}
## A service is always a combination of multiple word characters at the start of the line
if ($PortAttribs -match '^\w+$') {
$Out.Service = ($PortAttribs -match '^\w+$')[0]
}
[pscustomobject]$Out
}
## Keep the counter
$i++
}
} catch {
Write-Error "Error: $($_.Exception.Message) - Line Number: $($_.InvocationInfo.ScriptLineNumber)"
}
}

You may do the following:
# skipping header
$ns = netstat -anb | Select -Skip 3
$ns | Foreach-Object {
# Trim surrounding spaces
$line = $_.Trim()
# Check for lines starting with TCP
if ($line -cmatch '^TCP') {
# Split lines by spaces
$p,$l,$f,$s = $line -split '\s+'
# Output $obj if it already exists before new one is created
if ($obj) { $obj }
# service and process owner are blanked since they are on another line
$obj = new-object -TypeName Psobject -Property #{
Protocol=$p
State=$s
IPVersion=('IPv6','IPv4')[$l -match ':.*:']
LocalAddress=($l -replace '[\[\]]|:[^:]+$')
LocalPort=$l -replace '^.*:'
RemoteAddress=($f -replace '[\[\]]|:[^:]+$')
RemotePort=$f -replace '^.*:'
ProcessOwner=''
Service=''
}
}
elseif ($line -cmatch '^UDP') {
$p,$l,$f = $line -split '\s+'
if ($obj) { $obj }
$obj = new-object -TypeName Psobject -Property #{
Protocol=$p
State=''
IPVersion=('IPv4','IPv6')[$l -match ':.*:']
LocalAddress=($l -replace '[\[\]]|:[^:]+$')
LocalPort=$l -replace '^.*:'
RemoteAddress=($f -replace '[\[\]]|:[^:]+$')
RemotePort=$f -replace '^.*:'
ProcessOwner=''
Service=''
}
}
# line starts with [ then it is service name
elseif ($line -match '^\[') {
$obj.Service = $line -replace '[\[\]]'
}
else {
$obj.ProcessOwner = $line
}
}

Thanks AdminOfThings your first comment was on the right track. The line $Netstat = netstat -anb | where {$_ -and ($_ -ne 'Active Connections')} | foreach { $_.Trim() } | Select-Object -Skip 1 | foreach {$_ -replace '\s{2,}','|'}' works however I also had to change the line at the end from [pscustomobject]$Out to New-Object -TypeName PSObject -Property $Out because [pscustomobject] apparently was another new thing in PS 3. With these changes it works on Server 2008R2, 2012R2, and 2016.

Related

Powershell : Find a group of text in a file then extract a specific line in that group of text

I've been on this for few days now, I'm trying to parse multiple text files containing data like this :
[Cluster1]
GatewayIp=xx.xxx.xxx.xx
IpAddress=xx.xxx.xxx.x
MTU=0000
NetMask=xxx.xxx.xxx.0
Port=xxx
Protocol=xxxx/xxxxx
Sessions=xxxxxx
Bands=xxx, xxx, x
Binding=xxxxx
GroupNumber=x
InitQueue=xxxxxx
Interface=xxxxxx
Process=xxx
SupportsCar=No
SupportsCom=Yes
SupportsPos=Yes
SupportsXvd=No
[Cluster2]
GatewayIp=xx.xxx.xxx.xx
IpAddress=xx.xxx.xxx.x
MTU=0000
NetMask=xxx.xxx.xxx.0
Port=xxx
Protocol=xxxx/xxxxx
Sessions=xxxxxx
Bands=xxx, xxx, x
Binding=xxxxx
GroupNumber=x
InitQueue=xxxxxx
Interface=xxxxxx
Process=xxx
SupportsCar=No
SupportsCom=No
SupportsPos=No
SupportsXvd=Yes
I want to extract the "IpAddress" in the section where thoses lines are present :
SupportsCom=Yes
SupportsPos=Yes
The thing is, I've tried using -context to grab the nth line after the section name "[Cluster1]", but that section name is different from file to file ...
$ip = Select-String -Path "$location" -Pattern "\[Cluster1\]" -Context 0,2 |
Foreach-Object {$_.Context.PostContext}
I've tried using the Precontext to grab the Nth line before SupportsCom=Yes, but the line position of "IpAddress=" is different from file to file ...
$ip = Select-String -Path "$location" -Pattern " SupportsCom=Yes" -Context 14,0 |
Foreach-Object { $_.Line,$_.Context.PreContext[0].Trim()}
Is there a way to grab the section containing "SupportsCom=Yes" knowing that the section is delimited by a blank line above and below, then search in that section a string that contains "IpAddress=" then return the value afterthe "=" ?
Ok, since you are not allowed to use a module (perhaps later..), this should get you what you want
# change the extension in the Filter to match that of your files
$configFiles = Get-ChildItem -Path 'X:\somewhere' -Filter '*.ini' -File
$result = foreach ($file in $configFiles) {
# initialize these variables to $null
$IpAddress = $supportsCom = $supportsPos = $null
# loop through the file line by line and try regex matches on them
switch -Regex -File $file {
'^\[([^\]]+)]' {
# did we get all wanted entries from the previous cluster?
if ($IpAddress -and $supportsCom -and $supportsPos) {
if ($supportsCom -eq 'Yes' -and $supportsPos -eq 'Yes') {
# just output the IpAddress so it gets collected in variable $result
$IpAddress
}
# reset the variables to $null
$IpAddress = $supportsCom = $supportsPos = $null
}
# start a new cluster
$cluster = $matches[1]
}
'^\s+IpAddress\s*=\s*(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' { $IpAddress = $matches[1]}
'^\s+SupportsCom\s*=\s*(Yes|No)' { $supportsCom = $matches[1] }
'^\s+SupportsPos\s*=\s*(Yes|No)' { $supportsPos = $matches[1]}
}
}
# show results on screen
$result
# or save as text file
$result | Set-Content -Path 'X:\somewhere\IpAddresses.txt'
Updated answer:
If you don't care about the name of the section(s), where IpAddress is found in, you can use this "one-liner" (broken into multiple lines for readability):
$ip = (Get-Content $location -Raw) -split '\[.+?\]' |
ConvertFrom-StringData |
Where-Object { $_.SupportsCom -eq 'Yes' -and $_.SupportsPos -eq 'Yes' } |
ForEach-Object IpAddress
The Get-Content line reads the input file as a single multi-line string and splits it at the section headers (e. g. [Cluster1]).
ConvertFrom-StringData converts the Key = Value lines into one hashtable per section.
For each hashtable, Where-Object checks whether it contains SupportsCom=Yes and SupportsPos=Yes
ForEach-Object IpAddress is shorthand for writing Select-Object -ExpandProperty IpAddress which gives you the actual value of IpAddress instead of an object that contains a member named IpAddress.
Note that $ip can be either a single string value or an array of strings (if there are multiple matching sections).
Original answer:
You could also write a general-purpose function that converts INI sections into objects. This enables you to use the pipeline with a simple Where-Object statement to get the data you are interested in.
Generic function to output INI sections as objects, one by one:
Function Read-IniObjects {
[CmdletBinding()]
param (
[Parameter(Mandatory, ValueFromPipeline)] [String] $Path
)
process {
$section = #{} # A hashtable that stores all properties of the currently processed INI section.
# Read file line by line and match each line by the given regular expressions.
switch -File $Path -RegEx {
'^\s*\[(.+?)\]\s*$' { # [SECTION]
# Output all data of previous section
if( $section.Count ) { [PSCustomObject] $section }
# Create new section data
$section = [ordered] #{ IniSection = $matches[ 1 ] }
}
'^\s*(.+?)\s*=\s*(.+?)\s*$' { # KEY = VALUE
$key, $value = $matches[ 1..2 ]
$section.$key = $value
}
}
# Output all data of last section
if( $section.Count ) { [PSCustomObject] $section }
}
}
Usage:
$ip = Read-IniObjects 'test.ini' |
Where-Object { $_.SupportsCom -eq 'Yes' -and $_.SupportsPos -eq 'Yes' } |
ForEach-Object IpAddress
Notes:
The INI file is parsed using the switch statement, which can directly use a file as input. This is much faster than using a Get-Content loop.
As we are using -RegEx parameter, the switch statement matches each line of the file to the given regular expressions, entering the case branches only if the current line matches.
Get detailed explanation about how the RegEx's work:
match lines like [Section] -> RegEx101
match lines like Key = Value -> RegEx101
ForEach-Object IpAddress is shorthand for writing Select-Object -ExpandProperty IpAddress which gives you the actual value of IpAddress instead of an object that contains a member named IpAddress.
Note that $ip can be either a single string value or an array of strings (if there are multiple matching sections).

Powershell Get-Content specific content inside text

I receive a text file with a multiple lists like shown below (edit: more accurate example dataset included)
# SYSTEM X
# SINGULAR
192.168.1.3
# SUB-SYSTEM V
192.168.1.4
192.168.1.5
192.168.1.6
# SYSTEM Y
# MANDATORY
192.168.1.7
192.168.1.8
192.168.1.9
192.168.1.7
192.168.1.8
192.168.1.9
Each "SYSTEM comment" means its a new set after it.
I want to read each block of content separately so each set should be assigned to an object discarding the embedded comments. I just need the IPs.
Something like:
$ipX = get-content -path [file.txt] [set X]
$ipY = get-content -path [file.txt] [set Y]
$ipZ = get-content -path [file.txt] [set Z]
But I'm not sure how to actually assign these sets separately.
Help please.
Here's one possible solution. The result will be a hashtable, each key containing any array of ips for the set:
$result = #{}
get-content file.txt | foreach {
if ($_ -match "#\s*SET\s+(\w+)") {
$result[($key = $matches.1)] = #()
}
elseif ($_ -notlike "#*") {
$result[$key] += $_
}
}
Contents of $result:
Name Value
---- -----
Y {[ip], [ip], [more ips]}
Z {[ip], [ip], [more ips]}
X {[ip], [ip], [more ips]}
Here's another approach. We will take advantage of Foreach-Object's -End block to [PSCustomObject] the final one.
Get-Content $file | Foreach-Object {
if($_ -match 'SET (.+?)'){
if($ht){[PSCustomObject]$ht}
$ht = [ordered]#{Set = $Matches.1}
}
if($_ -match '^[^#]'){
$ht["IPs"] += $_
}
} -End {if($ht){[PSCustomObject]$ht}}
Output
Set IPs
--- ---
X [ip][ip][more ips]
Y [ip][ip][more ips]
Z [ip][ip][more ips]
If you want to also ensure $ht is empty to start with you could use the -Begin block.
Get-Content $file | Foreach-Object -Begin{$ht=$null}{
if($_ -match 'SET (.+?)'){
if($ht){[PSCustomObject]$ht}
$ht = [ordered]#{Set = $Matches.1}
}
if($_ -match '^[^#]'){
$ht["IPs"] += $_
}
} -End {if($ht){[PSCustomObject]$ht}}
You can use Select-String to extract a specific section of text:
# Update $section to be the set you want to target
$section = 'Set Y'
Get-Content a.txt -Raw |
Select-String -Pattern "# $section.*\r?\n(?s)(.*?)(?=\r?\n# Set|$)" | Foreach-Object
{$_.Matches.Groups[1].Value}
Using Get-Content with -Raw reads in the file as a single string making multi-line matching easier. With PowerShell 7, Select-String includes a -Raw switch making this process a bit simpler.
This outputs capture group 1 results, which match the (.*?). If you want to capture between comments rather than between Set <something> and Set <something>, you can edit the -Pattern value at the end to only be # rather than # Set.
Regex Breakdown:
# matches the characters # literally
$section substitutes your variable value matches the value literally provided there are no regex characters in the string
.* matches any character (except for line terminators)
\r matches a carriage return
? Quantifier — Matches between zero and one times, as many times as
possible, giving back as needed (greedy)
\n matches a line-feed (newline) character
(?s) modifier: single line. Dot matches newline characters
1st Capturing Group (.*?)
.*? matches any characters lazily
Positive Lookahead (?=\r?\n# Set)
\r? matches a carriage return zero or more times
\n matches a line-feed (newline) character
# Set matches the characters # Set literally
$ matches the end of the string
If I understand the question with the new example correctly, you want to parse out the file and create single variables of that each holding an array ip IP addresses.
If that is the case, you could do:
# loop through the file line-by-line
$result = switch -Regex -File 'D:\Test\thefile.txt' {
'#\sSYSTEM\s(\w+)' {
# start a new object, output the earlier object if available
if ($obj) { $obj }
$obj = [PsCustomObject]#{ 'System' = $Matches[1]; 'Ip' = #() }
}
'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}' {
# looks like an IPv4 address. Add it to the Ip property array of the object
$obj.Ip += $_
}
default {}
}
Now you have an array ob objects in $result:
System Ip
------ --
Y {192.168.1.7, 192.168.1.8, 192.168.1.9, 192.168.1.7...}
X {192.168.1.3, 192.168.1.4, 192.168.1.5, 192.168.1.6}
To make separate variables of that is as easy as:
$ipX = ($result | Where-Object { $_.System -eq 'X' }).Ip
$ipY = ($result | Where-Object { $_.System -eq 'Y' }).Ip
$ipZ = ($result | Where-Object { $_.System -eq 'Z' }).Ip
Your example has duplicate IP addresses. If you don't want these do
$ipX = ($result | Where-Object { $_.System -eq 'X' }).Ip | Select-Object -Unique (same for the others)

How to remove the entire row when any one field of CVS is null in powershell?

ProcessName UserName PSComputerName
AnyDesk NT-AUTORITÄT\SYSTEM localhost
csrss dc-01
ctfmon SAD\Administrator rdscb-01
SAD\Administrator srv-01
Remove the second and last row here
Based on your comments, if $data is read from a CSV file and contains custom objects, you can do the following:
$data | where { $_.PsObject.Properties.Value -notcontains $null -and $_.PsObject.Properties.Value -notcontains '' }
This will apply to every property and won't require supplying named properties.
There are more elegant ways, but, here is a kind of ugly answer, to illustrate this...
$Data = #"
"ProcessName","UserName","PSComputerName"
"AnyDesk","NT-AUTORITÄT\SYSTEM","localhost"
"csrss","","dc-01"
"ctfmon","SAD\Administrator","rdscb-01"
"","SAD\Administrator","srv-01"
"# | Out-File -FilePath 'D:\Temp\ProcData.csv'
$headers = (
(Get-Content -Path 'D:\Temp\ProcData.csv') -replace '"','' |
select -First 1
) -split ','
$data = Import-Csv -Path 'D:\Temp\ProcData.csv'
$colCnt = $headers.count
$lineNum = 0
:newline
foreach ($line in $data)
{
$lineNum++
for ($i = 0; $i -lt $colCnt; $i++)
{
# test to see if contents of a cell is empty
if (-not $line.$($headers[$i]))
{
Write-Warning -Message "$($lineNum): $($headers[$i]) is blank"
continue newline
}
}
"$($lineNum): OK"
# Perform other actions with good data
}
<#
# Results
1: OK
WARNING: 2: UserName is blank
3: OK
WARNING: 4: ProcessName is blank
#>

Parse a list line by line, create a new list in Powershell

I need to read in a file that contains lines of source/destination IPs and ports as well as a tag. I'm using Get-Content:
Get-Content $logFile -ReadCount 1 | % {
} | sort | get-unique | Out-File "C:\Log\logout.txt"
This is an example of the input file:
|10.0.0.99|345|195.168.4.82|58164|spam|
|10.0.0.99|345|195.168.4.82|58164|robot|
|10.0.0.99|231|195.168.4.82|58162|spam|
|195.168.4.82|58162|10.0.0.99|231|robot|
|10.0.0.99|345|195.168.4.82|58168|spam|
|10.0.0.99|345|195.168.4.82|58169|spam|
What I need to do is output a new list, but if the same source/destination IPs/ports are both 'spam' and 'robot' I just need to output that line as 'robot' (lines 1 and 2 above).
I need to do the same if the reverse direction of an existing connection is either 'spam' or 'robot', I just need one or the other and it would be 'robot' (lines 3 and 4 above). There will be plenty of 'spam' lines without a duplicate or reverse connection (the last couple lines above), they need to just stay the same.
This is what i've been using to create the reverse direction of the connection, but I haven't been able to figure out how to properly create the new list:
$reverse = '|' + ($_.Split("|")[3,4,1,2,5] -join '|') + '|'
Output of the above would be:
|10.0.0.99|345|195.168.4.82|58164|robot|
|195.168.4.82|58162|10.0.0.99|231|robot|
|10.0.0.99|345|195.168.4.82|58168|spam|
|10.0.0.99|345|195.168.4.82|58169|spam|
(except that second line didn't have to be the reversed direction)
Thanks for any help!
Since both direct and reverse connections are checked and their line order may not be sequential, I would use a hashtable to store the type of both directions and do everything algorithmically:
$checkPoints = #{}
$output = [ordered]#{}
$reader = [IO.StreamReader]'R:\1.txt'
while (!$reader.EndOfStream) {
$line = $reader.ReadLine()
$s = $line.split('|')
$direct = [string]::Join('|', $s[1..4])
$reverse = [string]::Join('|', ($s[3,4,1,2]))
$type = $s[5]
$known = $checkPoints[$direct]
if (!$known -or ($type -eq 'robot' -and $known -eq 'spam')) {
$checkPoints[$direct] = $checkPoints[$reverse] = $type
$output[$direct] = $line
$output.Remove($reverse)
} elseif ($type -eq 'spam' -and $known -eq 'robot') {
$output.Remove($reverse)
}
}
$reader.Close()
Set-Content r:\2.txt -Encoding utf8 -value #($output.Values)

What is an equivalent of *Nix 'cut' command in Powershell?

I have following content in a configuration file (sample.cfg),
Time_Zone_Variance(Mins):300
Alert_Interval(Mins):2
Server:10.0.0.9
Port:1840
I'm trying to store an each values after the : by using split in PowerShell. but i'm not able to produce require output.
Can someone tell me how to use PowerShell split for the above problem ?
You can read the contents of the file using Get-Content, then pipe each line through ForEach-Object, then use the split command on each line, taking the second item in the array as follows:
$filename = "sample.cfg"
Get-Content $filename | ForEach-Object {
$_.split(":")[1]
}
Output
300
2
10.0.0.9
1840
Update
I prefer the approach by #AnsgarWiechers, but if you really need specifically named values you could create a hashtable and replace the name with the value:
$configValues = #{
hour = "Time_Zone_Variance(Mins)"
min = "Alert_Interval(Mins)"
server = "Server"
port = "Port"
}
Get-Content $filename | ForEach-Object {
# Courtesy of Ansgar Wiechers
$key, $value = $_ -split ':', 2
foreach($configValuesKey in $($configValues.keys)) {
if ($configValues[$configValuesKey] -eq $key)
{
$configValues[$configValuesKey] = $value
}
}
}
write-host "`nAll Values:"
$configValues
write-host "`nIndividual value:"
$configValues.port
Output
All Values:
Name Value
---- -----
port 1840
min 2
server 10.0.0.9
hour 300
Individual value:
1840
How's this?
function cut {
param(
[Parameter(ValueFromPipeline=$True)] [string]$inputobject,
[string]$delimiter='\s+',
[string[]]$field
)
process {
if ($field -eq $null) { $inputobject -split $delimiter } else {
($inputobject -split $delimiter)[$field] }
}
}
PS C:\> 'hi:there' | cut -f 0 -d :
hi
PS C:\> 'hi:there' | cut -f 1 -d :
there
PS C:\> 'hi:there' | cut -f 0,1 -d :
hi
there
PS C:\> 'hi:::there' | cut -f 0 -d :+
hi
PS C:\> 'hi there' | cut
hi
there
For a more succint syntax, this will also do the trick:
((Get-Content "your-file.txt") -Split ":")[1]
So the trick to use the -Split method is to have a String object returned by Get-Content (alias cat can also be used, actually), and from the resulting String[] object you can use the brackets to extract the nth item.
Note: Using -Split without parenthesis around Get-Content won't work since -Split is not a parameter name for that command... 🤷‍♂️
I suppose you don't want to just split the lines, but actually create key/value pairs. That could be achieved like this:
$config = #{}
Get-Content 'C:\path\to\sample.cfg' | % {
$key, $value = $_ -split ':', 2
$config[$key] = $value
}
You could also use the ConvertFrom-StringData cmdlet:
Get-Content 'C:\path\to\sample.cfg' | % {
ConvertFrom-StringData ($_ -replace ':','=')
}
The -replace operation is necessary, because ConvertFrom-StringData expects key and value to be separated by =. If you could change the delimiter in the config file from : to =, you could use ConvertFrom-StringData $_ without replacement.