Scanning ports on multiple hosts

Recently we had an issue that called into the use of checking to see if some ports were open and listening on our servers.  Naturally, we could have used a tool such as portqry.exe to gather this information. However, you can only use it on one system at a time which meant either incorporating it with Powershell and call the .exe when needed.  While a good idea, I wanted to build something that was free of any extra tools and just rely on Powershell itself to complete this task.

After doing some research and looking some examples online, I found that this could in fact be easily done.  Initially I was going to have it scan for TCP ports, but decided that checking UDP ports would be a good idea as well. My goal was to not only be able to scan multiple hosts, but to also scan multiple ports and report back which ones were open and which were closed.

Using the System.Net.Sockets namespace, I was able to create an object that I could then use to test both TCP and UDP ports.

$tcpobject = new-Object system.Net.Sockets.TcpClient
$udpobject = new-Object system.Net.Sockets.Udplient

I can then use the .BeginConnect() method and plug in the hostname and the port that will be tested.

$connect = $tcpobject.BeginConnect($c,$p,$null,$null)
$connect = $udpobject.BeginConnect($c,$p,$null,$null)

I then configure a timeout using the following:

$wait = $connect.AsyncWaitHandle.WaitOne($timeout,$false)

If you expand out what is in the $connect variable, you see this:

AsyncState             :
AsyncWaitHandle        : System.Threading.ManualResetEvent
CompletedSynchronously : False
IsCompleted            : True

This link explains the System.Threading.ManalResetEvent that I am using for the timeout.

The rest of the script uses the Add-Member cmdlet to add items to the created PSObject that holds all of the information for the report. I also make use of string manipulation such as Split(), Replace() and TrimStart() to make the error messages more readable in the report.

I decided to run the port scan against my local domain controller and google.com to show that some ports are open and others are not.  In the case of DC1, only TCP port 53 (DNS) was open and at google.com, TCP ports 80 (HTTP) and 443 (HTTPS) were open.


List of ports:

http://www.iana.org/assignments/port-numbers

My script is also available at the Script Repository.

Code Update 20Feb2011

Script:

