I have a Powershell script that shuts down a list of servers which runs fine, but a part of the code is supposed to continually PING until the the server reports shutdown.
foreach ($line in Get-Content $GetLocation) {
if($line -match $regex){
echo "Server Name = $line "
# Stop-Computer -ComputerName $Line -Confirm
ping $line -t
}
}
Is there a way to get the script to stop when the PING returns a non-response of "Request Timed Out". Is the switch T the right switch to use? Any advice on this?
You don't want a continuous ping, you want to continuously ping it until it fails.
foreach ($line in Get-Content $GetLocation) {
if($line -match $regex){
echo "Server Name = $line "
# Stop-Computer -ComputerName $Line -Confirm
while((Test-Connection $line -Count 1 -Quiet) -eq $true){}
}
}
You may want to put a sleep in there as well.
foreach ($line in Get-Content $GetLocation) {
if($line -match $regex){
echo "Server Name = $line "
# Stop-Computer -ComputerName $Line -Confirm
while((Test-Connection $line -Count 1 -Quiet) -eq $true){
Start-Sleep -Seconds 1
}
}
}
Related
as i was wondering why my script takes so long i was seachring on google and also here in stackoverflow.
But all that i could find any close to helpful was this one here, Powershell Script Running Slowly
As I'm still pretty new to Powershell this is a little complicated to get through and take over to my script as i dont know how to handle those mentiond things anyway as i never heard of it before.
My Script is pretty easy and just gives me some Informations if there is something that returns an echo or not.
I wanted to "scan" our entire Network so I made an csv with out local Networks IP's and pass it to Powershell to "Ping" those.
But I realised that the "was not responing" part takes a long time to execute.
$list = Import-Csv -Path D:\ipcheck3.csv -UseCulture
$x=$list.IP
$ErrorActionPreference = "SilentlyContinue"
foreach ($y in $x)
{
try
{
if(Test-Connection $y -Count 1 -quiet)
{
write-host "$y responded"
$y | Export-Csv -Path D:\PingSucceded.csv -Append
}
else
{
Write-Host "$y was not responding"
$y | Export-Csv -Path D:\Pingfailed.csv -Append
}
}
catch
{
Write-Warning "Other Error occured"
}
}
There are not only Windows Clients out there so WMI is not an option and I don't know how to achvie this otherwise
EDIT:
After the Workflow input this is my "Try Version"
workflow Test-IPrange
{
Param
(
$IPs
)
$tocheck= $IPs.IP
foreach -parallel ($IP in $tocheck)
{
$pingsucceed = Test-Connection $IP -Count 1 -quiet
if($pingsucceed -eq "True")
{
$IP | Export-Csv -Path D:\testj.csv -Append
}
else
{
$IP | Export-Csv -Path D:\testn.csv -Append
}
}
}
Test-IPrange -IPs $(Import-Csv -Path D:\ipcheck3.csv -UseCulture)
My Output of Workflow Try
#TYPE System.String
PSComputerName,"PSShowComputerName","PSSourceJobInstanceId","Length"
localhost,"True","4e208e38-f7c2-492f-9d81-6583a103c3ac","12"
localhost,"True","4e208e38-f7c2-492f-9d81-6583a103c3ac","12"
With the Help of #Fourat
i edited my code to this form
Function Custom-Ping {
Param(
[string]$Address
)
$ping = ping $Address /w 1 /n 1
$result = ![string]::IsNullOrEmpty($ping -Like "*(0% Verlust)*")
return $result
}
$list = Import-Csv -Path D:\ipcheck3.csv -UseCulture
$x=$list.IP
$ErrorActionPreference = "SilentlyContinue"
foreach ($y in $x)
{
try
{
if(Custom-Ping $y)
{
Write-Host "$y responded"
$y | Export-Csv -Path D:\PingsuccededV3.csv -Append
}
else
{
Write-Host "$y was not responding"
$y | Export-Csv -Path D:\PingfailedV3.csv -Append
}
}
catch
{
Write-Warning "Textline from CMD Command or other Error"
}
}
which works properly good and is faster
I think that your process time is spoiled by the timeouts. If all your IPs are in the local network, try to reduce the timeout (because the default value is 5 seconds).
If you have Powershell 6 :
Test-Connection $y -Count 1 -quiet -TimeoutSeconds 1
If you don't, just use ping :
ping 58.47.45.1 /w 1 /n 1
You can also use a parallel for each loop, but it won't help much if you have multiple fails :
ForEach -Parallel ($x in $y)
{
...
}
UPDATE
In order to handle ping results, you can use a function like this (I used the keyword 'perte' because my computer is in French) :
Function Custom-Ping {
Param(
[string]$Address
)
$ping = ping $Address /w 1 /n 1
$result = ![string]::IsNullOrEmpty($ping -Like "*(perte 0%)*")
return $result
}
I've used Workflow to solve this issue my self. It's a few years ago I did it, so something better and newer is out there. But this works great for me...
I've ping over 2000 computers within a few Min...
workflow Test-ComputersConnection
{
Param
(
# Param1 help description
$Computernames#,
# Param2 help description
# [int]
# $Param2
)
foreach -parallel ($ComputerName in $Computernames)
{
$ConnectionTest = Test-Connection -ComputerName $ComputerName -ErrorAction SilentlyContinue -Count 1
if ($ConnectionTest.Address -eq $ComputerName) {
Write-Output $(Add-Member -MemberType NoteProperty -Name "Computername" -Value $ComputerName -InputObject $ConnectionTest -PassThru )
#Write-Verbose -Verbose -Message "[$($ComputerName)]: Replays on Ping."
}
Else {
#Write-Verbose -Verbose -Message "[$($ComputerName)]: Do not replays on Ping."
}
}
}
$OnlineNow0 = Test-ComputersConnection -Computernames $( Import-Csv -Path D:\ipcheck3.csv -UseCulture |
Select-Object -ExpandProperty name)
The code above is a quick edit of what I use... You will need to edit the $(Import ...) statement first, to make sure the PC name is being deliveret to the workflow.
I've just testet on my own computer and it gave me a reply...
I have a large list of hostnames I need to ping to see if they are up or down. I'm not really that great at scripting but I managed to figure this much out:
$names = Get-content "hnames.txt"
foreach ($name in $names){
if (Test-Connection -ComputerName $name -Count 1 -ErrorAction SilentlyContinue){
Write-Host "$name is up" -ForegroundColor Green
}
else{
Write-Host "$name is down" -ForegroundColor Red
}
}
This gets me what I need but i now need to write out these results to a csv file and i have no idea how to do that.
Please Help!
You can use the following code instead (I simply altered the write-host calls to CSV formatting) and execute it with "PowerShell.exe script.ps > output.csv"
Note that you must execute it from the folder that contains hnames.txt, or simply change the "hnames.txt" to a full path.
$names = Get-content "hnames.txt"
foreach ($name in $names){
if (Test-Connection -ComputerName $name -Count 1 -ErrorAction SilentlyContinue){
Write-Host "$name,up"
}
else{
Write-Host "$name,down"
}
}
P.S. You can also use the Out-File Cmdlet to create the CSV file
I am a complete newbie to Powershell, so I took this on as a learning task, as I needed a quick and simple way to check a list of PC's for up/down status. These tweaks were needed to get it to output cleanly to the screen and to a txt file
$Output= #()
$names = Get-content "hnames.txt"
foreach ($name in $names){
if (Test-Connection -ComputerName $name -Count 1 -ErrorAction SilentlyContinue){
$Output+= "$name,up"
Write-Host "$Name,up"
}
else{
$Output+= "$name,down"
Write-Host "$Name,down"
}
}
$Output | Out-file "C:\support\result.csv"
$Output= #()
$names = Get-Content ".\input\Servers.txt"
foreach ($name in $names){
if (Test-Connection -Delay 15 -ComputerName $name -Count 1 -ErrorAction SilentlyContinue -quiet){
$Output+= "$name,up"
Write-Host "$Name,up" -ForegroundColor Green
}
else{
$Output+= "$name,down"
Write-Host "$Name,down" -ForegroundColor Red
}
}
$Output | Out-file ".\output\result.csv"
This is a tad cleaner, and includes the original foreground options but, BTW, the 'delay' switch seems to be ignored -PB
I would do it this way. Using a list of computers and -asjob works very well. The Responsetime property (confusingly the header is "Time(ms)") will be non-null if the host is up.
$names = Get-content hnames.txt
test-connection $names -asjob -count 1 | receive-job -wait -auto
Source Destination IPV4Address IPV6Address Bytes Time(ms)
------ ----------- ----------- ----------- ----- --------
COMP001 yahoo.com 74.6.231.21 32 39
COMP001 microsoft.com 40.113.200.201 32
Lately I do it this way. It requires threadjobs installed in powershell 5.1. Or just use get-port. I stick it in a mymod\mymod.psm1 module file somewhere in $env:psmodulepath. I can check a classroom in under 10 seconds.
function get-pport { # multi-threaded
param($list)
$list |
% { $_ | start-threadjob { get-port $input } -throttlelimit 20 } |
receive-job -wait -auto
}
function Get-Port {
Param (
[parameter(ValueFromPipeline)]
[string[]]$Hostname='yahoo.com'
)
begin {
$ports = 22,5988,3389,5985
$ping = New-Object System.Net.Networkinformation.ping
$Timeout = 200 # ms
}
process {
$hostname | foreach {
$openPorts = #()
foreach ($port in $ports) {
$client = New-Object System.Net.Sockets.TcpClient
$beginConnect = $client.BeginConnect($_,$port,$null,$null)
Start-Sleep -Milli $TimeOut
if($client.Connected) { $openPorts += $port }
$client.Close()
}
$result = $Ping.Send($_, $timeout)
if (! $result) { write-error "hostname $_ not found" }
$pingstatus = ($result.status -eq 'Success')
New-Object -typename PSObject -Property #{
HostName = $_
Port = $openPorts
Ping = $pingstatus
} | select hostname,port,ping
} # end foreach
} # end process
}
Example:
$avid = cat afile.txt
pport $avid
HostName Port Ping
-------- ---- ----
A006 {3389, 5985} True
A011 {3389, 5985} True
A015 {3389} True
I have a scenario where I have below details in log.ini file.
Hostname : DLC1MQF
IP Address : 10.210.208.102
Temporary IP : 10.212.215.91
Subnet Mask : 255.255.248.0
Gateway : 10.212.208.1
What I am really looking here is I want check Default gateway is pinging or not. if not pinging I need to capture the IP,Subnet and Gateway from log.ini and assign that to my NIC.
Currently I am able to write a script like below and the problem is I am getting an error message that else is not a recognised command here.
$imagelog = Get-Content C:\Windows\ImageLog.ini
foreach ($line in $imagelog) {
if ($line -like "*Gateway*") {
$line | out-file -FilePath "C:\Windows\Gateway.txt"
}
}
$gatewayIP = get-content c:\windows\Gateway.txt
$GIP = $gatewayIp -replace '.*:.'.Trim()
if ( Test-Connection -ComputerName $Gip -Count 1 -ErrorAction SilentlyContinue ) {
Write-Host $GIP `t $GIP `t Ping Success -ForegroundColor Green
}
else{
$details = get-content c:\windows\imagelog.ini
foreach ($line in $details) {
if ($line -like "*IP Address*") {
$line | out-file -FilePath "C:\Windows\IP Address.txt"
}
#}
$IP = get-content c:\windows\IP Address.txt
$systemip = $Ip -replace '.*:.'.Trim()
foreach ($line in $details) {
if ($line -like "*Subnet Mask*") {
$line | out-file -FilePath "C:\Windows\subnet.txt"
}
}
$subnet = get-content c:\windows\subnet.txt
$subnetip = $subnet -replace '.*:.'.Trim()
netsh interface ip set address "Local Area Connection" static $systemip $subnetip $GIP
foreach ($line in $details) {
if (Hostname.StartsWith("LCG")) {
Set-DNSClientServerAddress –Local Area Connection –ServerAddresses (“10.0.6.65”,”10.0.25.65”,"10.0.0.1")
}
elseif (Hostname.StartsWith("ENG")) {
Set-DNSClientServerAddress –Local Area Connection –ServerAddresses (“10.80.38.33”,”10.0.25.65”,"10.0.0.1")
}
}
}
}
Any help is much appreciated.
You can wrap the test-connection in a try/catch block
Try{
test-connection $IP -ErrorAction Stop | out-null
write-output "working"
...do working stuff...
}Catch{
...do failed stuff...
write-output "failed"
}
I am trying to find a way (maybe with a job?) to timeout system jobs. It seems my regquery will normally time out after 42 seconds if the computer is not online, and the code can't reach the registry. I am looking to limit the query to around 5 seconds as I have a tonne of computers in our environment. I have tried to play around with creating jobs, a stopwatch, etc. but no luck :( Please help!
$File = Import-Csv 'c:\temp\regcomplist.txt'
$Results=""
$text="Machine Name,Regkey Value, Runtime"
$fileout = "C:\Temp\regquery.csv"
Write-host $text
Out-File -FilePath $fileout -InputObject $text -Force
$timeout = new-timespan -Seconds 5
$swtotal = [Diagnostics.Stopwatch]::StartNew()
foreach ($line in $file)
{
TRY{
$regkey = ""
$keyValue = ""
$machinename = $line.machinename
#trap [Exception] {continue}
$key = "SYSTEM\\CurrentControlSet\\Control\\Print\\Environments\\Windows x64\\Drivers\\Version-3\\Lexmark Universal v2 XL"
$sw = [Diagnostics.Stopwatch]::StartNew()
#while ($sw.elapsed -lt $timeout){
$reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey("LocalMachine",$MachineName)
$regkey = $reg.opensubkey($key)
$keyValue = $regKey.GetValue('Help File')
# return
#}
#start-sleep -seconds 5
#if ($ok -ne "OK"){$keyValue = "TIMED OUT: "+$sw.elapsed}
}
Catch{
$keyValue = "Error Opening Registry"
}
$text = $machinename+","+$keyValue+","+$sw.elapsed
$Results += $text
#Output Below Here:
Write-host $text
Out-File -InputObject $text -FilePath $fileout -Append
}
Write-host "Total time run:"$swtotal.elapsed
Thanks! That was exactly what I needed. I set the count to 1 ping, and it's MUCH faster than 42 second timeouts. Here is the code for anyone this might help...
$File = Import-Csv 'c:\temp\powershell\regcomplist.txt'
$Results=""
$text="Machine Name,Regkey Value, Runtime"
$fileout = "C:\Temp\powershell\regquery.csv"
Write-host $text
Out-File -FilePath $fileout -InputObject $text -Force
$timeout = new-timespan -Seconds 5
$swtotal = [Diagnostics.Stopwatch]::StartNew()
foreach ($line in $file){
$regkey = ""
$keyValue = ""
$machinename = $line.machinename
#trap [Exception] {continue}
$key = "SYSTEM\\CurrentControlSet\\Control\\Print\\Environments\\Windows x64\\Drivers\\Version-3\\Lexmark Universal v2 XL"
$sw = [Diagnostics.Stopwatch]::StartNew()
$pingtest=Test-Connection -ComputerName $machinename -Quiet -Count 1
if ($pingtest -eq $true ){
TRY{
#while ($sw.elapsed -lt $timeout){
$reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey("LocalMachine",$MachineName)
$regkey = $reg.opensubkey($key)
$keyValue = $regKey.GetValue('Help File')
# return
#}
#start-sleep -seconds 5
#if ($ok -ne "OK"){$keyValue = "TIMED OUT: "+$sw.elapsed}
}
Catch{
$keyValue = "Error Opening Registry"
}
$text = $machinename+","+$keyValue+","+$sw.elapsed
$Results += $text
#Output Below Here:
Write-host $text
Out-File -InputObject $text -FilePath $fileout -Append
}
else {write-host $machinename",doesn't ping!"}
}
Write-host "Total time run:"$swtotal.elapsed
I use a Tcp Test before querying registry:
if((Test-ComputerPort $ComputerName 445)) {
[Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $ComputerName).OpenSubKey("SYSTEM\CurrentControlSet\Control\Session Manager\Environment").GetValue('TEMP')
}
with Test-ComputerPort() :
function Test-ComputerPort($ComputerName,$port){
try {
$ip = [System.Net.Dns]::GetHostAddresses($ComputerName).IPAddressToString # confirm DNS resolving
if([system.Net.Sockets.TcpClient]::new().BeginConnect($ComputerName, $port, $null, $null).AsyncWaitHandle.WaitOne(40, $false) -or [system.Net.Sockets.TcpClient]::new().BeginConnect($ComputerName, $port, $null, $null).AsyncWaitHandle.WaitOne(80, $false)){ # with retry if down
return $ComputerName
}
return $false # tcp-port is down !
} catch {
return $null # conputername not resolved !
}
}
im currently finishing my PS script to get the time from a list of servers and export them to a .txt file. Thing is that servers with connection problems gives just a PS error. I want that servers with connection issues get logged also and by just a message i.e "Server server name not reachable". Thanks a lot for your help!
cls
$server = Get-Content srvtime_list.txt
Foreach ($item in $server)
{
net time \\$item | find /I "Local time" >> srvtime_result.txt
}
I'd probably rewrite your code a bit:
Get-Content srvtime_list.txt |
ForEach-Object {
$server = $_
try {
$ErrorActionPreference = 'Stop'
(net time \\$_ 2>$null) -match 'time'
} catch { "Server $server not reachable" }
} |
Out-File -Encoding UTF8 srvtime_result.txt
There are other/better ways to get the time (as others suggested) but to answer your question:
You can suppress errors by redirecting the error stream to null.
Check the $LASTEXITCODE variable, any result other than 0 means the command did not completed successfully.
Get-Content srvtime_list.txt | Foreach-Object{
net time \\$_ 2>$null | find /I "Current time" >> srvtime_result.txt
if($LASTEXITCODE -eq 0)
{
$result >> srvtime_result.txt
}
else
{
"Server '$_' not reachable" >> srvtime_result.txt
}
}
I'd do something like this:
Get-Content srvtime_list.txt | %{
$a = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $_ -erroraction 'silentlycontinue'
if ($a) { "$_ $($a.ConvertToDateTime($a.LocalDateTime))" } else { "Server $_ not reachable" }
} | Set-Content srvtime_result.txt
Use the Test-Connection cmdlet to verify that the remote system is reachable.
cls
$server = Get-Content srvtime_list.txt
Foreach ($item in $server)
{
if (test-connection $item) {
net time \\$item | find /I "Local time" >> srvtime_result.txt
} else {
"$item not reachable" | out-file errors.txt -append
}
}
But you can do this in pure Powershell, without resorting to net time - use WMI. This is untested as I don't have Windows handy at the moment, but it's at least 90% there.
cls
$server = Get-Content srvtime_list.txt
$ServerTimes = #();
Foreach ($item in $server)
{
if (test-connection $item) {
$ServerTimes += Get-WMIObject -computername $name win32_operatingsystem|select systemname,localdatetime
} else {
"$item not reachable" | out-file errors.txt -append
}
}
$ServerTimes |Out-File srvtime_result.txt