// Copyright (c) 2015, 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.Generic; using System.Linq; using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; using System.Runtime.Remoting; using System.Text; using System.Reflection; using Microsoft.Win32; using WebsitePanel.Providers.HostedSolution; using WebsitePanel.Server.Utils; using WebsitePanel.Providers.Utils; using WebsitePanel.Providers.OS; using WebsitePanel.Providers.Common; using System.Management; using System.Management.Automation; using System.Management.Automation.Runspaces; using System.Collections.ObjectModel; using System.DirectoryServices; using System.Security.Cryptography.X509Certificates; using System.Collections; using System.Xml; using WebsitePanel.EnterpriseServer.Base.RDS; namespace WebsitePanel.Providers.RemoteDesktopServices { public class Windows2012 : HostingServiceProviderBase, IRemoteDesktopServices { #region Constants private const string CapPath = @"RDS:\GatewayServer\CAP"; private const string RapPath = @"RDS:\GatewayServer\RAP"; private const string Computers = "Computers"; private const string AdDcComputers = "Domain Controllers"; private const string Users = "users"; private const string Admins = "Admins"; private const string RdsGroupFormat = "rds-{0}-{1}"; private const string RdsModuleName = "RemoteDesktopServices"; private const string AddNpsString = "netsh nps add np name=\"\"{0}\"\" policysource=\"1\" processingorder=\"{1}\" conditionid=\"0x3d\" conditiondata=\"^5$\" conditionid=\"0x1fb5\" conditiondata=\"{2}\" conditionid=\"0x1e\" conditiondata=\"UserAuthType:(PW|CA)\" profileid=\"0x1005\" profiledata=\"TRUE\" profileid=\"0x100f\" profiledata=\"TRUE\" profileid=\"0x1009\" profiledata=\"0x7\" profileid=\"0x1fe6\" profiledata=\"0x40000000\""; private const string WspAdministratorsGroupDescription = "WSP RDS Collection Adminstrators"; private const string RdsCollectionUsersGroupDescription = "WSP RDS Collection Users"; private const string RdsCollectionComputersGroupDescription = "WSP RDS Collection Computers"; private const string RdsServersOU = "RDSServersOU"; private const string RdsServersRootOU = "RDSRootServersOU"; private const string RDSHelpDeskComputerGroup = "Websitepanel-RDSHelpDesk-Computer"; private const string RDSHelpDeskGroup = "WSP-HelpDeskAdministrators"; private const string RDSHelpDeskGroupDescription = "WSP Help Desk Administrators"; private const string LocalAdministratorsGroupName = "Administrators"; private const string RDSHelpDeskRdRapPolicyName = "RDS-HelpDesk-RDRAP"; private const string RDSHelpDeskRdCapPolicyName = "RDS-HelpDesk-RDCAP"; private const string ScreenSaverGpoKey = @"HKCU\Software\Policies\Microsoft\Windows\Control Panel\Desktop"; private const string ScreenSaverValueName = "ScreenSaveActive"; private const string ScreenSaverTimeoutGpoKey = @"HKCU\Software\Policies\Microsoft\Windows\Control Panel\Desktop"; private const string ScreenSaverTimeoutValueName = "ScreenSaveTimeout"; private const string RemoveRestartGpoKey = @"HKCU\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer"; private const string RemoveRestartGpoValueName = "NoClose"; private const string RemoveRunGpoKey = @"HKCU\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer"; private const string RemoveRunGpoValueName = "NoRun"; private const string DisableTaskManagerGpoKey = @"HKCU\Software\Microsoft\Windows\CurrentVersion\Policies\System"; private const string DisableTaskManagerGpoValueName = "DisableTaskMgr"; private const string HideCDriveGpoKey = @"HKCU\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer"; private const string HideCDriveGpoValueName = "NoDrives"; private const string RDSSessionGpoKey = @"HKCU\Software\Policies\Microsoft\Windows NT\Terminal Services"; private const string RDSSessionGpoValueName = "Shadow"; private const string DisableCmdGpoKey = @"HKCU\Software\Policies\Microsoft\Windows\System"; private const string DisableCmdGpoValueName = "DisableCMD"; private const string DisallowRunParentKey = @"HKCU\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer"; private const string DisallowRunKey = @"HKCU\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer\DisallowRun"; private const string DisallowRunValueName = "DisallowRun"; #endregion #region Properties internal string PrimaryDomainController { get { return ProviderSettings["PrimaryDomainController"]; } } private string RootOU { get { return ProviderSettings["RootOU"]; } } private string ComputersRootOU { get { return ProviderSettings["ComputersRootOU"]; } } private string CentralNpsHost { get { return ProviderSettings["CentralNPS"]; } } private IEnumerable Gateways { get { return ProviderSettings["GWServrsList"].Split(';').Select(x => string.IsNullOrEmpty(x) ? x : x.Trim()); } } private bool CentralNps { get { return Convert.ToBoolean(ProviderSettings["UseCentralNPS"]); } } private string RootDomain { get { return ServerSettings.ADRootDomain; } } private string ConnectionBroker { get { return ProviderSettings["ConnectionBroker"]; } } #endregion #region HostingServiceProvider methods public override bool IsInstalled() { Server.Utils.OS.WindowsVersion version = WebsitePanel.Server.Utils.OS.GetVersion(); return version == WebsitePanel.Server.Utils.OS.WindowsVersion.WindowsServer2012 || version == WebsitePanel.Server.Utils.OS.WindowsVersion.WindowsServer2012R2; } public override string[] Install() { Runspace runSpace = null; PSObject feature = null; try { runSpace = OpenRunspace(); if (!IsFeatureInstalled("Desktop-Experience", runSpace)) { feature = AddFeature(runSpace, "Desktop-Experience", true, false); } if (!IsFeatureInstalled("NET-Framework-Core", runSpace)) { feature = AddFeature(runSpace, "NET-Framework-Core", true, false); } if (!IsFeatureInstalled("NET-Framework-45-Core", runSpace)) { feature = AddFeature(runSpace, "NET-Framework-45-Core", true, false); } } finally { CloseRunspace(runSpace); } return new string[]{}; } public bool CheckRDSServerAvaliable(string hostname) { bool result = false; var ping = new Ping(); var reply = ping.Send(hostname, 1000); if (reply.Status == IPStatus.Success) { result = true; } return result; } #endregion #region RDS Collections public List GetRdsCollectionSessionHosts(string collectionName) { var result = new List(); Runspace runspace = null; try { runspace = OpenRunspace(); Command cmd = new Command("Get-RDSessionHost"); cmd.Parameters.Add("CollectionName", collectionName); cmd.Parameters.Add("ConnectionBroker", ConnectionBroker); object[] errors; var hosts = ExecuteShellCommand(runspace, cmd, false, out errors); foreach (var host in hosts) { result.Add(GetPSObjectProperty(host, "SessionHost").ToString()); } } finally { CloseRunspace(runspace); } return result; } public bool AddRdsServersToDeployment(RdsServer[] servers) { var result = true; Runspace runSpace = null; try { runSpace = OpenRunspace(); foreach (var server in servers) { if (!ExistRdsServerInDeployment(runSpace, server)) { AddRdsServerToDeployment(runSpace, server); } } } catch (Exception e) { result = false; } finally { CloseRunspace(runSpace); } return result; } public bool CreateCollection(string organizationId, RdsCollection collection) { var result = true; Runspace runSpace = null; try { runSpace = OpenRunspace(); var existingServers = GetServersExistingInCollections(runSpace); existingServers = existingServers.Select(x => x.ToUpper()).Intersect(collection.Servers.Select(x => x.FqdName.ToUpper())).ToList(); if (existingServers.Any()) { throw new Exception(string.Format("Server{0} {1} already added to another collection", existingServers.Count == 1 ? "" : "s", string.Join(" ,", existingServers.ToArray()))); } foreach (var server in collection.Servers) { //If server will restart it will not be added to collection //Do not install feature here if (!ExistRdsServerInDeployment(runSpace, server)) { AddRdsServerToDeployment(runSpace, server); } } Command cmd = new Command("New-RDSessionCollection"); cmd.Parameters.Add("CollectionName", collection.Name); cmd.Parameters.Add("SessionHost", collection.Servers.Select(x => x.FqdName).ToArray()); cmd.Parameters.Add("ConnectionBroker", ConnectionBroker); if (!string.IsNullOrEmpty(collection.Description)) { cmd.Parameters.Add("CollectionDescription", collection.Description); } var collectionPs = ExecuteShellCommand(runSpace, cmd, false).FirstOrDefault(); if (collectionPs == null) { throw new Exception("Collection not created"); } EditRdsCollectionSettingsInternal(collection, runSpace); var orgPath = GetOrganizationPath(organizationId); CheckOrCreateAdGroup(GetComputerGroupPath(organizationId, collection.Name), orgPath, GetComputersGroupName(collection.Name), RdsCollectionComputersGroupDescription); CheckOrCreateHelpDeskComputerGroup(); string helpDeskGroupSamAccountName = CheckOrCreateAdGroup(GetHelpDeskGroupPath(RDSHelpDeskGroup), GetRootOUPath(), RDSHelpDeskGroup, RDSHelpDeskGroupDescription); string groupName = GetLocalAdminsGroupName(collection.Name); string groupPath = GetGroupPath(organizationId, collection.Name, groupName); string localAdminsGroupSamAccountName = CheckOrCreateAdGroup(groupPath, GetOrganizationPath(organizationId), groupName, WspAdministratorsGroupDescription); CheckOrCreateAdGroup(GetUsersGroupPath(organizationId, collection.Name), orgPath, GetUsersGroupName(collection.Name), RdsCollectionUsersGroupDescription); var capPolicyName = GetPolicyName(organizationId, collection.Name, RdsPolicyTypes.RdCap); var rapPolicyName = GetPolicyName(organizationId, collection.Name, RdsPolicyTypes.RdRap); foreach (var gateway in Gateways) { CreateHelpDeskRdCapForce(runSpace, gateway); CreateHelpDeskRdRapForce(runSpace, gateway); if (!CentralNps) { CreateRdCapForce(runSpace, gateway, capPolicyName, collection.Name, new List { GetUsersGroupName(collection.Name) }); } CreateRdRapForce(runSpace, gateway, rapPolicyName, collection.Name, new List { GetUsersGroupName(collection.Name) }); } if (CentralNps) { CreateCentralNpsPolicy(runSpace, CentralNpsHost, capPolicyName, collection.Name, organizationId); } //add user group to collection AddUserGroupsToCollection(runSpace, collection.Name, new List { GetUsersGroupName(collection.Name) }); //add session servers to group foreach (var rdsServer in collection.Servers) { MoveSessionHostToCollectionOU(rdsServer.Name, collection.Name, organizationId); AddAdGroupToLocalAdmins(runSpace, rdsServer.FqdName, helpDeskGroupSamAccountName); AddAdGroupToLocalAdmins(runSpace, rdsServer.FqdName, localAdminsGroupSamAccountName); AddComputerToCollectionAdComputerGroup(organizationId, collection.Name, rdsServer); } string collectionComputersPath = GetComputerGroupPath(organizationId, collection.Name); CreatePolicy(runSpace, organizationId, string.Format("{0}-administrators", collection.Name), new DirectoryEntry(GetGroupPath(organizationId, collection.Name, GetLocalAdminsGroupName(collection.Name))), new DirectoryEntry(collectionComputersPath), collection.Name); CreatePolicy(runSpace, organizationId, string.Format("{0}-users", collection.Name), new DirectoryEntry(GetUsersGroupPath(organizationId, collection.Name)) , new DirectoryEntry(collectionComputersPath), collection.Name); CreateHelpDeskPolicy(runSpace, new DirectoryEntry(GetHelpDeskGroupPath(RDSHelpDeskGroup)), new DirectoryEntry(collectionComputersPath), organizationId, collection.Name); } finally { CloseRunspace(runSpace); } return result; } public void EditRdsCollectionSettings(RdsCollection collection) { Runspace runSpace = null; try { runSpace = OpenRunspace(); if (collection.Settings != null) { var errors = EditRdsCollectionSettingsInternal(collection, runSpace); if (errors.Count > 0) { throw new Exception(string.Format("Settings not setted:\r\n{0}", string.Join("r\\n\\", errors.ToArray()))); } } } finally { CloseRunspace(runSpace); } } public List GetRdsUserSessions(string collectionName) { Runspace runSpace = null; var result = new List(); try { runSpace = OpenRunspace(); result = GetRdsUserSessionsInternal(collectionName, runSpace); } finally { CloseRunspace(runSpace); } return result; } public void LogOffRdsUser(string unifiedSessionId, string hostServer) { Runspace runSpace = null; try { runSpace = OpenRunspace(); object[] errors; Command cmd = new Command("Invoke-RDUserLogoff"); cmd.Parameters.Add("HostServer", hostServer); cmd.Parameters.Add("UnifiedSessionID", unifiedSessionId); cmd.Parameters.Add("Force", true); ExecuteShellCommand(runSpace, cmd, false, out errors); if (errors != null && errors.Length > 0) { throw new Exception(string.Join("r\\n\\", errors.Select(e => e.ToString()).ToArray())); } } finally { CloseRunspace(runSpace); } } public List GetServersExistingInCollections() { Runspace runSpace = null; List existingServers = new List(); try { runSpace = OpenRunspace(); existingServers = GetServersExistingInCollections(runSpace); } finally { CloseRunspace(runSpace); } return existingServers; } public RdsCollection GetCollection(string collectionName) { RdsCollection collection =null; Runspace runSpace = null; try { runSpace = OpenRunspace(); collection = runSpace.GetCollection(collectionName, ConnectionBroker, PrimaryDomainController); } finally { CloseRunspace(runSpace); } return collection; } public bool RemoveCollection(string organizationId, string collectionName, List servers) { var result = true; Runspace runSpace = null; try { runSpace = OpenRunspace(); Command cmd = new Command("Remove-RDSessionCollection"); cmd.Parameters.Add("CollectionName", collectionName); cmd.Parameters.Add("ConnectionBroker", ConnectionBroker); cmd.Parameters.Add("Force", true); ExecuteShellCommand(runSpace, cmd, false); DeleteGpo(runSpace, string.Format("{0}-administrators", collectionName)); DeleteGpo(runSpace, string.Format("{0}-users", collectionName)); DeleteHelpDeskPolicy(runSpace, collectionName); var capPolicyName = GetPolicyName(organizationId, collectionName, RdsPolicyTypes.RdCap); var rapPolicyName = GetPolicyName(organizationId, collectionName, RdsPolicyTypes.RdRap); foreach (var gateway in Gateways) { if (!CentralNps) { RemoveRdCap(runSpace, gateway, capPolicyName); } RemoveRdRap(runSpace, gateway, rapPolicyName); } if (CentralNps) { RemoveNpsPolicy(runSpace, CentralNpsHost, capPolicyName); } foreach(var server in servers) { RemoveGroupFromLocalAdmin(server.FqdName, server.Name, GetLocalAdminsGroupName(collectionName), runSpace); RemoveComputerFromCollectionAdComputerGroup(organizationId, collectionName, server); MoveRdsServerToTenantOU(server.Name, organizationId); } ActiveDirectoryUtils.DeleteADObject(GetComputerGroupPath(organizationId, collectionName)); ActiveDirectoryUtils.DeleteADObject(GetUsersGroupPath(organizationId, collectionName)); ActiveDirectoryUtils.DeleteADObject(GetGroupPath(organizationId, collectionName, GetLocalAdminsGroupName(collectionName))); ActiveDirectoryUtils.DeleteADObject(GetCollectionOUPath(organizationId, string.Format("{0}-OU", collectionName))); } catch (Exception e) { result = false; } finally { CloseRunspace(runSpace); } return result; } public bool SetUsersInCollection(string organizationId, string collectionName, List users) { var result = true; try { var usersGroupName = GetUsersGroupName(collectionName); var usersGroupPath = GetUsersGroupPath(organizationId, collectionName); SetUsersToCollectionAdGroup(collectionName, organizationId, users, usersGroupName, usersGroupPath); } catch (Exception e) { result = false; Log.WriteWarning(e.ToString()); } return result; } public void AddSessionHostServerToCollection(string organizationId, string collectionName, RdsServer server) { Runspace runSpace = null; try { runSpace = OpenRunspace(); if (!ExistRdsServerInDeployment(runSpace, server)) { AddRdsServerToDeployment(runSpace, server); } Command cmd = new Command("Add-RDSessionHost"); cmd.Parameters.Add("CollectionName", collectionName); cmd.Parameters.Add("SessionHost", server.FqdName); cmd.Parameters.Add("ConnectionBroker", ConnectionBroker); ExecuteShellCommand(runSpace, cmd, false); CheckOrCreateHelpDeskComputerGroup(); foreach(var gateway in Gateways) { CreateHelpDeskRdCapForce(runSpace, gateway); CreateHelpDeskRdRapForce(runSpace, gateway); } string helpDeskGroupSamAccountName = CheckOrCreateAdGroup(GetHelpDeskGroupPath(RDSHelpDeskGroup), GetRootOUPath(), RDSHelpDeskGroup, RDSHelpDeskGroupDescription); string groupName = GetLocalAdminsGroupName(collectionName); string groupPath = GetGroupPath(organizationId, collectionName, groupName); string localAdminsGroupSamAccountName = CheckOrCreateAdGroup(groupPath, GetOrganizationPath(organizationId), groupName, WspAdministratorsGroupDescription); AddAdGroupToLocalAdmins(runSpace, server.FqdName, LocalAdministratorsGroupName); AddAdGroupToLocalAdmins(runSpace, server.FqdName, helpDeskGroupSamAccountName); AddComputerToCollectionAdComputerGroup(organizationId, collectionName, server); } finally { CloseRunspace(runSpace); } } public void AddSessionHostServersToCollection(string organizationId, string collectionName, List servers) { foreach (var server in servers) { AddSessionHostServerToCollection(organizationId, collectionName, server); } } public void MoveSessionHostsToCollectionOU(List servers, string collectionName, string organizationId) { foreach(var server in servers) { MoveSessionHostToCollectionOU(server.Name, collectionName, organizationId); } } public void RemoveSessionHostServerFromCollection(string organizationId, string collectionName, RdsServer server) { Runspace runSpace = null; try { runSpace = OpenRunspace(); Command cmd = new Command("Remove-RDSessionHost"); cmd.Parameters.Add("ConnectionBroker", ConnectionBroker); cmd.Parameters.Add("SessionHost", server.FqdName); cmd.Parameters.Add("Force", true); ExecuteShellCommand(runSpace, cmd, false); RemoveGroupFromLocalAdmin(server.FqdName, server.Name, GetLocalAdminsGroupName(collectionName), runSpace); RemoveComputerFromCollectionAdComputerGroup(organizationId, collectionName, server); MoveRdsServerToTenantOU(server.Name, organizationId); } finally { CloseRunspace(runSpace); } } public void RemoveSessionHostServersFromCollection(string organizationId, string collectionName, List servers) { foreach (var server in servers) { RemoveSessionHostServerFromCollection(organizationId, collectionName, server); } } #endregion public void SetRDServerNewConnectionAllowed(bool newConnectionAllowed, RdsServer server) { Runspace runSpace = null; try { runSpace = OpenRunspace(); Command cmd = new Command("Set-RDSessionHost"); cmd.Parameters.Add("SessionHost", server.FqdName); cmd.Parameters.Add("NewConnectionAllowed", string.Format("${0}", newConnectionAllowed.ToString())); ExecuteShellCommand(runSpace, cmd, false); } catch (Exception e) { } finally { CloseRunspace(runSpace); } } #region Remote Applications public string[] GetApplicationUsers(string collectionName, string applicationName) { Runspace runspace = null; List result = new List(); try { runspace = OpenRunspace(); Command cmd = new Command("Get-RDRemoteApp"); cmd.Parameters.Add("CollectionName", collectionName); cmd.Parameters.Add("ConnectionBroker", ConnectionBroker); cmd.Parameters.Add("Alias", applicationName); var application = ExecuteShellCommand(runspace, cmd, false).FirstOrDefault(); if (application != null) { var users = (string[])(GetPSObjectProperty(application, "UserGroups")); if (users != null) { result.AddRange(users); } } } finally { CloseRunspace(runspace); } return result.ToArray(); } public bool SetApplicationUsers(string collectionName, RemoteApplication remoteApp, string[] users) { Runspace runspace = null; bool result = true; try { Log.WriteWarning(string.Format("App alias: {0}\r\nCollection Name:{2}\r\nUsers: {1}", remoteApp.Alias, string.Join("; ", users), collectionName)); runspace = OpenRunspace(); Command cmd = new Command("Set-RDRemoteApp"); cmd.Parameters.Add("CollectionName", collectionName); cmd.Parameters.Add("ConnectionBroker", ConnectionBroker); cmd.Parameters.Add("DisplayName", remoteApp.DisplayName); cmd.Parameters.Add("UserGroups", users); cmd.Parameters.Add("Alias", remoteApp.Alias); object[] errors; ExecuteShellCommand(runspace, cmd, false, out errors).FirstOrDefault(); if (errors.Any()) { Log.WriteWarning(string.Format("{0} adding users errors: {1}", remoteApp.DisplayName, string.Join("\r\n", errors.Select(e => e.ToString()).ToArray()))); } else { Log.WriteWarning(string.Format("{0} users added successfully", remoteApp.DisplayName)); } } catch(Exception) { result = false; } finally { CloseRunspace(runspace); } return result; } public List GetAvailableRemoteApplications(string collectionName) { var startApps = new List(); Runspace runSpace = null; try { runSpace = OpenRunspace(); Command cmd = new Command("Get-RDAvailableApp"); cmd.Parameters.Add("CollectionName", collectionName); cmd.Parameters.Add("ConnectionBroker", ConnectionBroker); var remoteApplicationsPS = ExecuteShellCommand(runSpace, cmd, false); if (remoteApplicationsPS != null) { startApps.AddRange(remoteApplicationsPS.Select(CreateStartMenuAppFromPsObject)); } } finally { CloseRunspace(runSpace); } return startApps; } public List GetCollectionRemoteApplications(string collectionName) { var remoteApps = new List(); Runspace runSpace = null; try { runSpace = OpenRunspace(); Command cmd = new Command("Get-RDRemoteApp"); cmd.Parameters.Add("CollectionName", collectionName); cmd.Parameters.Add("ConnectionBroker", ConnectionBroker); var remoteAppsPs = ExecuteShellCommand(runSpace, cmd, false); if (remoteAppsPs != null) { remoteApps.AddRange(remoteAppsPs.Select(CreateRemoteApplicationFromPsObject)); } } finally { CloseRunspace(runSpace); } return remoteApps; } public bool AddRemoteApplication(string collectionName, RemoteApplication remoteApp) { var result = false; Runspace runSpace = null; try { runSpace = OpenRunspace(); Command cmd = new Command("New-RDRemoteApp"); cmd.Parameters.Add("CollectionName", collectionName); cmd.Parameters.Add("ConnectionBroker", ConnectionBroker); cmd.Parameters.Add("Alias", remoteApp.Alias); cmd.Parameters.Add("DisplayName", remoteApp.DisplayName); cmd.Parameters.Add("FilePath", remoteApp.FilePath); cmd.Parameters.Add("ShowInWebAccess", remoteApp.ShowInWebAccess); if (!string.IsNullOrEmpty(remoteApp.RequiredCommandLine)) { cmd.Parameters.Add("CommandLineSetting", "Require"); cmd.Parameters.Add("RequiredCommandLine", remoteApp.RequiredCommandLine); } ExecuteShellCommand(runSpace, cmd, false); result = true; } finally { CloseRunspace(runSpace); } return result; } public bool AddRemoteApplications(string collectionName, List remoteApps) { var result = true; foreach (var remoteApp in remoteApps) { result = AddRemoteApplication(collectionName, remoteApp) && result; } return result; } public bool RemoveRemoteApplication(string collectionName, RemoteApplication remoteApp) { var result = false; Runspace runSpace = null; try { runSpace = OpenRunspace(); Command cmd = new Command("Remove-RDRemoteApp"); cmd.Parameters.Add("CollectionName", collectionName); cmd.Parameters.Add("ConnectionBroker", ConnectionBroker); cmd.Parameters.Add("Alias", remoteApp.Alias); cmd.Parameters.Add("Force", true); ExecuteShellCommand(runSpace, cmd, false); } finally { CloseRunspace(runSpace); } return result; } #endregion #region Gateaway (RD CAP | RD RAP) internal void CreateCentralNpsPolicy(Runspace runSpace, string centralNpshost, string policyName, string collectionName, string organizationId) { var showCmd = new Command("netsh nps show np"); var showResult = ExecuteRemoteShellCommand(runSpace, centralNpshost, showCmd); var processingOrders = showResult.Where(x => Convert.ToString(x).ToLower().Contains("processing order")).Select(x => Convert.ToString(x)); var count = 0; foreach(var processingOrder in processingOrders) { var order = Convert.ToInt32(processingOrder.Remove(0, processingOrder.LastIndexOf("=") + 1).Replace(" ", "")); if (order > count) { count = order; } } var userGroupAd = ActiveDirectoryUtils.GetADObject(GetUsersGroupPath(organizationId, collectionName)); var userGroupSid = (byte[])ActiveDirectoryUtils.GetADObjectProperty(userGroupAd, "objectSid"); var addCmdString = string.Format(AddNpsString, policyName.Replace(" ", "_"), count + 1, ConvertByteToStringSid(userGroupSid)); Command addCmd = new Command(addCmdString); var result = ExecuteRemoteShellCommand(runSpace, centralNpshost, addCmd); } internal void RemoveNpsPolicy(Runspace runSpace, string centralNpshost, string policyName) { var removeCmd = new Command(string.Format("netsh nps delete np {0}", policyName.Replace(" ", "_"))); var removeResult = ExecuteRemoteShellCommand(runSpace, centralNpshost, removeCmd); } internal void CreateRdCapForce(Runspace runSpace, string gatewayHost, string policyName, string collectionName, List groups) { //New-Item -Path "RDS:\GatewayServer\CAP" -Name "Allow Admins" -UserGroups "Administrators@." -AuthMethod 1 //Set-Item -Path "RDS:\GatewayServer\CAP\Allow Admins\SessionTimeout" -Value 480 -SessionTimeoutAction 0 if (ItemExistsRemote(runSpace, gatewayHost, Path.Combine(CapPath, policyName))) { RemoveRdCap(runSpace, gatewayHost, policyName); } var userGroupParametr = string.Format("@({0})",string.Join(",", groups.Select(x => string.Format("\"{0}@{1}\"", x, RootDomain)).ToArray())); Command rdCapCommand = new Command("New-Item"); rdCapCommand.Parameters.Add("Path", string.Format("\"{0}\"", CapPath)); rdCapCommand.Parameters.Add("Name", string.Format("\"{0}\"", policyName)); rdCapCommand.Parameters.Add("UserGroups", userGroupParametr); rdCapCommand.Parameters.Add("AuthMethod", 1); ExecuteRemoteShellCommand(runSpace, gatewayHost, rdCapCommand, RdsModuleName); } private void CreateHelpDeskRdCapForce(Runspace runSpace, string gatewayHost) { if (ItemExistsRemote(runSpace, gatewayHost, Path.Combine(CapPath, RDSHelpDeskRdCapPolicyName))) { return; } var userGroupParameter = string.Format("@({0})", string.Format("\"{0}@{1}\"", RDSHelpDeskGroup, RootDomain)); Command rdCapCommand = new Command("New-Item"); rdCapCommand.Parameters.Add("Path", string.Format("\"{0}\"", CapPath)); rdCapCommand.Parameters.Add("Name", string.Format("\"{0}\"", RDSHelpDeskRdCapPolicyName)); rdCapCommand.Parameters.Add("UserGroups", userGroupParameter); rdCapCommand.Parameters.Add("AuthMethod", 1); ExecuteRemoteShellCommand(runSpace, gatewayHost, rdCapCommand, RdsModuleName); } private void CreateHelpDeskRdRapForce(Runspace runSpace, string gatewayHost) { if (ItemExistsRemote(runSpace, gatewayHost, Path.Combine(RapPath, RDSHelpDeskRdRapPolicyName))) { return; } var userGroupParameter = string.Format("@({0})", string.Format("\"{0}@{1}\"", RDSHelpDeskGroup, RootDomain)); var computerGroupParameter = string.Format("\"{0}@{1}\"", RDSHelpDeskComputerGroup, RootDomain); Command rdRapCommand = new Command("New-Item"); rdRapCommand.Parameters.Add("Path", string.Format("\"{0}\"", RapPath)); rdRapCommand.Parameters.Add("Name", string.Format("\"{0}\"", RDSHelpDeskRdRapPolicyName)); rdRapCommand.Parameters.Add("UserGroups", userGroupParameter); rdRapCommand.Parameters.Add("ComputerGroupType", 1); rdRapCommand.Parameters.Add("ComputerGroup", computerGroupParameter); object[] errors; for (int i = 0; i < 3; i++) { ExecuteRemoteShellCommand(runSpace, gatewayHost, rdRapCommand, out errors, RdsModuleName); if (errors == null || !errors.Any()) { Log.WriteWarning("RD RAP Added Successfully"); break; } else { Log.WriteWarning(string.Join("\r\n", errors.Select(e => e.ToString()).ToArray())); } } } internal void RemoveRdCap(Runspace runSpace, string gatewayHost, string name) { RemoveItemRemote(runSpace, gatewayHost, string.Format(@"{0}\{1}", CapPath, name), RdsModuleName); } internal void CreateRdRapForce(Runspace runSpace, string gatewayHost, string policyName, string collectionName, List groups) { //New-Item -Path "RDS:\GatewayServer\RAP" -Name "Allow Connections To Everywhere" -UserGroups "Administrators@." -ComputerGroupType 1 //Set-Item -Path "RDS:\GatewayServer\RAP\Allow Connections To Everywhere\PortNumbers" -Value 3389,3390 if (ItemExistsRemote(runSpace, gatewayHost, Path.Combine(RapPath, policyName))) { RemoveRdRap(runSpace, gatewayHost, policyName); } var userGroupParametr = string.Format("@({0})", string.Join(",", groups.Select(x => string.Format("\"{0}@{1}\"", x, RootDomain)).ToArray())); var computerGroupParametr = string.Format("\"{0}@{1}\"", GetComputersGroupName(collectionName), RootDomain); Command rdRapCommand = new Command("New-Item"); rdRapCommand.Parameters.Add("Path", string.Format("\"{0}\"", RapPath)); rdRapCommand.Parameters.Add("Name", string.Format("\"{0}\"", policyName)); rdRapCommand.Parameters.Add("UserGroups", userGroupParametr); rdRapCommand.Parameters.Add("ComputerGroupType", 1); rdRapCommand.Parameters.Add("ComputerGroup", computerGroupParametr); object[] errors; for (int i = 0; i < 3; i++) { Log.WriteWarning(string.Format("Adding RD RAP ... {0}\r\nGateway Host\t{1}\r\nUser Group\t{2}\r\nComputer Group\t{3}", i + 1, gatewayHost, userGroupParametr, computerGroupParametr)); ExecuteRemoteShellCommand(runSpace, gatewayHost, rdRapCommand, out errors, RdsModuleName); if (errors == null || !errors.Any()) { Log.WriteWarning("RD RAP Added Successfully"); break; } else { Log.WriteWarning(string.Join("\r\n", errors.Select(e => e.ToString()).ToArray())); } } } internal void RemoveRdRap(Runspace runSpace, string gatewayHost, string name) { RemoveItemRemote(runSpace, gatewayHost, string.Format(@"{0}\{1}", RapPath, name), RdsModuleName); } #endregion #region Local Admins public void SaveRdsCollectionLocalAdmins(List users, List hosts, string collectionName, string organizationId) { Runspace runspace = null; try { runspace = OpenRunspace(); var index = ServerSettings.ADRootDomain.LastIndexOf("."); var domainName = ServerSettings.ADRootDomain; string groupName = GetLocalAdminsGroupName(collectionName); string groupPath = GetGroupPath(organizationId, collectionName, groupName); string helpDeskGroupSamAccountName = CheckOrCreateAdGroup(GetHelpDeskGroupPath(RDSHelpDeskGroup), GetRootOUPath(), RDSHelpDeskGroup, RDSHelpDeskGroupDescription); string localAdminsGroupSamAccountName = CheckOrCreateAdGroup(groupPath, GetOrganizationPath(organizationId), groupName, WspAdministratorsGroupDescription); if (index > 0) { domainName = ServerSettings.ADRootDomain.Substring(0, index); } foreach (var hostName in hosts) { AddAdGroupToLocalAdmins(runspace, hostName, helpDeskGroupSamAccountName); AddAdGroupToLocalAdmins(runspace, hostName, localAdminsGroupSamAccountName); SetUsersToCollectionAdGroup(collectionName, organizationId, users, GetLocalAdminsGroupName(collectionName), groupPath); } } finally { CloseRunspace(runspace); } } public List GetRdsCollectionLocalAdmins(string organizationId, string collectionName) { string groupName = GetLocalAdminsGroupName(collectionName); return GetUsersToCollectionAdGroup(collectionName, groupName, organizationId); } private void RemoveGroupFromLocalAdmin(string fqdnName, string hostName, string groupName, Runspace runspace) { var scripts = new List { string.Format("$GroupObj = [ADSI]\"WinNT://{0}/{1}\"", hostName, LocalAdministratorsGroupName), string.Format("$GroupObj.Remove(\"WinNT://{0}/{1}\")", ServerSettings.ADRootDomain, RDSHelpDeskGroup), string.Format("$GroupObj.Remove(\"WinNT://{0}/{1}\")", ServerSettings.ADRootDomain, groupName) }; object[] errors = null; ExecuteRemoteShellCommand(runspace, fqdnName, scripts, out errors); } #endregion #region GPO public void ApplyGPO(string organizationId, string collectionName, RdsServerSettings serverSettings) { string administratorsGpo = string.Format("{0}-administrators", collectionName); string usersGpo = string.Format("{0}-users", collectionName); Runspace runspace = null; try { runspace = OpenRunspace(); string collectionComputersPath = GetComputerGroupPath(organizationId, collectionName); CreatePolicy(runspace, organizationId, string.Format("{0}-administrators", collectionName), new DirectoryEntry(GetGroupPath(organizationId, collectionName, GetLocalAdminsGroupName(collectionName))), new DirectoryEntry(collectionComputersPath), collectionName); CreatePolicy(runspace, organizationId, string.Format("{0}-users", collectionName), new DirectoryEntry(GetUsersGroupPath(organizationId, collectionName)), new DirectoryEntry(collectionComputersPath), collectionName); CreateHelpDeskPolicy(runspace, new DirectoryEntry(GetHelpDeskGroupPath(RDSHelpDeskGroup)), new DirectoryEntry(collectionComputersPath), organizationId, collectionName); RemoveRegistryValue(runspace, ScreenSaverGpoKey, administratorsGpo); RemoveRegistryValue(runspace, ScreenSaverGpoKey, usersGpo); RemoveRegistryValue(runspace, RemoveRestartGpoKey, administratorsGpo); RemoveRegistryValue(runspace, RemoveRestartGpoKey, usersGpo); RemoveRegistryValue(runspace, DisableTaskManagerGpoKey, administratorsGpo); RemoveRegistryValue(runspace, DisableTaskManagerGpoKey, usersGpo); RemoveRegistryValue(runspace, DisableCmdGpoKey, usersGpo); RemoveRegistryValue(runspace, DisableCmdGpoKey, administratorsGpo); RemoveRegistryValue(runspace, DisallowRunKey, usersGpo); RemoveRegistryValue(runspace, DisallowRunParentKey, usersGpo); RemoveRegistryValue(runspace, DisallowRunKey, administratorsGpo); RemoveRegistryValue(runspace, DisallowRunParentKey, administratorsGpo); var setting = serverSettings.Settings.FirstOrDefault(s => s.PropertyName.Equals(RdsServerSettings.SCREEN_SAVER_DISABLED)); SetRegistryValue(setting, runspace, ScreenSaverGpoKey, administratorsGpo, usersGpo, ScreenSaverValueName, "0", "string"); setting = serverSettings.Settings.FirstOrDefault(s => s.PropertyName.Equals(RdsServerSettings.REMOVE_SHUTDOWN_RESTART)); SetRegistryValue(setting, runspace, RemoveRestartGpoKey, administratorsGpo, usersGpo, RemoveRestartGpoValueName, "1", "DWord"); setting = serverSettings.Settings.FirstOrDefault(s => s.PropertyName.Equals(RdsServerSettings.REMOVE_RUN_COMMAND)); SetRegistryValue(setting, runspace, RemoveRunGpoKey, administratorsGpo, usersGpo, RemoveRunGpoValueName, "1", "DWord"); setting = serverSettings.Settings.FirstOrDefault(s => s.PropertyName.Equals(RdsServerSettings.DISABLE_TASK_MANAGER)); SetRegistryValue(setting, runspace, DisableTaskManagerGpoKey, administratorsGpo, usersGpo, DisableTaskManagerGpoValueName, "1", "DWord"); setting = serverSettings.Settings.FirstOrDefault(s => s.PropertyName.Equals(RdsServerSettings.HIDE_C_DRIVE)); SetRegistryValue(setting, runspace, HideCDriveGpoKey, administratorsGpo, usersGpo, HideCDriveGpoValueName, "4", "DWord"); setting = serverSettings.Settings.FirstOrDefault(s => s.PropertyName.Equals(RdsServerSettings.DISABLE_CMD)); SetRegistryValue(setting, runspace, DisableCmdGpoKey, administratorsGpo, usersGpo, DisableCmdGpoValueName, "1", "DWord"); setting = serverSettings.Settings.FirstOrDefault(s => s.PropertyName.Equals(RdsServerSettings.LOCK_SCREEN_TIMEOUT)); double result; if (setting != null && !string.IsNullOrEmpty(setting.PropertyValue) && double.TryParse(setting.PropertyValue, out result)) { SetRegistryValue(setting, runspace, ScreenSaverTimeoutGpoKey, administratorsGpo, usersGpo, ScreenSaverTimeoutValueName, setting.PropertyValue, "string"); } SetRdsSessionHostPermissions(runspace, serverSettings, usersGpo, administratorsGpo); SetPowershellPermissions(runspace, serverSettings.Settings.FirstOrDefault(s => s.PropertyName.Equals(RdsServerSettings.REMOVE_POWERSHELL_COMMAND)), usersGpo, administratorsGpo); } finally { CloseRunspace(runspace); } } private void CheckPolicySecurityFiltering(Runspace runspace, string gpoName, DirectoryEntry collectionComputersEntry) { var scripts = new List{ string.Format("Get-GPPermissions -Name {0} -TargetName {1} -TargetType group", gpoName, string.Format("'{0}'", ActiveDirectoryUtils.GetADObjectProperty(collectionComputersEntry, "sAMAccountName").ToString())) }; object[] errors = null; ExecuteRemoteShellCommand(runspace, PrimaryDomainController, scripts, out errors); if (errors != null && errors.Any()) { scripts = new List{ string.Format("Set-GPPermissions -Name {0} -PermissionLevel gpoapply -TargetName {1} -TargetType group", gpoName, string.Format("'{0}'", ActiveDirectoryUtils.GetADObjectProperty(collectionComputersEntry, "sAMAccountName").ToString())) }; } ExecuteRemoteShellCommand(runspace, PrimaryDomainController, scripts, out errors); } private void SetPowershellPermissions(Runspace runspace, RdsServerSetting setting, string usersGpo, string administratorsGpo) { if (setting != null) { SetRegistryValue(setting, runspace, DisallowRunParentKey, administratorsGpo, usersGpo, DisallowRunValueName, "1", "Dword"); if (setting.ApplyAdministrators) { SetRegistryValue(runspace, DisallowRunKey, administratorsGpo, "powershell.exe", "string"); } if (setting.ApplyUsers) { SetRegistryValue(runspace, DisallowRunKey, usersGpo, "powershell.exe", "string"); } } } private void SetRdsSessionHostPermissions(Runspace runspace, RdsServerSettings settings, string usersGpo, string administratorsGpo) { var viewSetting = settings.Settings.FirstOrDefault(s => s.PropertyName.Equals(RdsServerSettings.RDS_VIEW_WITHOUT_PERMISSION)); var controlSetting = settings.Settings.FirstOrDefault(s => s.PropertyName.Equals(RdsServerSettings.RDS_CONTROL_WITHOUT_PERMISSION)); if (viewSetting == null || controlSetting == null) { return; } if (controlSetting.ApplyUsers) { SetRegistryValue(runspace, RDSSessionGpoKey, usersGpo, "2", RDSSessionGpoValueName, "DWord"); } else if (viewSetting.ApplyUsers) { SetRegistryValue(runspace, RDSSessionGpoKey, usersGpo, "4", RDSSessionGpoValueName, "DWord"); } else { SetRegistryValue(runspace, RDSSessionGpoKey, usersGpo, "3", RDSSessionGpoValueName, "DWord"); } if (controlSetting.ApplyAdministrators) { SetRegistryValue(runspace, RDSSessionGpoKey, administratorsGpo, "2", RDSSessionGpoValueName, "DWord"); } else if (viewSetting.ApplyAdministrators) { SetRegistryValue(runspace, RDSSessionGpoKey, administratorsGpo, "4", RDSSessionGpoValueName, "DWord"); } else { SetRegistryValue(runspace, RDSSessionGpoKey, administratorsGpo, "3", RDSSessionGpoValueName, "DWord"); } } private void RemoveRegistryValue(Runspace runspace, string key, string gpoName) { Command cmd = new Command("Remove-GPRegistryValue"); cmd.Parameters.Add("Name", gpoName); cmd.Parameters.Add("Key", string.Format("\"{0}\"", key)); Collection result = ExecuteRemoteShellCommand(runspace, PrimaryDomainController, cmd); } private void SetRegistryValue(RdsServerSetting setting, Runspace runspace, string key, string administratorsGpo, string usersGpo, string valueName, string value, string type) { if (setting == null) { return; } if (setting.ApplyAdministrators) { SetRegistryValue(runspace, key, administratorsGpo, value, valueName, type); } if (setting.ApplyUsers) { SetRegistryValue(runspace, key, usersGpo, value, valueName, type); } } private void SetRegistryValue(Runspace runspace, string key, string gpoName, string value, string type) { Command cmd = new Command("Set-GPRegistryValue"); cmd.Parameters.Add("Name", gpoName); cmd.Parameters.Add("Key", string.Format("\"{0}\"", key)); cmd.Parameters.Add("Value", value); cmd.Parameters.Add("Type", type); Collection result = ExecuteRemoteShellCommand(runspace, PrimaryDomainController, cmd); } private void SetRegistryValue(Runspace runspace, string key, string gpoName, string value, string valueName, string type) { Command cmd = new Command("Set-GPRegistryValue"); cmd.Parameters.Add("Name", gpoName); cmd.Parameters.Add("Key", string.Format("\"{0}\"", key)); cmd.Parameters.Add("Value", value); cmd.Parameters.Add("ValueName", valueName); cmd.Parameters.Add("Type", type); Collection result = ExecuteRemoteShellCommand(runspace, PrimaryDomainController, cmd); } private void CreateHelpDeskPolicy(Runspace runspace, DirectoryEntry entry, DirectoryEntry collectionComputersEntry, string organizationId, string collectionName) { string gpoName = string.Format("{0}-HelpDesk", collectionName); string gpoId = GetPolicyId(runspace, gpoName); if (string.IsNullOrEmpty(gpoId)) { gpoId = CreateAndLinkPolicy(runspace, gpoName, organizationId, collectionName); SetPolicyPermissions(runspace, gpoName, entry, collectionComputersEntry); SetRegistryValue(runspace, RDSSessionGpoKey, gpoName, "2", RDSSessionGpoValueName, "DWord"); } else { CheckPolicySecurityFiltering(runspace, gpoName, collectionComputersEntry); } } private string CreatePolicy(Runspace runspace, string organizationId, string gpoName, DirectoryEntry entry, DirectoryEntry collectionComputersEntry, string collectionName) { string gpoId = GetPolicyId(runspace, gpoName); if (string.IsNullOrEmpty(gpoId)) { gpoId = CreateAndLinkPolicy(runspace, gpoName, organizationId, collectionName); SetPolicyPermissions(runspace, gpoName, entry, collectionComputersEntry); } else { CheckPolicySecurityFiltering(runspace, gpoName, collectionComputersEntry); } return gpoId; } private void DeleteHelpDeskPolicy(Runspace runspace, string collectionName) { string gpoName = string.Format("{0}-HelpDesk", collectionName); DeleteGpo(runspace, gpoName); } private void DeleteGpo(Runspace runspace, string gpoName) { Command cmd = new Command("Remove-GPO"); cmd.Parameters.Add("Name", gpoName); Collection result = ExecuteRemoteShellCommand(runspace, PrimaryDomainController, cmd); } private void SetPolicyPermissions(Runspace runspace, string gpoName, DirectoryEntry entry, DirectoryEntry collectionComputersEntry) { var scripts = new List { string.Format("Set-GPPermissions -Name {0} -Replace -PermissionLevel None -TargetName 'Authenticated Users' -TargetType group", gpoName), string.Format("Set-GPPermissions -Name {0} -PermissionLevel gpoapply -TargetName {1} -TargetType group", gpoName, string.Format("'{0}'", ActiveDirectoryUtils.GetADObjectProperty(entry, "sAMAccountName").ToString())), string.Format("Set-GPPermissions -Name {0} -PermissionLevel gpoapply -TargetName {1} -TargetType group", gpoName, string.Format("'{0}'", ActiveDirectoryUtils.GetADObjectProperty(collectionComputersEntry, "sAMAccountName").ToString())) }; object[] errors = null; ExecuteRemoteShellCommand(runspace, PrimaryDomainController, scripts, out errors); } private string CreateAndLinkPolicy(Runspace runspace, string gpoName, string organizationId, string collectionName) { string gpoId = null; try { var entry = new DirectoryEntry(GetOrganizationPath(organizationId)); var distinguishedName = string.Format("\"{0}\"", ActiveDirectoryUtils.GetADObjectProperty(entry, "DistinguishedName")); Command cmd = new Command("New-GPO"); cmd.Parameters.Add("Name", gpoName); Collection result = ExecuteRemoteShellCommand(runspace, PrimaryDomainController, cmd); if (result != null && result.Count > 0) { PSObject gpo = result[0]; gpoId = ((Guid)GetPSObjectProperty(gpo, "Id")).ToString("B"); } cmd = new Command("New-GPLink"); cmd.Parameters.Add("Name", gpoName); cmd.Parameters.Add("Target", distinguishedName); ExecuteRemoteShellCommand(runspace, PrimaryDomainController, cmd); } catch (Exception) { gpoId = null; throw; } return gpoId; } private string GetPolicyId(Runspace runspace, string gpoName) { string gpoId = null; try { Command cmd = new Command("Get-GPO"); cmd.Parameters.Add("Name", gpoName); Collection result = ExecuteRemoteShellCommand(runspace, PrimaryDomainController, cmd); if (result != null && result.Count > 0) { PSObject gpo = result[0]; gpoId = ((Guid)GetPSObjectProperty(gpo, "Id")).ToString("B"); } } catch (Exception) { gpoId = null; throw; } return gpoId; } #endregion #region Shadow Session public void ShadowSession(string sessionId, bool control) { Runspace runspace = null; try { runspace = OpenRunspace(); var scripts = new List { string.Format("mstsc /Shadow:{0}{1}", sessionId, control ? " /Control" : "") }; object[] errors = null; ExecuteShellCommand(runspace, scripts, out errors); } finally { CloseRunspace(runspace); } } #endregion #region RDS Help Desk private string GetHelpDeskGroupPath(string groupName) { StringBuilder sb = new StringBuilder(); AppendProtocol(sb); AppendDomainController(sb); AppendCNPath(sb, groupName); AppendOUPath(sb, RootOU); AppendDomainPath(sb, RootDomain); return sb.ToString(); } private void CheckOrCreateHelpDeskComputerGroup() { if (!ActiveDirectoryUtils.AdObjectExists(GetHelpDeskGroupPath(RDSHelpDeskComputerGroup))) { ActiveDirectoryUtils.CreateGroup(GetRootOUPath(), RDSHelpDeskComputerGroup); } } private string CheckOrCreateAdGroup(string groupPath, string rootPath, string groupName, string description) { DirectoryEntry groupEntry = null; if (!ActiveDirectoryUtils.AdObjectExists(groupPath)) { ActiveDirectoryUtils.CreateGroup(rootPath, groupName); groupEntry = ActiveDirectoryUtils.GetADObject(groupPath); if (groupEntry.Properties.Contains("Description")) { groupEntry.Properties["Description"][0] = description; } else { groupEntry.Properties["Description"].Add(description); } groupEntry.CommitChanges(); } if (groupEntry == null) { groupEntry = ActiveDirectoryUtils.GetADObject(groupPath); } return ActiveDirectoryUtils.GetADObjectProperty(groupEntry, "sAMAccountName").ToString(); } private void AddAdGroupToLocalAdmins(Runspace runspace, string hostName, string samAccountName) { var scripts = new List { string.Format("$GroupObj = [ADSI]\"WinNT://{0}/{1}\"", hostName, LocalAdministratorsGroupName), string.Format("$GroupObj.Add(\"WinNT://{0}/{1}\")", ServerSettings.ADRootDomain, samAccountName) }; object[] errors = null; ExecuteRemoteShellCommand(runspace, hostName, scripts, out errors); } #endregion #region SSL public void InstallCertificate(byte[] certificate, string password, List hostNames) { Runspace runspace = null; try { var guid = Guid.NewGuid(); var x509Cert = new X509Certificate2(certificate, password, X509KeyStorageFlags.Exportable); var filePath = SaveCertificate(certificate, guid); runspace = OpenRunspace(); foreach (var hostName in hostNames) { var destinationPath = string.Format("\\\\{0}\\c$\\{1}.pfx", hostName, guid); var errors = CopyCertificateFile(runspace, filePath, destinationPath); if (!errors.Any()) { RemoveCertificate(runspace, hostName, x509Cert.Thumbprint); errors = ImportCertificate(runspace, hostName, password, string.Format("c:\\{0}.pfx", guid), x509Cert.Thumbprint); } DeleteCertificateFile(destinationPath, runspace); if (errors.Any()) { Log.WriteWarning(string.Join("\r\n", errors.Select(e => e.ToString()).ToArray())); throw new Exception(string.Join("\r\n", errors.Select(e => e.ToString()).ToArray())); } } if (File.Exists(filePath)) { File.Delete(filePath); } } finally { CloseRunspace(runspace); } } private void RemoveCertificate(Runspace runspace, string hostName, string thumbprint) { var scripts = new List { string.Format("Remove-Item -Path cert:\\LocalMachine\\My\\{0}", thumbprint) }; object[] errors = null; ExecuteRemoteShellCommand(runspace, hostName, scripts, out errors); } private object[] ImportCertificate(Runspace runspace, string hostName, string password, string certificatePath, string thumbprint) { var scripts = new List { string.Format("$mypwd = ConvertTo-SecureString -String {0} -Force –AsPlainText", password), string.Format("Import-PfxCertificate –FilePath \"{0}\" cert:\\localMachine\\my -Password $mypwd", certificatePath), string.Format("$cert = Get-Item cert:\\LocalMachine\\My\\{0}", thumbprint), string.Format("$path = (Get-WmiObject -class \"Win32_TSGeneralSetting\" -Namespace root\\cimv2\\terminalservices -Filter \"TerminalName='RDP-tcp'\").__path"), string.Format("Set-WmiInstance -Path $path -argument @{0}", string.Format("{{SSLCertificateSHA1Hash=\"{0}\"}}", thumbprint)) }; object[] errors = null; ExecuteRemoteShellCommand(runspace, hostName, scripts, out errors); return errors; } private string SaveCertificate(byte[] certificate, Guid guid) { var filePath = string.Format("{0}{1}.pfx", Path.GetTempPath(), guid); if (File.Exists(filePath)) { File.Delete(filePath); } File.WriteAllBytes(filePath, certificate); return filePath; } private object[] CopyCertificateFile(Runspace runspace, string filePath, string destinationPath) { var scripts = new List { string.Format("Copy-Item \"{0}\" -Destination \"{1}\" -Force", filePath, destinationPath) }; object[] errors = null; ExecuteShellCommand(runspace, scripts, out errors); return errors; } private object[] DeleteCertificateFile(string destinationPath, Runspace runspace) { var scripts = new List { string.Format("Remove-Item -Path \"{0}\" -Force", destinationPath) }; object[] errors = null; ExecuteShellCommand(runspace, scripts, out errors); return errors; } #endregion #region Import Collection public ImportedRdsCollection GetExistingCollection(string collectionName) { Runspace runspace = null; ImportedRdsCollection result; try { runspace = OpenRunspace(); var collection = runspace.GetCollection(collectionName, ConnectionBroker, PrimaryDomainController); result = new ImportedRdsCollection { CollectionName = collection.Name, Description = collection.Description }; if (collection == null) { throw new NullReferenceException(string.Format("Collection \"{0}\" not found", collectionName)); } object[] errors; result.CollectionSettings = runspace.GetCollectionSettings(collectionName, ConnectionBroker, PrimaryDomainController, out errors).ToList(); if (errors.Any()) { throw new Exception(string.Format("Collection not imported:\r\n{0}", string.Join("r\\n\\", errors.Select(e => e.ToString()).ToArray()))); } result.SessionHosts = runspace.GetSessionHosts(collectionName, ConnectionBroker, PrimaryDomainController, out errors); if (errors.Any()) { throw new Exception(string.Format("Collection not imported:\r\n{0}", string.Join("r\\n\\", errors.Select(e => e.ToString()).ToArray()))); } result.UserGroups = runspace.GetCollectionUserGroups(collectionName, ConnectionBroker, PrimaryDomainController, out errors).ToList(); if (errors.Any()) { throw new Exception(string.Format("Collection not imported:\r\n{0}", string.Join("r\\n\\", errors.Select(e => e.ToString()).ToArray()))); } } finally { CloseRunspace(runspace); } return result; } public void ImportCollection(string organizationId, RdsCollection collection, List users) { Runspace runSpace = null; try { Log.WriteStart(string.Format("Starting collection import: {0}", collection.Name)); runSpace = OpenRunspace(); var orgPath = GetOrganizationPath(organizationId); CheckOrCreateAdGroup(GetComputerGroupPath(organizationId, collection.Name), orgPath, GetComputersGroupName(collection.Name), RdsCollectionComputersGroupDescription); CheckOrCreateHelpDeskComputerGroup(); string helpDeskGroupSamAccountName = CheckOrCreateAdGroup(GetHelpDeskGroupPath(RDSHelpDeskGroup), GetRootOUPath(), RDSHelpDeskGroup, RDSHelpDeskGroupDescription); string groupName = GetLocalAdminsGroupName(collection.Name); string groupPath = GetGroupPath(organizationId, collection.Name, groupName); string localAdminsGroupSamAccountName = CheckOrCreateAdGroup(groupPath, GetOrganizationPath(organizationId), groupName, WspAdministratorsGroupDescription); CheckOrCreateAdGroup(GetUsersGroupPath(organizationId, collection.Name), orgPath, GetUsersGroupName(collection.Name), RdsCollectionUsersGroupDescription); var capPolicyName = GetPolicyName(organizationId, collection.Name, RdsPolicyTypes.RdCap); var rapPolicyName = GetPolicyName(organizationId, collection.Name, RdsPolicyTypes.RdRap); foreach (var gateway in Gateways) { CreateHelpDeskRdCapForce(runSpace, gateway); CreateHelpDeskRdRapForce(runSpace, gateway); if (!CentralNps) { CreateRdCapForce(runSpace, gateway, capPolicyName, collection.Name, new List { GetUsersGroupName(collection.Name) }); } CreateRdRapForce(runSpace, gateway, rapPolicyName, collection.Name, new List { GetUsersGroupName(collection.Name) }); } if (CentralNps) { CreateCentralNpsPolicy(runSpace, CentralNpsHost, capPolicyName, collection.Name, organizationId); } //add user group to collection AddUserGroupsToCollection(runSpace, collection.Name, new List { GetUsersGroupName(collection.Name) }); //add session servers to group foreach (var rdsServer in collection.Servers) { MoveSessionHostToCollectionOU(rdsServer.Name, collection.Name, organizationId); AddAdGroupToLocalAdmins(runSpace, rdsServer.FqdName, helpDeskGroupSamAccountName); AddAdGroupToLocalAdmins(runSpace, rdsServer.FqdName, localAdminsGroupSamAccountName); Log.WriteStart(string.Format("Adding server {0} to AD Group, OrgId: {1}", rdsServer.Name, organizationId)); AddComputerToCollectionAdComputerGroup(organizationId, collection.Name, rdsServer); Log.WriteStart(string.Format("Server {0} added to AD Group, OrgId: {1}", rdsServer.Name, organizationId)); } string collectionComputersPath = GetComputerGroupPath(organizationId, collection.Name); CreatePolicy(runSpace, organizationId, string.Format("{0}-administrators", collection.Name), new DirectoryEntry(GetGroupPath(organizationId, collection.Name, GetLocalAdminsGroupName(collection.Name))), new DirectoryEntry(collectionComputersPath), collection.Name); CreatePolicy(runSpace, organizationId, string.Format("{0}-users", collection.Name), new DirectoryEntry(GetUsersGroupPath(organizationId, collection.Name)) , new DirectoryEntry(collectionComputersPath), collection.Name); CreateHelpDeskPolicy(runSpace, new DirectoryEntry(GetHelpDeskGroupPath(RDSHelpDeskGroup)), new DirectoryEntry(collectionComputersPath), organizationId, collection.Name); SetUsersInCollection(organizationId, collection.Name, users); Log.WriteStart(string.Format("Collection imported: {0}", collection.Name)); } finally { CloseRunspace(runSpace); } } #endregion private void AddRdsServerToDeployment(Runspace runSpace, RdsServer server) { Command cmd = new Command("Add-RDserver"); cmd.Parameters.Add("Server", server.FqdName); cmd.Parameters.Add("Role", "RDS-RD-SERVER"); cmd.Parameters.Add("ConnectionBroker", ConnectionBroker); ExecuteShellCommand(runSpace, cmd, false); } private bool ExistRdsServerInDeployment(Runspace runSpace, RdsServer server) { Command cmd = new Command("Get-RDserver"); cmd.Parameters.Add("Role", "RDS-RD-SERVER"); cmd.Parameters.Add("ConnectionBroker", ConnectionBroker); var serversPs = ExecuteShellCommand(runSpace, cmd, false); if (serversPs != null) { foreach (var serverPs in serversPs) { var serverName = Convert.ToString( GetPSObjectProperty(serverPs, "Server")); if (string.Compare(serverName, server.FqdName, StringComparison.InvariantCultureIgnoreCase) == 0) { return true; } } } return false; } private void SetUsersToCollectionAdGroup(string collectionName, string organizationId, IEnumerable users, string groupName, string groupPath) { var orgPath = GetOrganizationPath(organizationId); var orgEntry = ActiveDirectoryUtils.GetADObject(orgPath); var groupUsers = ActiveDirectoryUtils.GetGroupObjects(groupName, "user", orgEntry); foreach (string userPath in groupUsers) { ActiveDirectoryUtils.RemoveObjectFromGroup(userPath, groupPath); } foreach (var user in users) { var userPath = GetUserPath(organizationId, user); if (ActiveDirectoryUtils.AdObjectExists(userPath)) { var userObject = ActiveDirectoryUtils.GetADObject(userPath); var samName = (string)ActiveDirectoryUtils.GetADObjectProperty(userObject, "sAMAccountName"); ActiveDirectoryUtils.AddObjectToGroup(userPath, groupPath); } } } private List GetUsersToCollectionAdGroup(string collectionName, string groupName, string organizationId) { var users = new List(); var orgPath = GetOrganizationPath(organizationId); var orgEntry = ActiveDirectoryUtils.GetADObject(orgPath); foreach (string userPath in ActiveDirectoryUtils.GetGroupObjects(groupName, "user", orgEntry)) { var userObject = ActiveDirectoryUtils.GetADObject(userPath); var samName = (string)ActiveDirectoryUtils.GetADObjectProperty(userObject, "sAMAccountName"); users.Add(samName); } return users; } private void AddUserGroupsToCollection(Runspace runSpace, string collectionName, List groups) { Command cmd = new Command("Set-RDSessionCollectionConfiguration"); cmd.Parameters.Add("CollectionName", collectionName); cmd.Parameters.Add("UserGroup", groups); cmd.Parameters.Add("ConnectionBroker", ConnectionBroker); ExecuteShellCommand(runSpace, cmd, false).FirstOrDefault(); } private void AddComputerToCollectionAdComputerGroup(string organizationId, string collectionName, RdsServer server) { var computerGroupName = GetComputersGroupName( collectionName); var computerObject = GetComputerObject(server.Name); Log.WriteStart(string.Format("ComputerGroupName: {0}\r\nServerName: {1}\r\nCollectionName: {2}", computerGroupName, server.Name, collectionName)); if (computerObject != null) { var samName = (string)ActiveDirectoryUtils.GetADObjectProperty(computerObject, "sAMAccountName"); Log.WriteStart(string.Format("sAMAccountName: {0}\r\nPath: {1}\r\n", samName, computerObject.Path)); if (!ActiveDirectoryUtils.IsComputerInGroup(samName, computerGroupName)) { string groupPath = GetComputerGroupPath(organizationId, collectionName); Log.WriteStart(string.Format("ComputerGroupPath: {0}", groupPath)); ActiveDirectoryUtils.AddObjectToGroup(computerObject.Path, groupPath); Log.WriteStart(string.Format("{0} added to {1}", computerObject.Path, groupPath)); } if (!ActiveDirectoryUtils.IsComputerInGroup(samName, RDSHelpDeskComputerGroup)) { string helpDeskGroupPath = GetHelpDeskGroupPath(RDSHelpDeskComputerGroup); Log.WriteStart(string.Format("HelpDeskGroupPath: {0}", helpDeskGroupPath)); ActiveDirectoryUtils.AddObjectToGroup(computerObject.Path, helpDeskGroupPath); Log.WriteStart(string.Format("{0} added to {1}", computerObject.Path, helpDeskGroupPath)); } } else { Log.WriteStart(string.Format("ComputerObject IS NULL")); } SetRDServerNewConnectionAllowed(false, server); } private void RemoveComputerFromCollectionAdComputerGroup(string organizationId, string collectionName, RdsServer server) { var computerGroupName = GetComputersGroupName(collectionName); var computerObject = GetComputerObject(server.Name); if (computerObject != null) { var samName = (string)ActiveDirectoryUtils.GetADObjectProperty(computerObject, "sAMAccountName"); if (ActiveDirectoryUtils.IsComputerInGroup(samName, computerGroupName)) { ActiveDirectoryUtils.RemoveObjectFromGroup(computerObject.Path, GetComputerGroupPath(organizationId, collectionName)); } if (ActiveDirectoryUtils.AdObjectExists(GetHelpDeskGroupPath(RDSHelpDeskComputerGroup))) { if (ActiveDirectoryUtils.IsComputerInGroup(samName, RDSHelpDeskComputerGroup)) { ActiveDirectoryUtils.RemoveObjectFromGroup(computerObject.Path, GetHelpDeskGroupPath(RDSHelpDeskComputerGroup)); } } } } public bool AddSessionHostFeatureToServer(string hostName) { bool installationResult = false; Runspace runSpace = null; try { runSpace = OpenRunspace(); var feature = AddFeature(runSpace, hostName, "RDS-RD-Server", true, true); installationResult = (bool)GetPSObjectProperty(feature, "Success"); if (!IsFeatureInstalled(hostName, "Desktop-Experience", runSpace)) { feature = AddFeature(runSpace, hostName, "Desktop-Experience", true, false); } if (!IsFeatureInstalled(hostName, "NET-Framework-Core", runSpace)) { feature = AddFeature(runSpace, hostName, "NET-Framework-Core", true, false); } } finally { CloseRunspace(runSpace); } return installationResult; } private void CheckOrCreateComputersRoot(string computersRootPath) { if (ActiveDirectoryUtils.AdObjectExists(computersRootPath) && !ActiveDirectoryUtils.AdObjectExists(GetRdsServersGroupPath())) { //ActiveDirectoryUtils.CreateGroup(computersRootPath, RdsServersRootOU); ActiveDirectoryUtils.CreateOrganizationalUnit(RdsServersRootOU, computersRootPath); } } public void MoveSessionHostToRdsOU(string hostName) { if (!string.IsNullOrEmpty(ComputersRootOU)) { CheckOrCreateComputersRoot(GetComputersRootPath()); } var computerObject = GetComputerObject(hostName); if (computerObject != null) { var samName = (string)ActiveDirectoryUtils.GetADObjectProperty(computerObject, "sAMAccountName"); if (!ActiveDirectoryUtils.IsComputerInGroup(samName, RdsServersRootOU)) { DirectoryEntry group = new DirectoryEntry(GetRdsServersGroupPath()); computerObject.MoveTo(group); } } } public void MoveSessionHostToCollectionOU(string hostName, string collectionName, string organizationId) { if (!string.IsNullOrEmpty(ComputersRootOU)) { CheckOrCreateComputersRoot(GetComputersRootPath()); } var computerObject = GetComputerObject(hostName); string collectionOUName = string.Format("{0}-OU", collectionName); string collectionOUPath = GetCollectionOUPath(organizationId, collectionOUName); if (!ActiveDirectoryUtils.AdObjectExists(collectionOUPath)) { ActiveDirectoryUtils.CreateOrganizationalUnit(collectionOUName, GetOrganizationPath(organizationId)); } if (computerObject != null) { var samName = (string)ActiveDirectoryUtils.GetADObjectProperty(computerObject, "sAMAccountName"); if (!ActiveDirectoryUtils.IsComputerInGroup(samName, collectionOUName)) { DirectoryEntry group = new DirectoryEntry(collectionOUPath); computerObject.MoveTo(group); } } } public void MoveRdsServerToTenantOU(string hostName, string organizationId) { var tenantComputerGroupPath = GetTenantComputerGroupPath(organizationId); if (!ActiveDirectoryUtils.AdObjectExists(tenantComputerGroupPath)) { ActiveDirectoryUtils.CreateOrganizationalUnit(RdsServersOU, GetOrganizationPath(organizationId)); } hostName = hostName.ToLower().Replace(string.Format(".{0}", ServerSettings.ADRootDomain.ToLower()), ""); var rootComputerPath = GetRdsServerPath(hostName); var tenantComputerPath = GetTenantComputerPath(hostName, organizationId); if (!string.IsNullOrEmpty(ComputersRootOU)) { CheckOrCreateComputersRoot(GetComputersRootPath()); } var computerObject = GetComputerObject(hostName); if (computerObject != null) { var samName = (string)ActiveDirectoryUtils.GetADObjectProperty(computerObject, "sAMAccountName"); if (!ActiveDirectoryUtils.IsComputerInGroup(samName, RdsServersOU)) { DirectoryEntry group = new DirectoryEntry(tenantComputerGroupPath); computerObject.MoveTo(group); } } } public void RemoveRdsServerFromTenantOU(string hostName, string organizationId) { var tenantComputerGroupPath = GetTenantComputerGroupPath(organizationId); hostName = hostName.ToLower().Replace(string.Format(".{0}", ServerSettings.ADRootDomain.ToLower()), ""); if (!string.IsNullOrEmpty(ComputersRootOU)) { CheckOrCreateComputersRoot(GetComputersRootPath()); } if (!ActiveDirectoryUtils.AdObjectExists(tenantComputerGroupPath)) { ActiveDirectoryUtils.CreateOrganizationalUnit(RdsServersOU, GetOrganizationPath(organizationId)); } var computerObject = GetComputerObject(hostName); if (computerObject != null) { var samName = (string)ActiveDirectoryUtils.GetADObjectProperty(computerObject, "sAMAccountName"); if (ActiveDirectoryUtils.AdObjectExists(GetComputersRootPath()) && !string.IsNullOrEmpty(ComputersRootOU) && !ActiveDirectoryUtils.IsComputerInGroup(samName, RdsServersRootOU)) { DirectoryEntry group = new DirectoryEntry(GetRdsServersGroupPath()); computerObject.MoveTo(group); } } } public bool CheckSessionHostFeatureInstallation(string hostName) { bool isInstalled = false; Runspace runSpace = null; try { runSpace = OpenRunspace(); Command cmd = new Command("Get-WindowsFeature"); cmd.Parameters.Add("Name", "RDS-RD-Server"); var feature = ExecuteRemoteShellCommand(runSpace, hostName, cmd).FirstOrDefault(); if (feature != null) { isInstalled = (bool) GetPSObjectProperty(feature, "Installed"); } } finally { CloseRunspace(runSpace); } return isInstalled; } public bool CheckServerAvailability(string hostName) { var ping = new Ping(); var ipAddress = GetServerIp(hostName); var reply = ping.Send(ipAddress, 3000); return reply != null && reply.Status == IPStatus.Success; } #region Helpers private static string ConvertByteToStringSid(Byte[] sidBytes) { StringBuilder strSid = new StringBuilder(); strSid.Append("S-"); try { // Add SID revision. strSid.Append(sidBytes[0].ToString()); // Next six bytes are SID authority value. if (sidBytes[6] != 0 || sidBytes[5] != 0) { string strAuth = String.Format ("0x{0:2x}{1:2x}{2:2x}{3:2x}{4:2x}{5:2x}", (Int16)sidBytes[1], (Int16)sidBytes[2], (Int16)sidBytes[3], (Int16)sidBytes[4], (Int16)sidBytes[5], (Int16)sidBytes[6]); strSid.Append("-"); strSid.Append(strAuth); } else { Int64 iVal = (Int32)(sidBytes[1]) + (Int32)(sidBytes[2] << 8) + (Int32)(sidBytes[3] << 16) + (Int32)(sidBytes[4] << 24); strSid.Append("-"); strSid.Append(iVal.ToString()); } // Get sub authority count... int iSubCount = Convert.ToInt32(sidBytes[7]); int idxAuth = 0; for (int i = 0; i < iSubCount; i++) { idxAuth = 8 + i * 4; UInt32 iSubAuth = BitConverter.ToUInt32(sidBytes, idxAuth); strSid.Append("-"); strSid.Append(iSubAuth.ToString()); } } catch { return ""; } return strSid.ToString(); } private StartMenuApp CreateStartMenuAppFromPsObject(PSObject psObject) { var remoteApp = new StartMenuApp { DisplayName = Convert.ToString(GetPSObjectProperty(psObject, "DisplayName")), FilePath = Convert.ToString(GetPSObjectProperty(psObject, "FilePath")), FileVirtualPath = Convert.ToString(GetPSObjectProperty(psObject, "FileVirtualPath")) }; remoteApp.Alias = remoteApp.DisplayName; return remoteApp; } private RemoteApplication CreateRemoteApplicationFromPsObject(PSObject psObject) { var remoteApp = new RemoteApplication { DisplayName = Convert.ToString(GetPSObjectProperty(psObject, "DisplayName")), FilePath = Convert.ToString(GetPSObjectProperty(psObject, "FilePath")), Alias = Convert.ToString(GetPSObjectProperty(psObject, "Alias")), ShowInWebAccess = Convert.ToBoolean(GetPSObjectProperty(psObject, "ShowInWebAccess")), Users = null }; var requiredCommandLine = GetPSObjectProperty(psObject, "RequiredCommandLine"); remoteApp.RequiredCommandLine = requiredCommandLine == null ? null : requiredCommandLine.ToString(); var users = (string[])(GetPSObjectProperty(psObject, "UserGroups")); if (users != null && users.Any()) { remoteApp.Users = users; } else { remoteApp.Users = null; } return remoteApp; } internal IPAddress GetServerIp(string hostname, AddressFamily addressFamily = AddressFamily.InterNetwork) { var address = GetServerIps(hostname); return address.FirstOrDefault(x => x.AddressFamily == addressFamily); } internal IEnumerable GetServerIps(string hostname) { var address = Dns.GetHostAddresses(hostname); return address; } internal void RemoveItem(Runspace runSpace, string path) { Command rdRapCommand = new Command("Remove-Item"); rdRapCommand.Parameters.Add("Path", path); rdRapCommand.Parameters.Add("Force", true); rdRapCommand.Parameters.Add("Recurse", true); ExecuteShellCommand(runSpace, rdRapCommand, false); } internal void RemoveItemRemote(Runspace runSpace, string hostname, string path, params string[] imports) { Command rdRapCommand = new Command("Remove-Item"); rdRapCommand.Parameters.Add("Path", string.Format("\"{0}\"", path)); rdRapCommand.Parameters.Add("Force", ""); rdRapCommand.Parameters.Add("Recurse", ""); ExecuteRemoteShellCommand(runSpace, hostname, rdRapCommand, imports); } private string GetPolicyName(string organizationId, string collectionName, RdsPolicyTypes policyType) { string policyName = string.Format("{0}-", collectionName); switch (policyType) { case RdsPolicyTypes.RdCap: { policyName += "RDCAP"; break; } case RdsPolicyTypes.RdRap: { policyName += "RDRAP"; break; } } return policyName; } private string GetComputersGroupName(string collectionName) { return string.Format(RdsGroupFormat, collectionName, Computers.ToLowerInvariant()); } private string GetUsersGroupName(string collectionName) { return string.Format(RdsGroupFormat, collectionName, Users.ToLowerInvariant()); } private string GetLocalAdminsGroupName(string collectionName) { return string.Format(RdsGroupFormat, collectionName, Admins.ToLowerInvariant()); } internal string GetComputerGroupPath(string organizationId, string collection) { StringBuilder sb = new StringBuilder(); AppendProtocol(sb); AppendDomainController(sb); AppendCNPath(sb, GetComputersGroupName(collection)); AppendOUPath(sb, organizationId); AppendOUPath(sb, RootOU); AppendDomainPath(sb, RootDomain); return sb.ToString(); } internal string GetUsersGroupPath(string organizationId, string collection) { StringBuilder sb = new StringBuilder(); AppendProtocol(sb); AppendDomainController(sb); AppendCNPath(sb, GetUsersGroupName(collection)); AppendOUPath(sb, organizationId); AppendOUPath(sb, RootOU); AppendDomainPath(sb, RootDomain); return sb.ToString(); } private string GetGroupPath(string organizationId, string collectionName, string groupName) { StringBuilder sb = new StringBuilder(); AppendProtocol(sb); AppendDomainController(sb); AppendCNPath(sb, groupName); AppendOUPath(sb, organizationId); AppendOUPath(sb, RootOU); AppendDomainPath(sb, RootDomain); return sb.ToString(); } private string GetCollectionOUPath(string organizationId, string collectionName) { StringBuilder sb = new StringBuilder(); AppendProtocol(sb); AppendDomainController(sb); AppendOUPath(sb, collectionName); AppendOUPath(sb, organizationId); AppendOUPath(sb, RootOU); AppendDomainPath(sb, RootDomain); return sb.ToString(); } private string GetUserPath(string organizationId, string loginName) { StringBuilder sb = new StringBuilder(); // append provider AppendProtocol(sb); AppendDomainController(sb); AppendCNPath(sb, loginName); AppendOUPath(sb, organizationId); AppendOUPath(sb, RootOU); AppendDomainPath(sb, RootDomain); return sb.ToString(); } private string GetRootOUPath() { StringBuilder sb = new StringBuilder(); // append provider AppendProtocol(sb); AppendDomainController(sb); AppendOUPath(sb, RootOU); AppendDomainPath(sb, RootDomain); return sb.ToString(); } private string GetOrganizationPath(string organizationId) { StringBuilder sb = new StringBuilder(); // append provider AppendProtocol(sb); AppendDomainController(sb); AppendOUPath(sb, organizationId); AppendOUPath(sb, RootOU); AppendDomainPath(sb, RootDomain); return sb.ToString(); } private DirectoryEntry GetComputerObject(string computerName) { DirectorySearcher deSearch = new DirectorySearcher { Filter = string.Format("(&(objectCategory=computer)(name={0}))", computerName) }; SearchResult results = deSearch.FindOne(); return results.GetDirectoryEntry(); } private string GetTenantComputerPath(string objName, string organizationId) { StringBuilder sb = new StringBuilder(); AppendProtocol(sb); AppendDomainController(sb); AppendCNPath(sb, objName); AppendOUPath(sb, RdsServersOU); AppendOUPath(sb, organizationId); AppendOUPath(sb, RootOU); AppendDomainPath(sb, RootDomain); return sb.ToString(); } private string GetComputersRootPath() { StringBuilder sb = new StringBuilder(); AppendProtocol(sb); AppendDomainController(sb); AppendOUPath(sb, ComputersRootOU); AppendDomainPath(sb, RootDomain); return sb.ToString(); } private string GetRdsServersGroupPath() { StringBuilder sb = new StringBuilder(); AppendProtocol(sb); AppendDomainController(sb); AppendOUPath(sb, RdsServersRootOU); AppendOUPath(sb, ComputersRootOU); AppendDomainPath(sb, RootDomain); return sb.ToString(); } private string GetRdsServerPath(string name) { StringBuilder sb = new StringBuilder(); AppendProtocol(sb); AppendDomainController(sb); AppendCNPath(sb, name); AppendOUPath(sb, RdsServersRootOU); AppendOUPath(sb, ComputersRootOU); AppendDomainPath(sb, RootDomain); return sb.ToString(); } private string GetRootPath() { StringBuilder sb = new StringBuilder(); AppendProtocol(sb); AppendDomainController(sb); AppendDomainPath(sb, RootDomain); return sb.ToString(); } internal string GetTenantComputerGroupPath(string organizationId) { StringBuilder sb = new StringBuilder(); AppendProtocol(sb); AppendDomainController(sb); AppendOUPath(sb, RdsServersOU); AppendOUPath(sb, organizationId); AppendOUPath(sb, RootOU); AppendDomainPath(sb, RootDomain); return sb.ToString(); } private static void AppendCNPath(StringBuilder sb, string organizationId) { if (string.IsNullOrEmpty(organizationId)) return; sb.Append("CN=").Append(organizationId).Append(","); } private void AppendDomainController(StringBuilder sb) { sb.Append(PrimaryDomainController + "/"); } private static void AppendProtocol(StringBuilder sb) { sb.Append("LDAP://"); } 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(","); } } #endregion #region Windows Feature PowerShell internal bool IsFeatureInstalled(string featureName, Runspace runSpace) { bool isInstalled = false; Command cmd = new Command("Get-WindowsFeature"); cmd.Parameters.Add("Name", featureName); var feature = ExecuteShellCommand(runSpace, cmd, false).FirstOrDefault(); if (feature != null) { isInstalled = (bool)GetPSObjectProperty(feature, "Installed"); } return isInstalled; } internal bool IsFeatureInstalled(string hostName, string featureName, Runspace runSpace) { bool isInstalled = false; Command cmd = new Command("Get-WindowsFeature"); cmd.Parameters.Add("Name", featureName); var feature = ExecuteRemoteShellCommand(runSpace, hostName, cmd).FirstOrDefault(); if (feature != null) { isInstalled = (bool) GetPSObjectProperty(feature, "Installed"); } return isInstalled; } internal PSObject AddFeature(Runspace runSpace, string featureName, bool includeAllSubFeature = true, bool restart = false) { Command cmd = new Command("Add-WindowsFeature"); cmd.Parameters.Add("Name", featureName); if (includeAllSubFeature) { cmd.Parameters.Add("IncludeAllSubFeature", true); } if (restart) { cmd.Parameters.Add("Restart", true); } return ExecuteShellCommand(runSpace, cmd, false).FirstOrDefault(); } internal PSObject AddFeature(Runspace runSpace, string hostName, string featureName, bool includeAllSubFeature = true, bool restart = false) { Command cmd = new Command("Add-WindowsFeature"); cmd.Parameters.Add("Name", featureName); if (includeAllSubFeature) { cmd.Parameters.Add("IncludeAllSubFeature", ""); } if (restart) { cmd.Parameters.Add("Restart", ""); } return ExecuteRemoteShellCommand(runSpace, hostName, cmd).FirstOrDefault(); } #endregion #region PowerShell integration private static InitialSessionState session = null; internal virtual Runspace OpenRunspace() { Log.WriteStart("OpenRunspace"); if (session == null) { session = InitialSessionState.CreateDefault(); session.ImportPSModule(new string[] { "ServerManager", "RemoteDesktop", "RemoteDesktopServices" }); } Runspace runSpace = RunspaceFactory.CreateRunspace(session); // runSpace.Open(); // runSpace.SessionStateProxy.SetVariable("ConfirmPreference", "none"); Log.WriteEnd("OpenRunspace"); return runSpace; } internal void CloseRunspace(Runspace runspace) { try { if (runspace != null && runspace.RunspaceStateInfo.State == RunspaceState.Opened) { runspace.Close(); } } catch (Exception ex) { Log.WriteError("Runspace error", ex); } } internal Collection ExecuteRemoteShellCommand(Runspace runSpace, string hostName, Command cmd, params string[] moduleImports) { object[] errors; return ExecuteRemoteShellCommand(runSpace, hostName, cmd, out errors, moduleImports); } internal Collection ExecuteRemoteShellCommand(Runspace runSpace, string hostName, Command cmd, out object[] errors, params string[] moduleImports) { Command invokeCommand = new Command("Invoke-Command"); invokeCommand.Parameters.Add("ComputerName", hostName); RunspaceInvoke invoke = new RunspaceInvoke(); string commandString = moduleImports.Any() ? string.Format("import-module {0};", string.Join(",", moduleImports)) : string.Empty; commandString += cmd.CommandText; if (cmd.Parameters != null && cmd.Parameters.Any()) { commandString += " " + string.Join(" ", cmd.Parameters.Select(x => string.Format("-{0} {1}", x.Name, x.Value)).ToArray()); } ScriptBlock sb = invoke.Invoke(string.Format("{{{0}}}", commandString))[0].BaseObject as ScriptBlock; invokeCommand.Parameters.Add("ScriptBlock", sb); return ExecuteShellCommand(runSpace, invokeCommand, false, out errors); } internal Collection ExecuteRemoteShellCommand(Runspace runSpace, string hostName, List scripts, out object[] errors, params string[] moduleImports) { Command invokeCommand = new Command("Invoke-Command"); invokeCommand.Parameters.Add("ComputerName", hostName); RunspaceInvoke invoke = new RunspaceInvoke(); string commandString = moduleImports.Any() ? string.Format("import-module {0};", string.Join(",", moduleImports)) : string.Empty; commandString = string.Format("{0};{1}", commandString, string.Join(";", scripts.ToArray())); ScriptBlock sb = invoke.Invoke(string.Format("{{{0}}}", commandString))[0].BaseObject as ScriptBlock; invokeCommand.Parameters.Add("ScriptBlock", sb); return ExecuteShellCommand(runSpace, invokeCommand, false, out errors); } 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, List scripts, out object[] errors) { Log.WriteStart("ExecuteShellCommand"); var errorList = new List(); Collection results; using (Pipeline pipeLine = runspace.CreatePipeline()) { foreach (string script in scripts) { pipeLine.Commands.AddScript(script); } results = pipeLine.Invoke(); 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); Log.WriteWarning(errorMessage); } } } errors = errorList.ToArray(); Log.WriteEnd("ExecuteShellCommand"); return results; } internal Collection ExecuteShellCommand(Runspace runSpace, Command cmd, bool useDomainController, out object[] errors) { Log.WriteStart("ExecuteShellCommand"); List errorList = new List(); if (useDomainController) { CommandParameter dc = new CommandParameter("DomainController", PrimaryDomainController); if (!cmd.Parameters.Contains(dc)) { cmd.Parameters.Add(dc); } } 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); Log.WriteWarning(errorMessage); } } } pipeLine = null; errors = errorList.ToArray(); Log.WriteEnd("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) { Log.WriteStart("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(); Log.WriteEnd("GetResultObjectIdentity"); return ret; } internal string GetResultObjectDN(Collection result) { Log.WriteStart("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(); Log.WriteEnd("GetResultObjectDN"); return ret; } internal bool ItemExists(Runspace runSpace, string path) { Command testPathCommand = new Command("Test-Path"); testPathCommand.Parameters.Add("Path", path); var testPathResult = ExecuteShellCommand(runSpace, testPathCommand, false).First(); var result = Convert.ToBoolean(testPathResult.ToString()); return result; } internal bool ItemExistsRemote(Runspace runSpace, string hostname,string path) { Command testPathCommand = new Command("Test-Path"); testPathCommand.Parameters.Add("Path", string.Format("\"{0}\"", path)); var testPathResult = ExecuteRemoteShellCommand(runSpace, hostname, testPathCommand, RdsModuleName).First(); var result = Convert.ToBoolean(testPathResult.ToString()); return result; } internal List GetServersExistingInCollections(Runspace runSpace) { var existingHosts = new List(); //var scripts = new List(); //scripts.Add(string.Format("$sessions = Get-RDSessionCollection -ConnectionBroker {0}", ConnectionBroker)); //scripts.Add(string.Format("foreach($session in $sessions){{Get-RDSessionHost $session.CollectionName -ConnectionBroker {0}|Select SessionHost}}", ConnectionBroker)); //object[] errors; //var sessionHosts = ExecuteShellCommand(runSpace, scripts, out errors); //foreach(var host in sessionHosts) //{ // var sessionHost = GetPSObjectProperty(host, "SessionHost"); // if (sessionHost != null) // { // existingHosts.Add(sessionHost.ToString()); // } //} return existingHosts; } internal List EditRdsCollectionSettingsInternal(RdsCollection collection, Runspace runspace) { object[] errors; Command cmd = new Command("Set-RDSessionCollectionConfiguration"); cmd.Parameters.Add("CollectionName", collection.Name); cmd.Parameters.Add("ConnectionBroker", ConnectionBroker); if (string.IsNullOrEmpty(collection.Settings.ClientDeviceRedirectionOptions)) { collection.Settings.ClientDeviceRedirectionOptions = "None"; } var properties = collection.Settings.GetType().GetProperties(); foreach(var prop in properties) { if (prop.Name.ToLower() != "id" && prop.Name.ToLower() != "rdscollectionid") { var value = prop.GetValue(collection.Settings, null); if (value != null) { cmd.Parameters.Add(prop.Name, value); } } } ExecuteShellCommand(runspace, cmd, false, out errors); if (errors != null) { return errors.Select(e => e.ToString()).ToList(); } return new List(); } internal List GetRdsUserSessionsInternal(string collectionName, Runspace runSpace) { var result = new List(); var scripts = new List(); scripts.Add(string.Format("Get-RDUserSession -ConnectionBroker {0} - CollectionName {1} | ft CollectionName, Username, UnifiedSessionId, SessionState, HostServer", ConnectionBroker, collectionName)); object[] errors; Command cmd = new Command("Get-RDUserSession"); cmd.Parameters.Add("CollectionName", collectionName); cmd.Parameters.Add("ConnectionBroker", ConnectionBroker); var userSessions = ExecuteShellCommand(runSpace, cmd, false, out errors); var properties = typeof(RdsUserSession).GetProperties(); foreach(var userSession in userSessions) { var session = new RdsUserSession { CollectionName = GetPSObjectProperty(userSession, "CollectionName").ToString(), DomainName = GetPSObjectProperty(userSession, "DomainName").ToString(), HostServer = GetPSObjectProperty(userSession, "HostServer").ToString(), SessionState = GetPSObjectProperty(userSession, "SessionState").ToString(), UnifiedSessionId = GetPSObjectProperty(userSession, "UnifiedSessionId").ToString(), SamAccountName = GetPSObjectProperty(userSession, "UserName").ToString(), }; session.IsVip = false; session.UserName = GetUserFullName(session.DomainName, session.SamAccountName, runSpace); result.Add(session); } return result; } private string GetUserFullName(string domain, string userName, Runspace runspace) { Command cmd = new Command("Get-WmiObject"); cmd.Parameters.Add("Class", "win32_useraccount"); cmd.Parameters.Add("Filter", string.Format("Domain = '{0}' AND Name = '{1}'", domain, userName)); var names = ExecuteShellCommand(runspace, cmd, false); if (names.Any()) { return names.First().Members["FullName"].Value.ToString(); } return ""; } #endregion #region Server Info public RdsServerInfo GetRdsServerInfo(string serverName) { var result = new RdsServerInfo(); Runspace runspace = null; try { runspace = OpenRunspace(); result = GetServerInfo(runspace, serverName); result.Status = GetRdsServerStatus(runspace, serverName); } finally { CloseRunspace(runspace); } return result; } public string GetRdsServerStatus(string serverName) { string result = ""; Runspace runspace = null; try { runspace = OpenRunspace(); result = GetRdsServerStatus(runspace, serverName); } finally { CloseRunspace(runspace); } return result; } public void ShutDownRdsServer(string serverName) { Runspace runspace = null; try { runspace = OpenRunspace(); var command = new Command("Stop-Computer"); command.Parameters.Add("ComputerName", serverName); command.Parameters.Add("Force", true); object[] errors = null; ExecuteShellCommand(runspace, command, false, out errors); if (errors.Any()) { Log.WriteWarning(string.Join("\r\n", errors.Select(e => e.ToString()).ToArray())); throw new Exception(string.Join("\r\n", errors.Select(e => e.ToString()).ToArray())); } } finally { CloseRunspace(runspace); } } public void RestartRdsServer(string serverName) { Runspace runspace = null; try { runspace = OpenRunspace(); var command = new Command("Restart-Computer"); command.Parameters.Add("ComputerName", serverName); command.Parameters.Add("Force", true); object[] errors = null; ExecuteShellCommand(runspace, command, false, out errors); if (errors.Any()) { Log.WriteWarning(string.Join("\r\n", errors.Select(e => e.ToString()).ToArray())); throw new Exception(string.Join("\r\n", errors.Select(e => e.ToString()).ToArray())); } } finally { CloseRunspace(runspace); } } private RdsServerInfo GetServerInfo(Runspace runspace, string serverName) { var result = new RdsServerInfo(); Command cmd = new Command("Get-WmiObject"); cmd.Parameters.Add("Class", "Win32_Processor"); cmd.Parameters.Add("ComputerName", serverName); object[] errors = null; var psProcInfo = ExecuteShellCommand(runspace, cmd, false, out errors).First(); if (errors.Any()) { Log.WriteWarning(string.Join("\r\n", errors.Select(e => e.ToString()).ToArray())); return result; } cmd = new Command("Get-WmiObject"); cmd.Parameters.Add("Class", "Win32_OperatingSystem"); cmd.Parameters.Add("ComputerName", serverName); var psMemoryInfo = ExecuteShellCommand(runspace, cmd, false, out errors).First(); if (errors.Any()) { Log.WriteWarning(string.Join("\r\n", errors.Select(e => e.ToString()).ToArray())); return result; } result.NumberOfCores = Convert.ToInt32(GetPSObjectProperty(psProcInfo, "NumberOfCores")); result.MaxClockSpeed = Convert.ToInt32(GetPSObjectProperty(psProcInfo, "MaxClockSpeed")); result.LoadPercentage = Convert.ToInt32(GetPSObjectProperty(psProcInfo, "LoadPercentage")); result.MemoryAllocatedMb = Math.Round(Convert.ToDouble(GetPSObjectProperty(psMemoryInfo, "TotalVisibleMemorySize")) / 1024, 1); result.FreeMemoryMb = Math.Round(Convert.ToDouble(GetPSObjectProperty(psMemoryInfo, "FreePhysicalMemory")) / 1024, 1); result.Drives = GetRdsServerDriveInfo(runspace, serverName).ToArray(); return result; } private string GetRdsServerStatus (Runspace runspace, string serverName) { if (CheckServerAvailability(serverName)) { if (CheckPendingReboot(runspace, serverName)) { return "Online - Pending Reboot"; } return "Online"; } else { return "Unavailable"; } } private List GetRdsServerDriveInfo(Runspace runspace, string serverName) { var result = new List(); Command cmd = new Command("Get-WmiObject"); cmd.Parameters.Add("Class", "Win32_LogicalDisk"); cmd.Parameters.Add("Filter", "DriveType=3"); cmd.Parameters.Add("ComputerName", serverName); object[] errors = null; var psDrives = ExecuteShellCommand(runspace, cmd, false, out errors); if (errors.Any()) { Log.WriteWarning(string.Join("\r\n", errors.Select(e => e.ToString()).ToArray())); return result; } foreach (var psDrive in psDrives) { var driveInfo = new RdsServerDriveInfo() { VolumeName = GetPSObjectProperty(psDrive, "VolumeName").ToString(), DeviceId = GetPSObjectProperty(psDrive, "DeviceId").ToString(), SizeMb = Math.Round(Convert.ToDouble(GetPSObjectProperty(psDrive, "Size"))/1024/1024, 1), FreeSpaceMb = Math.Round(Convert.ToDouble(GetPSObjectProperty(psDrive, "FreeSpace"))/1024/1024, 1) }; result.Add(driveInfo); } return result; } private bool CheckRDSServerAvaliability(string serverName) { var ping = new Ping(); var reply = ping.Send(serverName, 1000); if (reply.Status == IPStatus.Success) { return true; } return false; } private bool CheckPendingReboot(Runspace runspace, string serverName) { if (CheckPendingReboot(runspace, serverName, @"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing", "RebootPending")) { return true; } if (CheckPendingReboot(runspace, serverName, @"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update", "RebootRequired")) { return true; } if (CheckPendingReboot(runspace, serverName, @"HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager", "PendingFileRenameOperations")) { return true; } return false; } private bool CheckPendingReboot(Runspace runspace, string serverName, string registryPath, string registryKey) { Command cmd = new Command("Get-ItemProperty"); cmd.Parameters.Add("Path", registryPath); cmd.Parameters.Add("Name", registryKey); cmd.Parameters.Add("ErrorAction", "SilentlyContinue"); var feature = ExecuteRemoteShellCommand(runspace, serverName, cmd).FirstOrDefault(); if (feature != null) { return true; } return false; } #endregion } }