Loop Foreach only repeat offline computers - powershell

Not sure where I am going wrong I want to skip the computers which are coming back online but it keeps checking both online and online computers.
1st update is fine that its coming online and it should skip and only ping the offline one's a screenhot of output
g-10 Is coming online...Skipping to offline one's
192.168.0.1 Is coming online...Skipping to offline one's
Testing to see if Hero is coming online...
Hero is Offline. Pausing for 2 seconds. Remaining attempts: 1
Testing to see if zero is coming online...
zero is Offline. Pausing for 2 seconds. Remaining attempts: 1
Its fine upto here but online computers again repeated...
g-10 Is coming online...Skipping to offline one's
192.168.0.1 Is coming online...Skipping to offline one's
Testing to see if Hero is coming online...
Hero is Offline.
here is my code
$Comps = GC c:\restarted.txt
[int]$SleepTimer = "1" #minutes to attempt after
[int]$SleepSeconds = $SleepTimer * 2
[int]$Attempts = "2"
[int]$AttemptsCounter = 0
Do
{
$AttemptsCounter++
$RemainingAttempts = ([int]$Attempts - [int]$AttemptsCounter)
Foreach($comp in $comps){
$Online = Test-Connection -ComputerName $Comp -Quiet
IF($online -eq "True"){
Write-Host "$comp" -BackgroundColor Green -NoNewline
Write-Host " Is coming online...Skipping to offline one's"
}
elseIf ($Online -NE "True")
{
Write-Host "Testing to see if $Comp is coming online..."
Write-Host "$comp" -BackgroundColor Red -NoNewline
Write-Host " is Offline" -BackgroundColor Red -ForegroundColor Black -NoNewline
If ($AttemptsCounter -eq $Attempts) {
Write-Host "."
}
Else {
Write-Host ". Pausing for $SleepSeconds seconds. Remaining attempts: $RemainingAttempts"
}
}
}
#Check the number of attempts, break out if reached.
If ($AttemptsCounter -eq $Attempts) {break}
#Delay
Start-Sleep -s ($SleepTimer * 60)
}
While ($Online -NE "True")
If ($Online -NE "True") {
Write-Host "Maximum number of attempts reached"
}
Else {
Write-Host
Write-Host "Computer $Comp is " -NoNewline
Write-Host "ONLINE" -BackgroundColor Green
}

try the following code.
It basically does the following for each line in c:\restarted.txt:
tests if the computer is online (Note: I added -Count 1)
if it is online: prints some green text and break
if it is offline: print red text, wait and decrements the $RemainingAttempts variable and redo until $RemainingAttempts is 0 or the computer is online
it the $RemainingAttempts variable is 0 print out some more text
$ComputerNameArray = Get-Content -Path c:\restarted.txt
[int]$SleepTimer = "1" #minutes to attempt after
[int]$Attempts = "2"
$DefaultBackgroundColor = (Get-Host).ui.rawui.BackgroundColor
foreach($ComputerName in $ComputerNameArray) {
$AttemptsCounter = 0
$RemainingAttempts = $Attempts - $AttemptsCounter
Write-Host "Testing to see if ""$ComputerName"" is coming online..." -BackgroundColor $DefaultBackgroundColor
while($RemainingAttempts -gt 0) {
if(Test-Connection -ComputerName $ComputerName -Quiet -Count 1) {
Write-Host """$ComputerName""" -BackgroundColor Green -NoNewline
Write-Host " Is coming online...Skipping to offline one's"
break
} else {
Write-Host """$ComputerName""" -BackgroundColor Red -NoNewline
Write-Host " is Offline" -BackgroundColor Red -ForegroundColor Black -NoNewline
Write-Host ". Pausing for $SleepTimer minutes. Remaining attempts: $($RemainingAttempts - 1)"
Start-Sleep -Seconds ($SleepTimer * 60)
$RemainingAttempts--
}
}
if($RemainingAttempts -eq 0) {
Write-Host "Maximum number of attempts reached" -BackgroundColor $DefaultBackgroundColor
}
}
hope this helps!
Let me know if you have any question concerning the code.
KR
Guenther

