Powershell selection validation failing - powershell

I am having some issues with a dynamic picklist I am generating for a script. I can see it return the list of options and I can select each of them. However the validation I have is not working as I would have expected.
Write-Host "Gathering cluster information..." -foreground Green
$allclusters = Get-Cluster | Sort Name
$allHosts = $allclusters | Get-VMHost
Write-Host "Found " $allclusters.count " containing " $allHosts.count " Hosts." -Foreground Green
# LoopMain Start
Do {
$userMenuChoice = "y"
Write-Host "Select Cluster to patch:" -Foreground Yellow
for($i=0;$i -le $allclusters.length-1;$i++)
{"[{0}] - {1}" -f $i,$allclusters[$i]}
# Select VMCluster
Write-Host ""
Write-Host "Which Cluster would you like to use (0 to"($allclusters.length-1)")" -ForegroundColor Cyan -NoNewLine ; $clusterSelect = Read-Host " "
Write-Host ""
# Validate selection
IF ($clusterSelect -le ($allclusters.length-1))
{
Write-Host "Selection is valid"
# Display item from clusterarray
Write-Host ""
Write-Host "You selected " -NoNewLine ; Write-Host $allclusters[$clusterSelect] -ForegroundColor Cyan -NoNewLine ; $clusterSelectCont = Read-Host ". Shall we continue? (Y/N)"
}
ELSE
{
Write-Host "Selection is not valid"
$clusterSelectCont = "n"
}
The picklist will work for some number but not all. For example I can select number 12 form the list and everything is fine but number 7 fails. Is there something fundamentally wrong with the way I have created the picklist or is it the validation that is failing perhaps?

Ah, so discussed with a colleague here and the suggestion was to cast the variable to an integer using the following line:
[int]$clusterSelect = $clusterSelect
This now validates as expected and I can select all values without issue.

Related

Coloring output

I made a very very simple code that just checks a few regkeys. However to make it more nice to the eyes I was hoping that whenever it's False i can make it red and whenever it's True i can make it green.
I googled a bunch about this but couldn't find a clear solution for what i'm trying to accomplish. Any tips are very much appreciated.
Write-Host "Update Pending" -ForegroundColor Cyan
Test-Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Services\Pending'
""
Write-Host "Reboot Pending:" -ForegroundColor Cyan
Test-Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending'
""
Write-Host "Reboot Required:" -ForegroundColor Cyan
Test-Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired'
""
Write-Host "Pending File Rename Operations:" -ForegroundColor Cyan
Test-Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\PendingFileRenameOperations'
""
Write-Host "Beschikbare Updates:" -ForegroundColor Cyan
Test-Path 'HKLM:\SOFTWARE\Microsoft\Updates\UpdateExeVolatile'
Save the output from Test-Path to a variable, the use the value to specify one color or the other to pass as an argument to Write-Host:
Write-Host "Update Pending" -ForegroundColor Cyan
$testResult = Test-Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Services\Pending'
Write-Host $testResult -ForegroundColor #('Red', 'Green')[$testResult]
Write-Host ''
PowerShell will implicitly convert $False to 0 and $True to 1 when used in an array indexer, so $False results in 'Red' being picked.
Since you're basically repeating the same test operation every time, you can simplify your code by organizing the labels and registry keys into a hashtable and then only write the code to test and output once:
$testCases = [ordered]#{
"Update Pending" = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Services\Pending'
"Reboot Pending" = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending'
"Reboot Required" = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired'
"Pending File Rename Operations" = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\PendingFileRenameOperations'
"Beschikbare Updates" = 'HKLM:\SOFTWARE\Microsoft\Updates\UpdateExeVolatile'
}
foreach($label in $testCases.psbase.Keys){
Write-Host "${label}:" -ForegroundColor Cyan
$testResult = Test-Path $testCases[$label]
Write-Host $testResult -ForegroundColor #('Red','Green')[$testResult]
Write-Host ''
}

Powershell read file for text wait until found then proceed

