Created
April 30, 2022 19:48
-
-
Save mgreenegit/f67fc553b865716d1e3e69bda150bda6 to your computer and use it in GitHub Desktop.
An example DSC resource where only the functions need to be edited
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This is the Get function | |
function Get-Resource { | |
param( | |
[Parameter(Mandatory = $true)] | |
[String] | |
$Ensure, | |
[Parameter(Mandatory = $true)] | |
[string] | |
$PropertyA, | |
[Parameter(Mandatory = $true)] | |
[boolean] | |
$PropertyB | |
) | |
# base paths | |
$psBasePath = 'HKLM:\Software\Policies\Microsoft\Windows\PowerShell\TEST' | |
if (Test-Path $psBasePath) { | |
# Get key value | |
$keyA = Get-ItemProperty $psBasePath -Name KeyA -ErrorAction SilentlyContinue | |
$keyB = Get-ItemProperty $psBasePath -Name KeyB -ErrorAction SilentlyContinue | |
} | |
# The Ensure property should indicate whether the machine is overall in the right state | |
if ($keyA.KeyA -eq $PropertyA -and $keyB.KeyB -eq $PropertyB) { | |
$Ensure = 'Present' | |
} | |
else { | |
$Ensure = 'Absent' | |
} | |
# Information returned by Get will be available in Azure via API | |
return @{ | |
Ensure = $Ensure | |
PropertyA = $keyA.KeyA | |
PropertyB = $keyB.KeyB | |
} | |
} | |
# This is the Set function | |
function Set-Resource { | |
param( | |
[Parameter(Mandatory = $true)] | |
[String] | |
$Ensure, | |
[Parameter(Mandatory = $true)] | |
[string] | |
$PropertyA, | |
[Parameter(Mandatory = $true)] | |
[boolean] | |
$PropertyB | |
) | |
# base path | |
$psBasePath = 'HKLM:\Software\Policies\Microsoft\Windows\PowerShell\TEST' | |
# Create key if it does not exist | |
if (-not (Test-Path $psBasePath)) { $null = New-Item $psBasePath -Force } | |
if ('Present' -eq $Ensure) { | |
# Write registry key | |
Set-ItemProperty $psBasePath -Name KEYA -Value $PropertyA | |
Set-ItemProperty $psBasePath -Name KEYB -Value $PropertyB | |
} | |
if ('Absent' -eq $Ensure) { | |
# Remove registry key | |
Remove-Item 'HKLM:\Software\Policies\Microsoft\Windows\PowerShell\TEST' -Force -Recurse | |
} | |
} | |
# This is the Test function | |
function Test-Resource { | |
param( | |
[Parameter(Mandatory = $true)] | |
[String] | |
$Ensure, | |
[Parameter(Mandatory = $true)] | |
[string] | |
$PropertyA, | |
[Parameter(Mandatory = $true)] | |
[boolean] | |
$PropertyB | |
) | |
$get = Get-Resource @PSBoundParameters | |
# The Ensure property is already testing values, there is no need to test them all again | |
# For some scenarios, it might be good to compare additional properties (-and) | |
if ($Ensure -eq $get.Ensure) { | |
$test = $true | |
} | |
else { $test = $false } | |
return $test | |
} | |
# Make Properties less intimindating to edit | |
# These will be inherited by the DSC resource | |
class DscProperties { | |
# Example Ensure enum workaround | |
[DscProperty(Key)] | |
[ValidateSet('Present', 'Absent')] | |
[String]$Ensure | |
# Example sring | |
[DscProperty()] | |
[ValidateNotNullOrEmpty()] | |
[String]$PropertyA | |
# Example of boolean workaround | |
[DscProperty()] | |
[ValidateSet('True', 'False')] | |
[String]$PropertyB | |
} | |
# This provides a simple way to show more information in phrase | |
function Get-AdditionalText { | |
'#psdsc' | |
} | |
# This class should normally not need to be updated | |
class Reason { | |
[DscProperty()] | |
[string] $Code | |
[DscProperty()] | |
[string] $Phrase | |
} | |
# This class should normally not need to be updated | |
[DscResource()] | |
class ConfigurationResource : DscProperties { | |
# This proeprty should normally not need to be updated | |
[DscProperty(NotConfigurable)] | |
[Reason[]]$Reasons | |
[ConfigurationResource] Get() { | |
# Splat properties to Get function | |
$DscProperties = $this.GetConfigurableDscProperties() | |
$get = Get-Resource @DscProperties | |
# Adds Reasons to get | |
$additionalText = Get-AdditionalText | |
$get.Add('Reasons', $this.FormatReasons($get,$additionalText)) | |
# Return hashtable produced by Get function | |
return $get | |
} | |
[Void] Set() { | |
# Splat properties to Set function | |
$DscProperties = $this.GetConfigurableDscProperties() | |
Set-Resource @DscProperties | |
} | |
[Bool] Test() { | |
# Splat properties to Test function | |
$DscProperties = $this.GetConfigurableDscProperties() | |
return Test-Resource @DscProperties | |
} | |
[Hashtable] GetConfigurableDscProperties() { | |
# This method returns a hashtable of properties with two special workarounds | |
# The hashtable will not include any properties marked as "NotConfigurable" | |
# Any properties with a ValidateSet of "True","False" will beconverted to Boolean type | |
# The intent is to simplify splatting to functions | |
$dscProperties = @{} | |
foreach ($property in [DscProperties].GetProperties().Name) { | |
# Checks if "NotConfigurable" attribute is set | |
$notConfigurable = [DscProperties].GetProperty($property).GetCustomAttributes($false).Where({ $_ -is [System.Management.Automation.DscPropertyAttribute] }).NotConfigurable | |
if (!$notConfigurable) { | |
$value = $this.$property | |
# Gets the list of valid values from the ValidateSet attribute | |
$validateSet = [DscProperties].GetProperty($property).GetCustomAttributes($false).Where({ $_ -is [System.Management.Automation.ValidateSetAttribute] }).ValidValues | |
if ($validateSet) { | |
# Workaround for boolean types | |
if ($null -eq (Compare-Object @('True', 'False') $validateSet)) { | |
$value = [System.Convert]::ToBoolean($this.$property) | |
} | |
} | |
# Add property to new | |
$dscProperties.add($property, $value) | |
} | |
} | |
return $dscProperties | |
} | |
[Reason[]] FormatReasons([hashtable]$get,[string]$additionalText) { | |
# This method takes information about a DSC resource and returns an array of Reason objects | |
# Including text for phrase that will render well in a browser | |
$dscProperties = $this.GetConfigurableDscProperties() | |
$state0 = $state1 = $null | |
foreach ($key in $dscProperties.Keys) { | |
if ($dscProperties.$key -eq $get.$key) { | |
$state0 += "`t`t[+] $key" + ':' + "`n`t`t`tExpected value to be `"$($dscProperties.$key)`"`n`t`t`tActual value was `"$($get.$key)`"`n" | |
} | |
else { | |
$state1 += "`t`t[-] $key" + ':' + "`n`t`t`tExpected value to be `"$($dscProperties.$key)`"`n`t`t`tActual value was `"$($get.$key)`"`n" | |
} | |
} | |
$Reason = [reason]::new() | |
$Reason.code = $this.GetType().Name + ':' + $this.GetType().Name + ':Configuration' | |
$phrase = "The machine returned the following configuration details.`n`n" | |
$phrase += "`tSettings in desired state:`n$state0`n" | |
$phrase += "`tSettings not in desired state:`n$state1" | |
$phrase += "`n$additionalText" | |
$Reason.phrase = $phrase | |
$return = @() | |
$return += $Reason | |
return $return | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment