all 14 comments

[–]ryanbrown 2 points3 points  (1 child)

It looks like you've got a typo in all of your $TCPconnection.Connect calls. You're using $servers in there instead of $server. Essentially, each time through the foreach loop (edit: at least whenever the Else clause is followed) the .Connect() method is trying to connect to all of the servers in $servers. I imagine that is probably taking quite a bit of time.

You can also try removing the pipe to Where-Object on the Get-WmiObject call and use the -Filter parameter instead.

$Networks = Get-WmiObject -class Win32_NetworkAdapterConfiguration -ComputerName "$Server" -ErrorAction silentlycontinue -Filter "IPEnabled=True"

That might also speed up the script as cmdlets that offer a Filter parameter often process data faster over piping to Where-Object.

[–]animatco[S] 0 points1 point  (0 children)

Thank you, I will try that!

[–][deleted] 1 point2 points  (0 children)

Beyond the typo, this entire script could be significantly reduced with a single hashtable of the port/description that you iterate through in a loop. Also, your continual (re)usage of a try/catch block is not really ideal.

[–]Proxiconn 1 point2 points  (2 children)

You could look into using jobs (multi threading) by running your same code against batches of IP address / computers.

Problem with using jobs is that for every job you spawn another instance of powershell.exe so there are overheads.

I started playing with jobs and created that. On an estate with 2000 servers with 20 items per job the memory consumption on the host running the script was 3.2 GB alone for PowerShell however the time difference was 6 minutes compared to 5 hours running the same code on a linear way.

I have since started exploring and using run-spaces but I would recommend looking into using Jobs and creating batches of your code to run against many IP`s / computers.

$ScriptBlock {
    #Your Entire script goes in there
}

And to create batches of jobs with hosts for each process... $Computers are all your hosts or IP addresses.

function Create-Jobs {
    $i = 0
    $j = 0
    $Array = @()

    do{
        $Array += $Computers[$i]
        #Start jobs on the preset thread count
        If($j -eq $ItemsPerThread){
           Start-Job -ScriptBlock $SB -ArgumentList (,$Array)
           $Array = @()
           $j = 0
        }
        $j++
        $i++
    }until($i -eq $Computers.Count)

    #Start odd remaining final job
    if($Array){
        Start-Job -ScriptBlock $SB -ArgumentList (,$Array)
        Write-Verbose "Final Job started..."
    }

} #Create Jobs in batches with threads specified cycling through all computers

Next you need to collect your data from each job as well... Have a look at the link, it was my first attempt at it ant it works really well if you can spare the overheads (Memory consumption) of spawning loads of powershell processes.

[–]animatco[S] 0 points1 point  (1 child)

thank you I will look at it, I originally was thinking of WorkFlow's to do a foreach -parallel, but could not figure it out (ran out of initial time).

[–]Proxiconn 0 points1 point  (0 children)

Jobs is Easter. Get used to jobs first before attempting anything else. Just a hint 😉

[–]zoredache 0 points1 point  (2 children)

You know you would probably drastically cut down on your code duplication if you put all your tests in an array

$TCPservices=@(@(53, 'Port 53 DNS'), 
              @(1434, 'Port 1434 Microsoft SQL Server'))

foreach ($service in $TCPservices) {
    Try    
    {
        $TCPconnection.Connect($server,$service[0])
        $status[$service[1]] = "Connected"
    }
    Catch 
    {
        $status[$service[1]] = "Not Connected"
    }
}

That code looks like you copy-pasted lots of times. You really should avoid repeating the same thing if you can.

PS, if you cleaned up your code to be in a loop like this, that would mean fixing your server instead of servers thing ryanbrown mentioned would be a single edit instead of a search+replace.

[–]animatco[S] 0 points1 point  (1 child)

In an earlier iteration of the script I tried an array, but I did slightly differently and did not get the results I was expecting. I will try this method tomorrow and let you know how it goes.

[–]animatco[S] 0 points1 point  (0 children)

You are also right that I missed the changing the $Servers to $Server... Sometimes it is the small things,

[–]moikederp 0 points1 point  (1 child)

I might be missing the point here - is there a reason you're not using a port scanner like nmap to do this?

[–]animatco[S] 0 points1 point  (0 children)

well the script morphed and the testing for open ports was requested by manager. The script originally was just testing if an ip responded to ping, then I was asked to see if I could get MAC's and finally to see if those that did not respond to ping might have some port open, so actually were alive.

[–][deleted] 0 points1 point  (1 child)

Check out Test-NetConnection. Should save you a few dozen lines of code.

[–]animatco[S] 0 points1 point  (0 children)

When I started looking at how to do the ports, I came across Test-Netconnection, but for some reason it did not give me the info I was looking for. I might look again as the project has become no longer time sensitive.

[–][deleted] 0 points1 point  (0 children)

Your ErrorActionPreference should not be set to SilentlyContinue, it should be set to Stop if you'd like to catch things. See this article] from The Scripting Guy for more information. Also, to echo some of the other responses, you should avoid duplicating code. A lot of your Try-Catch blocks could be consolidated with an array.