Is there a way to disable and enable network connection when internet is lost - powershell

this is my first post so hopefully I am doing it right.
I am looking for a way to automate the process of disabling my Ethernet network connection and then reenable it.
I am on a camp internet connection (work away for weeks at a time) and the internet connection is fine besides it dropping out every half an hour or so and requiring me to refresh the connection. Usually I just disconnect the Ethernet plug for a second and plug it back in but I am wanting to automate the process so I can remote connect to the pc when I am away.
Any help on how to accomplish this would be appreciated.
Edit: I left a vital part out of my description. I am wanting the script to detect when the internet connection is lost and then refresh the connection. Once the connection is lost it never regains it until I refresh the connection.

If you are using DHCP
Then Dollars to donuts you are just needing to renew your DHCP lease.
That is something you can do in CMD easily (I'm sure there is likely as easy a method in PS, but I don't know it offhand)
This will release the currently assigned DHCP Address wait a few seconds and then renew it.
IPConfig /Release & timeout 5 & IPConfig /Renew
At the CLI or in a batch script you could set up a do-while loop to loop until you return an expected up address if you wanted.
Example of a costume loop in cmd cli.
For /L %L (1,1,2147483648) DO (
IPConfig /Release & timeout 3 & IPConfig /Renew &timeout 3 &(
Ping -n 2 4.2.2.2 | find /I "Reply From" | find /I " 4.2.2.2" &&(
Exit /b 0
)
)
)
Disabling and enabling your NIC can be done through CMD or Powershell.
Here Powershell is easier.
Get only currently enabled adapters, use that to disable them wait a few seconds and enable them again
$NIC_Original_State = get-NetAdapter | ? {$_.Status -eq "Enabled"}
$NIC_Original_State | Disable-NetAdapter -name $_.name -confirm:$false
Sleep 5
$NIC_Original_State | Enable-NetAdapter -name $_.name -confirm:$false
Combine with checking the ping to the internet (as in cmd example) to generate a loop until we have a good connection.
$NIC_Original_State = get-NetAdapter | ? {$_.Status -eq "Enabled"}
DO {
$NIC_Original_State | Disable-NetAdapter -name $_.name -confirm:$false
Sleep 5
$NIC_Original_State | Enable-NetAdapter -name $_.name -confirm:$false
Sleep 5
} While ( ! $(test-connection -TargetName 4.2.2.2 -Quiet ) )
Now, PowerShell will need to be instantiated from a CMD prompt, I usually write a .cmd script wrapper for the .ps1 script to be called from in task scedualer or by hand.
Sometimes I will write a combo .cmd script with the ps code embedded and just create a hardline. To the original .cmd script as a. .ps1 to execute the ps code.
Although for something this simple it might be simplest to use PowerShell.exe to run the script as an in-line command.
But all roads lead to Rome, you do you on executing PowerShell, but in order to run it periodically, you will need to schedule the script to run, and by default there is no way to run ps scripts directly, so pick a method that you like that works for running the script and then use windows scheduler to schedule the task to run every x interval you like (say hourly)
Make surw you select a new task (not a basic task) and select run with highest priviledges" checkbox, and select the newest version of windows available in the compatibility list-box.
Make sure to provide a useename and password of an administrator, and select to save the password/run whether the user is logged on or not.
Set your triggers and put in the method you chose to execute the script and run some tests to make sure it works as expected.
Edit:
The original method is int he form DO { Action } WHILE ( Condition To Check Returns True) Since the While is at the end it guarantees one iteration.
Since you don't want to disable and enable the NIC even once if the internet is pingable, you can use a WHILE ( Condition To Check Returns True) { Action }
$NIC_Original_State = get-NetAdapter | ? {$_.Status -eq "Enabled"}
While ( ! $( test-connection -ComputerName 4.2.2.2 -Quiet ) ) {
$NIC_Original_State | Disable-NetAdapter -name $_.name -confirm:$false
Sleep 5
$NIC_Original_State | Enable-NetAdapter -name $_.name -confirm:$false
Sleep 5
}
You absolutely need to have a scheduled task to run this regularly.
Even if you want to only start it once and leave it in the background, and havit checking always (which we could do) you'll need to set up a scheduled task to kill it if still running and start it again at some interval as it's possible to have a process become unresponsive or be killed or have the computer restart etc and you forget to start the script etc.
This version will just loop every one minute to check, and only executes when the internet is not reachable.
while ($true) {
$NIC_Original_State = get-NetAdapter | ? {$_.Status -eq "Enabled"}
While ( ! $( test-connection -ComputerName 4.2.2.2 -Quiet ) ) {
$NIC_Original_State | Disable-NetAdapter -name $_.name -confirm:$false
Sleep 5
$NIC_Original_State | Enable-NetAdapter -name $_.name -confirm:$false
Sleep 5
}
sleep 60
}
Again, you ca just run this in powershell, sure, btu then if it gets stopped for any reason it won;t be started again.
Use task scheduler ad set up an action to run this o Startup and once an hour, killing any previously running copy of the script.
Usually for running a CMD script I just write a simple cmd script so if I want to edit any part of anything I only change the script never the task.
<# ## & REM Script Name:
#(
SETLOCAL ENABLEDELAYEDEXPANSION
ECHO OFF
(
NET SESSION 2>&1 >NUL
) || (
powershell.exe -Command "Start-Process cmd \"/k %~dpnx0\" -Verb RunAs"
pause
GOTO :EOF
)
IF EXIST "%~dpn0.ps1" (
DEL /Q /F %~dpn0.ps1"
)
MKLINK /H "%~dpn0.ps1" "%~f0"
)
Powershell.exe -ExecutionPolicy Bypass -File "%~dpn0.ps1"
(
ENDLOCAL
EXIT /B %_eLvl%
)
#>
## Script:
## Powershell Portion Begins
while ($true) {
$NIC_Original_State = get-NetAdapter | ? {$_.Status -eq "Enabled"}
While ( ! $( test-connection -ComputerName 4.2.2.2 -Quiet ) ) {
$NIC_Original_State | Disable-NetAdapter -name $_.name -confirm:$false
Sleep 5
$NIC_Original_State | Enable-NetAdapter -name $_.name -confirm:$false
Sleep 5
}
sleep 60
}

