Wednesday, October 14, 2015

Checking OWA.. Is Mine Hacked?

We've recently had a number of our staff ask about the recent OWA hack. To appease their fears, I went through and checked my OWA boxes to make sure that the OWAAuth.DLL hadn't been replaced or re-registered using a hacked version.

$servers = @("OWAServer1","OWAServer2") # get-ExchangeServer
$sbFileVersion = {
$FilePath = "C:\Program Files\Exchange\ClientAccess\Owa\auth\OWAAuth.dll"
Get-ChildItem $FilePath |  Select-Object Name,length,@{Name="Version";Expression={$_.versionInfo.FileVersion}},LastWriteTime
Invoke-Command -ScriptBlock $sbFileVersion -ComputerName $servers # | group fileversion
$SBRegistry = {
 #Return installed folder path for OWAAuth.DLL
 $RegKey = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components\66A06D0DD155D354CB4C311E0ED2EE9D"
 (Get-ItemProperty $regkey).$regvalue
Invoke-Command -ScriptBlock $SBRegistry -ComputerName $servers 
This does two things. First off, it checks the install path of the OWAAuth.DLL and returns the version and size of the file. I skimmed these to look for differences in the installed files.

Second step, it checks the registry on these same servers and looks to see what's registered. I visually checked to see if the path in the registry matches the install path. (Those registry values I found by searching one of my OWA box registries for the filename.)

My server running Exchange 2010 SP3 RU10 returned:

Name               : OWAAuth.dll
Length             : 104632
Version            : 14.03.0248.002
LastWriteTime      : 5/27/2015 1:47:42 PM
PSComputerName     : OWASERVER1

C:\Program Files\Exchange\ClientAccess\Owa\auth\OWAAuth.dll

Monday, May 11, 2015

Powershell IsNumeric

For the longest time, I've been using a visual basic trick to determine if a variable is a numeric value.

function isNumeric([string] $a) {
    $b = ([Microsoft.VisualBasic.Information]::isnumeric($a))
    return $b

IsNumeric "12"

IsNumeric "Bob"

Looking at the code, all I am doing is type-casting the variable as a integer and seeing if I get an error. So I could simply.

Try {
      [Int]$Variable -is [Int]
} Catch {

So, now in my code instead of evaluating the variable if it's a numeric value, then run through a "if numeric then ____ else _____". Now I simply encapsulate my THEN _ ELSE _  portions into my Try _ Catch _.

try {
 $DaysInt = [int]$daysBack
 $EndDate = Get-Date
 $StartDate = $EndDate.AddDays(-1 * $DaysBack).ToShortDateString()
} catch {
 $StartDate = Get-Date $daysback -Format g -ErrorAction silentlycontinue
  if ($StartDate -ne $null) {
  Write-Host "you entered a date" $StartDate 
  $EndDatestr = (Read-Host "Specify an End Date (enter for today)").trim()
  if ($enddatestr -eq "") {
   $EndDate = Get-Date
  } else {
   $endDate = Get-Date $EndDateStr  

Tuesday, April 14, 2015

Categorize Mailbox Sizes In Buckets

We are starting to plan for our migration to Exchange 2013. As part of the process we are running through the calculator. One of the steps for this report are to categorize your users.  "10% 100mb mailboxes, 50% 500mb users, etc.. " Sure... I could simply create a series of IF THEN statements..

ForEach Mailbox in this group
IF MailboxSize <= 500mb THEN LT500++
IF MailboxSize > 500mb and Mailbox <= 1GB then GT500++

But where's the elegance in that? All I want is a count of mailbox sizes, I don't care to know what the actual sizes are for this report.

Then I was working on an issue and found this mount point space script. This gave me an idea.. I could use expressions to count for me.. So, the idea being, I would do a simple query "Get-MailboxStatistics" and feed it to a formula to do all the math for me.

#Initialize the HASHTABLE.
$hash = @{}
$hash["lt 500mb"]=0
$hash["500 to 1gb"]=0
$hash["le 5gb"]=0
$hash["gt 5gb"]=0

#Create the expressions to do the counting..
$FiveH = @{Expression={$msize=[math]::Round($_.sizebytes /1mb);if ($msize -le 512) {$hash["lt 500mb"]++}}}
$Thou = @{Expression={$msize=[math]::Round($_.sizebytes /1mb);if ($msize -ge 513 -and $msize -lt 1024) {$hash["500 to 1gb"]++}}}
$TwoThou = @{Expression={$msize=[math]::Round($_.sizebytes /1mb);if ($msize -ge 1024 -and $msize -lt 5120) {$hash["le 5gb"]++}}}
$FiveThou = @{Expression={$msize=[math]::Round($_.sizebytes /1mb);if ($msize -gt 5120 ) {$hash["gt 5gb"]++}}}

#Now for the heavy lifting, We run all the mailboxes through the calculations..
Get-mailboxDatabase NWSALES-* | Get-mailboxstatistics | select @{name="SizeBytes";Expression={$_.totalitemsize.value.tobytes()}} | select $FiveH,$thou,$TwoThou,$FiveThou 
When it runs, it doesn't display anything useful. Simply a bunch of blank lines. I suppose I could have it echo the current value at the end, but that isn't necessary. After about 30seconds to a minute, I get back to the PS prompt. The final result of the script is the Hash Table $HASH.

Name Value 
---- ----- 
gt 5gb 223 
le 5gb 1169 
500 to 1gb 670 
lt 500mb 2109 

 With this, I can see half this population is under 500mb, but with another fairly large population between the 1gb to 5gb size.

Tuesday, February 3, 2015


This document will be updated regularly. I use it when checking determining Outlook versions of clients connecting to my environment. Version is that reported in the Exchange 2010 RCA logs for an Outlook client. Later versions I started including the KB article #. OutlookVersionMap.CSV
5.0.3165.0Outlook 2000
6.0.7654.12Outlook 2000
6.0.8153.0Outlook 2000
6.0.8165.0Outlook 2000
6.0.8211.0Outlook 2000
6.0.8244.0Outlook 2000 2002 2002 SP2 2002 SP3 2002 SP3
11.0.5604.0Outlook 2003 RTM
11.0.6352.0Outlook 2003 SP1
11.0.6555.0Outlook 2003 SP2
11.0.8000.0Outlook 2003 SP2
11.0.8161.0Outlook 2003 SP3
11.0.8200.0Outlook 2003 SP3
11.0.8303.0Outlook 2003 SP3 968688
12.0.4518.1014Outlook 2007 RTM
12.0.6024.5000Outlook 2007 RU1
12.0.6211.1000Outlook 2007 SP1
12.0.6212.1000Outlook 2007 SP1
12.0.6300.5000Outlook 2007 SP1
12.0.6315.5000Outlook 2007 SP1
12.0.6423.1000Outlook 2007 SP2
12.0.6504.5001Outlook 2007 SP2
12.0.6509.5000Outlook 2007 SP2
12.0.6529.5000Outlook 2007 SP2
12.0.6539.5000Outlook 2007 SP2
12.0.6550.5000Outlook 2007 SP2
12.0.6554.5000Outlook 2007 SP2
12.0.6557.5000Outlook 2007 SP2
12.0.6562.5003Outlook 2007 SP2
12.0.6606.1000Outlook 2007 SP3
12.0.6607.1000Outlook 2007 SP3
12.0.6661.5000Outlook 2007 SP3 2596598
12.0.6665.5000Outlook 2007 SP3 2687336
12.0.6670.5004Outlook 2007 SP3 2596802
12.0.6672.5000Outlook 2007 SP3 2768023
14.0.4734.1000Outlook 2010 RC/Beta
14.0.4760.1000Outlook 2010 RTM
14.0.4763.1000Outlook 2010 RTM
14.0.6029.1000Outlook 2010 SP1
14.0.6025.1000Outlook 2010 SP1
14.0.6109.5000Outlook 2010 SP1
14.0.6117.5001Outlook 2010 SP1
14.0.6126.5000Outlook 2010 SP1 2687351
14.0.6131.5002Outlook 2010 SP12597090
14.0.7010.1000Outlook 2010 SP1 2817371
14.0.7104.5000Outlook 2010 SP1 2817574
14.0.7106.5003Outlook 2010 SP2
14.0.7108.5000Outlook 2010 SP2 2849973
14.0.7113.5005Outlook 2010 SP2 2687567
14.0.7140.5001Outlook 2010 SP2 28782641/8/2015
14.0.7143.5000Outlook 2010 SP2 ###02/10/2015
15.0.4128.1019Outlook 2013 Preview
15.0.4420.1017Outlook 2013 2817619
15.0.4517.1003Outlook 2013 2817468
15.0.4517.1504Outlook 2013 2727096
15.0.4535.1001Outlook 2013 2817627
15.0.4551.1004Outlook 2013 2825677
15.0.4551.1505Outlook 2013 2837626
15.0.4569.1503Outlook 2013 SP1
15.0.4569.1508Outlook 2013 SP1 2863911
15.0.4615.1000Outlook 2013 SP1 2880470
15.0.4659.1000Outlook 2013 SP1 2889951
15.0.4667.1000Outlook 2013 SP1 289950411/11/2014
15.0.4675.1000Outlook 2013 SP1 292073412/9/2014
15.0.4675.1003Outlook 2013 SP1 291092312/17/2014
15.0.4693.1000Outlook 2013 SP1 292079802/10/2015
15.0.4701.1000Outlook 2013 SP1 295617003/10/2015

15.0.4711.1000 Outlook 2013 SP1 4/16/2015
14.0.7147.5001  Outlook 2010 SP2 4/16/2015
16.0.3930.1008  Outlook 2016 Preview 5/6/2015

Thursday, December 4, 2014

Exchange 2010 MoveRequest Fails Due to Access Rights

Over the weekend, one of my co-workers was attempting to do a database compression on a series of databases. As part of the process, he wanted to move a large number of these mailboxes off to another, empty DB. Today, when checking the status, I noticed that his mailbox moves had all failed.

Get-MoveRequest "UserMailbox" | Get-MoveRequestStatistics | fl message

Message : Error: Active Directory operation failed on LocalDomainController. This error is not retriable. Additional information: Insufficient access rights to perform the operation.
          Active directory response: 00002098: SecErr: DSID-3150BB9, problem 4003 (INSUFF_ACCESS_RIGHTS), data 0 --> The user has insufficient access rights.

I found a TON of blogs that stated the AD portion of the mailbox doesn't have the 'include inheritable permissions from this object's parent' checkbox selected. This was NOT the issue in my case. I tested this by taking the very same object and simply resuming the move. It completed normally.

Now the difference between myself and my co-worker? He has Domain Admin rights to the Exchange domain. As a DA, he has specific Deny set on the databases.

get-mailboxdatabase New-DB01 | get-adpermission | ?{$_.user -like "*domain admin*" -and $_.deny} | select extendedrights


If I had to guess, he can't read/write from the databases this way. Moral, don't have domain admins moving mailboxes.

Tuesday, November 25, 2014

Quickly Find Mailboxes on Retention Hold.

I am looking to implement a process to exclude/include mailboxes from mailbox retention policies. Doing some research, I found you can use the FILTER option on the get-mailbox for LIT holds, but not a retention hold. Then I stumbled upon this post where they broke down the AD property "msExchELCMailboxFlags". Now using the native AD module command, I can quickly search my entire environment for mailboxes where "Expiration Suspended".

get-aduser -Properties msExchELCMailboxFlags -filter {msExchELCMailboxFlags -band 1}

Wednesday, October 22, 2014

DIMM Report on Servers

I needed to verify that all my mailbox servers all had 80GB ram installed. I was tempted to simply remote to each server and review the BGInfo. After a quick search, I found this post by Jesse Hamrick on Powershell Pro! The script breaks down the WMI class to return the # of DIMM slots, which have chips installed and the capacity of each. This was great, but the results echo'd directly back to the screen.

From this, I tweaked the script to make it a bit more useful for my project. I added a command-line variable so that I don't have to type in each name via a read-host. Then I converted the output format so that I would return a variable instead.

   Report-RAM.PS1 - Returns a DIMM report on the remote server. 
   Uses WMI to read PhysicalMemory and PhysicalMemoryArray varia
   Name of the remote maching you want to query. Use "." for local PC. 

 .\Report-Ram.ps1 -ComputerName "."


ComputerName        : .
AvailableDIMMCount  : 4
Slots               : {@{Capacity=4294967296; DeviceLocator=DIMM3}, @{Capacity=4294967296; DeviceLocator=DIMM1}}
InstalledCapacity   : 8589934592
InstalledCapacityGB : 8
InstalledDIMMCount  : 2



$RAMSummary = New-Object -TypeName PSObject 
$RAMSummary | Add-Member -Name "ComputerName" -MemberType NoteProperty -Value $ComputerName 
$colSlots = Get-WmiObject -Class "win32_PhysicalMemoryArray" -namespace "root\CIMV2" -computerName $ComputerName
$colRAM = Get-WmiObject -Class "win32_PhysicalMemory" -namespace "root\CIMV2" -computerName $ComputerName
$TotalSlots = 0
Foreach ($objSlot In $colSlots){
     #"Total Number of DIMM Slots: " + $objSlot.MemoryDevices
  $TotalSlots += $objSlot.MemoryDevices
 $RAMSummary | Add-Member -Name "AvailableDIMMCount" -MemberType NoteProperty -Value $TotalSlots
 $InstalledRam = 0
 $ChipCount = 0
 $Slots = @()
 Foreach ($objRAM In $colRAM) {  
  $Slot = New-Object -TypeName PSObject 
 $Slot | Add-Member -Name "Capacity" -MemberType NoteProperty -Value $objRAM.Capacity 
 $Slot | Add-Member -Name "DeviceLocator" -MemberType NoteProperty -Value $objRAM.DeviceLocator
 $Slots += $Slot
     #"Memory Installed: " + $objRAM.DeviceLocator
  $InstalledRam += $objRAM.Capacity
  $ChipCount += 1
     #"Memory Size: " + ($objRAM.Capacity / 1GB) + " GB"
$RAMSummary | Add-Member -Name "Slots" -MemberType NoteProperty -Value $Slots
$RAMSummary | Add-Member -Name "InstalledCapacity" -MemberType NoteProperty -Value $InstalledRam
$RAMSummary | Add-Member -Name "InstalledCapacityGB" -MemberType NoteProperty -Value ($InstalledRam /1GB)
$RAMSummary | Add-Member -Name "InstalledDIMMCount" -MemberType NoteProperty -Value $ChipCount 

Return $RAMSummary

For my purposes, I ran it against all my mailbox servers. So I did something like:
$RamReport = get-mailboxserver | %{.\report-RAM.ps1 $_.Name}
$RamReport | ?{$_.InstalledCapacity -lt 80GB} | Select ComputerName,InstalledCapacityGB | export-CSV "c:\report.csv"