In this part we will see how we can automate RDS deployment with Desired State Configuration (DSC).

If you are working with RDS and if you are installing / re-installing it a lot then DSC is the perfect tool for you. We live in a world of automation and we don’t have time to click us through the wizard over and over again every time we need to rebuild our deplyoment and start from scratch.

I will run configuration from DC01. All servers are domain joined.

INFO! In this demo I will use plain text password. Be sure to protect the password with certificate in a production environment.

Infrastructure:

DC01 -> Domain Controller

RDCB01 -> Will become Connection Broker and Licensing server

RDSH01 -> Will become Session Host

RDWA01 -> Will become Web Access

Before we go deep into the configuration, there are some prerequisite that we need to fulfill before we can make this work.

Prerequisites:

  1. Configure LCM (Local Configuration Manager) on the target servers
  2. Make sure that the DSC resources exist on both source and target servers

So the first thing we need to do is to configure LCM. There are 3 settings that are important to us: ActionAfterRebootConfigurationMode and RebootNodeIfNeeded

ActionAfterReboot –> Specifies what happens after a reboot during the application of a configuration. Default is ContinueConfiguration so unless you didn’t change this you are good to go. If yes, change it back to defaults by adding ActionAfterReboot = ‘ContinueConfiguration’

Next is ConfigurationMode

LCM has 3 configuration modes:

  • ApplyOnly –> This is one shoot. DSC applies the configuration and that’s it. It does not check for drift from a previously configured state.
  • ApplyAndMonitor –> This is the default value. After initial application of a new configuration, if the target node drifts from the desired state, DSC reports the discrepancy in logs
  • ApplyandAutoCorrect –->  After initial application of a new configuration, if the target node drifts from the desired state, DSC reports the discrepancy in logs, and then re-applies the current configuration. So if you change something on the server DSC will detect that and revert the change to desired state which is specified in config file (MOF)

I chose ApplyOnly, because ApplyAndAutoCorrect is overkill for what I need.

Last but not least is the RebootNodeIfNeeded. Set this to $true to automatically reboot the node after a configuration that requires reboot is applied. Otherwise, you will have to manually reboot the node for any configuration that requires it. Session host server will require reboot.

2018-10-18 08_35_17-Window

Let’s configure the LCM first.


[DscLocalConfigurationManager()]

configuration LCM {

param (

[parameter(Mandatory=$true)]
[string[]]$computername

)

node $computername {

settings {

ConfigurationMode = 'ApplyOnly'
RebootNodeIfNeeded = $true

}
}
}

$computername = 'RDCB01','RDSH01','RDWA01'

LCM -OutputPath 'C:\DSC' -computername $computername -verbose

2018-12-13 22_27_27-Window

This will generate 3 meta.mof files. meta.mof files hold configuration for LCM.

2018-12-13 22_36_26-Window.png

Let’s push these configs to our target servers.

Set-DscLocalConfigurationManager -Path ‘C:\DSC’ -Force -Verbose -ComputerName $computername

-verbose -> This will give us some additional information as it runs

2018-12-13 22_53_42-Window.png

To confirm, we can use invoke-command and remote to the destination computers.

2018-12-13 22_58_48-Window.png

Our next requirement is to download required DSC module. Key to point here is that everytime when you are configuring something like RDS, DCHP, WebServer etc. you will need to have modules on source and destination servers.

Required Module:

  • xRemoteDesktopSessionHost

INFO!!!! Before you go and install this module from repository you will have to know that you will not be able to configure RDS across 3 or more nodes without modifying the module and removing some code. If you install it and try to do the same thing we are doing here you will run into issues with session collection and you will never be able to configure session collection settings etc. I modified the resources in that module and you can download and install them from here. 

xRemoteDesktopSessionHost

Before we go and use this module, let’s explore it.

Get-DscResource -Module xRemoteDesktopSessionHost

We can see here resources that we can use to build our RDS deployment.

2018-10-18 16_29_14-Window.png

We can use -syntax to further explore resources and to see how we can use them and which one is mandatory.

2018-10-18 16_32_04-Window.png

Once we know that we can start to build our configuration. Be sure to have that module on all servers that will participate in configuration.

Now, let’s install and configure RDS

2018-12-22 19_05_03-ADMIN NEDIM MEHIC _ NEDIMMEHIC.ORG

Whole code


$data = @{
AllNodes = @(

@{
NodeName = '*'
PSDscAllowPlainTextPassword = $true
PSDscAllowDomainUser = $true

},

@{

NodeName = 'rdcb01.mehic.se'
Role = 'Connection Broker'

},

@{

NodeName = 'rdsh01.mehic.se'
Role = 'Session Host'

},

@{

NodeName = 'rdwa01.mehic.se'
Role = 'Web Access'

}
);

RDSData = @{

ConnectionBroker = 'rdcb01.mehic.se'
SessionHost = 'rdsh01.mehic.se'
WebAccessServer = 'rdwa01.mehic.se'
CollectionName = 'NM'
AutomaticReconnectionEnabled = $true
DisconnectedSessionLimitMin = 360
IdleSessionLimitMin = 360
BrokenConnectionAction = 'Disconnect'
UserGroup = 'RDS Users'
LicenseServer = 'rdcb01.mehic.se'
LicenseMode = 'PerUser'

}

}