Test-Connection returns an array of objects or a $null depending if the ip or hostname is live.
so its enough if you do this:
IF($online){
// if its alive
}
else
{
// if its not responding
}
another thing to point out is you dont have to
if(condition) {
}
elseif(condition) {
}
if you are testing "bool question-answer" you don't need the elseif. In your case, if you are checking the state of computer then you can't have more answers than 2 (yes/no). So the elseif is in this case redundant.
if(condition) {
// if the condition is true
}
else {
// if the condition is not true. you can't have the third option
}
Also your if condition If ($Online -NE "True") is checking if the variable $Online is not equal the exact string "true" or "True" since -ne is case insensitive condition operator.
You should check if the variable holds anything or holds $Null

While I know Guenther Schmitz has already got the green check, I wanted to follow up with some additional details and pointers.
Test-Connection with the -Quiet switch returns a [Boolean] type. This is a simple true/false. I like to be extra careful with comparing string values to Boolean values because you can get some odd results depending on how you frame your tests. For example:
'false' -eq $true
$true -eq 'false'
Top one equals false, bottom one equals true because PowerShell is converting the second object into the same type as the first, and a non-empty string is true. So my strong recommendation is to stick to typed comparisons. So the first code change I'd start with is:
if ($Online -eq $true) {
As #pandemic already mentioned, you're doing a simple comparison, so no need for elseif, just else.
The second issue you have is that there is nothing removing computers from your testing list. The way your code is written, if the last computer in your testing list is "down" then it'd go through all the computers again. If the last computer in the list is "up" then it'd quit the do|while loop, irregardless of how many computers you are testing and how many are down. You can verify this with a simple test of putting 1 known hosts, and 1 fake host in the test file. If the known good host is first, it'll keep looping until $AttemptsCounter reaches the maximum allowed. If you flip the list around, it'll bail out immediately after testing the known good host.
Your initial code looped through all the computers once then went back and started again. In Guenther's example, they loop one computer at a time, and check until it comes up, or bail out after exceeding the test count. I think Guenther's example was probably what you were after, but if you wanted to make it cycle through all the computers, and then test only those that had failed, I'd write something like this:
$comps = Get-Content -path 'c:\restarted.txt'
[int]$SleepTimer = 1
[int]$Attempts = 2
[int]$AttemptsCounter = 0
do {
Write-Host "Testing Network, attempt $($AttemptsCount + 1)"
$newComps = #()
foreach($comp in $comps) {
$online = Test-Connection -Quiet -Count 1 -ComputerName $comp
if ($online) {
Write-Host "$comp" -BackgroundColor Green -NoNewline
Write-Host " Is coming online...Skipping to offline one's"
} else {
$newComps += $comp
Write-Host "$comp" -BackgroundColor Red -NoNewline
Write-Host " is Offline" -BackgroundColor Red -ForegroundColor Black
}
}
$comps = $newComps
$AttemptsCounter++
Start-Sleep -Seconds ($SleepTimer * 60)
}
while (($comps.count -gt 0) -and ($AttemptsCounter -lt $Attempts))
if ($comps.Count -gt 0) {
Write-Host "Exceeded Attempts Counter" -BackgroundColor Red
}
In this code, it is taking the list of computers, and when it fails on a computer, it stuffs it into a temporary list for reuse.

Related

Color Guessing Game in Powershell [closed]

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed last year.
Improve this question
I'm working on writing a script in PowerShell for a Color guessing game. The computer randomly picks a color then the player tries to guess the color. I had it working up until I switched some lines of code in the script in an attempt to get these two variables to display correctly. Now, I can't get the code to run past the point where a player declares if they want to play the game or not. My current error is with a do loop, where the console doesn't see that I have a while loop, and so throws an error and won't run the rest of the code.
I managed to get the formatting fixed with Visual Studio Code's format document feature, but I still can't get this one while loop problem figured out.
Write-Host ''; 'Hello again my friend!'; ''
$name = Read-Host "What is your name?"
Write-Host ''; "It's good to see you again, $name! Would you like to guess my favorite color?"; ''
$command = Read-Host #'
"How do you answer? (Yes or No?)"
1. Yes (y)
2. No (n)
3. Quit (q)
Enter Choice
'#
switch -Wildcard ($command) {
'Y' { 'Yes!'; '' }
{ $_ -eq 'y' -or $_ -like 'Ye*' } {
Write-Host "This will be fun! Let us begin!"
Break
}
'N' { 'No!'; '' }
{ $_ -eq 'n' -or $_ -like 'No*' } {
Write-Host "That's too bad, perhaps another day!"
Exit
}
'Q' { 'Quit'; '' }
{ $_ -eq 'q' -or $_ -like 'qu*' } {
Write-Host 'So long!'
Exit
}
default {
'Invalid Command, Please start over.'
Exit
}
}
[string]$playagain = 'y'
[int]$playerwins = 0
[int]$compwins = 0
[int]$totalguesses = 0
[int]$playergames = 0
[int]$compgames = 0
[int]$round = 1
[int]$game = 1
$cpuchoice = $color
while ($playagain -eq 'y') {
Write-Host ''; "Game $game!"; ''
$cpuchoice = #([System.Enum]::GetValues([System.ConsoleColor])) | Get-Random -Count 1
do {
Write-Host "Round $round! What is my favorite color?"; ''
$listcolor = Read-Host "Would you like to see a list of available colors? Choose 'y' for yes, and 'n' for no."
if ($listcolor -eq 'y') {
[System.Enum]::GetValues([System.ConsoleColor])
}
elseif ($listcolor -eq 'n') {
Write-Host "Suit yourself, let's start."
}
else {
Write-Host "Your choice was invalid. Please choose 'y' for yes, or 'n' for no."
}
do {
$playerchoice = Read-host "Enter your guess"
} while (([System.Enum]::GetValues([System.ConsoleColor])) -notcontains $playerchoice) {
if ($playerchoice -eq $cpuchoice ) {
Write-Host "You win, my favorite color is $cpuchoice." -ForegroundColor $cpuchoice; ''
$playerwins = $playerwins + 1
$totalguesses = $totalguesses + 1
}
elseif ($playerchoice -ne $cpuchoice ) {
Write-Host "You lose, try again."; ''
$playerguesses += $playerchoice
$playerguesses = $playerguesses.Split(',')
$totalguesses = $totalguesses + 1
Write-Host "Here are your guesses so far: "
$playerguesses
''
}
$round = $round + 1
}
until($playerwins -eq 1) {
$playergames = $playergames + 1
Write-Host "You've won this round and have won $playergames games." -ForegroundColor Green
Write-Host "Your total guesses: $totalguesses."
Write-Host "Your wins - $playergames" -ForegroundColor Yellow
Write-Host "Computer wins - $compgames" -ForegroundColor Yellow
''
}
$playagain = Read-Host "I enjoyed this game. Would you like to challenge again, $name? Y or N"
while (("y", "n") -notcontains $playagain) {
if ($playagain -eq "y") {
Write-Host "I look forward to our next battle!"; ''
$playerwins = 0
$compwins = 0
$game = $game + 1
}
elseif ($playagain -eq "n") {
Write-Host "Thank you for playing!"
exit
}
}
}
}
The do loop that causes the error is the one that starts with "Write-Host "Round $round!" after the first while statement.
Any help is appreciated! Thanks!
Your program is being parsed like this:
while ($playagain -eq 'y')
{
# do loop #1
do
{
# do loop #2
do {
}
while (([System.Enum]::GetValues([System.ConsoleColor])) -notcontains $playerchoice)
# floating script block #1
# (doesn't execute the scriptblock, but it gets sent to the output stream instead)
{
if ($playerchoice -eq $cpuchoice ) {
... etc ...
}
# try to invoke a cmdlet "until" with 2 parameters
# i.e. ($playerwins -eq 1) and { ... }
until ($playerwins -eq 1) {
$playergames = $playergames + 1
... etc ...
}
# while loop #1
while (("y", "n") -notcontains $playagain) {
...
}
}
}
The error is telling you the first do (do loop #1) doesn't have a trailing while or until.
There's no clear and simple fix I can offer to make your code run short of a significant rework because there's a number of issues (e.g. the floating script block #1, the dangling until and the while \ until-less do), but that's what the current error is saying anyway...

Powershell script providing false positives (Exchange Online)

I seem to be coming across issues with a check i have put in place to confirm a mailbox has ben converted from a users mailbox to shared mailbox.
Connect-ExchangeOnline -UserPrincipalName $UserCredential.UserName
Write-Host "checking mailbox has converted to a shared mailbox...."
$Mailboxcheck = Get-Mailbox "$username" | select isshared
if ($Mailboxcheck.IsShared -eq $False) {
[System.Windows.MessageBox]::Show("Mailbox for $($username) has NOT been converted to shared, please confirm.")
} else {
[System.Windows.MessageBox]::Show("Mailbox for $($username) has been converted to shared.")
}
the issue is sometimes this check will work and other times it won't (randomly). So we convert the mailbox to a shared mailbox via the above script then the check will sometimes come up as the $False message rather then the else message but when we go to manually check it after it's run the mailbox has been converted to shared.
The only things i can think of is to add the disconnect and reconnect to exchange online in case it's due to some syncing time, Possibly add a Start-sleep to the script to give it more time to sync, or its to do with the if statement and the variable $false as $mailboxcheck.isshared should only provide a value of "True" or "False"
if ($Mailboxcheck.IsShared -eq $False)
Another way i have made the script to check is with a switch statement but am unsure if that will be me any benefit and unsure if it's the right use case or should stick to the if statement
$Mailboxcheck = Get-Mailbox "$username" | select isshared
switch ($Mailboxcheck.isshared)
{
True {[System.Windows.MessageBox]::Show("Mailbox for $($username) has been converted to shared."); Break}
False {[System.Windows.MessageBox]::Show("Mailbox for $($username) has NOT been converted to shared, please confirm."); Break}
default {[System.Windows.MessageBox]::Show("Unable to check mailbox type for $($username). Please confirm it has been converted to shared.")}
}
Let me know your thoughts.
Try running this code along side the other one to see when the IsShared property actually changes:
$repeat = $true
do
{
$Mailboxcheck = Get-Mailbox "$username"
Write-Host "Date = $(get-date)" -for Green
write-host "RecipientTypeDetails = $($Mailboxcheck.RecipientTypeDetails)" -ForegroundColor Cyan
write-host "IsShared = $($Mailboxcheck.isShared)" -ForegroundColor yellow
""
sleep 10
}
while ($repeat -eq $true)

Powershell, ping results in color

been working on a script/module in PowerShell for my work and can't seem to find where/what to input to make the ping results på green if it's a success and red if they timed out.
Param (
[Parameter(Position=0,Mandatory=$true)]
[String]$StoreID
)
$StoreNetworkArray = #(
("1017","xxx.x.x.x","x.x.x.x"),
)
$checkPing = $False
foreach ($currentStore in $StoreNetworkArray) {
if($currentStore[0] -eq $StoreID) {
$checkPing = $True # StoreID matches!
$WanIP = $currentStore[1]
$BreakoutIP = $currentStore[2]
} # end if
} # end foreach
if ($checkPing) {
# Store found, lets ping!
Write-Host "Checking status for store $StoreID using ping"
Write-Host "-----------------------------------------"
Write-Host "Pinging WAN IP: $WanIP"
ping $WanIP /n 10
Write-Host "-----------------------------------------"
Write-Host "Pinging Breakout IP: $BreakoutIP"
ping $BreakoutIP /n 10
Write-Host "-----------------------------------------"
}
} # End Function Check-StorePing```
ping returns an array of strings so you can check Foreach returned line:
ping $WanIP /n 10 | foreach {
if ($_ -like '*TTL=*'){
Write-Host $_ -ForegroundColor Green
}elseif($_ -like '*timed out*'){ # you might need to localize this part
Write-Host $_ -ForegroundColor Red
}else{ # remove the last else to get rid of the extra lines before and after the ping
Write-Host $_
}
}
You can even fit all of this in a single line but for now this has a better overview. You might need to localize the if. E.g. in a german environment Request timed out is Zeitberschreitung der Anforderung.

How to configure a timeout for Read-Host in PowerShell

Like I said, this code works in PowerShell version 2, but not in PowerShell version 5.
function wait
{
$compte = 0
Write-Host "To continue installation and ignore configuration warnings type [y], type any key to abort"
While(-not $Host.UI.RawUI.KeyAvailable -and ($compte -le 20))
{
$compte++
Start-Sleep -s 1
}
if ($compte -ge 20)
{
Write-Host "Installation aborted..."
break
}
else
{
$key = $host.ui.rawui.readkey("NoEcho,IncludeKeyup")
}
if ($key.character -eq "y")
{Write-Host "Ignoring configuration warnings..."}
else
{Write-Host "Installation aborted..."
}}
The official documentation or Read-Host -? will tell that it's not possible to use Read-Host in that manner. There is no possible parameter to tell it to run with some kind of timeout.
But there are various other questions detailing how to do this in PowerShell (usually utilizing C#).
The idea seems to be to check whenever the user pressed a key using $Host.UI.RawUI.KeyAvailable and check that for the duration of your timeout.
A simple working example could be the following:
$secondsRunning = 0;
Write-Output "Press any key to abort the following wait time."
while( (-not $Host.UI.RawUI.KeyAvailable) -and ($secondsRunning -lt 5) ){
Write-Host ("Waiting for: " + (5-$secondsRunning))
Start-Sleep -Seconds 1
$secondsRunning++
}
You could use $host.UI.RawUI.ReadKey to get the key that was pressed. This solution probably would not be acceptable if you need more complex input than a simple button press. See also:
Windows PowerShell Tip of the Week - Pausing a Script Until the User Presses a Key
PowerTip: Use PowerShell to Wait for a Key Press (Hey, Scripting Guy!)
Seth, thank you for your solution. I expanded on the example you provided and wanted to give that back to the community.
The use case is a bit different here - I have a loop checking if an array of VMs can be migrated and if there are any failures to that check the operator can either remediate those until the checks clear or they can opt to "GO" and have those failing VMs excluded from the operation. If something other than GO is typed state remains within the loop.
One downside to this is if the operator inadvertently presses a key the script will be blocked by Read-Host and may not be immediately noticed. If that's a problem for anyone I'm sure they can hack around that ;-)
Write-Host "Verifying all VMs have RelocateVM_Task enabled..."
Do {
$vms_pivoting = $ph_vms | Where-Object{'RelocateVM_Task' -in $_.ExtensionData.DisabledMethod}
if ($vms_pivoting){
Write-Host -ForegroundColor:Red ("Some VMs in phase have method RelocateVM_Task disabled.")
$vms_pivoting | Select-Object Name, PowerState | Format-Table -AutoSize
Write-Host -ForegroundColor:Yellow "Waiting until this is resolved -or- type GO to continue without these VMs:" -NoNewline
$secs = 0
While ((-not $Host.UI.RawUI.KeyAvailable) -and ($secs -lt 15)){
Start-Sleep -Seconds 1
$secs++
}
if ($Host.UI.RawUI.KeyAvailable){
$input = Read-Host
Write-Host ""
if ($input -eq 'GO'){
Write-Host -ForegroundColor:Yellow "NOTICE: User prompted to continue migration without the blocked VM(s)"
Write-Host -ForegroundColor:Yellow "Removing the following VMs from the migration list"
$ph_vms = $ph_vms | ?{$_ -notin $vms_pivoting} | Sort-Object -Property Name
}
}
} else {
Write-Host -ForegroundColor:Green "Verified all VMs have RelocateVM_Task method enabled."
}
} Until(($vms_pivoting).Count -eq 0)
Also note that all this $Host.UI stuff doesn't work from the Powershell ISE.
To find out from within a script you could test for $Host.Name -eq "ConsoleHost". When true you can use the code from this topic. Otherwise you could use $Host.UI.PromptForChoice or any other way of showing a dialog box. With System.Windows.Forms.Timer you can then set a timer, and code to close the dialog box or form can be run when it expires.

Powershell -or on multiple checks

I have the following script that is causing an issue. It's doing a simple check to see if the number of characters entered is either 7 or 8. However, even after entering either 7 or 8 characters it doesn't move onto the else statement and displays "invalid number of characters".
If I remove the -or and stick to just one check then it works fine. I can't seem to get it to work when checking with the -or operator. All the examples I've found seem to suggest the code should work fine so can anyone spot what I'm missing?
Function number {
$global:change = read-host "Enter a number"
if ($global:change.length -ne 7 -or $global:change.length -ne 8) {
write-host $global:change.length
write-host -foregroundcolor red "Invalid number of characters"
number
}
else {
write-host "You entered the correct amount of characters"
}
}
number
You just need to flip your logic a bit to get this working. Based on the logic in your code, one of the cases will always be true. If you change the code to the following, you will get the desired effect.
Function number {
$global:change = read-host "Enter a number"
if (($global:change.length -eq 7) -or ($global:change.length -eq 8))
{
write-host "You entered the correct amount of characters"
}
else
{
write-host $global:change.length
write-host -foregroundcolor red "Invalid number of characters"
number
}
}
number
This will test whether you enter a value that is either 7 or 8 and loop if another value is entered.
Change the -or in an -and or style the code in this way:
Function number {
$global:change = read-host "Enter a number"
if ($global:change.length -eq 7 -or $global:change.length -eq 8) {
write-host "You entered the correct amount of characters"
}
else {
write-host $global:change.length
write-host -foregroundcolor red "Invalid number of characters"
number
}
}
number