Skip to content

Commit a91133c

Browse files
committed
Refactor byte parsing and property testing
1 parent a8ef6ac commit a91133c

File tree

1 file changed

+93
-108
lines changed

1 file changed

+93
-108
lines changed

source/DSCResources/DSC_xServiceResource/DSC_xServiceResource.psm1

Lines changed: 93 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -89,25 +89,20 @@ function Get-TargetResource
8989
$serviceFailureActions = Get-ServiceFailureActions -Service $service.Name
9090

9191
$serviceResource = @{
92-
Name = $Name
93-
Ensure = 'Present'
94-
Path = $serviceCimInstance.PathName
95-
StartupType = $startupType
96-
BuiltInAccount = $builtInAccount
97-
State = $service.Status.ToString()
98-
DisplayName = $service.DisplayName
99-
Description = $serviceCimInstance.Description
100-
DesktopInteract = $serviceCimInstance.DesktopInteract
101-
Dependencies = $dependencies
102-
ResetPeriodSeconds = $serviceFailureActions.resetPeriodSeconds
103-
FailureCommand = $serviceFailureActions.failureCommand
104-
RebootMessage = $serviceFailureActions.rebootMessage
105-
failure1Action = $serviceFailureActions.failureAction1.actionType
106-
failure1Delay = $serviceFailureActions.failureAction1.delay
107-
failure2Action = $serviceFailureActions.failureAction2.actionType
108-
failure2Delay = $serviceFailureActions.failureAction2.delay
109-
failure3Action = $serviceFailureActions.failureAction3.actionType
110-
failure3Delay = $serviceFailureActions.failureAction3.delay
92+
Name = $Name
93+
Ensure = 'Present'
94+
Path = $serviceCimInstance.PathName
95+
StartupType = $startupType
96+
BuiltInAccount = $builtInAccount
97+
State = $service.Status.ToString()
98+
DisplayName = $service.DisplayName
99+
Description = $serviceCimInstance.Description
100+
DesktopInteract = $serviceCimInstance.DesktopInteract
101+
Dependencies = $dependencies
102+
ResetPeriodSeconds = $serviceFailureActions.resetPeriodSeconds
103+
FailureCommand = $serviceFailureActions.failureCommand
104+
RebootMessage = $serviceFailureActions.rebootMessage
105+
FailureActionsCollection = $serviceFailureActions.ActionsCollection
111106
}
112107
}
113108
else
@@ -529,28 +524,8 @@ function Test-TargetResource
529524
$FailureCommand,
530525

531526
[Parameter()]
532-
[ACTION_TYPE]
533-
$Failure1Action,
534-
535-
[Parameter()]
536-
[System.UInt32]
537-
$Failure1Delay,
538-
539-
[Parameter()]
540-
[ACTION_TYPE]
541-
$Failure2Action,
542-
543-
[Parameter()]
544-
[System.UInt32]
545-
$Failure2Delay,
546-
547-
[Parameter()]
548-
[ACTION_TYPE]
549-
$Failure3Action,
550-
551-
[Parameter()]
552-
[System.UInt32]
553-
$Failure3Delay,
527+
[System.Object[]]
528+
$FailureActionsCollection,
554529

555530
[Parameter()]
556531
[ValidateNotNull()]
@@ -723,47 +698,34 @@ function Test-TargetResource
723698
return $false
724699
}
725700