Configuration RDS {

param (

[Parameter(Mandatory=$true)]
[pscredential]$domainadmin

)
#region DSC Resource Modules
#OBS!!! Be sure that the modules exist on the destination host servers

Import-DscResource -ModuleName PSDesiredStateConfiguration,
@{ModuleName='xRemoteDesktopSessionHost';ModuleVersion="1.8.0.0"}

#endregion

Node $AllNodes.Where{$_.Role -eq 'Connection Broker'}.NodeName {
$RDData = $data.RDSData

WindowsFeature RDSConnectionBroker {

Name = 'RDS-Connection-Broker'
Ensure = 'Present'
}

WindowsFeature RDLicensing
{
Ensure = "Present"
Name = "RDS-Licensing"
}

WaitForAll SessionHost {

NodeName = 'rdsh01.mehic.se'
ResourceName = '[WindowsFeature]SessionHost'
RetryIntervalSec = 15
RetryCount = 50
DependsOn = '[WindowsFeature]RDLicensing'
}

WaitForAll WebAccess {

NodeName = 'rdwa01.mehic.se'
ResourceName = '[WindowsFeature]WebAccess'
RetryIntervalSec = 15
RetryCount = 50
DependsOn = '[WaitForAll]SessionHost'
}
xRDSessionDeployment NewDeployment {

ConnectionBroker = $RDData.ConnectionBroker
SessionHost = $RDData.SessionHost
WebAccessServer = $RDData.WebAccessServer
DependsOn = '[WaitForAll]WebAccess'
PsDscRunAsCredential = $domainadmin
}
xRDSessionCollection collection {

CollectionName = $RDData.CollectionName
SessionHost = $RDData.SessionHost
ConnectionBroker = $RDData.ConnectionBroker
DependsOn = '[xRDSessionDeployment]NewDeployment'
PsDscRunAsCredential = $domainadmin
}

xRDSessionCollectionConfiguration collectionconfig {

CollectionName = $RDData.CollectionName
ConnectionBroker = $RDData.ConnectionBroker
AutomaticReconnectionEnabled = $true
DisconnectedSessionLimitMin = $RDData.DisconnectedSessionLimitMin
IdleSessionLimitMin = $RDData.IdleSessionLimitMin
BrokenConnectionAction = $RDData.BrokenConnectionAction
UserGroup = $RDData.UserGroup
DependsOn = '[xRDSessionCollection]collection'
PsDscRunAsCredential = $domainadmin
}

xRDLicenseConfiguration licenseconfig {

ConnectionBroker = $RDData.ConnectionBroker
LicenseServer = $RDData.LicenseServer
LicenseMode = $RDData.LicenseMode
DependsOn = '[xRDSessionCollectionConfiguration]collectionconfig'
PsDscRunAsCredential = $domainadmin
}

}

Node $AllNodes.Where{$_.Role -eq 'Session Host'}.NodeName {

WindowsFeature SessionHost {

Name = 'RDS-RD-Server'
Ensure = 'Present'
}

}

Node $AllNodes.Where{$_.Role -eq 'Web Access'}.NodeName {

WindowsFeature WebAccess {

Name = 'RDS-Web-Access'
Ensure = 'Present'
}
}
}

RDS -OutputPath 'C:\DSC Configuration' -domainadmin $domainadmin -ConfigurationData $data -Verbose

Run the code and specify account with domain admin privileges. This will create 3 mof files.

Push the configuration to the remote servers with

Start-DscConfiguration -ComputerName $computername -Wait -Verbose -Force -Path ‘<path to the mof files>’

2018-12-22 19_13_24-A-DC01 on HYPER - Virtual Machine Connection

2018-12-22 19_14_45-A-RDCB2016 on HYPER - Virtual Machine Connection

2018-12-22 19_14_58-A-RDCB2016 on HYPER - Virtual Machine Connection

That’s it. Now what if we want to publish some remote app? One thing to know about DSC is that you never touch the machine. When you implement DSC in your environment you will administer everything through the DSC, you will not go to the machine and start configure it after you push/pull the config.  DSC can minimize human error as well. Now if you want to deploy RDS with remote apps instead you will need to add xRDRemoteApp resource to it.


xRDRemoteApp chrome {

CollectionName = $RDData.CollectionName
Alias = 'Chrome'
DisplayName = 'Chrome'
FilePath = 'C:\Program Files (x86)\Google\Chrome\Application\chrome.exe'
DependsOn = '[xRDLicenseConfiguration]licenseconfig'
PsDscRunAsCredential = $domainadmin
}

I added just 1 remote app (Chrome) but you can add many more.

That’s it. I hope this has been informative for you. If I find time I will make few posts with DSC which will show you how we can configure DC, DNS, DHCP, join machines to domain and configure roles and features and much more.

Stay Tuned!

Cheers,

Nedim