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)

Mass enabling windows features via PowerShell

Recently I needed to add the SNMP service to a few hundred systems. There are few more involved methods for this via unattended installs with SCCM or batch scripted GPO-linked entries but if you want a quick and easy way how, try the below.

Target OS: Server 2008, 2008R2, 2012 and 2012R2

Source system was a 2012R2 box with the AD and ServerManager PowerShell modules installed

Import-Module ActiveDirectory
$myservers = Get-ADComputer -SearchBase "OU=My Servers,DC=liquidobject,DC=com" -Filter "*"
foreach($i in $myservers)
{
 $mysession = New-PSSession -ComputerName $i.name
 Invoke-Command -Session $mysession {Import-Module Servermanager}
 Invoke-Command -Session $mysession {Add-WindowsFeature SNMP-Service}
}

Any server in the OU offline or running an unsupported os (ie Server 2003….we all have them) will throw an error. The above is pretty basic example on how you can install any Windows feature remotely. The remote PSSession and Invoke-Command methods allow you to perform any supported PowerShell command remotely.

In the event your running a legacy version of Windows, there are still options available. The limitation of the below is that it requires you to login to the given system and run the command with an administrative command prompt.

servermanagercmd.exe -install snmp-service
logout
, , , , ,
October 16, 2014 at 1:59 pm 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)

Clearing old Print Jobs

When using Windows Server as a print server over time the queues eventually begin to fill up. Print jobs are sent one day when a printer is offline and days later after the printer is turned back on sometimes dozens of print jobs start coming from the printer. The other issue that arises is caused by this backup of print jobs, slowly the print server will use disk space until in space cases it just runs out. Here is a simple PowerShell script to clear up the stale print jobs.

$TooOld = (Get-Date).AddDays(-2)
Get-WmiObject Win32_PrintJob | Where-Object { $_.ConvertToDateTime($_.TimeSubmitted) -lt $TooOld } | Foreach-Object { $_.Delete() }

This can be setup as an easy scheduled task to take of ever needing to worry about this issue in the future.

, , ,
June 25, 2013 at 11:41 am Comments (0)

Runaway process checking

Recently I ran into an issue with PHP exhaustion on a Windows Server running IIS. In this scenario the PHP-CGI.exe process would continue to spawn additional instances as load on the server would increase but over time the application pool would struggle and begin to slow to a crawl. In the past I have seen other applications during various iterations of development run into the same issue where if you run into more than “x” instances of an application it is unhealthy or less than “y” instances it is not running properly.

 

$myprocess = "php-cgi"
$myserver = "WebServer"
$mydomain = "liquidobject.com"
$mail_server = "mail.liquidobject.com"
$mail_recipient = "my_support_team@liquidobject.com"
$toomany = 40
$waytoomany = 80

$mail_sender = "$myserver@$mydomain"
$mailreport_subject = "Script: $myserver $myprocess count"
$body = " "

function SendEmailReport
{
    $body = [string]::join([environment]::NewLine, ($body)) 
    $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)
}


$mycount =  (Get-Process -Name $myprocess).count

if($toomany -lt $mycount)
{
	$body = "We have $mycount $myprocess processes, something is unusual."
	if($waytoomany -lt $mycount)
        {
             IISRESET /STOP
             IISRESET /START
             $body = "We have $mycount $myprocess processes, IIS has been reset."
        }
        SendEmailReport
}

In this case we are sending an email notification to the fictional “support team” when more than 40 instances of the php-cgi process are running and in the event no one responds by the time 80 instances are hit the site is automatically bounced to ensure it’s availability.

The simple method for checking is the use of Task Scheduler and call up the script every 5 minutes, pretty simple yet effective.

, , , ,
May 11, 2013 at 7:30 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)

« Older Posts