通过Powershell操作Exchange/Lync Service 2010

iOSbird 2012-07-13

      最近在做两个项目:邮箱管理系统和Lync开户系统。其中邮件管理系统主要对Exchange Service上的邮箱和群组进行管理,邮箱(MailBox)管理包括创建邮箱、禁用邮箱、删除邮箱、设置主地址等功能,群组(DistributionGroup)管理包括创建/删除群组、添加/删除群组成员、添加/删除群组白名单等;Lync开户系统主要是为邮箱开通使用 Microsoft Lync拨打电话的功能,并设置Lync内部号码。

       由于项目组成员都是Java程序员(声明自己也是一个Java程序员),两个项目都是使用java语句开发,但是用Java访问微软的东东还是很不方便的,为此使用.net封装了操作Exchange/Lync Service的WebService服务,以供Java调用。呵呵,C#与Java语言最类似,入手最快就使用它了~

     在开发过程中,查了很多资料,但大部分都很凌乱,入门教程并有详细说明的只找到了一篇(还是值得看的:http://www.cnblogs.com/gongguo/archive/2012/03/12/2392049.html ),但需要多封装一个COM组件并且COM组件的配置也很复杂,为此写一篇Exchange/Lync开发的入门教程,一是总结最近的开发经验,二是让大家少走点弯路。看了网上很多资料,第一次决定开博客记录自己的技术成长轨迹,就多啰嗦了,请见谅~O(∩_∩)O~

       .net操作Exchange/LyncService的基本思想是:利用System.Management.Automation.dll提供的接口调用WindowsPowershell的cmdlets命令,而Windows Powershell可以通过Add-Pssnapin和import-module载入Exchange和Lync模块,从而可以直接调用 Exchange和Lync Service的相关命令。【这种方式最大的缺点是执行速度太慢,为此像修改邮箱的基本信息这类的操作,建议通过LDAP的方式直接修改AD账号的属性 ---详见后续文章(使用LDAP协议访问AD)】

     开发环境:(x64)Win7 Service/AD域(dc=test,dc=com)/Windows Powershell(x86,x64)/Exchange Service2010(或LyncService2010)/IIS7.0

     开发工具:Visual Studio 2010

       开发步骤:

首先,创建类库PowerShellComponent.dll,以封装Powershell命令的调用接口:

1.打开VS,新建项目--->类库,名称PowerShellComponent,.net选择:.net Framework4.0(2.0或2.0以上即可)

2.将类名Class1.cs重命名为PowerShellCore.cs

3.添加Dll引用:C:\Windows\assembly\GAC_MSIL\System.Management.Automation\1.0.0.0__31bf3856ad364e35\System.Management.Automation.dll

4.引入两个命名空间:

using System.Management.Automation;using System.Management.Automation.Runspaces;

5.创建执行Powershell命令的方法ExceCommand(cmdlet,parms):

public bool ExceCommand(string commandName, Hashtable parms)
        {
            RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create();
            PSSnapInException warning = null;
            //Exchange Service
            PSSnapInInfo info = runspaceConfiguration.AddPSSnapIn("Microsoft.Exchange.Management.PowerShell.E2010", out warning);            
            Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration);
            /*
            InitialSessionState iss = InitialSessionState.CreateDefault();
            iss.ImportPSModule(new String[]{"Lync"});//Lync Service
            //Exchange Service2
            //iss.ImportPSSnapIn("Microsoft.Exchange.Management.PowerShell.E2010",out warning);
            Runspace runspace = RunspaceFactory.CreateRunspace(iss);
            */

            runspace.Open();
            Pipeline pipeline = runspace.CreatePipeline();
            string message = string.Empty;
            using (pipeline)
            {
                Command item = new Command(commandName);
                foreach (DictionaryEntry entry in parms)
                {
                    item.Parameters.Add(entry.Key.ToString(), entry.Value);
                }
                pipeline.Commands.Add(item);
                ICollection<PSObject> is2 = pipeline.Invoke();
                if ((pipeline.Error != null) && (pipeline.Error.Count > 0))
                {
                    foreach (object obj2 in pipeline.Error.ReadToEnd())
                    {
                        message = message + obj2.ToString() + "|";
                    }
                    throw new Exception(message);
                }
            }
            pipeline = null;
            runspace.Close();
            runspace = null;
            return string.IsNullOrEmpty(message);
        }
 

 从上面的代码可以看到,有两种方式将Exchange模块引入Powershell,但本质都是使用Add-Pssnapin命令,有一种方式载入Lync模块,本质是调用Import-Module命令。