function Test-Port{
<#
.SYNOPSIS
Tests port on computer.
.DESCRIPTION
Tests port on computer.
.PARAMETER computer
Name of server to test the port connection on.
.PARAMETER port
Port to test
.PARAMETER tcp
Use tcp port
.PARAMETER udp
Use udp port
.PARAMETER UDPTimeOut
Sets a timeout for UDP port query. (In milliseconds, Default is 1000)
.PARAMETER TCPTimeOut
Sets a timeout for TCP port query. (In milliseconds, Default is 1000)
.NOTES
Name: Test-Port.ps1
Author: Boe Prox
DateCreated: 18Aug2010
List of Ports: http://www.iana.org/assignments/port-numbers To Do:
Add capability to run background jobs for each host to shorten the time to scan.
.LINK
https://boeprox.wordpress.org
.EXAMPLE
Test-Port -computer ‘server’ -port 80
Checks port 80 on server ‘server’ to see if it is listening
.EXAMPLE
‘server’ | Test-Port -port 80
Checks port 80 on server ‘server’ to see if it is listening
.EXAMPLE
Test-Port -computer @(“server1″,”server2”) -port 80
Checks port 80 on server1 and server2 to see if it is listening
.EXAMPLE
@(“server1″,”server2”) | Test-Port -port 80
Checks port 80 on server1 and server2 to see if it is listening
.EXAMPLE
(Get-Content hosts.txt) | Test-Port -port 80
Checks port 80 on servers in host file to see if it is listening
.EXAMPLE
Test-Port -computer (Get-Content hosts.txt) -port 80
Checks port 80 on servers in host file to see if it is listening
.EXAMPLE
Test-Port -computer (Get-Content hosts.txt) -port @(1..59)
Checks a range of ports from 1-59 on all servers in the hosts.txt file

#>
[cmdletbinding(
DefaultParameterSetName = ”,
ConfirmImpact = ‘low’
)]
Param(
[Parameter(
Mandatory = $True,
Position = 0,
ParameterSetName = ”,
ValueFromPipeline = $True)]
[array]$computer,
[Parameter(
Position = 1,
Mandatory = $True,
ParameterSetName = ”)]
[array]$port,
[Parameter(
Mandatory = $False,
ParameterSetName = ”)]
[int]$TCPtimeout=1000,
[Parameter(
Mandatory = $False,
ParameterSetName = ”)]
[int]$UDPtimeout=1000,
[Parameter(
Mandatory = $False,
ParameterSetName = ”)]
[switch]$TCP,
[Parameter(
Mandatory = $False,
ParameterSetName = ”)]
[switch]$UDP

)
Begin {
If (!$tcp -AND !$udp) {$tcp = $True}
#Typically you never do this, but in this case I felt it was for the benefit of the function
#as any errors will be noted in the output of the report
$ErrorActionPreference = “SilentlyContinue”
$report = @()
}
Process {
ForEach ($c in $computer) {
ForEach ($p in $port) {
If ($tcp) {
#Create temporary holder
$temp = “” | Select Server, Port, TypePort, Open, Notes
#Create object for connecting to port on computer
$tcpobject = new-Object system.Net.Sockets.TcpClient
#Connect to remote machine’s port
$connect = $tcpobject.BeginConnect($c,$p,$null,$null)
#Configure a timeout before quitting
$wait = $connect.AsyncWaitHandle.WaitOne($TCPtimeout,$false)
#If timeout
If(!$wait) {
#Close connection
$tcpobject.Close()
Write-Verbose “Connection Timeout”
#Build report
$temp.Server = $c
$temp.Port = $p
$temp.TypePort = “TCP”
$temp.Open = “False”
$temp.Notes = “Connection to Port Timed Out”
}
Else {
$error.Clear()
$tcpobject.EndConnect($connect) | out-Null
#If error
If($error[0]){
#Begin making error more readable in report
[string]$string = ($error[0].exception).message
$message = (($string.split(“:”)[1]).replace(‘”‘,””)).TrimStart()
$failed = $true
}
#Close connection
$tcpobject.Close()
#If unable to query port to due failure
If($failed){
#Build report
$temp.Server = $c
$temp.Port = $p
$temp.TypePort = “TCP”
$temp.Open = “False”
$temp.Notes = “$message”
}
#Successfully queried port
Else{
#Build report
$temp.Server = $c
$temp.Port = $p
$temp.TypePort = “TCP”
$temp.Open = “True”
$temp.Notes = “”
}
}
#Reset failed value
$failed = $Null
#Merge temp array with report
$report += $temp
}
If ($udp) {
#Create temporary holder
$temp = “” | Select Server, Port, TypePort, Open, Notes
#Create object for connecting to port on computer
$udpobject = new-Object system.Net.Sockets.Udpclient($p)
#Set a timeout on receiving message
$udpobject.client.ReceiveTimeout = $UDPTimeout
#Connect to remote machine’s port
Write-Verbose “Making UDP connection to remote server”
$udpobject.Connect(“$c”,$p)
#Sends a message to the host to which you have connected.
Write-Verbose “Sending message to remote host”
$a = new-object system.text.asciiencoding
$byte = $a.GetBytes(“$(Get-Date)”)
[void]$udpobject.Send($byte,$byte.length)
#IPEndPoint object will allow us to read datagrams sent from any source.
Write-Verbose “Creating remote endpoint”
$remoteendpoint = New-Object system.net.ipendpoint([system.net.ipaddress]::Any,0)

Try {
#Blocks until a message returns on this socket from a remote host.
Write-Verbose “Waiting for message return”
$receivebytes = $udpobject.Receive([ref]$remoteendpoint)
[string]$returndata = $a.GetString($receivebytes)
}

Catch {
If ($Error[0].ToString() -match “\bRespond after a period of time\b”) {
#Close connection
$udpobject.Close()
#Make sure that the host is online and not a false positive that it is open
If (Test-Connection -comp $c -count 1 -quiet) {
Write-Verbose “Connection Open”
#Build report
$temp.Server = $c
$temp.Port = $p
$temp.TypePort = “UDP”
$temp.Open = “True”
$temp.Notes = “”
}
Else {
<#
It is possible that the host is not online or that the host is online,
but ICMP is blocked by a firewall and this port is actually open.
#>
Write-Verbose “Host maybe unavailable”
#Build report
$temp.Server = $c
$temp.Port = $p
$temp.TypePort = “UDP”
$temp.Open = “False”
$temp.Notes = “Unable to verify if port is open or if host is unavailable.”
}
}
ElseIf ($Error[0].ToString() -match “forcibly closed by the remote host” ) {
#Close connection
$udpobject.Close()
Write-Verbose “Connection Timeout”
#Build report
$temp.Server = $c
$temp.Port = $p
$temp.TypePort = “UDP”
$temp.Open = “False”
$temp.Notes = “Connection to Port Timed Out”
}
Else {
$udpobject.close()
}
}
#Merge temp array with report
$report += $temp
}
}
}
}
End {
#Generate Report
$report
}
}

