Simplifying my PowerShell version 5 Class Based DSC Resource for Configuring Remote Desktop

Last week I wrote a blog article about a PowerShell Desired State Configuration Class Based Resource for Configuring Remote Desktop. Since then I've discovered and learned a couple of new things about enumerations in PowerShell that can be used to simply the code even further.

My original code used a couple of enumerations which I've removed to show how they can be used to further simply the code:

 1class RemoteDesktop {
 2
 3    [DscProperty(Key)]
 4    [string]$UserAuthenication
 5
 6    [DscProperty(Mandatory)]
 7    [string]$Ensure
 8
 9    [RemoteDesktop]Get() {
10
11        $this.UserAuthenication = $this.GetAuthSetting()
12        $this.Ensure = $this.GetTSSetting()
13
14        Return $this
15
16    }
17
18    [string]GetAuthSetting(){
19
20        $AuthCurrentSetting = Get-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp' -Name 'UserAuthentication' |
21                              Select-Object -ExpandProperty UserAuthentication
22
23        $AuthSetting = switch ($AuthCurrentSetting) {
24            0 {'NonSecure'; Break}
25            1 {'Secure'; Break}
26        }
27
28        Return $AuthSetting
29
30    }
31
32    [string]GetTSSetting(){
33
34        $TSCurrentSetting = Get-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Terminal Server' -Name 'fDenyTSConnections' |
35                            Select-Object -ExpandProperty fDenyTSConnections
36
37        $TSSetting = switch ($TSCurrentSetting) {
38            0 {'Present'; Break}
39            1 {'Absent'; Break}
40        }
41
42        Return $TSSetting
43
44    }
45
46}

The code shown in the previous example uses switch statements to translate the numeric values returned from the registry into human readable names. There are several different ways to instantiate a copy of the RemoteDesktop class and call the Get() method:

1(New-Object -TypeName RemoteDesktop).Get()
2
3([RemoteDesktop]::new()).Get()
4
5$RDP = [RemoteDesktop]::new()
6$RDP.Get()

rdp-class-enum1a.png

I'll remove the switch statements to show that numeric values are indeed returned without them:

 1class RemoteDesktop {
 2
 3    [DscProperty(Key)]
 4    [string]$UserAuthenication
 5
 6    [DscProperty(Mandatory)]
 7    [string]$Ensure
 8
 9    [RemoteDesktop]Get() {
10
11        $this.UserAuthenication = $this.GetAuthSetting()
12        $this.Ensure = $this.GetTSSetting()
13
14        Return $this
15
16    }
17
18    [string]GetAuthSetting(){
19
20        $AuthCurrentSetting = Get-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp' -Name 'UserAuthentication' |
21                              Select-Object -ExpandProperty UserAuthentication
22
23        Return $AuthCurrentSetting
24
25    }
26
27    [string]GetTSSetting(){
28
29        $TSCurrentSetting = Get-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Terminal Server' -Name 'fDenyTSConnections' |
30                            Select-Object -ExpandProperty fDenyTSConnections
31
32        Return $TSCurrentSetting
33
34    }
35
36}

rdp-class-enum2a.png

My original code from the previously referenced blog article used enumerations instead of ValidateSet, but I've since learned that enumerations in PowerShell offer a lot more functionality than just input validation.

 1enum UserAuthenication {
 2    NonSecure
 3    Secure
 4}
 5enum Ensure {
 6    Absent
 7    Present
 8}
 9
10class RemoteDesktop {
11
12    [DscProperty(Key)]
13    [UserAuthenication]$UserAuthenication
14
15    [DscProperty(Mandatory)]
16    [Ensure]$Ensure
17
18    [RemoteDesktop]Get() {
19
20        $this.UserAuthenication = $this.GetAuthSetting()
21        $this.Ensure = $this.GetTSSetting()
22
23        Return $this
24
25    }
26
27    [UserAuthenication]GetAuthSetting(){
28
29        $AuthCurrentSetting = Get-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp' -Name 'UserAuthentication' |
30                              Select-Object -ExpandProperty UserAuthentication
31
32        Return $AuthCurrentSetting
33
34    }
35
36    [Ensure]GetTSSetting(){
37
38        $TSCurrentSetting = Get-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Terminal Server' -Name 'fDenyTSConnections' |
39                            Select-Object -ExpandProperty fDenyTSConnections
40
41        Return $TSCurrentSetting
42
43    }
44
45}

rdp-class-enum3a.png

By simply using the enumerations, the values are automatically translated from their numeric values returned from the registry into their human readable names that are defined in the enumeration so there's no need to perform the translation with switch statements.

The only problem at this point is the incorrect value is being return by the Ensure enumeration. By default the first item in the enumeration is 0, the second one is 1, and so on. I could simply swap the order of the two items in the Ensure enumeration to correct this problem:

1enum Ensure {
2    Present
3    Absent
4}

A better option is to be more declarative and define the value of each item in the enumerations so the order doesn't matter:

 1enum UserAuthenication {
 2    NonSecure = 0
 3    Secure = 1
 4}
 5enum Ensure {
 6    Present = 0
 7    Absent = 1
 8}
 9
10[DscResource()]
11class RemoteDesktop {
12
13    [DscProperty(Key)]
14    [UserAuthenication]$UserAuthenication
15
16    [DscProperty(Mandatory)]
17    [Ensure]$Ensure
18
19    [RemoteDesktop]Get() {
20
21        $this.UserAuthenication = $this.GetAuthSetting()
22        $this.Ensure = $this.GetTSSetting()
23
24        Return $this
25
26    }
27
28    [void]Set(){
29
30        if ($this.TestAuthSetting() -eq $false) {
31            Set-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp' -Name 'UserAuthentication' -Value $this.UserAuthenication
32        }
33
34        if ($this.TestTSSetting() -eq $false) {
35            Set-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Terminal Server' -Name 'fDenyTSConnections' -Value $this.Ensure
36        }
37
38    }
39
40    [bool]Test(){
41
42        if ($this.TestAuthSetting() -and $this.TestTSSetting() -eq $true) {
43            Return $true
44        }
45        else {
46            Return $false
47        }
48
49    }
50
51    [UserAuthenication]GetAuthSetting(){
52
53        $AuthCurrentSetting = Get-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp' -Name 'UserAuthentication' |
54                              Select-Object -ExpandProperty UserAuthentication
55
56        Return $AuthCurrentSetting
57
58    }
59
60    [Ensure]GetTSSetting(){
61
62        $TSCurrentSetting = Get-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Terminal Server' -Name 'fDenyTSConnections' |
63                            Select-Object -ExpandProperty fDenyTSConnections
64
65        Return $TSCurrentSetting
66
67    }
68
69    [bool]TestAuthSetting(){
70
71        if ($this.UserAuthenication -eq $this.GetAuthSetting()){
72            Return $true
73        }
74        else {
75            Return $false
76        }
77
78    }
79
80    [bool]TestTSSetting(){
81
82        if ($this.Ensure -eq $this.GetTSSetting()){
83            Return $true
84        }
85        else {
86            Return $false
87        }
88
89    }
90
91}

As shown in the previous code example, I also decided to move the code from the SetAuthSetting() and SetTSSetting() methods back into the actual Set() method since other methods don't call them which means that there's no redundant code eliminated by separating them and separating them adds complexity.

The updated version of the PowerShell Desired State Configuration Class Based Resource for Configuring Remote Desktop shown in this blog article can be downloaded from my DSC repository on GitHub.

µ