6.另外,上述方法中的commandName参数取值也可也是脚本文件的路径,比如C:/ps/setLyncNumber.ps1。其中setLyncNumber.ps1的内容为:

param([string] $username,[string] $lineURI)

//Import-Module 'C:\Program Files\Common Files\Microsoft Lync Server 2010\Modules\Lync\Lync.psd1'

Set-CsUser -Identity $username  -LineURI $lineURI
 
其中,Import-Module与代码iss.ImportPSModule(new String[]{"Lync"})的功能相同,可以去掉。

7.编译生成PowerShellcomponent.dll库。

其次,创建WebService服务ADBaseService,提供一个调用PowershellComponent的测试类

1.新建项目--->Web 服务程序,名称:ADBaserService, .net为.net framework3.5(我的.net4.0没有创建WS的选项)

2.重命名Service1.asmx为ADService.asmx,同时别忘修改"标记“

3.进入ADBaseService的属性,设置目标框架为:.net framework4.0. (完成后,测试HelloWorld程序,确保运行正常)

4.添加对项目或dll库PowerShellComponent的引用

5.添加测试类Test.cs,并添加方法NewMailbox(samAccountName)及SetLyncNumber(name,lineUri):

public string NewMailbox(string samAccountName)
        {
            try
            {
                Hashtable parms = new Hashtable();
                parms.Add("Name", samAccountName);
                parms.Add("SamAccountName", samAccountName);
                parms.Add("UserPrincipalName", samAccountName + "@test.com");

                parms.Add("OrganizationalUnit", "TestOU");
                char[] chArray = "admin@1243".ToCharArray();
                SecureString str = new SecureString();
                foreach (char ch in chArray)
                {
                    str.AppendChar(ch);
                }
                parms.Add("Password", str);


                new PowerShellCore().ExceCommand("New-Mailbox", parms);
            }
            catch (Exception e)
            {
                return e.StackTrace;
            }

            return "Success";
        }
 
public string SetLyncNumber(string name, string lineURI)
        {
            try
            {
                Hashtable parms = new Hashtable();
                parms.Add("Identity ", "test\\" + name);
                parms.Add("LineURI", lineURI);

                new PowerShellCore().ExceCommand("Set-CsUser", parms);
            }
            catch (Exception e)
            {
                return e.StackTrace;
            }

            return "Success";
        }

 6.在ADService.asmx.cs中添加调用上述两个方法的Webmethod:NewMailBox和SetLyncNumber.

7.启动VS的调试模式,分别在LyncService2010上测试SetLyncNumber、在ExchangeService2010上测试NewMailBox,结果为:

(1)SetLyncNumber测试通过!

(2)NewMailBox测试失败,抛出异常:没有为Windows PowerShell版本2注册管理单元

问题分析

      首先定位异常抛出位置为:

PSSnapInInfo info = runspaceConfiguration.AddPSSnapIn("Microsoft.Exchange.Management.PowerShell.E2010", out warning);

而这行代码等同于在Powershell中调用命令:

Add-Pssnapin Microsoft.Exchange.Management.PowerShell.E2010