You can handle this using Get-NetAdapter , Enable-NetAdapter and Disable-NetAdapter commands.

Related

How to prevent multiple instances of the same PowerShell 7 script?

Context
On a build server, a PowerShell 7 script script.ps1 will be started and will be running in the background in the remote computer.
What I want
A safenet to ensure that at most 1 instance of the script.ps1 script is running at once on the build server or remote computer, at all times.
What I tried:
I tried meddling with PowerShell 7 background jobs (by executing the script.ps1 as a job inside a wrapper script wrapper.ps1), however that didn't solve the problem as jobs do not carry over (and can't be accessed) in other PowerShell sessions.
What I tried looks like this:
# inside wrapper.ps1
$running_jobs = $(Get-Job -State Running) | Where-Object {$_.Name -eq "ImportantJob"}
if ($running_jobs.count -eq 0) {
Start-Job .\script.ps1 -Name "ImportantJob" -ArgumentList #($some_variables)
} else {
Write-Warning "Could not start new job; Existing job detected must be terminated beforehand."
}
To reiterate, the problem with that is that $running_jobs only returns the jobs running in the current session, so this code only limits one job per session, allowing for multiple instances to be ran if multiple sessions were mistakenly opened.
What I also tried:
I tried to look into Get-CimInstance:
$processes = Get-CimInstance -ClassName Win32_Process | Where-Object {$_.Name -eq "pwsh.exe"}
While this does return the current running PowerShell instances, these elements carry no information on the script that is being executed, as shown after I run:
foreach ($p in $processes) {
$p | Format-List *
}
I'm therefore lost and I feel like I'm missing something.
I appreciate any help or suggestions.
I like to define a config path in the $env:ProgramData location using a CompanyName\ProjectName scheme so I can put "per system" configuration.
You could use a similar scheme with a defined location to store a lock file created when the script run and deleted at the end of it (as suggested already within the comments).
Then, it is up to you to add additional checks if needed (What happen if the script exit prematurely while the lock is still present ?)
Example
# Define default path (Not user specific)
$ConfigLocation = "$Env:ProgramData\CompanyName\ProjectName"
# Create path if it does not exist
New-Item -ItemType Directory -Path $ConfigLocation -EA 0 | Out-Null
$LockFilePath = "$ConfigLocation\Instance.Lock"
$Locked = $null -ne (Get-Item -Path $LockFilePath -EA 0)
if ($Locked) {Exit}
# Lock
New-Item -Path $LockFilePath
# Do stuff
# Remove lock
Remove-Item -Path $LockFilePath
Alternatively, on Windows, you could also use a scheduled task without a schedule and with the setting "If the task is already running, then the following rule applies: Do not start a new instance". From there, instead of calling the original script, you call a proxy script that just launch the scheduled task.

Stop a continuous Ping