Posted in powershell, scripts | Tagged , , , , | 20 Comments

PowerCLI 4.1 cmdlet poster

Working with a VMWare infrastructure, I have been getting more involved with the PowerCLI snap-in that is available for download to manage our systems.  In case anyone was wondering, you can download and print off a poster listing all of the cmdlets and includes a quick reference guide.

http://blogs.vmware.com/files/powercli-poster.pdf

Download PowerCLI 4.1 from here:

http://communities.vmware.com/community/vmtn/vsphere/automationtools/powercli?source=web&cd=1&sqi=2&ved=0CBMQFjAA&url=http://www.vmware.com/go/powercli&rct=j&q=download%20powercli&ei=FJiFTMSoGsaAlAfEsMWgDw&usg=AFQjCNFddkbqT43ZTSqBGo7UBPzAQnwPPg

Posted in PowerCLI, powershell, VMWare | Tagged , , | 1 Comment

Converting CSV file or files into an Excel workbook

Update 07SEPT2010: Updated script based on an excellent comment to use the Paste() method for converting CSV files into an excel workbook. Thanks Luc!

While going through the Technet Powershell Forums and Scripting Guys Forums, I came across a question by an individual who was looking for the best way to convert a .csv file into an excel file. As it stood, the individual only need to convert one csv file and that made for a fairly easy solution.

$xl = new-object -comobject excel.application
$xl.visible = $true
$Workbook = $xl.workbooks.open(“$loglocation\errors_$server.csv”)
$Worksheets = $Workbooks.worksheets
$Workbook.SaveAs(“$loglocation\errors_$server.xls”,1)
$Workbook.Saved = $True
$xl.Quit()

Simple enough. Just open up the CSV file using a created excel com object in Powershell and then save that file as an excel spreadsheet.

This got me to thinking about what if I wanted to save multiple csv files into an excel workbook? How would that be done? And of course, can this all be automated to auto-fit the columns and provide a name to each worksheet that was relevant to each csv file that was being converted?

The short answer, is…Yes! This can be done with Powershell and accomplish all of the requirements.

Below is the script that I wrote that takes multiple csv files and adds them all into excel.  It also auto-fits each column in excel so when you open it up, you already have access to all of the information without having to manually re-size each column. Plus, for each csv that you submit, a separate worksheet is created with the name of the csv file submitted, minus the extension.  Depending on the size of the csv, the time it takes for the conversion will vary.

As shown in the picture below, I have two csv files that are being converted into one excel workbook and saved to whatever filename I want using the .xlsx format.

When I open up the excel file, I now see both of my CSV’s there to view.

Simple enough!

You can find this script below or at this link on the Technet Gallery:

ConvertCSV-ToExcel.ps1

Script Below

Function Release-Ref ($ref)
{
([System.Runtime.InteropServices.Marshal]::ReleaseComObject(
[System.__ComObject]$ref) -gt 0)
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
}

