LiquidObject

Monitoring Exchange 2010 for Spammers

When using Exchange as your outside facing transport servers in either a dedicated Edge role under 2010 or within a multi-role setup finding out when you have a spammer from within historically has been done via blacklist notifications. What if we can catch the spammers in the act? What if we can stop the spam midstream? As a side benefit, you’ll get notification if mail is backing up for other reasons as well…ie random email providers being offline or if you end up having routing issues.

$servername = Get-Content env:computername
$mail_sender = "$servername@contoso.com"
$mail_server = "my_smtp_server.contoso.com"
$mail_recipient = "my_email@contoso.com"
$mailreport_subject = "Script: $servername Message Queues"
#At what level do you want to be emailed?
$maxinqueue = 40
$body = ""

Add-pssnapin Microsoft.Exchange.Management.PowerShell.E2010 -ErrorAction SilentlyContinue

function SendEmailReport
{

    $msg = New-Object System.Net.Mail.MailMessage $mail_sender, $mail_recipient, $mailreport_subject, $body
    $client = New-Object System.Net.Mail.SmtpClient $mail_server
    $client.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials
    $client.Send($msg)
}

$i = 0
while($i -lt 29)
{
	$mymessages = get-message -resultsize unlimited	
	#$mysenders  = $mymessages | select-object fromaddress
	if($mymessages.count -gt $maxinqueue)
	{
		$body = "Warning the current queue on $servername has exceeded the queue count of $maxinqueue and is currently at " + $mymessages.count
		$body += "`r`n"
		$body += $mymessages | out-string

		SendEmailReport
		$body = ""
	}
	$mymessages = $null
	write-host $i
	Sleep 60
	$i++
}

The $maxinqueue variable is the real trick, at what level of messages in the queue is normal for your organization?

Then all that needs to be done is configuration of a simple scheduled task say run every 30 minutes, the scripting logic is configured to run in a loop to cover at the per minute within a 30 minute window.

, ,
July 11, 2013 at 9:04 am Comments (0)

Exchange 2010 Holiday Calendar Loading

Within an organization I had need to bulk load behind the scenes a number of calendar items on all employee’s accounts. This script had multiple sources with the original idea coming from http://www.mikepfeiffer.net/2011/01/creating-calendar-items-with-powershell-and-the-ews-managed-api/

To get this working requires a few things.
1) Exchange 2010 with SP1 (currently running under SP2)
2) The Exchange EMS shell installed
3) The Exchange Web Services API to be installed (http://www.microsoft.com/en-us/download/details.aspx?id=28952)
4) Elevated permissions within the Exchange environment.

Beyond your normal administration rights is the requirement for the ability to Impersonate all users within the organization. Because of the level of access required to make these changes please verify with the organization that there are no legal issues with attempting this.

#Path to your holiday file
$inputfile = "D:\Data\HolidayCalendar2012-2013.csv"
#Path to the EWS DLL file
$Webservices = "C:\Program Files\Microsoft\Exchange\Web Services\1.2\Microsoft.Exchange.WebServices.dll"
#Where are the users
$baseOU = "OU=My Employees,DC=liquidobject,DC=com"

#Need to update each year
$myyears = "2012-2013"
$myuniquebody = "My Unique Calendar Automation Message" + $myyears
#The above body entry was the easiest way for users to see that the items were loaded by the IT department and also provides the one place where we can go back and modify/delete calendar entries later.

#If we need to run the once per year everyone run, set this to true
$YearlyRun = $false

#Testing mode options
$testmode = $false
$testuser = "Testuser99"

####################### End Options ########################

if(!(Get-Module | where {$_.Name -eq "ActiveDirectory"})){Import-Module ActiveDirectory}
if(!(Get-PSSnapin -Name "Microsoft.Exchange.Management.PowerShell.E2010" -ErrorAction SilentlyContinue)){Add-pssnapin Microsoft.Exchange.Management.PowerShell.E2010}
if(!(Get-PSSnapin -Name "Microsoft.Exchange.Management.PowerShell.support" -ErrorAction SilentlyContinue)){Add-pssnapin Microsoft.Exchange.Management.PowerShell.support}

if(!(Test-Path $Webservices))
{
    Write-Host "`n`nExchange Web Services API is required and is not installed`nhttp://www.microsoft.com/en-us/download/details.aspx?id=28952`n"
    Exit
}
if(!(Test-Path $inputfile))
{
    Write-Host "`n`nInput file of: $inputfile is missing, cannot proceed without this file.`n`n"
    Exit
}
Write-Host "`nEWS Web Services - Loading"
Add-Type -Path $Webservices
Write-Host "EWS Web Services API - Loaded`n"

