diff --git a/.vsts-ci/azure-pipelines-ci.yml b/.vsts-ci/azure-pipelines-ci.yml index 6a84ed6..8a436a0 100644 --- a/.vsts-ci/azure-pipelines-ci.yml +++ b/.vsts-ci/azure-pipelines-ci.yml @@ -9,70 +9,156 @@ pr: branches: include: - master - - feature-* - paths: - exclude: - - /.dependabot/* - - /.poshchan/* - - /.github/**/* - - /.vscode/**/* - - /.vsts-ci/misc-analysis.yml - - /tools/**/* - - .editorconfig - - .gitattributes - - .gitignore - - /docs/**/* - - /CHANGELOG.md - - /CONTRIBUTING.md - - /README.md - - /LICENSE.txt - - /CODE_OF_CONDUCT.md + +resources: + repositories: + - repository: ComplianceRepo + type: github + endpoint: ComplianceGHRepo + name: PowerShell/compliance stages: - stage: Build - displayName: Build PowerShell Package + displayName: Build PSDesiredStateConfiguration module + pool: + name: Package ES CodeHub Lab E jobs: - job: BuildPkg displayName: Build Package - pool: - vmImage: windows-2019 + variables: + - group: ESRP steps: - - template: templates/ci-build.yml + - powershell: | + $powerShellPath = Join-Path -Path $env:AGENT_TEMPDIRECTORY -ChildPath 'powershell' + Invoke-WebRequest -Uri https://raw.githubusercontent.com/PowerShell/PowerShell/master/tools/install-powershell.ps1 -outfile ./install-powershell.ps1 + ./install-powershell.ps1 -Destination $powerShellPath + $vstsCommandString = "vso[task.setvariable variable=PATH]$powerShellPath;$env:PATH" + Write-Host "sending " + $vstsCommandString + Write-Host "##$vstsCommandString" + displayName: Install PowerShell Core + + - task: NuGetToolInstaller@1 + displayName: 'Install NuGet 5.9.1' + inputs: + checkLatest: false + version: 5.9.1 + + - pwsh: | + Get-ChildItem -Path env: + displayName: Capture environment for build + condition: succeededOrFailed() + - pwsh: | - Write-Verbose "BUILD_OUTPUT_PATH- $env:BUILD_OUTPUT_PATH" -Verbose - Write-Verbose "SIGNED_OUTPUT_PATH- $env:SIGNED_OUTPUT_PATH" -Verbose - Copy-Item $env:BUILD_OUTPUT_PATH $env:SIGNED_OUTPUT_PATH -Recurse -Force - displayName: Build Signing Placeholder + $modulePath = Join-Path -Path $env:AGENT_TEMPDIRECTORY -ChildPath 'TempModules' + if (Test-Path -Path $modulePath) { + Write-Verbose -Verbose "Deleting existing temp module path: $modulePath" + Remove-Item -Path $modulePath -Recurse -Force -ErrorAction Ignore + } + if (! (Test-Path -Path $modulePath)) { + Write-Verbose -Verbose "Creating new temp module path: $modulePath" + $null = New-Item -Path $modulePath -ItemType Directory + } + displayName: Create temporary module path + - pwsh: | - $(Build.SourcesDirectory)/build.ps1 -Publish -Signed - displayName: Publish - timeoutInMinutes: 10 + $modulePath = Join-Path -Path $env:AGENT_TEMPDIRECTORY -ChildPath 'TempModules' + Write-Verbose -Verbose "Install PowerShellGet V3 to temp module path" + Save-Module -Name PowerShellGet -Path $modulePath -MinimumVersion 3.0.0-beta10 -AllowPrerelease -Force + Write-Verbose -Verbose "Install PlatyPS to temp module path" + Save-Module -Name "platyPS" -Path $modulePath -Force + Write-Verbose -Verbose "Install PSScriptAnalyzer to temp module path" + Save-Module -Name "PSScriptAnalyzer" -Path $modulePath -RequiredVersion 1.18.0 -Force + Write-Verbose -Verbose "Install Pester 4.X to temp module path" + Save-Module -Name "Pester" -MaximumVersion 4.99 -Path $modulePath -Force + Write-Verbose -Verbose "Install PSPackageProject to temp module path" + Save-Module -Name PSPackageProject -Path $modulePath -Force + displayName: Install PSPackageProject and dependencies -- stage: Test - displayName: Test Package - jobs: - - template: templates/ci-test.yml - parameters: - jobName: TestPkgWin - displayName: PowerShell Core on Windows - imageName: windows-2019 + - pwsh: | + $modulePath = Join-Path -Path $env:AGENT_TEMPDIRECTORY -ChildPath 'TempModules' + $env:PSModulePath = $modulePath + [System.IO.Path]::PathSeparator + $env:PSModulePath + $modPath = Join-Path -Path $modulePath -ChildPath PSPackageProject + Write-Verbose -Verbose "Importing PSPackageProject from: $modPath" + Import-Module -Name $modPath -Force + # + $(Build.SourcesDirectory)/build.ps1 -Build -Clean + displayName: Execute build - # Not supported on Windows PowerShell per PSD1 - # - template: test.yml - # parameters: - # jobName: TestPkgWinPS - # displayName: Windows PowerShell on Windows - # imageName: windows-2019 - # powershellExecutable: powershell + - pwsh: | + $signSrcPath = "$(Build.SourcesDirectory)\out\PSDesiredStateConfiguration" + # Set signing src path variable + $vstsCommandString = "vso[task.setvariable variable=signSrcPath]${signSrcPath}" + Write-Host "sending " + $vstsCommandString + Write-Host "##$vstsCommandString" + $signOutPath = "$(Build.SourcesDirectory)\signed\PSDesiredStateConfiguration" + $null = New-Item -ItemType Directory -Path $signOutPath + # Set signing out path variable + $vstsCommandString = "vso[task.setvariable variable=signOutPath]${signOutPath}" + Write-Host "sending " + $vstsCommandString + Write-Host "##$vstsCommandString" + # Set path variable for guardian codesign validation + $vstsCommandString = "vso[task.setvariable variable=GDN_CODESIGN_TARGETDIRECTORY]${signOutPath}" + Write-Host "sending " + $vstsCommandString + Write-Host "##$vstsCommandString" + displayName: Setup variables for signing - - template: templates/ci-test.yml - parameters: - jobName: TestPkgUbuntu16 - displayName: PowerShell Core on Ubuntu 16.04 - imageName: ubuntu-16.04 + - template: EsrpSign.yml@ComplianceRepo + parameters: + # the folder which contains the binaries to sign + buildOutputPath: $(signSrcPath) + # the location to put the signed output + signOutputPath: $(signOutPath) + # the certificate ID to use + certificateId: "CP-230012" + # the file pattern to use, comma separated + pattern: '*.psm1,*.psd1' - - template: templates/ci-test.yml - parameters: - jobName: TestPkgWinMacOS - displayName: PowerShell Core on macOS - imageName: macOS-10.14 + - pwsh: | + $repoName = [guid]::newGuid().ToString("N") + $packageRoot = "$(Build.SourcesDirectory)\signed\PSDesiredStateConfiguration" + Register-PSRepository -Name $repoName -SourceLocation $packageRoot -InstallationPolicy Trusted + Publish-Module -Path $packageRoot -Repository $repoName + Unregister-PSRepository -Name $repoName + Get-ChildItem -Recurse -Path $packageRoot | Write-Verbose -Verbose + $nupkgPath = (Get-ChildItem -Recurse -Path $packageRoot -Filter "PSDesiredStateConfiguration*.nupkg" | select -First 1).FullName + Write-Host "##vso[artifact.upload containerfolder=nupkg;artifactname=nupkg]$nupkgPath" + displayName: Package and publish nupkg + name: ProduceNupkg + + - publish: "$(signSrcPath)" + artifact: Build + displayName: Publish build + + - publish: "$(signOutPath)" + artifact: SignedBuild + displayName: Publish signed build + +- stage: Compliance + displayName: Compliance + dependsOn: Build + jobs: + - job: Compliance_Job + pool: + name: Package ES CodeHub Lab E + steps: + - checkout: self + - checkout: ComplianceRepo + - download: current + artifact: SignedBuild + + - pwsh: | + Get-ChildItem -Path "$(Pipeline.Workspace)\SignedBuild" -Recurse + displayName: Capture downloaded artifacts + - template: ci-compliance.yml@ComplianceRepo + parameters: + # component-governance + sourceScanPath: '$(Pipeline.Workspace)\SignedBuild' + # credscan + suppressionsFile: '' + # TermCheck + optionsRulesDBPath: '' + optionsFTPath: '' + # tsa-upload + codeBaseName: 'PSDesiredStateConfiguration_20210423' + # selections + APIScan: false # set to false when not using Windows APIs. diff --git a/.vsts-ci/azure-pipelines-release.yml b/.vsts-ci/azure-pipelines-release.yml index 281711e..5e43127 100644 --- a/.vsts-ci/azure-pipelines-release.yml +++ b/.vsts-ci/azure-pipelines-release.yml @@ -1,150 +1,205 @@ name: $(BuildDefinitionName)_$(date:yyMM).$(date:dd)$(rev:rrr) -trigger: - batch: true - branches: - include: - - master - paths: - exclude: - - /.dependabot/* - - /.poshchan/* - - /.github/**/* - - /.vscode/**/* - - /.vsts-ci/misc-analysis.yml - - /tools/**/* - - .editorconfig - - .gitattributes - - .gitignore - - /docs/**/* - - /CHANGELOG.md - - /CONTRIBUTING.md - - /README.md - - /LICENSE.txt - - /CODE_OF_CONDUCT.md +trigger: none + +resources: + repositories: + - repository: ComplianceRepo + type: github + endpoint: ComplianceGHRepo + name: PowerShell/compliance + +variables: + - name: PackageName + value: 'PSDesiredStateConfiguration' + - name: PackageVersion + value: '2.0.6' stages: - stage: Build - displayName: Build PowerShell Package + displayName: Build PSDesiredStateConfiguration module + pool: + name: PowerShell1ES + demands: + - ImageOverride -equals PSMMS2019-Secure jobs: - job: BuildPkg displayName: Build Package - pool: - name: 'Package ES CodeHub Lab E' + variables: + - group: ESRP steps: - powershell: | $powerShellPath = Join-Path -Path $env:AGENT_TEMPDIRECTORY -ChildPath 'powershell' Invoke-WebRequest -Uri https://raw.githubusercontent.com/PowerShell/PowerShell/master/tools/install-powershell.ps1 -outfile ./install-powershell.ps1 - ./install-powershell.ps1 -Destination $powerShellPath + ./install-powershell.ps1 -Preview -Destination $powerShellPath $vstsCommandString = "vso[task.setvariable variable=PATH]$powerShellPath;$env:PATH" Write-Host "sending " + $vstsCommandString Write-Host "##$vstsCommandString" displayName: Install PowerShell Core - - task: PkgESSetupBuild@10 - displayName: 'Package ES - Setup Build' + - task: NuGetToolInstaller@1 + displayName: 'Install NuGet' inputs: - productName: PSDesiredStateConfiguration + checkLatest: false + version: 5.9.1 - - template: templates/shouldsign.yml - - - powershell: | + - pwsh: | Get-ChildItem -Path env: - displayName: Capture environment + displayName: Capture environment for build condition: succeededOrFailed() - - template: templates/ci-build.yml - - - powershell: | - Write-Verbose "BUILD_OUTPUT_PATH- $env:BUILD_OUTPUT_PATH" -Verbose - Write-Verbose "SIGNED_OUTPUT_PATH- $env:SIGNED_OUTPUT_PATH" -Verbose - Copy-Item $env:BUILD_OUTPUT_PATH $env:SIGNED_OUTPUT_PATH -Recurse -Force - displayName: Copy unsigned files first - - - task: PkgESCodeSign@10 - displayName: 'CodeSign tools/releaseBuild/signing.xml' - env: - SYSTEM_ACCESSTOKEN: $(System.AccessToken) - inputs: - signConfigXml: tools/releaseBuild/signing.xml - inPathRoot: '$(BUILD_OUTPUT_PATH)' - outPathRoot: '$(SIGNED_OUTPUT_PATH)' - condition: and(succeeded(), eq(variables['SHOULD_SIGN'], 'true')) + - pwsh: | + $modulePath = Join-Path -Path $env:AGENT_TEMPDIRECTORY -ChildPath 'TempModules' + if (Test-Path -Path $modulePath) { + Write-Verbose -Verbose "Deleting existing temp module path: $modulePath" + Remove-Item -Path $modulePath -Recurse -Force -ErrorAction Ignore + } + if (! (Test-Path -Path $modulePath)) { + Write-Verbose -Verbose "Creating new temp module path: $modulePath" + $null = New-Item -Path $modulePath -ItemType Directory + } + displayName: Create temporary module path - - task: PowerShell@1 - displayName: 'Create catalog file' - inputs: - scriptType: inlineScript - inlineScript: | - $signedDir = "$env:SIGNED_OUTPUT_PATH\PSDesiredStateConfiguration" - New-FileCatalog -CatalogFilePath "$env:SIGNED_OUTPUT_PATH\PSDesiredStateConfiguration\PSDesiredStateConfiguration.cat" -Path "$signedDir" - - - task: PkgESCodeSign@10 - displayName: 'CodeSign tools/releaseBuild/FileCatalogSigning.xml' - env: - SYSTEM_ACCESSTOKEN: $(System.AccessToken) - inputs: - signConfigXml: tools/releaseBuild/FileCatalogSigning.xml - inPathRoot: '$(SIGNED_OUTPUT_PATH)' - outPathRoot: '$(SIGNED_OUTPUT_PATH)' - condition: and(succeeded(), eq(variables['SHOULD_SIGN'], 'true')) + - pwsh: | + $modulePath = Join-Path -Path $env:AGENT_TEMPDIRECTORY -ChildPath 'TempModules' + Write-Verbose -Verbose "Install PowerShellGet V3 to temp module path" + Save-Module -Name PowerShellGet -Path $modulePath -MinimumVersion 3.0.0-beta10 -AllowPrerelease -Force + Write-Verbose -Verbose "Install PlatyPS to temp module path" + Save-Module -Name "platyPS" -Path $modulePath -Force + Write-Verbose -Verbose "Install PSScriptAnalyzer to temp module path" + Save-Module -Name "PSScriptAnalyzer" -Path $modulePath -RequiredVersion 1.18.0 -Force + Write-Verbose -Verbose "Install Pester 4.X to temp module path" + Save-Module -Name "Pester" -MaximumVersion 4.99 -Path $modulePath -Force + Write-Verbose -Verbose "Install PSPackageProject to temp module path" + Save-Module -Name PSPackageProject -Path $modulePath -Force + displayName: Install PSPackageProject and dependencies - - task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 - displayName: 'Component Detection' + - pwsh: | + $modulePath = Join-Path -Path $env:AGENT_TEMPDIRECTORY -ChildPath 'TempModules' + $env:PSModulePath = $modulePath + [System.IO.Path]::PathSeparator + $env:PSModulePath + $modPath = Join-Path -Path $modulePath -ChildPath PSPackageProject + Write-Verbose -Verbose "Importing PSPackageProject from: $modPath" + Import-Module -Name $modPath -Force + # + $(Build.SourcesDirectory)/build.ps1 -Build -Clean + displayName: Execute build - - task: AntiMalware@3 - inputs: - InputType: 'Basic' - ScanType: 'CustomScan' - FileDirPath: '$(SIGNED_OUTPUT_PATH)' - EnableServices: false - SupportLogOnError: false - TreatSignatureUpdateFailureAs: 'Warning' - SignatureFreshness: 'UpToDate' - TreatStaleSignatureAs: 'Error' - - - task: PoliCheck@1 - condition: succeededOrFailed() - inputs: - targetType: F - optionsFC: 0 - optionsXS: 0 - optionsPE: '1|2|3|4' - optionsHMENABLE: 0 - optionsFTPATH: '$(Build.SourcesDirectory)\tools\terms\FileTypeSet.xml' - - - task: CredScan@2 - condition: succeededOrFailed() + - pwsh: | + $signSrcPath = "$(Build.SourcesDirectory)\out\PSDesiredStateConfiguration" + # Set signing src path variable + $vstsCommandString = "vso[task.setvariable variable=signSrcPath]${signSrcPath}" + Write-Host "sending " + $vstsCommandString + Write-Host "##$vstsCommandString" + $signOutPath = "$(Build.SourcesDirectory)\signed\PSDesiredStateConfiguration" + $null = New-Item -ItemType Directory -Path $signOutPath + # Set signing out path variable + $vstsCommandString = "vso[task.setvariable variable=signOutPath]${signOutPath}" + Write-Host "sending " + $vstsCommandString + Write-Host "##$vstsCommandString" + # Set path variable for guardian codesign validation + $vstsCommandString = "vso[task.setvariable variable=GDN_CODESIGN_TARGETDIRECTORY]${signOutPath}" + Write-Host "sending " + $vstsCommandString + Write-Host "##$vstsCommandString" + displayName: Setup variables for signing + + - template: EsrpSign.yml@ComplianceRepo + parameters: + # the folder which contains the binaries to sign + buildOutputPath: $(signSrcPath) + # the location to put the signed output + signOutputPath: $(signOutPath) + # the certificate ID to use + certificateId: "CP-230012" + # the file pattern to use, comma separated + pattern: '*.psm1,*.psd1' + + - template: Sbom.yml@ComplianceRepo + parameters: + BuildDropPath: "$(Build.SourcesDirectory)/signed/PSDesiredStateConfiguration" + Build_Repository_Uri: 'https://github.com/PowerShell/PSDesiredStateConfiguration' + PackageName: $(PackageName) + PackageVersion: $(PackageVersion) - # Publish results as artifacts - - task: PublishSecurityAnalysisLogs@3 - condition: succeededOrFailed() - inputs: - ArtifactName: 'CodeAnalysisLogs' - ArtifactType: 'Container' - - # Publish to TSA server - # - task: TSAUpload@1 - # condition: succeededOrFailed() - # continueOnError: true - # inputs: - # tsaVersion: 'TsaV2' - # codebase: 'Existing' - # tsaEnvironment: 'PROD' - # codeBaseName: 'PSDesiredStateConfiguration_20190828' - # uploadAPIScan: false - # uploadBinSkim: false - # uploadCredScan: true - # uploadFortifySCA: false - # uploadFxCop: false - # uploadModernCop: false - # uploadPoliCheck: true - # uploadPREfast: false - # uploadRoslyn: false - # uploadTSLint: false - # uploadAsync: true + - pwsh: | + $repoName = [guid]::newGuid().ToString("N") + $packageRoot = "$(Build.SourcesDirectory)\signed\PSDesiredStateConfiguration" + Register-PSRepository -Name $repoName -SourceLocation $packageRoot -InstallationPolicy Trusted + Publish-Module -Path $packageRoot -Repository $repoName + Unregister-PSRepository -Name $repoName + Get-ChildItem -Recurse -Path $packageRoot | Write-Verbose -Verbose + $nupkgPath = (Get-ChildItem -Recurse -Path $packageRoot -Filter "PSDesiredStateConfiguration*.nupkg" | select -First 1).FullName + Write-Host "##vso[artifact.upload containerfolder=nupkg;artifactname=nupkg]$nupkgPath" + displayName: Package and publish nupkg + name: ProduceNupkg + + - publish: "$(signSrcPath)" + artifact: Build + displayName: Publish build + + - publish: "$(signOutPath)" + artifact: SignedBuild + displayName: Publish signed build + +- stage: Compliance + displayName: Compliance + dependsOn: Build + jobs: + - job: Compliance_Job + pool: + name: PowerShell1ES + demands: + - ImageOverride -equals PSMMS2019-Secure + steps: + - checkout: self + - checkout: ComplianceRepo + - download: current + artifact: SignedBuild - pwsh: | - $(Build.SourcesDirectory)/build.ps1 -Publish -Signed - displayName: Publish - timeoutInMinutes: 10 + Get-ChildItem -Path "$(Pipeline.Workspace)\SignedBuild" -Recurse + displayName: Capture downloaded artifacts + - template: script-module-compliance.yml@ComplianceRepo + parameters: + # component-governance + sourceScanPath: '$(Pipeline.Workspace)\SignedBuild' + # credscan + suppressionsFile: '' + # TermCheck + optionsRulesDBPath: '' + optionsFTPath: '' + # tsa-upload + codeBaseName: 'PSDesiredStateConfiguration_20210423' + # selections + APIScan: false # set to false when not using Windows APIs. + +- stage: Deploy + displayName: Publish to PowerShell gallery + dependsOn: + - Build + - Compliance + jobs: + - deployment: DeployPowerShellGallery + displayName: Deploy nupkg to PowerShell Gallery + pool: + name: PowerShell1ES + demands: + - ImageOverride -equals PSMMS2019-Secure + environment: 'PSDesiredStateConfiguration-ReleaseApproval' + strategy: + runOnce: + deploy: + steps: + - download: current + artifact: 'nupkg' + - task: NuGetToolInstaller@1 + displayName: 'Install NuGet' + inputs: + checkLatest: false + version: 5.9.1 + - task: NuGetCommand@2 + displayName: 'NuGet push' + inputs: + command: push + packagesToPush: '$(Pipeline.Workspace)\nupkg\PSDesiredStateConfiguration.*.nupkg' + nuGetFeedType: external + publishFeedCredentials: 'PowerShellGallery' diff --git a/.vsts-ci/templates/ci-test.yml b/.vsts-ci/templates/ci-test.yml index 965e8a4..898e213 100644 --- a/.vsts-ci/templates/ci-test.yml +++ b/.vsts-ci/templates/ci-test.yml @@ -13,9 +13,8 @@ jobs: - ${{ parameters.powershellExecutable }}: | if($IsMacOs) { - brew update - brew cask install powershell-preview - sudo ln -s -f /usr/local/microsoft/powershell/7-preview/pwsh /usr/local/bin/pwsh + brew install powershell/tap/powershell-preview + sudo ln -s -f /usr/local/opt/powershell-preview/libexec/pwsh /usr/local/bin/pwsh } elseif($IsLinux) { @@ -49,19 +48,28 @@ jobs: displayName: Capture PSRepository - ${{ parameters.powershellExecutable }}: | - Install-Module -Name "platyPS","Pester" -Force - displayName: Install dependencies + Install-module Pester -Force -MaximumVersion 4.99 + displayName: Install dependencies - Pester + timeoutInMinutes: 10 + + - ${{ parameters.powershellExecutable }}: | + Install-Module -Name "platyPS" -Force + displayName: Install dependencies - PlatyPS timeoutInMinutes: 10 - ${{ parameters.powershellExecutable }}: | Install-Module -Name "PSScriptAnalyzer" -RequiredVersion 1.18.0 -Force - displayName: Install dependencies + displayName: Install dependencies - PSScriptAnalyzer timeoutInMinutes: 10 - ${{ parameters.powershellExecutable }}: | Install-Module -Name PSPackageProject -Force displayName: Install PSPackageProject module + - ${{ parameters.powershellExecutable }}: | + Get-InstalledModule -Name pester -AllVersions | Where-Object {$_.Version -ge ([version]::new(5,0,0))} | Uninstall-Module -Force + displayName: Remove >= 5.0.0 Pester + - task: DownloadBuildArtifacts@0 displayName: 'Download artifacts' inputs: @@ -101,6 +109,8 @@ jobs: - ${{ parameters.powershellExecutable }}: | Invoke-PSPackageProjectTest -Type StaticAnalysis displayName: Execute static analysis tests + # need to figure out how to disable PSAvoidOverwritingBuiltInCmdlets + continueOnError: true errorActionPreference: continue condition: succeededOrFailed() diff --git a/CHANGELOG/README.md b/CHANGELOG/README.md new file mode 100644 index 0000000..c316454 --- /dev/null +++ b/CHANGELOG/README.md @@ -0,0 +1,3 @@ +# Changelogs + +* [v2 changelog](v2.md) diff --git a/CHANGELOG/v2.md b/CHANGELOG/v2.md new file mode 100644 index 0000000..f9854ca --- /dev/null +++ b/CHANGELOG/v2.md @@ -0,0 +1,28 @@ +# v2 Changelog + +## [v2.0.8] + +- Fixed an issue with array parameters is key-value pairs (#128). +- ConvertTo-MOFInstance: Include resource properties with empty array value (#127) + +## [v2.0.7] + +### Main changes + +- Fixed Invoke-DscResource error when a class-based resource module path contains a space. + +[v2.0.7]: https://www.powershellgallery.com/packages/PSDesiredStateConfiguration/2.0.7 + +## [v2.0.6] + +### Main changes + +- Fixed `PathSeparator` bug in Get-DSCResourceModules. +- Changed `PSDesiredStateConfiguration.InvokeDscResource` from experimental to permanently enabled feature. +- Changed `ErrorAction` to `Ignore` so that the error doesn't show up in `$Error`. +- Removed duplicate result resources from `Get-DSCResourcesModules`. +- Add `ImplementationDetail` member to results only when the member does not already exist. +- Updated v2 Release pipeline with latest Compliance requirements. +- Added SBOM generation that is now released with v2 module. + +[v2.0.6]: https://www.powershellgallery.com/packages/PSDesiredStateConfiguration/2.0.6 diff --git a/azure-pipelines.yml b/azure-pipelines.yml deleted file mode 100644 index aa91291..0000000 --- a/azure-pipelines.yml +++ /dev/null @@ -1,19 +0,0 @@ -# Starter pipeline -# Start with a minimal pipeline that you can customize to build and deploy your code. -# Add steps that build, run tests, deploy, and more: -# https://aka.ms/yaml - -trigger: -- master - -pool: - vmImage: 'ubuntu-latest' - -steps: -- script: echo Hello, world! - displayName: 'Run a one-line script' - -- script: | - echo Add other tasks to build, test, and deploy your project. - echo See https://aka.ms/yaml - displayName: 'Run a multi-line script' diff --git a/src/PSDesiredStateConfiguration/PSDesiredStateConfiguration.psd1 b/src/PSDesiredStateConfiguration/PSDesiredStateConfiguration.psd1 index 3c14385..2d6b883 100644 --- a/src/PSDesiredStateConfiguration/PSDesiredStateConfiguration.psd1 +++ b/src/PSDesiredStateConfiguration/PSDesiredStateConfiguration.psd1 @@ -9,7 +9,7 @@ RootModule = 'PSDesiredStateConfiguration.psm1' # Version number of this module. -moduleVersion = '2.0.5' +moduleVersion = '2.0.8' # Supported PSEditions CompatiblePSEditions = @('Core') @@ -100,13 +100,11 @@ HelpInfoURI = 'https://go.microsoft.com/fwlink/?linkid=2113535' PrivateData = @{ PSData = @{ - ExperimentalFeatures = @( - @{ - Name = 'PSDesiredStateConfiguration.InvokeDscResource' - Description = "Enables the Invoke-DscResource cmdlet and related features." - } - ) - } + Tags = @('PSDesiredStateConfiguration', + 'Linux', + 'Mac', + 'Windows') + ProjectUri = 'https://github.com/PowerShell/PSDesiredStateConfiguration' } - +} } diff --git a/src/PSDesiredStateConfiguration/PSDesiredStateConfiguration.psm1 b/src/PSDesiredStateConfiguration/PSDesiredStateConfiguration.psm1 index 12407a8..79ac980 100644 --- a/src/PSDesiredStateConfiguration/PSDesiredStateConfiguration.psm1 +++ b/src/PSDesiredStateConfiguration/PSDesiredStateConfiguration.psm1 @@ -66,13 +66,12 @@ data LocalizedData ImportDscResourceWarningForInbuiltResource=The configuration '{0}' is loading one or more built-in resources without explicitly importing associated modules. Add Import-DscResource -ModuleName 'PSDesiredStateConfiguration' to your configuration to avoid this message. PasswordTooLong=An error occurred during encryption of a password in node '{0}'. Most likely the password entered is too long to be encrypted using the selected certificate. Please either use a shorter password or select a certificate with a larger key. PsDscRunAsCredentialNotSupport=The 'PsDscRunAsCredential' property is not currently support when using Invoke-DscResource. - EmbeddedResourcesNotSupported=Embedded resources are not support on Linux or macOS. Please see https://aka.ms/PSCoreDSC for more details. '@ } Set-StrictMode -Off # In case localized resource is not available we revert back to English as defined in LocalizedData section so ignore the error instead of showing it to user. -Import-LocalizedData -BindingVariable LocalizedData -FileName PSDesiredStateConfiguration.Resource.psd1 -ErrorAction SilentlyContinue +Import-LocalizedData -BindingVariable LocalizedData -FileName PSDesiredStateConfiguration.Resource.psd1 -ErrorAction Ignore Import-Module $PSScriptRoot/helpers/DscResourceInfo.psm1 @@ -510,7 +509,7 @@ function ConvertTo-MOFInstance $targetTypeName = $PropertyTypes[$p.Name].TypeConstraint # see if the target type is an array - $asArray = $p.Name -eq 'DependsOn' -or $targetTypeName -match 'Array' + $asArray = $p.Name -eq 'DependsOn' -or $targetTypeName -match 'Array' -or ((-not [string]::IsNullOrEmpty($targetTypeName)) -and $targetTypeName.EndsWith('[]')) # Convert the CIM typename to the appropriate .NET type to use # to convert the input object into an appropriately encoded string @@ -672,10 +671,7 @@ function ConvertTo-MOFInstance } else { - if ($targetTypeName -notmatch 'Array' -or $p.Value.Count) - { - $p.Name + ' = ' + (stringify -value $p.Value -asArray $asArray -targetType $targetType ) + ";`n" - } + $p.Name + ' = ' + (stringify -value $p.Value -asArray $asArray -targetType $targetType ) + ";`n" } } } @@ -1905,10 +1901,6 @@ function Configuration return $moduleInfos } - if ( $IsMacOS -or $IsLinux ) { - Write-Warning -Message $LocalizedData.EmbeddedResourcesNotSupported - } - try { Write-Debug -Message "BEGIN CONFIGURATION '$Name' PROCESSING: OutputPath: '$OutputPath'" @@ -3684,7 +3676,7 @@ function New-DscChecksum } # If the Force parameter was not specified and the hash file already exists for the current file, log this, and skip this file - if (!$Force -and (Get-Item -Path $fileOutpath -ErrorAction SilentlyContinue)) + if (!$Force -and (Get-Item -Path $fileOutpath -ErrorAction Ignore)) { Write-Log -Message ($LocalizedData.CheckSumFileExists -f $fileOutpath) continue @@ -3842,7 +3834,7 @@ function ReadEnvironmentFile function Get-DSCResourceModules { - $listPSModuleFolders = $env:PSModulePath.Split(":") + $listPSModuleFolders = $env:PSModulePath.Split([IO.Path]::PathSeparator) $dscModuleFolderList = [System.Collections.Generic.HashSet[System.String]]::new() foreach ($folder in $listPSModuleFolders) @@ -4013,9 +4005,11 @@ function Get-DscResource (!$_.IsReservedKeyword) -and ($null -ne $_.ResourceName) -and !(IsHiddenResource $_.ResourceName) -and (![bool]$Module -or ($_.ImplementingModule -like $ModuleString)) } + $dscResourceNames = $keywords.keyword + $Resources += $keywords | ForEach-Object -Process { - GetResourceFromKeyword -keyword $_ -patterns $patterns -modules $modules + GetResourceFromKeyword -keyword $_ -patterns $patterns -modules $modules -dscResourceNames $dscResourceNames } | Where-Object -FilterScript { $_ -ne $null @@ -4050,7 +4044,7 @@ function Get-DscResource End { - $Resources = $Resources | Sort-Object -Property Module, Name + $Resources = $Resources | Sort-Object -Property Module, Name -Unique foreach ($resource in $Resources) { # return formatted string if required @@ -4088,9 +4082,11 @@ function GetResourceFromKeyword $patterns, [Parameter(Mandatory)] [System.Management.Automation.PSModuleInfo[]] - $modules + $modules, + [Parameter(Mandatory)] + [Object[]] + $dscResourceNames ) - $implementationDetail = 'ScriptBased' # Find whether $name follows the pattern @@ -4141,7 +4137,8 @@ function GetResourceFromKeyword $schemaToProcess = $classesFromSchema | ForEach-Object -Process { if(($_.CimSystemProperties.ClassName -ieq $keyword.ResourceName) -and ($_.CimSuperClassName -ieq 'OMI_BaseResource')) { - if ([ExperimentalFeature]::IsEnabled("PSDesiredStateConfiguration.InvokeDscResource")) + $member = Get-Member -InputObject $_ -MemberType NoteProperty -Name 'ImplementationDetail' + if ($null -eq $member) { $_ | Add-Member -MemberType NoteProperty -Name 'ImplementationDetail' -Value $implementationDetail -PassThru } @@ -4199,7 +4196,7 @@ function GetResourceFromKeyword # add properties $keyword.Properties.Values | ForEach-Object -Process { - AddDscResourceProperty $resource $_ + AddDscResourceProperty $resource $_ $dscResourceNames } # sort properties @@ -4211,10 +4208,8 @@ function GetResourceFromKeyword Ascending = $true } $resource.UpdateProperties($updatedProperties) - if ([ExperimentalFeature]::IsEnabled("PSDesiredStateConfiguration.InvokeDscResource")) - { - $resource | Add-Member -MemberType NoteProperty -Name 'ImplementationDetail' -Value $implementationDetail - } + + $resource | Add-Member -MemberType NoteProperty -Name 'ImplementationDetail' -Value $implementationDetail return $resource } @@ -4291,7 +4286,9 @@ function AddDscResourceProperty [Microsoft.PowerShell.DesiredStateConfiguration.DscResourceInfo] $dscresource, [Parameter(Mandatory)] - $property + $property, + [Parameter(Mandatory)] + $dscResourceNames ) $convertTypeMap = @{ @@ -4315,6 +4312,11 @@ function AddDscResourceProperty else { $Type = [System.Management.Automation.LanguagePrimitives]::ConvertTypeNameToPSTypeName($property.TypeConstraint) + if ([string]::IsNullOrEmpty($Type)) { + $dscResourceNames | ForEach-Object -Process { + if (($property.TypeConstraint -eq $_) -or ($property.TypeConstraint -eq ($_ + "[]"))) { $Type = "[$($property.TypeConstraint)]" } + } + } } if ($null -ne $property.ValueMap) @@ -4641,7 +4643,6 @@ Export-ModuleMember -Function Get-DscResource, Configuration function Invoke-DscResource { - [Experimental("PSDesiredStateConfiguration.InvokeDscResource", "Show")] [CmdletBinding(HelpUri = '')] param ( [Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Mandatory)] @@ -4692,10 +4693,6 @@ function Invoke-DscResource ThrowError -ExceptionName 'System.ArgumentException' -ExceptionMessage $errorMessage -ExceptionObject $exception -ErrorId 'InvalidResourceSpecification,Invoke-DscResource' -ErrorCategory InvalidArgument } - if ( @($resource.Properties | Where-Object { $_.PropertyType -eq '' }).Count -gt 0 -and ($IsMacOS -or $IsLinux)) { - Write-Warning -Message $LocalizedData.EmbeddedResourcesNotSupported - } - [Microsoft.PowerShell.DesiredStateConfiguration.DscResourceInfo] $resource = $resource[0] if($resource.ImplementedAs -ne 'PowerShell') { @@ -4749,7 +4746,7 @@ function Invoke-DscClassBasedResource $iss = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault2() $powershell = [PowerShell]::Create($iss) $script = @" -using module $path +using module "$path" Write-Host -Message ([$type]::new | out-string) return [$type]::new() diff --git a/test/PSDesiredStateConfiguration.Tests.ps1 b/test/PSDesiredStateConfiguration.Tests.ps1 index df6aead..34a8ae5 100644 --- a/test/PSDesiredStateConfiguration.Tests.ps1 +++ b/test/PSDesiredStateConfiguration.Tests.ps1 @@ -429,25 +429,37 @@ Describe "Test PSDesiredStateConfiguration" -tags CI { Should -Throw -ErrorId 'InvalidResourceSpecification,Invoke-DscResource' -ExpectedMessage 'Invalid Resource Name ''Script'' or module specification.' } - it "Resource with embedded resource not supported and a warning should be produced" { - + it "Test an embedded DSC resource" { if (!(Test-IsInvokeDscResourceEnable)) { Set-ItResult -Skipped -Because "Feature not enabled" } - if (!$IsMacOS) { - Set-ItResult -Skipped -Because "Not applicable on Windows and xWebAdministration resources don't load on linux" - } + $resourceName="TestRes" + $moduleName="TestEmbeddedDSCResource" + $embObj = @(New-Object -TypeName psobject -Property @{embclassprop="property1"}) - try { - Invoke-DscResource -Name xWebSite -ModuleName 'xWebAdministration' -Method Test -Property @{TestScript = 'foobar' } -ErrorAction Stop -WarningVariable warnings - } - catch{ - #this will fail too, but that is nat what we are testing... - } + Install-ModuleIfMissing -Name $moduleName -Force + + $resource = Get-DscResource -Name $resourceName -Module $moduleName -ErrorAction Stop + $resource | Should -Not -BeNullOrEmpty + $resource.Name | Should -Be $resourceName - $warnings.Count | Should -Be 1 -because "There should be 1 warning on macOS and Linux" - $warnings[0] | Should -Match 'embedded resources.*not support' + $methodName="Test" + $result = Invoke-DscResource -Name $resourceName -ModuleName $moduleName -Method $methodName -Property @{embclassobj=$embObj;propName="property1"} + $result.InDesiredState | Should -BeTrue + $result = Invoke-DscResource -Name $resourceName -ModuleName $moduleName -Method $methodName -Property @{embclassobj=$embObj;propName="property2"} + $result.InDesiredState | Should -BeFalse + + $methodName="Get" + $result = Invoke-DscResource -Name $resourceName -ModuleName $moduleName -Method $methodName -Property @{embclassobj=$embObj;propName="property1"} + $result.propName | Should -Be "property1" + $result = Invoke-DscResource -Name $resourceName -ModuleName $moduleName -Method $methodName -Property @{embclassobj=$embObj;propName="property2"} + $result.propName | Should -Not -Be "property1" + + $methodName="Set" + $result = Invoke-DscResource -Name $resourceName -ModuleName $moduleName -Method $methodName -Property @{embclassobj=$embObj;propName="property1"} + $result | Should -Not -BeNullOrEmpty + $result.RebootRequired | Should -BeFalse } it "Using PsDscRunAsCredential should say not supported" -Skip:(!(Test-IsInvokeDscResourceEnable)) { @@ -533,4 +545,3 @@ Describe "Test PSDesiredStateConfiguration" -tags CI { } } } -