Powershell, ping results in color - powershell

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.

Related

How do I add a string to a variable called from a CSV file before it is called in a function?

I am trying to ping a list of IPs, but I need to test variations of them for RACs and KVMs. I want to take the IP address (in the $ColumnHeader variable) and append "rac" to it before pinging it. I also will need to ping "arac", "raca", "kv", "akv", "kva", and "kvm", but I was just going to substitute those manually into the script after running it each time.
Param(
[Parameter(Mandatory=$true, position=0)][string]$csvfile
)
$ColumnHeader = "Name"
#$string = "rac"
#$subServer = "$($ColumnHeader)$($string)"
Write-Host "Reading file" $csvfile
$ipaddresses = Import-Csv $csvfile | Select-Object $ColumnHeader
Write-Host "Started Pinging.."
foreach ($ip in $ipaddresses) {
if (Test-Connection $ip.($ColumnHeader) -Count 1 -Quiet) {
Write-Host $ip.("Name") "Ping succeeded." -Foreground Green
} else {
Write-Host $ip.("Name") "Ping failed." -Foreground Red
}
}
Write-Host "Pinging Completed."
I've tried creating new variables $string and $subServer and using those in place of $ColumnHeader on line 14, but I get a "Cannot validate argument on parameter 'ComputerName'." error.
EDIT: I changed Line 7 from $subServer = $ColumnHeader + $string to $subServer = "$($ColumnHeader)$($string)", and I can successfully call $subServer on Line 14 in place of $ColumnHeader, but when I uncomment $string on Line 6, I get the aforementioned 'ComputerName' error again.

Loop Foreach only repeat offline computers

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.

ForEach from a text file with my PS script

I was hoping someone would be able to help me add a foreach to my script that pulls hostnames from a text file. I know this is so basic, but I can't make it work. Thank you in advance.
Here is my script:
#Days passed from last SEP client update
$Error.Clear();
try {
$res=(Get-ItemProperty "HKLM:\SOFTWARE\Wow6432Node\Symantec\Symantec Endpoint Protection\AV" PatternFileDate).PatternFileDate
}
catch
{
Write-Host "ERROR: $($Error[0])";
exit 1;
}
if ($Error.Count -eq 0) {
$y1=[int]$res[0]+1970;
$m1=[int]$res[1]+1;
$d1=[int]$res[2];
$stat2 = [string](get-date -uformat "%m %d %Y")
$t2=$stat2.split(" ")
$m2=[int]$t2[0];
$d2=[int]$t2[1];
$y2=[int]$t2[2];
$diff=($y2-$y1)*365+($m2-$m1)*30+($d2-$d1);
write-host "Statistic: $diff";
write-host "Message: Last SEP client update date: $m1/$d1/$y1";
exit 0;
}
write-host "Message: Can't find ""HKLM:\SOFTWARE\Symantec\Symantec Endpoint Protection\AV\PatternFileDate"" registry value";
You are looking for the Get-Content cmdlet. Example:
Get-Content 'PATH_TO_YOUR_TEXT_FILE' | ForEach-Object {
$hostname = $_
}
The text file should look like this:
host1
host2
host3

How can I implement Get-Credential with a timeout

