DscResources/DscLcmController/DscLcmController.schema.psm1
$dscLcmPostponeScript = @' $writeTranscripts = Get-ItemPropertyValue -Path HKLM:\SOFTWARE\DscLcmController -Name WriteTranscripts $path = Join-Path -Path ([System.Environment]::GetFolderPath('CommonApplicationData')) -ChildPath 'Dsc\LcmController' if ($writeTranscripts) { Start-Transcript -Path "$path\LcmPostpone.log" -Append } $currentLcmSettings = Get-DscLocalConfigurationManager $maxConsistencyCheckInterval = if ($currentLcmSettings.ConfigurationModeFrequencyMins -eq 30) { #44640) 44639 #value must be changed in order to reset the LCM timer } else { 44640 #minutes for 31 days } $maxRefreshInterval = if ($currentLcmSettings.RefreshFrequencyMins -eq 30) { #44640) 44639 #value must be changed in order to reset the LCM timer } else { 44640 #minutes for 31 days } $metaMofFolder = mkdir -Path "$path\MetaMof" -Force if (Test-Path -Path C:\Windows\System32\Configuration\MetaConfig.mof) { $mofFile = Copy-Item -Path C:\Windows\System32\Configuration\MetaConfig.mof -Destination "$path\MetaMof\localhost.meta.mof" -Force -PassThru } else { $mofFile = Get-Item -Path "$path\MetaMof\localhost.meta.mof" -ErrorAction Stop } $content = Get-Content -Path $mofFile.FullName -Raw -Encoding Unicode $pattern = '(ConfigurationModeFrequencyMins +=)( +\d+)(;)' $content = $content -replace $pattern, ('$1 {0}$3' -f $maxConsistencyCheckInterval) $pattern = '(RefreshFrequencyMins +=)( +\d+)(;)' $content = $content -replace $pattern, ('$1 {0}$3' -f $maxRefreshInterval) $content | Out-File -FilePath $mofFile.FullName -Encoding unicode Set-DscLocalConfigurationManager -Path $metaMofFolder -Verbose "$(Get-Date) - Postponed LCM" | Add-Content -Path "$path\LcmPostponeSummery.log" Stop-Transcript '@ $dscLcmControlScript = @' $writeTranscripts = Get-ItemPropertyValue -Path HKLM:\SOFTWARE\DscLcmController -Name WriteTranscripts $path = Join-Path -Path ([System.Environment]::GetFolderPath('CommonApplicationData')) -ChildPath 'Dsc\LcmController' if ($writeTranscripts) { Start-Transcript -Path "$path\LcmController.log" -Append } $namespace = 'root/Microsoft/Windows/DesiredStateConfiguration' $className = 'MSFT_DSCLocalConfigurationManager' $now = Get-Date $configurationMode = (Get-DscLocalConfigurationManager).ConfigurationMode $doConsistencyCheck = $false $doRefresh = $false $inMaintenanceWindow = $false $currentTime = Get-Date $maintenanceWindows = Get-ChildItem -Path HKLM:\SOFTWARE\DscLcmController\MaintenanceWindows $consistencyCheckIntervalOverride = Get-ItemPropertyValue -Path HKLM:\SOFTWARE\DscLcmController -Name ConsistencyCheckIntervalOverride $refreshIntervalOverride = Get-ItemPropertyValue -Path HKLM:\SOFTWARE\DscLcmController -Name RefreshIntervalOverride [datetime]$lastConsistencyCheck = try { Get-ItemPropertyValue -Path HKLM:\SOFTWARE\DscLcmController -Name LastConsistencyCheck } catch { Get-Date -Date 0 } [timespan]$consistencyCheckInterval = Get-ItemPropertyValue -Path HKLM:\SOFTWARE\DscLcmController -Name ConsistencyCheckInterval [datetime]$lastRefresh = try { Get-ItemPropertyValue -Path HKLM:\SOFTWARE\DscLcmController -Name lastRefresh } catch { Get-Date -Date 0 } [timespan]$refreshInterval = Get-ItemPropertyValue -Path HKLM:\SOFTWARE\DscLcmController -Name RefreshInterval $maintenanceWindowOverride = Get-ItemPropertyValue -Path HKLM:\SOFTWARE\DscLcmController -Name MaintenanceWindowOverride if ($maintenanceWindows) { $inMaintenanceWindow = foreach ($maintenanceWindow in $maintenanceWindows) { Write-Host "Reading maintenance window '$($maintenanceWindow.PSChildName)'" [datetime]$startTime = Get-ItemPropertyValue -Path $maintenanceWindow.PSPath -Name StartTime [timespan]$timespan = Get-ItemPropertyValue -Path $maintenanceWindow.PSPath -Name Timespan [datetime]$endTime = $startTime + $timespan [string]$dayOfWeek = try { Get-ItemPropertyValue -Path $maintenanceWindow.PSPath -Name DayOfWeek } catch { } [string]$on = try { Get-ItemPropertyValue -Path $maintenanceWindow.PSPath -Name On } catch { } if ($dayOfWeek) { if ((Get-Date).DayOfWeek -ne $dayOfWeek) { Write-Host "DayOfWeek is set to '$dayOfWeek'. Current day of week is '$((Get-Date).DayOfWeek)', maintenance window does not apply" continue } else { Write-Host "Maintenance Window is configured for week day '$dayOfWeek' which is the current day of week." } } if ($on) { $daysOfWeek = [System.Enum]::GetNames([System.DayOfWeek]) if ($on -ne 'last') { $on = [int][string]$on[0] } $daysInMonth = [datetime]::DaysInMonth($now.Year, $now.Month) $daysInMonth = for ($i = 1; $i -le $daysInMonth; $i++) { Get-Date -Date $now -Day $i } $daysInMonth = $daysInMonth | Where-Object { $_.DayOfWeek -eq $dayOfWeek } $daysInMonth = if ($on -eq 'last') { $daysInMonth | Select-Object -Last 1 } else { $daysInMonth | Select-Object -Index ($on - 1) } if ($daysInMonth.ToShortDateString() -ne $now.ToShortDateString()) { Write-Host "Today is not the '$on' $dayOfWeek in the current month" continue } else { Write-Host "The LCM is supposed to run on the '$on' $dayOfWeek which applies to today" } } Write-Host "Maintenance window: $($startTime) - $($endTime)." if ($currentTime -gt $startTime -and $currentTime -lt $endTime) { Write-Host "Current time '$currentTime' is in maintenance window '$($maintenanceWindow.PSChildName)'" Write-Host "IN MAINTENANCE WINDOW: Setting 'inMaintenanceWindow' to 'true' as the current time is in a maintanence windows." $true break } else { Write-Host "Current time '$currentTime' is not in maintenance window '$($maintenanceWindow.PSChildName)'" } } } else { Write-Host "No maintenance windows defined. Setting 'inMaintenanceWindow' to 'true'." $inMaintenanceWindow = $true } Write-Host if (-not $inMaintenanceWindow -and $maintenanceWindowOverride) { Write-Host "OVERRIDE: 'inMaintenanceWindow' is 'false' but 'maintenanceWindowOverride' is enabled, setting 'inMaintenanceWindow' to 'true'" $inMaintenanceWindow = $true } elseif (-not $inMaintenanceWindow) { Write-Host "NOT IN MAINTENANCE WINDOW: 'inMaintenanceWindow' is 'false'. The current time is not in any of the $($maintenanceWindows.Count) maintenance windows." } #always invoke the consistency check if in 'ApplyAndMonitor' but when in 'ApplyAndAutoCorrect' only if in maintenance window. if ($configurationMode -eq 'ApplyAndAutoCorrect' -and $inMaintenanceWindow) { $doConsistencyCheck = $true } elseif ($configurationMode -in 'ApplyAndMonitor', 'MonitorOnly') { Write-Host Write-Host "Setting 'doConsistencyCheck' to 'true' as the LCM is in '$((Get-DscLocalConfigurationManager).ConfigurationMode)' mode" $doConsistencyCheck = $true } $doRefresh = $inMaintenanceWindow #Consistency Check $nextConsistencyCheck = $lastConsistencyCheck + $consistencyCheckInterval Write-Host "" Write-Host "The previous consistency check was done on '$lastConsistencyCheck', the next one will not be triggered before '$nextConsistencyCheck'. ConsistencyCheckInterval is $consistencyCheckInterval." if ($currentTime -gt $nextConsistencyCheck) { Write-Host 'It is time to trigger a consistency check per the defined interval.' $doConsistencyCheck = $doConsistencyCheck -band 1 } else { if ($consistencyCheckIntervalOverride) { Write-Host "OVERRIDE: It is NOT time to trigger a consistency check per the defined interval but 'ConsistencyCheckIntervalOverride' is enabled." $doConsistencyCheck = $doConsistencyCheck -band 1 } else { Write-Host 'It is NOT time to trigger a consistency check per the defined interval.' $doConsistencyCheck = $doConsistencyCheck -band 0 } } Write-Host #Refresh Check $nextRefresh = $lastRefresh + $refreshInterval Write-Host "" Write-Host "The previous refresh check was done on '$lastRefresh', the next one will not triggered before '$nextRefresh'. RefreshInterval is $refreshInterval." if ($currentTime -gt $nextRefresh) { Write-Host 'It is time to trigger a refresh check per the defined interval.' $doRefresh = $doRefresh -band 1 } else { if ($refreshIntervalOverride) { Write-Host "OVERRIDE: It is NOT time to trigger a consistency check per the defined interval but 'RefreshIntervalOverride' is enabled." $doRefresh = $doRefresh -band 1 } else { Write-Host 'It is NOT time to trigger a refresh per the defined interval.' $doRefresh = $doRefresh -band 0 } } Write-Host $consistencyCheckErrors = $false $refreshErrors = $false if ($doConsistencyCheck) { Write-Host "ACTION: Invoking Cim Method 'PerformRequiredConfigurationChecks' with Flags '1' (Consistency Check)." try { Invoke-CimMethod -ClassName $className -Namespace $namespace -MethodName PerformRequiredConfigurationChecks -Arguments @{ Flags = [uint32]1 } -ErrorAction Stop | Out-Null $dscLcmControl = Get-Item -Path HKLM:\SOFTWARE\DscLcmController Set-ItemProperty -Path $dscLcmControl.PSPath -Name LastConsistencyCheck -Value (Get-Date) -Type String -Force } catch { Write-Error "Error invoking 'PerformRequiredConfigurationChecks'. The message is: '$($_.Exception.Message)'" $consistencyCheckErrors = $true } } else { Write-Host "NO ACTION: 'doConsistencyCheck' is false, not invoking Cim Method 'PerformRequiredConfigurationChecks' with Flags '1' (Consistency Check)." } if ($doRefresh) { Write-Host "ACTION: Invoking Cim Method 'PerformRequiredConfigurationChecks' with Flags'5' (Pull and Consistency Check)." try { Invoke-CimMethod -ClassName $className -Namespace $namespace -MethodName PerformRequiredConfigurationChecks -Arguments @{ Flags = [uint32]5 } -ErrorAction Stop | Out-Null $dscLcmControl = Get-Item -Path HKLM:\SOFTWARE\DscLcmController Set-ItemProperty -Path $dscLcmControl.PSPath -Name LastRefresh -Value (Get-Date) -Type String -Force } catch { Write-Error "Error invoking 'PerformRequiredConfigurationChecks'. The message is: '$($_.Exception.Message)'" $refreshErrors = $true } } else { Write-Host "NO ACTION: 'doRefresh' is false, not invoking Cim Method 'PerformRequiredConfigurationChecks' with Flags '5' (Pull and Consistency Check)." } $logItem = [pscustomobject]@{ CurrentTime = Get-Date InMaintenanceWindow = [int]$inMaintenanceWindow DoConsistencyCheck = $doConsistencyCheck DoRefresh = $doRefresh LastConsistencyCheck = $lastConsistencyCheck ConsistencyCheckInterval = $consistencyCheckInterval ConsistencyCheckIntervalOverride = $consistencyCheckIntervalOverride ConsistencyCheckErrors = $consistencyCheckErrors LastRefresh = $lastRefresh RefreshInterval = $refreshInterval RefreshIntervalOverride = $refreshIntervalOverride RefreshErrors = $refreshErrors } | Export-Csv -Path "$path\LcmControlSummery.txt" -Append if ($writeTranscripts) { Stop-Transcript } '@ Configuration DscLcmController { Param( [Parameter(Mandatory)] [timespan]$ConsistencyCheckInterval, [bool]$ConsistencyCheckIntervalOverride, [Parameter(Mandatory)] [timespan]$RefreshInterval, [bool]$RefreshIntervalOverride, [Parameter(Mandatory)] [timespan]$ControllerInterval, [Parameter(Mandatory)] [timespan]$PostponeInterval, [bool]$MaintenanceWindowOverride, [bool]$WriteTranscripts ) Import-DscResource -ModuleName xPSDesiredStateConfiguration Import-DscResource -ModuleName ComputerManagementDsc Import-DscResource -ModuleName PSDesiredStateConfiguration xRegistry DscLcmControl_ConsistencyCheckInterval { Key = 'HKEY_LOCAL_MACHINE\SOFTWARE\DscLcmController' ValueName = 'ConsistencyCheckInterval' ValueData = $ConsistencyCheckInterval ValueType = 'String' Ensure = 'Present' Force = $true } xRegistry DscLcmControl_PostponeInterval { Key = 'HKEY_LOCAL_MACHINE\SOFTWARE\DscLcmController' ValueName = 'PostponeInterval' ValueData = $PostponeInterval ValueType = 'String' Ensure = 'Present' Force = $true } xRegistry DscLcmControl_ConsistencyCheckIntervalOverride { Key = 'HKEY_LOCAL_MACHINE\SOFTWARE\DscLcmController' ValueName = 'ConsistencyCheckIntervalOverride' ValueData = [int]$ConsistencyCheckIntervalOverride ValueType = 'DWord' Ensure = 'Present' Force = $true } xRegistry DscLcmControl_RefreshInterval { Key = 'HKEY_LOCAL_MACHINE\SOFTWARE\DscLcmController' ValueName = 'RefreshInterval' ValueData = $RefreshInterval ValueType = 'String' Ensure = 'Present' Force = $true } xRegistry DscLcmControl_RefreshIntervalOverride { Key = 'HKEY_LOCAL_MACHINE\SOFTWARE\DscLcmController' ValueName = 'RefreshIntervalOverride' ValueData = [int]$RefreshIntervalOverride ValueType = 'DWord' Ensure = 'Present' Force = $true } xRegistry DscLcmControl_ControllerInterval { Key = 'HKEY_LOCAL_MACHINE\SOFTWARE\DscLcmController' ValueName = 'ControllerInterval' ValueData = $ControllerInterval ValueType = 'String' Ensure = 'Present' Force = $true } xRegistry DscLcmControl_MaintenanceWindowOverride { Key = 'HKEY_LOCAL_MACHINE\SOFTWARE\DscLcmController' ValueName = 'MaintenanceWindowOverride' ValueData = [int]$MaintenanceWindowOverride ValueType = 'DWord' Ensure = 'Present' Force = $true } xRegistry DscLcmControl_WriteTranscripts { Key = 'HKEY_LOCAL_MACHINE\SOFTWARE\DscLcmController' ValueName = 'WriteTranscripts' ValueData = [int]$WriteTranscripts ValueType = 'DWord' Ensure = 'Present' Force = $true } File DscLcmPostponeScript { Ensure = 'Present' Type = 'File' DestinationPath = 'C:\ProgramData\Dsc\LcmController\LcmPostpone.ps1' Contents = $dscLcmPostponeScript } ScheduledTask DscPostponeTask { DependsOn = '[File]DscLcmPostponeScript' TaskName = 'DscLcmPostpone' TaskPath = '\DscController' ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' ActionArguments = '-File C:\ProgramData\Dsc\LcmController\LcmPostpone.ps1' ScheduleType = 'Once' RepeatInterval = $PostponeInterval RepetitionDuration = 'Indefinitely' StartTime = (Get-Date) } File DscLcmControlScript { Ensure = 'Present' Type = 'File' DestinationPath = 'C:\ProgramData\Dsc\LcmController\LcmController.ps1' Contents = $dscLcmControlScript } ScheduledTask DscControlTask { DependsOn = '[File]DscLcmControlScript' TaskName = 'DscLcmController' TaskPath = '\DscController' ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' ActionArguments = '-File C:\ProgramData\Dsc\LcmController\LcmController.ps1' ScheduleType = 'Once' RepeatInterval = $ControllerInterval RepetitionDuration = 'Indefinitely' StartTime = (Get-Date) } } |