Exchange CAS IIS Log Maintenance

One of the first things a young Exchange admin will learn is that there is no built in IIS log maintenance capability for clearing out old log files baked in to Exchange. Hopefully this lesson comes when SCOM starts throwing drive space alerts, and not when the C: drive fills up and craters the server :/. To mitigate this risk, most admins create a simple script for regularly performing IIS log maintenance by deleting CAS log files older than a certain date. Here are a couple of scripts you can use, depending on your environment.

Properly scheduled IIS log maintenance will help keep your C: drive clean (what was happening on 6/14!?)
15 items so the script is working as expected. Based on file size you may want to shorten the retention window to 7 days in your environment.

For info on how to create the Scheduled Task to run this script daily, look here

If you just have one CAS (or one server with the Client Access Service in the case of Exchange 2016), this one-liner will get the job done:

#################################################################
# Exchange CAS IIS Log Maintenance                              #
# This script deletes IIS log files older than 14 days from the #
# local Exchange CAS server					#
#								#
# Created by Eric Kukkuck 3/3/2015				#
#################################################################

Get-ChildItem –Path  “C:\inetpub\logs\LogFiles\W3SVC1” | Where-Object {$_.CreationTime –lt (Get-Date).AddDays(-14)} | Remove-Item

If you have multiple CASes and an Admin server, this script will pull a list of CAS servers from a text file and clear the old log files remotely. It will also remove log files from the first two additional log file directories, if you happen to have three directories in play (as I did at one point, the default website, a secondary website for basic auth ActiveSync devices, and a third website for the AV solution)

#################################################################
# Exchange CAS IIS Log Maintenance                              #
# This script deletes IIS log files older than 14 days from all #
# the Exchange CAS servers listed in the        		#
# D:\ABZ\Scripts\ExchangeCASServers.txt file			#
#								#
# Created by Eric Kukkuck 3/3/2015				#
#################################################################

$servers = get-content "D:\ABZ\Scripts\ExchangeCASServers.txt"

foreach ($server in $servers)
{
	Get-ChildItem –Path  “\\$server\c$\inetpub\logs\LogFiles\W3SVC1”,"\\$server\c$\inetpub\logs\LogFiles\W3SVC2","\\$server\c$\inetpub\logs\LogFiles\W3SVC3",“\\$server\c$\inetpub\logs\LogFiles\W3SVC4” | Where-Object {$_.CreationTime –lt (Get-Date).AddDays(-14)} | Remove-Item
}

-Eric

IIS Log Parser Script for Finding Two Items Per Line

Troubleshooting Exchange connectivity issues can often be a chore, especially if you’re trying to go line by line in the IIS logs. The close formatting in those text files makes for a blurry and mind-numbing experience, not to mention how easy it is to miss what you’re actually looking for. To make that experience easier (and to offload the hard work to PowerShell) I’ve put together the following IIS log parser script* that will allow you to search IIS logs remotely on multiple servers for lines that contain two different items and export that data to a CSV. For example, if your helpdesk reports to you that some people are unable to access their mailbox from their ActiveSync device, you may want to start your investigation by searching the IIS logs on multiple CASes for lines that contain both “ActiveSync” and “401”. This script will do just that!

*I’d like to give credit to who created the original script I edited to make this one, but I cannot seem to find it online 🙁

-Eric

#########################################################
# Search Multiple IIS Logs for Multiple Items Per Line 	#
# Created By Eric Kukkuck   04/16/2014			#
#########################################################

# Edit the variables below to meet your needs #

$Path = "\\SERVER1\c$\inetpub\logs\LogFiles\W3SVC1","\\SERVER2\c$\inetpub\logs\LogFiles\W3SVC1"
$PathArray = @()
$ResultsLog = "C:\Temp\IISSearchResults.csv"
$Variable1 = "ActiveSync"
$Variable2 = "401"

# The meat and potatoes #

if (Test-Path $ResultsLog -PathType Leaf) { 
write-host "Delete the current log file and try again " 
exit
}
# This code snippet gets all the files in $Path that end in “.log”.
Get-ChildItem $Path -Recurse -Filter “*.log” |
Where-Object { $_.Attributes -ne “Directory”} |
ForEach-Object {gc $_.FullName | % { if($_ -match "($Variable1.*$Variable2)") {
$_ | add-content -path $ResultsLog }
    }
}

O365, On-prem Exchange, and SPF Records

One of the first things you should update before integrating Exchange Online or EOP into your mail flow is your organization’s SPF record. If you plan to use EOP as your perimeter gateway you may think that the O365 IPs are all you need, and you can get away with an SPF record that looks something like this:

