Ping Code to reply another color - powershell

I am very new to coding so I know very little.
I am writing my first code which is a ping. It can ping any workstation ID that you put in it. I would like that when it pings, the reply would be green and request timed out would be red.
I just want to test it out and learn from it. So far I have this:
$Search = Read-Host "Enter Workstation ID"
$Reply = ping -t $Search
if ($Reply -like "Request Timed Out.") {
Write-Host $Reply -ForegroundColor Red
}
But that is not working.

If you look at the usage message from ping.exe, you'll see that the -t switch makes ping.exe continue pinging the host until interrupted by Ctrl+C or Ctrl+Break.
That's why it seems like it's "doing nothing".
I would probably go for the Test-Connection cmdlet, rather than ping.exe:
$Hostname = Read-Host "please enter the hostname..."
if(Test-Connection -ComputerName $Hostname -Quiet)
{
Write-Host "Ping succeeded!" -ForegroundColor Green
}
else
{
Write-Host "Ping failed!" -ForegroundColor Red
}
It doesn't have a -t parameter, but you can provide a ridiculously high value to the -Count parameter, and make due with that (with a second in between each request, [int]::MaxValue gives you 2^31 seconds, or 68 years worth of pinging):
Test-Connection $Hostname -Count ([int]::MaxValue)
If you sincerely want the -t switch, you can't rely on assignment to a variable, since PowerShell will wait for ping.exe to return (which, intentionally, never happens).
You can however pipe the standard output from ping.exe, and PowerShell's asynchronous runtime will keep them coming for as long as you like:
function Keep-Pinging
{
param([string]$Hostname)
ping.exe -t $Hostname |ForEach-Object {
$Color = if($_ -like "Request timed out*") {
"Red"
} elseif($_ -like "Reply from*") {
"Green"
} else {
"Gray"
}
Write-Host $_ -ForegroundColor $Color
}
}

Related

Ping and Install applications for multiple PCs

I hope someone can help me with the following script:
The script I built works for 1 computer. It asks the user to enter the computer name, and It will check if the computer is ONLINE, if it is it will copy and install the application.
Now, I want to add another functionality. I want users to create a TXT file with all the computer names (line by line) and save them somewhere in the computer. The user will run the script, and a Windows Explorer would open requesting the location of the TXT file. So, the script will collect that data (I might be wrong, but should I use arrays for that?) From there, I am assuming a ForEach statement should do it right?
The most difficult (for me) part is... if the computer is OFFLINE, it will skip that computer, and go for the next one... when it reaches the end of the lines, it should go back to the first line and check if the computer (that was offline) is online. The tricky part is... if the computer already has the app installed, it should skip it, and continue to keep pinging computers and so on. I want the script to run until someone manually stops it, installed the app to all the pcs, or runs for 3 days.
This might be too much to ask, but... it would also be nice to see a report of all the computers where the app was successfully installed (I am sure this should be on the ForEACH portion).
Thank you all
function HarvesterMenu {
$PCName = read-host -Prompt "Enter the computer name"
$PCName = $PCName -replace '(^\s+|\s+$)','' -replace '\s+',' '
$pingPC = Test-Connection -ComputerName $PCName -Quiet -Count 1 -ErrorAction SilentlyContinue
if ($pingPC -eq $True) {
Write-host "ONLINE"
harvesterInstall($PCName)
}
else {
Write-host "OFFLINE"
}
}
function HarvesterInstall($InstallOnPC) {
$Sourcefile = "\\SERVER1\discovery03$\HarvesterRemote.msi"
Copy-Item -Path $Sourcefile -Destination "\\$InstallOnPC\c$\windows\temp\HarvesterRemote.msi"
Invoke-Command -ComputerName $InstallOnPC -ScriptBlock {
Start-Process c:\windows\temp\HarvesterRemote.msi -ArgumentList "/quiet" -Wait
}
Start-Sleep 3
$InstCompleted = Test-Path -Path '\\$InstallOnPC\C$\Program Files (x86)\Labs\Harvester Remote\Harvester\Harvester.exe'
Write-Host $InstCompleted
if ($InstCompleted -eq $True) {
Write-host "Harvester was successfuly installed on $InstallOnPC"
}
else {
Write-host "Something went wrong. Ping the server and try again"
}
$BacktoMenu = read-host -Prompt "Press Y to go back to the Main Menu or N to exist"
if ($backtoMenu -eq "y") {
harvesterMenu
}
else {
Exit
}
}
HarvesterMenu
You can do that easily with a Foreach-Loop.
U need a File with the pc's u wanna install the application. maybe .txt or .csv
Then u can just address it in a foreach.
something like that:
$InputFile = 'C:\Temp\lIST.csv'
$addresses = get-content $InputFile
foreach($address in $addresses) {
if (test-Connection -ComputerName $address -Count 1 -Quiet ) {
write-Host "$address Online"
#ur harvesterinstall
}
else
{
Write-Warning "$address Offline"
}
}

