From 990840579ee976a0d0304364659ce56599e2afec Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Thu, 26 May 2022 15:52:04 +0200 Subject: [PATCH] Update pipeline files from Sampler (#268) --- .gitattributes | 9 +- .../ISSUE_TEMPLATE/Problem_with_resource.md | 57 --- .../ISSUE_TEMPLATE/Problem_with_resource.yml | 87 ++++ .github/ISSUE_TEMPLATE/Resource_proposal.md | 21 - .github/ISSUE_TEMPLATE/Resource_proposal.yml | 39 ++ .github/ISSUE_TEMPLATE/config.yml | 6 + .github/PULL_REQUEST_TEMPLATE.md | 51 +- .gitignore | 19 +- .vscode/settings.json | 7 +- .vscode/tasks.json | 2 +- CHANGELOG.md | 10 +- CONTRIBUTING.md | 4 + GitVersion.yml | 6 +- LICENSE | 12 +- README.md | 95 +--- RequiredModules.psd1 | 11 +- Resolve-Dependency.ps1 | 346 +++++++++---- SECURITY.md | 30 ++ azure-pipelines.yml | 92 ++-- build.ps1 | 464 ++++++++++++------ build.yaml | 50 +- codecov.yml | 5 +- source/DSCResources/DSC_Cluster/README.md | 10 + source/DSCResources/DSC_ClusterDisk/README.md | 7 + .../DSCResources/DSC_ClusterNetwork/README.md | 29 ++ .../DSC_ClusterPreferredOwner/README.md | 8 + .../DSC_ClusterProperty/README.md | 7 + .../DSCResources/DSC_ClusterQuorum/README.md | 9 + .../DSCResources/DSC_WaitForCluster/README.md | 7 + source/FailoverClusterDsc.psm1 | 4 + source/WikiSource/Home.md | 76 +++ .../Unit/DSC_ClusterPreferredOwner.Tests.ps1 | 6 +- 32 files changed, 1053 insertions(+), 533 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/Problem_with_resource.md create mode 100644 .github/ISSUE_TEMPLATE/Problem_with_resource.yml delete mode 100644 .github/ISSUE_TEMPLATE/Resource_proposal.md create mode 100644 .github/ISSUE_TEMPLATE/Resource_proposal.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 SECURITY.md create mode 100644 source/DSCResources/DSC_Cluster/README.md create mode 100644 source/DSCResources/DSC_ClusterDisk/README.md create mode 100644 source/DSCResources/DSC_ClusterNetwork/README.md create mode 100644 source/DSCResources/DSC_ClusterPreferredOwner/README.md create mode 100644 source/DSCResources/DSC_ClusterProperty/README.md create mode 100644 source/DSCResources/DSC_ClusterQuorum/README.md create mode 100644 source/DSCResources/DSC_WaitForCluster/README.md create mode 100644 source/FailoverClusterDsc.psm1 create mode 100644 source/WikiSource/Home.md diff --git a/.gitattributes b/.gitattributes index d49b050..96c2e0d 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,8 +1,15 @@ # Needed for publishing of examples, build worker defaults to core.autocrlf=input. -* text eol=crlf +* text eol=autocrlf + +*.mof text eol=crlf +*.sh text eol=lf +*.svg eol=lf # Ensure any exe files are treated as binary *.exe binary *.jpg binary *.xl* binary *.pfx binary +*.png binary +*.dll binary +*.so binary diff --git a/.github/ISSUE_TEMPLATE/Problem_with_resource.md b/.github/ISSUE_TEMPLATE/Problem_with_resource.md deleted file mode 100644 index 159e81c..0000000 --- a/.github/ISSUE_TEMPLATE/Problem_with_resource.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -name: Problem with a resource -about: If you have a problem, bug, or enhancement with a resource in this resource module. ---- - -#### Details of the scenario you tried and the problem that is occurring - -#### Verbose logs showing the problem - -#### Suggested solution to the issue - -#### The DSC configuration that is used to reproduce the issue (as detailed as possible) -```powershell -# insert configuration here -``` - -#### The operating system the target node is running - - -#### Version and build of PowerShell the target node is running - - -#### Version of the DSC module that was used diff --git a/.github/ISSUE_TEMPLATE/Problem_with_resource.yml b/.github/ISSUE_TEMPLATE/Problem_with_resource.yml new file mode 100644 index 0000000..2e6c6ba --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Problem_with_resource.yml @@ -0,0 +1,87 @@ +name: Problem with a resource +description: If you have a problem, bug, or enhancement with a resource in this resource module. +labels: [] +assignees: [] +body: + - type: markdown + attributes: + value: | + Please prefix the issue title (above) with the resource name, e.g. 'ResourceName: Short description of my issue'! + + Your feedback and support is greatly appreciated, thanks for contributing! + - type: textarea + id: description + attributes: + label: Problem description + description: Details of the scenario you tried and the problem that is occurring. + validations: + required: true + - type: textarea + id: logs + attributes: + label: Verbose logs + description: | + Verbose logs showing the problem. **NOTE! Sensitive information should be obfuscated.** _Will be automatically formatted as plain text._ + placeholder: | + Paste verbose logs here + render: text + validations: + required: true + - type: textarea + id: configuration + attributes: + label: DSC configuration + description: | + The DSC configuration that is used to reproduce the issue (as detailed as possible). **NOTE! Sensitive information should be obfuscated.** _Will be automatically formatted as PowerShell code._ + placeholder: | + Paste DSC configuration here + render: powershell + validations: + required: true + - type: textarea + id: suggestedSolution + attributes: + label: Suggested solution + description: Do you have any suggestions how to solve the issue? + validations: + required: true + - type: textarea + id: targetNodeOS + attributes: + label: Operating system the target node is running + description: | + Please provide as much as possible about the target node, for example edition, version, build, and language. _Will be automatically formatted as plain text._ + + On OS with WMF 5.1 the following command can help get this information: `Get-ComputerInfo -Property @('OsName','OsOperatingSystemSKU','OSArchitecture','WindowsVersion','WindowsBuildLabEx','OsLanguage','OsMuiLanguages')` + placeholder: | + Add operating system information here + render: text + validations: + required: true + - type: textarea + id: targetNodePS + attributes: + label: PowerShell version and build the target node is running + description: | + Please provide the version and build of PowerShell the target node is running. _Will be automatically formatted as plain text._ + + To help with this information, please run this command: `$PSVersionTable` + placeholder: | + Add PowerShell information here + render: text + validations: + required: true + - type: textarea + id: moduleVersion + attributes: + label: FailoverClusterDsc version + description: | + Please provide the version of the FailoverClusterDsc module that was used. _Will be automatically formatted as plain text._ + + To help with this information, please run this command: `Get-Module -Name 'FailoverClusterDsc' -ListAvailable | ft Name,Version,Path` + placeholder: | + Add module information here + render: text + validations: + required: true + diff --git a/.github/ISSUE_TEMPLATE/Resource_proposal.md b/.github/ISSUE_TEMPLATE/Resource_proposal.md deleted file mode 100644 index 9f2a069..0000000 --- a/.github/ISSUE_TEMPLATE/Resource_proposal.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -name: New resource proposal -about: If you have a new resource proposal that you think should be added to this resource module. ---- - -### Description - -### Proposed properties - -### Special considerations or limitations diff --git a/.github/ISSUE_TEMPLATE/Resource_proposal.yml b/.github/ISSUE_TEMPLATE/Resource_proposal.yml new file mode 100644 index 0000000..2ddd098 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Resource_proposal.yml @@ -0,0 +1,39 @@ +name: New resource proposal +description: If you have a new resource proposal that you think should be added to this resource module. +title: "NewResourceName: New resource proposal" +labels: [] +assignees: [] +body: + - type: markdown + attributes: + value: | + Please replace `NewResourceName` in the issue title (above) with your proposed resource name. + + Thank you for contributing and making this resource module better! + - type: textarea + id: description + attributes: + label: Resource proposal + description: Provide information how this resource will/should work and how it will help users. + validations: + required: true + - type: textarea + id: proposedProperties + attributes: + label: Proposed properties + description: | + List all the proposed properties that the resource should have (key, required, write, and/or read). For each property provide a detailed description, the data type, if a default value should be used, and if the property is limited to a set of values. + value: | + Property | Type qualifier | Data type | Description | Default value | Allowed values + --- | --- | --- | --- | --- | --- + PropertyName | Key | String | Detailed description | None | None + validations: + required: true + - type: textarea + id: considerations + attributes: + label: Special considerations or limitations + description: | + Provide any considerations or limitations you can think of that a contributor should take in account when coding the proposed resource, and or what limitations a user will encounter or should consider when using the proposed resource. + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..9917040 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,6 @@ +blank_issues_enabled: false +contact_links: + - name: "Virtual PowerShell User Group #DSC channel" + url: https://dsccommunity.org/community/contact/ + about: "To talk to the community and maintainers of DSC Community, please visit the #DSC channel." + diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 73031e9..8c6462e 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,50 +1,49 @@ + #### Pull Request (PR) description + #### This Pull Request (PR) fixes the following issues + #### Task list + -- [ ] Added an entry to the change log under the Unreleased section of the - file CHANGELOG.md. Entry should say what was changed and how that - affects users (if applicable), and reference the issue being resolved - (if applicable). + +- [ ] Added an entry to the change log under the Unreleased section of the file CHANGELOG.md. + Entry should say what was changed and how that affects users (if applicable), and + reference the issue being resolved (if applicable). - [ ] Resource documentation added/updated in README.md. -- [ ] Resource parameter descriptions added/updated in README.md, schema.mof - and comment-based help. +- [ ] Resource parameter descriptions added/updated in README.md, schema.mof and comment-based + help. - [ ] Comment-based help added/updated. - [ ] Localization strings added/updated in all localization files as appropriate. - [ ] Examples appropriately added/updated. diff --git a/.gitignore b/.gitignore index 7ccfd2e..e4af2f9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,18 @@ -.vscode -.vs output/ + +**.bak +*.local.* +!**/README.md +.kitchen/ + +*.nupkg +*.suo +*.user +*.coverage +.vscode +.vs +.psproj +.sln +markdownissues.txt +node_modules +package-lock.json diff --git a/.vscode/settings.json b/.vscode/settings.json index 93d9aae..72f9013 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,12 +7,15 @@ "powershell.codeFormatting.whitespaceAroundOperator": true, "powershell.codeFormatting.whitespaceAfterSeparator": true, "powershell.codeFormatting.ignoreOneLineBlock": false, + "powershell.codeFormatting.pipelineIndentationStyle": "IncreaseIndentationForFirstPipeline", "powershell.codeFormatting.preset": "Custom", "powershell.codeFormatting.alignPropertyValuePairs": true, - "files.trimTrailingWhitespace": true, - "files.insertFinalNewline": true, + "powershell.developer.bundledModulesPath": "${cwd}/output/RequiredModules", "powershell.scriptAnalysis.settingsPath": ".vscode\\analyzersettings.psd1", "powershell.scriptAnalysis.enable": true, + "files.trimTrailingWhitespace": true, + "files.trimFinalNewlines": true, + "files.insertFinalNewline": true, "files.associations": { "*.ps1xml": "xml" }, diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 4085e3e..2991140 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -41,7 +41,7 @@ "label": "build", "type": "shell", "command": "&${cwd}/build.ps1", - "args": ["-AutoRestore"], + "args": [], "presentation": { "echo": true, "reveal": "always", diff --git a/CHANGELOG.md b/CHANGELOG.md index bf9e749..7ce55ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -# Change log for FailOverClusterDsc +# Changelog for FailOverClusterDsc The format is based on and uses the types of changes according to [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). @@ -7,8 +7,16 @@ For older change log history see the [historic changelog](HISTORIC_CHANGELOG.md) ## [Unreleased] +- FailoverClusterDsc + - Update pipeline files to the latest from the Sampler project. + - Move somme documentation from README-md to the GitHub repository Wiki. +- ClusterPreferredOwner + - Minor fix to tests. + ## [2.0.0] - 2022-05-25 + ### Changed + - BREAKING CHANGE - Renamed _xFailOverCluster_ to _FailoverClusterDsc_ - fixes [Issue #69](https://github.com/PowerShell/xFailOverCluster/issues/69). - Changed all MSFT_xResourceName to DSC_ResourceName. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f6895ab..3544bcc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,3 +1,7 @@ # Contributing Please check out common DSC Community [contributing guidelines](https://dsccommunity.org/guidelines/contributing). + +## Running the Tests + +If want to know how to run this module's tests you can look at the [Testing Guidelines](https://dsccommunity.org/guidelines/testing-guidelines/#running-tests) diff --git a/GitVersion.yml b/GitVersion.yml index a4f4cf2..8a364cd 100644 --- a/GitVersion.yml +++ b/GitVersion.yml @@ -1,7 +1,7 @@ mode: ContinuousDelivery -next-version: 1.14.0 -major-version-bump-message: '\s?(breaking|major|breaking\schange)' -minor-version-bump-message: '\s?(add|feature|minor)' +next-version: 0.0.1 +major-version-bump-message: '(breaking\schange|breaking|major)\b' +minor-version-bump-message: '(adds?|features?|minor)\b' patch-version-bump-message: '\s?(fix|patch)' no-bump-message: '\+semver:\s?(none|skip)' assembly-informational-format: '{NuGetVersionV2}+Sha.{Sha}.Date.{CommitDate}' diff --git a/LICENSE b/LICENSE index f4c3ee9..53a72e6 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ -The MIT License (MIT) +MIT License -Copyright (c) DSC Community contributors. +Copyright (c) DSC Community contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -9,13 +9,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index f887bc3..d89e17c 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ -# FailOverClusterDsc +# FailoverClusterDsc -[![Build Status](https://dev.azure.com/dsccommunity/FailOverClusterDsc/_apis/build/status/dsccommunity.FailOverClusterDsc?branchName=main)](https://dev.azure.com/dsccommunity/FailOverClusterDsc/_build/latest?definitionId=5&branchName=main) -![Azure DevOps coverage (branch)](https://img.shields.io/azure-devops/coverage/dsccommunity/FailOverClusterDsc/5/main) -[![codecov](https://codecov.io/gh/dsccommunity/FailOverClusterDsc/branch/main/graph/badge.svg)](https://codecov.io/gh/dsccommunity/FailOverClusterDsc) -[![Azure DevOps tests](https://img.shields.io/azure-devops/tests/dsccommunity/FailOverClusterDsc/5/main)](https://dsccommunity.visualstudio.com/FailOverClusterDsc/_test/analytics?definitionId=5&contextType=build) -[![PowerShell Gallery (with prereleases)](https://img.shields.io/powershellgallery/vpre/FailOverClusterDsc?label=FailOverClusterDsc%20Preview)](https://www.powershellgallery.com/packages/FailOverClusterDsc/) -[![PowerShell Gallery](https://img.shields.io/powershellgallery/v/FailOverClusterDsc?label=FailOverClusterDsc)](https://www.powershellgallery.com/packages/FailOverClusterDsc/) +This module contains DSC resources for the management and +configuration of Windows Server Failover Cluster. -This module contains DSC resources for deployment and configuration of -Windows Server Failover Cluster +[![Build Status](https://dev.azure.com/dsccommunity/FailoverClusterDsc/_apis/build/status/dsccommunity.FailoverClusterDsc?branchName=main)](https://dev.azure.com/dsccommunity/FailoverClusterDsc/_build/latest?definitionId=5&branchName=main) +![Azure DevOps coverage (branch)](https://img.shields.io/azure-devops/coverage/dsccommunity/FailoverClusterDsc/5/main) +[![codecov](https://codecov.io/gh/dsccommunity/FailoverClusterDsc/branch/main/graph/badge.svg)](https://codecov.io/gh/dsccommunity/FailoverClusterDsc) +[![Azure DevOps tests](https://img.shields.io/azure-devops/tests/dsccommunity/FailoverClusterDsc/5/main)](https://dsccommunity.visualstudio.com/FailoverClusterDsc/_test/analytics?definitionId=5&contextType=build) +[![PowerShell Gallery (with prereleases)](https://img.shields.io/powershellgallery/vpre/FailoverClusterDsc?label=FailoverClusterDsc%20Preview)](https://www.powershellgallery.com/packages/FailoverClusterDsc/) +[![PowerShell Gallery](https://img.shields.io/powershellgallery/v/FailoverClusterDsc?label=FailoverClusterDsc)](https://www.powershellgallery.com/packages/FailoverClusterDsc/) ## Code of Conduct @@ -29,6 +29,19 @@ Please check out common DSC Community [contributing guidelines](https://dsccommu A full list of changes in each version can be found in the [change log](CHANGELOG.md). +## Documentation + +The documentation can be found in the [FailoverClusterDsc Wiki](https://github.com/dsccommunity/FailoverClusterDsc/wiki). +The DSC resources schema files is used to automatically update the +documentation on each PR merge. + +### Examples + +You can review the [Examples](/source/Examples) directory in the FailoverClusterDsc module +for some general use scenarios for all of the resources that are in the module. + +The resource examples are also available in the [FailoverClusterDsc Wiki](https://github.com/dsccommunity/FailoverClusterDsc/wiki). + ## Resources * [**Cluster**](#cluster) Ensures that a group of machines form a cluster. @@ -45,15 +58,6 @@ A full list of changes in each version can be found in the [change log](CHANGELO ### Cluster -Used to configure a failover cluster. If the cluster does not exist, it will be -created in the domain and the static IP address will be assigned to the cluster. -When the cluster exist (either it was created or already existed), it will add -the target node ($env:COMPUTERNAME) to the cluster. - -#### Requirements for Cluster - -* Target machine must be running Windows Server 2008 R2 or later. - #### Parameters for Cluster * **`[String]` Name** _(Key)_: Name of the failover cluster. @@ -87,12 +91,6 @@ the target node ($env:COMPUTERNAME) to the cluster. ### ClusterDisk -Configures shared disks in a cluster. - -#### Requirements for ClusterDisk - -* Target machine must be running Windows Server 2008 R2 or later. - #### Parameters for ClusterDisk * **`[String]` Number** _(Key)_: The disk number of the cluster disk. @@ -109,14 +107,6 @@ Configures shared disks in a cluster. ### ClusterNetwork -Configures a cluster network in a failover cluster. - -This resource is only able to change properties on cluster networks. To add or remove networks from the cluster, add or remove them from the cluster members. By adding a new subnet on one of the cluster nodes, the network will be added to the cluster, and metadata can be set using the ClusterNetwork module. - -#### Requirements for ClusterNetwork - -* Target machine must be running Windows Server 2008 R2 or later. - #### Parameters for ClusterNetwork * **`[String]` Address** _(Key)_: The address for the cluster network in the format @@ -132,33 +122,12 @@ This resource is only able to change properties on cluster networks. To add or the cluster network metric number is not in desired state it will be changed to match this metric number. -#### Role parameter - -This parameter sets the role of the cluster network. If the cluster network role -is not in desired state it will change to match this role. - -The cluster network role can be set to either the value 0, 1 or 3. - -0 = Do not allow cluster network communication -1 = Allow cluster network communication only -3 = Allow cluster network communication and client connectivity - -See this article for more information about cluster network role values; -[Configuring Windows Failover Cluster Networks](https://blogs.technet.microsoft.com/askcore/2014/02/19/configuring-windows-failover-cluster-networks/) - #### Examples for ClusterNetwork * [Change properties of two cluster network resources in the failover cluster](/source/Examples/Resources/ClusterNetwork/1-ClusterNetwork_ChangeClusterNetworkConfig.ps1) ### ClusterPreferredOwner -Configures preferred owners of a cluster group and cluster resources in a failover -cluster. - -#### Requirements for ClusterPreferredOwner - -* Target machine must be running Windows Server 2008 R2 or later. - #### Parameters for ClusterPreferredOwner * **`[String]` ClusterGroup** _(Key)_: Name of the cluster group. @@ -176,12 +145,6 @@ cluster. ### ClusterProperty -Configures cluster properties on a failover cluster. - -#### Requirements for ClusterProperty - -* Target machine must be running Windows Server 2008 R2 or later. - #### Parameters for ClusterProperty * **`[String]` Name** _(Key)_: Name of the cluster. @@ -241,14 +204,6 @@ Configures cluster properties on a failover cluster. ### ClusterQuorum -Configures quorum in a cluster. For information on how to choose the correct -quorum type, please see the article -[Understanding Quorum Configurations in a Failover Cluster](https://technet.microsoft.com/en-us/library/cc731739(v=ws.11).aspx). - -#### Requirements for ClusterQuorum - -* Target machine must be running Windows Server 2008 R2 or later. - #### Parameters for ClusterQuorum * **`[String]` IsSingleInstance** _(Key)_: Specifies the resource is a single @@ -273,12 +228,6 @@ quorum type, please see the article ### WaitForCluster -Ensures that a node waits for a remote cluster is created. - -#### Requirements for WaitForCluster - -* Target machine must be running Windows Server 2008 R2 or later. - #### Parameters for WaitForCluster * **`[String]` Name** _(Key)_: Name of the cluster to wait for. diff --git a/RequiredModules.psd1 b/RequiredModules.psd1 index e8d6133..b239cfb 100644 --- a/RequiredModules.psd1 +++ b/RequiredModules.psd1 @@ -1,23 +1,24 @@ @{ PSDependOptions = @{ - AddToPath = $True + AddToPath = $true Target = 'output\RequiredModules' Parameters = @{ + Repository = 'PSGallery' } } - invokeBuild = 'latest' + InvokeBuild = 'latest' PSScriptAnalyzer = 'latest' - pester = '4.10.1' + Pester = '4.10.1' Plaster = 'latest' ModuleBuilder = 'latest' ChangelogManagement = 'latest' Sampler = 'latest' 'Sampler.GitHubTasks' = 'latest' MarkdownLinkCheck = 'latest' + 'DscResource.Common' = 'latest' 'DscResource.Test' = 'latest' 'DscResource.AnalyzerRules' = 'latest' - 'DscResource.DocGenerator' = 'latest' - 'DscResource.Common' = 'latest' xDscResourceDesigner = 'latest' + 'DscResource.DocGenerator' = 'latest' } diff --git a/Resolve-Dependency.ps1 b/Resolve-Dependency.ps1 index 4928e62..294cb68 100644 --- a/Resolve-Dependency.ps1 +++ b/Resolve-Dependency.ps1 @@ -1,108 +1,183 @@ +<# + .DESCRIPTION + Bootstrap script for PSDepend. + + .PARAMETER DependencyFile + Specifies the configuration file for the this script. The default value is + 'RequiredModules.psd1' relative to this script's path. + + .PARAMETER PSDependTarget + Path for PSDepend to be bootstrapped and save other dependencies. + Can also be CurrentUser or AllUsers if you wish to install the modules in + such scope. The default value is './output/RequiredModules' relative to + this script's path. + + .PARAMETER Proxy + Specifies the URI to use for Proxy when attempting to bootstrap + PackageProvider and PowerShellGet. + + .PARAMETER ProxyCredential + Specifies the credential to contact the Proxy when provided. + + .PARAMETER Scope + Specifies the scope to bootstrap the PackageProvider and PSGet if not available. + THe default value is 'CurrentUser'. + + .PARAMETER Gallery + Specifies the gallery to use when bootstrapping PackageProvider, PSGet and + when calling PSDepend (can be overridden in Dependency files). The default + value is 'PSGallery'. + + .PARAMETER GalleryCredential + Specifies the credentials to use with the Gallery specified above. + + .PARAMETER AllowOldPowerShellGetModule + Allow you to use a locally installed version of PowerShellGet older than + 1.6.0 (not recommended). Default it will install the latest PowerShellGet + if an older version than 2.0 is detected. + + .PARAMETER MinimumPSDependVersion + Allow you to specify a minimum version fo PSDepend, if you're after specific + features. + + .PARAMETER AllowPrerelease + Not yet written. + + .PARAMETER WithYAML + Not yet written. + + .NOTES + Load defaults for parameters values from Resolve-Dependency.psd1 if not + provided as parameter. +#> [CmdletBinding()] param ( - [Parameter()] - [String] + [System.String] $DependencyFile = 'RequiredModules.psd1', [Parameter()] - [String] - # Path for PSDepend to be bootstrapped and save other dependencies. - # Can also be CurrentUser or AllUsers if you wish to install the modules in such scope - # Default to $PWD.Path/output/modules - $PSDependTarget = (Join-Path $PSScriptRoot './output/RequiredModules'), + [System.String] + $PSDependTarget = (Join-Path -Path $PSScriptRoot -ChildPath './output/RequiredModules'), [Parameter()] - [uri] - # URI to use for Proxy when attempting to Bootstrap PackageProvider & PowerShellGet + [System.Uri] $Proxy, [Parameter()] - # Credential to contact the Proxy when provided - [PSCredential]$ProxyCredential, + [System.Management.Automation.PSCredential] + $ProxyCredential, [Parameter()] [ValidateSet('CurrentUser', 'AllUsers')] - [String] - # Scope to bootstrap the PackageProvider and PSGet if not available + [System.String] $Scope = 'CurrentUser', [Parameter()] - [String] - # Gallery to use when bootstrapping PackageProvider, PSGet and when calling PSDepend (can be overridden in Dependency files) + [System.String] $Gallery = 'PSGallery', [Parameter()] - [PSCredential] - # Credentials to use with the Gallery specified above + [System.Management.Automation.PSCredential] $GalleryCredential, - [Parameter()] - [switch] - # Allow you to use a locally installed version of PowerShellGet older than 1.6.0 (not recommended, default to $False) + [System.Management.Automation.SwitchParameter] $AllowOldPowerShellGetModule, [Parameter()] - [String] - # Allow you to specify a minimum version fo PSDepend, if you're after specific features. + [System.String] $MinimumPSDependVersion, [Parameter()] - [Switch] + [System.Management.Automation.SwitchParameter] $AllowPrerelease, [Parameter()] - [Switch] + [System.Management.Automation.SwitchParameter] $WithYAML ) -# Load Defaults for parameters values from Resolve-Dependency.psd1 if not provided as parameter try { - Write-Verbose -Message "Importing Bootstrap default parameters from '$PSScriptRoot/Resolve-Dependency.psd1'." - $ResolveDependencyDefaults = Import-PowerShellDataFile -Path (Join-Path $PSScriptRoot '.\Resolve-Dependency.psd1' -Resolve -ErrorAction Stop) - $ParameterToDefault = $MyInvocation.MyCommand.ParameterSets.Where{ $_.Name -eq $PSCmdlet.ParameterSetName }.Parameters.Keys - if ($ParameterToDefault.Count -eq 0) + if ($PSVersionTable.PSVersion.Major -le 5) + { + if (-not (Get-Command -Name 'Import-PowerShellDataFile' -ErrorAction 'SilentlyContinue')) + { + Import-Module -Name Microsoft.PowerShell.Utility -RequiredVersion '3.1.0.0' + } + + <# + Making sure the imported PackageManagement module is not from PS7 module + path. The VSCode PS extension is changing the $env:PSModulePath and + prioritize the PS7 path. This is an issue with PowerShellGet because + it loads an old version if available (or fail to load latest). + #> + Get-Module -ListAvailable PackageManagement | + Where-Object -Property 'ModuleBase' -NotMatch 'powershell.7' | + Select-Object -First 1 | + Import-Module -Force + } + + Write-Verbose -Message 'Importing Bootstrap default parameters from ''$PSScriptRoot/Resolve-Dependency.psd1''.' + + $resolveDependencyConfigPath = Join-Path -Path $PSScriptRoot -ChildPath '.\Resolve-Dependency.psd1' -Resolve -ErrorAction 'Stop' + + $resolveDependencyDefaults = Import-PowerShellDataFile -Path $resolveDependencyConfigPath + + $parameterToDefault = $MyInvocation.MyCommand.ParameterSets.Where{ $_.Name -eq $PSCmdlet.ParameterSetName }.Parameters.Keys + + if ($parameterToDefault.Count -eq 0) { - $ParameterToDefault = $MyInvocation.MyCommand.Parameters.Keys + $parameterToDefault = $MyInvocation.MyCommand.Parameters.Keys } - # Set the parameters available in the Parameter Set, or it's not possible to choose yet, so all parameters are an option - foreach ($ParamName in $ParameterToDefault) + + # Set the parameters available in the Parameter Set, or it's not possible to choose yet, so all parameters are an option. + foreach ($parameterName in $parameterToDefault) { - if (-Not $PSBoundParameters.Keys.Contains($ParamName) -and $ResolveDependencyDefaults.ContainsKey($ParamName)) + if (-not $PSBoundParameters.Keys.Contains($parameterName) -and $resolveDependencyDefaults.ContainsKey($parameterName)) { - Write-Verbose -Message "Setting $ParamName with $($ResolveDependencyDefaults[$ParamName])" + Write-Verbose -Message "Setting $parameterName with $($resolveDependencyDefaults[$parameterName])." + try { - $variableValue = $ResolveDependencyDefaults[$ParamName] - if ($variableValue -is [string]) + $variableValue = $resolveDependencyDefaults[$parameterName] + + if ($variableValue -is [System.String]) { $variableValue = $ExecutionContext.InvokeCommand.ExpandString($variableValue) } - $PSBoundParameters.Add($ParamName, $variableValue) - Set-Variable -Name $ParamName -value $variableValue -Force -ErrorAction SilentlyContinue + + $PSBoundParameters.Add($parameterName, $variableValue) + + Set-Variable -Name $parameterName -value $variableValue -Force -ErrorAction 'SilentlyContinue' } catch { - Write-Verbose -Message "Error adding default for $ParamName : $($_.Exception.Message)" + Write-Verbose -Message "Error adding default for $parameterName : $($_.Exception.Message)." } } } } catch { - Write-Warning -Message "Error attempting to import Bootstrap's default parameters from $(Join-Path $PSScriptRoot '.\Resolve-Dependency.psd1'): $($_.Exception.Message)." + Write-Warning -Message "Error attempting to import Bootstrap's default parameters from '$resolveDependencyConfigPath': $($_.Exception.Message)." } -Write-Progress -Activity "Bootstrap:" -PercentComplete 0 -CurrentOperation "NuGet Bootstrap" +Write-Progress -Activity 'Bootstrap:' -PercentComplete 0 -CurrentOperation 'NuGet Bootstrap' + +# TODO: This should handle the parameter $AllowOldPowerShellGetModule. +$powerShellGetModule = Import-Module -Name 'PowerShellGet' -MinimumVersion '2.0' -ErrorAction 'SilentlyContinue' -PassThru -if (!(Get-PackageProvider -Name NuGet -ForceBootstrap -ErrorAction SilentlyContinue)) +# Install the package provider if it is not available. +$nuGetProvider = Get-PackageProvider -Name 'NuGet' -ListAvailable | Select-Object -First 1 + +if (-not $powerShellGetModule -and -not $nuGetProvider) { - $providerBootstrapParams = @{ + $providerBootstrapParameters = @{ Name = 'nuget' - force = $true + Force = $true ForceBootstrap = $true ErrorAction = 'Stop' } @@ -111,47 +186,60 @@ if (!(Get-PackageProvider -Name NuGet -ForceBootstrap -ErrorAction SilentlyConti { 'Proxy' { - $providerBootstrapParams.Add('Proxy', $Proxy) + $providerBootstrapParameters.Add('Proxy', $Proxy) } + 'ProxyCredential' { - $providerBootstrapParams.Add('ProxyCredential', $ProxyCredential) + $providerBootstrapParameters.Add('ProxyCredential', $ProxyCredential) } + 'Scope' { - $providerBootstrapParams.Add('Scope', $Scope) + $providerBootstrapParameters.Add('Scope', $Scope) } } if ($AllowPrerelease) { - $providerBootstrapParams.Add('AllowPrerelease', $true) + $providerBootstrapParameters.Add('AllowPrerelease', $true) } - Write-Information "Bootstrap: Installing NuGet Package Provider from the web (Make sure Microsoft addresses/ranges are allowed)" + Write-Information -MessageData 'Bootstrap: Installing NuGet Package Provider from the web (Make sure Microsoft addresses/ranges are allowed).' + $null = Install-PackageProvider @providerBootstrapParams - $latestNuGetVersion = (Get-PackageProvider -Name NuGet -ListAvailable | Select-Object -First 1).Version.ToString() - Write-Information "Bootstrap: Importing NuGet Package Provider version $latestNuGetVersion to current session." - $Null = Import-PackageProvider -Name NuGet -RequiredVersion $latestNuGetVersion -Force + + $nuGetProvider = Get-PackageProvider -Name 'NuGet' -ListAvailable | Select-Object -First 1 + + $nuGetProviderVersion = $nuGetProvider.Version.ToString() + + Write-Information -MessageData "Bootstrap: Importing NuGet Package Provider version $nuGetProviderVersion to current session." + + $Null = Import-PackageProvider -Name 'NuGet' -RequiredVersion $nuGetProviderVersion -Force } -Write-Progress -Activity "Bootstrap:" -PercentComplete 10 -CurrentOperation "Ensuring Gallery $Gallery is trusted" +Write-Progress -Activity 'Bootstrap:' -PercentComplete 10 -CurrentOperation "Ensuring Gallery $Gallery is trusted" + +# Fail if the given PSGallery is not registered. +$previousGalleryInstallationPolicy = (Get-PSRepository -Name $Gallery -ErrorAction 'Stop').InstallationPolicy + +Set-PSRepository -Name $Gallery -InstallationPolicy 'Trusted' -ErrorAction 'Ignore' -# Fail if the given PSGallery is not Registered -$Policy = (Get-PSRepository $Gallery -ErrorAction Stop).InstallationPolicy -Set-PSRepository -Name $Gallery -InstallationPolicy Trusted -ErrorAction Ignore try { - Write-Progress -Activity "Bootstrap:" -PercentComplete 25 -CurrentOperation "Checking PowerShellGet" - # Ensure the module is loaded and retrieve the version you have - $PowerShellGetVersion = (Import-Module PowerShellGet -PassThru -ErrorAction SilentlyContinue).Version + Write-Progress -Activity 'Bootstrap:' -PercentComplete 25 -CurrentOperation 'Checking PowerShellGet' - Write-Verbose "Bootstrap: The PowerShellGet version is $PowerShellGetVersion" - # Versions below 1.6.0 are considered old, unreliable & not recommended - if (!$PowerShellGetVersion -or ($PowerShellGetVersion -lt [System.version]'1.6.0' -and !$AllowOldPowerShellGetModule)) + # Ensure the module is loaded and retrieve the version you have. + $powerShellGetVersion = (Import-Module -Name 'PowerShellGet' -PassThru -ErrorAction 'SilentlyContinue').Version + + Write-Verbose -Message "Bootstrap: The PowerShellGet version is $powerShellGetVersion" + + # Versions below 2.0 are considered old, unreliable & not recommended + if (-not $powerShellGetVersion -or ($powerShellGetVersion -lt [System.Version] '2.0' -and -not $AllowOldPowerShellGetModule)) { - Write-Progress -Activity "Bootstrap:" -PercentComplete 40 -CurrentOperation "Installing newer version of PowerShellGet" - $InstallPSGetParam = @{ + Write-Progress -Activity 'Bootstrap:' -PercentComplete 40 -CurrentOperation 'Installing newer version of PowerShellGet' + + $installPowerShellGetParameters = @{ Name = 'PowerShellGet' Force = $True SkipPublisherCheck = $true @@ -164,49 +252,64 @@ try { 'Proxy' { - $InstallPSGetParam.Add('Proxy', $Proxy) + $installPowerShellGetParameters.Add('Proxy', $Proxy) } + 'ProxyCredential' { - $InstallPSGetParam.Add('ProxyCredential', $ProxyCredential) + $installPowerShellGetParameters.Add('ProxyCredential', $ProxyCredential) } + 'GalleryCredential' { - $InstallPSGetParam.Add('Credential', $GalleryCredential) + $installPowerShellGetParameters.Add('Credential', $GalleryCredential) } } - Install-Module @InstallPSGetParam - Remove-Module PowerShellGet -force -ErrorAction SilentlyContinue - Import-Module PowerShellGet -Force - $NewLoadedVersion = (Get-Module PowerShellGet).Version.ToString() - Write-Information "Bootstrap: PowerShellGet version loaded is $NewLoadedVersion" - Write-Progress -Activity "Bootstrap:" -PercentComplete 60 -CurrentOperation "Installing newer version of PowerShellGet" + Write-Progress -Activity 'Bootstrap:' -PercentComplete 60 -CurrentOperation 'Installing newer version of PowerShellGet' + + Install-Module @installPowerShellGetParameters + + Remove-Module -Name 'PowerShellGet' -Force -ErrorAction 'SilentlyContinue' + Remove-Module -Name 'PackageManagement' -Force + + $powerShellGetModule = Import-Module PowerShellGet -Force -PassThru + + $powerShellGetVersion = $powerShellGetModule.Version.ToString() + + Write-Information -MessageData "Bootstrap: PowerShellGet version loaded is $powerShellGetVersion" + } + + # Try to import the PSDepend module from the available modules. + $getModuleParameters = @{ + Name = 'PSDepend' + ListAvailable = $true } - # Try to import the PSDepend module from the available modules - try + $psDependModule = Get-Module @getModuleParameters + + if ($PSBoundParameters.ContainsKey('MinimumPSDependVersion')) { - $ImportPSDependParam = @{ - Name = 'PSDepend' - ErrorAction = 'Stop' - Force = $true + try + { + $psDependModule = $psDependModule | Where-Object -FilterScript { $_.Version -ge $MinimumPSDependVersion } } - - if ($MinimumPSDependVersion) + catch { - $ImportPSDependParam.add('MinimumVersion', $MinimumPSDependVersion) + throw ('There was a problem finding the minimum version of PSDepend. Error: {0}' -f $_) } - $null = Import-Module @ImportPSDependParam } - catch + + if (-not $psDependModule) { - # PSDepend module not found, installing or saving it + # PSDepend module not found, installing or saving it. if ($PSDependTarget -in 'CurrentUser', 'AllUsers') { - Write-Debug "PSDepend module not found. Attempting to install from Gallery $Gallery" - Write-Warning "Installing PSDepend in $PSDependTarget Scope" - $InstallPSDependParam = @{ + Write-Debug -Message "PSDepend module not found. Attempting to install from Gallery $Gallery." + + Write-Warning -Message "Installing PSDepend in $PSDependTarget Scope." + + $installPSDependParameters = @{ Name = 'PSDepend' Repository = $Gallery Force = $true @@ -217,50 +320,69 @@ try if ($MinimumPSDependVersion) { - $InstallPSDependParam.add('MinimumVersion', $MinimumPSDependVersion) + $installPSDependParameters.Add('MinimumVersion', $MinimumPSDependVersion) } - Write-Progress -Activity "Bootstrap:" -PercentComplete 75 -CurrentOperation "Installing PSDepend from $Gallery" - Install-Module @InstallPSDependParam + Write-Progress -Activity 'Bootstrap:' -PercentComplete 75 -CurrentOperation "Installing PSDepend from $Gallery" + + Install-Module @installPSDependParameters } else { - Write-Debug "PSDepend module not found. Attempting to Save from Gallery $Gallery to $PSDependTarget" - $SaveModuleParam = @{ + Write-Debug -Message "PSDepend module not found. Attempting to Save from Gallery $Gallery to $PSDependTarget" + + $saveModuleParameters = @{ Name = 'PSDepend' Repository = $Gallery Path = $PSDependTarget + Force = $true } if ($MinimumPSDependVersion) { - $SaveModuleParam.add('MinimumVersion', $MinimumPSDependVersion) + $saveModuleParameters.add('MinimumVersion', $MinimumPSDependVersion) } - Write-Progress -Activity "Bootstrap:" -PercentComplete 75 -CurrentOperation "Saving & Importing PSDepend from $Gallery to $Scope" - Save-Module @SaveModuleParam + Write-Progress -Activity 'Bootstrap:' -PercentComplete 75 -CurrentOperation "Saving & Importing PSDepend from $Gallery to $Scope" + + Save-Module @saveModuleParameters } } - finally + + Write-Progress -Activity 'Bootstrap:' -PercentComplete 80 -CurrentOperation 'Loading PSDepend' + + $importModulePSDependParameters = @{ + Name = 'PSDepend' + ErrorAction = 'Stop' + Force = $true + } + + if ($PSBoundParameters.ContainsKey('MinimumPSDependVersion')) { - Write-Progress -Activity "Bootstrap:" -PercentComplete 100 -CurrentOperation "Loading PSDepend" - # We should have successfully bootstrapped PSDepend. Fail if not available - Import-Module PSDepend -ErrorAction Stop + $importModulePSDependParameters.Add('MinimumVersion', $MinimumPSDependVersion) } + # We should have successfully bootstrapped PSDepend. Fail if not available. + $null = Import-Module @importModulePSDependParameters + if ($WithYAML) { - if (-Not (Get-Module -ListAvailable -Name 'PowerShell-Yaml')) + Write-Progress -Activity 'Bootstrap:' -PercentComplete 82 -CurrentOperation 'Verifying PowerShell module PowerShell-Yaml' + + if (-not (Get-Module -ListAvailable -Name 'PowerShell-Yaml')) { - Write-Verbose "PowerShell-Yaml module not found. Attempting to Save from Gallery $Gallery to $PSDependTarget" + Write-Progress -Activity 'Bootstrap:' -PercentComplete 85 -CurrentOperation 'Installing PowerShell module PowerShell-Yaml' + + Write-Verbose -Message "PowerShell-Yaml module not found. Attempting to Save from Gallery $Gallery to $PSDependTarget" + $SaveModuleParam = @{ Name = 'PowerShell-Yaml' Repository = $Gallery Path = $PSDependTarget + Force = $true } Save-Module @SaveModuleParam - Import-Module "PowerShell-Yaml" -ErrorAction Stop } else { @@ -268,22 +390,28 @@ try } } + Write-Progress -Activity 'Bootstrap:' -PercentComplete 90 -CurrentOperation 'Invoke PSDepend' + Write-Progress -Activity "PSDepend:" -PercentComplete 0 -CurrentOperation "Restoring Build Dependencies" - if (Test-Path $DependencyFile) + + if (Test-Path -Path $DependencyFile) { - $PSDependParams = @{ + $psDependParameters = @{ Force = $true Path = $DependencyFile } - # TODO: Handle when the Dependency file is in YAML, and -WithYAML is specified - Invoke-PSDepend @PSDependParams + # TODO: Handle when the Dependency file is in YAML, and -WithYAML is specified. + Invoke-PSDepend @psDependParameters } + Write-Progress -Activity "PSDepend:" -PercentComplete 100 -CurrentOperation "Dependencies restored" -Completed + + Write-Progress -Activity 'Bootstrap:' -PercentComplete 100 -CurrentOperation "Bootstrap complete" -Completed } finally { # Reverting the Installation Policy for the given gallery - Set-PSRepository -Name $Gallery -InstallationPolicy $Policy - Write-Verbose "Project Bootstrapped, returning to Invoke-Build" + Set-PSRepository -Name $Gallery -InstallationPolicy $previousGalleryInstallationPolicy + Write-Verbose -Message "Project Bootstrapped, returning to Invoke-Build" } diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..7bd7bc2 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,30 @@ +## Security + +The DSC Community takes the security of our modules seriously, which includes all source code repositories managed through our GitHub organization. + +If you believe you have found a security vulnerability in any DSC Community owned repository, please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them to one or several members of the DSC Community organization. +The easiest way to do so is to send us a direct message via twitter or slack. + +You should receive a response within 48 hours. If for some reason you do not, please follow up to other member of the community. + +Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: + + * Type of issue + * Full paths of source file(s) related to the manifestation of the issue + * The location of the affected source code (tag/branch/commit or direct URL) + * Any special configuration required to reproduce the issue + * Step-by-step instructions to reproduce the issue + * Proof-of-concept or exploit code (if possible) + * Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +## Preferred Languages + +We prefer all communications to be in English. diff --git a/azure-pipelines.yml b/azure-pipelines.yml index cd9e755..fd182ae 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -10,11 +10,14 @@ trigger: - "v*" exclude: - "*-*" + variables: buildFolderName: output buildArtifactName: output testResultFolderName: testResults testArtifactName: testResults + sourceFolderName: source + defaultBranch: main stages: - stage: Build @@ -33,7 +36,6 @@ stages: } Write-Host -Object "##vso[build.updatebuildnumber]$($gitVersionObject.FullSemVer)" displayName: Calculate ModuleVersion (GitVersion) - - task: PowerShell@2 name: package displayName: 'Build & Package Module' @@ -43,13 +45,13 @@ stages: pwsh: true env: ModuleVersion: $(NuGetVersionV2) - - - task: PublishBuildArtifacts@1 + - task: PublishPipelineArtifact@1 displayName: 'Publish Build Artifact' inputs: - pathToPublish: '$(buildFolderName)/' - artifactName: $(buildArtifactName) - publishLocation: 'Container' + targetPath: '$(buildFolderName)/' + artifact: $(buildArtifactName) + publishLocation: 'pipeline' + parallel: true - stage: Test dependsOn: Build @@ -57,16 +59,15 @@ stages: - job: Test_HQRM displayName: 'HQRM' pool: - vmImage: 'windows-2019' + vmImage: 'windows-latest' timeoutInMinutes: 0 steps: - - task: DownloadBuildArtifacts@0 + - task: DownloadPipelineArtifact@2 displayName: 'Download Build Artifact' inputs: buildType: 'current' - downloadType: 'single' - artifactName: 'output' - downloadPath: '$(Build.SourcesDirectory)' + artifactName: $(buildArtifactName) + targetPath: '$(Build.SourcesDirectory)/$(buildFolderName)' - task: PowerShell@2 name: test displayName: 'Run HQRM Test' @@ -85,50 +86,48 @@ stages: - job: Test_Unit displayName: 'Unit' pool: - vmImage: 'windows-2019' + vmImage: 'windows-latest' timeoutInMinutes: 0 steps: - - task: DownloadBuildArtifacts@0 + - task: DownloadPipelineArtifact@2 displayName: 'Download Build Artifact' inputs: buildType: 'current' - downloadType: 'single' artifactName: $(buildArtifactName) - downloadPath: '$(Build.SourcesDirectory)' + targetPath: '$(Build.SourcesDirectory)/$(buildFolderName)' - task: PowerShell@2 name: test displayName: 'Run Unit Test' inputs: filePath: './build.ps1' arguments: "-Tasks test -PesterScript 'tests/Unit'" - pwsh: false + pwsh: true - task: PublishTestResults@2 displayName: 'Publish Test Results' condition: succeededOrFailed() inputs: testResultsFormat: 'NUnit' testResultsFiles: '$(buildFolderName)/$(testResultFolderName)/NUnit*.xml' - testRunTitle: 'Unit (Windows Server Core)' - - task: PublishBuildArtifacts@1 + testRunTitle: 'Unit' + - task: PublishPipelineArtifact@1 displayName: 'Publish Test Artifact' inputs: - pathToPublish: '$(buildFolderName)/$(testResultFolderName)/' + targetPath: '$(buildFolderName)/$(testResultFolderName)/' artifactName: $(testArtifactName) - publishLocation: 'Container' + parallel: true - job: Test_Integration displayName: 'Integration' pool: - vmImage: 'windows-2019' + vmImage: 'windows-latest' timeoutInMinutes: 0 steps: - - task: DownloadBuildArtifacts@0 + - task: DownloadPipelineArtifact@2 displayName: 'Download Build Artifact' inputs: buildType: 'current' - downloadType: 'single' artifactName: $(buildArtifactName) - downloadPath: '$(Build.SourcesDirectory)' + targetPath: '$(Build.SourcesDirectory)/$(buildFolderName)' - task: PowerShell@2 name: configureWinRM displayName: 'Configure WinRM' @@ -141,7 +140,7 @@ stages: displayName: 'Run Integration Test' inputs: filePath: './build.ps1' - arguments: "-Tasks test -PesterScript 'tests/Integration' -CodeCoverageThreshold 0" + arguments: "-Tasks test -CodeCoverageThreshold 0 -PesterScript 'tests/Integration'" pwsh: false - task: PublishTestResults@2 displayName: 'Publish Test Results' @@ -149,7 +148,7 @@ stages: inputs: testResultsFormat: 'NUnit' testResultsFiles: '$(buildFolderName)/$(testResultFolderName)/NUnit*.xml' - testRunTitle: 'Integration (Windows Server Core)' + testRunTitle: 'Integration' - job: Code_Coverage displayName: 'Publish Code Coverage' @@ -158,36 +157,28 @@ stages: vmImage: 'ubuntu-latest' timeoutInMinutes: 0 steps: - - pwsh: | - $repositoryOwner,$repositoryName = $env:BUILD_REPOSITORY_NAME -split '/' - echo "##vso[task.setvariable variable=RepositoryOwner;isOutput=true]$repositoryOwner" - echo "##vso[task.setvariable variable=RepositoryName;isOutput=true]$repositoryName" - name: dscBuildVariable - displayName: 'Set Environment Variables' - - task: DownloadBuildArtifacts@0 + - task: DownloadPipelineArtifact@2 displayName: 'Download Build Artifact' inputs: buildType: 'current' - downloadType: 'single' artifactName: $(buildArtifactName) - downloadPath: '$(Build.SourcesDirectory)' - - task: DownloadBuildArtifacts@0 + targetPath: '$(Build.SourcesDirectory)/$(buildFolderName)' + - task: DownloadPipelineArtifact@2 displayName: 'Download Test Artifact' inputs: buildType: 'current' - downloadType: 'single' artifactName: $(testArtifactName) - downloadPath: '$(Build.SourcesDirectory)/$(buildFolderName)' + targetPath: '$(Build.SourcesDirectory)/$(buildFolderName)/$(testResultFolderName)' - task: PublishCodeCoverageResults@1 - displayName: 'Publish Azure Code Coverage' + displayName: 'Publish Code Coverage to Azure DevOps' condition: succeededOrFailed() inputs: codeCoverageTool: 'JaCoCo' - summaryFileLocation: '$(buildFolderName)/$(testResultFolderName)/JaCoCo_coverage.xml' - pathToSources: '$(Build.SourcesDirectory)/$(buildFolderName)/$(dscBuildVariable.RepositoryName)' + summaryFileLocation: '$(Build.SourcesDirectory)/$(buildFolderName)/$(testResultFolderName)/JaCoCo_coverage.xml' + pathToSources: '$(Build.SourcesDirectory)/$(sourceFolderName)/' - script: | bash <(curl -s https://codecov.io/bash) -f "./$(buildFolderName)/$(testResultFolderName)/JaCoCo_coverage.xml" - displayName: 'Upload to Codecov.io' + displayName: 'Publish Code Coverage to Codecov.io' condition: succeededOrFailed() - stage: Deploy @@ -207,13 +198,12 @@ stages: pool: vmImage: 'ubuntu-latest' steps: - - task: DownloadBuildArtifacts@0 - displayName: 'Download Build Artifact' + - task: DownloadPipelineArtifact@2 + displayName: 'Download Pipeline Artifact' inputs: buildType: 'current' - downloadType: 'single' - artifactName: 'output' - downloadPath: '$(Build.SourcesDirectory)' + artifactName: $(buildArtifactName) + targetPath: '$(Build.SourcesDirectory)/$(buildArtifactName)' - task: PowerShell@2 name: publishRelease displayName: 'Publish Release' @@ -224,8 +214,8 @@ stages: env: GitHubToken: $(GitHubToken) GalleryApiToken: $(GalleryApiToken) - ReleaseBranch: main - MainGitBranch: main + ReleaseBranch: $(defaultBranch) + MainGitBranch: $(defaultBranch) - task: PowerShell@2 name: sendChangelogPR displayName: 'Send Changelog PR' @@ -235,5 +225,5 @@ stages: pwsh: true env: GitHubToken: $(GitHubToken) - ReleaseBranch: main - MainGitBranch: main + ReleaseBranch: $(defaultBranch) + MainGitBranch: $(defaultBranch) diff --git a/build.ps1 b/build.ps1 index a94f13b..5579df1 100644 --- a/build.ps1 +++ b/build.ps1 @@ -1,353 +1,513 @@ <# + .DESCRIPTION + Bootstrap and build script for PowerShell module CI/CD pipeline -.DESCRIPTION - Bootstrap and build script for PowerShell module pipeline + .PARAMETER Tasks + The task or tasks to run. The default value is '.' (runs the default task). + .PARAMETER CodeCoverageThreshold + The code coverage target threshold to uphold. Set to 0 to disable. + The default value is '' (empty string). + + .PARAMETER BuildConfig + Not yet written. + + .PARAMETER OutputDirectory + Specifies the folder to build the artefact into. The default value is 'output'. + + .PARAMETER BuiltModuleSubdirectory + Subdirectory name to build the module (under $OutputDirectory). The default + value is '' (empty string). + + .PARAMETER RequiredModulesDirectory + Can be a path (relative to $PSScriptRoot or absolute) to tell Resolve-Dependency + and PSDepend where to save the required modules. It is also possible to use + 'CurrentUser' och 'AllUsers' to install missing dependencies. You can override + the value for PSDepend in the Build.psd1 build manifest. The default value is + 'output/RequiredModules'. + + .PARAMETER PesterScript + One or more paths that will override the Pester configuration in build + configuration file when running the build task Invoke_Pester_Tests. + + If running Pester 5 test, use the alias PesterPath to be future-proof. + + .PARAMETER PesterTag + Filter which tags to run when invoking Pester tests. This is used in the + Invoke-Pester.pester.build.ps1 tasks. + + .PARAMETER PesterExcludeTag + Filter which tags to exclude when invoking Pester tests. This is used in + the Invoke-Pester.pester.build.ps1 tasks. + + .PARAMETER DscTestTag + Filter which tags to run when invoking DSC Resource tests. This is used + in the DscResource.Test.build.ps1 tasks. + + .PARAMETER DscTestExcludeTag + Filter which tags to exclude when invoking DSC Resource tests. This is + used in the DscResource.Test.build.ps1 tasks. + + .PARAMETER ResolveDependency + Not yet written. + + .PARAMETER BuildInfo + The build info object from ModuleBuilder. Defaults to an empty hashtable. + + .PARAMETER AutoRestore + Not yet written. #> [CmdletBinding()] param ( [Parameter(Position = 0)] - [string[]]$Tasks = '.', + [System.String[]] + $Tasks = '.', [Parameter()] - [String] + [System.String] $CodeCoverageThreshold = '', [Parameter()] - [validateScript( + [System.String] + [ValidateScript( { Test-Path -Path $_ } )] - $BuildConfig = './build.yaml', + $BuildConfig, [Parameter()] - # A Specific folder to build the artefact into. + [System.String] $OutputDirectory = 'output', [Parameter()] - # Subdirectory name to build the module (under $OutputDirectory) + [System.String] $BuiltModuleSubdirectory = '', - # Can be a path (relative to $PSScriptRoot or absolute) to tell Resolve-Dependency & PSDepend where to save the required modules, - # or use CurrentUser, AllUsers to target where to install missing dependencies - # You can override the value for PSDepend in the Build.psd1 build manifest - # This defaults to $OutputDirectory/modules (by default: ./output/modules) [Parameter()] + [System.String] $RequiredModulesDirectory = $(Join-Path 'output' 'RequiredModules'), - # Filter which tags to run when invoking Pester tests - # This is used in the Invoke-Pester.pester.build.ps1 tasks [Parameter()] - [string[]] - $PesterTag, + # This alias is to prepare for the rename of this parameter to PesterPath when Pester 4 support is removed + [Alias('PesterPath')] + [System.Object[]] + $PesterScript, [Parameter()] - [string[]] - $PesterScript, + [System.String[]] + $PesterTag, - # Filter which tags to exclude when invoking Pester tests - # This is used in the Invoke-Pester.pester.build.ps1 tasks [Parameter()] - [string[]] + [System.String[]] $PesterExcludeTag, + [Parameter()] + [System.String[]] + $DscTestTag, + + [Parameter()] + [System.String[]] + $DscTestExcludeTag, + [Parameter()] [Alias('bootstrap')] - [switch]$ResolveDependency, + [System.Management.Automation.SwitchParameter] + $ResolveDependency, [Parameter(DontShow)] [AllowNull()] + [System.Collections.Hashtable] $BuildInfo, [Parameter()] - [switch] + [System.Management.Automation.SwitchParameter] $AutoRestore ) -# The BEGIN block (at the end of this file) handles the Bootstrap of the Environment before Invoke-Build can run the tasks -# if the -ResolveDependency (aka Bootstrap) is specified, the modules are already available, and can be auto loaded +<# + The BEGIN block (at the end of this file) handles the Bootstrap of the Environment + before Invoke-Build can run the tasks if the parameter ResolveDependency (or + parameter alias Bootstrap) is specified. +#> process { if ($MyInvocation.ScriptName -notLike '*Invoke-Build.ps1') { - # Only run the process block through InvokeBuild (Look at the Begin block at the bottom of this script) + # Only run the process block through InvokeBuild (look at the Begin block at the bottom of this script). return } - # Execute the Build Process from the .build.ps1 path. - Push-Location -Path $PSScriptRoot -StackName BeforeBuild + # Execute the Build process from the .build.ps1 path. + Push-Location -Path $PSScriptRoot -StackName 'BeforeBuild' try { - Write-Host -ForeGroundColor magenta "[build] Parsing defined tasks" + Write-Host -Object "[build] Parsing defined tasks" -ForeGroundColor Magenta - # Load Default BuildInfo if not provided as parameter - if (!$PSBoundParameters.ContainsKey('BuildInfo')) + # Load the default BuildInfo if the parameter BuildInfo is not set. + if (-not $PSBoundParameters.ContainsKey('BuildInfo')) { try { - if (Test-Path $BuildConfig) + if (Test-Path -Path $BuildConfig) { - $ConfigFile = (Get-Item -Path $BuildConfig) - Write-Host "[build] Loading Configuration from $ConfigFile" - $BuildInfo = switch -Regex ($ConfigFile.Extension) + $configFile = Get-Item -Path $BuildConfig + + Write-Host -Object "[build] Loading Configuration from $configFile" + + $BuildInfo = switch -Regex ($configFile.Extension) { # Native Support for PSD1 '\.psd1' { + if (-not (Get-Command -Name Import-PowerShellDataFile -ErrorAction SilentlyContinue)) + { + Import-Module -Name Microsoft.PowerShell.Utility -RequiredVersion 3.1.0.0 + } + Import-PowerShellDataFile -Path $BuildConfig } + # Support for yaml when module PowerShell-Yaml is available '\.[yaml|yml]' { - Import-Module -ErrorAction Stop -Name 'powershell-yaml' - ConvertFrom-Yaml -Yaml (Get-Content -Raw $ConfigFile) + Import-Module -Name 'powershell-yaml' -ErrorAction Stop + + ConvertFrom-Yaml -Yaml (Get-Content -Raw $configFile) } + # Native Support for JSON and JSONC (by Removing comments) '\.[json|jsonc]' { - $JSONC = (Get-Content -Raw -Path $ConfigFile) - $JSON = $JSONC -replace '(?m)\s*//.*?$' -replace '(?ms)/\*.*?\*/' - # This should probably be converted to hashtable for splatting - $JSON | ConvertFrom-Json + $jsonFile = Get-Content -Raw -Path $configFile + + $jsonContent = $jsonFile -replace '(?m)\s*//.*?$' -replace '(?ms)/\*.*?\*/' + + # Yaml is superset of JSON. + ConvertFrom-Yaml -Yaml $jsonContent } + + # Unknown extension, return empty hashtable. default { - Write-Error "Extension '$_' not supported. using @{}" + Write-Error -Message "Extension '$_' not supported. using @{}" + @{ } } } } else { - Write-Host -Object "Configuration file $BuildConfig not found" -ForegroundColor Red + Write-Host -Object "Configuration file '$($BuildConfig.FullName)' not found" -ForegroundColor Red + + # No config file was found, return empty hashtable. $BuildInfo = @{ } } } catch { - Write-Host -Object "Error loading Config $ConfigFile.`r`n Are you missing dependencies?" -ForegroundColor Yellow - Write-Host -Object "Make sure you run './build.ps1 -ResolveDependency -tasks noop' to restore the Required modules the first time" -ForegroundColor Yellow + $logMessage = "Error loading Config '$($BuildConfig.FullName)'.`r`nAre you missing dependencies?`r`nMake sure you run './build.ps1 -ResolveDependency -tasks noop' before running build to restore the required modules." + + Write-Host -Object $logMessage -ForegroundColor Yellow + $BuildInfo = @{ } - Write-Error $_.Exception.Message + + Write-Error -Message $_.Exception.Message } } - # If the Invoke-Build Task Header is specified in the Build Info, set it + # If the Invoke-Build Task Header is specified in the Build Info, set it. if ($BuildInfo.TaskHeader) { - Set-BuildHeader ([scriptblock]::Create($BuildInfo.TaskHeader)) + Set-BuildHeader -Script ([scriptblock]::Create($BuildInfo.TaskHeader)) + } + + <# + Add BuildModuleOutput to PSModule Path environment variable. + Moved here (not in begin block) because build file can contains BuiltSubModuleDirectory value. + #> + if ($BuiltModuleSubdirectory) + { + if (-not (Split-Path -IsAbsolute -Path $BuiltModuleSubdirectory)) + { + $BuildModuleOutput = Join-Path -Path $OutputDirectory -ChildPath $BuiltModuleSubdirectory + } + else + { + $BuildModuleOutput = $BuiltModuleSubdirectory + } + } # test if BuiltModuleSubDirectory set in build config file + elseif ($BuildInfo.ContainsKey('BuiltModuleSubDirectory')) + { + $BuildModuleOutput = Join-Path -Path $OutputDirectory -ChildPath $BuildInfo['BuiltModuleSubdirectory'] + } + else + { + $BuildModuleOutput = $OutputDirectory } - # Import Tasks from modules via their exported aliases when defined in BUild Manifest - # https://github.com/nightroman/Invoke-Build/tree/master/Tasks/Import#example-2-import-from-a-module-with-tasks - if ($BuildInfo.containsKey('ModuleBuildTasks')) + # Pre-pending $BuildModuleOutput folder to PSModulePath to resolve built module from this folder. + if ($powerShellModulePaths -notcontains $BuildModuleOutput) { - foreach ($Module in $BuildInfo['ModuleBuildTasks'].Keys) + Write-Host -Object "[build] Pre-pending '$BuildModuleOutput' folder to PSModulePath" -ForegroundColor Green + + $env:PSModulePath = $BuildModuleOutput + [System.IO.Path]::PathSeparator + $env:PSModulePath + } + + <# + Import Tasks from modules via their exported aliases when defined in Build Manifest. + https://github.com/nightroman/Invoke-Build/tree/master/Tasks/Import#example-2-import-from-a-module-with-tasks + #> + if ($BuildInfo.ContainsKey('ModuleBuildTasks')) + { + foreach ($module in $BuildInfo['ModuleBuildTasks'].Keys) { try { - Write-Host -ForegroundColor DarkGray -Verbose "Importing tasks from module $Module" - $LoadedModule = Import-Module $Module -PassThru -ErrorAction Stop - foreach ($TaskToExport in $BuildInfo['ModuleBuildTasks'].($Module)) + Write-Host -Object "Importing tasks from module $module" -ForegroundColor DarkGray + + $loadedModule = Import-Module -Name $module -PassThru -ErrorAction Stop + + foreach ($TaskToExport in $BuildInfo['ModuleBuildTasks'].($module)) { - $LoadedModule.ExportedAliases.GetEnumerator().Where{ - # using -like to support wildcard - Write-Host -ForegroundColor DarkGray "`t Loading $($_.Key)..." + $loadedModule.ExportedAliases.GetEnumerator().Where{ + Write-Host -Object "`t Loading $($_.Key)..." -ForegroundColor DarkGray + + # Using -like to support wildcard. $_.Key -like $TaskToExport }.ForEach{ - # Dot sourcing the Tasks via their exported aliases + # Dot-sourcing the Tasks via their exported aliases. . (Get-Alias $_.Key) } } } catch { - Write-Host -ForegroundColor Red -Object "Could not load tasks for module $Module." - Write-Error $_ + Write-Host -Object "Could not load tasks for module $module." -ForegroundColor Red + + Write-Error -Message $_ } } } - # Loading Build Tasks defined in the .build/ folder (will override the ones imported above if same task name) - Get-ChildItem -Path ".build/" -Recurse -Include *.ps1 -ErrorAction Ignore | ForEach-Object { - "Importing file $($_.BaseName)" | Write-Verbose - . $_.FullName - } + # Loading Build Tasks defined in the .build/ folder (will override the ones imported above if same task name). + Get-ChildItem -Path '.build/' -Recurse -Include '*.ps1' -ErrorAction Ignore | + ForEach-Object { + "Importing file $($_.BaseName)" | Write-Verbose - # Synopsis: Empty task, useful to test the bootstrap process + . $_.FullName + } + + # Synopsis: Empty task, useful to test the bootstrap process. task noop { } - # Define default task sequence ("."), can be overridden in the $BuildInfo + # Define default task sequence ("."), can be overridden in the $BuildInfo. task . { - Write-Build Yellow "No sequence currently defined for the default task" + Write-Build -Object 'No sequence currently defined for the default task' -ForegroundColor Yellow } - # Load Invoke-Build task sequences/workflows from $BuildInfo - Write-Host -ForegroundColor DarkGray "Adding Workflow from configuration:" - foreach ($Workflow in $BuildInfo.BuildWorkflow.keys) + Write-Host -Object 'Adding Workflow from configuration:' -ForegroundColor DarkGray + + # Load Invoke-Build task sequences/workflows from $BuildInfo. + foreach ($workflow in $BuildInfo.BuildWorkflow.keys) { - Write-Verbose "Creating Build Workflow '$Workflow' with tasks $($BuildInfo.BuildWorkflow.($Workflow) -join ', ')" - $WorkflowItem = $BuildInfo.BuildWorkflow.($Workflow) - if ($WorkflowItem.Trim() -match '^\{(?[\w\W]*)\}$') + Write-Verbose -Message "Creating Build Workflow '$Workflow' with tasks $($BuildInfo.BuildWorkflow.($Workflow) -join ', ')." + + $workflowItem = $BuildInfo.BuildWorkflow.($workflow) + + if ($workflowItem.Trim() -match '^\{(?[\w\W]*)\}$') { - $WorkflowItem = [ScriptBlock]::Create($Matches['sb']) + $workflowItem = [ScriptBlock]::Create($Matches['sb']) } - Write-Host -ForegroundColor DarkGray " +-> $Workflow" - task $Workflow $WorkflowItem + + Write-Host -Object " +-> $workflow" -ForegroundColor DarkGray + + task $workflow $workflowItem } - Write-Host -ForeGroundColor magenta "[build] Executing requested workflow: $($Tasks -join ', ')" + Write-Host -Object "[build] Executing requested workflow: $($Tasks -join ', ')" -ForeGroundColor Magenta } finally { - Pop-Location -StackName BeforeBuild + Pop-Location -StackName 'BeforeBuild' } } Begin { + # Find build config if not specified. + if (-not $BuildConfig) + { + $config = Get-ChildItem -Path "$PSScriptRoot\*" -Include 'build.y*ml', 'build.psd1', 'build.json*' -ErrorAction Ignore + + if (-not $config -or ($config -is [System.Array] -and $config.Length -le 0)) + { + throw 'No build configuration found. Specify path via parameter BuildConfig.' + } + elseif ($config -is [System.Array]) + { + if ($config.Length -gt 1) + { + throw 'More than one build configuration found. Specify which path to use via parameter BuildConfig.' + } + + $BuildConfig = $config[0] + } + else + { + $BuildConfig = $config + } + } + # Bootstrapping the environment before using Invoke-Build as task runner - if ($MyInvocation.ScriptName -notLike '*Invoke-Build.ps1') + if ($MyInvocation.ScriptName -notlike '*Invoke-Build.ps1') { - Write-Host -foregroundColor Green "[pre-build] Starting Build Init" - Push-Location $PSScriptRoot -StackName BuildModule + Write-Host -Object "[pre-build] Starting Build Init" -ForegroundColor Green + + Push-Location $PSScriptRoot -StackName 'BuildModule' } if ($RequiredModulesDirectory -in @('CurrentUser', 'AllUsers')) { - # Installing modules instead of saving them - Write-Host -foregroundColor Green "[pre-build] Required Modules will be installed for $RequiredModulesDirectory, not saved." - # Tell Resolve-Dependency to use provided scope as the -PSDependTarget if not overridden in Build.psd1 + # Installing modules instead of saving them. + Write-Host -Object "[pre-build] Required Modules will be installed to the PowerShell module path that is used for $RequiredModulesDirectory." -ForegroundColor Green + + <# + The variable $PSDependTarget will be used below when building the splatting + variable before calling Resolve-Dependency.ps1, unless overridden in the + file Resolve-Dependency.psd1. + #> $PSDependTarget = $RequiredModulesDirectory } else { - if (-Not (Split-Path -IsAbsolute -Path $OutputDirectory)) + if (-not (Split-Path -IsAbsolute -Path $OutputDirectory)) { $OutputDirectory = Join-Path -Path $PSScriptRoot -ChildPath $OutputDirectory } - # Resolving the absolute path to save the required modules to - if (-Not (Split-Path -IsAbsolute -Path $RequiredModulesDirectory)) + # Resolving the absolute path to save the required modules to. + if (-not (Split-Path -IsAbsolute -Path $RequiredModulesDirectory)) { $RequiredModulesDirectory = Join-Path -Path $PSScriptRoot -ChildPath $RequiredModulesDirectory } - # Create the output/modules folder if not exists, or resolve the Absolute path otherwise - if (Resolve-Path $RequiredModulesDirectory -ErrorAction SilentlyContinue) + # Create the output/modules folder if not exists, or resolve the Absolute path otherwise. + if (Resolve-Path -Path $RequiredModulesDirectory -ErrorAction SilentlyContinue) { - Write-Debug "[pre-build] Required Modules path already exist at $RequiredModulesDirectory" - $RequiredModulesPath = Convert-Path $RequiredModulesDirectory + Write-Debug -Message "[pre-build] Required Modules path already exist at $RequiredModulesDirectory" + + $requiredModulesPath = Convert-Path -Path $RequiredModulesDirectory } else { - Write-Host -foregroundColor Green "[pre-build] Creating required modules directory $RequiredModulesDirectory." - $RequiredModulesPath = (New-Item -ItemType Directory -Force -Path $RequiredModulesDirectory).FullName - } + Write-Host -Object "[pre-build] Creating required modules directory $RequiredModulesDirectory." -ForegroundColor Green - # Prepending $RequiredModulesPath folder to PSModulePath to resolve from this folder FIRST - if ($RequiredModulesDirectory -notIn @('CurrentUser', 'AllUsers') -and - (($Env:PSModulePath -split [io.path]::PathSeparator) -notContains $RequiredModulesDirectory)) - { - Write-Host -foregroundColor Green "[pre-build] Prepending '$RequiredModulesDirectory' folder to PSModulePath" - $Env:PSModulePath = $RequiredModulesDirectory + [io.path]::PathSeparator + $Env:PSModulePath + $requiredModulesPath = (New-Item -ItemType Directory -Force -Path $RequiredModulesDirectory).FullName } - # Checking if the user should -ResolveDependency - if ((!(Get-Module -ListAvailable powershell-yaml) -or !(Get-Module -ListAvailable InvokeBuild) -or !(Get-Module -ListAvailable PSDepend)) -and !$ResolveDependency) + $powerShellModulePaths = $env:PSModulePath -split [System.IO.Path]::PathSeparator + + # Pre-pending $requiredModulesPath folder to PSModulePath to resolve from this folder FIRST. + if ($RequiredModulesDirectory -notin @('CurrentUser', 'AllUsers') -and + ($powerShellModulePaths -notcontains $RequiredModulesDirectory)) { - if ($AutoRestore -or !$PSBoundParameters.ContainsKey('Tasks') -or $Tasks -contains 'build') - { - Write-Host -ForegroundColor Yellow "[pre-build] Dependency missing, running './build.ps1 -ResolveDependency -Tasks noop' for you `r`n" - $ResolveDependency = $true - } - else - { - Write-Warning "Some required Modules are missing, make sure you first run with the '-ResolveDependency' parameter." - Write-Warning "Running 'build.ps1 -ResolveDependency -Tasks noop' will pull required modules without running the build task." - } + Write-Host -Object "[pre-build] Pre-pending '$RequiredModulesDirectory' folder to PSModulePath" -ForegroundColor Green + + $env:PSModulePath = $RequiredModulesDirectory + [System.IO.Path]::PathSeparator + $env:PSModulePath } - if ($BuiltModuleSubdirectory) + $powerShellYamlModule = Get-Module -Name 'powershell-yaml' -ListAvailable + $invokeBuildModule = Get-Module -Name 'InvokeBuild' -ListAvailable + $psDependModule = Get-Module -Name 'PSDepend' -ListAvailable + + # Checking if the user should -ResolveDependency. + if (-not ($powerShellYamlModule -and $invokeBuildModule -and $psDependModule) -and -not $ResolveDependency) { - if (-Not (Split-Path -IsAbsolute $BuiltModuleSubdirectory)) + if ($AutoRestore -or -not $PSBoundParameters.ContainsKey('Tasks') -or $Tasks -contains 'build') { - $BuildModuleOutput = Join-Path $OutputDirectory $BuiltModuleSubdirectory + Write-Host -Object "[pre-build] Dependency missing, running './build.ps1 -ResolveDependency -Tasks noop' for you `r`n" -ForegroundColor Yellow + + $ResolveDependency = $true } else { - $BuildModuleOutput = $BuiltModuleSubdirectory + Write-Warning -Message "Some required Modules are missing, make sure you first run with the '-ResolveDependency' parameter. Running 'build.ps1 -ResolveDependency -Tasks noop' will pull required modules without running the build task." } } - else - { - $BuildModuleOutput = $OutputDirectory - } - - # Prepending $BuildModuleOutput folder to PSModulePath to resolve built module from this folder - if (($Env:PSModulePath -split [io.path]::PathSeparator) -notContains $BuildModuleOutput) - { - Write-Host -foregroundColor Green "[pre-build] Prepending '$BuildModuleOutput' folder to PSModulePath" - $Env:PSModulePath = $BuildModuleOutput + [io.path]::PathSeparator + $Env:PSModulePath - } - # Tell Resolve-Dependency to use $RequiredModulesPath as -PSDependTarget if not overridden in Build.psd1 - $PSDependTarget = $RequiredModulesPath + <# + The variable $PSDependTarget will be used below when building the splatting + variable before calling Resolve-Dependency.ps1, unless overridden in the + file Resolve-Dependency.psd1. + #> + $PSDependTarget = $requiredModulesPath } if ($ResolveDependency) { - Write-Host -Object "[pre-build] Resolving dependencies." -foregroundColor Green - $ResolveDependencyParams = @{ } + Write-Host -Object "[pre-build] Resolving dependencies." -ForegroundColor Green + $resolveDependencyParams = @{ } - # If BuildConfig is a Yaml file, bootstrap powershell-yaml via ResolveDependency + # If BuildConfig is a Yaml file, bootstrap powershell-yaml via ResolveDependency. if ($BuildConfig -match '\.[yaml|yml]$') { - $ResolveDependencyParams.add('WithYaml', $True) + $resolveDependencyParams.Add('WithYaml', $true) } - $ResolveDependencyAvailableParams = (Get-Command -Name '.\Resolve-Dependency.ps1').parameters.keys - foreach ($CmdParameter in $ResolveDependencyAvailableParams) - { + $resolveDependencyAvailableParams = (Get-Command -Name '.\Resolve-Dependency.ps1').Parameters.Keys + foreach ($cmdParameter in $resolveDependencyAvailableParams) + { # The parameter has been explicitly used for calling the .build.ps1 - if ($MyInvocation.BoundParameters.ContainsKey($CmdParameter)) + if ($MyInvocation.BoundParameters.ContainsKey($cmdParameter)) { - $ParamValue = $MyInvocation.BoundParameters.ContainsKey($CmdParameter) - Write-Debug " adding $CmdParameter :: $ParamValue [from user-provided parameters to Build.ps1]" - $ResolveDependencyParams.Add($CmdParameter, $ParamValue) + $paramValue = $MyInvocation.BoundParameters.Item($cmdParameter) + + Write-Debug " adding $cmdParameter :: $paramValue [from user-provided parameters to Build.ps1]" + + $resolveDependencyParams.Add($cmdParameter, $paramValue) } # Use defaults parameter value from Build.ps1, if any else { - if ($ParamValue = Get-Variable -Name $CmdParameter -ValueOnly -ErrorAction Ignore) + $paramValue = Get-Variable -Name $cmdParameter -ValueOnly -ErrorAction Ignore + + if ($paramValue) { - Write-Debug " adding $CmdParameter :: $ParamValue [from default Build.ps1 variable]" - $ResolveDependencyParams.add($CmdParameter, $ParamValue) + Write-Debug " adding $cmdParameter :: $paramValue [from default Build.ps1 variable]" + + $resolveDependencyParams.Add($cmdParameter, $paramValue) } } } - Write-Host -foregroundColor Green "[pre-build] Starting bootstrap process." - .\Resolve-Dependency.ps1 @ResolveDependencyParams + Write-Host -Object "[pre-build] Starting bootstrap process." -ForegroundColor Green + + .\Resolve-Dependency.ps1 @resolveDependencyParams } - if ($MyInvocation.ScriptName -notLike '*Invoke-Build.ps1') + if ($MyInvocation.ScriptName -notlike '*Invoke-Build.ps1') { - Write-Verbose "Bootstrap completed. Handing back to InvokeBuild." + Write-Verbose -Message "Bootstrap completed. Handing back to InvokeBuild." + if ($PSBoundParameters.ContainsKey('ResolveDependency')) { - Write-Verbose "Dependency already resolved. Removing task" + Write-Verbose -Message "Dependency already resolved. Removing task." + $null = $PSBoundParameters.Remove('ResolveDependency') } - Write-Host -foregroundColor Green "[build] Starting build with InvokeBuild." + + Write-Host -Object "[build] Starting build with InvokeBuild." -ForegroundColor Green + Invoke-Build @PSBoundParameters -Task $Tasks -File $MyInvocation.MyCommand.Path - Pop-Location -StackName BuildModule + + Pop-Location -StackName 'BuildModule' + return } } diff --git a/build.yaml b/build.yaml index 8e7e559..5a615bb 100644 --- a/build.yaml +++ b/build.yaml @@ -2,25 +2,26 @@ #################################################### # ModuleBuilder Configuration # #################################################### + CopyPaths: - - DSCResources - - en-US + - en-US + - DSCResources Encoding: UTF8 VersionedOutputDirectory: true #################################################### -# ModuleBuilder Dependent Modules Configuration # +# ModuleBuilder Submodules Configuration # #################################################### NestedModule: - DscResource.Common: - CopyOnly: true - Path: ./output/RequiredModules/DscResource.Common - AddToManifest: false - Exclude: PSGetModuleInfo.xml + DscResource.Common: + CopyOnly: true + Path: ./output/RequiredModules/DscResource.Common + AddToManifest: false + Exclude: PSGetModuleInfo.xml #################################################### -# Pipeline Configuration # +# Sampler Pipeline Configuration # #################################################### BuildWorkflow: '.': @@ -32,6 +33,8 @@ BuildWorkflow: - Build_Module_ModuleBuilder - Build_NestedModules_ModuleBuilder - Create_changelog_release_output + - Generate_Conceptual_Help + - Generate_Wiki_Content pack: - build @@ -45,8 +48,11 @@ BuildWorkflow: - Pester_if_Code_Coverage_Under_Threshold publish: - - Publish_release_to_GitHub - publish_module_to_gallery + - Publish_Release_To_GitHub + - Publish_GitHub_Wiki_Content + + #################################################### # PESTER Configuration # @@ -58,7 +64,6 @@ Pester: - Modules/DscResource.Common Script: - tests/Unit - ExcludeTag: Tag: CodeCoverageThreshold: 85 CodeCoverageOutputFile: JaCoCo_coverage.xml @@ -66,23 +71,21 @@ Pester: DscTest: ExcludeTag: - - 'Common Tests - New Error-Level Script Analyzer Rules' + - "Common Tests - New Error-Level Script Analyzer Rules" + Tag: ExcludeSourceFile: - output ExcludeModuleFile: - Modules/DscResource.Common MainGitBranch: main -Resolve-Dependency: - Gallery: 'PSGallery' - AllowPrerelease: false - Verbose: false - ModuleBuildTasks: Sampler: - '*.build.Sampler.ib.tasks' Sampler.GitHubTasks: - '*.ib.tasks' + DscResource.DocGenerator: + - 'Task.*' TaskHeader: | param($Path) @@ -101,3 +104,16 @@ GitHubConfig: GitHubConfigUserName: dscbot GitHubConfigUserEmail: dsccommunity@outlook.com UpdateChangelogOnPrerelease: false + +#################################################### +# DscResource.DocGenerator Configuration # +#################################################### +DscResource.DocGenerator: + Generate_Conceptual_Help: + MarkdownCodeRegularExpression: + - '\`(.+?)\`' # Match inline code-block + - '\\(\\)' # Match escaped backslash + - '\[[^\[]+\]\((.+?)\)' # Match markdown URL + - '_(.+?)_' # Match Italic (underscore) + - '\*\*(.+?)\*\*' # Match bold + - '\*(.+?)\*' # Match Italic (asterisk) diff --git a/codecov.yml b/codecov.yml index 55765b0..bff75ea 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,11 +1,10 @@ codecov: - notify: require_ci_to_pass: no # main should be the baseline for reporting branch: main comment: - layout: "reach, diff" + layout: "reach, diff, flags, files" behavior: default coverage: @@ -26,4 +25,4 @@ coverage: threshold: 5 fixes: - - "\d+\.\d+\.\d+\/::source/" # move path "X.Y.Z/" => "source/" + - '^\d+\.\d+\.\d+::source' # move path "X.Y.Z" => "source" diff --git a/source/DSCResources/DSC_Cluster/README.md b/source/DSCResources/DSC_Cluster/README.md new file mode 100644 index 0000000..1e22d4d --- /dev/null +++ b/source/DSCResources/DSC_Cluster/README.md @@ -0,0 +1,10 @@ +# Description + +Used to configure a failover cluster. If the cluster does not exist, it will be +created in the domain and the static IP address will be assigned to the cluster. +When the cluster exist (either it was created or already existed), it will add +the target node ($env:COMPUTERNAME) to the cluster. + +## Requirements + +* Target machine must be running Windows Server 2008 R2 or later. diff --git a/source/DSCResources/DSC_ClusterDisk/README.md b/source/DSCResources/DSC_ClusterDisk/README.md new file mode 100644 index 0000000..bfa70d6 --- /dev/null +++ b/source/DSCResources/DSC_ClusterDisk/README.md @@ -0,0 +1,7 @@ +# Description + +Configures shared disks in a cluster. + +## Requirements + +* Target machine must be running Windows Server 2008 R2 or later. diff --git a/source/DSCResources/DSC_ClusterNetwork/README.md b/source/DSCResources/DSC_ClusterNetwork/README.md new file mode 100644 index 0000000..1a5105d --- /dev/null +++ b/source/DSCResources/DSC_ClusterNetwork/README.md @@ -0,0 +1,29 @@ +# Description + +Configures a cluster network in a failover cluster. + +This resource is only able to change properties on cluster networks. +To add or remove networks from the cluster, add or remove them from +the cluster members. By adding a new subnet on one of the cluster +nodes, the network will be added to the cluster, and metadata can be +set using the ClusterNetwork module. + +## Requirements + +* Target machine must be running Windows Server 2008 R2 or later. + +## Parameters + +### Role + +This parameter sets the role of the cluster network. If the cluster network role +is not in desired state it will change to match this role. + +The cluster network role can be set to either the value 0, 1 or 3. + +0 = Do not allow cluster network communication +1 = Allow cluster network communication only +3 = Allow cluster network communication and client connectivity + +See this article for more information about cluster network role values; +[Configuring Windows Failover Cluster Networks](https://blogs.technet.microsoft.com/askcore/2014/02/19/configuring-windows-failover-cluster-networks/) diff --git a/source/DSCResources/DSC_ClusterPreferredOwner/README.md b/source/DSCResources/DSC_ClusterPreferredOwner/README.md new file mode 100644 index 0000000..f99fff3 --- /dev/null +++ b/source/DSCResources/DSC_ClusterPreferredOwner/README.md @@ -0,0 +1,8 @@ +# Description + +Configures preferred owners of a cluster group and cluster resources in a failover +cluster. + +## Requirements + +* Target machine must be running Windows Server 2008 R2 or later. diff --git a/source/DSCResources/DSC_ClusterProperty/README.md b/source/DSCResources/DSC_ClusterProperty/README.md new file mode 100644 index 0000000..15378fb --- /dev/null +++ b/source/DSCResources/DSC_ClusterProperty/README.md @@ -0,0 +1,7 @@ +# Description + +Configures cluster properties on a failover cluster. + +## Requirements + +* Target machine must be running Windows Server 2008 R2 or later. diff --git a/source/DSCResources/DSC_ClusterQuorum/README.md b/source/DSCResources/DSC_ClusterQuorum/README.md new file mode 100644 index 0000000..4fd9057 --- /dev/null +++ b/source/DSCResources/DSC_ClusterQuorum/README.md @@ -0,0 +1,9 @@ +# Description + +Configures quorum in a cluster. For information on how to choose the correct +quorum type, please see the article +[Understanding Quorum Configurations in a Failover Cluster](https://technet.microsoft.com/en-us/library/cc731739(v=ws.11).aspx). + +## Requirements + +* Target machine must be running Windows Server 2008 R2 or later. diff --git a/source/DSCResources/DSC_WaitForCluster/README.md b/source/DSCResources/DSC_WaitForCluster/README.md new file mode 100644 index 0000000..d467efb --- /dev/null +++ b/source/DSCResources/DSC_WaitForCluster/README.md @@ -0,0 +1,7 @@ +# Description + +Ensures that a node waits for a remote cluster is created. + +## Requirements + +* Target machine must be running Windows Server 2008 R2 or later. diff --git a/source/FailoverClusterDsc.psm1 b/source/FailoverClusterDsc.psm1 new file mode 100644 index 0000000..81d556e --- /dev/null +++ b/source/FailoverClusterDsc.psm1 @@ -0,0 +1,4 @@ +<# + This file is intentionally left empty. It is must be left here for the module + manifest to refer to. It is recreated during the build process. +#> diff --git a/source/WikiSource/Home.md b/source/WikiSource/Home.md new file mode 100644 index 0000000..40ce274 --- /dev/null +++ b/source/WikiSource/Home.md @@ -0,0 +1,76 @@ +# Welcome to the FailoverClusterDsc wiki + +*FailoverClusterDsc v#.#.#* + +Here you will find all the information you need to make use of the FailoverClusterDsc +DSC resources in the latest release. This includes details of the resources +that are available, current capabilities and known issues, and information +to help plan a DSC based implementation of FailoverClusterDsc. + +Please leave comments, feature requests, and bug reports for this module in +the [issues section](https://github.com/dsccommunity/FailoverClusterDsc/issues) +for this repository. + +## Deprecated resources + +The documentation, examples, unit test, and integration tests have been removed +for these deprecated resources. These resources will be removed +in a future release. + +*No deprecated resources*. + +## Getting started + +To get started either: + +- Install from the PowerShell Gallery using PowerShellGet by running the + following command: + +```powershell +Install-Module -Name FailoverClusterDsc -Repository PSGallery +``` + +- Download FailoverClusterDsc from the [PowerShell Gallery](http://www.powershellgallery.com/packages/FailoverClusterDsc/) + and then unzip it to one of your PowerShell modules folders (such as + `$env:ProgramFiles\WindowsPowerShell\Modules`). + +To confirm installation, run the below command and ensure you see the FailoverClusterDsc +DSC resources available: + +```powershell +Get-DscResource -Module FailoverClusterDsc +``` + +## Prerequisites + +### Powershell + +The minimum Windows Management Framework (PowerShell) version required is 5.0 +or higher, which ships with Windows 10 or Windows Server 2016, +but can also be installed on Windows 7 SP1, Windows 8.1, Windows Server 2012, +and Windows Server 2012 R2. + +### Windows Failover Cluster Feature + +The Windows Server feature Windows Failover Cluster must be installed prior +to using the DSC resources in this module. + +```powershell +WindowsFeature AddFailoverFeature +{ + Ensure = 'Present' + Name = 'Failover-clustering' +} + +WindowsFeature AddRemoteServerAdministrationToolsClusteringPowerShellFeature +{ + Ensure = 'Present' + Name = 'RSAT-Clustering-PowerShell' + + DependsOn = '[WindowsFeature]AddFailoverFeature' +} +``` + +## Change log + +A full list of changes in each version can be found in the [change log](https://github.com/dsccommunity/FailoverClusterDsc/blob/main/CHANGELOG.md). diff --git a/tests/Unit/DSC_ClusterPreferredOwner.Tests.ps1 b/tests/Unit/DSC_ClusterPreferredOwner.Tests.ps1 index 76e9dcd..865bdb7 100644 --- a/tests/Unit/DSC_ClusterPreferredOwner.Tests.ps1 +++ b/tests/Unit/DSC_ClusterPreferredOwner.Tests.ps1 @@ -140,7 +140,7 @@ foreach ($moduleVersion in @('2012', '2016')) } } - Describe "ClusterDisk_$moduleVersion\Get-TargetResource" { + Describe "ClusterPreferredOwner_$moduleVersion\Get-TargetResource" { BeforeAll { Mock -CommandName 'Get-ClusterGroup' -ParameterFilter $mockGetClusterGroup_ParameterFilter -MockWith $mockGetClusterGroup Mock -CommandName 'Get-ClusterOwnerNode' -MockWith $GetClusterOwnerNode @@ -233,7 +233,7 @@ foreach ($moduleVersion in @('2012', '2016')) } } - Describe "ClusterDisk_$moduleVersion\Test-TargetResource" { + Describe "ClusterPreferredOwner_$moduleVersion\Test-TargetResource" { BeforeAll { Mock -CommandName 'Get-ClusterGroup' -ParameterFilter $mockGetClusterGroup_ParameterFilter -MockWith $mockGetClusterGroup Mock -CommandName 'Get-ClusterOwnerNode' -MockWith $GetClusterOwnerNode @@ -289,7 +289,7 @@ foreach ($moduleVersion in @('2012', '2016')) } } - Describe "ClusterDisk_$moduleVersion\Set-TargetResource" { + Describe "ClusterPreferredOwner_$moduleVersion\Set-TargetResource" { BeforeAll { Mock -CommandName 'Get-ClusterGroup' -ParameterFilter $mockGetClusterGroup_ParameterFilter -MockWith $mockGetClusterGroup Mock -CommandName 'Get-ClusterOwnerNode' -MockWith $GetClusterOwnerNode