v=spf1 include:spf.protection.outlook.com -all

Unfortunately, that isn’t the case. When Exchange Online shuffles emails around internally between tenants, any message from your on-prem environment will still need to be validated against your SPF record. What’s even more interesting, is if you have mailboxes in Exchange Online, Exchange on-prem, and decide to put a perimeter gateway like Proofpoint in front of EOP, then you get to have three sets of IPS (Exchange Online’s, your on-prem environment’s, and your Proofpoint gateway’s) in your SPF record!

-Eric

Get CAS Active User Counts (Exchange 2010)

A lot of times when troubleshooting a potential Exchange CAS server issue or performing maintenance on an Exchange 2010 client access server, you’ll find you need to identify how many active connections exist on your CAS. The script found on Technet will pull your RPC, OWA, and EAS connections, but I prefer a more complete view so I’ve added Address Book, POP, IMAP, and EWS connections to fill out the script.

One thing to note about running this script – it creates a function to be called later, so you need to save it as a .ps1 file and execute it by entering “. .\filename.ps1” at the prompt. Once the function has been added to the shell you call it by entering “Get-CASActiveUsers -ComputerName casname“.

-Eric

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

  process {
    switch($PsCmdlet.ParameterSetName) {
      "Value" {$servers = $ComputerName}
      "Pipeline" {$servers = $Name}
    }
    $servers | %{
      $RPC = Get-Counter "\MSExchange RpcClientAccess\User Count" -ComputerName $_
      $OWA = Get-Counter "\MSExchange OWA\Current Unique Users" -ComputerName $_
      $AS = Get-Counter “\MSExchange ActiveSync\Current Requests” -ComputerName $_
      $AB = Get-Counter "\MSExchangeAB\NSPI Connections Current" -ComputerName $_
      $IMAP =  get-counter "\MSExchangeimap4(_Total)\current connections" -ComputerName $_
      $EWS = Get-Counter "\W3SVC_W3WP(*msexchangeservicesapppool)\Active Requests" -ComputerName $_
      New-Object PSObject -Property @{
        Server = $_
         "RPC Client Access" = $RPC.CounterSamples[0].CookedValue
         "Outlook Web App" = $OWA.CounterSamples[0].CookedValue
	 "ActiveSync" = $AS.CounterSamples[0].CookedValue
	 "Address Book" = $AB.CounterSamples[0].CookedValue
	 "IMAP" = $IMAP.CounterSamples[0].CookedValue
	 "EWS" = $EWS.CounterSamples[0].CookedValue
      } | Select-Object Server,"RPC Client Access","Outlook Web App",IMAP,ActiveSync,"Address Book",EWS | FT -Auto
    }
  }
}

Scheduled Tasks and the Exchange Management Shell (EMS)

As an Exchange admin, one of my favorite features of a Windows OS is the trusty Task Scheduler. This simple capability allows me to generate reports, perform cleanup tasks, and ensure things in my environment stay configured as I expect. The one issue that I sometimes run into however is how to have the Task Scheduler run a task within the Exchange Management Shell (EMS;, so to make it easy I’m going to share that info with you.

As you can see in the screenshot below, we’ve got all of our fields populated.

The Action field is self-explanatory, but the data in the other three goes as follows:

Program/script:

C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe

Add arguments (optional):

-version 2.0 -NonInteractive -WindowStyle Hidden -command “. ‘C:\Program Files\Microsoft\Exchange Server\V14\bin\RemoteExchange.ps1’; Connect-ExchangeServer -auto; .\ScriptName.ps1

Start in (optional):

D:\ABZ\Scheduled Tasks

-Eric

Admin Server\Jump Server

One of the things that makes life much, much easier on a team of Exchange admins is having a shared server that everyone can connect to and manage the Exchange environment. Every time I work in a new environment and see that they don’t have one setup I recommend it early in the process, and use the following points to help sell the idea:

  1. The Exchange tools only need to be installed once for all the admins to use.
  2. You can more easily control which version of the Exchange tools are used
  3. The Admin server can act as a repository for your Exchange ISOs, rollups, cumulative updates, admin tools, etc.
  4. Tasks that need to run on a reoccurring basis, such as CAS log cleanup scripts, can be setup and centrally managed on the Admin server
  5. All scripts used by the team can be consolidated, and documentation can be created to ensure the right script is used for the right purpose
  6. You can use an Admin server as your DAG File Share Witness (FSW)
  7. Working from an Admin server means you can keep people off the actual Exchange servers
  8. You can give the helpdesk access to the server and build your training and documentation around the single access point (and keep them off the Exchange servers)

-Eric