New Member with my first question

This topic contains 7 replies, has 2 voices, and was last updated by Profile photo of William Crum William Crum 5 months ago.

  • Author
    Posts
  • #46384
    Profile photo of William Crum
    William Crum
    Participant

    I apologize for the vague subject, but I really did not know a good short subject to cover my question.

    I am new to powershell and brand new to these forums, and I figured I would ask for some help. Rather than go all through what I am having problems with (Multiple aspects of my script), I will tell you what I want to do, and show you my half finished script with my problem areas I am stuck on highlighted, and I am sure you guys will probably be able to give me a 10 line code that will do what my 1000 line script would of done haha.

    Task: Test-Connection multiple Servers from a text file (I can load this and do the for-each) but instead of simply returning a yes or no, I want to take that and either start or check a timer for each server, so I do not count it as bad until it has been down for a configurable amount of time ($AllowedDownTime) before an email notification is sent. I also have a Location for each Server as well, for logging purposes, and the two are loaded into arrays in the same statement so that the index is the definition of the array.

    Problem: I know how to build the location and computer arrays to have the correct index, but I cannot figure out an efficient way to assign my timers in the same way... so what my code shows (Underlined) is me starting a switch statement to ID the correct facility and define the timer. This is already looking to take a LOT of code just for this one part, not to mention the logging which I hope to do in a Date(Folder)\Server(File) format and start a new folder daily, so then I can clean old logs older than 30 days by using the folders.

    I know I am probably recreating the wheel here, there are lots of examples of testing servers, but I couldn't find any with logging, notifications etc. and that is what is I am having the most trouble with.

    Thanks for any help and guidance you can provide, and it is great to be a new member of the site here and learning this wonderful tool!

    ###########################################################
    ##
    ## Script Purpose: Check Summit Downtime Computers
    ## By: Bill   ----    Date: 7/9/2016
    ## 
    ## Instructions: Running this script from any PC will
    ##               Begin monitoring the Summit Downtime
    ##               Computers defined in the Serverlist.txt
    ##               file.
    ##
    ## Script Directory: C:\Scripts\SummitCheck
    ## Log Directory: C:\Scripts\SummitCheck\Logs
    ##
    $ServerList="C:\Scripts\SummitCheck\ServerList.txt"
    $Logpath="C:\Scripts\SummitCheck\Logs"
    $Rawlist=Get-Content -Path $Serverlist
    $Computers = @()
    $Facilities = @()
    Foreach ($strObj in $Rawlist)
        {
        $Items=$strObj.Split(",")
        $Computers+=$Items[0]
        $Facilities+=$Items[1]
        #$FacTimer+=[Diagnostics.Stopwatch]::StartNew()
        #$FacTimer.Reset()
        }
    Function Check-PC
        {
        Param ([String]$ComputerName, [String]$Loc)
        If(Test-Connection -ComputerName $ComputerName)
            {
            #Do code for good PC
            }
        Else
            {
            #Do Code for down PC
            }
        }
    Function Log-Bad
        {
        Param ([String]$ComputerName)
        $Log=$Logpath + "\$ComputerName"
        $BadTime=Get-Date
        Add-Content -path $Log -Value "$BadTime -- Connection Failed to $ComputerName!"
        }
    Function Check-Timer ##This is the function I am having problems with :( and looking for a more efficient way.
        {
        Param ([String]$Loc)
        Switch($Loc)
            {
                "FWCH"
                    {
                    If($tmrFWCH)
                        {
                        If($tmrFWCH.isRunning)
                            {Return $tmrFWCH.Elapsed.TotalSeconds}
                        Else
                            {$tmrFWCH.Start();Return $tmrFWCH.Elapsed.TotalSeconds}
                        }
                    }
    
    
    
            }
    
        }
    
  • #46388
    Profile photo of William Crum
    William Crum
    Participant

    Well, I just commented the code where I was having a problem... I was afraid underlining in the code block would mess it up.

  • #46390
    Profile photo of William Crum
    William Crum
    Participant

    I think I may of figured out a way to do what I need, but I am not sure if this is best practice or if it might present problems later in referencing the timers.

    Foreach ($strObj in $Rawlist)
        {
        $tmrIndex=[Array]::indexof($strObj, $Rawlist)
        $Items=$strObj.Split(",")
        $Computers+=$Items[0]
        $Facilities+=$Items[1]
        $FacTimer[$tmrIndex]=[Diagnostics.Stopwatch]::StartNew()
        $FacTimer[$tmrIndex].Reset()
        }
    

    Is it going to be inefficient to have 8 timers running at one time continuously? This script will be a constantly running script.

  • #46392
    Profile photo of William Crum
    William Crum
    Participant

    OK, That didn't work :(....

    Unable to index into an object of type System.Diagnostics.Stopwatch.
  • #46394
    Profile photo of William Crum
    William Crum
    Participant

    Persistence pays off!!!

    More research into multidimensional arrays led me to a post where a Hash was defined. I had never heard of a Hash so I learned something new with this.

    My Code is now working correctly, but I still do not know if having 8 timers constantly instantiated is a bad idea performance wise. I run multiple scripts on the server I plan to run this on, and I don't want to hinder performance to awful bad.

    Below is the good code, note the hash definition $FacTimer = @{} with {} instead of (). This opens up SO much more :).

    $ServerList="C:\Scripts\SummitCheck\ServerList.txt"
    $Logpath="C:\Scripts\SummitCheck\Logs"
    $Rawlist=Get-Content -Path $Serverlist
    $Computers = @()
    $Facilities = @()
    $FacTimer = @{}
    Foreach ($strObj in $Rawlist)
        {
        $tmrIndex=[Array]::indexof($strObj, $Rawlist)
        $Items=$strObj.Split(",")
        $Computers+=$Items[0]
        $Facilities+=$Items[1]
        $FacTimer[$tmrIndex]=[Diagnostics.Stopwatch]::StartNew()
        #$FacTimer[$tmrIndex].Reset()
        Sleep -Seconds 1
        $FacTimer[$tmrIndex].Elapsed.TotalSeconds
        }
    

    Thanks for listening to me question my way through this problem, and I would still love to hear any suggestions on a better way to do this if anyone has any more efficient ways to code this.

    • #46398
      Profile photo of Jonathan Warnken
      Jonathan Warnken
      Participant

      The timers by themselves should not be a big performance hit. That being said the only way to know the impact would be to measure it. For what you are describing collecting permon data for memory and cpu load as baseline for a few days and then do the same once your script is running would let you identify the impact your script has on the server.

  • #46396
    Profile photo of Jonathan Warnken
    Jonathan Warnken
    Participant

    Personally I would have the check record the failed test-connection in a data file (For a large number of systems) or variable (for a small number of systems) with the computer name and the date and time. Subsequent checks would check to see if there was a previous failure for that computer name and then check time to see if the notification should be sent. This approach would mean that when the test-connection succeeds you would also need to check for previous failures and clear the record.

  • #46407
    Profile photo of William Crum
    William Crum
    Participant

    Thanks Jonathan! I wish I had read this before I completed my script haha!

    I am done however, and like I said... with my lack of experience I took a lot of lines of code, to do something most on here can probably do in under 10-20 lines. It does work however, and I learned a lot doing it.

    Removed the e-mail variable values for privacy, and I changed to one log file per day instead of a log file for each PC each day. I will add the log cleaner later, it is easy.

    ###########################################################
    ##
    ## Script Purpose: Check Summit Downtime Computers
    ## By: Bill   ----    Date: 7/9/2016
    ## 
    ## Instructions: Running this script from any PC will
    ##               Begin monitoring.
    ##
    ## Script Directory: C:\Scripts\SummitCheck
    ## Log Directory: C:\Scripts\SummitCheck\Logs
    ##
    $TimeLimit=120 #This is the time in seconds before the Email Notification is sent. 120 = 2 minutes.
    $Delay=30 #Time between ping checks
    $Mailfrom = 
    $MailTo = 
    $MailSubject = 
    $SMTPServer =
    $ServerList="C:\Scripts\SummitCheck\ServerList.txt"
    $Logpath="C:\Scripts\SummitCheck\Logs"
    if(!(Test-Path -Path $Logpath )){New-Item -ItemType directory -Path $Logpath}
    $Rawlist=Get-Content -Path $Serverlist
    $A=0
    $Computers = @()
    $Facilities = @()
    $FacTimer = @{}
    $Notified=@()
    Foreach ($strObj in $Rawlist)
        {
        $tmrIndex=[Array]::indexof($Rawlist, $strObj)
        $Items=$strObj.Split(",")
        $Computers+=$Items[1]
        $Facilities+=$Items[0]
        $FacTimer[$tmrIndex]=[Diagnostics.Stopwatch]::StartNew()
        $FacTimer[$tmrIndex].Reset()
        $Notified+=0
        }
    Function Log-Bad
        {
        Param ([String]$ComputerName,[String]$Loc,[Double]$TimerSeconds)
        $Today=(get-date -Format d).Replace("/","-")
        $Log="$Logpath\$Today.txt"
        $BadTime=Get-Date
        [Int]$Seconds=$TimerSeconds
        Add-Content -path $Log -Value "$BadTime -- $Loc -- Connection Failed to $ComputerName for $TimerSeconds Seconds!"
        }
    Function Log-Good
        {
        Param ([String]$ComputerName,[String]$Loc)
        $Today=(get-date -Format d).Replace("/","-")
        $Log="$Logpath\$Today.txt"
        $GoodTime=Get-Date
        Add-Content -path $Log -Value "$GoodTime -- $Loc -- Connection Successful to $ComputerName!"
        }
    
    Do
    {
    ForEach($PC in $Computers)
        {
        $MIndex=[Array]::indexof($Computers, $PC)
        If(Test-Connection $PC -Count 1 -Quiet)
            {
            Log-Good -ComputerName $PC -Loc $Facilities[$MIndex]
            $FacTimer[$MIndex].Reset()
            $Notified[$MIndex]=0
            $Facility=$Facilities[$MIndex]
            }
        Else
            {
            If($FacTimer[$MIndex])
                {
                If($FacTimer[$MIndex].IsRunning)
                    {
                    $Elapsed=$FacTimer[$MIndex].Elapsed.TotalSeconds
                    Log-Bad -ComputerName $PC -Loc $Facilities[$MIndex] -TimerSeconds $Elapsed
                    If($Elapsed -gt $Timelimit)
                        {
                        If($Notified[$MIndex] -ne 1)
                            {
                            $Loc=$Facilities[$MIndex]
                            $MailBody="The Summit Downtime Computer at $Loc has not responded in $Elapsed Seconds!The Computer Name for that Location is: $PC"
                            Try
                                {
                                Send-MailMessage -to $MailTo -From $Mailfrom -Subject $MailSubject -BodyasHTML -body $MailBody -SmtpServer $SMTPServer -ErrorAction Stop
                                }
                            Catch
                                {
                                Sleep -Seconds 5
                                Send-MailMessage -to $MailTo -From $Mailfrom -Subject $MailSubject -BodyasHTML -body "$MailBody 2nd Attempt" -SmtpServer $SMTPServer -ErrorAction SilentlyContinue
                                }
                            $Notified[$MIndex]=1
                            }
                        Else
                            {
                            If($Elapsed -gt 3600){$Notified[$MIndex]=0}
                            }
                        }
                    }
                Else
                    {
                    $FacTimer[$MIndex].Start()
                    }
                }
            Else
                {
                Write-Host -ForegroundColor Yellow "Timer not initialized!"
                Exit
                }
            }
        }
    
        Sleep $Delay
    }
    Until($A -ne 0)
    

You must be logged in to reply to this topic.