181 lines
5.4 KiB
PowerShell
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}
|
||
|
|
||
|
}
|