前言

微软的Exchange服务器是和AD服务器是紧密相关的,如果AD服务器发生了崩溃就会立即引起Exchange服务器的故障。在我们的生产环境中经常会有这样一种场景,只有一台活动目录域控制器和一台Exchange的服务器,如果AD服务器发生了不可修复的故障,而且没有任何的AD服务器的系统状态备份可用,那么管理员可以将用户帐户和邮箱数据还原吗?

在一定的前提下,答案是肯定的,你完全可以恢复这些用户帐号和其相关的邮箱数据。这个前提就是你需要确保你的Exchange数据库文件的状态是Clean Shutdown的。并且能够被复制到其它的机器上去。如下图所示

利用Exchange的数据库文件生成活动目录帐号_Exchange

1 创建一个新的AD环境

在Windows Server 2012中新建一台AD服务器,创建AD,域名以及各服务器IP地址最好和原环境保持一致。计算机名也最好和原环境保持一直。此过程不做过多描述

2 在此AD中安装Exchange服务器

在新的AD环境中创建一台Exchange服务器,FQDN和原环境保持一致。在此AD中部署好Exchange Server。此过程不做过多描述。若有疑问参考本人博客Exchange2013 RTM部署 http://scnbwy.blog.51cto.com/5275956/1055376

3 载入原AD组织中的Exchange数据库文件

将原来已经损坏环境中Exchange Server 上的数据库文件拷贝到新建的Exchange Server环境中。在新搭建的Exchange环境中打开ECP,创建一个和原来已崩溃环境中名称一样的数据库。比如我这里原来损坏的数据库名称是 Mailbox Database 0707605192,在新的环境中创建一个数据库名称为Mailbox Database 0707605192即可。注:创建新数据库时,不要立即装载数据库

打开此数据库,在“维护”中,勾选“还原时可以覆盖此数据库”以便数据恢复。如下图:

利用Exchange的数据库文件生成活动目录帐号_Exchange_02

将之前崩溃环境中的数据库文件拷贝到新创建的数据库目录下面,如下图:

利用Exchange的数据库文件生成活动目录帐号_Exchange_03复制完成后,进入ECP装入此数据库。如下图:

利用Exchange的数据库文件生成活动目录帐号_数据库_04

在装载的时候却失败了。这是为什么?使用eseutil工具检查下数据库,原来数据库的状态是Dirty Shutdown状态。如下图:

利用Exchange的数据库文件生成活动目录帐号_Exchange_05

使用eseutil /p对数据库进行修复。等待修复完成。如下图:

利用Exchange的数据库文件生成活动目录帐号_数据库_06

完成之后再检查数据库,此时数据库状态为Clean Shutdown状态。如下图:

利用Exchange的数据库文件生成活动目录帐号_Exchange_07

再回到ECP界面对该数据库进行装载,数据库顺利完装入。如下图所示:

利用Exchange的数据库文件生成活动目录帐号_数据库_08

此时,进入收件人。进入高级模式,选择“连接邮箱”即可看到断开邮箱的用户,如下图所示:

只在ECP中看到账户,而没有在AD中看到账户。很明显是没有用户账户的。不过首先我们来测试下用户的邮箱数据是否丢失。

利用Exchange的数据库文件生成活动目录帐号_数据库_09

选择邮件服务器则可看到用户名

利用Exchange的数据库文件生成活动目录帐号_Exchange_10

接下来在AD中创建一个,test用户。在上面断开的邮箱用户列表中找到test用户,将此用户连接到新建的test用户。如下图:

利用Exchange的数据库文件生成活动目录帐号_Exchange_11

连接完成之后,使用test账户登录邮件系统,查看之前邮件。原来的邮件都还在。看来这样恢复是比较靠谱的。如下图:

利用Exchange的数据库文件生成活动目录帐号_Exchange_12

4 创建未链接邮箱的用户帐户列表,保存为LDF格式的文件

既然这样是成功的,那么接下来就是使用脚本来收集这些已断开连接的邮箱的帐户并生成LDF文件来用于创建这些AD帐户。

在Exchange Server 2007中微软已经帮我们写好了这个脚本。该脚本命名为CreateLdifFromDisconnectedMailboxes.ps1并将此脚本保存到Exchange安装路径下C:\ProgramFiles\Microsoft\Exchange Server\Scripts。但是在ExchangeServer 2013中似乎没有,就需要创建这个脚本文件。

将以下代码复制到称为 CreateLdifFromDisconnectedMailboxes.ps1的文本文件中,然后将该文件保存到安装Exchange 的文件夹下的 Scripts文件夹中

以下红色字体为脚本内容:

Param(

[string] $ContainerDN,

[string] $Database = "",

[bool] $append = $false

)


#function to validate input parameters

function ValidateParams

{

$validInputs = $true

$errorString = ""


if ($ContainerDN -eq "")

{

$validInputs = $false

$errorString += "`nMissing Parameter: The -ContainerDN parameter is required.Please pass in a valid container in which to create the user accounts."

}


if (!$ContainerDN.Contains(","))

{

$validInputs = $false

$errorString += "`nInvalid Container DN. Make sure to enclose the entire DN in doublequotes or it will not be parsed properly."

}


if (!$validInputs)

{

Write-error "$errorString"

}


return $validInputs

}


#function to get the display name and alias from mailbox data in theExchange store

function ExtractDisplayNameAndAlias($obj)

{

[string[]]$legacyDNSplit = $obj.LegacyDN.Split('/')

$alias =$legacyDNSplit[$legacyDNSplit.Length-1].Remove(0,3).ToLower()

$output = "dn: CN=" + $obj.DisplayName + "," +$ContainerDN + "`r`nchangetype: add`r`nuserAccountControl:544`r`nmsExchUserAccountControl: 0`r`npwdLastSet: -1`r`ndisplayName: " +$obj.DisplayName + "`r`nobjectClass: user`r`nsAMAccountName: " +$alias + "`r`n"

write-output $output | out-file -filePath "c:\ldifout.ldf"-append -noClobber

}