726-
# Check the failure 1 action
727-
if($PSBoundParameters.ContainsKey('Failure1Action') -and $Failure1Action -ine $serviceResource.failure1Action)
728-
{
729-
Write-Verbose -Message ($script:localizedData.ServicePropertyDoesNotMatch -f 'Failure1Action', $Name, $Failure1Action, $serviceResource.failure1Action)
730-
return $false
731-
}
701+
# Check the failure actions collection
702+
if($PSBoundParameters.ContainsKey('FailureActionsCollection')) {
703+
$inSync = $true
704+
if($FailureActionsCollection.count -ne $serviceResource.FailureActionsCollection.count) {
705+
Write-Verbose -Message ($script:localizedData.ServicePropertyDoesNotMatch -f 'FailureActionsCollection.count', $Name, $FailureActionsCollection.count, $serviceResource.FailureActionsCollection.count)
706+
return $false
707+
}
732708

733-
# Check failure 1 delay
734-
if($PSBoundParameters.ContainsKey('Failure1Delay') -and $Failure1Delay -ine $serviceResource.failure1Delay)
735-
{
736-
Write-Verbose -Message ($script:localizedData.ServicePropertyDoesNotMatch -f 'Failure1Delay', $Name, $Failure1Delay, $serviceResource.failure1Delay)
737-
return $false
738-
}
709+
foreach ($actionIndex in (0..($FailureActionsCollection.count - 1))) {
710+
$parameterAction = $FailureActionsCollection[$actionIndex]
711+
$serviceResourceAction = $serviceResource.FailureActionsCollection[$actionIndex]
739712

740-
# Check the failure 2 action
741-
if($PSBoundParameters.ContainsKey('Failure2Action') -and $Failure2Action -ine $serviceResource.failure2Action)
742-
{
743-
Write-Verbose -Message ($script:localizedData.ServicePropertyDoesNotMatch -f 'Failure2Action', $Name, $Failure2Action, $serviceResource.failure2Action)
744-
return $false
745-
}
713+
if($parameterAction.type -ne $serviceResourceAction.type) {
714+
Write-Verbose -Message ($script:localizedData.ServicePropertyDoesNotMatch -f "FailureActionsCollection Action $actionIndex type", $Name, $parameterAction.type, $serviceResourceAction.type)
715+
$inSync = $false
716+
}
746717

747-
# Check failure 2 delay
748-
if($PSBoundParameters.ContainsKey('Failure2Delay') -and $Failure2Delay -ine $serviceResource.failure2Delay)
749-
{
750-
Write-Verbose -Message ($script:localizedData.ServicePropertyDoesNotMatch -f 'Failure2Delay', $Name, $Failure2Delay, $serviceResource.failure2Delay)
751-
return $false
752-
}
718+
if($parameterAction.delaySeconds -ne $serviceResourceAction.delaySeconds) {
719+
Write-Verbose -Message ($script:localizedData.ServicePropertyDoesNotMatch -f "FailureActionsCollection Action $actionIndex delaySeconds", $Name, $parameterAction.delaySeconds, $serviceResourceAction.delaySeconds)
720+
$inSync = $false
721+
}
722+
}
753723

754-
# Check the failure 3 action
755-
if($PSBoundParameters.ContainsKey('Failure3Action') -and $Failure3Action -ine $serviceResource.failure3Action)
756-
{
757-
Write-Verbose -Message ($script:localizedData.ServicePropertyDoesNotMatch -f 'Failure3Action', $Name, $Failure3Action, $serviceResource.failure3Action)
758-
return $false
724+
if(-not $inSync) {
725+
return $inSync
726+
}
759727
}
760728

761-
# Check failure 3 delay
762-
if($PSBoundParameters.ContainsKey('Failure3Delay') -and $Failure3Delay -ine $serviceResource.failure3Delay)
763-
{
764-
Write-Verbose -Message ($script:localizedData.ServicePropertyDoesNotMatch -f 'Failure3Delay', $Name, $Failure3Delay, $serviceResource.failure3Delay)
765-
return $false
766-
}
767729
}
768730

769731
return $true
@@ -2029,9 +1991,7 @@ function Get-ServiceFailureActions {
20291991
failureActionCount = $null
20301992
failureCommand = $null
20311993
rebootMessage = $null
2032-
failureAction1 = @{actionType = $null; delay = $null}
2033-
failureAction2 = @{actionType = $null; delay = $null}
2034-
failureAction3 = @{actionType = $null; delay = $null}
1994+
ActionsCollection = $null
20351995
}
20361996