I am trying to create Powershell script where
User will provide text file with ipaddress/ hostname and test Duration
• Script should take each ip address/ Hostname, open cmd/ powershell and run ping test
For this I created a bat file containing
ping -t %1 |find /v ""|cmd /q /v:on /c "for /l %%a in (0) do (set "data="&set /p "data="&if defined data echo(!Date! !time! !data!)" > %2
In powershell I am running it
$Script = "C:\Ping\pingTest\pingstat.cmd $hostName $outputFile"
$Runpingtest = cmd.exe /c $script
Please give me ideas to spot the contiguous ping as per test duration
You can't stop a continuous ping without signaling the ping process to stop. You would need to use ping /n COUNT for a number of pings.
But you can do something similar with pure PowerShell and Test-NetConnection, then export the results to CSV like so:
1..$pingCount | Foreach-Object {
Test-NetConnection google.com |
Select-Object #{
Name='Date'
Expression={ Get-Date -Format 'ddd MM/dd/yyyy hh:mm:ss.ff' }
}, ComputerName, SourceAddress, RemoteAddress, PingSucceeded,
#{
Name='Latency'
Expression={ "$($_.PingReplyDetails.RoundTripTime) ms" }
}
} | Export-Csv -NoTypeInformation C:\Path\To\file.csv
Note that Test-NetConnection doesn't have a -Count parameter, which is why the my above code wraps the Test-NetConnection call in a loop. You may also need to adjust the Get-Date format for your locale as my code uses a date formation suitable in the US.
Also note that I'm using Select-Object to create custom Date and Latency columns, as Test-NetConnection doesn't provide the current time it was run, and the ping latency is buried in a nested object.

Powershell script Stop process and proceed

I have a script where i run the shutdown.exe command on a list of computers. The script works fine until it hangs for some reason.
Is there a way that i can "ctrl + c" the shutdown command and then proceed to the next PC.
here is what im using.
buttonRestartWorkstations_Click={
#TODO: Place custom script here
$online = $checkedlistbox1.CheckedItems | where { Test-Connection -
ComputerName $_ -Count 1 -Quiet }
$computercount = $online.Items.Count
$progressbar1.Maximum = $online.Count
$progressbar1.Step = 1
$progressbar1.Value = 0
foreach ($computer in $online)
{
$progressbar1.PerformStep()
shutdown -r -t $textbox3.Text -m $computer
Start-Sleep -s 1
}
$label2.Visible = $true
$label2.Text = "Selected Servers will reboot on the " + $textbox1.text
The Restart-Computer cmdlet would give you the ability to target multiple computers in parallel, without a problem on one computer affecting execution on others.
As you state, Restart-Computer is not an option for you because you want to have a delay before the restart is initiated on a given computer (which is what shutdown -r -t <secs> gives you; note that while Restart-Computer does have a -Delay parameter, its purpose is different).
If:
your target computers are set up for PowerShell remoting
and you can run your script elevated (with administrative privileges)
you can use Invoke-Command to target the computers in parallel and then locally run shutdown.exe on them (PSv3+ syntax):
$delay = $textbox3.Text
Invoke-Command -ComputerName $online {
shutdown -r -t $using:delay
"$(('FAILED to initiate', 'Successfully initiated')[$LASTEXITCODE -eq 0]) reboot on $env:COMPUTERNAME."
} | ForEach-Object { $progressbar1.PerformStep() }
Just like with your original code, execution on each target computer will return once the restart has been initiated, though execution will happen in parallel, and the responses received from the target computers are not guaranteed to be in input order.
If you wanted to verify and wait for successful restarts, more work would be needed.
Any errors are printed to the console in red and can later be examined in the $Error collection.
Note the primary purpose of "$(('FAILED to initiate', 'Successfully initiated')[$LASTEXITCODE -eq 0]) reboot on $env:COMPUTERNAME." is to unconditionally produce some (non-error) output on each computer, so that the ForEach-Object script block is invoked for each (shutdown produces no stdout output by default, and stderr output is not acted on by ForEach-Object).

Errors running remote powershell script with PsEcec.exe

I have a powershell script on a remote windows box that finds the folder pointed to by a junction. The contents of the script looks like this:
return fsutil reparsepoint query C:\foo\bar\junction_name | where-object { $_ -imatch 'Print Name:' } | foreach-object { $_ -replace 'Print Name\:\s*','' }
When I run this on the remote box, it executes as expected :)
However, when I try to run this remotely from my local machine:
C:\Users\foo>C:\pstools\PsExec.exe \\remote_server_name "powershell D:\bar\my_script.ps1"
I get errors:
PsExec could not start powershell D:\bar\my_script.ps1 on
remote_server_name: The filename, directory name, or volume label
syntax is incorrect.
Any ideas what this error is telling me (given that I can run the script directly on the remote box with no issues)?
Thx!
1- maybe you should avoid psexec and take advantage of powershell remoting
invoke-command -computername remote_server_name -scriptblock {. "D:\bar\my_script.ps1"}
2- if you want to keep psexec, look at the starting directory switch -w
PsExec.exe \\remote_server_name -w D:\bar "powershell -file my_script.ps1"
PS Remoting would be the best way to go here and I'd actually put up a good fight for opening up TCP/5985 on your machines. The minuscule security risk is, by far, worth the management benefits you'll get with it.
Worst case scenario use the WMI Win32_Process class. Something like this might work.
$wmiParams = #{
'ComputerName' = 'Somecomputer'
'Class' = 'Win32_Process'
'Name' = 'Create'
'Args' = 'fsutil reparsepoint query C:\foo\bar\junction_name > C:\temp.txt'
}
Invoke-WmiMethod #wmiParams
Get-Content \\somecomputer\c$\temp.txt | where-object { $_ -imatch 'Print Name:' } | foreach-object { $_ -replace 'Print Name\:\s*', '' }
I managed to get the following to work:
PsExec.exe \\remote_server_name powershell.exe D:\bar\my_script.ps1
However, the powershell session did not close as expected and remained in a hanging state after my script returned so calling it via cmd as detailed here seems to fix that:
PsExec.exe \\remote_server_name cmd /c "echo . | powershell.exe D:\bar\my_script.ps1"
Thanks for all of the suggestions...

