LiquidObject

Exchange 2016 Holiday Calendar Loading

As time goes by scripts get updated. Here is an updated version of my old () which was originally written for Exchange 2010 under Server 2008.


$inputfile = "C:\Input\<mycalendarfile.csv"
$Webservices = "C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll"
$baseOU = "OU=Domain Users,DC=liquidobject,DC=com"
#Need to update each year, in the event we need to pull entries we can search of it potentially
$myyears = "2017-2018"
#If we need to run the once per year everyone run, set this to true
$YearlyRun = $false

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

####################### 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 = "Calendar Automation " + $myyears
    $appointment.LegacyFreeBusyStatus = "Free"
    $appointment.IsAllDayEvent = $true
    $appointment.IsReminderSet = $False
    $appointment.Save([Microsoft.Exchange.WebServices.Data.SendInvitationsMode]::SendToNone)
    $appointment = $null
    sleep 0.2
}

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

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
{
    $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
}

$myusers = $myusers | Sort-Object samaccountname
if($testmode)
{
    $myusers = @{"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.2
}
,
July 31, 2017 at 8:35 am Comments (0)

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)

Large exchange distribution automation

Exchange distribution groups are a very useful method for delivering email to a large number of clients, however every design has it’s limits. I needed to use a distribution list for a rotating number of users with a total count of close to 20,000 members. When looking at distribution groups with more than a few thousand entries causes scalability limits. Naturally I’d rather not have to manually load lists every night.

if(Get-Module -Name ActiveDirectory){}
else{Import-Module ActiveDirectory}

Write-Host "Loading employees"
$myusers = Get-ADUser -filter "*" -SearchBase "OU=Employees,DC=liquidobject,DC=com" -properties description | Select-Object samaccountname
Write-Host "Successfully loaded" $myusers.count "employee accounts."

$alpha = "a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"
foreach($i in $alpha)
{
    $mygroup = "EmployeeSub_$i"
    $myoldgroupmembers = Get-AdGroupMember -identity $mygroup | Select-Object SamAccountName
    Write-Host "Group:" $mygroup "has" $myoldgroupmembers.count "members"
    $mycurrentusers = $myusers | where {$_.samaccountname -like "$i*"}
    Write-Host "We currently have" $mycurrentusers.count "which should be in this group"
    
    $mydiff =  Compare-Object -ReferenceObject $myoldgroupmembers -DifferenceObject $mycurrentusers -property samaccountname
    $mydiff
    foreach($i in $mydiff)
    {
        if($i.SideIndicator -eq "=>"){Add-AdGroupMember -identity $mygroup -members $i.samaccountname}
        else {Remove-AdGroupMember -identity $mygroup -members $i.samaccountname -confirm:$False}
    }
}

The above provides a differential solution by splitting the single very large group into a series of 26 smaller, more manageable groups. Then we can wrap the 26 groups with a query-based distribution group for simplified delivery to clients using

new-DynamicDistributionGroup All_Employees -OrganizationalUnit "OU=My OU,DC=liquidobject,DC=com" -RecipientFilter {RecipientContainer -eq "OU=EmployeeGroups,My OU,DC=liquidobject,DC=com"}
, , ,
March 21, 2013 at 7:58 pm 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)

Messagetracking log compilation

If you ever needed to quickly all external messages sent to your organization but have the problem of multiple transport servers, the below script should help give some insight into the messages recently received.

$myminutes = 1
$myservers = "cashub-1","cashub-2","cashub-3"
$mydomain = "testdomain.local"

cls
$mystart = (Get-Date).addminutes(-$myminutes)
foreach($myserver in $myservers)
	{
		Write-host "------------------- start " $myserver "-------------------"
		Get-MessageTrackingLog -Start $mystart -server $myserver | where {($_.Sender -notlike "*$mydomain") -and ($_.Source -eq "SMTP")}
		Write-host "-------------------   end " $myserver "-------------------`n"
	}
, ,
July 30, 2012 at 3:54 am Comments (0)

« Older Posts