tasks/DscResource.Authoring.build.ps1
|
<# .SYNOPSIS Build task for creating Microsoft DSC adapted resource manifests from a built PowerShell module using the DscResource.Authoring module. .DESCRIPTION Provides two Invoke-Build tasks that use the DscResource.Authoring module to generate Microsoft DSC adapted resource manifests from class-based DSC resources found in the built module: - Create_DscAdaptedResourceManifests: Creates one `.dsc.adaptedResource.json` file per class-based DSC resource found in the built module and writes them alongside the module manifest. - Create_DscResourceManifestsList: Creates a single `.dsc.manifests.json` bundle file that contains every adapted resource manifest and writes it alongside the module manifest. Both tasks target the module manifest produced by the ModuleBuilder build step and therefore must run after the module has been built (i.e., after `Build_Module_ModuleBuilder`). Configuration for both tasks is read from the `DscResource.Authoring` section of the build configuration file (e.g., `build.yaml`). .PARAMETER ProjectName The name of the project being built. .PARAMETER SourcePath The path to the source directory of the module. .PARAMETER OutputDirectory The base directory for all build output. Defaults to 'output' relative to the build root. .PARAMETER BuiltModuleSubdirectory The sub-directory under OutputDirectory where the built module is placed. .PARAMETER VersionedOutputDirectory Whether the built module is placed in a versioned sub-directory. Defaults to $true. .PARAMETER ModuleVersion The version of the module being built. .PARAMETER BuildInfo The build configuration hashtable, typically populated from `build.yaml`. .NOTES This task file is intended to be placed in the Sampler module's tasks directory so that it is exported as an alias and loaded via the `ModuleBuildTasks` mechanism in `build.yaml`. The DscResource.Authoring module must be available to the build environment before these tasks run. Add it to `RequiredModules.psd1` to ensure it is resolved as a build dependency. #> param ( [Parameter()] [System.String] $ProjectName = (property ProjectName ''), [Parameter()] [System.String] $SourcePath = (property SourcePath ''), [Parameter()] [System.String] $OutputDirectory = (property OutputDirectory (Join-Path $BuildRoot 'output')), [Parameter()] [System.String] $BuiltModuleSubdirectory = (property BuiltModuleSubdirectory ''), [Parameter()] [System.Management.Automation.SwitchParameter] $VersionedOutputDirectory = (property VersionedOutputDirectory $true), [Parameter()] [System.String] $ModuleVersion = (property ModuleVersion ''), [Parameter()] [System.Collections.Hashtable] $BuildInfo = (property BuildInfo @{ }) ) <# .SYNOPSIS Converts property override configuration entries into DscPropertyOverride objects. .DESCRIPTION Maps each hashtable entry from the build configuration PropertyOverrides section into a DscPropertyOverride object understood by Update-DscAdaptedResourceManifest. Each entry must contain at least a 'Name' key. Supported optional keys are 'Description', 'Title', 'JsonSchema', 'RemoveKeys', and 'Required'. This function must only be called after DscResource.Authoring has been imported into the session. .PARAMETER OverrideConfig An array of hashtables, each describing one property override. .EXAMPLE $overrides = ConvertTo-DscPropertyOverrideFromConfig -OverrideConfig $configEntries Converts a list of configuration hashtables into DscPropertyOverride objects. #> function ConvertTo-DscPropertyOverrideFromConfig { [CmdletBinding()] [OutputType([object[]])] param ( [Parameter(Mandatory = $true)] [object[]] $OverrideConfig ) $overrides = [System.Collections.Generic.List[object]]::new() foreach ($entry in $OverrideConfig) { if (-not $entry.ContainsKey('Name') -or [string]::IsNullOrEmpty($entry['Name'])) { Write-Warning 'Skipping a property override entry with a missing or empty Name key.' continue } $overrideParams = @{ Name = [string] $entry['Name'] } if ($entry.ContainsKey('Description') -and -not [string]::IsNullOrEmpty($entry['Description'])) { $overrideParams['Description'] = [string] $entry['Description'] } if ($entry.ContainsKey('Title') -and -not [string]::IsNullOrEmpty($entry['Title'])) { $overrideParams['Title'] = [string] $entry['Title'] } if ($entry.ContainsKey('JsonSchema') -and $null -ne $entry['JsonSchema']) { $overrideParams['JsonSchema'] = $entry['JsonSchema'] } if ($entry.ContainsKey('RemoveKeys') -and $null -ne $entry['RemoveKeys']) { $overrideParams['RemoveKeys'] = @($entry['RemoveKeys']) } if ($entry.ContainsKey('Required') -and $null -ne $entry['Required']) { $overrideParams['Required'] = [bool] $entry['Required'] } $overrides.Add((New-DscPropertyOverride @overrideParams)) } return , $overrides.ToArray() } <# Synopsis: Creates individual Microsoft DSC adapted resource manifest files (.dsc.adaptedResource.json) for every class-based DSC resource found in the built module. One file is created per resource class and written to the root of the built module directory alongside the module manifest (.psd1). The file name follows the pattern `<ModuleName>.<ResourceName>.dsc.adaptedResource.json`. Use this task if you want to leverage Microsoft's DSC engine with the PowerShell discovery extension. #> Task Create_DscAdaptedResourceManifests { # Get the task variables. See https://github.com/gaelcolas/Sampler#task-variables. . Set-SamplerTaskVariable "`tBuilt Module Manifest = '$BuiltModuleManifest'" "`tBuilt Module Base = '$BuiltModuleBase'" if ([System.String]::IsNullOrEmpty($BuiltModuleManifest) -or -not (Test-Path -Path $BuiltModuleManifest)) { throw "The built module manifest '$BuiltModuleManifest' could not be found. Make sure the module has been built before running this task." } $taskConfig = @{} if ($BuildInfo.ContainsKey('DscResource.Authoring') -and $BuildInfo['DscResource.Authoring'].ContainsKey('Create_DscAdaptedResourceManifests')) { $taskConfig = $BuildInfo['DscResource.Authoring']['Create_DscAdaptedResourceManifests'] } if ($null -eq $taskConfig) { $taskConfig = @{} } "`tTask Configuration = $(if ($taskConfig.Count -gt 0) { $taskConfig | ConvertTo-Json -Depth 5 } else { '(none)' })" $fileNamePattern = '{ProjectName}.{ResourceName}.dsc.adaptedResource.json' if ($taskConfig.ContainsKey('FileNamePattern') -and -not [System.String]::IsNullOrEmpty($taskConfig['FileNamePattern'])) { $fileNamePattern = $taskConfig['FileNamePattern'] } "`tFile Name Pattern = '$fileNamePattern'" $propertyOverridesConfig = @{} if ($taskConfig.ContainsKey('PropertyOverrides') -and $null -ne $taskConfig['PropertyOverrides']) { $propertyOverridesConfig = $taskConfig['PropertyOverrides'] } Write-Build -Color 'DarkGray' -Text "`tGenerating adapted resource manifests from '$BuiltModuleManifest'..." $adaptedManifests = New-DscAdaptedResourceManifest -Path $BuiltModuleManifest if (-not $adaptedManifests) { Write-Build -Color 'Yellow' -Text "`tNo class-based DSC resources found in '$BuiltModuleManifest'. No manifest files were created." return } foreach ($manifest in $adaptedManifests) { $resourceName = ($manifest.Type -split '/')[-1] if ($propertyOverridesConfig.ContainsKey($resourceName)) { Write-Build -Color 'DarkGray' -Text "`tApplying property overrides for '$resourceName'..." $overrideList = ConvertTo-DscPropertyOverrideFromConfig -OverrideConfig @($propertyOverridesConfig[$resourceName]) $manifest = $manifest | Update-DscAdaptedResourceManifest -PropertyOverride $overrideList } $outputFileName = $fileNamePattern -replace '\{ProjectName\}', $ProjectName -replace '\{ResourceName\}', $resourceName $outputFilePath = Join-Path -Path $BuiltModuleBase -ChildPath $outputFileName Write-Build -Color 'DarkGray' -Text "`tWriting '$outputFilePath'..." $manifest.ToJson() | Set-Content -Path $outputFilePath -Encoding 'UTF8' -Force Write-Build -Color 'Green' -Text "`tCreated adapted resource manifest '$outputFileName'." } Write-Build -Color 'Green' -Text "`tCreated $(@($adaptedManifests).Count) adapted resource manifest file(s) in '$BuiltModuleBase'." } <# Synopsis: Creates a single Microsoft DSC resource manifests bundle file (.dsc.manifests.json) that contains all adapted resource manifests for every class-based DSC resource found in the built module. The bundle file is written to the root of the built module directory alongside the module manifest (.psd1). The output file name can be configured via the `OutputFileName` key under `DscResource.Authoring.Create_DscResourceManifestsList` in the build configuration file. When not specified it defaults to `<ProjectName>.dsc.manifests.json`. Use this task if you want to leverage Microsoft's DSC engine with the PowerShell discovery extension. #> Task Create_DscResourceManifestsList { # Get the task variables. See https://github.com/gaelcolas/Sampler#task-variables. . Set-SamplerTaskVariable "`tBuilt Module Manifest = '$BuiltModuleManifest'" "`tBuilt Module Base = '$BuiltModuleBase'" if ([System.String]::IsNullOrEmpty($BuiltModuleManifest) -or -not (Test-Path -Path $BuiltModuleManifest)) { throw "The built module manifest '$BuiltModuleManifest' could not be found. Make sure the module has been built before running this task." } $taskConfig = @{} if ($BuildInfo.ContainsKey('DscResource.Authoring') -and $BuildInfo['DscResource.Authoring'].ContainsKey('Create_DscResourceManifestsList')) { $taskConfig = $BuildInfo['DscResource.Authoring']['Create_DscResourceManifestsList'] } if ($null -eq $taskConfig) { $taskConfig = @{} } "`tTask Configuration = $(if ($taskConfig.Count -gt 0) { $taskConfig | ConvertTo-Json -Depth 5 } else { '(none)' })" $outputFileName = "$ProjectName.dsc.manifests.json" if ($taskConfig.ContainsKey('OutputFileName') -and -not [System.String]::IsNullOrEmpty($taskConfig['OutputFileName'])) { $outputFileName = $taskConfig['OutputFileName'] } "`tOutput File Name = '$outputFileName'" $outputFilePath = Join-Path -Path $BuiltModuleBase -ChildPath $outputFileName "`tOutput File Path = '$outputFilePath'" $propertyOverridesConfig = @{} if ($taskConfig.ContainsKey('PropertyOverrides') -and $null -ne $taskConfig['PropertyOverrides']) { $propertyOverridesConfig = $taskConfig['PropertyOverrides'] } Write-Build -Color 'DarkGray' -Text "`tImporting module 'DscResource.Authoring'..." Import-Module -Name 'DscResource.Authoring' -ErrorAction 'Stop' Write-Build -Color 'DarkGray' -Text "`tGenerating adapted resource manifests from '$BuiltModuleManifest'..." $adaptedManifests = New-DscAdaptedResourceManifest -Path $BuiltModuleManifest if (-not $adaptedManifests) { Write-Build -Color 'Yellow' -Text "`tNo class-based DSC resources found in '$BuiltModuleManifest'. No manifest list file was created." return } if ($propertyOverridesConfig.Count -gt 0) { $adaptedManifests = foreach ($manifest in $adaptedManifests) { $resourceName = ($manifest.Type -split '/')[-1] if ($propertyOverridesConfig.ContainsKey($resourceName)) { Write-Build -Color 'DarkGray' -Text "`tApplying property overrides for '$resourceName'..." $overrideList = ConvertTo-DscPropertyOverrideFromConfig -OverrideConfig @($propertyOverridesConfig[$resourceName]) $manifest | Update-DscAdaptedResourceManifest -PropertyOverride $overrideList } else { $manifest } } } Write-Build -Color 'DarkGray' -Text "`tBuilding manifest list from $(@($adaptedManifests).Count) adapted resource manifest(s)..." $manifestList = $adaptedManifests | New-DscResourceManifest Write-Build -Color 'DarkGray' -Text "`tWriting '$outputFilePath'..." $manifestList.ToJson() | Set-Content -Path $outputFilePath -Encoding 'UTF8' -Force Write-Build -Color 'Green' -Text "`tCreated DSC resource manifests list '$outputFileName' with $($manifestList.AdaptedResources.Count) adapted resource(s) in '$BuiltModuleBase'." } |