Introduction

Bon faut que je l'avoue, j'avais très très envi de tester ce nouveau langage de script ! Un doux mélange de shell Unix et de langage objet, le PShell prend le meilleur de ces deux mondes. Pour illustrer, les commandes renvoient des objets et le pipe peut passer ces objets d'une cmdlet (commande PShell) à une autre !!! Truc de ouf o_O
Le but de cet article n'est pas d'introduire PShell mais de donner des exemples de scripts intéressants pour une organisation Exchange 2010.

Mon besoin initial est d'avoir un état du DAG (cluster de base de données pour les mailbox) car celui ci continue de fonctionner même quand ça va pas et c'est problématique pour intervenir. J'ai donc écris un script qui m'envoi un tableau de bord, c'est du pur PowerShell avec un pti appel à du WMI...

J'ai fais ces scripts début 2010, sur un Exchange 2010 non SP1 et RollUp 4. Il est possible que depuis, il y ai de nouvelles cmdlets. Heureusement pour moi, depuis, je ne fais plus de M$ mouahahahaha.

Un rapport sur l'état de l'organisation

Le script suivant envoi un mail contenant plusieurs infos : Les files d'attentes sur les HUB, l'état des bases dans le DAG, l'état du file system sur chaque MBX.

# init
    . 'C:\Program Files\Microsoft\Exchange Server\V14\bin\RemoteExchange.ps1'
    Connect-ExchangeServer -auto
 
$mail_source = "<serveur_exchange>@<domaine>"
$mail_cible = "<admin_exchange>@<domaine>"
$mail_server = "<hub>.<domaine>"
 
$mail_subject = "Exchange - Daily report " + (get-date).ToShortdateString() + " " + (get-date).ToShortTimeString()
 
$mail_body = "<html><body>"
 
#
# Mail queue
#
    $mail_body += "<fieldset><legend>Exchange Mail Queue</legend>"
    $mail_body += "<table><tr><td><b>Queue</b></td><td><b>Target</b></td><td><b>Status</b></td><td><b>#Message</b></td></tr>"
    $list_queue = Get-TransportServer | Get-Queue | Where-Object { $_.messageCount -gt 0 } | sort -property $_.MessageCount | foreach {
        $queue_name = $_.identity
        $queue_target = $_.NextHopDomain
        $queue_status = $_.Status
        $queue_count = $_.MessageCount
 
        $mail_body += "<tr><td>$queue_name</td><td>$queue_target</td><td>$queue_status</td><td>$queue_count</td></tr>"
    }
    $mail_body += "</table></fieldset><br/>"
 
#
# Database Status
#
    $mail_body += "<fieldset><legend>Exchange Database Status</legend>"
    $mail_body += "<table><tr><td><b>Database</b></td><td><b>Active on</b></td><td><b>Status</b></td><td><b>#Mailbox</b></td></tr>"
    $list_database = get-mailboxdatabase | sort -property $_.name | foreach {
        $database_name = $_.name
        $mount_server = (Get-MailboxDatabaseCopyStatus -Identity $_.name -Active).ActiveDatabaseCopy
        $mount_status = (Get-MailboxDatabaseCopyStatus -Identity $_.name -Active).Status
        $count_mailbox = (Get-Mailbox -Database $_.name | Group-Object { $_.database }).count
 
        $mail_body += "<tr><td>$database_name</td><td>$mount_server</td><td>$mount_status</td><td>$count_mailbox</td></tr>"
    }
    $mail_body += "</table></fieldset><br/>"
 
#
# LUN Usage
#
    Get-MailboxServer | foreach {
        $server_name = $_.identity
        $mail_body += "<fieldset><legend>LUN Usage on $server_name</legend>"
        $mail_body += "<table><tr><td><b>Name</b></td><td><b>Capacity</b></td><td><b>Size</b></td><td><b>Usage</b></td></tr>"
        Get-wmiObject -computerName $_.Identity win32_volume | where-object { $_.drivetype -eq 3 -and ($_.name -like "L:\*" -or $_.name -like "C:\*") } | foreach {
            $lun_name = $_.name
            $lun_capacity = [math]::round($_.capacity / 1GB)
            $lun_size = [math]::round(($_.capacity - $_.freespace) / 1GB)
            $lun_usage = [math]::round(($lun_size / $lun_capacity) * 100)
            $mail_body += "<tr><td>$lun_name</td><td>$lun_capacity GB</td><td>$lun_size GB</td><td>$lun_usage %</td></tr>"
        }         
        $mail_body += "</table></fieldset><br/>"
    }
 
 
$mail_body += "</body></html>"
 
#write-output $mail_body > daily_report.html
send-mailmessage -smtpserver $mail_server -from $mail_source -to $mail_cible -subject $mail_subject -body $mail_body -BodyAsHtml

J'ai eu des soucis de partition saturée, surtout celles des logs de bases de données. Mais le DAG étant hautement disponible, je ne détectais pas le problème immédiatement. Ce script me permettait de voir si j'avais un incident sur le DAG, si les bases actives avaient basculées sur un autre noeud, alors il y avait des chances d'avoir un dysfonctionnement sur le MBX.

Une liste des utilisateurs prochent de l'over-quota

