Variables for Input(s) and output

Hello,
Pretty new to powershell, (at least on the learning how to script front) and have recently started to dig in and figure out how to do everything.
I have some good scripts from a previous co-worker, and after 24 hours of Powershell youtube videos, I can understand everything they do now! I count that as a win already. But where I’m still lacking is how to structure anything I want to add.
Take the below script for example. It is to pull Network card information.
I can run successfully, added an output variable to show results when ran, and it will send to email.
But i want to add input from text file for computers list, which i know how to do through get-content, but not sure how to tie it in on a loop with the existing format. The syntax I can usually find, it’s just structure at this point.
Also, not sure of how to tie in an output variable to make it an excel sheet that gets emailed to me.

Any help would be appreciated, because it’s probably one of just a couple things i’m missing that will hopefully put this altogether for me and help me understand it better. Trying to make it click in my head!

function Convert-IpToInt-Func ($ip) {
    $bytes = $ip.GetAddressBytes()
    [array]::Reverse($bytes)
    return [system.bitconverter]::ToUInt32($bytes, 0)
}

function Get-Subnet-Func ([ipaddress]$ipAddress, [ipaddress]$subnetMask) {
    $bytes = $subnetMask.GetAddressBytes()
    $string = New-Object -TypeName Text.StringBuilder
    foreach ($byte in $bytes){
        [void]$string.Append([convert]::ToString($byte, 2))
    }
    $bytes = [system.bitconverter]::GetBytes((Convert-IpToInt-Func -ip $subnetMask) -band (Convert-IpToInt-Func -ip $ipAddress))
    [array]::Reverse($bytes)
    return "$(([ipaddress]$bytes).ToString())/$(($string.ToString().TrimEnd("0")).Length)"
}

$output = @()

$nics = Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter "IPEnabled=""True""" | Where-Object {($null -ne $_.DefaultIPGateway) -and ($null -ne $_.IPAddress) -and ($null -ne $_.DNSServerSearchOrder)}

