Requirements/Checklist.psm1

181 lines
5.4 KiB
PowerShell

enum Status {
NotRun
Pass
Fail
}
class Log {
# We originally used [Status] values as keys instead of casting [Status] values to strings.
# We cast values to strings for now due to a bug in PSScriptAnalyzer v1.16.1
static [hashtable] $Symbols = @{
Pass = [char]8730
Fail = "X"
NotRun = " "
}
static [hashtable] $Colors = @{
Pass = "Green"
NotRun = "Yellow"
Fail = "Red"
}
static [int] $LastLineLength
static [void] WriteLine([string] $message, [Status] $status) {
$message = [Log]::FormatMessage($message, $status)
[Log]::LastLineLength = $message.Length
$color = [Log]::Colors[ [string]$status ]
Write-Host $message -ForegroundColor $color -NoNewline
}
static [void] OverwriteLine([string] $message, [Status] $status) {
Write-Host "`r$(' ' * [Log]::LastLineLength)" -NoNewline
$message = [Log]::FormatMessage($message, $status)
[Log]::LastLineLength = $message.Length
$color = [Log]::Colors[ [string]$status ]
Write-Host "`r$message" -ForegroundColor $color
}
static [string] FormatMessage([string] $message, [Status] $status) {
$symbol = [Log]::Symbols[ [string]$status ]
return "$(Get-Date -Format 'hh:mm:ss') [ $symbol ] $message"
}
static [void] WriteError([string] $message) {
Write-Host "`n$message`n" -ForegroundColor Red
exit -1
}
}
<#
.SYNOPSIS
Ensures a requirement is met.
.DESCRIPTION
This cmdlet allows for declaratively defining requirements and implementing consistent logging and idempotency around the status of the requirements.
.PARAMETER Describe
A description of the requirement that is enforced.
.PARAMETER Test
If present, 'Test' is a scriptblock that returns 'true' if the requirement is already met and the 'Set' scriptblock should not run. If not present, 'Set' will always run.
.PARAMETER Set
A scriptblock that imposes the requirement when run. If a "Test' scriptblock is not provided, 'Set' must be idempotent.
.PARAMETER Message
An error message printed if an idempotent 'Set' scriptblock fails during execution.
.EXAMPLE
# A non-idempotent 'Set' scriptblock
Invoke-ChecklistRequirement `
-Describe "'Hello world' is logged" `
-Test {Get-Content $MyLogFilePath | ? {$_ -eq "Hello world"}} `
-Set {"Hello world" >> $MyLogFilePath}
# An idempotent 'Set' scriptblock
Invoke-ChecklistRequirement `
-Describe "'Hello world' is logged" `
-Set {"Hello world" > $MyLogFilePath} `
-Message "Could not log 'Hello World'"
#>
function Invoke-ChecklistRequirement {
Param(
[Parameter(Mandatory, ParameterSetName = "ApplyIfNeeded")]
[Parameter(Mandatory, ParameterSetName = "ApplyAlways")]
[Parameter(Mandatory, ParameterSetName = "Information")]
[ValidateNotNullOrEmpty()]
[string] $Describe,
[Parameter(Mandatory, ParameterSetName = "ApplyIfNeeded")]
[ValidateNotNullOrEmpty()]
[scriptblock] $Test,
[Parameter(Mandatory, ParameterSetName = "ApplyIfNeeded")]
[Parameter(Mandatory, ParameterSetName = "ApplyAlways")]
[ValidateNotNullOrEmpty()]
[scriptblock] $Set,
[Parameter(Mandatory, ParameterSetName = "ApplyAlways")]
[ValidateNotNullOrEmpty()]
[string] $Message,
[switch] $ListRequirement
)
try {
if ($ListRequirement) {
[Log]::WriteLine("$Describe`n", [Status]::NotRun)
return
}
switch ($PSCmdlet.ParameterSetName) {
"ApplyIfNeeded" {
[Log]::WriteLine($Describe, [Status]::NotRun)
if (&$Test) {
[Log]::OverwriteLine($Describe, [Status]::Pass)
}
else {
&$Set | Out-Null
if (&$Test) {
[Log]::OverwriteLine($Describe, [Status]::Pass)
}
else {
[Log]::OverwriteLine($Describe, [Status]::Fail)
[Log]::WriteError("Requirement validation failed")
}
}
}
"ApplyAlways" {
[Log]::WriteLine($Describe, [Status]::NotRun)
if (&$Set) {
[Log]::OverwriteLine($Describe, [Status]::Pass)
}
else {
[Log]::OverwriteLine($Describe, [Status]::Fail)
[Log]::WriteError($Message)
}
}
"Information" {
[Log]::WriteLine("$Describe`n", [Status]::NotRun)
}
}
}
catch {
[Log]::OverwriteLine($Describe, [Status]::Fail)
Write-Host ""
throw $_
}
}
function Invoke-ChecklistDscRequirement {
Param(
[string]$Describe,
[string]$ResourceName,
[string]$ModuleName,
[hashtable]$Property
)
$dscParams = @{
Name = $ResourceName
ModuleName = $ModuleName
Property = $Property
}
Invoke-ChecklistRequirement `
-Describe $Describe `
-Test {Invoke-DscResource -Method "Test" @dscParams} `
-Set {Invoke-DscResource -Method "Set" @dscParams}
}