Statements has no effect (if/else) in PS

I'm working on a script that gets executed only if X account is found, but is not working as intended the if/else statements get bypassed and the code gets executed anyways. What am i doing wrong?
$Account = "XXXX"
Get-LocalUser -name $Account
if (($Account) -eq $true) {
} else {
Write-host -foreground cyan "I found it"
}
exit
If i ran the script as is it will output the text on the console even tho "XXX" account is not present, could something like that can be done?
This should do it:
$Account = "XXXX"
$AccountObject=Get-LocalUser -name $Account -ErrorAction SilentlyContinue
if (($AccountObject)) {
Write-host -foreground cyan "I found it"
} else {
Write-host -foreground cyan "No luck"
}
The issue with the sniplet provided - the return of Get-LocalUser was not used. Instead you were using a string value which is always set therefore true - as you set it to 'XXXX' in your first line.
As Bill_Stewart explains, the reason that the else block is reached is because ($Account) -eq $true evaluates to $false unless the account name is "true".
In order to test whether Get-LocalUser succeeded or failed to retrieve the user account, you can instead inspect the $? automatic variable - it will have a value of $false only if the previous command threw an error:
$AccountName = "nonExistingUser"
# Try to fetch existing user account, don't show any errors to the user
$UserAccount = Get-LocalUser -Name $AccountName -ErrorAction SilentlyContinue
# Test if the call was successful
if($?) {
Write-Host "Found account named '$AccountName'!" -ForegroundColor Cyan
$UserAccount
} else {
Write-Host "No account named '$AccountName' was found ..." -ForegroundColor Magenta
}

Test-NetConnection being executed in switch when it should not

I have a powershell script that uses a menu and a selection. So loops until selection is 0. For one selection it tests entered port with "Test-NetConnection". But for some reason after this is ran, whenever another selection is made it seems "Test-NetConnection" is always ran, the banner shows up in powershell (even though it is very quick, but it shouldn't be executed.
I removed some selections. And I also have a few custom functions. But not sure why this is happening. Any help would be greatly appreciated!
Powershell...
while ($selection -ne 0) {
Write-Output ""
Write-Output "__MENU__"
Write-Output "0. Exit"
Write-Output "1. Set Target Machines"
Write-Output "2. Set Save Directory"
Write-Output "4. Save MSINFO32"
Write-Output "5. Save All Installed Software"
Write-Output "9. Save GPO Info (gpresult)
Write-Output "10. Test WMI"
Write-Output "11. Test TCP Port"
Write-Output "14. Test Syslog"
Write-Output ""
Write-Output "Target Machines: $($targetComputers -Join ',')"
Write-Output "Save Directory: $savePath`n"
$selection = Read-Host -Prompt "Selection"
Write-Output ""
switch ($selection) {
0 { break }
1 { ... }
2 { ... }
11 {
$testPort = Read-Host -Prompt "TCP Port to Test (Leave blank for default: 80)"
Write-Output ""
if (($testPort -eq $null) -Or ($testPort -eq "")) { $testPort = 80 }
foreach ($computer in $targetComputers) {
try {
$netcon = Test-NetConnection -ComputerName $computer -Port $testPort -ErrorAction "Stop"
if($netcon.TcpTestSucceeded) { Write-Output "TCP Port $testPort test was successful on $computer." }
else { Write-Warning "TCP Port $testPort test was unsuccessful on $computer." }
}
catch { Write-Warning "TCP Port $testPort test was unsuccessful on $computer." }
}
} #11
14 {
$sysServer = Read-Host -Prompt "Syslog Server (Leave blank for default: ${env:computername})"
$sysProto = Read-Host -Prompt "Syslog Server Protocol (Leave blank for default: TCP)"
$sysPort = Read-Host -Prompt "Syslog Server Port (Leave blank for default: 514)"
Write-Output ""
if (($sysServer -eq $null) -Or ($sysServer -eq "")) { $sysServer = $env:computername }
if (($sysProto -eq $null) -Or ($sysProto -eq "")) { $sysProto = "TCP" }
if (($sysPort -eq $null) -Or ($sysPort -eq "")) { $sysPort = 514 }
sendSyslog -Server $sysServer -Protocol $sysProto -Port $sysPort
} #14
default { break }
} #switch
} #while
Your menu option "9. Save GPO Info (gpresult) is missing a closing quote.
Make this easier for yourself and use a Here-String for the menu:
$menu = #"
__MENU__
0. Exit
1. Set Target Machines
2. Set Save Directory
4. Save MSINFO32
5. Save All Installed Software
9. Save GPO Info (gpresult)
10. Test WMI
11. Test TCP Port
14. Test Syslog
"#
Then instead of looping like while ($selection -ne 0), run an endless loop you can exit as soon as the user types a '0'.
# enter an endless loop here.
# exit that loop right after the Read-Host when the user type '0'
while ($true) {
Write-Host $menu
# some in-between code.. #
$selection = Read-Host -Prompt "Selection"
if ($selection -eq '0') { break } # or exit if you want to stop the entire script here
# rest of your code.. #
}
P.S. anything coming back from Read-Host is a string, not a number.

Checking for multiple conditions without a break in Powershell

I am working on a validation script for a Domain Controller. I have an account that has 4 things that I need to verify. The code that I have works but I don't want it to break if one of the conditions aren't met and that is what it is doing now. So, basically I'm needing it to check all 4 criteria, regardless if the condition is met or not.
Here is my code:
if(net user "user.dadm") {
Write-Host "[√] User.dadm was created successfully" -fore GREEN
if((((uACADA "user.dadm") -band 65536) -ne 0) -eq $true) {
Write-Host "[√] Password for user.dadm is set to never expire" -fore GREEN
if((Get-ADGroupMember -Identity "Domain Admins").Name -contains "user.dadm") {
Write-Host "[√] User.dadm was added to Domain Admins" -fore GREEN
if((((uACADA "user.dadm") -band 1048576) -ne 0) -eq $true) {
Write-Host "[√] Account Delegation Authority was removed" -fore GREEN
} else {
Write-Host "[X] Account Delegation was not removed" -fore RED
}
} else {
Write-Host "[X] User.dadm was not added to Domain Admins" -fore RED
}
} else {
Write-Host "[X] Password for user.dadm has been set to expire" -fore RED
}
} else {
Write-Host "[X] User.adam was not created" -fore RED
}
What this does is it will break to the else statement if any of the conditions aren't met but I need it to continue checking each condition.
I know that I could break this up and check each condition individually but I prefer to have it as compressed as possible as it is already going to be over a thousand lines.
I guess the REAL question is if this can be done without making 4 separate IF/ELSE statements.
I'm not sure if its a structuring issue (nested IF) or a command issue (using IF and ELSE when I should use IF and ELSEIF).
Could someone point me in the right direction? Thanks in advance.
not tested, but this may be a starting point, though you may just choose the simplicity of multiple if statements.
$user = 'user.dadm'
$info = [ordered]#{
created = "[X] $user was not created"
neverexp = "[X] Password for $user has been set to expire"
admin = "[X] $user was not added to Domain Admins"
delegation = "[X] Account Delegation for $user was not removed"
}
switch ($true) {
{net user $user} {
$info.created = "[√] $user was created successfully"
}
{((uACADA $user) -band 65536) -ne 0} {
$info.neverexp = "[√] Password for $user is set to never expire"
}
{(Get-ADGroupMember -Identity 'Domain Admins').Name -contains $user} {
$info.admin = "[√] $user was added to Domain Admins"
}
{((uACADA $user) -band 1048576) -ne 0} {
$info.delegation = "[√] Account Delegation Authority for $user was removed"
}
default {Write-Host 'all are false'}
}
$info.Keys | % {
if ($info.$_.startswith('[X]')) {
Write-Host $($info.$_) -ForegroundColor Red
} else {
Write-Host $($info.$_) -ForegroundColor Green
}
}
Yes, you can write it without if tests, e.g.
$colors = #('Red', 'Green')
$messages = #('[X] Failed -', '[√] Succeeded -')
$username = "user.adm"
$result = [bool](net user "$username")
Write-Host "$($messages[$result]) Check user account exists" -ForegroundColor $colors[$result]
$result = [bool]((uACADA "$username") -band 65536)
Write-Host "$($messages[$result]) Check password never expires" -ForegroundColor $colors[$result]
$result = [bool]((Get-ADGroupMember -Identity "Domain Admins").Name -contains "$username")
Write-Host "$($messages[$result]) Check account is a domain admin" -ForegroundColor $colors[$result]
$result = [bool]((uACADA "$username") -band 1048576)
Write-Host "$($messages[$result]) Check Delegation Authority removed" -ForegroundColor $colors[$result]
NB. that if you aren't using if/else, you need some other way to do the true/false testing. I'm casting results to [bool] and using 2-element arrays. $array[$false] casts $false -> 0 and gets element 0, $array[$true] casts $true -> 1 and gets element 1. That's how the result is turned into the appropriate colors and messages.
Another reasonable way to write this would be to move the Write-Hosts into a function.
Function Report-Result {
param($Result, $Text)
if ($Result) {
Write-Host "Success - $Text" -Fore Green
} else {
Write-Host "Failure - $text" -Fore Red
}
}
$result = [bool](...)
report-result $result "Check password never expires"
... etc.
Output looks like:
Which is okayyyy - it gets your 20 lines of code down to ~10, and has no nesting, and runs all the tests.
But it really feels like you're re-inventing PowerShell DSC ("my desired state is that accounts with .adm at the end have their passwords set to never expire") or Pester - PowerShell's test framework, ("test that all .adm accounts have passwords set to never expire").
I'm not sure that you can exactly fit Pester into your use case, but it makes me think I would change everything about the script from the way it's structured to the output messages, to make it look and feel like Pester. Particularly I want clear separation of test definitions and printed output, e.g.:
1. Here are my tests, with self-explanatory names
2. Here is a loop which runs all tests, and reports on them
and that gives me something like:
Function Validate-DCUserAccountShouldExist {
param($Username) [bool](net user "$UserName")
}
Function Validate-DCUserAccountPasswordNeverExpireShouldBeSet {
param($Username) [bool]((uACADA "$username") -band 65536)
}
Function Validate-DCUserAccountShouldBeADomainAdmin {
param($Username) [bool]((Get-ADGroupMember -Identity "Domain Admins").Name -contains "$username")
}
Function Validate-DCUserAccountShouldHaveDelegationAuthorityRemoved {
param($Username) [bool]((uACADA "$Username") -band 1048576)
}
# Search for all Validation functions and run them
$tests = (gci function: |Where Name -Like 'Validate-DCUserAccount*').Name
foreach ($test in $tests) {
$result = try {
& $test "user.adm"
} catch {
$false
}
if ($result) {
Write-Host -ForegroundColor Green "[√] $test - succeeded"
} else {
Write-Host -ForegroundColor Red "[X] $test - Failed"
}
}
Which gives an output like:
My view of my second code is that:
It's longer
more complex, less easy to follow
a lot less duplication of Write-Host
more structured, separation of test definitions and output
function names explain what the state should be, which makes the output messages work for success/failure
Output messages are neater in the code, but uglier to read in the output (but could be adjusted, e.g. add spaces when printing them)
would scale to more tests nicely, just by adding more tests

