DSCResources/ResourceController/ResourceController.psm1
# Suppress Global Vars PSSA Error because $global:DSCMachineStatus must be allowed [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidGlobalVars', '')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')] param() function Get-TargetResource { [CmdletBinding()] [OutputType([Hashtable])] Param ( [Parameter(Mandatory = $true)] [string] $InstanceName, [Parameter(Mandatory = $true)] [System.String] $ResourceName, [Parameter()] [System.String] $Properties, [Parameter()] [Boolean] $SuppressReboot, [Parameter()] [Microsoft.Management.Infrastructure.CimInstance[]] $MaintenanceWindow, [Parameter(Mandatory = $true)] [System.String] $ResourceVersion, [Parameter()] [Microsoft.Management.Infrastructure.CimInstance[]] $Credentials ) $functionName = "Get-TargetResource" $PropertiesHashTable = [scriptblock]::Create($Properties).Invoke() $PropertiesHashTable = ConvertTo-Hashtable -Hashtable $PropertiesHashTable -Credentials $Credentials $dscResource = (Get-DscResource -Name $ResourceName).Where( {$_.Version -eq $ResourceVersion})[0] try { Import-Module $dscResource.Path -Function $functionName -Prefix $ResourceName try { Test-ParameterValidation -Name $functionName.Replace("-", "-$ResourceName") -Values $PropertiesHashTable } catch { throw $_.Exception.Message } Write-Verbose "Parameters passed in have validated succesfully." $splatProperties = Get-ValidParameter -Name $functionName.Replace("-", "-$ResourceName") -Values $PropertiesHashTable Write-Verbose "Calling Get-TargetResource" $get = & "Get-${ResourceName}TargetResource" @splatProperties $CimGetResults = New-Object -TypeName 'System.Collections.ObjectModel.Collection`1[Microsoft.Management.Infrastructure.CimInstance]' foreach ($row in $get.Keys.GetEnumerator()) { $value = $get.$row $CimProperties = @{ Namespace = 'root/Microsoft/Windows/DesiredStateConfiguration' ClassName = "MSFT_KeyValuePair" Property = @{ Key = "$row" Value = "$value" } } $CimGetResults += New-CimInstance -ClientOnly @CimProperties } $returnValue = @{ InstanceName = $InstanceName ResourceName = $ResourceName Properties = $Properties Result = $CimGetResults SuppressReboot = $SuppressReboot MaintenanceWindow = $MaintenanceWindow } return $returnValue } finally { Remove-Module -Name $dscResource.ResourceType } } function Test-TargetResource { [OutputType([Boolean])] [CmdletBinding()] Param ( [Parameter(Mandatory = $true)] [string] $InstanceName, [Parameter(Mandatory = $true)] [string] $ResourceName, [Parameter()] [System.String] $Properties, [Parameter()] [Boolean] $SuppressReboot, [Parameter()] [Microsoft.Management.Infrastructure.CimInstance[]] $MaintenanceWindow, [Parameter(Mandatory = $true)] [System.String] $ResourceVersion, [Parameter()] [Microsoft.Management.Infrastructure.CimInstance[]] $Credentials ) $functionName = "Test-TargetResource" $PropertiesHashTable = [scriptblock]::Create($Properties).Invoke() $PropertiesHashTable = ConvertTo-Hashtable -Hashtable $PropertiesHashTable -Credentials $Credentials $dscResource = (Get-DscResource -Name $ResourceName).Where( {$_.Version -eq $ResourceVersion})[0] try { Import-Module $dscResource.Path -Function $functionName -Prefix $ResourceName try { $null = Test-ParameterValidation -Name $functionName.Replace("-", "-$ResourceName") -Values $PropertiesHashTable } catch { throw $_.Exception.Message } Write-Verbose "Parameters passed in have validated succesfully." $splatProperties = Get-ValidParameter -Name $functionName.Replace("-", "-$ResourceName") -Values $PropertiesHashTable Write-Verbose "Calling Test-TargetResource" $result = &"Test-${ResourceName}TargetResource" @splatProperties return $result } finally { Remove-Module -Name $dscResource.ResourceType } } function Set-TargetResource { [CmdletBinding()] Param ( [Parameter(Mandatory = $true)] [string] $InstanceName, [Parameter(Mandatory = $true)] [string] $ResourceName, [Parameter()] [System.String] $Properties, [Parameter()] [Boolean] $SuppressReboot, [Parameter()] [Microsoft.Management.Infrastructure.CimInstance[]] $MaintenanceWindow, [Parameter(Mandatory = $true)] [System.String] $ResourceVersion, [Parameter()] [Microsoft.Management.Infrastructure.CimInstance[]] $Credentials ) $inMaintenanceWindow = $false foreach ($window in $MaintenanceWindow) { $maintenanceWindowProperties = @{} $params = @("Frequency", "StartTime", "EndTime", "DayOfWeek", "Week", "Day", "StartDate", "EndDate") foreach ($param in $params) { if ($window.$param) { $maintenanceWindowProperties.Add($param, $window.$param) } } if ($(Test-MaintenanceWindow @maintenanceWindowProperties)) { $inMaintenanceWindow = $true } } if (-not $inMaintenanceWindow -and $MaintenanceWindow) { Write-Verbose "You are outside the maintenance window. No changes will be made." return } $functionName = "Set-TargetResource" $PropertiesHashTable = [scriptblock]::Create($Properties).Invoke() $PropertiesHashTable = ConvertTo-Hashtable -Hashtable $PropertiesHashTable -Credentials $Credentials $dscResource = (Get-DscResource -Name $ResourceName).Where( {$_.Version -eq $ResourceVersion})[0] try { Import-Module $dscResource.Path -Function $functionName -Prefix $ResourceName try { Test-ParameterValidation -Name $functionName.Replace("-", "-$ResourceName") -Values $PropertiesHashTable } catch { throw $_.Exception.Message } Write-Verbose "Parameters passed in have validated succesfully." $splatProperties = Get-ValidParameter -Name $functionName.Replace("-", "-$ResourceName") -Values $PropertiesHashTable Write-Verbose "Calling Set-TargetResource." &"Set-${ResourceName}TargetResource" @splatProperties -Verbose if ($SuppressReboot -and $global:DSCMachineStatus -ne 0) { $global:DSCMachineStatus = 0 } } finally { Remove-Module -Name $dscResource.ResourceType } } function Test-MaintenanceWindow { param( [Parameter(Mandatory = $true)] [string] $Frequency, [Parameter()] [Nullable[DateTime]] $StartTime, [Parameter()] [Nullable[DateTime]] $EndTime, [Parameter()] [string[]] $DayOfWeek, [Parameter()] [int[]] $Week, [Parameter()] [int[]] $Day, [Parameter()] [Nullable[DateTime]] $StartDate, [Parameter()] [Nullable[DateTime]] $EndDate ) $now = Get-Date if ($StartDate) { if ($now.Date -lt $StartDate.Date) { return $false } } if ($EndDate) { if ($now.Date -gt $EndDate.Date) { return $false } } if ($StartTime) { if ($now.TimeOfDay -lt $StartTime.TimeOfDay) { return $false } } if ($EndTime) { if ($now.TimeOfDay -gt $EndTime.TimeOfDay) { return $false } } switch ($Frequency) { 'Daily' { if ($DayOfWeek.Count -eq 0) { $DayOfWeek = @("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday") } if (-not ($DayOfWeek -Contains $now.DayOfWeek)) { return $false } } 'Weekly' { if ($DayOfWeek.Count -eq 0) { $DayOfWeek = @("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday") } if ($Week.Count -eq 0) { $Week = @("0", "1", "2", "3", "4") } if (-not ($DayOfWeek -Contains $now.DayOfWeek)) { return $false } $dow = $now.DayOfWeek $WorkingDate = Get-Date -Year $now.Year -Month $now.Month -Day 1 $weekCount = 0 for ($i = 1; $i -le $now.Day; $i++) { if ($WorkingDate.DayOfWeek -eq $dow) { $weekCount++ } $WorkingDate = $WorkingDate.AddDays(1) } if (-not ($Week -contains $weekCount)) { if ($Week -contains 0) { $WorkingDate = Get-Date -Year $now.Year -Month $now.Month -Day $([DateTime]::DaysInMonth($now.Year, $now.Month)) while ($dow -ne $WorkingDate.DayOfWeek) { $WorkingDate = $WorkingDate.AddDays(-1) } if ($WorkingDate.Day -ne $now.Day) { return $false } } else { return $false } } } 'Monthly' { if ($Day.Count -eq 0) { $Day = @("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31") } if (-not ($Day -contains $now.Day)) { if ($Day -contains 0) { $lastDayofMonth = $([DateTime]::DaysInMonth($now.Year, $now.Month)) if ($lastDayofMonth -ne $now.Day) { return $false } } else { return $false } } } } return $true } function Assert-Validation { param( [Parameter(Mandatory = $true)] $element, [Parameter(Mandatory = $true)] [psobject] $ParameterMetadata ) $BindingFlags = 'static', 'nonpublic', 'instance' $errorMessage = @() foreach ($attribute in $ParameterMetadata.Attributes) { try { $Method = $attribute.GetType().GetMethod('ValidateElement', $BindingFlags) if ($Method) { $Method.Invoke($attribute, @($element)) } } catch { $errorMessage += "Error on parameter $($ParameterMetadata.Name): $($_.Exception.InnerException.Message)" } } if ($errorMessage.Count -gt 0) { throw $errorMessage -join "`n" } } function Test-ParameterValidation { param( [Parameter(Mandatory = $true)] [string] $Name, [Parameter(Mandatory = $true)] [Hashtable] $Values ) $ignoreResourceParameters = [System.Management.Automation.Cmdlet]::CommonParameters + [System.Management.Automation.Cmdlet]::OptionalCommonParameters $errorMessage = @() $command = Get-Command -Name $name $parameterNames = $command.Parameters foreach ($name in $parameterNames.Keys) { if ($ignoreResourceParameters -notcontains $name) { $metadata = $command.Parameters.$($name) if ($Values.$($name)) { try { Assert-Validation -element $Values.$($name) -ParameterMetadata $metadata } catch { $errorMessage += $_.Exception.Message } } elseif ($($metadata.Attributes | Where-Object {$_.TypeId.Name -eq "ParameterAttribute"}).Mandatory) { $errorMessage += "Parameter '$name' is mandatory." } } } if ($errorMessage.Count -gt 0) { throw $errorMessage -join "`n" } } function Get-ValidParameter { param( [Parameter(Mandatory = $true)] [string] $Name, [Parameter(Mandatory = $true)] [Hashtable] $Values ) $ignoreResourceParameters = [System.Management.Automation.Cmdlet]::CommonParameters + [System.Management.Automation.Cmdlet]::OptionalCommonParameters $command = Get-Command -Name $name $parameterNames = $command.Parameters $properties = @{} foreach ($name in $parameterNames.Keys) { if ($ignoreResourceParameters -notcontains $name) { if ($Values.ContainsKey($name)) { $properties.Add($Name, $Values.$name) } } } return $properties } function ConvertTo-Hashtable { [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '')] param ( [Parameter(Mandatory = $true)] $Hashtable, [Parameter()] [Microsoft.Management.Infrastructure.CimInstance[]] $Credentials ) $tempHashtable = @{} foreach ($row in $Hashtable.Keys) { if ($Hashtable.$row -imatch "\[pscredential\]:(.*)") { $split = $Hashtable.$row -split ':', 2 $credential = $Credentials | Where-Object -FilterScript { $_.Name -eq $split[1] } | Select-Object -First 1 $password = ConvertTo-SecureString -String $credential.Credential.Password -AsPlainText -Force $credentialObject = [PsCredential]::new($credential.Credential.UserName, $password) $tempHashtable.Add($row, $credentialObject ) } else { $tempHashtable.Add($row, $Hashtable.$row ) } } return $tempHashtable } Export-ModuleMember -Function *-TargetResource |