ExchangeMemoryLimits.ps1

<#PSScriptInfo
 
.VERSION 0.1
 
.GUID cd40f0ac-fdb5-48dd-8880-3b63a0818d40
 
.AUTHOR asherto
 
.COMPANYNAME
 
.COPYRIGHT
 
.TAGS PowerShell Exchange Memory Limits Limit Size msExchESEParamCacheSizeMin msExchESEParamCacheSizeMax
 
.LICENSEURI
 
.PROJECTURI https://github.com/asheroto/Microsoft-Exchange-Memory-Limits
 
.ICONURI
 
.EXTERNALMODULEDEPENDENCIES
 
.REQUIREDSCRIPTS
 
.EXTERNALSCRIPTDEPENDENCIES
 
.RELEASENOTES
[Version 0.1] - Initial Release
 
.PRIVATEDATA
 
#>


<#
.SYNOPSIS
    Modify Microsoft Exchange Database Cache Size Memory Limit
.DESCRIPTION
    Limits the amount of memory the Microsoft Exchange server can use for its cache.
     
Example Usage:
    Set-ExchangeMemoryLimits -MinSize 2GB -MaxSize 4GB
    Set-ExchangeMemoryLimits -ListValues
    Set-ExchangeMemoryLimits -Reset
.NOTES
    Created by : asheroto
    Version : 0.1
    Date Coded : 2/3/2021
    More info: : https://github.com/asheroto/Microsoft-Exchange-Memory-Limits
.EXAMPLE
    Set-ExchangeMemoryLimits -MinSize 2GB -MaxSize 4GB
 
    Set-ExchangeMemoryLimits -ListValues
 
    Set-ExchangeMemoryLimits -Reset
#>


# Parameters
[CmdletBinding(DefaultParameterSetName = "Setexec")]
param (
    [parameter(mandatory = $true, ParameterSetName = "Setexec", HelpMessage = "Enter the size in KB, MB or GB")][String]$MinSize,
    [parameter(mandatory = $true, ParameterSetName = "Setexec", HelpMessage = "Enter the size in KB, MB or GB")][String]$MaxSize,
    [parameter(mandatory = $false, ParameterSetName = "Resetexec")][switch]$Reset,
    [parameter(mandatory = $false, ParameterSetName = "List")][switch]$ListValues,
    [parameter(mandatory = $false, ParameterSetName = "Log")][switch]$Log
)

if ($Log.IsPresent) {
    $LogPath = "$($env:USERPROFILE)\Desktop\ADExchLog_$((Get-Date).ToString("yyyy-MM-dd"))_$([GUID]::newGuid().guid).txt"
}

# Functions
function underline-headline {
    param($headline, $ul = "-")
    $uline = $ul * ($headerline.Length)
    return "$headline`n$uline"
}
function get-parameters {
    param($scriptinfo)
    $ParameterList = (Get-Command -Name $scriptinfo).Parameters
    $myparameters = @()
    foreach ($key in $ParameterList.keys) {
        $myparam = Get-Variable -Name $key -ErrorAction SilentlyContinue;
        if ($myparam.value) { $myparameters += [pscustomobject]@{Parameter = "-$($myparam.name)"; Value = "$($myparam.value)" } }
    }
    return "`nParameters you specified: `n$($myparameters | Format-Table -AutoSize | out-string)"
}
function handle-ADmodule {
    if (get-module -listavailable -Name ActiveDirectory) {
        if (!(get-module -Name ActiveDirectory)) {
            import-module ActiveDirectory
        }
        return $true
    }
    return $false
}
function navigate-ad {
    param($name, $level)
    return get-childitem -path "AD:\$(($level | Where-Object {$_.name -eq $name}).DistinguishedName)" -Force
}
function get-schemainfo {
    param($levelzero)
    $SchemaVersionTable = @{
        "13"    = "Windows 2000 Schema" ;
        "30"    = "Windows 2003 Schema";
        "31"    = "Windows 2003 R2 Schema" ;
        "39"    = "Windows 2008 BETA Schema" ;
        "44"    = "Windows 2008 Schema" ;
        "47"    = "Windows 2008 R2 Schema" ;
        "S51"   = "Windows Server 8 Developer Preview Schema" ;
        "S52"   = "Windows Server 8 BETA Schema" ;
        "S6"    = "Windows Server 2012 Schema" ;
        "69"    = "Windows Server 2012 R2 Schema" ;
        "81"    = "Windows Server 2016 Technical Preview Schema" ;
        "87"    = "Windows Server 2016 Schema" ;
        "88"    = "Windows Server 2019 Schema" ;
        "4397"  = "Exchange 2000 RTM Schema" ;
        "4406"  = "Exchange 2000 SP3 Schema" ;
        "6870"  = "Exchange 2003 RTM Schema" ;
        "6936"  = "Exchange 2003 SP3 Schema" ;
        "10637" = "Exchange 2007 RTM Schema" ;
        "11116" = "Exchange 2007 RTM Schema" ;
        "14622" = "Exchange 2007 SP2 & Exchange 2010 RTM Schema" ;
        "14625" = "Exchange 2007 SP3 Schema" ;
        "14726" = "Exchange 2010 SP1 Schema" ;
        "14732" = "Exchange 2010 SP2 Schema" ;
        "14734" = "Exchange 2010 SP3 Schema" ;
        "15137" = "Exchange 2013 RTM Schema" ;
        "15254" = "Exchange 2013 CUL Schema" ;
        "15281" = "Exchange 2013 CU2 Schema" ;
        "15283" = "Exchange 2013 CU3 Schema" ;
        "15292" = "Exchange 2013 SP1/CU4 Schema" ;
        "15300" = "Exchange 2013 CUS Schema" ;
        "15303" = "Exchange 2013 CU6 Schema" ;
        "15312" = "Exchange 2013 CU7/CU8/CU9/CU10 and later Schema";
        "15317" = "Exchange 2016 RTM/Preview Schema";
        "15323" = "Exchange 2016 CU1 Schema";
        "15325" = "Exchange 2016 CU2 Schema";
        "15326" = "Exchange 2016 CU3-CU5 Schema";
        "15330" = "Exchange 2016 CU6 Schema";
        "15332" = "Exchange 2016 CU7-CU18 Schema";
        "15333" = "Exchange 2016 CU19 Schema";
        "17000" = "Exchange 2019 RTM/CU1 Schema";
        "17001" = "Exchange 2019 CU2-CU7 Schema";
        "17002" = "Exchange 2019 CU8 Schema";
    }

    $level2schema = ($levelzero | Where-Object { $_.name -eq "Schema" }).distinguishedname.toString()
    $schemaVersionId = (get-ADobject -Identity $level2schema -Properties "objectVersion").objectversion
    $adschema = $SchemaVersionTable[$schemaVersionId.tostring()]
    
    $level2vpath = (get-childitem -Path "AD:\$level2schema" | Where-Object { $_.name -eq "ms-Exch-Schema-Version-Pt" }).tostring()
    $exchangeversionID = (get-ADobject -Identity $level2vpath -Properties rangeUpper).rangeUpper
    $exchSchema = $SchemaVersionTable[$exchangeversionID.tostring()]
    
    $pagesize = "32KB"

    if ($exchSchema -like "*2007*") { $pagesize = "8KB" }
    return [pscustomobject]@{DetectedActiveDirectorySchema = $adschema; DetectedExchangeSchema = $exchSchema; SelectedDatabasePageSize = $pagesize }

}
function Get-ADValues {
    param ($identity, $pagesize)
    $mincurrentsize = (Get-ADObject -Identity $identity -Properties msExchESEParamCacheSizeMin).msExchESEParamCacheSizeMin
    $maxcurrentsize = (Get-ADObject -Identity $identity -Properties msExchESEParamCacheSizeMax).msExchESEParamCacheSizeMax

    $minsizeGB = $(($mincurrentsize) * $pagesize / 1GB)
    $maxsizeGB = $(($maxcurrentsize) * $pagesize / 1GB)

    $memsizevalues = [pscustomobject]@{"msExchESEParamCacheSizeMin" = $mincurrentsize; "Minimum size in GB" = $minsizeGB; "msExchESEParamCacheSizeMax" = $maxcurrentsize; "Maximum size in GB" = $maxsizeGB }

    return $memsizevalues
}
function parse-size {
    param ($size, $pagesize = 32KB)
    $sbsize = [scriptblock]::Create($size / $pagesize)
    $mynumber = invoke-command -ScriptBlock $sbsize
    iF ($mynumber % [int]$mynumber -gt 0) {
        Smynumber = $mynumber - 0.5
    }
    return [System.Math]::Round($mynumber, 0)
}

