Bir önceki yazımda (“Bulutta Hibrit Senaryolar – Windows Azure Connect Servisini Yapılandırma“) Windows Azure Bulut ortamı üzerinde yayınladığımız web uygulamasının, Windows Server tabanlı lokal domain’imizde yer alan kaynaklarla nasıl görüştürebileceğimizi anlatmaya çalıştım. Şimdi ise, lokalde sanal olarak çalışan Exchange Server ve Lync Server’a buluttaki web uygulamamızdan nasıl komut gönderebileceğimize bakalım.
Senaryo
Windows Server 2008 R2 SP1 Enterprise Edition işletim sistemi temelli Exchanger Server 2010 SP1 yüklü sanal makina üzerinde Exchange Enterprise lisansı etkin durumda. Hub Transport, Client Access, Unified Messaging ve Mailbox rolleri stand-alone olarak bu makina üzerinde varsayılan ayarlarıyla konfigüre edildi. Mailbox Database ve Retention Policy de varsayılan ayarlarında. Makina adı EXC. Domain Controller adı SERVER. Makina üzerinde yeni bir mailbox yaratıldığında, otomatik olarak DC tarafında da ilgili kullanıcının objesi oluşturulmakta.
Resim-1
Windows Server 2008 R2 SP1 Enterprise Edition işletim sistemi temelli Lync Server 2010 yüklü ikinci sanal makina üzerinde Standard Edition Server etkin durumda. Enterprise Voice özelliği sayesinde, bu özellik etkinleştirilmiş kullanıcılar telekonferans, vidyo konferansı, anında mesajlaşma gibi birleşik iletişim olanaklarından faydalanabiliyorlar. Etkinleştirme işlemi manüel olarak tek tek her kullanıcı için yapılabiliyor. Makina adı LYNC. Domain Controller adı SERVER.
Resim-2
Windows Azure Guest OS 2.1 üzerinde deploy ettiğimiz ve Windows Azure SDK 1.3 ile oluşturduğumuz bir adet WebRole’üne sahip web uygulamasının, önceki makalede LDAP sorgusuyla domain’deki belirli bir Organizational Unit altında tanımlı olan kullanıcıların isim, soyisim ve e-mail adresi gibi bilgilerini çekip bir textbox içerisinde yazdırdığını gördük (not: Azure Guest OS ve SDK sürekli güncellenmektedir). Şimdi ise, yine web uygulamasının arayüzünden yeni domain kullanıcısı yaratma, mailbox oluşturma ve Enterprise Voice özelliğini etkinleştirme işlemlerini gerçekleştirelim.
Resim-3
Sayfa Tasarımı
Web uygulamasındaki kullanıcı sayfası aşağıdaki gibi olsun. LDAP ile çekilen mevcut kullanıcı bilgileri listelensin ve “+” ikonuna basıldığında yeni kullanıcı bilgilerinin girileceği pencere açılsın:
Resim-4
Resim-5
Exchange Server Üzerinde Uzaktan Yeni Mailbox Yaratma
Mailbox yaratma işlemini otomatize etmenin yolu, PowerShell komutlarından oluşan bir script oluşturmak. Bilindiği üzere PowerShell, XP SP2/Server 2003/Vista ile hayatımıza girdi. 7/Server 2008 R2’de sürüm numarası 2.0’a yükseldi ve varsayılan olarak yüklü hale geldi. Özellikle server ayağında, server yöneticileri tarafından, kullanıcı arayüzü ile yapılabilen birçok şeyi uzaktan yapma ve otomatize etme amacıyla kullanılıyor. .NET altyapısı ile entegre çalışan PowerShell komutları yani cmdlet’ler (command-let), doğrudan Component Object Model (COM) ve Windows Management Instrumentation (WMI) erişim yetkisine sahipler ve yerel ile uzaktaki kaynakları yönetebilirler. PowerShell’i hayal ederken soğan gibi çok katmanlı bir yapı düşünün. İçteki katmanın komutları bellidir ve dıştaki komutları tanımlayamaz. Ancak dış katmanlar, içtekileri kapsar. Yani farklı amaçlar için yeni komut kütüphanelerine ihtiyacımız olduğunda, PowerShell’imize modül ekleme veya oturum çağırma yöntemlerini uygulayabiliyoruz. Server 2008 R2 üzerinde varsayılan gelen PowerShell ile mailbox yaratamayacağımızdan Exchange için session import, Lync için module import yöntemlerini kullanacağız.
Web uygulamasından PowerShell scriptlerini çağırmak için Windows SDK 7.1 yüklememiz gerekir. Daha sonra yeni bir User class’ı yaratalım ve class içerisinde createUser() metodu oluşturalım. Bu metod ile şunu yapıyor olacağız:
- Metodun parametreleriyle alınana kullanıcı bilgisine göre, yaratılmak istenen kullanıcının halihazırda tanımlı olup olmadığını kontrol edeceğiz.
- Öyleyse hata mesajı yazdıracağız.
- Tanımlı değilse, yaratacağız. Bunun için iki adet script oluşturacağız.
- Birinci script uzaktaki makina yani EXC üzerinde çalışacak. String array’i olarak her komutu tek tek alıyoruz ve her bir array değerini .txt uzantılı bir dosyaya her komut yeni satırda olacak şekilde yazdırıyoruz. Daha sonra .txt uzantısını PowerShell script uzantısı olan .ps1‘e çevirip bu dosyayı EXC üzerinde paylaşılan bir klasörde kaydediyoruz. Bu script, Exchange üzerindeki PowerShell exe’sini çağıracak, Exchange Session‘ı açıp, bu session’ı kendi üzerine aktaracak ve yeni mailbox oluşturmak için gereken komutları parametreleriyle çalıştıracak.
- İkinci script, web uygulamasını host eden yani IIS’i barındıran lokal makina (aslında bizim örneğimizde buluttaki sanal makina) üzerinde oluşturuluyor. Yine bir string array’ine komutları tek tek yazıp onları text dosyasında saklayıp, uzantısını .ps1’e çeviriyoruz ve doğrudan C: sürücüsünün altına kaydediyoruz. Bu script, EXC üzerindeki scripti tetikleyecek, yani oradaki işlemleri başlatacak.
- Scriplerimizi oluşturduktan sonra sıra geldi onları çalıştırmaya. Bunun için gerekli komutları giriyoruz ve ikinci oluşturduğumuz script’i execute ediyoruz.
Script’leri çalıştırırken dikkat edilmesi gerekenler: Çalıştırıldıkları makina üzerindeki Execution Policy ayarı ve bağlanmaya çalıştıkları makinanın parolasının Secure String şeklinde olması. Get-ExecutionPolicy komutu ile mevcut ayarı görüntüleyebiliyoruz. Default olarak AllSigned gelmekte, yani sadece imzalı script’lerin çalıştırılmasın izin veriliyor. Seviyeyi RemoteSigned veya Unrestricted’a çektiğimizde bu sorunu halletmiş oluyoruz. Diğer yandan, uzaktaki makina yani Exchange Server üzerinde yönetimsel haklara sahip kullanıcının (excadmin) parolasını plain-text olarak gönderiyoruz ve onu Secure String haline çevirtiyoruz çünkü bağlantıyı otomatize etmek anca bu şekilde mümkün oluyor. Aksi halde parolayı girmek için prompt çıkması gerekiyor karşımıza, ancak web uygulamasından bu prompt’u görmek imkansız olduğu için hata verecektir.
Mailbox yaratma komutlarıyla giremediğimiz değerleri, yani yaratılan kullanıcının telefon numarası, departman adı, iş ünvanı, not kısmı, security group üyeliği ve ilk logon’da parolasının değiştirme zorunluluğu gibi komutları script adımlarından hemen sonra LDAP yordamıyla yapıyoruz.
Lync Server Üzerinde, Yeni Yaratılan Kullanıcıyı Uzaktan Enterprise Voice İçin Etkinleştirme
Yeni kullanıcıya telekonferans, anında mesajlaşma, masaüstü ve program paylaşma gibi özellikleri kullandırtmak için onu, Lync üzerinde Enterprise Voice Enabled haline getirmemiz gerekir. Bunun için Exchange’teki benzer bir şekilde iki script oluşturacağız.
- Birinci script uzaktaki makina yani LYNC üzerinde çalışacak. String array’i olarak her komutu tek tek alıyoruz ve her bir array değerini .txt uzantılı bir dosyaya her komut yeni satırda olacak şekilde yazdırıyoruz. Daha sonra .txt uzantısını PowerShell script uzantısı olan .ps1’e çevirip bu dosyayı LYNC üzerinde paylaşılan bir klasörde kaydediyoruz. Bu script, Lync üzerindeki PowerShell exe’sini çağıracak, Lync Module‘ünü import edip, kullanıcıyı etkinleştirmek için gereken komutları parametreleriyle çalıştıracak.
- İkinci script, web uygulamasını host eden yani IIS’i barındıran lokal makina (aslında bizim örneğimizde buluttaki sanal makina) üzerinde oluşturuluyor. Yine bir string array’ine komutları tek tek yazıp onları text dosyasında saklayıp, uzantısını .ps1’e çeviriyoruz ve doğrudan C: sürücüsünün altına kaydediyoruz. Bu script, LYNC üzerindeki scripti tetikleyecek, yani oradaki işlemleri başlatacak.
- Scriplerimizi oluşturduktan sonra sıra geldi onları çalıştırmaya. Bunun için gerekli komutları giriyoruz ve ikinci oluştuduğumuz script’i execute ediyoruz.
Sonraki adımda, User class’ındaki createUser() metoduna erişim ve parametre gönderimi yeni kullanıcı yaratma penceresindeki Save butonunun click event’ine bağlandı.
İşlemler doğru şekilde tamamlanırsa Exchange üzerinde yaratılan mailbox ile DC üzerinde yeni kullanıcı objesi oluşturulmuş olacak ve Lync üzerinde bu kullanıcı etkinleştirilmiş hale gelecek. Buluttaki uygulamamız ve lokalde çalışan makinalarımız arasındaki ağ trafiği gecikmeli olduğundan benim örneğimde bu işlem ortalama olarak 1.45 dakika arasında sürdü.
Mevcut kullanıcıyı ve mailbox’ını silme ile Lync üzerinde disable etme işlemleri de yine aynı yöntemle yapılabilmekte.
Kaynak kodları aşağıdadır:
User.cs
using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.DirectoryServices;
using System.Net;
using System.Net.Mail;
using System.Web.Security;
using System.Security.Principal;
using System.Runtime.InteropServices;
using System.Security;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Management;
using System.IO;
using System.Text;
namespace WebRole1
{
public class User
{
/// <summary>
///
/// </summary>
/// <param name=”firstName”></param>
/// <param name=”lastName”></param>
/// <param name=”userPrincipalName”></param>
/// <param name=”displayName”></param>
/// <param name=”name”></param>
/// <param name=”password”></param>
/// <param name=”email”></param>
/// <param name=”alias”></param>
/// <param name=”phone”></param>
/// <param name=”title”></param>
/// <param name=”department”></param>
/// <param name=”priority”></param>
/// <returns>
/// 1 = successfull
/// 2 = user already exists
/// 3 = ailed
/// </returns>
public int createUser(string firstName, string lastName, string userPrincipalName, string displayName, string name, string password, string email, string alias, string phone,
string title, string department, string priority)
{
try
{
//check if the user with given displayName already exists in the Actice Directory
DirectoryEntry entry = new DirectoryEntry(“LDAP:// *******.com:389/OU=BermudaUsers,DC=*******,DC=com”, “Administrator”, “*******”);
DirectorySearcher search = new DirectorySearcher(entry);
search.Filter = “(objectClass=user)”;
search.PropertiesToLoad.Add(“cn”);
SearchResultCollection results = search.FindAll();
foreach (SearchResult resEnt in results)
{
//if user exists return 2
if (resEnt.Properties[“cn”][0].ToString() == name)
return 2;
}
entry.Close();
entry.Dispose();
search.Dispose();
//following script is created on Exchange Serve side, it creates New User in AD and its New Mailbox
string[] remoteScript = { “PowerShell Set-ExecutionPolicy RemoteSigned”,
@”$pass = convertto-securestring -asplaintext “”” + password + @””” -force”,
@”$credential = new-object -typename System.Management.Automation.PSCredential -argumentlist “”*******\excadmin”” , $pass”,
@”$s = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://exc.*******.com/PowerShell/ -Authentication Kerberos -Credential $credential”,
@”Import-PSSession $s”,
@”New-Mailbox -Name:””” + name + @””” -Password:$pass -UserPrincipalName:””” + userPrincipalName + @””” -displayName:””” + displayName + @””” -PrimarySmtpAddress:””” + email + @””” -Alias:””” + alias + @””” -DomainController:””server.*******.com”” -Database:””Mailbox Database 0887331500″” -OrganizationalUnit:””*******.com/BermudaUsers”” -FirstName:””” + firstName + @””” -lastName:””” + lastName + @””””,
“”};
//txt file with paramerets of the script are created on Exchange and the extension is converted to ps1
System.IO.File.WriteAllLines(@”\\exc\excshare2\newUserScript.txt”, remoteScript);
if (File.Exists(@”\\exc\excshare2\newUserScript.ps1″))
File.Delete(@”\\exc\excshare2\newUserScript.ps1″);
File.Move(@”\\exc\excshare2\newUserScript.txt”, Path.ChangeExtension(@”\\exc\excshare2\newUserScript.txt”, “ps1”));
//following script is created on local computer which hosts the website. it calls above-created script
string[] localScript = { “PowerShell Set-ExecutionPolicy RemoteSigned”,
@”$pass = convertto-securestring -asplaintext “”*******”” -force”,
@”$credential = new-object -typename System.Management.Automation.PSCredential -argumentlist “”*******\excadmin”” , $pass”,
@”Invoke-Command -ComputerName EXC.*******.com -Credential $credential -scriptBlock {\\exc\excshare2\newUserScript.ps1}”,
“”};
System.IO.File.WriteAllLines(@”c:\callNewUserScript.txt”, localScript);
if (File.Exists(@”c:\callNewUserScript.ps1″))
File.Delete(@”c:\callNewUserScript.ps1″);
File.Move(@”c:\callNewUserScript.txt”, Path.ChangeExtension(@”c:\callNewUserScript.txt”, “ps1″));
//powershell runspace is created and local script is executed
RunspaceConfiguration runspConfig = RunspaceConfiguration.Create();
Runspace runsp = RunspaceFactory.CreateRunspace(runspConfig);
runsp.Open();
RunspaceInvoke script = new RunspaceInvoke(runsp);
Command scripcmd = new Command(@”c:\callNewUserScript.ps1”);
Pipeline pipe = runsp.CreatePipeline();
pipe.Commands.Add(scripcmd);
pipe.Invoke();
runsp.Close();
runspConfig = null;
//phone number, deparment, title and priority values cannot be set using previous command, the following step they are added to newly created user’s information:
DirectoryEntry entry2 = new DirectoryEntry(“LDAP://*******.com:389/OU=BermudaUsers,DC=*******,DC=com”, “Administrator”, “*******”);
DirectoryEntry group = new DirectoryEntry(“LDAP://*******.com:389/CN=bermudausers,OU=BermudaUsers,DC=*******,DC=com”, “Administrator”, “********”);
DirectorySearcher search2 = new DirectorySearcher(entry);
search2.Filter = “(objectClass=user)”;
search2.PropertiesToLoad.Add(“telephoneNumber”);
search2.PropertiesToLoad.Add(“cn”);
SearchResultCollection results3 = search.FindAll();
foreach (SearchResult resEnt in results3)
{
if (resEnt.Properties[“cn”][0].ToString() == name)
{
DirectoryEntry entry3 = new DirectoryEntry(resEnt.Path, “Administrator”, “*******”);
entry3.Properties[“telephoneNumber”].Value = phone;
entry3.Properties[“department”].Value = department;
entry3.Properties[“title”].Value = title;
entry3.Properties[“info”].Value = priority;
entry3.Properties[“pwdLastSet”].Value = 0;
group.Properties[“member”].Add(“CN=” + name + “,OU=BermudaUsers,DC=*******,DC=com”);
group.CommitChanges();
entry3.CommitChanges();
entry3.Close();
entry3.Dispose();
}
}
entry2.Close();
entry2.Dispose();
group.Close();
group.Dispose();
search2.Dispose();
//newly created user now has been defined in Active Directory and its Mailbox has been created
//at following step, this user is being enabled on Lync Server in order to use Enterprise Voice features
//following script is created on Lync side, it enables the user
string[] remoteLyncScript = { “PowerShell Set-ExecutionPolicy RemoteSigned”, “Import-Module Lync”, @”Enable-CsUser -Identity “”” + name + @””” -RegistrarPool “”lync.*******.com”” -SipAddressType EmailAddress -SipDomain *******.com -DomainController server.*******.com | Set-CsUser -Identity “”” + name + @””” -EnterpriseVoiceEnabled $true”, “” };
System.IO.File.WriteAllLines(@”\\lync\lyncshare2\enableUserScript.txt”, remoteLyncScript);
if (File.Exists(@”\\lync\lyncshare2\enableUserScript.ps1″))
File.Delete(@”\\lync\lyncshare2\enableUserScript.ps1″);
File.Move(@”\\lync\lyncshare2\enableUserScript.txt”, Path.ChangeExtension(@”\\lync\lyncshare2\enableUserScript.txt”, “ps1”));
//following script is created on local side, it calls the above-created script
string[] localLyncScript = { “PowerShell Set-ExecutionPolicy RemoteSigned”,
@”$pass = convertto-securestring -asplaintext “”*********”” -force”,
@”$credential = new-object -typename System.Management.Automation.PSCredential -argumentlist “”*******\lyncadmin”” , $pass”,
@”Invoke-Command -ComputerName LYNC -Credential $credential -scriptBlock {\\lync\lyncshare2\enableUserScript.ps1}”,
“”};
System.IO.File.WriteAllLines(@”c:\callEnableUserScript.txt”, localLyncScript);
if (File.Exists(@”c:\callEnableUserScript.ps1″))
File.Delete(@”c:\callEnableUserScript.ps1″);
File.Move(@”c:\callEnableUserScript.txt”, Path.ChangeExtension(@”c:\callEnableUserScript.txt”, “ps1″));
//powershell runspace is created and local script is executed
RunspaceConfiguration runspConfig2 = RunspaceConfiguration.Create();
Runspace runsp2 = RunspaceFactory.CreateRunspace(runspConfig2);
runsp2.Open();
RunspaceInvoke script2 = new RunspaceInvoke(runsp2);
Command scripcmd2 = new Command(@”c:\callEnableUserScript.ps1”);
Pipeline pipe2 = runsp2.CreatePipeline();
pipe2.Commands.Add(scripcmd2);
pipe2.Invoke();
runsp2.Close();
runspConfig2 = null;
//if everything is executed without error, the method returns “successfull” value
return 1;
}
catch (Exception e)
{
//any error catched is stored in log file at local computer and “failed” value is returned
string[] exArray = { e.ToString()};
if (File.Exists(@”c:\newUserErrorLog.txt”))
File.Delete(@”c:\newUserErrorLog.txt”);
System.IO.File.WriteAllLines(@”c:\newUserErrorLog.txt”, exArray);
return 3;
}
}
Bu konuyla ilgili sorularınızı Http://forum.mshowto.org alt kısımda bulunan yorumlar alanını kullanarak sorabilirsiniz.
Referanslar