How can I write a PowerShell script to automate this set of commands?
Telnet to a machine,
execute few commands,
analyze at the output in the telnet window,
based on that output, send few more commands
Ok this isn't the most elegant solution, and it does rely on shudder VBscript but here it goes...
Create a VBScript to actually expedite the telnet session, this is an example
set oShell = CreateObject("WScript.Shell")
oShell.run("Telnet")
WScript.Sleep 1000
oShell.SendKeys("Open 127.0.0.1 23")
WScript.Sleep 1000
oShell.SendKeys("{Enter}")
WScript.Sleep 1000
oShell.SendKeys("n")
WScript.Sleep 1000
oShell.SendKeys("{Enter}")
WScript.Sleep 1000
oShell.SendKeys"MyName"
WScript.Sleep 1000
oShell.SendKeys("{Enter}")
WScript.Sleep 1000
oShell.SendKeys("MyPassword")
WScript.Sleep 1000
oShell.SendKeys("{Enter}")
WScript.Sleep 1000
oShell.SendKeys("MyCommand")
WScript.Sleep 1000
oShell.SendKeys("{Enter}")
WScript.Sleep 1000
Then use Powershell to invoke that script and pass it the commands you want executing, in the example below these commands are stored in a file called CommandList.txt
function Connect-MyTelnet{
Param(
[string] $IPAddress,
[string] $Port,
[string] $UserName,
[string] $Password,
[string] $cmdlistPath
)
## - Setting default values:
if($port -eq $null){ $Port = "23"; };
if($cmdlistPath -eq $null) { $CmdlistPath = 'c:\temp\cmdlist.txt'; };
## create vbscript file: MyTelnetSession.vbs
## - For Microsoft Telnet:
$MyVBScript = #"
set oShell = CreateObject("WScript.Shell")`r`n
oShell.run("Telnet")`r`n
WScript.Sleep 1000`r`n
oShell.SendKeys("Open $IPAddress $Port")`r`n
WScript.Sleep 1000`r`n
oShell.SendKeys("{Enter}")`r`n
WScript.Sleep 1000`r`n
oShell.SendKeys("n")`r`n
WScript.Sleep 1000`r`n
oShell.SendKeys("{Enter}")`r`n
WScript.Sleep 1000`r`n
oShell.SendKeys("$UserName")`r`n
WScript.Sleep 1000`r`n
oShell.SendKeys("{Enter}")`r`n
WScript.Sleep 1000`r`n
oShell.SendKeys("$Password")`r`n
WScript.Sleep 1000`r`n
oShell.SendKeys("{Enter}")`r`n
WScript.Sleep 1000`r`n
"#;
## - Get file with telnet commands:
[array] $Cmdlist = Get-Content $cmdlistPath;
## loop through and build each telnet command line:
foreach($cmd in $cmdlist)
{
## - Build VBscript lines:
$MyVBScript += 'oShell.SendKeys("'+$cmd+'")'+"`r`n";
$MyVBScript += "WScript.Sleep 1000`r`n";
$MyVBScript += 'oShell.SendKeys("{Enter}")'+"`r`n";
$MyVBScript += 'WScript.Sleep 1000'+"`r`n";
}
## - Close Telnet Session:
$MyVBScript += 'oShell.SendKeys(" QUIT")'+"`r`n";
$MyVBScript += "WScript.Sleep 1000`r`n";
$MyVBScript += 'oShell.SendKeys("{Enter}")'+"`r`n";
$MyVBScript += 'WScript.Sleep 1000'+"`r`n";
## - Save and execute generated VBscript:
$MYVBScript | Out-File -FilePath c:\temp\MyTelnet.vbs -Encoding ASCII;
& c:\temp\MyTelnet.vbs
}; Set-Alias ct Connect-MyTelnet;
And that should do what you are asking...
Note: Not my solution, found from this blog post and I have made use of it once or twice.
Rather than try to automate a telnet executable, just create the socket and issue the commands, read them back, and make decisions based on that. Here is an oversimplified example connecting to my local web server:
function test() {
$msg = [System.Text.Encoding]::ASCII.GetBytes("GET / HTTP/1.0`r`nHost: localhost`r`n`r`n")
$c = New-Object System.Net.Sockets.TcpClient("localhost", 80)
$str = $c.GetStream()
$str.Write($msg, 0, $msg.Length)
$buf = New-Object System.Byte[] 4096
$count = $str.Read($buf, 0, 4096)
[System.Text.Encoding]::ASCII.GetString($buf, 0, $count)
$str.Close()
$c.Close()
}
Obviously you would need to change it from port 80, and pass a username/password instead of a web request header... but this should be enough to get you started.
I wouldn't do anything with sockets here because you are going to need to implement at least parts of the telnet spec. If I remember, that spec is a bit funny. But there are some .NET telnet implementations listed here: C# Telnet Library that you can probably adapt or use directly from powershell in the same way that Goyuix is using the socket code in his answer.
I suggest you to use TeraTerm this free software.
you can telnet to your machine, and then run a TTL script.
it is very powerful and reliable. I am using it every day for my work.
you can do more search if you are interested.
example of TTL script:
i = 100
do while i>0
sendln 'un 1357'
wait '>'
sendln '.w 4 42800024 0000000a'
wait '>'
sendln '.w 4 42800014 00000004'
wait 'in service'
sendln 'info'
wait'>'
sendln 'start'
wait '20'#13#10'>' '0'#13#10'>'
if result!=2 then
break
endif
i = i - 1
loop
Here's a very basic Telnet client in PowerShell. It's essentially just the .net Framework's TcpClient, with some extra code to reject any IAC commands (i.e. when negotiating its abilities with the server it just says "I don't/won't do that" to all requests, ensuring that the most basic NVT implementation can be used).
Code maintained here: https://gist.github.com/JohnLBevan/e28fbb6c0dfdd45a21e03c104999c212
Function New-TelnetClient {
[CmdletBinding()]
Param (
[Parameter()]
[string]$ComputerName = '127.0.0.1'
,
[Parameter()]
[int]$PortNo = 23
,
[Parameter()]
[System.Text.Encoding]$Encoding = [System.Text.Encoding]::ASCII
,
[Parameter()]
[int]$BufferSize = 1024
)
[System.Net.Sockets.TcpClient]$telnet = New-Object 'System.Net.Sockets.TcpClient'
try {
$telnet.PSTypeNames.Add('ClearChannel.Net.Sockets.TelnetClient')
$telnet | Add-Member -MemberType 'NoteProperty' -Name 'Encoding' -Value ($Encoding)
$telnet | Add-Member -MemberType 'NoteProperty' -Name 'EndOfCommand' -Value ([System.Environment]::NewLine)
$telnet | Add-Member -MemberType 'NoteProperty' -Name 'BufferSize' -Value ($BufferSize)
$telnet.Connect($ComputerName, $PortNo)
$telnet | Add-Member -MemberType 'NoteProperty' -Name 'Writer' -Value (New-Object -TypeName 'System.IO.StreamWriter' -ArgumentList ($telnet.GetStream()))
$telnet.Writer.AutoFlush = $true
$telnet | Add-Member -MemberType 'ScriptMethod' -Name 'SendCommand' -Value ({
Param([string]$CommandText)
#$this.Writer.WriteLine($CommandText + $this.EndOfCommand) #writeline should stick the line endings in place anyway, but just to be sure, added this
$this.Writer.WriteLine($CommandText)
(New-Object -TypeName 'PSObject' -Property #{Direction='Input'; Value=$CommandText; When=((Get-Date).ToUniversalTime())})
})
$telnet | Add-Member -MemberType 'ScriptMethod' -Name 'HandleIac' -Value ({
if ($this.Available) {
[int]$byte = $this.GetStream().ReadByte()
[byte]$defaultResponse = 254 # for most IAC requests, we'll respond with don't
switch ($byte) {
-1 { # end of stream (shouldn't happen, but handled in case)
Write-Warning 'Unexpected end of stream whilst processing IAC'
return
}
255 { # Escaped IAC character
Write-Debug 'IAC Escaped'
return $byte
}
253 { #if we get a DO, change default response to WON'T instead of DON'T
$defaultResponse = 252
# do not break; continue to next case statement
}
{(251, 252, 253, 254) -contains $_} { # Will, Won't, Do, Don't
$byte = $this.GetStream().ReadByte() # this is the option we need to respond to; currently we just deny all options to get a raw NVT
switch ($byte) {
-1 {
Write-Warning 'Unexpected end of stream whilst processing IAC'
}
# if we want to handle specific IAC codes we can add support here
default {
$this.GetStream().WriteByte(255) # IAC
$this.GetStream().WriteByte($defaultResponse) # Don't/Won't
$this.GetStream().WriteByte($byte) # whatever you told me
}
}
return
}
default {
Write-Warning "$byte is not a control character, but was received after an IAC character"
}
}
}
})
$telnet | Add-Member -MemberType 'ScriptMethod' -Name 'GetBytes' -Value ({
Start-Sleep -Milliseconds 500 #added to get correct output; otherwise we seem to fly past the handshake :/
while ($this.Available -gt 0) {
[int]$byte = $this.GetStream().ReadByte() #held as int to allow -1 status code for end of stream
switch ($byte) {
-1 { # end of stream
return
}
255 { #IAC control character received
Write-Verbose 'IAC Command Received'
$this.HandleIac()
break
}
{($_ -ge 0) -and ($_ -lt 255)} { # normal data (not sure if it's worth returning the 0s... haven't seen anything to suggest that they're special though, as -1 is the eof.
[byte]$byte
Write-Debug "found $byte"
break
}
default {
throw "Received value $_ when expecting a byte (0-255)"
}
}
}
})
$telnet | Add-Member -MemberType 'ScriptMethod' -Name 'GetOutput' -Value ({
[byte[]]$bytes = $this.GetBytes()
if (($null -ne $bytes) -and ($bytes.Length -gt 0)) {
Write-Verbose "raw output is $(($bytes | %{"$_"}) -join ', ')"
$this.Encoding.GetString($bytes)
} else {
write-verbose 'no output this time'
}
})
$telnet | Add-Member -MemberType 'ScriptMethod' -Name 'ReceiveThenSendCommands' -Value ({
Param([string[]]$Commands)
foreach ($commandText in $commands) {
$this.GetOutput()
$this.SendCommand($commandText)
}
$this.GetOutput()
})
if ($telnet.Connected) {
$telnet
} else {
throw 'Failed to connect'
}
} catch {
Remove-TelnetClient -TelnetClient $telnet
}
}
Function Remove-TelnetClient {
[CmdletBinding()]
Param (
[Parameter(Mandatory = $true)]
[AllowNull()]
[PSObject]$TelnetClient
)
if ($null -ne $TelnetClient) {
if ($TelnetClient.Connected) {
$TelnetClient.GetStream().Close()
$TelnetClient.Close()
}
if($TelnetClient.Dispose) {
$TelnetClient.Dispose()
}
}
}
Here's an example of how it would be used in a normal scripted session:
# Example Usage
$telnet = New-TelnetClient -ComputerName 'TelnetServerNameFqdnOrIp'
try {
$telnet.ReceiveThenSendCommands(#(
'myTelnetUsername'
'myPlaintextTelnetPassword'
'DIR' #or whatever command I want to run
)) | Format-List # show the output in a readable format, including when it contains new line characters
} finally {
Remove-TelnetClient $telnet
}
But if you wanted to run it in interactive mode, just call SendCommand when you want to push commands to the server, and GetOutput to see results; e.g. you can run each line below one at a time.
$telnet = New-TelnetClient -ComputerName 'TelnetServerNameFqdnOrIp'
$telnet.GetOutput() # will probably display a welcome message & logon prompt
$telnet.SendCommand('myUsername') # send your username
$telnet.GetOutput() # will probably echo back your username then prompt for a password
$telnet.SendCommand('myPassword') # send your password
$telnet.GetOutput() # unlikely to output anything for a valid password; will give an error for an invalid one
$telnet.SendCommand('DIR') # send whatever commands you want to run
$telnet.GetOutput() # get the output of those commands
Remove-TelnetClient $telnet # once you're done, cleanly closes down the client
I have created a powershell script to telnet multiple stores from single host and has options to capture or no capture the tracert and ping command
Command to telnet multiple host and capture tracert and ping command
#Mutlple Telneting guide
#Created by : Mohit
#How to use ?
#Step 1 : Add mutiple IPs in DestinationIP.csv
#Step 2 : Run Batch file TelnetMultipleHost.bat
####################################################################################################################
$DestinationIP= Get-Content .\DestinationIP.csv
$ipV4 = (Test-Connection -ComputerName (hostname) -Count 1).IPV4Address.IPAddressToString
####################################################################################################################
write-host "-------------------Welcome to Multiple Telnet Host Panel-------------------------"
write-host ""
write-host ""
write-host "IMPORTANT: Make sure you are running this tool from source IP which in this case is " $ipV4
write-host ""
$Ports = Read-Host -Prompt "Enter Destination Port No.(# for multple ports just seperate ports with ,)"
write-host ""
write-host "Port No. you entered:" $Ports
write-host ""
write-host "Select Option"
write-host ""
write-host "Type 1 for telnet Host WITH trace logs"
write-host "Type 2 for telnet Host WITHOUT trace logs"
write-host ""
$option =Read-Host -Prompt "Type here"
write-host ""
Start-Transcript -Path .\TraceLogs_$ipV4.txt
switch($option)
{
#Type 1 for telnet Host WITH trace logs
1{
foreach ($Destination in $DestinationIP)
{
foreach ($Port in $Ports) {
# Create a Net.Sockets.TcpClient object to use for # checking for open TCP ports.
$Socket = New-Object Net.Sockets.TcpClient
# Suppress error messages
$ErrorActionPreference = 'SilentlyContinue'
# Try to connect
$Socket.Connect($Destination, $Port)
# Make error messages visible again
$ErrorActionPreference = 'Continue'
# Determine if we are connected.
if ($Socket.Connected) {
"${Destination}: Port $Port is open"
$Socket.Close()
}
else {
"${Destination}: Port $Port is closed or filtered"
if (test-connection $Destination -count 1 -quiet) {
write-host $Destination "Ping succeeded." -foreground green
} else {
write-host $Destination "Ping failed." -foreground red
}
Test-NetConnection $Destination -TraceRoute
}
# Apparently resetting the variable between iterations is necessary.
$Socket = $null
}
}
}
# Type 2 for telnet Host WITHOUT trace logs
2{
foreach ($Destination in $DestinationIP) {
foreach ($Port in $Ports) {
# Create a Net.Sockets.TcpClient object to use for
# checking for open TCP ports.
$Socket = New-Object Net.Sockets.TcpClient
# Suppress error messages
$ErrorActionPreference = 'SilentlyContinue'
# Try to connect
$Socket.Connect($Destination, $Port)
# Make error messages visible again
$ErrorActionPreference = 'Continue'
# Determine if we are connected.
if ($Socket.Connected) {
"${Destination}: Port $Port is open"
$Socket.Close()
}
else {
"${Destination}: Port $Port is closed or filtered"
}
# Apparently resetting the variable between iterations is necessary.
$Socket = $null
}
}
}
}
Stop-Transcript
Please note: TelnetMultipleHost.bat this batch is used to run the powershell command
Make sure we have bat, ps1 file in same directory
Code for batch file:
#echo off
Powershell.exe -executionpolicy remotesigned -File .\TelnetMultipleHost.ps1
pause
i use below script for telnet to multiple ip's:
$server_list = #('1.1.1.1:443', '10.100.8.22:3389', '10.100.8.21:22')
Foreach ($t in $server_list)
{
$source = $t.Split(':')[0]
$port = $t.Split(':')[1]
Write-Host "Connecting to $source on port $port" | Out-File 'output.txt' -Append
try
{
$socket = New-Object System.Net.Sockets.TcpClient($source, $port)
}
catch [Exception]
{
Write-Host $_.Exception.GetType().FullName | Out-File 'output.txt' -Append
Write-Host $_.Exception.Message | Out-File 'output.txt' -Append
}
Write-Host "Connected`n" | Out-File 'output.txt' -Append
}
when you are connected to ip's script show you you are connected to
Related
I am a PowerShell newbie trying to make a TCP and UDP port scanner. My code has a switch statement, where if you press 1 and enter that goes to the TCP function, 2 and enter the UDP function, or Q to quit it.
The issue is that the switch doesn't work properly. If you press 1 for TCP that just gives you the syntax prompt and asks you to press enter, then just ends the script - the same for TCP option 2. The Q button to quit works fine.
Can someone help? I can't spot what is wrong. My code is below
Thank you
Write-Host "Port Scanner"
function Show-Menu
{
param (
[string]$Title = 'Options'
)
Clear-Host
Write-Host "================ $Title ================"
Write-Host "1: Press '1' for TCP."
Write-Host "2: Press '2' for UDP."
Write-Host "Q: Press 'Q' to quit."
}
do
{
Show-Menu
$selection = Read-Host "Make a selection"
switch ($selection)
{
'1' {
Write-Host "To scan for open ports, run the following command: scan-port [devicename] -ports"
Function Scan-Port {
Param([string]$computername=$env:computername,
[array]$ports=#("21","22","23","25","80","443","3389")
)
#turn off error pipeline
$ErrorActionPreference = "SilentlyContinue"
#set values for Write-Progress
$activity="Port Scan"
$status="Scanning $computername"
$i=0
foreach ($port in $ports){
$i++
Write-Progress -Activity $activity -status $status `
-currentoperation "port $port" -percentcomplete (($i/$ports.count)*100)
#create empty custom object
$obj=New-Object PSObject
$obj | Add-Member -MemberType Noteproperty -name "Computername" -value $computername.ToUpper()
$obj | Add-Member -MemberType Noteproperty -name "Port" -value $port
$tcp=New-Object System.Net.Sockets.TcpClient($computername, $port)
if ($tcp.client.connected) {
$obj | Add-Member -MemberType Noteproperty -name "PortOpen" -value $True
[string]$rep=$tcp.client.RemoteEndPoint
[string]$ip=$rep.substring(0,$rep.indexof(":"))
}
else {
# Write-Warning "$computername not open on port: $port"
$obj | Add-Member -MemberType Noteproperty -name "PortOpen" -value $False
} #end Else
write $obj
$obj | Export-Csv -Append -path '.\PortScanResults.csv' -Delimiter ";" -Force
#disconnect the socket connection
$tcp.client.disconnect($False)
} #end foreach
#dispose and disconnect
$tcp.close()
Write-Progress -Activity $activity -status "Complete" -Completed
} #end function
} '2' {
Function port-scan-udp {
param($hosts,$ports)
if (!$ports) {
Write-Host "usage: test-port-udp <host|hosts> <port|ports>"
Write-Host " e.g.: test-port-udp 192.168.1.2 445`n"
return
}
$out = ".\scanresults.txt"
foreach($p in [array]$ports) {
foreach($h in [array]$hosts) {
$x = (gc $out -EA SilentlyContinue | select-string "^$h,udp,$p,")
if ($x) {
gc $out | select-string "^$h,udp,$p,"
continue
}
$msg = "$h,udp,$p,"
$u = new-object system.net.sockets.udpclient
$u.Client.ReceiveTimeout = 500
$u.Connect($h,$p)
# Send a single byte 0x01
[void]$u.Send(1,1)
$l = new-object system.net.ipendpoint([system.net.ipaddress]::Any,0)
$r = "Filtered"
try {
if ($u.Receive([ref]$l)) {
# We have received some UDP data from the remote host in return
$r = "Open"
}
} catch {
if ($Error[0].ToString() -match "failed to respond") {
# We haven't received any UDP data from the remote host in return
# Let's see if we can ICMP ping the remote host
if ((Get-wmiobject win32_pingstatus -Filter "address = '$h' and Timeout=1000 and ResolveAddressNames=false").StatusCode -eq 0) {
# We can ping the remote host, so we can assume that ICMP is not
# filtered. And because we didn't receive ICMP port-unreachable before,
# we can assume that the remote UDP port is open
$r = "Open"
}
} elseif ($Error[0].ToString() -match "forcibly closed") {
# We have received ICMP port-unreachable, the UDP port is closed
$r = "Closed"
}
}
$u.Close()
$msg += $r
Write-Host "$msg"
echo $msg >>$out
}
}
}
}
}
pause
}
until ($selection -eq 'q')
I have another issue I am hoping you can help with. The TCP bit now works but the UDP scanner isn't accepting any paramaters. If I put in the syntax and then press Enter nothing happens. Thanks
} '2' {
Function portudp {
Param($hosts,$ports)
if (!$ports) {
Write-Host "usage: portudp <host|hosts> <port|ports>"
Write-Host " e.g.: portudp 192.168.1.2 445`n"
return
}
$out = ".\scanresults.txt"
foreach($p in [array]$ports) {
foreach($h in [array]$hosts) {
$x = (gc $out -EA SilentlyContinue | select-string "^$h,udp,$p,")
if ($x) {
gc $out | select-string "^$h,udp,$p,"
continue
}
$msg = "$h,udp,$p,"
$u = new-object system.net.sockets.udpclient
$u.Client.ReceiveTimeout = 500
$u.Connect($h,$p)
# Send a single byte 0x01
[void]$u.Send(1,1)
$l = new-object system.net.ipendpoint([system.net.ipaddress]::Any,0)
$r = "Filtered"
try {
if ($u.Receive([ref]$l)) {
# We have received some UDP data from the remote host in return
$r = "Open"
}
} catch {
if ($Error[0].ToString() -match "failed to respond") {
# We haven't received any UDP data from the remote host in return
# Let's see if we can ICMP ping the remote host
if ((Get-wmiobject win32_pingstatus -Filter "address = '$h' and Timeout=1000 and ResolveAddressNames=false").StatusCode -eq 0) {
# We can ping the remote host, so we can assume that ICMP is not
# filtered. And because we didn't receive ICMP port-unreachable before,
# we can assume that the remote UDP port is open
$r = "Open"
}
} elseif ($Error[0].ToString() -match "forcibly closed") {
# We have received ICMP port-unreachable, the UDP port is closed
$r = "Closed"
}
}
$u.Close()
$msg += $r
Write-Host "$msg"
echo $msg >>$out
$out | Export-Csv -Append -path '.\UDPPPortScanResults.csv' -Delimiter ";" -Force
}
}
}
portudp
}
}
pause
}
until ($selection -eq 'q')
UDP Function Output
This is my first program in powershell, Im trying to get from the user input and then pinging the IP address or the hostname, Creating text file on the desktop.
But if the user wants the add more than one IP I get into infinite loop.
Here Im asking for IP address.
$dirPath = "C:\Users\$env:UserName\Desktop"
function getUserInput()
{
$ipsArray = #()
$response = 'y'
while($response -ne 'n')
{
$choice = Read-Host '
======================================================================
======================================================================
Please enter HOSTNAME or IP Address, enter n to stop adding'
$ipsArray += $choice
$response = Read-Host 'Do you want to add more? (y\n)'
}
ForEach($ip in $ipsArray)
{
createFile($ip)
startPing($ip)
}
}
Then I creating the file for each IP address:
function createFile($ip)
{
$textPath = "$($dirPath)\$($ip).txt"
if(!(Test-Path -Path $textPath))
{
New-Item -Path $dirPath -Name "$ip.txt" -ItemType "file"
}
}
And now you can see the problem, Because I want the write with TIME format, I have problem with the ForEach loop, When I start to ping, And I cant reach the next element in the array until I stop
the cmd.exe.
function startPing($ip)
{
ping.exe $ip -t | foreach {"{0} - {1}" -f (Get-Date), $_
} >> $dirPath\$ip.txt
}
Maybe I should create other files ForEach IP address and pass params?
Here's a old script I have. You can watch a list of computers in a window.
# pinger.ps1
# example: pinger yahoo.com
# pinger c001,c002,c003
# $list = cat list.txt; pinger $list
param ($hostnames)
#$pingcmd = 'test-netconnection -port 515'
$pingcmd = 'test-connection'
$sleeptime = 1
$sawup = #{}
$sawdown = #{}
foreach ($hostname in $hostnames) {
$sawup[$hostname] = $false
$sawdown[$hostname] = $false
}
#$sawup = 0
#$sawdown = 0
while ($true) {
# if (invoke-expression "$pingcmd $($hostname)") {
foreach ($hostname in $hostnames) {
if (& $pingcmd -count 1 $hostname -ea 0) {
if (! $sawup[$hostname]) {
echo "$([console]::beep(500,300))$hostname is up $(get-date)"
$sawup[$hostname] = $true
$sawdown[$hostname] = $false
}
} else {
if (! $sawdown[$hostname]) {
echo "$([console]::beep(500,300))$hostname is down $(get-date)"
$sawdown[$hostname] = $true
$sawup[$hostname] = $false
}
}
}
sleep $sleeptime
}
pinger microsoft.com,yahoo.com
microsoft.com is down 11/08/2020 17:54:54
yahoo.com is up 11/08/2020 17:54:55
Have a look at PowerShell Jobs. Note that there are better and faster alternatives (like thread jobs, runspaces, etc), but for a beginner, this would be the easiest way. Basically, it starts a new PowerShell process.
A very simple example:
function startPing($ip) {
Start-Job -ScriptBlock {
param ($Address, $Path)
ping.exe $Address -t | foreach {"{0} - {1}" -f (Get-Date), $_ } >> $Path
} -ArgumentList $ip, $dirPath\$ip.txt
}
This simplified example does not take care of stopping the jobs. So depending on what behavior you want, you should look that up.
Also, note there there is also PowerShell's equivalent to ping, Test-Connection
I have a PowerShell script that calls plink.exe regularly. Normally, the two output lines about keyboard-interactive prompts are simply annoying.
However, when run using Start-Job, they get output as error text as soon as I call Receive-Job.
Is there any way to suppress these? I'd rather not suppress all errors.
My test code:
$test_scriptblock = [scriptblock]::Create(#"
param(
`$argumentlist
)
`$pw = `$argumentlist.pw
& 'C:\Program Files\Putty\Plink.exe' -ssh `"admin#*.*.*.*" -pw `$pw -batch whoami
"#)
$testParm = #{
pw = Read-Host "password"
}
$testjob = Start-Job -scriptblock $test_scriptblock -Argumentlist $testParm
$i = 0
do {
$i++
sleep 2
$results = Receive-Job $testjob
ForEach ($result in $results) {
Write-Host $result
}
if ($testjob.State -eq "Completed") {
$jobcompleted = $true
}
If ($i -gt 10) {
Stop-job $testjob
$jobcompleted = $true
}
} until ($jobcompleted)
Just add the stderr redirect to your plink or pscp commandline, to an extra dummy file, like
pscp ... 2> stderr.txt
With a caveat that it may swallow other valid error msgs, at your own risk :)
There's no way to suppress keyboard-interactive prompts in Plink.
I suggest you use a native .NET SSH implementation, like SSH.NET instead of driving an external console application.
Ir was a bit cumbersome, but finally I managed to suppress the "keyboard-interactive" messages this way:
[String] $Plink = 'C:\Program Files\PuTTY\plink.exe'
[Array] $PlinkPar = #("-ssh", "-l", $usr, "-pw", $pwd, $hst) # Set plink parameters
[Boolean] $PlinkOK = $True
Write-Host "Accept possibly unknown host key"
"y" | & $Plink $PlinkPar "exit" 2>&1 | Tee-Object -Variable PlinkOut | Out-Null
$PlinkOut | Foreach-Object {
$PlinkStr = $_.ToString()
If ($_ -is [System.Management.Automation.ErrorRecord]) {
If (! $PlinkStr.Contains("eyboard-interactive")) {
Write-Host "Error: $PlinkStr"
$PlinkOK = $False
}
} else {
Write-Host "$PlinkStr"
}
}
If (! $PlinkOK) { exit }
$PlinkPar += "-batch
And the output is like this:
>powershell .\InstCR.ps1 -usr myuser -pwd mypassword -hst doesnotexist
Accept possibly unknown host key
Error: Unable to open connection:
Error: Host does not exist
This plink call is just to accept a possibly unknown host key (without "-batch" and piping the "y" to answer the prompt). Then "-batch" is added to the Plink parameters to be used on all subsequent plink calls.
I'm trying to make a windows 10 batch file that swap my dns from 2 options. The first one is 8.8.8.8 (google dns) and the second one are a custom DNS that allows me to watch US netflix (like 90.90.90.90).
The bash command to change dns is
netsh interface ipv4 add dnsserver "Ethernet" address=8.8.8.8 index=1
the command to check which dns is up and running is:
nslookup
Now i want to do an IF THEN ELSE that work like this:
if (dns == 8.8.8.8)
then (change them to 90.90.90.90)
else (change them to 8.8.8.8)
Even a powershell script is fine
This should work:
#echo off
set "dns=8.8.8.8"
for /F "skip=1 tokens=2" %%a in ('nslookup^<NUL') do if "%%a" equ "8.8.8.8" set "dns=90.90.90.90"
netsh interface ipv4 add dnsserver "Ethernet" address=%dns% index=1
Basically:
# Setup the dos command process information:
$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = "nslookup"
$pinfo.Arguments = ""
$pinfo.UseShellExecute = $false
$pinfo.CreateNoWindow = $true
$pinfo.RedirectStandardOutput = $true
$pinfo.RedirectStandardError = $true
# Start the process:
$process.Start() | Out-Null
# Wait a while for the process to do something:
sleep -Seconds 5
# If the process is still active kill it:
if (!$process.HasExited) {
$process.Kill()
}
# get output from stdout and stderr:
$stdout = $process.StandardOutput.ReadToEnd()
$stderr = $process.StandardError.ReadToEnd()
# Parse the IP address from the output:
$regex = [regex] "\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b"
$regex.Matches($stdout) | %{ $Condition = $_.value }
# Do your dns change based on the output:
if ($Condition -eq "8.8.8.8")
{
"set other dns"
}
Else
{
"set google dns"
}
Sorry I couldn't mature the answer a bit more; unexpected visitors.
-Edit:
Taking into account some of the comments, and a bug with my regular expression, this might be a more suitable solution:
[cmdletbinding()]
param ([parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] [string[]]$ComputerName = $env:computername )
begin {}
process {
# get current DNS setting:
foreach ($Computer in $ComputerName) {
if(Test-Connection -ComputerName $Computer -Count 1 -ea 0)
{
try {
$Networks = Get-WmiObject Win32_NetworkAdapterConfiguration -ComputerName $Computer -EA Stop | ? {$_.IPEnabled}
}
catch
{
Write-Warning "Error occurred while querying $computer."
Continue
}
foreach ($Network in $Networks)
{
$Condition = $Network.DNSServerSearchOrder;
break;
}
}
}
# Do your dns change based on the output:
if ($Condition -eq "8.8.8.8")
{
"set other dns";
}
Else
{
"set google dns";
}
}
end {}
I'm currently trying to put together a script that queries AD for a list of computers, pings the computers to determine which ones are still active, and then telnets into a specific port on all the pingable computers. The output I'm looking for is a full list of pingable computers in AD for which I can't telnet to the said port.
I've read these few questions, but they don't quite hit on what I'm trying to do. I just want to see if the telnet connection is successful without entering telnet (or automate the quitting of telnet) and move on to the next machine to test. The AD and pinging portions of my script are set, I'm just stuck here. The things I've tried haven't quite worked as planned.
Here is the code for the first parts of the script, if needed:
Get-ADComputer -Filter * -SearchBase 'DC=hahaha,DC=hehehe' | ForEach {
$computerName = $_.Name
$props = #{
ComputerName = $computerName
Alive = $false
PortOpen = $false
}
If (Test-Connection -ComputerName $computerName -Count 1 -Quiet) {
$props.Alive = $true
}
Adapting this code into your own would be the easiest way. This code sample comes from the PowerShellAdmin wiki. Collect the computer and port you want to check. Then attempt to make a connection to that computer on each port using Net.Sockets.TcpClient.
foreach ($Computer in $ComputerName) {
foreach ($Port in $Ports) {
# Create a Net.Sockets.TcpClient object to use for
# checking for open TCP ports.
$Socket = New-Object Net.Sockets.TcpClient
# Suppress error messages
$ErrorActionPreference = 'SilentlyContinue'
# Try to connect
$Socket.Connect($Computer, $Port)
# Make error messages visible again
$ErrorActionPreference = 'Continue'
# Determine if we are connected.
if ($Socket.Connected) {
"${Computer}: Port $Port is open"
$Socket.Close()
}
else {
"${Computer}: Port $Port is closed or filtered"
}
# Apparently resetting the variable between iterations is necessary.
$Socket = $null
}
}
Here is a complete powershell script that will:
1. read the host and port details from CSV file
2. perform telnet test
3. write the output with the test status to another CSV file
checklist.csv
remoteHost,port
localhost,80
asdfadsf,83
localhost,135
telnet_test.ps1
$checklist = import-csv checklist.csv
$OutArray = #()
Import-Csv checklist.csv |`
ForEach-Object {
try {
$rh = $_.remoteHost
$p = $_.port
$socket = new-object System.Net.Sockets.TcpClient($rh, $p)
} catch [Exception] {
$myobj = "" | Select "remoteHost", "port", "status"
$myobj.remoteHost = $rh
$myobj.port = $p
$myobj.status = "failed"
Write-Host $myobj
$outarray += $myobj
$myobj = $null
return
}
$myobj = "" | Select "remoteHost", "port", "status"
$myobj.remoteHost = $rh
$myobj.port = $p
$myobj.status = "success"
Write-Host $myobj
$outarray += $myobj
$myobj = $null
return
}
$outarray | export-csv -path "result.csv" -NoTypeInformation
result.csv
"remoteHost","port","status"
"localhost","80","failed"
"asdfadsf","83","failed"
"localhost","135","success"