function New-CalendarItem {
    [CmdletBinding()]
    param(
        [Parameter(Position=1, Mandatory=$true)]$CalendarUser,
        [Parameter(Position=2, Mandatory=$true)]$Subject,
        [Parameter(Position=3, Mandatory=$true)]$Date
        )
    
    $sid = (Get-ADUser -Identity $CalendarUser).SID
    $user = [ADSI]"LDAP://<SID=$sid>"
    $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService -ArgumentList ([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP1)
    $service.AutodiscoverUrl($user.Properties.mail)
    
    $Impersonate = (Get-Mailbox -Identity $calendaruser).PrimarySMTPAddress
    $ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId -ArgumentList ([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress),$Impersonate
    $service.ImpersonatedUserId = $ImpersonatedUserId
    
    $appointment = New-Object Microsoft.Exchange.WebServices.Data.Appointment -ArgumentList $service
    $appointment.Subject = $Subject
    $startdate = Get-Date($Date)
    $appointment.Start = $startdate
    $enddate = (Get-Date($Date)).adddays(1)
    $appointment.End = $enddate
    $appointment.body = $myuniquebody 
    $appointment.LegacyFreeBusyStatus = "Free"
    $appointment.IsAllDayEvent = $true
    $appointment.IsReminderSet = $False
    $appointment.Save([Microsoft.Exchange.WebServices.Data.SendInvitationsMode]::SendToNone)
#    Write-Host "New Entry: " $CalendarUser " - " $Subject " - " $Date
    $appointment = $null
    sleep 0.3
}

$mydata = Import-Csv $inputfile
Write-Host "Preparing to process" $mydata.count "calendar entries`n"

#Everyone, we need this once per year
if($YearlyRun)
{
    $myusers = Get-ADuser -Filter {(mail -like "*") -and (ObjectClass -eq "user") -and (Enabled -eq $true)} -SearchBase $baseOU -Properties created | Select-Object samaccountname, created
}
else
{
    #Normally grab just the last 7 days of users for a weekly run.
    $mydays = (Get-Date).Adddays(-7)
    $myusers = Get-ADuser -Filter {(mail -like "*") -and (ObjectClass -eq "user") -and (Created -gt $mydays) -and (Enabled -eq $true)} -SearchBase $baseOU -Properties created | Select-Object samaccountname, created
}

#sorting for easy troubleshooting
$myusers = $myusers | Sort-Object samaccountname
#For testing lock to a single user
if($testmode)
{
    $myusers2 = @{"samAccountName"=$testuser}
}

$today = Get-Date
$mycount = $myusers.count
$currentpos = 1
foreach($i in $myusers)
{
    Write-Host "Loading $currentpos of $mycount for:" $i.samaccountname
    foreach($entry in $mydata)
    {
        if($today -lt $entry.date)
        {
            #Write-Host $entry.date " - " $entry.Subject
            New-CalendarItem -CalendarUser $i.samaccountname -Subject $entry.Subject -date $entry.date
            sleep 0.2
        }
        else
        {
            if($YearlyRun) #only load past dates on the yearly run
            {
                New-CalendarItem -CalendarUser $i.samaccountname -Subject $entry.Subject -date $entry.date
                sleep 0.2
            }
        }
        
        sleep 0.1
    }
    $currentpos++
    sleep 1.5
}

All items created are all-day events with availability as as free with no reminders to annoy the staff. This is designed for an annual run and then adjustment of the “$YearlyRun” variable for weekly loads on all new employees.

The CSV file is in the following form:
Subject,Date
New Years Day, 2013/1/1
April Fools, 2013/4/1

Be careful on the characters used in the Subject entries as extra ” or ‘ characters can cause lots of headaches.

, ,
December 27, 2012 at 4:14 pm Comments (0)

Current Exchange CAS user counts

Below you’ll find a slight modification to a post from Mike Pfeiffer on querying Exchange 2010 CAS servers to obtain the current count of OWA and RPC clients.

function Get-CASActiveUsers {
  [CmdletBinding()]
  param(
      [Parameter(Position=0, ValueFromPipelineByPropertyName=$true, Mandatory=$true)]
      [String[]]$Name
      )

  process {
    $Name | %{
      $RPC = Get-Counter "\MSExchange RpcClientAccess\User Count" -ComputerName $_
      $OWA = Get-Counter "\MSExchange OWA\Current Unique Users" -ComputerName $_
      New-Object PSObject -Property @{
        Server = $_
        "RPC Client Access" = $RPC.CounterSamples[0].CookedValue
        "Outlook Web App" = $OWA.CounterSamples[0].CookedValue
      }
    }
  }
}

Get-CASActiveUsers CAS-1,CAS-2,CAS-3
, , ,
December 10, 2012 at 3:49 pm Comments (0)

Removing old SMTP Addresses in Exchange

If you’ve ever changed domain names or changed e-mail addresses within an organization usually there are always some leftovers which no one wants to clean up because it too much of a headache. For example smithj@testdomain.local changed to smithj@mynewdomain.local along with thousands of other users. Now after removing the old relaying information and MX records the addresses are no longer valid. Wrong, they are still valid for are users within the same Exchange Organization. Now to clean the addresses out for good you can process them one at a time or run something similar to the below.

In this sample script I have it divided up only to run on certain mailbox databases at a time, this is handy if you are consolidating multiple exchange organizations into a single organization where some users already exist and do not need to be looked at.

 

$mydb = "Employees_5"
$mydb = "Employees_6"
$mydb = "Employees_7"
$mydb = "Employees_8"
$mydb = "Employees_12"
$mydb = "Employees_15"
$icount = 0


if((Get-PSSnapin Microsoft.Exchange.Management.PowerShell.*).count -lt 2) {Add-PSSnapin Microsoft.Exchange.Management.PowerShell.*}
sleep 1

$mailbox = get-mailbox -Database "$mydb" -resultsize unlimited
$mailbox | foreach {
for ($i=0;$i -lt $_.EmailAddresses.Count; $i++)
{
    $address = $_.EmailAddresses[$i]
    if($_.EmailAddresses.Count -gt 1)
    {
    Write-host $_.SamAccountName","$_.EmailAddresses","$_.EmailAddresses.Count
        $i++
    }
    
    if ($address.SmtpAddress -notlike "*@liquidobject.com" )
    {
        Write-host("Removed smtp address: " + $address.AddressString.ToString() )
        $icount++
        $_.EmailAddresses.RemoveAt($i)
    }
}
write-host "Total of: " $icount " users in DB: $mydb"
, ,
April 16, 2012 at 10:22 am Comments (0)

Exchange Set-OutOfOffice

Recently I’ve had the need to have a programmable solution to toggling the out-of-office auto-reply functionality on a users mailbox. The below script will let you disable or enable the feature or get the current status of the out-of-office state. In my scenario I was looking for a way to time of day based enabling/disabling of a standardized auto-reply feature for a service desk which is open during the day but not at night. When combining this script with a scheduled task we can easily accomplish this.

 

The script below was originally saved as: Set-OutOfOffice.ps1

 

if(Get-PSSnapin Microsoft.Exchange.Management.PowerShell.*){}
else {
    Remove-PSSnapin Add-PSSnapin Microsoft.Exchange.Management.PowerShell.*
    Add-PSSnapin Microsoft.Exchange.Management.PowerShell.*
}

$error = 0

if($args[0] -ne $NULL)
{
    $mailbox = $args[0]
    if($args[1] -ne $NULL)
    {
        $enabledisable = $args[1]
    }
    else
    {
        Write-host The script needs to be called in the format: .\Set-OutOfOffice.ps1 mailbox-to-adjust disable/enable/status
        $error = 1
    }
}
else
{
    Write-host The script needs to be called in the format: .\Set-OutOfOffice.ps1 mailbox-to-adjust disable/enable/status
    $error = 1
}



if($error -eq 0)
{
    if($enabledisable -like "disable")
    {
        Write-Host ""
        Write-Host ""
        write-host "Disabling auto-reply on mailbox: $mailbox"
        get-mailbox $mailbox | Set-MailboxAutoReplyConfiguration -AutoReplyState Disabled
        Write-Host ""
        Write-Host ""
    }
    if($enabledisable -like "enable")
    {
        Write-Host ""
        Write-Host ""
        write-host "Enabling auto-reply on mailbox: $mailbox"
        get-mailbox $mailbox | Set-MailboxAutoReplyConfiguration -AutoReplyState Enabled
        Write-Host ""
        Write-Host ""
    }
    if($enabledisable -like "status")
    {
        Write-Host ""
        Write-Host ""
        $mystate = get-mailbox $mailbox | Get-MailboxAutoReplyConfiguration | Select-Object AutoReplyState
        write-host "Auto-reply for mailbox" $mailbox "is currently configured as:" $mystate.AutoReplyState
        Write-Host ""
        Write-Host ""
    }
    
}
, , ,
April 16, 2012 at 10:14 am Comments (0)

Simple ActiveSync Device Reporting

Here’s a quick script for reporting back users their ActiveSync partnerships.

$myusers= Get-CASMailbox -resultsize unlimited -Filter {HasActiveSyncDevicePartnership -eq $true} | select samaccountname,Name

$mydevices = foreach($i in $myusers){Get-ActiveSyncDeviceStatistics -Mailbox $i.samaccountname | select-object identity}

$mydevices | Export-Csv ActiveSyncDevices.csv
,
April 16, 2012 at 9:56 am Comments (0)

« Older Posts