估计的Exchange Server 2007客户端访问许可证(CAL)的数量和类型的CAL要求给予您的组织的Exchange环境中作业特性(例如,对某些高级功能的用户启用)。这可能有助于理解您的组织的Exchange Server 2007功能的使用,也可以协助您的内部审计和验证中,您许可证。本报告不计算设备CAL,和您的组织的实际牌照责任可能因不同的授权计划提供单品,它已经授权的基础。无论本报告的结果,它是你的组织的责任,以确保它完全为这个产品的授权。
# Trap block
trap {
write-host "An error has occurred running the script:"
write-host $_
$Global:AdminSessionADSettings.DefaultScope = $OriginalDefaultScope
exit
}
# Function that returns true if the incoming argument is a help request
function IsHelpRequest
{
param($argument)
return ($argument -eq "-?" -or $argument -eq "-help");
}
# Function that displays the help related to this script following
# the same format provided by get-help or <cmdletcall> -?
function Usage
{
@"
NAME:
`tReportExchangeCALs.ps1
SYNOPSIS:
`tReports Exchange client access licenses (CALs) of this organization in Enterprise or Standard categories.
SYNTAX:
`tReportExchangeCALs.ps1
PARAMETERS:
USAGE:
`t.\ReportExchangeCALs.ps1
"@
}
# Function that resets AdminSessionADSettings.DefaultScope to original value and exits the script
function Exit-Script
{
$Global:AdminSessionADSettings.DefaultScope = $OriginalDefaultScope
exit
}
########################
## Script starts here ##
########################
# Check for Usage Statement Request
$args | foreach { if (IsHelpRequest $_) { Usage; Exit-Script; } }
# Introduction message
write-host "Report Exchange client access licenses (CALs) in use in the organization"
write-host "It will take some time if there are a large amount of users......"
write-host ""
# Report all recipients in the org.
$OriginalDefaultScope = $Global:AdminSessionADSettings.DefaultScope
$Global:AdminSessionADSettings.DefaultScope = $Null
$TotalMailboxes = 0
$TotalEnterpriseCALs = 0
$UMUserCount = 0
$ManagedCustomFolderUserCount = 0
$AdvancedActiveSyncUserCount = 0
$AdvancedAntispamUserCount = 0
$AdvancedAntispamEnabled = $False
$OrgWideJournalingEnabled = $False
$AllMailboxIDs = @{}
$EnterpriseCALMailboxIDs = @{}
$JournalingUserCount = 0
$JournalingMailboxIDs = @{}
$JournalingDGMailboxMemberIDs = @{}
$TotalStandardCALs = 0
$VisitedGroups = @{}
$DGStack = new-object System.Collections.Stack
# Bool variable for outputing progress information running this script.
$EnableProgressOutput = $True
if ($EnableProgressOutput -eq $True) {
write-host "Progress:"
}
################
## Debug code ##
################
# Bool variable for output hash table information for debugging purpose.
$EnableOutputCounts = $False
function Output-Counts
{
if ($EnableOutputCounts -eq $False) {
return
}
write-host "Hash Table Name Count"
write-host "--------------- -----"
write-host "AllMailboxIDs: " $AllMailboxIDs.Count
write-host "EnterpriseCALMailboxIDs: " $EnterpriseCALMailboxIDs.Count
write-host "JournalingMailboxIDs: " $JournalingMailboxIDs.Count
write-host "JournalingDGMailboxMemberIDs: " $JournalingDGMailboxMemberIDs.Count
write-host "VisitedGroups: " $VisitedGroups.Count
write-host ""
write-host ""
}
function Merge-Hashtables
{
$Table1 = $args[0]
$Table2 = $args[1]
$Result = @{}
if ($null -ne $Table1)
{
$Result += $Table1
}
if ($null -ne $Table2)
{
foreach ($entry in $Table2.GetEnumerator())
{
$Result[$entry.Key] = $entry.Value
}
}
$Result
}
# Function that outputs Exchange CALs in the organization
function Output-Report {
write-host "========================="
write-host "Exchange CAL Usage Report"
write-host "========================="
write-host ""
write-host "Total Users: $TotalMailboxes"
write-host "Total Standard CALs: $TotalStandardCALs"
write-host "Total Enterprise CALs: $TotalEnterpriseCALs"
}
#################
## Total Users ##
#################
# Note!!!
# Only user, shared and linked mailboxes are counted.
# Resource mailboxes and legacy mailboxes are NOT counted.
Get-Mailbox -ResultSize 'Unlimited' -Filter { (RecipientTypeDetails -eq 'UserMailbox') -or
(RecipientTypeDetails -eq 'SharedMailbox') -or
(RecipientTypeDetails -eq 'LinkedMailbox') } | foreach {
$Mailbox = $_
$AllMailboxIDs[$Mailbox.Identity] = $null
$Script:TotalMailboxes++
}
if ($TotalMailboxes -eq 0) {
# No mailboxes in the org. Just output the report and exit
Output-Report
Exit-Script
}
#########################
## Total Standard CALs ##
#########################
# All users are counted as Standard CALs
$TotalStandardCALs = $TotalMailboxes
## Progress output ......
if ($EnableProgressOutput -eq $True) {
write-host "Total Standard CALs calculated: $TotalStandardCALs"
}
#############################
## Per-org Enterprise CALs ##
#############################
# If advanced anti-spam is turned on, all mailboxes are counted as Enterprise CALs
Get-TransportServer | foreach {
# If advanced anti-spam is turned on any Hub/Edge server, all mailboxes in the org are counted as Exchange CALs
$AntispamUpdates = Get-AntispamUpdates $_
if (($AntispamUpdates.SpamSignatureUpdatesEnabled -eq $True) -or
($AntispamUpdates.IPReputationUpdatesEnabled -eq $True) -or
($AntispamUpdates.UpdateMode -eq "Automatic")) {
$Script:AdvancedAntispamEnabled = $True
$Script:AdvancedAntispamUserCount = $Script:TotalMailboxes
$Script:TotalEnterpriseCALs = $Script:TotalMailboxes
## Progress output ......
if ($EnableProgressOutput -eq $True) {
write-host "Advanced Anti-spam Enabled: True"
write-host "Total Enterprise CALs calculated: $TotalEnterpriseCALs"
write-host ""
}
# All mailboxes are counted as Enterprise CALs, report and exit.
Output-Counts
Output-Report
Exit-Script
}
}
## Progress output ......
if ($EnableProgressOutput -eq $True) {
write-host "Advanced Anti-spam Enabled: False"
}
##############################
## Per-user Enterprise CALs ##
##############################
#
# Calculate Enterprise CAL users using UM, MRM Managed Custom Folder, and advanced ActiveSync policy settings
#
$AllMailboxIDs.Keys | foreach {
$Mailbox = Get-Mailbox $_
# UM usage classifies the user as an Enterprise CAL
if ($Mailbox.UMEnabled)
{
$Script:UMUserCount++
$Script:EnterpriseCALMailboxIDs[$Mailbox.Identity] = $null
}
# MRM Managed Custom Folder usage classifies the user as an Enterprise CAL
if ($Mailbox.ManagedFolderMailboxPolicy -ne $null)
{
$ManagedFolderLinks = (Get-ManagedFolderMailboxPolicy $Mailbox.ManagedFolderMailboxPolicy).ManagedFolderLinks
foreach ($FolderLink in $ManagedFolderLinks) {
$ManagedFolder = Get-ManagedFolder $FolderLink
# Managed Custom Folders require an Enterprise CAL
If ($ManagedFolder.FolderType -eq "ManagedCustomFolder")
{
$Script:ManagedCustomFolderUserCount++
$Script:EnterpriseCALMailboxIDs[$Mailbox.Identity] = $null
break
}
}
}
# Advanced ActiveSync policies classify the user as an Enterprise CAL
$Mailbox = Get-CASMailbox $_
if ($Mailbox.ActiveSyncEnabled -and ($Mailbox.ActiveSyncMailboxPolicy -ne $null))
{
$ASPolicy = Get-ActiveSyncMailboxPolicy $CASMailbox.ActiveSyncMailboxPolicy
if (($ASPolicy.AllowDesktopSync -eq $False) -or
($ASPolicy.AllowStorageCard -eq $False) -or
($ASPolicy.AllowCamera -eq $False) -or
($ASPolicy.AllowTextMessaging -eq $False) -or
($ASPolicy.AllowWiFi -eq $False) -or
($ASPolicy.AllowBluetooth -eq "Disable") -or
($ASPolicy.AllowIrDA -eq $False) -or
($ASPolicy.AllowInternetSharing -eq $True) -or
($ASPolicy.AllowRemoteDesktop -eq $True) -or
($ASPolicy.AllowPOPIMAPEmail -eq $False) -or
($ASPolicy.AllowConsumerEmail -eq $True) -or
($ASPolicy.AllowBrowser -eq $True) -or
($ASPolicy.AllowUnsignedApplications -eq $True) -or
($ASPolicy.AllowUnsignedInstallationPackages -eq $True) -or
($ASPolicy.ApprovedApplicationList -ne $null) -or
($ASPolicy.UnapprovedInROMApplicationList -ne $null)) {
$Script:AdvancedActiveSyncUserCount++
$Script:EnterpriseCALMailboxIDs[$Mailbox.Identity] = $null
}
}
}
## Progress output ......
if ($EnableProgressOutput -eq $True) {
write-host "Unified Messaging Users calculated: $UMUserCount"
write-host "Managed Custom Folder Users calculated: $ManagedCustomFolderUserCount"
write-host "Advanced ActiveSync Policy Users calculated: $AdvancedActiveSyncUserCount"
}
#
# Calculate Enterprise CAL users using Journaling
#
# Help function for function Get-JournalingGroupMailboxMember to traverse members of a DG/DDG/group
function Traverse-GroupMember
{
$GroupMember = $args[0]
if( $GroupMember -eq $null )
{
return
}
# Note!!!
# Only user, shared and linked mailboxes are counted.
# Resource mailboxes and legacy mailboxes are NOT counted.
if ( ($GroupMember.RecipientTypeDetails -eq 'UserMailbox') -or
($GroupMember.RecipientTypeDetails -eq 'SharedMailbox') -or
($GroupMember.RecipientTypeDetails -eq 'LinkedMailbox') ) {
# Journal one mailbox
$Script:JournalingMailboxIDs[$GroupMember.Identity] = $null
} elseif ( ($GroupMember.RecipientType -eq "Group") -or ($GroupMember.RecipientType -like "Dynamic*Group") -or ($GroupMember.RecipientType -like "Mail*Group") ) {
# Push this DG/DDG/group into the stack.
$DGStack.Push(@($GroupMember.Identity, $GroupMember.RecipientType))
}
}
# Function that returns all mailbox members including duplicates recursively from a DG/DDG
function Get-JournalingGroupMailboxMember
{
# Skip this DG/DDG if it was already enumerated.
if ( $Script:VisitedGroups.ContainsKey($args[0]) ) {
return
}
$DGStack.Push(@($args[0],$args[1]))
while ( $DGStack.Count -ne 0 ) {
$StackElement = $DGStack.Pop()
$GroupIdentity = $StackElement[0]
$GroupRecipientType = $StackElement[1]
if ( $Script:VisitedGroups.ContainsKey($GroupIdentity) ) {
# Skip this this DG/DDG if it was already enumerated.
continue
}
# Check the members of the current DG/DDG/group in the stack.
if ( ($GroupRecipientType -like "Mail*Group") -or ($GroupRecipientType -eq "Group" ) ) {
$varGroup = Get-Group $GroupIdentity -ErrorAction SilentlyContinue
if ( $varGroup -eq $Null )
{
$errorMessage = "Invalid group/distribution group/dynamic distribution group: " + $GroupIdentity
write-error $errorMessage
return
}
$varGroup.members | foreach {
# Count users and groups which could be mailboxes.
$varGroupMember = Get-User $_ -ErrorAction SilentlyContinue
if ( $varGroupMember -eq $Null ) {
$varGroupMember = Get-Group $_ -ErrorAction SilentlyContinue
}
if ( $varGroupMember -ne $Null ) {
Traverse-GroupMember $varGroupMember
}
}
} else {
# The current stack element is a DDG.
$varGroup = Get-DynamicDistributionGroup $GroupIdentity -ErrorAction SilentlyContinue
if ( $varGroup -eq $Null )
{
$errorMessage = "Invalid group/distribution group/dynamic distribution group: " + $GroupIdentity
write-error $errorMessage
return
}
Get-Recipient -RecipientPreviewFilter $varGroup.LdapRecipientFilter -OrganizationalUnit $varGroup.RecipientContainer -ResultSize 'Unlimited' | foreach {
Traverse-GroupMember $_
}
}
# Mark this DG/DDG as visited as it's enumerated.
$Script:VisitedGroups[$GroupIdentity] = $null
}
}
# Check all journaling mailboxes for all journaling rules.
foreach ($JournalRule in Get-JournalRule){
# There are journal rules in the org.
if ( $JournalRule.Recipient -eq $Null ) {
# One journaling rule journals the whole org (all mailboxes)
$OrgWideJournalingEnabled = $True
$Script:JournalingUserCount = $Script:TotalMailboxes
$Script:TotalEnterpriseCALs = $Script:TotalMailboxes
break
} else {
$JournalRecipient = Get-Recipient $JournalRule.Recipient.Local -ErrorAction SilentlyContinue
if ( $JournalRecipient -ne $Null ) {
# Note!!!
# Only user, shared and linked mailboxes are counted.
# Resource mailboxes and legacy mailboxes are NOT counted.
if ( ($JournalRecipient.RecipientTypeDetails -eq 'UserMailbox') -or
($JournalRecipient.RecipientTypeDetails -eq 'SharedMailbox') -or
($JournalRecipient.RecipientTypeDetails -eq 'LinkedMailbox') ) {
# Journal a mailbox
$Script:JournalingMailboxIDs[$JournalRecipient.Identity] = $null
} elseif ( ($JournalRecipient.RecipientType -like "Mail*Group") -or ($JournalRecipient.RecipientType -like "Dynamic*Group") ) {
# Journal a DG or DDG.
# Get all mailbox members for the current journal DG/DDG and add to $JournalingDGMailboxMemberIDs
Get-JournalingGroupMailboxMember $JournalRecipient.Identity $JournalRecipient.RecipientType
Output-Counts
}
}
}
}
if ( !$OrgWideJournalingEnabled ) {
# No journaling rules journaling the entire org.
# Get all journaling mailboxes
$Script:JournalingMailboxIDs = Merge-Hashtables $Script:JournalingDGMailboxMemberIDs $Script:JournalingMailboxIDs
$Script:JournalingUserCount = $Script:JournalingMailboxIDs.Count
}
## Progress output ......
if ($EnableProgressOutput -eq $True) {
write-host "Journaling Users calculated: $JournalingUserCount"
}
#
# Calculate Enterprise CALs
#
if ( !$OrgWideJournalingEnabled ) {
# Calculate Enterprise CALs as not all mailboxes are Enterprise CALs
$Script:EnterpriseCALMailboxIDs = Merge-Hashtables $Script:JournalingMailboxIDs $Script:EnterpriseCALMailboxIDs
$Script:TotalEnterpriseCALs = $Script:EnterpriseCALMailboxIDs.Count
}
## Progress output ......
if ($EnableProgressOutput -eq $True) {
write-host "Total Enterprise CALs calculated: $TotalEnterpriseCALs"
write-host ""
}
###################
## Output Report ##
###################
Output-Counts
Output-Report
$Global:AdminSessionADSettings.DefaultScope = $OriginalDefaultScope
################################
## Sample Exchange CAL Report ##
################################
#[PS] D:\>.\ReportExchangeCALs.ps1
#Report Exchange client access licenses (CALs) in use in the organization
#It will take some time if there are a large amount of users......
#
#Progress:
#Total Standard CALs calculated: 10000
#Advanced Anti-spam Enabled: False
#Unified Messaging Users calculated: 2000
#Managed Custom Folder Users calculated: 1000
#Advanced ActiveSync Policy Users calculated: 200
#Journaling Users calculated: 500
#Total Enterprise CALs calculated: 2200
#
#=========================
#Exchange CAL Usage Report
#=========================
#
#Total Users: 10000
#Total Standard CALs: 10000
#Total Enterprise CALs: 2200