// Copyright (c) 2012, Outercurve Foundation. // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: // // - Redistributions of source code must retain the above copyright notice, this // list of conditions and the following disclaimer. // // - Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // - Neither the name of the Outercurve Foundation nor the names of its // contributors may be used to endorse or promote products derived from this // software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using System; using System.IO; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Text; using System.Reflection; using System.Globalization; using System.DirectoryServices; using Microsoft.Win32; using WebsitePanel.Providers; using WebsitePanel.Providers.HostedSolution; using WebsitePanel.Providers.Utils; using WebsitePanel.Server.Utils; using System.Management; using System.Management.Automation; using System.Management.Automation.Runspaces; using Microsoft.Rtc.Management.Hosted; using Microsoft.Rtc.Management.WritableConfig.Settings.Edge; using Microsoft.Rtc.Management.WritableConfig.Settings.SimpleUrl; namespace WebsitePanel.Providers.HostedSolution { public class Lync2010 : HostingServiceProviderBase, ILyncServer { #region Static constructor static Lync2010() { LyncRegistryPath = "SOFTWARE\\Microsoft\\Real-Time Communications"; } internal static string LyncRegistryPath { get; set; } #endregion #region Properties /// /// Pool FQDN /// private string PoolFQDN { get { return ProviderSettings[LyncConstants.PoolFQDN]; } } private string SimpleUrlRoot { get { return ProviderSettings[LyncConstants.SimpleUrlRoot]; } } internal string PrimaryDomainController { get { return ProviderSettings["PrimaryDomainController"]; } } private string RootOU { get { return ProviderSettings["RootOU"]; } } private string RootDomain { get { return ServerSettings.ADRootDomain; } } #endregion #region ILyncServer implementation public string CreateOrganization(string organizationId, string sipDomain, bool enableConferencing, bool enableConferencingVideo, int maxConferenceSize, bool enabledFederation, bool enabledEnterpriseVoice) { return CreateOrganizationInternal(organizationId, sipDomain, enableConferencing, enableConferencingVideo, maxConferenceSize, enabledFederation, enabledEnterpriseVoice); } public bool DeleteOrganization(string organizationId, string sipDomain) { return DeleteOrganizationInternal(organizationId, sipDomain); } public bool CreateUser(string organizationId, string userUpn, LyncUserPlan plan) { return CreateUserInternal(organizationId, userUpn, plan); } public LyncUser GetLyncUserGeneralSettings(string organizationId, string userUpn) { return GetLyncUserGeneralSettingsInternal(organizationId, userUpn); } public bool SetLyncUserPlan(string organizationId, string userUpn, LyncUserPlan plan) { return SetLyncUserPlanInternal(organizationId, userUpn, plan, null); } public bool DeleteUser(string userUpn) { return DeleteUserInternal(userUpn); } public LyncFederationDomain[] GetFederationDomains(string organizationId) { return GetFederationDomainsInternal(organizationId); } public bool AddFederationDomain(string organizationId, string domainName, string proxyFqdn) { return AddFederationDomainInternal(organizationId, domainName, proxyFqdn); } public bool RemoveFederationDomain(string organizationId, string domainName) { return RemoveFederationDomainInternal(organizationId, domainName); } public void ReloadConfiguration() { ReloadConfigurationInternal(); } #endregion #region organization private string CreateOrganizationInternal(string organizationId, string sipDomain, bool enableConferencing, bool enableConferencingVideo, int maxConferenceSize, bool enabledFederation, bool enabledEnterpriseVoice) { HostedSolutionLog.LogStart("CreateOrganizationInternal"); HostedSolutionLog.DebugInfo("organizationId: {0}", organizationId); HostedSolutionLog.DebugInfo("sipDomain: {0}", sipDomain); string TenantId = string.Empty; LyncTransaction transaction = StartTransaction(); Runspace runSpace = null; try { runSpace = OpenRunspace(); // create sip domain Command cmd = new Command("New-CsSipDomain"); cmd.Parameters.Add("Identity", sipDomain); ExecuteShellCommand(runSpace, cmd, false); transaction.RegisterNewSipDomain(sipDomain); //set the msRTCSIP-Domains, TenantID, ObjectID Guid id = Guid.NewGuid(); string path = AddADPrefix(GetOrganizationPath(organizationId)); DirectoryEntry ou = ActiveDirectoryUtils.GetADObject(path); ActiveDirectoryUtils.SetADObjectPropertyValue(ou, "msRTCSIP-Domains", sipDomain); ActiveDirectoryUtils.SetADObjectPropertyValue(ou, "msRTCSIP-TenantId", id); ActiveDirectoryUtils.SetADObjectPropertyValue(ou, "msRTCSIP-ObjectId", id); ou.CommitChanges(); //Create simpleurls CreateSimpleUrl(runSpace, sipDomain, id); transaction.RegisterNewSimpleUrl(sipDomain, id.ToString()); //create conferencing policy cmd = new Command("New-CsConferencingPolicy"); cmd.Parameters.Add("Identity", organizationId); cmd.Parameters.Add("MaxMeetingSize", maxConferenceSize); cmd.Parameters.Add("AllowIPVideo", enableConferencingVideo); ExecuteShellCommand(runSpace, cmd, false); transaction.RegisterNewConferencingPolicy(organizationId); //create external access policy cmd = new Command("New-CsExternalAccessPolicy"); cmd.Parameters.Add("Identity", organizationId); cmd.Parameters.Add("EnableFederationAccess", true); cmd.Parameters.Add("EnableOutsideAccess", true); cmd.Parameters.Add("EnablePublicCloudAccess", false); cmd.Parameters.Add("EnablePublicCloudAudioVideoAccess", false); ExecuteShellCommand(runSpace, cmd, false); transaction.RegisterNewCsExternalAccessPolicy(organizationId); //Enable for federation AllowList allowList = new AllowList(); DomainPattern domain = new DomainPattern(sipDomain); allowList.AllowedDomain.Add(domain); cmd = new Command("Set-CsTenantFederationConfiguration"); cmd.Parameters.Add("Tenant", id); cmd.Parameters.Add("AllowFederatedUsers", true); cmd.Parameters.Add("AllowedDomains", allowList); ExecuteShellCommand(runSpace, cmd, false); //create mobility policy cmd = new Command("New-CsMobilityPolicy"); cmd.Parameters.Add("Identity", organizationId + " EnableOutSideVoice"); cmd.Parameters.Add("EnableMobility", true); cmd.Parameters.Add("EnableOutsideVoice", true); ExecuteShellCommand(runSpace, cmd, false); transaction.RegisterNewCsMobilityPolicy(organizationId + " EnableOutSideVoice"); cmd = new Command("New-CsMobilityPolicy"); cmd.Parameters.Add("Identity", organizationId + " DisableOutSideVoice"); cmd.Parameters.Add("EnableMobility", true); cmd.Parameters.Add("EnableOutsideVoice", false); ExecuteShellCommand(runSpace, cmd, false); transaction.RegisterNewCsMobilityPolicy(organizationId + " DisableOutSideVoice"); cmd = new Command("Invoke-CsManagementStoreReplication"); ExecuteShellCommand(runSpace, cmd, false); TenantId = id.ToString(); } catch (Exception ex) { HostedSolutionLog.LogError("CreateOrganizationInternal", ex); RollbackTransaction(transaction); throw; } finally { CloseRunspace(runSpace); } HostedSolutionLog.LogEnd("CreateOrganizationInternal"); return TenantId; } private bool DeleteOrganizationInternal(string organizationId, string sipDomain) { HostedSolutionLog.LogStart("DeleteOrganizationInternal"); HostedSolutionLog.DebugInfo("organizationId: {0}", organizationId); HostedSolutionLog.DebugInfo("sipDomain: {0}", sipDomain); bool ret = true; Runspace runSpace = null; try { runSpace = OpenRunspace(); Command cmd = new Command("Get-CsTenant"); cmd.Parameters.Add("Identity", GetOrganizationPath(organizationId)); Collection result = ExecuteShellCommand(runSpace, cmd, false); if ((result != null) && (result.Count > 0)) { Guid tenantId = (Guid)GetPSObjectProperty(result[0], "TenantId"); // create sip domain DeleteSipDomain(runSpace, sipDomain); //clear the msRTCSIP-Domains, TenantID, ObjectID string path = AddADPrefix(GetOrganizationPath(organizationId)); DirectoryEntry ou = ActiveDirectoryUtils.GetADObject(path); ActiveDirectoryUtils.ClearADObjectPropertyValue(ou, "msRTCSIP-Domains"); ActiveDirectoryUtils.ClearADObjectPropertyValue(ou, "msRTCSIP-TenantId"); ActiveDirectoryUtils.ClearADObjectPropertyValue(ou, "msRTCSIP-ObjectId"); ou.CommitChanges(); try { DeleteConferencingPolicy(runSpace, organizationId); } catch (Exception) { } try { DeleteExternalAccessPolicy(runSpace, organizationId); } catch (Exception) { } try { DeleteMobilityPolicy(runSpace, organizationId + " EnableOutSideVoice"); } catch (Exception) { } try { DeleteMobilityPolicy(runSpace, organizationId + " DisableOutSideVoice"); } catch (Exception) { } try { DeleteSimpleUrl(runSpace, sipDomain, tenantId); } catch (Exception) { } } cmd = new Command("Invoke-CsManagementStoreReplication"); ExecuteShellCommand(runSpace, cmd, false); } catch (Exception ex) { ret = false; HostedSolutionLog.LogError("DeleteOrganizationInternal", ex); throw; } finally { CloseRunspace(runSpace); } HostedSolutionLog.LogEnd("DeleteOrganizationInternal"); return ret; } #endregion #region Users private bool CreateUserInternal(string organizationId, string userUpn, LyncUserPlan plan) { HostedSolutionLog.LogStart("CreateUserInternal"); HostedSolutionLog.DebugInfo("organizationId: {0}", organizationId); HostedSolutionLog.DebugInfo("userUpn: {0}", userUpn); bool ret = true; Guid tenantId = Guid.Empty; LyncTransaction transaction = StartTransaction(); Runspace runSpace = null; try { runSpace = OpenRunspace(); Command cmd = new Command("Get-CsTenant"); cmd.Parameters.Add("Identity", GetOrganizationPath(organizationId)); Collection result = ExecuteShellCommand(runSpace, cmd, false); if ((result != null) && (result.Count > 0)) { tenantId = (Guid)GetPSObjectProperty(result[0], "TenantId"); //enable for lync cmd = new Command("Enable-CsUser"); cmd.Parameters.Add("Identity", userUpn); cmd.Parameters.Add("RegistrarPool", PoolFQDN); cmd.Parameters.Add("SipAddressType", "UserPrincipalName"); ExecuteShellCommand(runSpace, cmd); transaction.RegisterNewCsUser(userUpn); //set groupingID and tenantID cmd = new Command("Get-CsAdUser"); cmd.Parameters.Add("Identity", userUpn); result = ExecuteShellCommand(runSpace, cmd); string path = AddADPrefix(GetResultObjectDN(result)); DirectoryEntry user = ActiveDirectoryUtils.GetADObject(path); ActiveDirectoryUtils.SetADObjectPropertyValue(user, "msRTCSIP-GroupingID", tenantId); ActiveDirectoryUtils.SetADObjectPropertyValue(user, "msRTCSIP-TenantId", tenantId); string[] tmp = userUpn.Split('@'); if (tmp.Length > 0) { string Url = SimpleUrlRoot + tmp[1]; ActiveDirectoryUtils.SetADObjectPropertyValue(user, "msRTCSIP-BaseSimpleUrl", Url); } user.CommitChanges(); //set-plan SetLyncUserPlanInternal(organizationId, userUpn, plan, runSpace); //initiate addressbook generation cmd = new Command("Update-CsAddressBook"); ExecuteShellCommand(runSpace, cmd, false); //initiate user database replication cmd = new Command("Update-CsUserDatabase"); ExecuteShellCommand(runSpace, cmd, false); } else { ret = false; HostedSolutionLog.LogError("Failed to retrieve tenantID", null); } } catch (Exception ex) { ret = false; HostedSolutionLog.LogError("CreateUserInternal", ex); RollbackTransaction(transaction); throw; } finally { CloseRunspace(runSpace); } HostedSolutionLog.LogEnd("CreateUserInternal"); return ret; } private LyncUser GetLyncUserGeneralSettingsInternal(string organizationId, string userUpn) { HostedSolutionLog.LogStart("GetLyncUserGeneralSettingsInternal"); HostedSolutionLog.DebugInfo("organizationId: {0}", organizationId); HostedSolutionLog.DebugInfo("userUpn: {0}", userUpn); LyncUser lyncUser = new LyncUser(); Runspace runSpace = null; try { runSpace = OpenRunspace(); Command cmd = new Command("Get-CsUser"); cmd.Parameters.Add("Identity", userUpn); Collection result = ExecuteShellCommand(runSpace, cmd); PSObject user = result[0]; lyncUser.DisplayName = (string)GetPSObjectProperty(user, "DisplayName"); lyncUser.PrimaryUri = (string)GetPSObjectProperty(user, "SipAddress"); lyncUser.LineUri = (string)GetPSObjectProperty(user, "LineURI"); } catch (Exception ex) { HostedSolutionLog.LogError("GetLyncUserGeneralSettingsInternal", ex); throw; } finally { CloseRunspace(runSpace); } HostedSolutionLog.LogEnd("GetLyncUserGeneralSettingsInternal"); return lyncUser; } private bool SetLyncUserPlanInternal(string organizationId, string userUpn, LyncUserPlan plan, Runspace runSpace) { HostedSolutionLog.LogStart("SetLyncUserPlanInternal"); HostedSolutionLog.DebugInfo("organizationId: {0}", organizationId); HostedSolutionLog.DebugInfo("userUpn: {0}", userUpn); bool ret = true; bool bCloseRunSpace = false; try { if (runSpace == null) { runSpace = OpenRunspace(); bCloseRunSpace = true; } //CsExternalAccessPolicy Command cmd = new Command("Grant-CsExternalAccessPolicy"); cmd.Parameters.Add("Identity", userUpn); cmd.Parameters.Add("PolicyName", plan.Federation ? organizationId : null); ExecuteShellCommand(runSpace, cmd); //CsConferencingPolicy cmd = new Command("Grant-CsConferencingPolicy"); cmd.Parameters.Add("Identity", userUpn); cmd.Parameters.Add("PolicyName", plan.Federation ? organizationId : null); ExecuteShellCommand(runSpace, cmd); //CsMobilityPolicy cmd = new Command("Grant-CsMobilityPolicy"); cmd.Parameters.Add("Identity", userUpn); if (plan.Mobility) cmd.Parameters.Add("PolicyName", plan.MobilityEnableOutsideVoice ? organizationId + " EnableOutSideVoice" : organizationId + " DisableOutSideVoice"); else cmd.Parameters.Add("PolicyName", null); ExecuteShellCommand(runSpace, cmd); //initiate user database replication cmd = new Command("Update-CsUserDatabase"); ExecuteShellCommand(runSpace, cmd, false); } catch (Exception ex) { ret = false; HostedSolutionLog.LogError("SetLyncUserPlanInternal", ex); throw; } finally { if (bCloseRunSpace) CloseRunspace(runSpace); } HostedSolutionLog.LogEnd("SetLyncUserPlanInternal"); return ret; } private bool DeleteUserInternal(string userUpn) { HostedSolutionLog.LogStart("DeleteUserInternal"); HostedSolutionLog.DebugInfo("userUpn: {0}", userUpn); bool ret = true; Runspace runSpace = null; try { runSpace = OpenRunspace(); //Delete User DeleteUser(runSpace, userUpn); //Clear groupingID and tenantID Command cmd = new Command("Get-CsAdUser"); cmd.Parameters.Add("Identity", userUpn); Collection result = ExecuteShellCommand(runSpace, cmd); string path = AddADPrefix(GetResultObjectDN(result)); DirectoryEntry user = ActiveDirectoryUtils.GetADObject(path); ActiveDirectoryUtils.ClearADObjectPropertyValue(user, "msRTCSIP-GroupingID"); ActiveDirectoryUtils.ClearADObjectPropertyValue(user, "msRTCSIP-TenantId"); ActiveDirectoryUtils.ClearADObjectPropertyValue(user, "msRTCSIP-BaseSimpleUrl"); user.CommitChanges(); //initiate addressbook generation cmd = new Command("Update-CsAddressBook"); ExecuteShellCommand(runSpace, cmd, false); //initiate user database replication cmd = new Command("Update-CsUserDatabase"); ExecuteShellCommand(runSpace, cmd, false); } catch (Exception ex) { ret = false; HostedSolutionLog.LogError("DeleteUserInternal", ex); throw; } finally { CloseRunspace(runSpace); } HostedSolutionLog.LogEnd("DeleteUserInternal"); return ret; } internal void DeleteUser(Runspace runSpace, string userUpn) { HostedSolutionLog.LogStart("DeleteUser"); HostedSolutionLog.DebugInfo("userUpn : {0}", userUpn); Command cmd = new Command("Disable-CsUser"); cmd.Parameters.Add("Identity", userUpn); cmd.Parameters.Add("Confirm", false); ExecuteShellCommand(runSpace, cmd, false); HostedSolutionLog.LogEnd("DeleteUser"); } #endregion #region SipDomains internal void DeleteSipDomain(Runspace runSpace, string id) { HostedSolutionLog.LogStart("DeleteSipDomain"); HostedSolutionLog.DebugInfo("SipDomain : {0}", id); Command cmd = new Command("Remove-CsSipDomain"); cmd.Parameters.Add("Identity", id); cmd.Parameters.Add("Confirm", false); cmd.Parameters.Add("Force", true); ExecuteShellCommand(runSpace, cmd, false); HostedSolutionLog.LogEnd("DeleteSipDomain"); } private void CreateSimpleUrl(Runspace runSpace, string sipDomain, Guid id) { //Create the simpleUrlEntry Command cmd = new Command("Get-CsSipDomain"); Collection sipDomains = ExecuteShellCommand(runSpace, cmd, false); IList SimpleUrls = new List(); foreach (PSObject domain in sipDomains) { string d = (string)GetPSObjectProperty(domain, "Name"); string Url = SimpleUrlRoot + d; //Create the simpleUrlEntry cmd = new Command("New-CsSimpleUrlEntry"); cmd.Parameters.Add("Url", Url); Collection simpleUrlEntry = ExecuteShellCommand(runSpace, cmd, false); //Create the simpleUrl cmd = new Command("New-CsSimpleUrl"); cmd.Parameters.Add("Component", "meet"); cmd.Parameters.Add("Domain", d); cmd.Parameters.Add("SimpleUrl", simpleUrlEntry[0]); cmd.Parameters.Add("ActiveUrl", Url); Collection simpleUrl = ExecuteShellCommand(runSpace, cmd, false); SimpleUrls.Add(simpleUrl[0]); } Hashtable properties = new Hashtable(); properties.Add("Add", SimpleUrls); //PSListModifier cmd = new Command("Set-CsSimpleUrlConfiguration"); cmd.Parameters.Add("Identity", "Global"); cmd.Parameters.Add("Tenant", id); cmd.Parameters.Add("SimpleUrl", SimpleUrls); ExecuteShellCommand(runSpace, cmd, false); } internal void DeleteSimpleUrl(Runspace runSpace, string sipDomain, Guid id) { /* //build the url string Url = SimpleUrlRoot + sipDomain; //Create the simpleUrlEntry Command cmd = new Command("New-CsSimpleUrlEntry"); cmd.Parameters.Add("Url", Url); Collection simpleUrlEntry = ExecuteShellCommand(runSpace, cmd, false); //Create the simpleUrl cmd = new Command("New-CsSimpleUrl"); cmd.Parameters.Add("Component", "meet"); cmd.Parameters.Add("Domain", sipDomain); cmd.Parameters.Add("SimpleUrl", simpleUrlEntry[0]); cmd.Parameters.Add("ActiveUrl", Url); Collection simpleUrl = ExecuteShellCommand(runSpace, cmd, false); //PSListModifier Hashtable properties = new Hashtable(); properties.Add("Remove", simpleUrl[0]); cmd = new Command("Set-CsSimpleUrlConfiguration"); cmd.Parameters.Add("Identity", "Global"); cmd.Parameters.Add("Tenant", id); cmd.Parameters.Add("SimpleUrl", properties); ExecuteShellCommand(runSpace, cmd, false); */ } #endregion #region Policies internal void DeleteConferencingPolicy(Runspace runSpace, string policyName) { HostedSolutionLog.LogStart("DeleteConferencingPolicy"); HostedSolutionLog.DebugInfo("policyName : {0}", policyName); Command cmd = new Command("Remove-CsConferencingPolicy"); cmd.Parameters.Add("Identity", policyName); cmd.Parameters.Add("Confirm", false); cmd.Parameters.Add("Force", true); ExecuteShellCommand(runSpace, cmd, false); HostedSolutionLog.LogEnd("DeleteConferencingPolicy"); } internal void DeleteExternalAccessPolicy(Runspace runSpace, string policyName) { HostedSolutionLog.LogStart("DeleteExternalAccessPolicy"); HostedSolutionLog.DebugInfo("policyName : {0}", policyName); Command cmd = new Command("Remove-CsExternalAccessPolicy"); cmd.Parameters.Add("Identity", policyName); cmd.Parameters.Add("Confirm", false); cmd.Parameters.Add("Force", true); ExecuteShellCommand(runSpace, cmd, false); HostedSolutionLog.LogEnd("DeleteExternalAccessPolicy"); } internal void DeleteMobilityPolicy(Runspace runSpace, string policyName) { HostedSolutionLog.LogStart("DeleteMobilityPolicy"); HostedSolutionLog.DebugInfo("policyName : {0}", policyName); Command cmd = new Command("Remove-CsMobilityPolicy"); cmd.Parameters.Add("Identity", policyName); cmd.Parameters.Add("Confirm", false); cmd.Parameters.Add("Force", true); ExecuteShellCommand(runSpace, cmd, false); HostedSolutionLog.LogEnd("DeleteMobilityPolicy"); } #endregion #region Sytsem Related Methods private void ReloadConfigurationInternal() { HostedSolutionLog.LogStart("ReloadConfigurationInternal"); Runspace runSpace = null; try { runSpace = OpenRunspace(); Command cmd = new Command("Enable-CsComputer"); ExecuteShellCommand(runSpace, cmd, false); } catch (Exception ex) { HostedSolutionLog.LogError("ReloadConfigurationInternal", ex); throw; } finally { CloseRunspace(runSpace); } HostedSolutionLog.LogEnd("ReloadConfigurationInternal"); } #endregion #region Federation Domains private LyncFederationDomain[] GetFederationDomainsInternal(string organizationId) { HostedSolutionLog.LogStart("GetFederationDomainsInternal"); HostedSolutionLog.DebugInfo("organizationId: {0}", organizationId); LyncFederationDomain[] domains = null; Runspace runSpace = null; try { runSpace = OpenRunspace(); Guid tenantId = Guid.Empty; domains = GetFederationDomainsInternal(runSpace, organizationId, ref tenantId); } catch (Exception ex) { HostedSolutionLog.LogError("GetFederationDomainsInternal", ex); throw; } finally { CloseRunspace(runSpace); } HostedSolutionLog.LogEnd("GetFederationDomainsInternal"); return domains; } private LyncFederationDomain[] GetFederationDomainsInternal(Runspace runSpace, string organizationId, ref Guid tenantId) { //Get TenantID List domains = null; Command cmd = new Command("Get-CsTenant"); cmd.Parameters.Add("Identity", GetOrganizationPath(organizationId)); Collection result = ExecuteShellCommand(runSpace, cmd, false); if ((result != null) && (result.Count > 0)) { tenantId = (Guid)GetPSObjectProperty(result[0], "TenantId"); //Get-CSTenantFederationConfiguration (AllowedDomains) cmd = new Command("Get-CsTenantFederationConfiguration"); cmd.Parameters.Add("Tenant", tenantId); result = ExecuteShellCommand(runSpace, cmd, false); if ((result != null) && (result.Count > 0)) { domains = new List(); if (GetPSObjectProperty(result[0], "AllowedDomains").GetType().ToString() == "Microsoft.Rtc.Management.WritableConfig.Settings.Edge.AllowList") { AllowList allowList = (AllowList)GetPSObjectProperty(result[0], "AllowedDomains"); foreach (DomainPattern d in allowList.AllowedDomain) { LyncFederationDomain domain = new LyncFederationDomain(); domain.DomainName = d.Domain.ToString(); domains.Add(domain); } } } } if (domains != null) return domains.ToArray(); else return null; } private bool AddFederationDomainInternal(string organizationId, string domainName, string proxyFqdn) { HostedSolutionLog.LogStart("AddFederationDomainInternal"); HostedSolutionLog.DebugInfo("organizationId: {0}", organizationId); HostedSolutionLog.DebugInfo("domainName: {0}", domainName); Runspace runSpace = null; try { runSpace = OpenRunspace(); Guid tenantId = Guid.Empty; Command cmd = new Command("Get-CsTenant"); cmd.Parameters.Add("Identity", GetOrganizationPath(organizationId)); Collection result = ExecuteShellCommand(runSpace, cmd, false); if ((result != null) && (result.Count > 0)) { tenantId = (Guid)GetPSObjectProperty(result[0], "TenantId"); //Get-CSTenantFederationConfiguration (AllowedDomains) cmd = new Command("Get-CsTenantFederationConfiguration"); cmd.Parameters.Add("Tenant", tenantId); result = ExecuteShellCommand(runSpace, cmd, false); if ((result != null) && (result.Count > 0)) { AllowList allowList = null; if (GetPSObjectProperty(result[0], "AllowedDomains").GetType().ToString() == "Microsoft.Rtc.Management.WritableConfig.Settings.Edge.AllowList") { allowList = (AllowList)GetPSObjectProperty(result[0], "AllowedDomains"); DomainPattern domain = new DomainPattern(domainName); allowList.AllowedDomain.Add(domain); } else { allowList = new AllowList(); DomainPattern domain = new DomainPattern(domainName); allowList.AllowedDomain.Add(domain); } cmd = new Command("Set-CsTenantFederationConfiguration"); cmd.Parameters.Add("Tenant", tenantId); cmd.Parameters.Add("AllowedDomains", allowList); ExecuteShellCommand(runSpace, cmd, false); } } } catch (Exception ex) { HostedSolutionLog.LogError("AddFederationDomainInternal", ex); throw; } finally { CloseRunspace(runSpace); } HostedSolutionLog.LogEnd("AddFederationDomainInternal"); return true; } private bool RemoveFederationDomainInternal(string organizationId, string domainName) { HostedSolutionLog.LogStart("RemoveFederationDomainInternal"); HostedSolutionLog.DebugInfo("organizationId: {0}", organizationId); HostedSolutionLog.DebugInfo("domainName: {0}", domainName); Runspace runSpace = null; try { runSpace = OpenRunspace(); Guid tenantId = Guid.Empty; Command cmd = new Command("Get-CsTenant"); cmd.Parameters.Add("Identity", GetOrganizationPath(organizationId)); Collection result = ExecuteShellCommand(runSpace, cmd, false); if ((result != null) && (result.Count > 0)) { tenantId = (Guid)GetPSObjectProperty(result[0], "TenantId"); //Get-CSTenantFederationConfiguration (AllowedDomains) cmd = new Command("Get-CsTenantFederationConfiguration"); cmd.Parameters.Add("Tenant", tenantId); result = ExecuteShellCommand(runSpace, cmd, false); if ((result != null) && (result.Count > 0)) { AllowList allowList = null; if (GetPSObjectProperty(result[0], "AllowedDomains").GetType().ToString() == "Microsoft.Rtc.Management.WritableConfig.Settings.Edge.AllowList") { allowList = (AllowList)GetPSObjectProperty(result[0], "AllowedDomains"); DomainPattern domain = new DomainPattern(domainName); allowList.AllowedDomain.Remove(domain); } cmd = new Command("Set-CsTenantFederationConfiguration"); cmd.Parameters.Add("Tenant", tenantId); cmd.Parameters.Add("AllowedDomains", allowList); ExecuteShellCommand(runSpace, cmd, false); } } } catch (Exception ex) { HostedSolutionLog.LogError("RemoveFederationDomainInternal", ex); throw; } finally { CloseRunspace(runSpace); } HostedSolutionLog.LogEnd("RemoveFederationDomainInternal"); return true; } #endregion #region PowerShell integration private static InitialSessionState session = null; internal virtual Runspace OpenRunspace() { HostedSolutionLog.LogStart("OpenRunspace"); if (session == null) { session = InitialSessionState.CreateDefault(); session.ImportPSModule(new string[] { "ActiveDirectory", "Lync", "LyncOnline" }); } Runspace runSpace = RunspaceFactory.CreateRunspace(session); // runSpace.Open(); // runSpace.SessionStateProxy.SetVariable("ConfirmPreference", "none"); HostedSolutionLog.LogEnd("OpenRunspace"); return runSpace; } internal void CloseRunspace(Runspace runspace) { try { if (runspace != null && runspace.RunspaceStateInfo.State == RunspaceState.Opened) { runspace.Close(); } } catch (Exception ex) { HostedSolutionLog.LogError("Runspace error", ex); } } internal Collection ExecuteShellCommand(Runspace runSpace, Command cmd) { return ExecuteShellCommand(runSpace, cmd, true); } internal Collection ExecuteShellCommand(Runspace runSpace, Command cmd, bool useDomainController) { object[] errors; return ExecuteShellCommand(runSpace, cmd, useDomainController, out errors); } internal Collection ExecuteShellCommand(Runspace runSpace, Command cmd, out object[] errors) { return ExecuteShellCommand(runSpace, cmd, true, out errors); } internal Collection ExecuteShellCommand(Runspace runSpace, Command cmd, bool useDomainController, out object[] errors) { HostedSolutionLog.LogStart("ExecuteShellCommand"); List errorList = new List(); if (useDomainController) { CommandParameter dc = new CommandParameter("DomainController", PrimaryDomainController); if (!cmd.Parameters.Contains(dc)) { cmd.Parameters.Add(dc); } } HostedSolutionLog.DebugCommand(cmd); Collection results = null; // Create a pipeline Pipeline pipeLine = runSpace.CreatePipeline(); using (pipeLine) { // Add the command pipeLine.Commands.Add(cmd); // Execute the pipeline and save the objects returned. results = pipeLine.Invoke(); // Log out any errors in the pipeline execution // NOTE: These errors are NOT thrown as exceptions! // Be sure to check this to ensure that no errors // happened while executing the command. if (pipeLine.Error != null && pipeLine.Error.Count > 0) { foreach (object item in pipeLine.Error.ReadToEnd()) { errorList.Add(item); string errorMessage = string.Format("Invoke error: {0}", item); HostedSolutionLog.LogWarning(errorMessage); } } } pipeLine = null; errors = errorList.ToArray(); HostedSolutionLog.LogEnd("ExecuteShellCommand"); return results; } internal object GetPSObjectProperty(PSObject obj, string name) { return obj.Members[name].Value; } /// /// Returns the identity of the object from the shell execution result /// /// /// internal string GetResultObjectIdentity(Collection result) { HostedSolutionLog.LogStart("GetResultObjectIdentity"); if (result == null) throw new ArgumentNullException("result", "Execution result is not specified"); if (result.Count < 1) throw new ArgumentException("Execution result is empty", "result"); if (result.Count > 1) throw new ArgumentException("Execution result contains more than one object", "result"); PSMemberInfo info = result[0].Members["Identity"]; if (info == null) throw new ArgumentException("Execution result does not contain Identity property", "result"); string ret = info.Value.ToString(); HostedSolutionLog.LogEnd("GetResultObjectIdentity"); return ret; } internal string GetResultObjectDN(Collection result) { HostedSolutionLog.LogStart("GetResultObjectDN"); if (result == null) throw new ArgumentNullException("result", "Execution result is not specified"); if (result.Count < 1) throw new ArgumentException("Execution result does not contain any object"); if (result.Count > 1) throw new ArgumentException("Execution result contains more than one object"); PSMemberInfo info = result[0].Members["DistinguishedName"]; if (info == null) throw new ArgumentException("Execution result does not contain DistinguishedName property", "result"); string ret = info.Value.ToString(); HostedSolutionLog.LogEnd("GetResultObjectDN"); return ret; } #endregion #region Transactions internal LyncTransaction StartTransaction() { return new LyncTransaction(); } internal void RollbackTransaction(LyncTransaction transaction) { HostedSolutionLog.LogStart("RollbackTransaction"); Runspace runSpace = null; try { runSpace = OpenRunspace(); for (int i = transaction.Actions.Count - 1; i > -1; i--) { //reverse order try { RollbackAction(transaction.Actions[i], runSpace); } catch (Exception ex) { HostedSolutionLog.LogError("Rollback error", ex); } } } catch (Exception ex) { HostedSolutionLog.LogError("Rollback error", ex); } finally { CloseRunspace(runSpace); } HostedSolutionLog.LogEnd("RollbackTransaction"); } private void RollbackAction(TransactionAction action, Runspace runspace) { HostedSolutionLog.LogInfo("Rollback action: {0}", action.ActionType); switch (action.ActionType) { case TransactionAction.TransactionActionTypes.LyncNewSipDomain: DeleteSipDomain(runspace, action.Id); break; //case TransactionAction.TransactionActionTypes.LyncNewSimpleUrl: //DeleteSimpleUrl(runspace, action.Id); //break; case TransactionAction.TransactionActionTypes.LyncNewUser: DeleteUser(runspace, action.Id); break; case TransactionAction.TransactionActionTypes.LyncNewConferencingPolicy: DeleteConferencingPolicy(runspace, action.Id); break; case TransactionAction.TransactionActionTypes.LyncNewExternalAccessPolicy: DeleteExternalAccessPolicy(runspace, action.Id); break; case TransactionAction.TransactionActionTypes.LyncNewMobilityPolicy: DeleteMobilityPolicy(runspace, action.Id); break; } } #endregion #region helpers private string GetOrganizationPath(string organizationId) { StringBuilder sb = new StringBuilder(); // append provider AppendOUPath(sb, organizationId); AppendOUPath(sb, RootOU); AppendDomainPath(sb, RootDomain); return sb.ToString(); } private static void AppendOUPath(StringBuilder sb, string ou) { if (string.IsNullOrEmpty(ou)) return; string path = ou.Replace("/", "\\"); string[] parts = path.Split('\\'); for (int i = parts.Length - 1; i != -1; i--) sb.Append("OU=").Append(parts[i]).Append(","); } private static void AppendDomainPath(StringBuilder sb, string domain) { if (string.IsNullOrEmpty(domain)) return; string[] parts = domain.Split('.'); for (int i = 0; i < parts.Length; i++) { sb.Append("DC=").Append(parts[i]); if (i < (parts.Length - 1)) sb.Append(","); } } internal string AddADPrefix(string path) { string dn = path; if (!dn.ToUpper().StartsWith("LDAP://")) { dn = string.Format("LDAP://{0}/{1}", PrimaryDomainController, dn); } return dn; } #endregion public override bool IsInstalled() { string value = ""; bool bResult = false; RegistryKey root = Registry.LocalMachine; RegistryKey rk = root.OpenSubKey(LyncRegistryPath); if (rk != null) { value = (string)rk.GetValue("ProductVersion", null); if (value == "4.0.7577.0") bResult = true; rk.Close(); } return bResult; } } }