126 lines
6.0 KiB
PowerShell
126 lines
6.0 KiB
PowerShell
function Test-DSCConfigurationDataDeployment {
|
|
[CmdletBinding()]
|
|
Param(
|
|
[Parameter(Mandatory=$true)]
|
|
[string]
|
|
$Path,
|
|
|
|
[Parameter(Mandatory=$false)]
|
|
[switch]
|
|
$PassThru
|
|
)
|
|
|
|
$ResolvedPath = Repair-ConfigurationDataPathEncoding -Path $Path
|
|
$Issues = @()
|
|
|
|
if(-not (Test-Path -LiteralPath $ResolvedPath -PathType Leaf)){
|
|
$Issues += [PSCustomObject]@{
|
|
Severity = "Error"
|
|
Code = "DeploymentNotFound"
|
|
Path = $ResolvedPath
|
|
Message = "Deployment file was not found."
|
|
}
|
|
}else{
|
|
try {
|
|
$DeploymentData = Import-PowerShellDataFile -LiteralPath $ResolvedPath
|
|
}
|
|
catch {
|
|
$Issues += [PSCustomObject]@{
|
|
Severity = "Error"
|
|
Code = "DeploymentImportFailed"
|
|
Path = $ResolvedPath
|
|
Message = $_.Exception.Message
|
|
}
|
|
}
|
|
}
|
|
|
|
if($null -ne $DeploymentData){
|
|
if(-not (Test-ConfigurationDataDictionaryKey -Dictionary $DeploymentData -Key "Metadata")){
|
|
$Issues += [PSCustomObject]@{ Severity = "Error"; Code = "MetadataMissing"; Path = $ResolvedPath; Message = "Metadata block is missing." }
|
|
}elseif($DeploymentData.Metadata.TemplateType -ne "Deployment"){
|
|
$Issues += [PSCustomObject]@{ Severity = "Error"; Code = "InvalidTemplateType"; Path = $ResolvedPath; Message = "Metadata.TemplateType must be [Deployment]." }
|
|
}
|
|
|
|
foreach($Key in @("Parameters", "Variables", "Resources")){
|
|
if(-not (Test-ConfigurationDataDictionaryKey -Dictionary $DeploymentData -Key $Key)){
|
|
$Issues += [PSCustomObject]@{ Severity = "Error"; Code = "$Key`Missing"; Path = $ResolvedPath; Message = "Required block [$Key] is missing." }
|
|
}
|
|
}
|
|
|
|
if((Test-ConfigurationDataDictionaryKey -Dictionary $DeploymentData -Key "Resources") -and
|
|
(-not (Test-ConfigurationDataDictionaryKey -Dictionary $DeploymentData.Resources -Key "AllNodes"))){
|
|
$Issues += [PSCustomObject]@{ Severity = "Error"; Code = "AllNodesMissing"; Path = $ResolvedPath; Message = "Resources.AllNodes is missing." }
|
|
}
|
|
|
|
if((Test-ConfigurationDataDictionaryKey -Dictionary $DeploymentData -Key "Resources") -and
|
|
(Test-ConfigurationDataDictionaryKey -Dictionary $DeploymentData.Resources -Key "AllNodes")){
|
|
$NodeNames = @{}
|
|
foreach($Node in @($DeploymentData.Resources.AllNodes)){
|
|
if(-not (Test-ConfigurationDataDictionaryKey -Dictionary $Node -Key "NodeName") -or [string]::IsNullOrWhiteSpace([string]$Node.NodeName)){
|
|
$Issues += [PSCustomObject]@{ Severity = "Error"; Code = "NodeNameMissing"; Path = $ResolvedPath; Message = "An AllNodes item has no NodeName." }
|
|
continue
|
|
}
|
|
|
|
$NodeName = [string]$Node.NodeName
|
|
if($NodeNames.ContainsKey($NodeName)){
|
|
$Issues += [PSCustomObject]@{ Severity = "Error"; Code = "DuplicateNodeName"; Path = $ResolvedPath; Message = "Duplicate NodeName [$NodeName]." }
|
|
}else{
|
|
$NodeNames[$NodeName] = $true
|
|
}
|
|
}
|
|
}
|
|
|
|
if($DeploymentData.Metadata -is [System.Collections.IDictionary] -and
|
|
(Test-ConfigurationDataDictionaryKey -Dictionary $DeploymentData.Metadata -Key "Sources") -and
|
|
$DeploymentData.Metadata.Sources -is [System.Collections.IDictionary] -and
|
|
(Test-ConfigurationDataDictionaryKey -Dictionary $DeploymentData.Metadata.Sources -Key "Files")){
|
|
foreach($Source in @($DeploymentData.Metadata.Sources.Files)){
|
|
$SourcePath = if($Source -is [System.Collections.IDictionary]){ [string]$Source.Path } else { [string]$Source }
|
|
if([string]::IsNullOrWhiteSpace($SourcePath)){
|
|
$Issues += [PSCustomObject]@{ Severity = "Warning"; Code = "SourcePathMissing"; Path = $ResolvedPath; Message = "A source file entry has no path." }
|
|
continue
|
|
}
|
|
|
|
try {
|
|
$ActualPath = Resolve-ConfigurationDataSourcePath -Path $SourcePath -DeploymentPath $ResolvedPath
|
|
}
|
|
catch {
|
|
$Issues += [PSCustomObject]@{ Severity = "Warning"; Code = "SourceMissing"; Path = $SourcePath; Message = $_.Exception.Message }
|
|
continue
|
|
}
|
|
|
|
if($Source -is [System.Collections.IDictionary] -and (Test-ConfigurationDataDictionaryKey -Dictionary $Source -Key "Hash")){
|
|
$Algorithm = [string](Get-ConfigurationDataDictionaryValue -Dictionary $Source -Key "Algorithm" -DefaultValue "SHA256")
|
|
$ActualHash = Get-FileHash -LiteralPath $ActualPath -Algorithm $Algorithm
|
|
if($ActualHash.Hash -ne [string]$Source.Hash){
|
|
$Issues += [PSCustomObject]@{
|
|
Severity = "Warning"
|
|
Code = "SourceChanged"
|
|
Path = $SourcePath
|
|
Message = "Source file hash has changed."
|
|
ExpectedHash = [string]$Source.Hash
|
|
ActualHash = $ActualHash.Hash
|
|
Algorithm = $Algorithm
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}else{
|
|
$Issues += [PSCustomObject]@{ Severity = "Warning"; Code = "SourceMetadataMissing"; Path = $ResolvedPath; Message = "Metadata.Sources.Files is missing." }
|
|
}
|
|
}
|
|
|
|
$Result = [PSCustomObject]@{
|
|
Path = $ResolvedPath
|
|
IsValid = @($Issues | Where-Object { $_.Severity -eq "Error" }).Count -eq 0
|
|
IsStale = @($Issues | Where-Object { $_.Code -in @("SourceChanged", "SourceMissing") }).Count -gt 0
|
|
Issues = $Issues
|
|
}
|
|
|
|
if($PassThru){
|
|
return $Result
|
|
}
|
|
|
|
return $Result.IsValid -and (-not $Result.IsStale)
|
|
}
|