Function ConvertCSV-ToExcel
{
<#
.SYNOPSIS
Converts one or more CSV files into an excel file.

.DESCRIPTION
Converts one or more CSV files into an excel file. Each CSV file is imported into its own worksheet with the name of the
file being the name of the worksheet.

.PARAMETER inputfile
Name of the CSV file being converted

.PARAMETER output
Name of the converted excel file

.EXAMPLE
Get-ChildItem *.csv | ConvertCSV-ToExcel -output ‘report.xlsx’

.EXAMPLE
ConvertCSV-ToExcel -inputfile ‘file.csv’ -output ‘report.xlsx’

.EXAMPLE
ConvertCSV-ToExcel -inputfile @(“test1.csv”,”test2.csv”) -output ‘report.xlsx’

.NOTES
Author: Boe Prox
Date Created: 01SEPT210
Last Modified:

#>

#Requires -version 2.0
[CmdletBinding(
SupportsShouldProcess = $True,
ConfirmImpact = ‘low’,
DefaultParameterSetName = ‘file’
)]
Param (
[Parameter(
ValueFromPipeline=$True,
Position=0,
Mandatory=$True,
HelpMessage=”Name of CSV/s to import”)]
[ValidateNotNullOrEmpty()]
[array]$inputfile,
[Parameter(
ValueFromPipeline=$False,
Position=1,
Mandatory=$True,
HelpMessage=”Name of excel file output”)]
[ValidateNotNullOrEmpty()]
[string]$output
)

Begin {
#Configure regular expression to match full path of each file
[regex]$regex = “^\w\:\\”

#Find the number of CSVs being imported
$count = ($inputfile.count -1)

#Create Excel Com Object
$excel = new-object -com excel.application

#Disable alerts
$excel.DisplayAlerts = $False

#Show Excel application
$excel.Visible = $False

#Add workbook
$workbook = $excel.workbooks.Add()

#Remove other worksheets
$workbook.worksheets.Item(2).delete()
#After the first worksheet is removed,the next one takes its place
$workbook.worksheets.Item(2).delete()

#Define initial worksheet number
$i = 1
}

Process {
ForEach ($input in $inputfile) {
#If more than one file, create another worksheet for each file
If ($i -gt 1) {
$workbook.worksheets.Add() | Out-Null
}
#Use the first worksheet in the workbook (also the newest created worksheet is always 1)
$worksheet = $workbook.worksheets.Item(1)
#Add name of CSV as worksheet name
$worksheet.name = “$((GCI $input).basename)”

#Open the CSV file in Excel, must be converted into complete path if no already done
If ($regex.ismatch($input)) {
$tempcsv = $excel.Workbooks.Open($input)
}
ElseIf ($regex.ismatch(“$($input.fullname)”)) {
$tempcsv = $excel.Workbooks.Open(“$($input.fullname)”)
}
Else {
$tempcsv = $excel.Workbooks.Open(“$($pwd)\$input”)
}
$tempsheet = $tempcsv.Worksheets.Item(1)
#Copy contents of the CSV file
$tempSheet.UsedRange.Copy() | Out-Null
#Paste contents of CSV into existing workbook
$worksheet.Paste()

#Close temp workbook
$tempcsv.close()

#Select all used cells
$range = $worksheet.UsedRange

#Autofit the columns
$range.EntireColumn.Autofit() | out-null
$i++
}
}

End {
#Save spreadsheet
$workbook.saveas(“$pwd\$output”)

Write-Host -Fore Green “File saved to $pwd\$output”

#Close Excel
$excel.quit()

#Release processes for Excel
$a = Release-Ref($range)
}
}

Posted in powershell, scripts | Tagged , , | 34 Comments

Convert bytes to highest available unit

Unfortunately, I had something else in mind to post this weekend, but life and some other things prevented me from doing so.  Maybe next weekend…

Because I wanted to get at least something out this weekend, I am going to use an advanced function I wrote during the 2010 Scripting Games that takes a size in bytes that would normally get if you do a WMI query against something like the Win32_LogicalDisk class for the size of free space and total space on each drive. You would get something like this:

PS C:\temp> gwmi win32_logicaldisk | Select DeviceID,FreeSpace, SizeDeviceID                                                              FreeSpace                                    Size
——–                                                              ———                                    —-
C:                                                                  88777068544                            309185212416
D:                                                                   4628267008                             10737414144
E:

Now this is fine and all but it leaves little to be desired when it comes to readability. Given, can do something like the following to get a specific size, such as GB:

PS C:\temp> gwmi win32_logicaldisk | Select DeviceID,@{Expression={$_.Freespace /1GB};Label=”FreeSpaceGB”}, @{Expression=
$_.Size / 1GB};Label=”SizeGB”}
DeviceID                                                              FreeSpaceGB                                    SizeGB

——–                                                              ———                                    —-

C:                                                              82.679988861084                        287.951168060303

D:                                                             4.31040954589844                        9.99999618530273

E:                                                                            0                                       0