Le script suivant a pour but d'avertir les administrateurs sur les utilisateurs proches de l'over-quota. On paramètre un seuil en pourcentage, on choisit un valeur inférieur au seuil de warning Exchange. En fonction du quota appliqué à la base et/ou à la boite aux lettres, les boites qui ont une utilisation supérieur au seuil sont listées. Les administrateurs peuvent donc contacter l'utilisateur avant même que celui ci est un message d'avertissement sur son quota.

# init
    . 'C:\Program Files\Microsoft\Exchange Server\V14\bin\RemoteExchange.ps1'
    Connect-ExchangeServer -auto
 
# Parametres messagerie
    $mail_source = "<serveur_exchange>@<domaine>"
    $mail_cible = "<admin_exchange>@<domaine>"
    $mail_server = "<hub>.<domaine>"
# Seuil d'alerte - 80% par défaut
    $quota_limit = "70"
 
# Entete mail
    $mail_subject = "Exchange - Check Quota " + (get-date).ToShortdateString() + " " + (get-date).ToShortTimeString()
    $mail_body = "<html><body>"
    $mail_body += "<fieldset><legend>Exchange - Check Quota</legend>"
    $mail_body += "<table><tr><th>Name</th><th>Database</th><th>Usage</th><th>Size</th><th>Quota</th><th>LastLogon</th></tr>"
 
# On check toutes les boites
$list_mb = get-mailbox -Domaincontroller par-un-dcroot01
$list_mb | foreach {
    $mb_name = $_.identity
    $mb_display = $_.name + " (" + $_.alias + ")"
    $mb_db = $_.database
    $db = Get-MailboxDatabase -Identity $mb_db
 
    # Determine le quota applique sur la boite sinon le quota s'applique sur la base
    if(!$_.ProhibitSendQuota.isUnlimited) {
        $mb_quota_mb = $_.ProhibitSendQuota.value.tomb()
        $mb_quota_b = $_.ProhibitSendQuota.value.tobytes()
    }elseif(!$db.ProhibitSendQuota.isunlimited){
        $mb_quota_mb = $db.ProhibitSendQuota.value.tomb()
        $mb_quota_b = $db.ProhibitSendQuota.value.tobytes()
    }else{
        
    }
    
    # Taille courante de la boite
    $mb_stat = Get-MailboxStatistics -Identity $mb_name
    $mb_size_mb = $mb_stat.TotalItemSize.value.tomb()
    $mb_size_b = $mb_stat.TotalItemSize.value.tobytes()
    $mb_last_logon = $mb_stat.LastLogonTime.tostring()
    
    # On check le seuil
    $mb_usage = [math]::round(($mb_size_b / $mb_quota_b) * 100)
    if($mb_usage -gt $quota_limit) {
        $mail_body += "<tr><td>$mb_display</td><td>$mb_db</td><td>$mb_usage%</td><td>"+$mb_size_mb+"MB</td><td>"+$mb_quota_mb+"MB</td><td>$mb_last_logon</td></tr>"
    }
}
 
$mail_body += "</table></fieldset>"
$mail_body += "</body></html>"
 
# Envoi
    #write-output $mail_body > check_quota.html
    send-mailmessage -smtpserver $mail_server -from $mail_source -to $mail_cible -subject $mail_subject -body $mail_body -BodyAsHtml

Je fais tourner ce script sur une organisation Exchange d'environ 500 boites. Pour des orga plus grande, il sera nécessaire d'adapter le script pour lui faire traiter des lots car son temps d’exécution pour monter en flèche.

Quelques satistiques

Pour finir, un mini script renvoyant quelques infos quantitatives dont nos décideurs sont très friands.

# init
    . 'C:\Program Files\Microsoft\Exchange Server\V14\bin\RemoteExchange.ps1'
    Connect-ExchangeServer -auto
 
$total_bal = 0
$total_size_bal = 0
$max_size_bal = 0
 
$list_bal = get-mailbox -Domaincontroller <domain_controller>
$list_bal | foreach {
    $total_bal += 1
    
    $current_bal = Get-mailboxStatistics -Identity $_.identity
    $total_size_bal += $current_bal.TotalItemSize.Value.toBytes()
 
    if($current_bal.TotalItemSize.Value.toBytes() -gt $max_size_bal) {
        $max_size_bal = $current_bal.TotalItemSize.Value.toBytes()
    }
}
 
$avg_size_bal = $total_size_bal / $total_bal
 
write "Nombre des boites : $total_bal"
write "Espace total : $total_size_bal"
write "Taille moyenne : $avg_size_bal"
write "Taille maximum : $max_size_bal"

Cas d'une organisation complexe

J'ai installé ces scripts sur une autre orga Exchange plus complexe, avec de nombreux domaines AD. Il a été nécessaire de préciser un nom de contrôleur de domaine dans certaines commande avec le paramètre -Domaincontroller <domain_controller>. J'ai aussi eu une difficulté avec les files d'attente des serveurs EDGE car ceci étant accroché à aucun domaine, le serveur exécutant le script ne peut pas les contacter.

Réferences


Image Sherlock Holmes Tux : http://tux.crystalxp.net/fr.id.5975-brunocb-sherlock-holmes-tux.html