I'm working on a process to start up some services across remote servers, however server 2 can't start up until a message is found in server1 logs, server 3 can't start until same message in server 2, etc.
My question is, is it possible to read the file in a "loop" and not proceed with my initial loop until that message is found (and then move forward)? I was thinking I could do below, however while it does recognize that the string in the log file is found, it just repeats that it found it until the timer built in finishes and then moves forward.
So, the process would look like "read this file, if string is found, move forward. If string is not found, wait 30 seconds and rescan the file. If found, move forward, if not found, wait an additional 30 seconds and rescan (I'm fine with a continuous repeat) < do this until string is found.
Example: enter image description here
Any advice would be much appreciated as I think I might be approaching this from the wrong angle...
-- I left out the majority of the script prior to this script and only included the If/Else statement as this is where it checks the files.
$SEL = Select-String -Path \\$Server\$RootDir\folder\anotherfolder\A-Log-File.log -Pattern "Switching to status: STARTED"
if ($SEL -ne $null)
{
Write-Host "FOUND: Switching to status: STARTED" -ForegroundColor Yellow -BackgroundColor DarkGreen
}
else
{
Write-Host **** Waiting 60 seconds for cache to build ****
[int]$Time = 60
$Lenght = $Time / 100
For ($Time; $Time -gt 0; $Time--) {
$min = [int](([string]($Time/60)).split('.')[0])
$text = " " + $min + " minutes " + ($Time % 60) + " seconds left"
Write-Progress -Activity "Waiting for Started Message" -Status $Text -PercentComplete ($Time / $Lenght)
Start-Sleep 1
$SEL = Select-String -Path \\$Server\$RootDir\folder\anotherfolder\A-Log-File.log -Pattern "Switching to status: STARTED"
if ($SEL -ne $null)
{
Write-Host "FOUND: Switching to status: STARTED" -ForegroundColor Yellow -BackgroundColor DarkGreen
}
else
{
Write-Host **** A-Log-File.log Log does NOT contain a started message **** -ForegroundColor Red -BackgroundColor Yellow
Write-Host **** Investigate further or increase the int-time time on Line 54 to 180 seconds **** -ForegroundColor Red -BackgroundColor Yellow ##This part goes away once action can be taken based on reading contents of the file
}
}
}
You don't need a loop, just use Get-Content -Wait:
$null = Get-Content "\\$Server\$RootDir\folder\anotherfolder\A-Log-File.log" -Wait |Where-Object { $_ -match 'Switching to status: STARTED' } |Select -First 1
Get-Content -Wait will continue outputting new lines written to the file until it's interrupted - luckily we can use Select -First 1 to stop the pipeline once we observe the string
You said "loop" so why aren't you using a loop?
while ($true) {
# (re)try
$SEL = Select-String -Path "\\$Server\$RootDir\folder\anotherfolder\A-Log-File.log" -Pattern "Switching to status: STARTED"
if ($SEL -ne $null)
{
Write-Host "FOUND: Switching to status: STARTED" -ForegroundColor Yellow -BackgroundColor DarkGreen
# exit the loop
break;
}
# wait
Write-Host "**** Waiting 60 seconds for cache to build ****"
Start-Sleep 1
}
#marsze, I meant to come back to this and update. I ended up using your suggestion and also wanted to add in that I was able to build in a timer. I wanted to provide the example for anyone that stumbles upon this.
while ($true) {
# (re)try
$SEL = Select-String -Path "\\$Server\$SomeRoot\A.Folder\b.folder2\ThisFileToParse.log" -Pattern "Message Here"
if ($SEL -ne $null)
{
Write-Host "FOUND: Message Here" -ForegroundColor Yellow -BackgroundColor DarkGreen
# exit the loop
break;
}
# wait
Write-Host "**** File Does NOT contain a Message Here message ****" -ForegroundColor Red -BackgroundColor Yellow
Write-Host "**** Waiting 30 seconds, will loop until message is found ****" -ForegroundColor Red -BackgroundColor Yellow
#Start-Sleep 1
[int]$Time = 30
$Lenght = $Time / 100
For ($Time; $Time -gt 0; $Time--) {
$min = [int](([string]($Time/60)).split('.')[0])
$text = " " + $min + " minutes " + ($Time % 60) + " seconds left"
Write-Progress -Activity "Waiting for ThisFileToParse.log to update showing Message Here... " -Status $Text -PercentComplete ($Time / $Lenght)
Start-Sleep 1
}

Best way to create multiple lines with different colors

I am trying to figure out, what is the best way, to output multi-lines with different colors.
There are several options that I have but each of them is challenging in some way. The possibilities I assume I have are:
Listbox
Textbox
Label
I am building a vocabulary trainer, where during a test, you have to write words. At the end of the test, I would like to summarize the test.
The first challenge is, that the test are different in length, meaning it can be a test of only 10 words or more. The target is, to show to each question the answer and analyze if it is correct and wrong and if wrong, where it went wrong. That part I have solved.
But now the question is, how to present the results.
Listbox and Textbox are surely easier to handle but if I understand correctly, there will be a challenge with the output having different colors.
Label would be easier to handle but there I am struggling with Multiline and so far, the only solution I have so far is to generate for each result a different label, which means, I have to build labels according to the sum of the output, meaning 10 test questions would require 10 Labels.
When you run the test script below, you will see as a result, you wrote: Where the mistake is shown with a red-letter.
The idea would be, to have such output for each line, where the mistake is shown and if correct, it has a green color, to show, it is all ok.
Clear-Host
$Frage = 'Hello World'
$Antwort = 'Hello Warld'
$Script:Counter = 0
$wordlength = $Frage.Length
Do {
if ($Antwort[$Script:Counter] -ne $Frage[$Script:Counter]) { break }
Write-Host $Frage[$Script:Counter] -NoNewline;
Write-Host -ForegroundColor Red ' '$Antwort[$Script:Counter]
$Script:Counter++
} # End of 'Do'
Until ($Script:Counter -eq $wordlength)
If ($Antwort.Length -gt $Frage.Length) {
$TooShortDifference = $Antwort[($Frage.Length)..($Antwort.Length)]
Write-Host ''
Write-Host -ForegroundColor Red 'Your Answer has too many letters'
Write-Host ''
Write-Host -ForegroundColor Green -NoNewline 'You wrote: '
Write-Host $Firstpart -NoNewline;
Write-Host $TooShortDifference -ForegroundColor Red
Write-Host ''
Write-Host -ForegroundColor Green -NoNewline 'The answer is: '
Write-Host -ForegroundColor White $Frage
Write-Host ''
}
Elseif ($Antwort.Length -lt $Frage.Length) {
$TooLongDifference = $Frage[($Antwort.Length)..$Frage.Length]
Write-Host ''
Write-Host -ForegroundColor Red 'Your Answer has not enough letters'
Write-Host ''
Write-Host -ForegroundColor Green -NoNewline 'You wrote: '
Write-Host -ForegroundColor White $Antwort
Write-Host ''
Write-Host -ForegroundColor Green -NoNewline 'The answer is: '
Write-Host -ForegroundColor White $Antwort -NoNewline
Write-Host -ForegroundColor Red $TooLongDifference -Separator ''
}
Elseif ($Antwort.Length -eq $Frage.Length) {
$SameLengthWithError = $Frage[0..($Script:Counter - 1)]
Write-Host ''
Write-Host -ForegroundColor Red 'Your Answer has a letter mismatch'
Write-Host ''
Write-Host -ForegroundColor White 'You Wrote: ' -NoNewline
Write-Host $SameLengthWithError -NoNewline -Separator ''
Write-Host $Antwort[$Script:Counter] -ForegroundColor Red -NoNewline
Write-Host $Antwort[($Script:Counter + 1)..$Antwort.Length] -Separator ''
Write-Host ''
Write-Host -ForegroundColor Green 'The Answer is: ' -NoNewline
Write-Host -ForegroundColor Green $Frage
}
Else {
Write-Host -ForegroundColor Green 'Everything was correct'
}
If you would build something similar, which way would you use and how would you show multiple lines, where within the font colors would change on each line?

How can i correctly output my try-catch into a txt file?

I'm working on a powershell script that would allow me to see if my DHCP servers are running well and what's their current state (how much address left, how much of them are used...).
So far, so good. I'm getting all the info i need but, the first test here is to check if the server is responding pretty much.
For this, i'm using try-catch and i'd like to output into a .txt all of the servers that are not responding.
Problem : i'm only outputting the last server that did not respond, i'm not getting the previous one that did not respond aswell.
Tried | out-file
| set-content and | add-content
There's nothing that i've found searching seems to work.
$DHCPSRV=""
$myError=0
$myArray=#( Import-Csv .\CSV\DHCP_list.csv)
foreach ($element in $myArray) {
try {
Write-Output ""
$DHCPSRV=$element.FQDN
$Message = "Server DHCP: " + $DHCPSRV
Write-Output $Message
Write-Output ""
$srv=get-dhcpserverv4statistics -ComputerName $DHCPSRV
$Message ="Server start time : " + $srv.ServerStartTime
Write-Output $Message
$Message ="Number of address : " + $srv.TotalAddresses
Write-Output $Message
$Message ="Address used : " + $srv.AddressesInUse
Write-Output $Message
$Message ="% remaining : " + $srv.PercentageAvailable + " %"
Write-Output $Message
Get-DhcpServerSetting -ComputerName $DHCPSRV
Write-Output ""
}
catch{
Write-host "Server not responding " $DHCPSRV -BackgroundColor red -ForegroundColor White
$myerror=$error+1
$test = $DHCPSRV
}
}
if ($myError -eq 0){
Write-Output ""
Write-host "All DHCP are working good" -BackgroundColor green -ForegroundColor black
}
$test | Set-Content '.\Output\dhcp_failed.txt'
$test | Add-Content '.\Output\dhcp_failed.txt'
Write-Output ""
Write-Output ""
Write-Output "------------------------------------------------"
pause
I'd like to output all of the server that failed the try-catch test in my txt!
solved by bluuf - Thank you !
Just had to add the -append in my catch
catch{
Write-host "Server not responding " $DHCPSRV -BackgroundColor red -ForegroundColor White
$myerror=$error+1
$DHCPSRV | out-file '.\path\file.txt' -append
}
also added clear-content '.\path\file.txt' at the beginning of the script so my file get cleared every time i launch it!

Do-Until loop ask twice value

The problem is with loop DoUntil.
The script must take the URL, than ask what to do with him and gave the result after the user pick the number(1-4)
and with output give the menu again, and user can pick another number if he want or finish the script typing 'X'.
But this script works in a different way, it is ask for URL, after I will pick the number, and after this it asks again the URL and again the number and after this(if for example I will pick twice the same number) it will bring me the value of 2 URLs together.
Please, advice, what I'm doing wrong.
write-host ""
write-host "Past URL that you need to check"
write-host ""
write-host -nonewline "Type your URL and press Enter: "
$URLok = read-host
write-host ""
if ($URLok.Length -lt 1) {
write-host "Invalid selection"
}
do {
write-host "What do you want to do with this URL?"
write-host ""
write-host "DNS - Selection 1"
write-host " MX - Selection 2"
write-host " NS - Selection 3"
write-host "TXT - Selection 4"
write-host ""
write-host "X - Exit"
write-host ""
write-host -nonewline "Type your choice and press Enter: "
$choice = read-host
$ok = $choice -match '^[1234x]+$'
if ( -not $ok) {
write-host "Invalid selection"
}
switch -Regex ( $choice ) {
"1"
{
[System.Net.Dns]::resolve($URLok)
}
"2"
{
Resolve-DnsName -Name $URLok -Type MX -DnsOnly | select Name,Type,NameExchange
}
"3"
{
Resolve-DnsName -Name '888ladies.com' -Type NS -DnsOnly | select Name,Type,NameHost,PrimaryServer
}
"4"
{
Resolve-DnsName -Name $URLok -Type TXT | select Name,Type,Strings
}
"5"
{
write-host ""
write-host "Past URL that you need to check"
write-host ""
write-host -nonewline "Type your URL and press Enter: "
$URLok = read-host
write-host ""
}
}
} while($choice -ne "X")
P.s. And Little remark, in the 2-3-4 options I have 'Resolve-DNSName' but this script wouldn't show it on the console before the loop doUntil finished. So I can see the result only if I pick 'X' and finish the loop
The answer was with the pip Format-Table and it's work!
For Example:
Resolve-DnsName -Name $URLok -Type NS -DnsOnly | select Name,Type,NameHost,PrimaryServer| Format-Table