也就说在Windows Powershell中执行这条命令应该报同样的错误。

      其次,查找Powershell的问题。我们知道在X64 Service中,存在两个Powershell命令窗口,分别位于“%SystemRoot%\System32\WindowsPowerShell\V1.0” 和“%SystemRoot%\SysWOW64\WindowsPowerShell\V1.0”中。在这两个命令窗口中都执行上面的命令,结果发现System32下的正常,而SysWOW64下的报错了,错误与程序中的一样,验证了我们的猜测。

      到此应该清楚问题的原因了:VS调试时以x86程序的方式启动,而Win7 X64位系统中,X86程序只能以WOW64的模式运行,因此程序调用了SysWOW64下的Powershell。然而Exchange Service的cmdlets模块是X64的,无法在X86的Powershell中加载。[Lync Service的cmdlets模块是X86的,在两个Powershell中都可以加载,因此后续修复该问题时的变动对LyncService无影响,能一直保证它的正确性。]

(备注:SysWOW64下是X86的Powershell而不是X64,有关这方面的知识请查找”x86程序在x64为机器上执行的原理“;X64的程序可以加载X86的Dll库,但X86的程序无法加载X64的Dll库)

解决方案

       将ADBaseService以X64的方式运行。

方案一:

      首先将PowershellComponent项目的”目标平台“设为:x64;然后重新编译。其次将ADBaseService项目的”目标平台“也设置为:x64,最后启动调试即可。

 备注:(1)注意不要引用X86下的System.Management.Automation.dll;其他类库的引用可以不变。

           (2)启动调试时,如果报错误:Could not load file or assembly 'XXXXXX' or one of its dependencies. An attempt was made to load a program with an incorrect format【未能加载文件或程序集“ADBaseService”或它的某一个依赖项。试图加载格式不正确的程序。】。。。。。

这是因为VS不支持X64位调试的原因,有关这个问题的描述及微软的答复,参考:http://connect.microsoft.com/VisualStudio/feedback/details/556670/could-not-load-file-or-assembly-error-when-referencing-a-64-bit-assembly

虽然上面提供了一种解决方案,但是本人对VS不熟,也没兴趣研究,不在做过多说明。

方案二:

      直接将ADBaseService部署到IIS7.0服务器上,利用IIS对X64位程序的支持来解决这个问题。

备注:(1)这种方案其实就是线上部署过程,详见下面的描述;

         (2)该方案的缺点是:调试不方便,但可以通过日志输出的方式代替。

最后,将WS服务ADBaseService部署到IIS7.0上,为Java程序调用提供接口:

1.打开Internet信息服务器IIS,选择添加网站:填写”网站名称“:ADBaseService,指定一个具体的”物理路径“,分配端口为8088,最后单击确定。

2.在”应用程序池中“找到ADBaseService,右键选择”高级设置“:.net framework版本---4.0,启用32位应用程序---False(默认就为False),标识--->自定义账户--->设置:用户名--域名\账号,密码---****(确保该账号具有执行Powershell命令的权限即可),确定即可。

3.VS中,选择发布ADBaseService,填写服务URL---localhost,网站/应用程序--ADBaseService,最后单击”发布“,完成发布工作。

4.重启IIS上的ADBaseService,然后浏览http://localhost:8088/ADService.asmx,测试WS方法NewMailBox。测试通过,大功告成!

备注:(1)如果你是IIS6.0,那需要设置IIS6.0,让它支持x64位的.net程序,设置方式参考:http://support.microsoft.com/kb/894435

        (2)如果测试没有通过,注意检查.net版本以及账号的权限,同时查看System.Management.Automation.dll的引用是否是x64的。

到此完成了通过Powershell访问Exchange Service2010/Lync Service2010的全部工作,本文也只给出了创建邮箱一个方法,其他操作查看msdn的帮助文档即可:http://msdn.microsoft.com/zh-cn/library/aa997174.aspx

文章开头提到的使用LDAP协议访问AD账号是对操作Exchange/Lync service的补充,能获得很好的效率。后面的文章中将会进一步介绍。

相关推荐