In some of my scripts I have a fall-back approach for getting a PSCredential object via the Get-Credential cmdlet. This is very useful when running those scripts interactively, esp. during testing etc.
If & when I run these scripts via a task scheduler, I'd like to ensure that they don't get stuck waiting for interactive input and simply fail if they don't have the necessary credentials. Note that this should never happen if the task is set to run under the correct application account.
Does anyone know of an approach that would allow me to prompt for input with a timeout (and presumably a NULL credential object) so the script doesn't get stuck?
Happy to consider a more general case with Read-Host instead of Get-Credential.
I use something similar in some of my scripts based around a timeout on read-host, code here :
Function Read-HostTimeout {
# Description: Mimics the built-in "read-host" cmdlet but adds an expiration timer for
# receiving the input. Does not support -assecurestring
# Set parameters. Keeping the prompt mandatory
# just like the original
param(
[Parameter(Mandatory=$true,Position=1)]
[string]$prompt,
[Parameter(Mandatory=$false,Position=2)]
[int]$delayInSeconds=5,
[Parameter(Mandatory=$false,Position=3)]
[string]$defaultValue = 'n'
)
# Do the math to convert the delay given into milliseconds
# and divide by the sleep value so that the correct delay
# timer value can be set
$sleep = 250
$delay = ($delayInSeconds*1000)/$sleep
$count = 0
$charArray = New-Object System.Collections.ArrayList
Write-host -nonewline "$($prompt): "
# While loop waits for the first key to be pressed for input and
# then exits. If the timer expires it returns null
While ( (!$host.ui.rawui.KeyAvailable) -and ($count -lt $delay) ){
start-sleep -m $sleep
$count++
If ($count -eq $delay) { "`n"; return $defaultValue}
}
# Retrieve the key pressed, add it to the char array that is storing
# all keys pressed and then write it to the same line as the prompt
$key = $host.ui.rawui.readkey("NoEcho,IncludeKeyUp").Character
$charArray.Add($key) | out-null
# Comment this out if you want "no echo" of what the user types
Write-host -nonewline $key
# This block is where the script keeps reading for a key. Every time
# a key is pressed, it checks if it's a carriage return. If so, it exits the
# loop and returns the string. If not it stores the key pressed and
# then checks if it's a backspace and does the necessary cursor
# moving and blanking out of the backspaced character, then resumes
# writing.
$key = $host.ui.rawui.readkey("NoEcho,IncludeKeyUp")
While ($key.virtualKeyCode -ne 13) {
If ($key.virtualKeycode -eq 8) {
$charArray.Add($key.Character) | out-null
Write-host -nonewline $key.Character
$cursor = $host.ui.rawui.get_cursorPosition()
write-host -nonewline " "
$host.ui.rawui.set_cursorPosition($cursor)
$key = $host.ui.rawui.readkey("NoEcho,IncludeKeyUp")
}
Else {
$charArray.Add($key.Character) | out-null
Write-host -nonewline $key.Character
$key = $host.ui.rawui.readkey("NoEcho,IncludeKeyUp")
}
}
Write-Host ""
$finalString = -join $charArray
return $finalString
}

Is there a way to specify a font color when using write-output

