As you may be familiar with, Microsoft’s cloud computing solutions include Windows Azure Platform at the Platform-as-a-Service (PaaS) front and Office 365 at the Software-as-a-Service (SaaS) side.
Windows Azure provides multiple rich management tools, a high available and dynamically scalable environment for your web application on an infrastructure (storage and computing) configured based on your preferences and a platform free of maintenance costs.
On the other hand, Office 365 (previously BPOS) is a software and services bundle of the online versions of Microsoft’s enterprise e-mail and messaging server Exchange, collaboration and documentation server SharePoint, and unified communication server Lync (previously Office Communication Server) working right on the cloud, and providing various and flexible licensing and usage options for small, mid-size and large enterprises.
Currently, Office 365 (O365) is hosted on its own infrastructure instead of being based on the Windows Azure Platform. Although both products are hosted and operating at Microsoft’s datacenters around the globe, they may also be used through the service provides within the Microsoft Partner Network ecosystem.
While Windows Azure and O365 seem to be independent to each other, it is possible to integrate and make them work together in various ways. The possible scenarios include the use of Exchange Management Shell, Business Connectivity Services and Online Services Module for PowerShell.
Exchange Management Shell is an extension to the command-let (cmdlet) set coming with PowerShell 2.0 and is used for managing on-premises Exchange Servers or Exchange Online environments remotely. For automating certain tasks in some extent, cmdlets of the shell can also be triggered from an Azure-hosted web application. But it has lots of limitations that narrow down the scope of automating your O365 management tasks.
Business Connectivity Services can be used to connect Line-of-Business (LOB) system to a SharePoint Online (SPO) site from an Azure-hosted web application, but again the functionality is limited to SPO.
Microsoft Online Services Module for Windows PowerShell on the other hand, brings cmdlet set designed specifically for managing and provisioning an O365 environment. The module is updated in occasional basis and with each update it extends its scope and capabilities being almost 100% replacement for the graphical interface of the management panel.
It is reasonable to assume that the module is design to be installed on a client or server operating system in your home, office or datacenter for remotely connecting and managing your O365 subscription. But in the following scenario, we are going to install the module on the virtual machine (VM) residing on the cloud and hosting an Azure application.
Assuming that you have the knowledge on creating an Azure web application and on configuring remote desktop connection for it, I am going to skip directly to the scenario of integrating the Azure application with O365.
First of all, you need to do the following settings on the VM of your Azure application you are remotely connected to:
1. Microsoft Online Services Sign-in Assistant must be installed before Microsoft Online Services Module for Windows PowerShell:
For 64-bit OS: http://g.microsoftonline.com/0BX00en/501
For 32-bit OS: http://g.microsoftonline.com/0BX00en/500
2. Install Microsoft Online Services Module for Windows PowerShell:
For 64-bit OS: http://g.microsoftonline.com/0BD00en-US/126
For 32-bit OS: http://g.microsoftonline.com/0BD00en-US/85
3. Run both 32-bit and 64-bit exe’s of Windows PowerShell from the Start menu and enter “Set-ExecutionPolicy RemoteSigned” command and type “Y” to commit the changes.
4. Give “Write” permission to Network Service, Everyone, IUSR and IIS USERS for approot and siteroot folders under the E: drive where your application is physically stored.
5. Open IIS Manager. From the Application Pools select the one that has a long hexadecimal name open Advanced Settings for changing its Identity. Normally, Network Service is selected by default, but this may lead to access and authorization problems. Therefore, you need to enter the credentials of the user account you are remotely connected with and change the Application Pool’s identity. Then, restart the IIS Services.
You may now go back to Visual Studio and write the necessary code for creating and reading from a PowerShell script using C# in a Windows Azure application with a single WebRole.
You must add the following libraries to the codebehind of the new page in your application.
using System.IO;
using System.Management;
using System.Management.Automation;
using System.Management.Automation.Host;
using System.Management.Automation.Runspaces;
You also need to note the name and password of your user account having admin rights in your O365 account. These credentials will be used in the following method by sending them as parameters from textboxes or by hard-coding them:
public bool CreateUserOn365(string userPrincipalName, string displayName, string firstName, string lastName, string newPassword, string exactDomain, string companyName)
{
try
{
string credentialID = “admin@pavelslavov.onmicrosoft.com”;
string credentialPW = “Passw0rd”;
string planType = GetO365PlanType(credentialID,credentialPW);
string[] psScript = { @"$pass = convertto-securestring -asplaintext """ + credentialPW + @""" -force",
@"$credential = new-object -typename System.Management.Automation.PSCredential -argumentlist """ + credentialID + @""", $pass",
@"Import-Module MSOnline",
@"Connect-MsolService -Credential $credential",
@"New-MsolUser -UserPrincipalName " + userPrincipalName + @" -DisplayName """ + displayName + @""" -FirstName """ + firstName + @""" -LastName """ + lastName + @""" -Password """ + newPassword + @""" -PasswordNeverExpires $true -ForceChangePassword $false -UsageLocation ""US""",
@"Set-MsolUserLicense -UserPrincipalName " + userPrincipalName + @" -AddLicenses """ + planType + "\"",
""};
if (File.Exists(common.getPath("scriptNewUser.ps1")))
File.Delete(common.getPath("scriptNewUser.ps1"));
System.IO.File.WriteAllLines(common.getPath("scriptNewUser.ps1"), psScript);
RunspaceConfiguration rsConfig = RunspaceConfiguration.Create();
Runspace myRunspace = RunspaceFactory.CreateRunspace(rsConfig);
myRunspace.Open();
Command command = new Command(common.getPath("scriptNewUser.ps1"));
Pipeline pipeline = myRunspace.CreatePipeline();
pipeline.Commands.Add(command);
StringBuilder stringBuilder = new StringBuilder();
Collection<PSObject> results = pipeline.Invoke();
// check for errors (non-terminating)
if (pipeline.Error.Count > 0)
{
//iterate over Error PipeLine until end
while (!pipeline.Error.EndOfPipeline)
{
//read one PSObject off the pipeline
var value = pipeline.Error.Read() as PSObject;
if (value != null)
{
//get the ErrorRecord
var r = value.BaseObject as ErrorRecord;
if (r != null)
{
//build whatever kind of message you want
stringBuilder.AppendLine(r.InvocationInfo.MyCommand.Name + " : " + r.Exception.Message);
stringBuilder.AppendLine(r.InvocationInfo.PositionMessage);
stringBuilder.AppendLine(string.Format("+ CategoryInfo: {0}", r.CategoryInfo));
stringBuilder.AppendLine(
string.Format("+ FullyQualifiedErrorId: {0}", r.FullyQualifiedErrorId));
}
}
}
pipeline.Dispose();
myRunspace.Close();
myRunspace.Dispose();
rsConfig = null;
db.Dispose();
//if pipeline has any errors, not exceptions, the user will be created and must be deleted because of the errors
//the following script deletes the user from Office 365
try
{
string[] script = { @"$pass = convertto-securestring -asplaintext """ + credentialPW + @""" -force",
@"$credential = new-object -typename System.Management.Automation.PSCredential -argumentlist """ + credentialID + @""", $pass",
@"Import-Module MSOnline",
@"Connect-MsolService -Credential $credential",
@"Remove-MsolUser -UserPrincipalName " + userPrincipalName + " -force",
""};
if (File.Exists(common.getPath("scriptDeleteUser.ps1")))
File.Delete(common.getPath("scriptDeleteUser.ps1"));
System.IO.File.WriteAllLines(common.getPath("scriptDeleteUser.ps1"), script);
RunspaceConfiguration config = RunspaceConfiguration.Create();
Runspace runspace = RunspaceFactory.CreateRunspace(config);
runspace.Open();
Command cmd = new Command(common.getPath("scriptDeleteUser.ps1"));
Pipeline pipe = runspace.CreatePipeline();
pipe.Commands.Add(cmd);
pipe.Invoke();
pipe.Dispose();
runspace.Close();
runspace.Dispose();
config = null;
}
catch (Exception ex)
{
common.CreateLog(ex.ToString());
}
return false;
}
pipeline.Dispose();
myRunspace.Close();
myRunspace.Dispose();
rsConfig = null;
db.Dispose();
return true;
}
catch (Exception e)
{
return false;
}
}
In this method, certain PowerShell commands are stored line-by-line a string array. With these commands, the password of the O365 admin user is converted to SecureString and assigned to credential variable together with the username of the user. Secondly, the Microsoft Online Services Module for Windows PowerShell is imported in order to be able call its cmdlets. By using Connect-MsolUser cmdlet we authenticate the admin user and connect to our O365 subscription remotely. New-MsolUser cmdlet automatically creates a new user account in O365 with the properties we have specified. Set-MsolUserLicense cmdlet assigns the necessary licenses (of Exchange Online, Lync Online and SharePoint Online) to the newly created user. For doing it automatically the following method is called:
public string GetO365PlanType(string credentialID, string credentialPW)
{
try
{
string[] psScript = { @"$pass = convertto-securestring -asplaintext """ + credentialPW + @""" -force",
@"$credential = new-object -typename System.Management.Automation.PSCredential -argumentlist """ + credentialID + @""", $pass",
@"Import-Module MSOnline",
@"Connect-MsolService -Credential $credential",
@"Get-MsolAccountSku",
""};
if (File.Exists(common.getPath("scriptGetPlan.ps1")))
File.Delete(common.getPath("scriptGetPlan.ps1"));
System.IO.File.WriteAllLines(common.getPath("scriptGetPlan.ps1"), psScript);
RunspaceConfiguration rsConfig = RunspaceConfiguration.Create();
Runspace myRunspace = RunspaceFactory.CreateRunspace(rsConfig);
myRunspace.Open();
Command command = new Command(common.getPath("scriptGetPlan.ps1"));
Pipeline pipeline = myRunspace.CreatePipeline();
pipeline.Commands.Add(command);
StringBuilder stringBuilder = new StringBuilder();
Collection<PSObject> results = pipeline.Invoke();
// check for errors (non-terminating)
if (pipeline.Error.Count > 0)
{
//iterate over Error PipeLine until end
while (!pipeline.Error.EndOfPipeline)
{
//read one PSObject off the pipeline
var value = pipeline.Error.Read() as PSObject;
if (value != null)
{
//get the ErrorRecord
var r = value.BaseObject as ErrorRecord;
if (r != null)
{
//build whatever kind of message you want
stringBuilder.AppendLine(r.InvocationInfo.MyCommand.Name + " : " + r.Exception.Message);
stringBuilder.AppendLine(r.InvocationInfo.PositionMessage);
stringBuilder.AppendLine(string.Format("+ CategoryInfo: {0}", r.CategoryInfo));
stringBuilder.AppendLine(
string.Format("+ FullyQualifiedErrorId: {0}", r.FullyQualifiedErrorId));
}
}
}
pipeline.Dispose();
myRunspace.Close();
myRunspace.Dispose();
rsConfig = null;
return "false";
}
if (results.Count > 0)
{
stringBuilder = new StringBuilder();
foreach (PSObject obj in results)
{
foreach (PSPropertyInfo psPropertyInfo in obj.Properties)
{
//get the value of plan type for the given account sku
if (psPropertyInfo.Name == "AccountSkuId")
stringBuilder.AppendLine(psPropertyInfo.Value.ToString().Trim());
}
}
pipeline.Dispose();
myRunspace.Close();
myRunspace.Dispose();
rsConfig = null;
string output = stringBuilder.ToString().Trim();
return output;
}
else
return "false";
}
catch (Exception e)
{
return "false";
}
}
This method, logs in to the O365 subscription with the specified credentials and checks and gets the licensing plan configured in the subscription with the use of Get-MsolAccountSku cmdlet. This information is passed to the previous method.
If every step concludes successfully, by using the credentials of your O365 subscription, you are able to automatically create and provision a new user account to O365 at the background while entering the necessary user account information in ASP.NET application hosted in Windows Azure Platform.
If you take a deeper look at the codes, you will notice that you can catch any errors occurring when executing the PowerShell scripts. You may also display them to the user in a form of error messages.
For more detailed information on managing O365 with PowerShell and the complete set of cmdlets in the Microsoft Online Services Module for Windows PowerShell please click here and here.
You may also test the following Proof of Concept application which demonstrates several capabilities in terms of integrating Windows Azure, SQL Azure and Office 365 platforms: http://technostore.cloudapp.net
