Network Discovery – Part 1
My most popular script on Spiceworks, by far, is Network Discovery. I posted it over 3 years ago, and actually wrote it much earlier than that! Time to take another look at it and give it a Powershell make-over. If you’re new to a company and want to find out the basics of what is going on, or a consultant going into new places all the time you’ll want to take a look at this script!
History
To go into the history of Network Discovery takes me back to my consulting days at All Covered. As a consultant for SMB’s, I was always walking into new places with no idea of the basic layout of the network. One of my primary jobs, in fact, was doing initial network assessments and writing up my findings for a minimal cost with the hope of landing more project work from our findings. Finding out the basic Active Directory layout, and what servers and workstations were on the network was a fairly manual process with me going to each server and running BGInfo, or some other utility to get the basic hardware configuration. Also just looking at a few workstations and so on.
But as I started using vbScript more and more I realized I could do all of this work in a script and return CSV’s with this information in it. Than I could simply import those CSV’s into my assessment documents and have a really nice inventory included in the assessment, which wasn’t bad considering I was usually only at a site for a couple of hours. Thus Network Discovery was born and eventually upgraded to the version you see today at Spiceworks.
Why No Powershell Upgrade Until Now?
While I love upgrading a script to Powershell just to see if it can be done better, this was a script that I had resisted doing that with because frankly the script was running great and I couldn’t really see a way for Powershell to bring anything better to the table. Until I started working more with multi-threading and it occurred to me that Network Discovery is a perfect candidate for a multi-threaded application. Let’s face it, as a consultant time is money and in assessment situations you’re often working at a loss anyway so getting in and out while being thorough is critical to a successful assessment. In vbScript Network Discovery (ND) went through every IP address in a particular range, one at a time. This could be quite time-consuming but with Powershell we could do the same thing but in much bigger chunks which should significantly cut the scan time down while still gathering all of the information.
Network Discovery (Literally)
First step was to have Powershell look at the configuration the host it’s running on and get the network information from it and then calculate the entire IP range from that. First I had to find some code that would calculate IP ranges–I’m good, but I’m not that good!–and I found just that over at http://www.indented.co.uk with Chris Dent’s blog. He hasn’t updated it in quite a while, which is too bad, but he had a great blog post about IPv4 Math with Powershell. Armed with these functions I am now able to calculate IP ranges based on an address and subnet mask! Next I just had to run a WMI query against the machine to find out the IP address and subnet mask.
But first I had to make a big decision about how to write this new script. Powershell has a lot of powerful cmdlet’s available to it for discovering the world around it (Active Directory, Network Adapter, etc). But most of that is only available if Remote Administration Server Tools is installed, or the server is at least Windows Server 2008 R2 or greater. After talking to some of the consultants on Spiceworks they’re just not seeing a lot of those, in fact a lot of the servers they’re still seeing are Windows 2003! Now Powershell will run on 2003 and it will need to have Powershell installed on it, but RSAT and the other cmdlets just won’t be there at all.
We’ll have to bite the bullet and require a minimum of Powershell installed, of course, but I’ll have to write the script to deal with the fact that it’ll probably encounter a lot of Windows XP, 2003 type operating systems. That means no RSAT tools and no CIM tools. WMI and ADSI for this script!
#Get IP Address and Subnetmask if not specified in Parameters $IPInfo = @(Get-WMIObject -Class Win32_NetworkAdapterConfiguration -Filter "IPEnabled='TRUE'") If (-not $Network) { $Network = $IPInfo[0].IPAddress } If (-not $SubnetMask) { $SubnetMask = $IPInfo[0].IPSubnet } #Calculate IP range and submit GetComputerInfo scriptblock as background jobs $Range = Get-NetworkRange $Network $SubnetMask
I can get IP network information from Win32_NetworkAdapterConfiguration. $Network and $SubnetMask are user configurable parameters that if you don’t set them, then ND will go out and get the information itself. Than I’ll call a functioned called Get-NetworkRange and use the functions from Chris to return an array of every IP address in the network range. Here’s the Get-NetworkRange function:
Function Get-NetworkRange { Param ( [String]$IP, [String]$Mask ) If ($IP.Contains("/")) { $Temp = $IP.Split("/") $IP = $Temp[0] $Mask = $Temp[1] } If (!$Mask.Contains(".")) { $Mask = ConvertTo-Mask $Mask } $DecimalIP = ConvertTo-DecimalIP $IP $DecimalMask = ConvertTo-DecimalIP $Mask $Network = $DecimalIP -BAnd $DecimalMask $Broadcast = $DecimalIP -BOr ((-BNot $DecimalMask) -BAnd [UInt32]::MaxValue) For ($i = $($Network + 1); $i -lt $Broadcast; $i++) { ConvertTo-DottedDecimalIP $i } } #End Get-NetworkRange
I won’t post the other functions as you can get them from Chris’ page, or just look at the full ND script. Now we have our full range of IP addresses loaded into an array we need to simply create a loop and start submitting background jobs to do a ping check of the IP address and attempt a WMI connection to gather basic hardware information. Since I’m using background jobs I decided to define a scriptblock variable for the job submission. Here it is:
$GetComputerInfo = { Param ( [string]$IP ) Function PrepSize { Param ( [double]$Size ) If ($Size -ge 1000000000) { $ReturnSize = "{0:N2} GB" -f ($Size / 1GB) } Else { $ReturnSize = "{0:N2} MB" -f ($Size / 1MB) } Return $ReturnSize } #Reverse Ping to get DNS name (if exists) $Ping = Get-WMIObject Win32_PingStatus -Filter "Address = '$IP' AND ResolveAddressNames = TRUE" If ($Ping.StatusCode -eq 0) { $ComputerName = $Ping.ProtocolAddressResolved ...bunch of WMI discovery here... New-Object PSObject -Property @{ 'Computer Name' = $ComputerName Status = $Status IP = $IP OS = $OS 'OS Version' = $OSVersion 'OS Service Pack' = $ServicePack CPU = $CPU 'Number of CPUs' = $NumCPUS 'Make/Model' = $MakeModel 'Service Tag' = $ServiceTag 'Serial Number' = $SerialNumber Memory = $Memory 'Hard Drives' = $Drives }
First we accept IP as a parameter, then define a function that will convert raw disk space in bytes to a user-friendly GB or MB. The interesting bit of this scriptblock is the reverse DNS WMI call here. Test-Connectivity is a nifty cmdlet that let’s you do a ping test the Powershell way, but it simply doesn’t return the DNS name if it exists. To do that ourselves we have to use the WMI class Win32_PingStatus.
In the interest of avoiding TL;DR I’m going to stop now and post the next exciting episode next week!
indented.co.uk belongs to me, Chris Dent, not Jason Stangroome 🙂
Cheers!
Chris
So sorry! Let me get that updated!
Thanks!
[…] Time for Part 2 of talking about Network Discovery, the Powershell version. Part 1 can be found here. […]
[…] there is a WMI class for doing this kind of thing, something I’ve used in the past with my Network Discovery script. Here’s how you would use that class, let’s assume $IP is the IP address we […]