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

ExtendedRights
--------------
{Send-As}
{Receive-As}
{ms-Exch-EPI-Impersonation}
{ms-Exch-EPI-Token-Serialization}
{ms-Exch-Store-Constrained-Delegation}
{ms-Exch-Store-Transport-Access}
{ms-Exch-Store-Read-Access}
{ms-Exch-Store-Read-Write-Access}

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.

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

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

       

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


.REFERENCE
    http://www.powershellpro.com/dimm-witt/200/
#>


[CmdLetBinding()]
param(
 [parameter(Mandatory=$true)]
 [String]$ComputerName
)

$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"

Friday, October 10, 2014

Quickie: Number of Members in All Groups

I wanted a quick report on all the groups for a certain project and which had no members. This line will provide you a quick member count on all your DLs. Since it uses native Exchange 2010 cmdlets, it won't give you a recursive member count.


Get-DistributionGroup -resultsize unlimited | Select Displayname, @{Name="MemberCount";Expression={$x = Get-DistributionGroupMember -resultsize unlimited -Identity $_.identity;if($x -is [array]){$x.count} elseif ($x -eq $null) {0}else {1}}}

Friday, September 12, 2014

State Holidays & Importing Into Outlook.


The other day, I started planning out vacations for the next year and realized I didn't have updated holidays in my calendar. My office provides us the same days off as the State of California, so I used their rules when creating this script.
  • January 1* (New Year’s Day)
  • Third Monday in January (Martin Luther King Jr. Day)
  • Third Monday in February (President’s Birthday)
  • March 31 (Cesar Chavez Day)
  • Last Monday in May (Memorial Day)
  • July 4* (Independence Day)
  • First Monday in September (Labor Day)
  • November 11** (Veteran's Day)
  • Thanksgiving Day and Day After Thanksgiving
  • December 25* (Christmas Day)

 The following Monday shall be observed as the holiday for any holiday which falls on a Sunday.
 *Holiday credit is accrued if any of these dates occur on a Saturday.
**For Veteran’s Day, the preceding Friday shall be observed as the holiday on years when November 11 falls on a Saturday.

The intention of this script is to create a Holiday file, that can be imported into Outlook. The basic format of this file is:

[Category] (# of events in category)
Title,Date
Title,Date

or 

[Expense Reports] 4
Q1 Expense Reports Due, 2007/04/15
Q2 Expense Reports Due, 2007/11/15
Q3 Expense Reports Due, 2007/12/15
Q4 Expense Reports Due, 2007/01/15


Running this script will generate output the 11 State holidays for the year that you specify when running the script.



  
.\Report-StateHolidays.ps1 2020

I then feed that into a simple write-host that formats the data into the HOL file format.

  
.\Report-StateHolidays.ps1 2020 | %{write-host $_.holiday,$_.date -Separator ","}

New Years Day,2020/01/01
Martin Luther King Jr.,2020/01/20
President's Day,2020/02/17
Cesar Chavez Day,2020/03/31
Memorial Day,2020/05/25
Independence Day,2020/07/04
Labor Day,2020/09/07
Veteran's Day,2020/11/11
Thanksgiving,2020/11/26
Day After Thanksgiving,2020/11/27
Christmas Day,2020/12/25

Open Notepad, create header, then copy and paste the script info. Repeat for all years you want the data.

[State Holidays 2020] 11
New Years Day,2020/01/01
Martin Luther King Jr.,2020/01/20
President's Day,2020/02/17
Cesar Chavez Day,2020/03/31
Memorial Day,2020/05/25
Independence Day,2020/07/04
Labor Day,2020/09/07
Veteran's Day,2020/11/11
Thanksgiving,2020/11/26
Day After Thanksgiving,2020/11/27
Christmas Day,2020/12/25


Then save the file, replacing .TXT with .HOL, like StateHolidays2020.HOL
Double-click the file and click the categories you want to import.




  
<# .SYNOPSIS
Report-StateHolidays.PS1: Find State Holiday's for specific year .DESCRIPTION Will generate dates suitable for creating Holiday file to import into Outlook. .PARAMETER <YEAR> Year want to find holiday's on. .EXAMPLE .\Report-StateHolidays.ps1 2020 | %{write-host $_.holiday,$_.date -Separator ","} January 1* (New Year’s Day) Third Monday in January (Martin Luther King Jr. Day) Third Monday in February (President’s Birthday) March 31 (Cesar Chavez Day) Last Monday in May (Memorial Day) July 4* (Independence Day) First Monday in September (Labor Day) November 11** (Veteran's Day) Thanksgiving Day and Day After Thanksgiving December 25* (Christmas Day) The following Monday shall be observed as the holiday for any holiday which falls on a Sunday. *Holiday credit is accrued if any of these dates occur on a Saturday. **For Veteran’s Day, the preceding Friday shall be observed as the holiday on years when November 11 falls on a Saturday. #> [CmdLetBinding()] param( [parameter(Position=0,Mandatory=$true,ValueFromPipelineByPropertyName=$true)] [string]$year ) $Holidays=@() #$FindMonday will find offset to first monday in Month. $FindMonday["Sunday"] will return 1. $FindMonday = @{"Sunday"=1;"Monday"=0;"Tuesday"=6;"Wednesday"=5;"Thursday"=4;"Friday"=3;"Saturday"=2} #Same as FindMonday, but biased for Thursday. Only used for US-Thanksgiving date. $FindThursday = @{"Sunday"=4;"Monday"=3;"Tuesday"=2;"Wednesday"=1;"Thursday"=0;"Friday"=6;"Saturday"=5} $Holiday = "New Years Day" $date = "1/1/"+$year $newYear = Get-Date $($date) if ($newyear.DayOfWeek -eq "Sunday") {$newYear=$newyear.AddDays(1)} $UserObject = New-Object psobject $UserObject | Add-Member -MemberType noteproperty -Name "Holiday" -Value $Holiday $UserObject | Add-Member -MemberType noteproperty -Name "Date" -Value $newyear.ToString("yyyy/MM/dd") $Holidays += $UserObject $Holiday = "Martin Luther King Jr." #Find 3rd Monday in Month. First Monday + 2 weeks. $mlk = $newYear.AddDays(14+$FindMonday[$newYear.DayOfWeek.tostring()]) $UserObject = New-Object psobject $UserObject | Add-Member -MemberType noteproperty -Name "Holiday" -Value $Holiday $UserObject | Add-Member -MemberType noteproperty -Name "Date" -Value $MLK.ToString("yyyy/MM/dd") $Holidays += $UserObject $Holiday = "President's Day" $February = Get-Date $("2/1/"+$year) #Find 3rd Monday in Month. First Monday + 2 weeks. $Presidents = $February.AddDays(14+$FindMonday[$February.DayOfWeek.tostring()]) $UserObject = New-Object psobject $UserObject | Add-Member -MemberType noteproperty -Name "Holiday" -Value $Holiday $UserObject | Add-Member -MemberType noteproperty -Name "Date" -Value $Presidents.ToString("yyyy/MM/dd") $Holidays += $UserObject $Holiday = "Cesar Chavez Day" $date = "3/31/"+$year $CC = Get-Date $($date) if ($CC.DayOfWeek -eq "Sunday") {$CC = $CC.AddDays(1)} $UserObject = New-Object psobject $UserObject | Add-Member -MemberType noteproperty -Name "Holiday" -Value $Holiday $UserObject | Add-Member -MemberType noteproperty -Name "Date" -Value $CC.ToString("yyyy/MM/dd") $Holidays += $UserObject $Holiday = "Memorial Day" $May = Get-Date $("5/1/"+$year) #Last Monday of Month = 4th Monday of month. This may break down if $memorial = $May.AddDays(21+$FindMonday[$May.DayOfWeek.tostring()]) $UserObject = New-Object psobject $UserObject | Add-Member -MemberType noteproperty -Name "Holiday" -Value $Holiday $UserObject | Add-Member -MemberType noteproperty -Name "Date" -Value $memorial.ToString("yyyy/MM/dd") $Holidays += $UserObject $Holiday = "Independence Day" $date = "7/4/"+$year $ID = Get-Date $($date) #if ($ID.DayOfWeek -eq "Saturday") {Write-Host "HC"} if ($ID.DayOfWeek -eq "Sunday") {$ID = $ID.AddDays(1)} #Write-Host $Holiday,"`t",$ID.tolongdateString() $UserObject = New-Object psobject $UserObject | Add-Member -MemberType noteproperty -Name "Holiday" -Value $Holiday $UserObject | Add-Member -MemberType noteproperty -Name "Date" -Value $ID.ToString("yyyy/MM/dd") $Holidays += $UserObject $Holiday = "Labor Day" $Sept = Get-Date $("9/1/"+$year) $ld = $Sept.AddDays($FindMonday[$Sept.DayOfWeek.tostring()]) $UserObject = New-Object psobject $UserObject | Add-Member -MemberType noteproperty -Name "Holiday" -Value $Holiday $UserObject | Add-Member -MemberType noteproperty -Name "Date" -Value $LD.ToString("yyyy/MM/dd") $Holidays += $UserObject $Holiday = "Vetern's Day" # Always celebrate holiday, Saturday falls to Friday, Sunday goes to Monday. $date = "11/11/"+$year $vets = Get-Date $($date) if ($vets.DayOfWeek -eq "Saturday") {$vets = $vets.AddDays(-1)} if ($vets.DayOfWeek -eq "Sunday") {$vets = $vets.AddDays(1)} $UserObject = New-Object psobject $UserObject | Add-Member -MemberType noteproperty -Name "Holiday" -Value $Holiday $UserObject | Add-Member -MemberType noteproperty -Name "Date" -Value $VETS.ToString("yyyy/MM/dd") $Holidays += $UserObject $Holiday = "Thanksgiving" $nov = Get-Date $("11/1/"+$year) #Find 4th Thursday in November. $Thanks = $nov.AddDays(21+$FindThursday[$nov.DayOfWeek.tostring()]) $UserObject = New-Object psobject $UserObject | Add-Member -MemberType noteproperty -Name "Holiday" -Value $Holiday $UserObject | Add-Member -MemberType noteproperty -Name "Date" -Value $Thanks.ToString("yyyy/MM/dd") $Holidays += $UserObject $Holiday = "Day After Thanksgiving" #No working on Black Friday. $BF = $Thanks.AddDays(1) $UserObject = New-Object psobject $UserObject | Add-Member -MemberType noteproperty -Name "Holiday" -Value $Holiday $UserObject | Add-Member -MemberType noteproperty -Name "Date" -Value $BF.ToString("yyyy/MM/dd") $Holidays += $UserObject $Holiday = "Christmas Day" $date = "12/25/"+$year $xmas = Get-Date $($date) if ($xmas.DayOfWeek -eq "Sunday") {$xmas = $xmas.AddDays(1)} $UserObject = New-Object psobject $UserObject | Add-Member -MemberType noteproperty -Name "Holiday" -Value $Holiday $UserObject | Add-Member -MemberType noteproperty -Name "Date" -Value $XMAS.ToString("yyyy/MM/dd") $Holidays += $UserObject Return $Holidays

Monday, August 4, 2014

Quickie Mailbox Statistics Report

So I support an o365 environment. One of the reports my manager asked me to run is to gather mailbox size information for our entire population. Sadly, due to RBAC controls, you can't get a numeric response back from a get-mailboxstatistics, only a string.

TotalItemSize
-------------------------
75.33 MB (78,991,829 bytes)

Using string manipulation, I am able to pull out the mailbox size.

"75.33 MB"

Then using Powershell's own Invoke-Expression, convert that down to a number (in bytes).

78989230.08

Now, I can use the Measure-Object command to give me statistics.

get-mailbox -resultsize 100 | select @{Name="Size";Expression={(Invoke-Expression ((get-mailboxStatistics -identity $_.identity).totalitemsize.tostring().split("(")[0].replace(" ",""))) / 1mb}} | measure-object -property size -min -max -average -sum

Count : 100
Average : 28.4768
Sum : 2847.68
Maximum : 500.8
Minimum : 0
Property : Size

Tuesday, July 22, 2014

CMDLET Bidnding Options

I just located this awesome post that details all the various options when doing cmdlet binding. For example, you can define your parameters to a script as they are inputting them. For example:
ValidateScript Validation Attribute
The ValidateScript attribute specifies a script that is used
to validate a parameter or variable value. Windows PowerShell
pipes the value to the script, and generates an error if the
script returns "false" or if the script throws an exception.
When you use the ValidateScript attribute, the value
that is being validated is mapped to the $_ variable. You can
use the $_ variable to refer to the value in the script.
In the following example, the value of the EventDate parameter
must be greater than or equal to the current date. 
Param
(
[parameter()]
[ValidateScript({$_ -ge (get-date)})]
[DateTime]
$EventDate
)
In the following example, the value of the variable $date must be
greater than or equal to the current date and time.
[DateTime][ValidateScript({$_ -ge (get-date)})]$date = (get-date)
I could see using this in a script where I absolutely want the identifier of a user mailbox. Using the ValidateScript I would do a simple Get-Mailbox and validate I get a legitimate answer.