if ($Log.isPresent) {
    Start-Transcript -Path $logpath -Append
}

Clear-Host
Write-Host "$(underline-headline -headline 'Exchange Cache Memory Operations via Active Directory')`n" -ForegroundColor Green
Get-Parameters -scriptinfo $MyInvocation.InvocationName

if (!(handle-ADmodule)) { Write-Host "`n`nActiveDirectory module is not available. Exiting...`n" -ForegroundColor Yellow; exit }

$level0 = get-childitem -Path AD:\
$level1 = navigate-ad -level $level0 -name "Configuration"
$level2 = navigate-ad -level $level1 -name "Services"
$level3 = navigate-ad -level $level2 -name "Microsoft Exchange"
$level4 = navigate-ad -level $level3 -name $level3.name.ToString()
$level5 = navigate-ad -level $level4 -name "Administrative Groups"
$level6 = navigate-ad -level $level5 -name $level5.name.ToString()
$level7 = navigate-ad -level $level6 -name "Servers"
Write-Host "Please select one or more listed servers" -ForegroundColor Yellow
[array]$servernames = $level7.name | Out-GridView -Title "Select the Exchange Server(s) to process" -PassThru

$adinfo = get-schemainfo -levelzero $level0
$adinfo | Format-Table -AutoSize
foreach ($servername in $servernames) {
    $level8 = navigate-ad -level $level7 -name $servername
    $level9path = ($level8 | Where-Object { $_.name -eq "InformationStore" }).distinguishedname.tostring()

    if ($ListValues.IsPresent) {
        Get-ADValues -identity $level9path -pagesize $adinfo.selectedDatabasePageSize
    } elseif ($Reset.IsPresent) { 
        Set-ADObject -Identity $level9path -Clear "msExchESEParamCacheSizeMin", "msExchESEParamCacheSizeMax"
        Write-Host "`nMinimum and maximum have been reset to defaults, which is 'not set'" -ForegroundColor Green
        Get-ADValues -identity $level9path -pagesize $adinfo.selectedDatabasePageSize
    } else {
        [int]$minnewsize = parse-Size -Size $MinSize -pagesize $adinfo.SelectedDatabasePageSize
        [int]$maxnewsize = parse-size -Size $MaxSize -pagesize $adinfo.SelectedDatabasePageSize
        
        Set-ADObject -Identity $level9path -Replace @{msExchESEParamCacheSizeMin = $minnewsize; msExchESEParamCacheSizeMax = $maxnewsize }
        Write-Host "`nNew AD Attributes Values" -ForegroundColor Green
        Get-ADValues -Identity $level9path -pagesize $adinfo.selectedDatabasePageSize
    }
}

if (Get-Module -Name ActiveDirectory) { Remove-Module -Name ActiveDirectory }

if ($Log.isPresent) {
    Stop-Transcript
}