20371997
if($registryData.GetvalueNames() -match 'FailureCommand') {
@@ -2048,62 +2008,87 @@ function Get-ServiceFailureActions {
20482008

20492009
# The first four bytes represent the Reset Period. The bytes are little endian
20502010
# so they are reversed, converted to hex, and then cast to an integer.
2051-
$failureActions.resetPeriodSeconds = Convert-RegistryBinaryValueToInt -Bytes $failureActionsBinaryData -Offset 0
2011+
$failureActions.resetPeriodSeconds = Get-FailureActionsProperty -PropertyName ResetPeriodSeconds -Bytes $failureActionsBinaryData
20522012

20532013
# Next found bytes indicate the presence of a reboot message in case one of the chosen failure actions is
20542014
# SC_ACTION_REBOOT. The actual value of the message is stored in the 'RebootMessage' property
2055-
$failureActions.hasRebootMessage = Convert-RegistryBinaryValueToInt -Bytes $failureActionsBinaryData -Offset 1
2015+
$failureActions.hasRebootMessage = Get-FailureActionsProperty -PropertyName HasRebootMsg -Bytes $failureActionsBinaryData
20562016

20572017
# The next four bytes indicate whether a failure action run command exists. This command
20582018
# would be run in the case one of the failure actions chosen is SC_ACTION_RUN_COMMAND
20592019
# If this value is true then the actual command string is stored in a different value.
2060-
$failureActions.hasFailureCommand = Convert-RegistryBinaryValueToInt -Bytes $failureActionsBinaryData -Offset 2
2020+
$failureActions.hasFailureCommand = Get-FailureActionsProperty -PropertyName HasFailureCommand -Bytes $failureActionsBinaryData
20612021

20622022
# These four bytes give the count of how many reboot failure actions have been defined.
20632023
# Up to three actions may be defined.
2064-
$failureActions.failureActionCount = Convert-RegistryBinaryValueToInt -Bytes $failureActionsBinaryData -Offset 3
2024+
$failureActions.failureActionCount = Get-FailureActionsProperty -PropertyName FailureActionCount -Bytes $failureActionsBinaryData
20652025

20662026
if($failureActions.failureActionCount -gt 0) {
2067-
foreach ($item in 1..$failureActions.failureActionCount) {
2068-
2069-
# Manually counting the array offset is easier than implementing some weird array arithmetic.
2070-
$offset = switch ($item) {
2071-
1 { 5 }
2072-
2 { 7 }
2073-
3 { 9 }
2074-
Default {-1}
2075-
}
2076-
# Occasionaly a service will store an array acount greater than 3. As far as I can tell
2077-
# A count greater than 3 has no meaning, so we only support 3.
2078-
if($offset -lt 0) { break }
2079-
$failureActions."failureAction$item".actionType = [ACTION_TYPE](Convert-RegistryBinaryValueToInt -Bytes $failureActionsBinaryData -offset $offset)
2080-
$failureActions."failureAction$item".delay = Convert-RegistryBinaryValueToInt -Bytes $failureActionsBinaryData -offset ($offset + 1)
2081-
}
2027+
$failureActions.ActionsCollection = Get-FailureActionCollection -Bytes $failureActionsBinaryData -ActionsCount $failureActions.failureActionCount
20822028
}
20832029
}
20842030

20852031
$failureActions
20862032
}
20872033
}
20882034

2089-
function Convert-RegistryBinaryValueToInt
2035+
function Get-FailureActionsProperty
2036+
{
2037+
[CmdletBinding()]
2038+
param (
2039+
[Parameter(Mandatory)]
2040+
[ValidateSet('ResetPeriodSeconds','HasRebootMsg','HasFailureCommand','FailureActionCount')]
2041+
[System.String]
2042+
$PropertyName,
2043+
[Parameter(Mandatory)]
2044+
[System.Byte[]]
2045+
$Bytes
2046+
)
2047+
2048+
process {
2049+
$byteRange = switch ($PropertyName) {
2050+
ResetPeriodSeconds { 0..3 }
2051+
HasRebootMsg { 4..7 }
2052+
HasFailureCommand { 8..11 }
2053+
FailureActionCount { 12..15 }
2054+
Default {}
2055+
}
2056+
2057+
[System.BitConverter]::ToInt32($Bytes[$byteRange],0)
2058+
}
2059+
}
2060+
2061+
function Get-FailureActionCollection
20902062
{
20912063
[CmdletBinding()]
20922064
param (
20932065
[Parameter(Mandatory)]
2094-
[byte[]]
2066+
[System.Byte[]]
20952067
$Bytes,
20962068
[Parameter(Mandatory)]
2097-
[int]
2098-
$Offset,
2099-
[int]
2100-
$Size = 4
2069+
[System.UInt32]
2070+
$ActionsCount
21012071
)
21022072

21032073
process {
2104-
$lastByte = (($offset + 1) * 4) - 1
2105-
$firstByte = $lastbyte - ($size - 1)
2106-
# Bytes in the registry are little endian, so we reverse them before conversion.
2107-
[int]"0x$(($bytes[$lastByte..$firstByte] | %{"{0:x}" -f $_}) -join '' | out-string)"
2074+
$actionsCollection = New-Object System.Collections.Generic.List[PSObject]
2075+
2076+
foreach($action in 0..($ActionsCount - 1)) {
2077+
2078+
# The structure of the _SERVICE_FAILURE_ACTIONS struct encoded in this registry key
2079+
# dictates that the array of _SC_ACTION structs will always start at byte 20.
2080+
# The 8 is because each _SC_ACTION is a 8 byte struct.
2081+
$actionTypeByteRange = (20 + (8 * $action))..(20 + (8 * $action) + 3)
2082+
$actionDelayByteRange = (20 + (8 * $action) + 4)..(20 + (8 * $action) + 7)
2083+
2084+
$currentAction = [PSCustomObject]@{
2085+
type = [ACTION_TYPE]([System.BitConverter]::ToInt32($Bytes[$actionTypeByteRange],0))
2086+
delaySeconds = [System.BitConverter]::ToInt32($Bytes[$actionDelayByteRange],0)
2087+
}
2088+
2089+
$actionsCollection.Add($currentAction) | Out-Null
2090+
}
2091+
2092+
$actionsCollection
21082093
}
21092094
}

0 commit comments

Comments
 (0)