powershell to Ping,RDP,RemoteRegistry,WMI

I wrote a Powershell script that will perform Ping, RDP(3389), Remote Registry and WMI connectivity to a remote computer. Below is the script that I have come up with. Now I would like to get the output in a short format such as:
PING : SUCCESS
RDP : FAIL
Remote Registry : FAIL
WMI : SUCCESS
Remote Registry check has FAILED.
RDP check has FAILED.
PING check SUCCEEDED
WMI check SUCCEEDED
I am looking for help as I am new to Powershell scripting. Thank you in advance!!
Write-Host `n
$inputfromuser = Read-Host "Enter Server Name"
If($inputfromuser -like "")
{
Write-Host "User Input cannot be blank. Please enter the server name"
Write-Host `n
}
else
{
Write-Host `n
Write-Host -ForegroundColor Yellow "CHECKING PING CONNECTIVITY TO SERVER $inputfromuser"
Test-Connection -ComputerName $inputfromuser -Quiet -Count 1
Write-Host `n
Write-Host -ForegroundColor Yellow "CHECKING RDP PORT 3389 CONNECTIVITY ...."
Test-NetConnection -ComputerName $inputfromuser -CommonTCPPort RDP -InformationLevel Quiet
Write-Host `n
Write-Host -ForegroundColor Yellow "CHECKING REMOTE REGISTRY CONNECTIVITY ...."
$regkey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine,$inputfromuser)
$ref = $regkey.OpenSubKey("SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall")
If (!$ref) {
Write-Host -ForegroundColor Red "REMOTE REGISTRY CHECK FAILED"
}
Else {
Write-Host -ForegroundColor Green "REMOTE REGISTRY CHECK SUCCESS"
}
Write-Host `n
Write-Host -ForegroundColor Yello "CHECKING REMOTE WMI CONNECTIVITY ...."
$wmi = GWMI -Query "Select * from Win32_PingStatus where Address = '$inputfromuser'"
If (!$wmi) {
Write-Host -ForegroundColor Red "REMOTE WMI CHECK FAILED"
}
Else {
Write-Host -ForegroundColor Green "REMOTE WMI CHECK SUCCESS"
}
Write-Host `n
}
I'd recommend separating tests from output creation. Assign the results of your checks to separate variables:
$ping = Test-Connection ...
$rdp = Test-NetConnection ...
$regkey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $inputfromuser)
$ref = $regkey.OpenSubKey("...")
$wmi = Get-WmiObject -Query "..."
create 2 hashtables:
$state_noun = #{
$true = 'SUCCESS'
$false = 'FAIL'
}
$state_verb = #{
$true = 'SUCCEEDED'
$false = 'FAILED'
}
and create the output with a here-string:
$result = #"
PING : $($state_noun[$ping])
RDP : $($state_noun[$rdp])
Remote Registry : $($state_noun[[bool]$ref])
WMI : $($state_noun[[bool]$wmi])
Remote Registry check has $($state_verb[[bool]$ref]).
RDP check has $($state_verb[$rdp]).
PING check $($state_verb[$ping])
WMI check $($state_verb[[bool]$wmi])
"#
Write-Host $result
If you must have highlighted/colored values in your output, use a custom output function like this:
function Write-Result {
[CmdletBinding()]
Param(
[Parameter()][string]$text,
[Parameter()][bool]$value
)
Write-Host $text -NoNewline
if ($value) {
Write-Host $value -NoNewline -ForegroundColor Green
} else {
Write-Host $value -NoNewline -ForegroundColor Red
}
Write-Host '.'
}
Write-Result 'PING : ' $state_noun[$ping]
...
Write-Result 'Remote Registry check has ' $state_verb[[bool]$ref]
...