Better, as long as you label somewhere what the default size is, someone who views this report will know what the size is of each hard drive.

What I wrote will take the size in bytes that will the given size and convert it to the highest possible size up to Petabyte. Not only that, but it will add the appropriate size after the integer so exactly what the size is.

PS C:\Users\boe\Downloads> gwmi win32_logicaldisk | Select DeviceID,@{Expression={Convert-BytesToSize $_.Freespace};Labe
l=”FreeSpace”}, @{Expression={Convert-BytesToSize $_.Size};Label=”Size”}

DeviceID                                FreeSpace                               Size
——–                                ———                               —-
C:                                      82.68GB                                 287.95GB
D:                                      4.31GB                                  10GB
E:                                      0Bytes                                  0Bytes


As you can see, the size has been converted to GB and beside each size is the corresponding GB that lets the user know exactly what the size is.  Below are some examples of each size having been converted from its initial bytes size.

PS C:\Users\boe\Downloads> Convert-BytesToSize 235
235Bytes
PS C:\Users\boe\Downloads> Convert-BytesToSize 2352323
2.24MB
PS C:\Users\boe\Downloads> Convert-BytesToSize 23523
22.97KB
PS C:\Users\boe\Downloads> Convert-BytesToSize 235232323
224.34MB
PS C:\Users\boe\Downloads> Convert-BytesToSize 23523232323
21.91GB
PS C:\Users\boe\Downloads> Convert-BytesToSize 2352323232323
2.14TB
PS C:\Users\boe\Downloads> Convert-BytesToSize 235232323232323
213.94TB
PS C:\Users\boe\Downloads> Convert-BytesToSize 23523232323232323
20.89PB

Script below:

Function Convert-BytesToSize
{
<#
.SYNOPSIS
Converts any integer size given to a user friendly size.

.DESCRIPTION


Converts any integer size given to a user friendly size.

.PARAMETER size


Used to convert into a more readable format.
Required Parameter

.EXAMPLE


ConvertSize -size 134217728
Converts size to show 128MB

#>

#Requires -version 2.0


[CmdletBinding()]
Param
(
[parameter(Mandatory=$False,Position=0)][int64]$Size

)


#Decide what is the type of size
Switch ($Size)
{
{$Size -gt 1PB}
{
Write-Verbose “Convert to PB”
$NewSize = “$([math]::Round(($Size / 1PB),2))PB”
Break
}
{$Size -gt 1TB}
{
Write-Verbose “Convert to TB”
$NewSize = “$([math]::Round(($Size / 1TB),2))TB”
Break
}
{$Size -gt 1GB}
{
Write-Verbose “Convert to GB”
$NewSize = “$([math]::Round(($Size / 1GB),2))GB”
Break
}
{$Size -gt 1MB}
{
Write-Verbose “Convert to MB”
$NewSize = “$([math]::Round(($Size / 1MB),2))MB”
Break
}
{$Size -gt 1KB}
{
Write-Verbose “Convert to KB”
$NewSize = “$([math]::Round(($Size / 1KB),2))KB”
Break
}
Default
{
Write-Verbose “Convert to Bytes”
$NewSize = “$([math]::Round($Size,2))Bytes”
Break
}
}
Return $NewSize

}

Posted in powershell, scripts | Tagged , , , | 3 Comments

Balloon Notifications with Powershell

Updates Balloon Pop-up

You’ve probably seen those balloon pop-ups at your computer at some point in time.  Most likely it was saying something about having updates that need to be installed on your computer.  They pop-up and say something and you can either click on it to bring up another window, close it out or let it disappear and continue on your merry way. One of the many things that Powershell can do is create the same type of pop-up using whatever icons and messages you want to display.

The first thing to do is to load the required assembly System.Windows.Forms to gain access to the notification capabilities and then create the notification object.

[void] [System.Reflection.Assembly]::LoadWithPartialName(“System.Windows.Forms”)
$notification = New-Object System.Windows.Forms.NotifyIcon

With this, you are then able to define what kind of title and message that your popup will display and show.

BalloonTipText   :
BalloonTipIcon   : None
BalloonTipTitle  :
ContextMenu      :
ContextMenuStrip :
Icon             :
Text             :
Visible          : False
Tag              :
Site             :
Container        :

