I have been working on script that pulls ip addresses from a text file, and then pings them in a forever loop, if any are dead then the status output turn red, and also sends an email for alerting.
my script..
$hostnamestxt = "C:\ping_machines.txt"
$servers = get-content "$hostnamestxt"
$date = get-date
while ($true) {
$i++
Write-Host "-- Round $i Machines--" # -foregroundcolor black -backgroundcolor green
foreach($server in $servers){
if (test-Connection -ComputerName $server -Count 2 -Quiet )
{
write-host "$server is ONLINE" -foregroundcolor black -backgroundcolor green
}
else
{
$to = $server
switch ($to)
{
"192.168.252.37" {$device="RAPID"; break}
"192.168.252.39" {$device="Sash line Welder83"; break}
"192.168.252.40" {$device="Sash line Welder84"; break}
default {$device="error";exit}
}
write-host "$device, $server is OFFLINE/UNREACHABLE" -foregroundcolor black -backgroundcolor red
send-mailmessage -to "myemail" -subject "$device, $server is OFFLINE/UNREACHABLE at $date" -Body "$device, $server is OFFLINE/UNREACHABLE at $date"
}
}
}
the above works ok, but the issue i have is that when adding new devices to monitor, i have to add the ip in the txt file then also amend script and into the case statment.
what i would like to do, but not sure how to do is.. read the ip addresses from and excel spreadsheet, say from column A, and then the names will be in column B.
If any device is down it then translates the name from column B, and puts that into a variable to process later.
Any help much appreciated, as i am pretty much stuck at the moment.
Instead of specifying a text file, use a CSV. Then you can use the Import-CSV commandlet and it will turn the list into a collection of objects, which will have properties like IP and Name, depending on what you put in the list. You could even add something for criticality. I used your script and made a couple of really quick changes. Something like this should work.
CSV:
IP,Name
192.168.252.37,RAPID
...
Script:
$hostnamestxt = "C:\ping_machines.csv"
$servers = Import-Csv "$hostnamestxt"
$date = get-date
while ($true) {
$i++
Write-Host "-- Round $i Machines--" # -foregroundcolor black -backgroundcolor green
foreach($server in $servers){
$serverName = $server.Name
$serverAddress = $server.IP
if (test-Connection -ComputerName $serverAddress -Count 2 -Quiet )
{
write-host "$serverName is ONLINE" -foregroundcolor black -backgroundcolor green
}
else
{
write-host "$serverName, $serverAddress is OFFLINE/UNREACHABLE" -foregroundcolor black -backgroundcolor red
send-mailmessage -to "myemail" -subject "$serverName, $serverAddress is OFFLINE/UNREACHABLE at $date" -Body "$serverName, $serverAddress is OFFLINE/UNREACHABLE at $date"
}
}
}
Related
Summary:
I have 4 systems running Windows Server 2022. One system has the PS code and the other 3 are targets to run this code on via PS "Invoke-Command". The objective is to turn off the automatic time sync option on the remote system, then setting the date to the current date + 1 day. Then checking again on the date on those servers.
First, the script collects the list of servers:
$servers = Get-content -Path .\testservers.txt
Then, turns off the time sync option (via the only way I found: registry)
foreach ($server in $servers){
Write-host "Turning off time sync on $server - " -NoNewline -ForegroundColor Yellow
$scriptBlock = {
$props = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Parameters\"
if ($props.Type -ne "NoSync"){
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Parameters" -Name Type -Value "NoSync"
}
}
$result = Invoke-command -ComputerName $server -ScriptBlock $scriptBlock
Write-Host "$result"
}
Then, I set the date
foreach ($server in $servers){
Write-host "Adding 1 day on $server - " -NoNewline -ForegroundColor Yellow
$scriptBlock = { Set-Date -Date ($Using:currentDateTime).AddDays(1) }
$result = Invoke-command -ComputerName $server -ScriptBlock $scriptBlock
Write-Host "$result"
}
Then after the date change, I recheck the date
foreach ($server in $servers){
Write-host "Date on server $server - " -NoNewline
$result = Invoke-command -ComputerName $server -ScriptBlock { Get-Date }
Write-Host "$result"
}
I often see at least one of them switched back by this point. If I do another loop to check the dates perhaps 30 seconds later, they've all reverted back to current date.
(Yes, I know the code can be more streamlined.)
Any ideas? Could this be a matter of the registry change or the time change being done in a PS session and only living within that session?
I have been at this for days now but can't seem to figure it out.
I am trying to compare values(PartNumber) from the database to the first line of the text file. If the PartNumber exists(in the first line of the file) display a message "Part Number Matches" if it doesn't send an email.
The issue is that I get messages saying parts do not match when they are clearly the same.
All wrongly mismatched part numbers have a letter in the beginning for ex. D78948, C56234, etc. At first I thought maybe it is because the part number is alphanumeric. But then the parts that are correctly matched are also alphanumeric.
The PartNumber in the terminal is the one from the database.You can clearly see from the picture that the part number from the database and the one in the text file are exactly the same but the check still fails.
Instead of If (Get-Content -Path $fullPath -TotalCount 1 | Select-String -Pattern $partNo -AllMatches) I have tried
If($eiaContent -like "*$partno*") ,
If($eiaContent | Select-String -Pattern $partno)
But none of them seem to work.
Any help is appriciated!
Code:
$emailFrom = " CNC Data Discrepancy <DoNotReply#outlook.com>"
$emailTo = "ABC#nicholsonmfg.com"
$server = "Sidney.sidney.nii"
$database = "NML_Sidney"
#Query to get MAFN values in the last x hours(ET-Elapsed Time)
$MafnQueryEt = "SELECT [MAFN] As MAFNET FROM [NML_Sidney].[dbo].[MfgAidsDocLibraryTransactions]"
#Invoke Sql
$MafnSqlEt = Invoke-Sqlcmd -ServerInstance $server -Database $database -Query $MafnQueryEt
Write-Host $MafnSqlEt.MAFNET
$aidLibPathFolder = "C:\Users\Desktop\CNC_Transfer_Test_Folders\AidLib_Test"
$filterA = "MAZAK INTEGREX E-1550 WFO"
$filterB = "MILLING_MAZAK_INTEGREX_E1550"
#Filter child items to get the .EIA files
Get-ChildItem -Path $aidLibPathFolder -Include *.EIA -Recurse | ForEach-Object{
#Splitting the folder name because the first part of the name is the MAFN in the db(less the zeros). The second part after the dot(.) is the version number
$fullPath = $_.FullName
$baseName = $_.Name
$folderName = $_.Directory.Name
$splitFolderName = $folderName.Split('.')
$firstPart = $splitFolderName[0].TrimStart("0")
$version = "VER"+ " " + $splitFolderName[1]
$ver = $splitFolderName[1].TrimStart("0")
$versionNoZero = "VER"+ " " + $ver
#Filter the EIA files that have the specific line of text.
If(Get-Content -Path $fullPath | Select-String -Pattern $filterA,$filterB){
#Get first line of file.
$eiaContent= Get-Content -Path $fullPath -TotalCount 1
If($MafnSqlEt.MAFNET -eq $firstPart){
$partNoQuery = "SELECT [PartNo] FROM [NML_Sidney].[dbo].[vMADL_EngParts] Where MAFN=$firstPart"
$partNoSql = Invoke-Sqlcmd -ServerInstance $server -Database $database -Query $partNoQuery
$revQuery = "SELECT [MA_Rev] As Rev FROM [NML_Sidney].[dbo].[MADL_EngParts] Where MAFN=$firstPart"
$revSql = Invoke-Sqlcmd -ServerInstance $server -Database $database -Query $revQuery
[System.String]$partNo = $partNoSql.PartNo
$rev = "REV" + " "+ $revSql.Rev
$partNo.GetType()
}
If (Get-Content -Path $fullPath -TotalCount 1 | Select-String -Pattern $partNo -AllMatches){
Write-Host "PART matches" -BackgroundColor Green -ForegroundColor Black
Write-Host `PartNumberMatch` $partNo `Folder` $folderName `FileName` $baseName -BackgroundColor Green -ForegroundColor Black
}
Else{
Write-Host "PART does not match" -BackgroundColor Red -ForegroundColor Black
Send-MailMessage -From $emailFrom -To 'hargul.sidhu#nicholsonmfg.com' -Subject "ALERT! DATA DISCREPANCY - '$folderName'" -Body "Incorrect Part Number in '$baseName' Path -'$fullPath'. **DO NOT REPLY. THIS INBOX IS NOT MONITORED**" -SmtpServer 'XXXX
Write-Host `PartNumberMismatch` $partNo `Folder` $folderName `FileName` $baseName -BackgroundColor Red -ForegroundColor Black
}
I'm new to Powershell. I'm trying to automate a few things. My biggest obstacle right now is trying to get a failed Test-Connection to export into Excel with a -Foreground color. Green for "Online" and red for "Offline."
Another issue, it does ping a computer offline, it writes to the Computer Pane as multiple exceptions. Is there a way to make it do it only one time?
I would like to have the "Offline" ping in Excel (1,4).
Any help will be definitely appreciated! If you don't mind explaining how and why it works too, that would be perfect.
Thank you.
$Comps = Get-Content -Path "FilePath\ComputerList.txt"
$Date = Get-Date -Format MM-dd-yyyy
#Stop-Process -Name "Excel" -ErrorAction SilentlyContinue
#Suspends activity for a # of seconds.
Start-Sleep 3
$XL = New-Object -comobject Excel.Application
$xL.displayAlerts = $false
$WB = $XL.Workbooks.Add()
$WS = $WB.Worksheets.Item(1)
$WS.Cells.Item(1,1) = "Date"
$WS.Cells.Item(1,2) = "IP Address"
$WS.Cells.Item(1,3) = "Computer Name - Online"
$WS.Cells.Item(1,4) = "Computer Name - Offline"
$counter = 2
$CompStatus = #()
$Comps = Get-Content -Path "FilePath\ComputerList.txt"
$Processes = Test-Connection -ComputerName $Comps -Count 1| select #{n='TimeStamp';e={Get-Date}}, Address, ProtocolAddress
foreach($Proc in $Processes) {
If(Test-Connection $Comps){
Test-Connection $Comp
}
write-host "$Comp is online" -ForegroundColor Green
$WS.cells.item(2,1) = $Proc.TimeStamp
$WS.cells.item($counter,2) = $Proc.ProtocolAddress
$WS.cells.item($counter,3) = $Proc.Address
$WS.columns.autofit()
$counter++
}else{
write-host "$Comp is offline" -ForegroundColor Red
$WS.cells.item($counter,4) = $Proc.$CompStatus += $Comp + "Offline"
}
$XL.Visible = $True
$XL.ActiveWorkbook.SaveAs("FilePath\ComputerListPinged_$Date")
You really should have a look at Douglas Finke's ImportExcel Module, it has a Conditional Formatting parameter that works fine for what you need. You can also set RGB colors on it same as you would see them on Excel (like the classic light green background with green font, and light red background with red fount, etc). I think it will make your life easier.
Here are a few examples: https://dfinke.github.io/powershell/2020/05/02/PowerShell-Excel-and-Conditional-Formatting.html
The module itself is pretty easy to use and there are plenty of guides on Google.
This script takes input from a test file, writes the results to screen, file, and generates an email under certain conditions. The issue is that it is extremely slow with a large lists of hosts. I have looking into multi threading but I am a PS newbie and have not been able to figure it out. Any help would be appreciated. Here is the script.
# start looping here
Do{
$available = #()
$notavailable = #()
$ComputerName = #()
#Path to text file of hostnames we want to monitor
$ComputerName = Get-Content hosts.txt
Write-Host (Get-Date)
# Read the File with the Hosts every cycle, this way to can add/remove hosts
# from the list without touching the script/scheduled task,
# also hash/comment (#) out any hosts that are going for maintenance or are down.
$ComputerName | Where-Object {!($_ -match "#")} |
#"test1","test2" | Where-Object {!($_ -match "#")} |
ForEach-Object {
if(Test-Connection -ComputerName $_ -Count 1 -ea silentlycontinue)
{
# if the Host is available then write it to the screen
$value = #()
$value = $_
Write-Host "Available host ---> "$_ -ForegroundColor Green
[String[]]$available += $_
if ($value -notmatch "[a-z]")
{
$hosttoip = #()
$iptohost = #()
$parsemac = #()
$getmac = #()
$getmac = #()
$findstr = #()
$iptoip = #()
$getmac = nbtstat -A $_ | findstr "Mac Address = **-**-**-**-**" | Select-String -Pattern "Mac Address" -SimpleMatch
$iptoip = $_
$iptohost = [System.Net.Dns]::gethostbyaddress($_).Hostname
$findstr = findstr "$_" output.csv
Write-Host $_ "Responds with:" -ForegroundColor White
Write-Host "Hostname" -ForegroundColor White
Write-Host "$iptohost" -ForegroundColor Cyan
Write-Host "IP Address:" -ForegroundColor White
Write-Host "$_" -ForegroundColor Cyan
Write-Host "Mac Address:" -ForegroundColor White
Write-Host "$getmac`n" -ForegroundColor Cyan
}
if ($value -match "[a-z]")
{
$iptohost = #()
$hosttoip = #()
$hosttohost =#()
$parsemac = #()
$getmac = #()
$getmac = #()
$findstr = #()
$hosttohost = $_
$getmac = nbtstat -a $_ | findstr "Mac Address = **-**-**-**-**" | Select-String -Pattern "Mac Address" -SimpleMatch
$hosttoip = [System.Net.Dns]::GetHostByName($_).AddressList[0].IpAddressToString
$findstr = findstr "$_" output.csv
Write-Host $_ "Responds with:" -ForegroundColor White
Write-Host "Hostname" -ForegroundColor White
Write-Host "$_" -ForegroundColor Cyan
Write-Host "IP Address:" -ForegroundColor White
Write-Host "$hosttoip" -ForegroundColor Cyan
Write-Host "Mac Address:" -ForegroundColor White
Write-Host "$getmac`n" -ForegroundColor Cyan
}
if ($findstr -eq $Null)
{
$WriteFile = #()
$WriteFile = "$_,$iptohost $hosttohost,$hosttoip $iptoip,$getmac"
$WriteFile | Out-file output.csv -Encoding ascii -Append
}
# if the Host was out and is now backonline, remove it from the OutageHosts list
if ($OutageHosts -ne $Null)
{
if ($OutageHosts.ContainsKey($_))
{
Write-host $_ "was on the outages list and came back online. Sending email notification" -ForegroundColor Yellow
$OutageHosts.Remove($_)
$Now = Get-date
if ($notifyonServerBackOnline)
{
$Body = "$_ is online right now $getmac - If available, the MAC address and hostname/IP will be recorded in ScriptLocation\output.csv"
Send-MailMessage -Body "$body" -to $tonotification -from $fromnotification -Subject "Monitored host $_ is up" -SmtpServer $smtpserver
}
}
}
}
else
{
# If the host is unavailable, give a warning to screen
Write-Host "Unavailable host ------------> "$_ -ForegroundColor Red
if(!(Test-Connection -ComputerName $_ -Count 2 -ea silentlycontinue))
{
# If the host is still unavailable for 4 full pings, write error and send email
Write-Host "Unavailable host ------------> "$_ -ForegroundColor Red
[Array]$notavailable += $_
if ($OutageHosts -ne $Null)
{
if (!$OutageHosts.ContainsKey($_))
{
# First time down add to the list and send email
Write-Host "$_ Is not in the OutageHosts list, first time down"
$OutageHosts.Add($_,(get-date))
$Now = Get-date
if ($notifyonServerDown)
{
$Body = "$_ has not responded for 5 pings at $Now"
Send-MailMessage -Body "This message was generated by our ping monitoring script`n$body" -to $tonotification -from $fromnotification -Subject "Host $_ is down" -SmtpServer $smtpserver
}
}
else
{
# If the host is in the list do nothing for 1 hour and then remove from the list.
Write-Host "$_ Is in the OutageHosts list"
if (((Get-Date) - $OutageHosts.Item($_)).TotalMinutes -gt $EmailTimeOut)
{
$OutageHosts.Remove($_)
}
}
}
else
{
# First time down create the list and send email
Write-Host "Adding $_ to OutageHosts."
$OutageHosts = #{$_=(get-date)}
$Now = Get-date
if ($notifyonServerDown)
{
$Body = "$_ has not responded for 5 pings at $Now"
Send-MailMessage -Body "This message was generated by our ping monitoring script`n$body" -to $tonotification -from $fromnotification -Subject "Host $_ is down" -SmtpServer $smtpserver
}
}
}
}
}
Write-Host ""
Write-Host "Sleeping $SleepTimeOut seconds"
Start-Sleep -Seconds $SleepTimeOut
if ($OutageHosts.Count -gt $MaxOutageCount)
{
# If there are more than a certain number of host down in an hour abort the script.
$Exit = $True
$body = $OutageHosts | Out-String
if ($notifyonMaxOutageCount)
{
Send-MailMessage -Body "This message was generated by our ping monitoring script`n$body" -to $tonotification -from $fromnotification -Subject "More than $MaxOutageCount Hosts down, monitoring aborted" -SmtpServer $smtpServer
}
}
}
while ($Exit -ne $True)
I have a data set that looks like below
headers data
ip 192.168.1.1
netmask 255.255.255.0
description class C
ip 172.20.1.1
netmask 255.255.0.0
description class B
ip 10.0.0.1
netmask 255.255.255.0
description class A
How can I turn that into my expected output of:
ip netmask description
192.168.1.1 255.255.255.0 class C
172.20.1.1 255.255.0.0 class B
10.0.0.1 255.0.0.0 class A
Currently I am importing this data from CSV like below:
$data = import-csv .\data.txt -headers headers,data
If there is an easy way to do this in excel that would be fantastic as well.
Any assistance would be great. Thank you.
The previous answer didn't use the data specified in the question, this one does. It's also fully commented to explain what it does. Below the code is a rundown of how it works:
$newrow = #{} # newrow contains the values on an output row at any given time
$newtable = [System.Collections.ArrayList]#() # newtable contains the final output
# Loop through each row in the input data, assign oldrow to the current row
foreach($oldrow in $data) {
# If the output row already has an ip, netmask or description, then start a new row (hashtable) and add the current row (hashtable) to output
if ($newrow[$oldrow.headers] -ne $null) {
Write-Host "$($oldrow.headers) Already found. Adding new row to output" -ForegroundColor Green
# Add the current row to the target object
[void]$newtable.Add($(New-Object PSObject -Property $newrow))
# Create a new empty row (hashtable)
$newrow = #{}
}
# For each iteration, keep adding data
Write-Host "Adding new entry for $($oldrow.headers)"
$newrow[$oldrow.headers] = $oldrow.data
}
# The final row is not added to the table within the loop, so it must be added here
Write-Host "Loop finished, adding final row to output" -ForegroundColor Green
[void]$newtable.Add($(New-Object PSObject -Property $newrow))
$newtable | select ip,netmask,description
Assumption
Upon encountering a record with the same header value, a new row is required. Until that point, records should be added to the same row, with the header value providing the new column name.
Process
Set up a hashtable called $newrow.
Set up an ArrayList called $newtable. ArrayLists are more flexible than standard Arrays as they have an Add() method.
Iterate through the data using a foreach loop
Start with an if statement that deals with finding a record with a column that we have already assigned. At this point fire a message using Write-Host, add the old row and set up the new row. On the first iteration of the foreach loop, this always evaluates to false as the hashtable is empty.
In the block outside the if condition, keep adding values to the hashtable.
At the end, add the final row and then use select-object to print the output in the right order.
#Import data
$data = Import-Csv .\data.txt -headers headers,data
$data | FT -AutoSize
#Remove the first line
get-content $data | select -Skip 1 | set-content "$data-temp"
move "$data-temp" $data -Force
#Convert the table
$Duration = Measure-Command {
$b = #()
foreach ($Property in $data.Property | Select -Unique) {
$Props = [ordered]#{ Property = $Property }
foreach ($Server in $data.Server | Select -Unique){
$Value = ($data.where({ $_.Server -eq $Server -and
$_.Property -eq $Property })).Value
$Props += #{ $Server = $Value }
}
$b += New-Object -TypeName PSObject -Property $Props
}
}
Write-Host "Finished transposing " -ForegroundColor Green -NoNewline
Write-Host "$(($data | Get-Member -MemberType Properties).count)/$($data.Count)" -ForegroundColor Yellow -NoNewline
Write-Host " columns/rows into " -ForegroundColor Green -NoNewline
Write-Host "$(($b | Get-Member -MemberType Properties).count)/$($b.Count)" -ForegroundColor Yellow -NoNewline
Write-Host " columns/rows in " -ForegroundColor Green -NoNewline
Write-Host $Duration.Milliseconds -ForegroundColor Yellow -NoNewline
Write-Host " Milliseconds" -ForegroundColor Green
$b | FT -AutoSize
$b | Out-GridView
$b | Export-Csv .\newData.csv -NoTypeInformation
newData.csv should look as you expected, if not, let me know again.