Windows Service operations on multiple remote servers in parallel

I have 3 servers on same network and having the same username and password to login, on which I want to perform following operations:
*Check if Service1 is running or not
*If it is running, stop the service
*Set Service1 to disable mode
*If it is not running, do nothing
*Check if Service2 is running
*If it is running, stop the service
*Set Service2 to disable mode
*If it is not running, do nothing
and so on for Service3,..4,..5...
I want to create a Master.bat file on 1st Server, which should perform above operations on all the 3 servers in parallel.
I am trying with below but not getting how i can do this for more than 1 services in same code and also its getting bit lengthy.Is there any other short method to do this in batch or powershell ?
original_serverlist.txt:
Server1
Server2
Server3
Code:
copy original_serverlist.txt serverlist.txt
:repeat
set currentserver=
for /f "eol=; tokens=1*" %%i in (serverlist.txt) do set currentserver=%%i
if "%currentserver%"=="" goto end
for /F "tokens=3 delims=: " %%H in ('SC query Service1 ^| findstr " STATE"') do (
if /I "%%H" "RUNNING" (
net stop Service1
echo Service1 stopped # %date% %time% > C:\result.txt
sc config Service1 start= disabled
echo Service1 set disabled # %date% %time% >> C:\result.txt
)
)
type serverlist.txt| find /v "%currentserver%"> serverlist1.txt
copy serverlist1.txt serverlist.txt
goto repeat
:end
del serverlist.txt
del serverlist1.txt
EDIT1
I am not that much comfortable in PS as in batch but i want to learn the things so putting my exact requirement here to learn how to implement it in powershell with code explanation.
I need to perform following same set of operations on all the three remote Servers in parallel (Server1,Server2,Server3) having Master.ps1 on Server1:
run command cmd /c iisreset/stop and set IIS service to Disabled mode
Stop service1 and set it to Disabled mode
Stop service2 and set it to Disabled mode
Stop service3 and set it to Disabled mode
Kill process jusched.exe
Kill process conhost.exe
In powershell 3.0
$services = "service1", "service2", "service3"
"server1", "server2", "server3" | % {
Get-Service -Computer $_ $services | ? State -eq "Running" | Stop-Service -Force -PassThru | Set-Service -StartupType Disabled
}
You need to enable powreshell remoting for this to work and add -Credential option anywhere you see -Computer argument if your current domain user doesn't have enough rights.
More generic option would be to use hash
$ServiceMap = #{ "server1": ("service11", "service12");
"server2": ("service21" ... ) }
You will also need to check for errors..
To achieve parallel execution you could do something like:
workflow set_services {
param(
[string[]]$Computers, [string[]]$Services
)
foreach -parallel ($computer in $computers) {
Get-Service -ComputerName $computer $services | ? State -eq "Running" | Stop-Service -Force -PassThru | Set-Service -StartupType Disabled
}
}
BTW, do not use batch scripts any more, anywhere.