Eliminate Inner Loop - powershell

i have two loops in my program.First loop for setting up the retry peocess and inner loop for testing a connection status.
for($retry=0;$retry<=3;$retry++)
{
while (!(Test-Connection "mycomputer"))
{
if (time exceed)
{
$status=$false
Write-Host "machine is offline"
break
}
}
if($status)
{
Write-Host "machine is online"
break
}
}
is there any way to eliminate the inner loop without changing the output

Not entirely sure what you mean by "time exceeded" - time to do what?
If you want to wait between Test-Connection attempts, you can introduce an artificial delay with Start-Sleep:
$Computer = "mycomputer"
$TimeoutSeconds = 5
for($retry=0; $retry -lt 3; $retry++)
{
if(Test-Connection -ComputerName $Computer -Count 1 -Quiet){
# Didn't work
Write-Host "Machine is offline"
# Let's wait a few seconds before retry
Start-Sleep -Seconds $TimeoutSeconds
} else {
Write-Host "Machine is online!"
break
}
}
The easiest way however, would be to use the Count and Delay parameters of Test-Connection:
$Status = Test-Connection -ComputerName $Computer -Count 3 -Delay $TimeoutSeconds

You don't have to use a loop as test-connection already have a count parameter

The other answers already go in depth about why you don't really need a loop but I wanted to add a solution to your refactoring question.
You can eliminate the inner loop and if statement by pre-initializing a $Result variable and changing it in your original loop if necessary.
Personally, I find this more readable (subjective) at the expense of an extra assignment up front.
$Result = "machine is online";
for($retry=0;$retry<=3;$retry++) {
while (!(Test-Connection "mycomputer"))
{
if (time exceed)
{
$Result = "machine is offline"
break
}
}
Write-Host $Result
Edit To test for multiple computers in parallel, I use following worflow
workflow Test-WFConnection {
param(
[string[]]$computers
)
foreach -parallel ($computer in $computers) {
Test-Connection -ComputerName $computer -Count 1 -ErrorAction SilentlyContinue
}
}
called as Test-WFConnection #("pc1", "pc2", ... , "pcn")
Not much of a difference when every computer is online but a world of difference when multiple computers are offline.

Related

Loop get-mailbox cmdlet until no error returned

I have hybrid setup where shared mailboxes are getting created on-prem and synced through to Exchange Online in a span of couple of minutes.
My routine is to create a shared mailbox on-prem and then convert it, populate, enable messagecopy, etc. through Connect-ExchangeOnline.
I want to have a tiny script to check if it synced to EO or not.
I've tried several different ways and seemingly this one should work, but unfortunately it breaks after both success or error without attempting to run get-mailbox in 10 seconds as I expect it to.
Please review and advise.
$ErrorActionPreference = 'SilentlyContinue'
$ID = "xxx#yyy"
$i=0
while ($i -le 10) {
try {
Get-Mailbox $ID
break
}
catch {
$i++
$i
Start-Sleep 10
}
}
As commented, to catch also non-terminating exceptions, you must use -ErrorAction Stop.
But why not simply do something like
$ID = "xxx#yyy"
for ($i = 0; $i -le 10; $i++) { # # loop 11 attempts maximum
$mbx = Get-Mailbox -Identity $ID -ErrorAction SilentlyContinue
if ($mbx) {
Write-Host "Mailbox for '$ID' is found" -ForegroundColor Green
break
}
Write-Host "Mailbox for '$ID' not found.. Waiting 10 more seconds"
Start-Sleep -Seconds 10
}
Or, if you want to use try{..} catch{..}
$ID = "xxx#yyy"
for ($i = 0; $i -le 10; $i++) { # loop 11 attempts maximum
try {
$mbx = Get-Mailbox -Identity $ID -ErrorAction Stop
Write-Host "Mailbox for '$ID' is found" -ForegroundColor Green
$i = 999 # exit the loop by setting the counter to a high value
}
catch {
Write-Host "Mailbox for '$ID' not found.. Waiting 10 more seconds"
Start-Sleep -Seconds 10
}
}

PowerShell - advice to get script slimmer

I have the following PS script that repeats in the two blocks the same amount of registry keys. So I have two questions:
How can I avoid to repeat that long list of reg keys? I've tried adding them to a variable that is assigned #" "#, but when I use the variable instead of the reg keys it does not work
The $faultReport does not get the addition when into the Catch block, how to fix that?
$faultReport = #()
if (Test-Connection $2FQDN -Quiet -count 2) {
Try {
Invoke-Command -ComputerName $1FQDN -ScriptBlock {
Loads of registry keys
}
}
Catch {
$faultReport += $1FQDN}
}
elseif (Test-Connection $2FQDN -Quiet -count 2) {
Try {
Invoke-Command -ComputerName $2FQDN -ScriptBlock {
Loads of registry keys
}
}
Catch {
$faultReport += $2FQDN
}
}
It is possible to assign a scriptblock to a variable
$scriptBlock = { ... }
Also you have an error in your code: You check the connection for $2FQDN (instead of $1FQDN) in your first if.
Additionally, you could simplify your code further: both bodies of the if-else are identical.
My suggestion:
$scriptBlock = {
# Loads of registry keys...
}
$faultReport = #()
$computerName = $null
if (Test-Connection $1FQDN -Quiet -count 2) {
$computerName = $1FQDN
}
elseif (Test-Connection $2FQDN -Quiet -count 2) {
$computerName = $2FQDN
}
if ($computerName) {
try {
Invoke-Command -ComputerName $computerName -ScriptBlock $scriptBlock
}
Catch {
$faultReport += $computerName
}
}
Note: Always use proper indentation. I can never stress that enough. It makes reading and troubleshooting your own code so much easier.

how to execute multiple command at the same time

I have script that stop and start windows services. Currently, it is working fine thru foreach loop; however, it is time consuming. I'm looking for a way to trigger all at the same time instead of go thru $computer object foreach loop and execute 1 by 1. Please advise. Great appreciate
Foreach ($v in $computers)
{
if ($v.value -eq "true")
{
$computers = $v.Name.Split("_")
Write-Host "Processing " $computers[1]
StartOrStopService $computers[1] $StartOrStopService.ToLower()
}
}
Best Regards,
I would suggest using jobs to get the execution go through in parallel
Foreach ($v in $computers)
{
Invoke-Command -ComputerName . -ScriptBlock {
Param($v, $StartOrStopService)
if ($v.value -eq "true")
{
$computers = $v.Name.Split("_")
Write-Host "Processing " $v.Name
StartOrStopService $v.Name $StartOrStopService.ToLower()
}
} -AsJob -ArgumentList $v, $startorStopService
}
You can use Get-Job | Receive-Job if you wanted to see the output of the commands.

How I can pass multiple array in value in PowerShell function

Below function I want to pass multiple value in array. When I'm passing more than one value I am getting an error.
function CheckProcess([String[]]$sEnterComputerNameHere, [String[]]$sEnterProccessNameHere) {
#Write-Host " $sEnterComputerNameHere hello"
#($sEnterComputerNameHere) | ForEach-Object {
# Calling Aarray
#($sEnterProccessNameHere) | ForEach-Object {
if (Get-Process -ComputerName $sEnterComputerNameHere | where {$_.ProcessName -eq $sEnterProccessNameHere}) {
Write-Output "$_ is running"
} else {
Write-Output "$_ is not running"
}
}
}
}
$script:sEnterProccessNameHere = #("VPNUI") # Pass the process agreement here
$script:sEnterComputerNameHere = #("hostname") # Pass the process agreement here
CheckProcess $sEnterComputerNameHere $sEnterProccessNameHere
Give it a try with this one:
Function CheckProcess([String[]]$sEnterComputerNameHere,[String[]]$sEnterProccessNameHere)
{ #Write-host " $sEnterComputerNameHere"
#($sEnterComputerNameHere) | Foreach-Object {
$computer = $_
Write-Host $computer
#($sEnterProccessNameHere) | Foreach-Object {
$process = $_
Write-Host $process
try{
$x = get-process -computername $computer #Save all processes in a variable
If ($x.ProcessName -contains $process) #use contains instead of equals
{
Write-Output "$process is running"
}
else
{
Write-Output "$process is not running"
}
}
catch
{
Write-Host "Computer $computer not found" -ForegroundColor Yellow
}
}
}
}
$script:sEnterProccessNameHere = #("VPNUI","Notepad++","SMSS")
$script:sEnterComputerNameHere = #("remotecomputer1","remotecomputer2")
CheckProcess -sEnterComputerNameHere $sEnterComputerNameHere -sEnterProccessNameHere $sEnterProccessNameHere
In general, it would be great if you write the error you get in your question. That helps others to help you.
If I work with arrays and | Foreach, I always write the $_in a new variable. That helps if I have another | Foreach (like you had) to know for sure, with which object I'm working with..
EDIT: I changed the script, so it uses "-contains" instead of "-eq" and I added a try/catch block, so if the other computer is not found, it gives you a message.. It works on my network
EDIT2: Do you have access to the other computers? If you run get-process -computername "name of remote computer" do you get the processes?

powershell Foreach-Object usage

I have script:
$servers = "server01", "s02", "s03"
foreach ($server in $servers) {
$server = (New-Object System.Net.NetworkInformation.Ping).send($servers)
if ($server.Status -eq "Success") {
Write-Host "$server is OK"
}
}
Error message:
An exception occured during a Ping request.
I need to ping each server in $servers array and display status. I think, that Foreach statement is not properly used, but I'm unable to find out where is the problem. Thank you for your advice
You should not be modifying the value of $server within the foreach loop. Declare a new variable (e.g. $result). Also, Ping.Send takes the individual server name, not an array of server names as an argument. The following code should work.
Finally, you will need to trap the PingException that will be thrown if the host is unreachable, or your script will print out a big red error along with the expected results.
$servers = "server1", "server2"
foreach ($server in $servers) {
& {
trap [System.Net.NetworkInformation.PingException] { continue; }
$result = (New-Object System.Net.NetworkInformation.Ping).send($server)
if ($result.Status -eq "Success") {
Write-Host "$server is OK"
}
else {
Write-Host "$server is NOT OK"
}
}
}