foreach ($nic in $nics) {
    $output += "" | Select-Object `
    @{n="Server";e={$env:COMPUTERNAME}}, 
    @{n="MAC Address";e={$nic.MACAddress}}, 
    @{n="IP Address";e={($nic | Select-Object -ExpandProperty IPAddress -Unique | Sort-Object | Where-Object {$_.Contains(".")}) -join ", "}}, 
    @{n="Subnet Mask";e={($nic | Select-Object -ExpandProperty IPSubnet -Unique | Sort-Object | Where-Object {$_.Contains(".")}) -join ", "}}, 
    @{n="Subnet";e={Get-Subnet-Func -ipAddress ($nic | Select-Object -ExpandProperty IPAddress -Unique | Sort-Object | Where-Object {$_.Contains(".")} | Select-Object -First 1) -subnetMask ($nic | Select-Object -ExpandProperty IPSubnet -Unique | Sort-Object | Where-Object {$_.Contains(".")} | Select-Object -First 1)}}, 
    @{n="Gateway";e={($nic | Select-Object -ExpandProperty DefaultIPGateway -Unique | Sort-Object | Where-Object {$_.Contains(".")}) -join ", "}}, 
    @{n="DNS Servers";e={($nic | Select-Object -ExpandProperty DNSServerSearchOrder -Unique | Sort-Object | Where-Object {$_.Contains(".")}) -join ", "}}
write-output $output 
$email = @{
From = ""
To = ""
Subject = "PowershellNicTest"
SMTPServer = ""
Body = $output | Out-String
}
Send-MailMessage @email
}

mrfloppy,
Welcome to the forum. :wave:t4:

Before we proceed please go back and edit your question to fix the formatting of your code.

When you post code or sample data or console output please format it as code using the preformatted text button ( </> ). Simply place your cursor on an empty line, click the button and paste your code.
Thanks in advance

Ah yes, that looks much better.
Thank you and i apologize for the mistake on the initial.
Saw the recommendation for doing it, didnt realize it was just a button though.

Hmmm … you’re actually asking for a review of your code. That’s actually a bad idea for a forum because you’re asking for opinions.

Anyway - here’s my opinion. :wink:

As long as your code does what you expect it to I’d be satisfied in the first place. Keep learning, keep trying, keep making. You will earn confidence over time and experience.

Some good general advices you can find in

The PowerShell Best Practices and Style Guide

According to your code. I’d try to make the functions a little more professional by adding a comment based help and writing them a little more verbose and writing one function for only one purpose. For example the gathering of the network adapter information. You mix it with sending an email … I wouldn’t do that. Instead I’d make my function more specific and focused on only one purpose like this

function Get-NetworkAdapterList {
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [string[]]
        $ComputerName = $env:COMPUTERNAME
    )
    process {
        $CimSession = New-CimSession -ComputerName $ComputerName
        $NetworkAdapterList = 
        Get-CimInstance Win32_NetworkAdapterConfiguration -Filter "IPEnabled=""True""" -CimSession $CimSession |
        Where-Object {
                    ($null -ne $_.DefaultIPGateway) -and 
                    ($null -ne $_.IPAddress) -and 
                    ($null -ne $_.DNSServerSearchOrder)
        }
        foreach ($NetworkAdapter in $NetworkAdapterList) {
            [PSCustomObject]@{
                ComputerName = $NetworkAdapter.DNSHostName
                MACAddress   = $NetworkAdapter.MACAddress
                IPv4Address  = $NetworkAdapter.IPAddress | Where-Object { $_.Contains('.') }
                IPv6Address  = $NetworkAdapter.IPAddress | Where-Object { $_.Contains(':') }
                ServiceName  = $NetworkAdapter.ServiceName
                Description  = $NetworkAdapter.Description
            }
        }
    }
}

This way you can run it locally or remotely and you can even use it in a pipeline … it works just like a builtin cmdlet.

Get-NetworkAdapterList
# or
Get-NetworkAdapterList -ComputerName 'Computer1'
# or 
Get-NetworkAdapterList -ComputerName 'Computer1', 'Computer2'
# or
 'Computer1', 'Computer2' | Get-NetworkAdapterList 
# or like this
'Computer1', 'Computer2' |
    Get-NetworkAdapterList |
        Select-Object -Property ComputerName, MACAddress, IPv4Address, ServiceName, Description |
            Format-Table -AutoSize

Now you just have to add a proper error handling. :wink:

1 Like

So for my use, I want to add it to a common server with other admins. You already stated that it would work remotely, but will it work for any logged on user?
My business reason is to have a text file that can be updated by anyone, but the script itself won’t change. So anyone can update the text file for the scan(s) they need, and get the proper result.

And yes, the original works, and I added the email and output just to test the send function and not only an output directly to console.
So trying to get 2 things, where to add the input.txt for each loop, and how to send to formatted excel.
I do like the cleanliness of the cmdlet add, and that might make the first part of my 2 things more easily attainable.

I’m not sure if I really got what you meant. If you want to make your code available to other colleagues you may make it available as a module on the computer you want and educate the colleagues how to use it. :wink:

That depends pretty much on the account and the rights this account has on the target computer. Some functions may need adminsitrative rights.

That sounds like you want to have people running tasks they actually do not understand. You may set up an automated task checking for an input file of your choice (JSON, CSV, plain text, XML, YAML, PSD1) and running the tasks when the input file changed or in a predefined interval.

At the beginning of your script!? :wink:

If you meant CSV you can simply use Export-CSV. If you really meant Excel you should take a look at the graet module from Doug Finke ImportExcel. It’s able to create and manipulate Excel-Sheets even without an installed instance of Excel.

If I got everything wrong you may elaborate a little more detailed about what you need.

Ok. So I decided to take it basic just so there’s hopefully no confusion on what I’m asking, and no confusion on my part with functions and new cmdlets…yet. Feel like I might be confusing myself and jumping ahead too fast for my skill set.

$Servers = Select-String -Path C:\temp\NICConfiguration\computers.txt -Pattern '.' | Select -ExpandProperty Line

This line (minus the $Servers =) will get me my list of servers from a text file.

$output = @()
$nics = Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter "IPEnabled=""True""" | Where-Object {($null -ne $_.DefaultIPGateway) -and ($null -ne $_.IPAddress) -and ($null -ne $_.DNSServerSearchOrder)}
 
foreach ($nic in $nics) {
    $output += "" | Select-Object `
    @{n="Server";e={$env:COMPUTERNAME}}, 
    @{n="MAC Address";e={$nic.MACAddress}}, 
    @{n="IP Address";e={($nic | Select-Object -ExpandProperty IPAddress -Unique | Sort-Object | Where-Object {$_.Contains(".")}) -join ", "}}, 
    @{n="Subnet Mask";e={($nic | Select-Object -ExpandProperty IPSubnet -Unique | Sort-Object | Where-Object {$_.Contains(".")}) -join ", "}}, 
    @{n="Gateway";e={($nic | Select-Object -ExpandProperty DefaultIPGateway -Unique | Sort-Object | Where-Object {$_.Contains(".")}) -join ", "}}, 
    @{n="DNS Servers";e={($nic | Select-Object -ExpandProperty DNSServerSearchOrder -Unique | Sort-Object | Where-Object {$_.Contains(".")}) -join ", "}}
write-output $output 
}

This will get me my NIC info and output to console.

So before I add anymore to the equation, how do I get the input string to iterate through the foreach nic string?
That’s where I’m getting stuck. Trying to do a foreach server from computers.txt, for a foreach nic.

:+1:t4:

That’s always a wise decission to follow the KISS principle. :wink:

I just realized that I messed up my own code suggestion in my answer above while trying to correct it. I corrected this now. :wink:

$Servers = 
    Select-String -Path C:\temp\NICConfiguration\computers.txt -Pattern '.' | 
        Select -ExpandProperty Line

That looks a little overcomplicated to me. How does your input file look like? If it’s one computername per line you may use something more basic like this:

$ComputerNameList = 
    Get-Content -Path 'C:\temp\NICConfiguration\computers.txt'

Now you have the servers you want to query in an array saved to a variable named $ComputerNameList.

As usual there are more than one options available. :wink: Assumed you are in a domain environment and you have administrative access to all the servers with the account you’re running this script with and you’re using the function I posted in my answer above you could simply do this:

Get-NetworkAdapterList -ComputerName $ComputerNameList

Another option would be to use explicit remoting with Invoke-Command like this:

Invoke-Command -ComputerName $ComputerNameList -ScriptBlock {
    ## your code
}

This would run your code remotely on all computers contained in the array of $ComputerNameList.

As you can see you don’t need a loop for this task at all. If you insist to use a loop you could use a nested loop. An outer loop for your servers and an inner loop for the networkadapters.

If you want to learn about loops you may start with reading the help topics about the different types of loops you have in PowerShell. You should always read the help topics for the cmdlets you’re about to use completely including the examples to learn how to use them.

Regardless of all that - It is recommended not to use Get-WmiObject anymore as it is deprecated. Use Get-CimInstance instead.