BalloonTipText is the message that you will show and BalloonTipTitle is, of course, the title that the balloon will display. You will also want to change the Visible property to True so the balloon will show up when you call it with the  ShowBalloonTip method.

You will noticed that there is a property for an icon,not to be confused with the BalloonTipIcon property that shows the icon of the actual balloon pop-up.  This dictates what icon will show up on the system tray when the notification is called.  You can either supply your own icon or use one that is already available. In my case, I am using the Information icon via the SystemIcons namespace.

[System.Drawing.SystemIcons]::Information

Listed below are the available icons from that namespace.

Application     Property
Asterisk        Property
Error           Property
Exclamation     Property
Hand            Property
Information     Property
Question        Property
Shield          Property
Warning         Property
WinLogo         Property

When you look at the methods and properties using the Get-Member cmdlet, you will also see some events that can be used with some event monitoring to run specific actions based on if the balloon tip is closed or you wait for it to closed after the timeout or if you click on the balloon.  There are also events that work with actions from the mouse but I will not be working with those.

Name                      MemberType
—-                      ———-
BalloonTipClicked         Event
BalloonTipClosed          Event
BalloonTipShown           Event
Click                     Event
Disposed                  Event
DoubleClick               Event
MouseClick                Event
MouseDoubleClick          Event
MouseDown                 Event
MouseMove                 Event
MouseUp                   Event
CreateObjRef              Method
Dispose                   Method
Equals                    Method
GetHashCode               Method
GetLifetimeService        Method
GetType                   Method
InitializeLifetimeService Method
ShowBalloonTip            Method
ToString                  Method

Using the Register-ObjectEvent will allow you to leverage those events and have the perform specific actions based on how the event is handled. For instance, closing the balloon could allow the system tray icon to close or clicking on the balloon could allow another window to pop-up with information related to the pop-up.  In my instance, I display messages that state whether the balloon was closed or clicked on.

The last thing you need to do is to call the notification which requires you to determine the timeout (measured in milliseconds) for how long the notification will remain in the system tray.

$notification.ShowBalloonTip(600)

Putting everything together, I was able to generate a balloon pop-up that depending on whether you click it, close it or wait for it to close, a windows message will be generated that will tell you what action took place.

Balloon Tip Pop-up

Balloon pop-up clicked

As you can see just by the examples, there is quite a bit you can do with the notifications and the actions that are associated with the notifications.  Below is the code I wrote to perform the balloon pop-ups.  Feel free to use and test with other things for reporting or configuration changes.

For more information regarding Balloon Notification alerts, check out this site:

http://msdn.microsoft.com/en-us/library/system.windows.forms.notifyicon.aspx

#Load the required assemblies
[void] [System.Reflection.Assembly]::LoadWithPartialName(“System.Windows.Forms”)
#Remove any registered events related to notifications
Remove-Event BalloonClicked_event -ea SilentlyContinue
Unregister-Event -SourceIdentifier BalloonClicked_event -ea silentlycontinue
Remove-Event BalloonClosed_event -ea SilentlyContinue
Unregister-Event -SourceIdentifier BalloonClosed_event -ea silentlycontinue
#Create the notification object
$notification = New-Object System.Windows.Forms.NotifyIcon
 

#Define the icon for the system tray
$notification.Icon = [System.Drawing.SystemIcons]::Information

#Display title of balloon window
$notification.BalloonTipTitle = “This is a Balloon Title”

#Type of balloon icon
$notification.BalloonTipIcon = “Info”

#Notification message
$title = “This is the message in the balloon tip.”
$notification.BalloonTipText = $title

#Make balloon tip visible when called
$notification.Visible = $True

## Register a click event with action to take based on event
#Balloon message clicked
register-objectevent $notification BalloonTipClicked BalloonClicked_event `
-Action {[System.Windows.Forms.MessageBox]::Show(“Balloon message clicked”,”Information”);$notification.Visible = $False} | Out-Null

#Balloon message closed
register-objectevent $notification BalloonTipClosed BalloonClosed_event `
-Action {[System.Windows.Forms.MessageBox]::Show(“Balloon message closed”,”Information”);$notification.Visible = $False} | Out-Null

#Call the balloon notification
$notification.ShowBalloonTip(600)

 


Posted in powershell | Tagged , | 7 Comments