# Function that returns true if the incoming argument is a helprequest

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:

CreateLdifFromDisconnectedMailboxes.ps1


SYNOPSIS:

Finds all disconnected mailboxes on the local server and creates anLDIF file

with an entry for each disconnected mailbox user. Use the LDIFDEutility to import this LDIF file to Active Directory, which generates the useraccounts. You can then reconnect Mailboxes

to these accounts by using the Connect-Mailbox cmdlet. You can

specify a particular database, or specify no database to search alldatabases

on the local server.


This script is mainly used for disaster recovery scenarios where alldata except

the mailbox databases have been lost. In these scenarios, without a backup ofActive

Directory, you must re-create the user accounts so they can be

connected to existing mailboxes. This is the main objective of thisscript.


SYNTAX:

CreateLdifFromDisconnectedMailbox -ContainerDN <AD ContainerDN>

-Database <Identity of Database> -Append `$false|`$true


AD Container DN is a valid Active Directory container indistinguished name format. This value

must be enclosed in quotes. Database is the Identity parameter ofthe

database. You can retrieve the Identity value for all databases onthe local

server by running the following cmdlet:


get-mailboxdatabase -server Server01 | fl Identity


Setting -append to `$true tells the script to append data to thecurrent

c:\ldifout.ldf file instead of overwriting it. This is therecommended

setting if you are piping output from other cmdlets to this script.If the

-append switch is not included, the script runs automatically inoverwrite mode.


EXAMPLES:


"Specifying Database ID"

CreateLdifFromDisconnectedMailbox -ContainerDN"CN=Users,DC=Domain,DC=com"

-Database "SERVER\Storage Group\Database"


"Run Against All Stores on Local Server"

CreateLdifFromDisconnectedMailbox -ContainerDN"CN=Users,DC=Domain,DC=com"


"Pipe output of another cmdlet into this script"

get-mailboxdatabase -server SERVER | foreach {CreateLdifFromDisconnectedMailboxes-ContainerDN


"CN=Users,DC=domain,DC=com" -Database `$_.Identity -append`$true}

"@

}


################################################################

##########################BEGIN SCRIPT##########################

################################################################


#Check if this is a help request

$args | foreach { if (IsHelpRequest $_) { Usage; exit; } }


#Delete existing LDIF file if it is there and append is set to false

if(!$append){$a = remove-item c:\ldifout.ldf -ea SilentlyContinue}


#Validate all input parameters

$ifValidParams = ValidateParams;

if (!$ifValidParams) { exit; }


#find all disconnected mailboxes and get required information

if ($Database -ne "")

{

write "Getting disconnected mailboxes for database$Database"

$getmbxcmd = get-mailboxstatistics -Database $Database | where{$_.DisconnectDate -ne $null}

}

else

{

write "Getting disconnected mailboxes for all databases onlocal server."

 $getmbxcmd =get-mailboxstatistics | where {$_.DisconnectDate -ne $null}

}


#Make sure at least one disconnected mailbox is found; if not, exitscript

if ($getmbxcmd -eq $null) {write "No disconnected mailboxesfound.";exit}


#loop through each disconnected mailbox and write entries to theoutput file

foreach ($entry in $getmbxcmd)

{

ExtractDisplayNameAndAlias $entry

}


write "LDIF file successfully written to C:\ldifout.ldf."

下图为LDF文件

利用Exchange的数据库文件生成活动目录帐号_Exchange_13

接下来在Exchange Powershell中运行如下命令:

CreateLdifFromDisconnectedMailboxes-ContainerDN "<DN of container to place users>"

注:传递给 ContainerDN参数的 <DN of container to place users>值必须是有效 Active Directory 容器的可分辨名称 (DN),并且必须为其加上双引号。例如,若要将新用户帐户放入 contoso.com 域中的 Users 组织单位 (OU),则应使用值 "CN=Users,DC=contoso,DC=com"。

5 使用LDF文件创建AD用户帐户

在命令提示符下,键入 ldifde.exe -i -fC:\ldifout.ldf,然后按 Enter 键。进行账户导入。注意:如果有重名则命令自动停止,需要手动更改重名的用户,然后继续使用此命令

利用Exchange的数据库文件生成活动目录帐号_Exchange_14

启动Active Directory 用户和计算机。则可以看到四个被恢复的账户

利用Exchange的数据库文件生成活动目录帐号_Exchange_15

如果导入成功,用户应出现在运行该脚本时指定的容器中。如果用户帐户存在,则继续执行下一步。


6 将未链接的邮箱和AD帐户匹配并链接邮箱

首先,在Exchange服务器上打开ExchangePowershell,运行以下命令连接特定邮箱数据库上的所有邮箱。

Get-MailboxStatistics | Where {$_.DisconnectDate -ne$null} | Connect-Mailbox -Database "Server01\SG1\MBX1"

此命令示例假定您要在服务器 Server01 上的存储组 SG1中,连接邮箱数据库 MBX1 中存储的所有邮箱。如下图:

利用Exchange的数据库文件生成活动目录帐号_Exchange_16

如果还有其他Mailbox Database的话也做上面相同的操作。

其次,重新启动IIS服务

然后,重启Microsoft Exchange 信息存储服务

最后,验证其他用户是否正常访问邮件系统

利用Exchange的数据库文件生成活动目录帐号_数据库_17

至此整个过程恢复完成


PS:各位一定要做好备份!不管是因为硬件还是软件出了问题导致AD崩溃那就麻烦了!