I have a powershell script that gives some status output via write-output. I am intentionally not using write-host because the output may be captured and written to a logfile like this:
./myscript.ps1 | out-file log.txt
But if the output is not redirected it would be nice to have colored output on the console, because the script is producing a lot of different status messages. I know that colored output is possible with write-host but the status messages should be pipeable.
Any ideas how to solve this?
I have tried this extra function and it basically works fine:
function Write-ColorOutput($ForegroundColor)
{
# save the current color
$fc = $host.UI.RawUI.ForegroundColor
# set the new color
$host.UI.RawUI.ForegroundColor = $ForegroundColor
# output
if ($args) {
Write-Output $args
}
else {
$input | Write-Output
}
# restore the original color
$host.UI.RawUI.ForegroundColor = $fc
}
# test
Write-ColorOutput red (ls)
Write-ColorOutput green (ls)
ls | Write-ColorOutput yellow
The result of this particular test is a little bit funny though: we really get lines in red, green and yellow but the table header is in red, i.e. the color of the the first call of the function.
This way:
function Green
{
process { Write-Host $_ -ForegroundColor Green }
}
function Red
{
process { Write-Host $_ -ForegroundColor Red }
}
Write-Output "this is a test" | Green
Write-Output "this is a test" | Red
Separate the results on the pipeline from the status messages in the console.
E.g., use a function like this in your script:
function write-status( $status ){
$status | write-host -fore green -back red; #send a status msg to the console
$status | write-output; #send a status object down the pipe
}
I would also recommend you use one of the following cmdlets over write-host for outputting status messages from your scripts:
write-debug
write-error
write-verbose
write-warning
The appearance of these status messages will vary depending on the cmdlet used. In addition, the user can disable specific levels of status using the $(warning|error|verbose|debug)preference variables, or capture specific status messages using the -(warning|error|verbose|debug)variable common cmdlet parameters.
I had the same problem, so I share my solution which I think works quite well:
Write-ColorOutput "Hello" Green Black -NoNewLine
Write-ColorOutput " World" Red
This is the Cmdlet to use it
function Write-ColorOutput
{
[CmdletBinding()]
Param(
[Parameter(Mandatory=$False,Position=1,ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True)][Object] $Object,
[Parameter(Mandatory=$False,Position=2,ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True)][ConsoleColor] $ForegroundColor,
[Parameter(Mandatory=$False,Position=3,ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True)][ConsoleColor] $BackgroundColor,
[Switch]$NoNewline
)
# Save previous colors
$previousForegroundColor = $host.UI.RawUI.ForegroundColor
$previousBackgroundColor = $host.UI.RawUI.BackgroundColor
# Set BackgroundColor if available
if($BackgroundColor -ne $null)
{
$host.UI.RawUI.BackgroundColor = $BackgroundColor
}
# Set $ForegroundColor if available
if($ForegroundColor -ne $null)
{
$host.UI.RawUI.ForegroundColor = $ForegroundColor
}
# Always write (if we want just a NewLine)
if($Object -eq $null)
{
$Object = ""
}
if($NoNewline)
{
[Console]::Write($Object)
}
else
{
Write-Output $Object
}
# Restore previous colors
$host.UI.RawUI.ForegroundColor = $previousForegroundColor
$host.UI.RawUI.BackgroundColor = $previousBackgroundColor
}
I know this post is ancient, but this could come handy to someone out there.
I wanted to change colors and the accepted answer was not the best solution. In my eyes, the following code is better solution as it takes advantage of the native PowerShell functionality:
EDIT:
# Print User message using String Array $message
function PrintMessageToUser {
param(
[Parameter( `
Mandatory=$True, `
Valuefrompipeline = $true)]
[String]$message
)
begin {
$window_private_data = (Get-Host).PrivateData;
# saving the original colors
$saved_background_color = $window_private_data.VerboseBackgroundColor
$saved_foreground_color = $window_private_data.VerboseForegroundColor
# setting the new colors
$window_private_data.VerboseBackgroundColor = 'Black';
$window_private_data.VerboseForegroundColor = 'Red';
}
process {
foreach ($Message in $Message) {
# Write-Host Considered Harmful - see http://www.jsnover.com/blog/2013/12/07/write-host-considered-harmful/
# first way how to correctly write it
#Write-host $message;
Write-Verbose -Message $message -Verbose;
# second correct way how to write it
#$VerbosePreference = "Continue"
#Write-Verbose $Message;
}
}
end {
$window_private_data.VerboseBackgroundColor = $saved_background_color;
$window_private_data.VerboseForegroundColor = $saved_foreground_color;
}
} # end PrintMessageToUser
I have the same problem, I need to log the output by screen with colors in interactive way and send that output to a file in autometed way.
My solution is to use a parameter to indicate the output type ('Screen' or 'File'), then the function can decide how to render de output.
function Write-Color([String[]]$Text, [ConsoleColor[]]$Color, [ConsoleColor]$BackgroundColor = ([console]::BackgroundColor), $OutputType='Screen') {
switch ($OutputType) {
'Screen' {
for ($i = 0; $i -lt $Text.Length; $i++) {
Write-Host $Text[$i] -Foreground $Color[$i] -NoNewLine -BackgroundColor $BackgroundColor
}
Write-Host
break
}
'File' {
# Assuming $OFS built-in Variable is an space
write-output "$Text"
break
}
Default {
throw '$OutputType must be "Screen" or "File".'
}
}
}
$CodeBlock = {
param ($OutputType)
Write-Color -T "=== STARTING ===" -C Cyan -B Gray -O $OutputType
Write-Color -T 'Date: ', (Get-Date).ToString('yyyy-MM-dd hh:mm:ss') -C Green, Yellow -O $OutputType
Write-Color -T 'Processing..' -C Cyan -O $OutputType
Write-Color -T 'Date: ', (Get-Date).AddSeconds(3).ToString('yyyy-MM-dd hh:mm:ss') -C Green, Yellow -O $OutputType
Write-Color -T "=== ENDING ===" -C Cyan -B Gray -O $OutputType
}
$Dir = 'D:\Tmp' # Set your output directory
#### Screen Test ####
& $CodeBlock -OutputType 'Screen'
& $CodeBlock -OutputType 'File'
### File Test ####
# This file have unwanted newlines, notice the IO redirection with "*>"
& $CodeBlock -OutputType 'Screen' *> "$Dir\Screen.log"
& $CodeBlock -OutputType 'File' > "$Dir\File.log"