header1   header
header
header Register : : Login header
header
connector   connector
menuleft menuright
submenu   submenu
left
Exchange 2007 external mail relay monitor
Last Post 13 Feb 2009 07:01 AM by Marco Shaw (MVP). 3 Replies.
Printer Friendly
  •  
  •  
  •  
  •  
  •  
Sort:
PrevPrev NextNext
You are not authorized to post a reply.
Author Messages
RobUser is Offline
New Member
New Member
Posts:19
Avatar

--
12 Feb 2009 04:21 PM

    This monitors external mail relays, tracks the round trip time of emails to the relay and back, and writes event log messages if the messages do not return in the configured time interval, if you want to use SCOM to alert on congestion or failures.

    I'm considering using a started task to stop it and re-start it within a maintenance window, but I still need to figure out the best way to insure that it gets restarted if the server re-boots, and that only one instance of it runs at a time.

    If anybody sees something that could be done better, let me know.

    $check_interval = 5
    $from_addr = ""
    $to_addr = ""
    $relays = "smtp1.relaydomain.com","smtp2.relaydomain.com"
    $hubservers = "",""
    $edgeservers = @{"" = "Edge1";"," = "Edge2"}
    $headings= '"Timestamp","Source","Relay","Edge_Recv","Hub_Recv","Trip_Time_seconds","Msg_Subj"'
    $outfile = $(get-date).tooadate().tostring() + "_mtr.csv"
    ac $outfile $headings

    $script_start = get-date

    #Create event log source and log start event in the application event log
    if (!([System.Diagnostics.EventLog]::SourceExists("Mail_Monitor"))){[system.diagnostics.eventlog]::createeventsource("Mail_Monitor","Application")}
    $app_log = new-object system.diagnostics.eventlog ("Application",".")
    $app_log.source = "Mail_Monitor"
    $app_log.writeentry("Mail Monitor script started.","Information",10220)

    $sent = @{}


    function sendemail {
    ####   Send email
    foreach ($relay in $relays){
       
    $from = "$from_addr"
       
    $to = "$to_addr"
       
    $subj = $subj_ts + $relay
       
    $body = get-date | out-string
       
    $SmtpClient = new-object system.net.mail.smtpClient
       
    $SmtpClient.Host = $relay
       
    $mailmessage = New-Object system.net.mail.mailmessage
       
    $mailmessage.from = ($from)
       
    $mailmessage.To.add($to)
       
    $mailmessage.Subject = $subj
       
    $mailmessage.Body = $body
       
       
    $smtpclient.Send($mailmessage)
       
    if (!($?)) {$app_log.writeentry("Error sending email $($subj) to $($relay) ","Warning",10222)}
       
    else {$sent.add($subj,$(get-date).tostring())}
        }
    }


    while ($true){
    $sentby = [environment]::machinename
    $subj_ts  = $(get-date).tooadate().tostring() + " Sent by " + $sentby + " To relay "
    sendemail
    $log_start = $(get-date).tostring()
    sleep -seconds $($check_interval * 60)
    $log_end = $(get-date).tostring()


    $msgtrk = @()
    $msg_returned = 0

    foreach ($hubserver in $hubservers){
    $msgtrk += get-messagetrackinglog -Sender $from_addr -Recipients $to_addr -Server $hubserver -EventID "RECEIVE" --Start $log_start -End $log_end
    }

    if ($msgtrk.count -ge 1){
    $msgtrk = $msgtrk | sort timestamp
    foreach ($msg in $msgtrk) {

    if ($sent[$msg.messagesubject] -and $msg.source -eq "SMTP"){
       
    $msg.messagesubject -match "^(\d{5}\.\d{1,10}?)\sSent\sby\s(.+)\sTo\srelay\s(.+)" > $nul
       
    $recvhost = $msg.serverhostname
       
    $recvedge = $msg.clientip
       
    $sent_ts = $matches[1]
       
    $mailrelay = $matches[3]
       
    $source = $matches[2]
       
    $trip_time = [timespan]::fromdays($msg.timestamp.tooadate() - $sent_ts)}
       
    $sent_time = [datetime]::fromoadate($sent_ts)
       
    $sent.remove($msg.messagesubject)
       
       
       
    $event_text = "Email round trip time for $mailrelay at $sent_time is $($trip_time.totalseconds) seconds."

    if ($trip_time.totalminutes -lt $check_interval){$app_log.writeentry($event_text,"Information",10221)}
    if ($trip_time.totalminutes -ge $check_interval){$app_log.writeentry("$event_text","Warning",10221)}

    $outstr = $msg.timestamp.tostring() + "," + $source + "," + $mailrelay + "," + $edgeservers[$recvedge] + "," + $recvhost + "," + $($trip_time.totalseconds)+ "," + $msg.messagesubject
    ac $outfile $outstr
    }
    }
    if ($sent.count -ne 0) {$app_log.writeentry("External email checks are overdue. `n $($sent) ","Warning",10222)}
    if ($sent.count -eq 0) {$app_log.writeentry("All external mail checks have been returned. ","Information",10222)}
    }



    ext_monitor.ps1

    Marco Shaw (MVP)User is Offline
    Veteran Member
    Veteran Member
    Posts:1647
    Avatar

    --
    13 Feb 2009 05:29 AM
    I've not reviewed the script. I thought WMI could say what was running like a script, but it appears not, at least on XP.

    I don't quite understand what you're saying about making sure the script starts on reboot.

    One approach for figuring out whether the script is already running is to use handle.exe from www.sysinternals.com. I can provide more details if you're stuck...


    Marco

    *Microsoft MVP - Windows PowerShell
    https://mvp.support.microsoft.com/profile/Marco.Shaw
    *Co-Author - Sams Windows PowerShell Unleashed 2nd Edition
    *Blog - http://marcoshaw.blogspot.com
    RobUser is Offline
    New Member
    New Member
    Posts:19
    Avatar

    --
    13 Feb 2009 06:24 AM
    I'm running the script on one of the Hub Transoport servers (Serrver 2003 x64), so I'll check on the WMI.

    I'd like for it to quit, and then restart sometime during the maintenance window to start a new event file, and free up memory.

    We run SCCM, so the server may get rebooted after an unattended patch installation, and the script needs to restart after a reboot. I think I can do that with a schtask command in a machine startup script, but I don't want to get one instance started by a reboot, and another one started by the scheduled task hitting it's run window.

    If it starts running multiple instance of the script, it starts logging multiple events in the appliation logs, and SCOM gets confused so I need to keep it single threaded.



    Marco Shaw (MVP)User is Offline
    Veteran Member
    Veteran Member
    Posts:1647
    Avatar

    --
    13 Feb 2009 07:01 AM
    Ah, yes, you will likely be doing something like this:
    powershell.exe -command "& {& 'C:\Documents and Settings\user\loop.ps1'}"

    In that case:
    PS > gwmi win32_process|?{$_.processname -eq "powershell.exe"}|%{$_.commandline}
    "C:\WINDOWS\system32\windowspowershell\v1.0\powershell.exe" -sta -noprofile
    powershell.exe -command "& {& 'C:\Documents and Settings\user\loop.ps1'}"

    Above, I have one powershell.exe console, and I just called powershell.exe from DOS. Notice that the full command-line shows so I can see that I have loop.ps1 running already.


    Marco

    *Microsoft MVP - Windows PowerShell
    https://mvp.support.microsoft.com/profile/Marco.Shaw
    *Co-Author - Sams Windows PowerShell Unleashed 2nd Edition
    *Blog - http://marcoshaw.blogspot.com
    You are not authorized to post a reply.


    Active Forums 4.3
    right
    footer   footer
    footer Sponsored by Quest Software • SAPIEN Technologies • Compellent • Microsoft Windows Server 2008 R2 footer
    footer   footer