From fd0f607e8e854c9e22e26f6f71a68488fba2deb2 Mon Sep 17 00:00:00 2001 From: Gael Date: Fri, 24 Mar 2023 16:35:13 +0100 Subject: [PATCH] Creating ChocolateyPackage Class-based DSC Resource (#65) --- .kitchen.yml | 89 ++++-- CHANGELOG.md | 6 + RequiredModules.psd1 | 7 + build.yaml | 5 + source/Classes/001.ChocolateyReason.ps1 | 8 + source/Classes/002.ChocolateyPackage.ps1 | 280 ++++++++++++++++ source/Classes/003.ChocolateySoftware.ps1 | 194 +++++++++++ .../ChocolateyPackage/ChocolateyPackage.psm1 | 300 ------------------ .../ChocolateyPackage.schema.mof | 11 - .../en-US/ChocolateyPackage.strings.psd1 | 1 - .../ChocolateySoftware.psm1 | 173 ---------- .../ChocolateySoftware.schema.mof | 15 - .../en-US/ChocolateySoftware.strings.psd1 | 1 - source/Enum/Ensure.ps1 | 5 + .../ChocolateyIsInstalled.config.ps1 | 15 + source/WikiSource/Home.md | 1 + source/prefix.ps1 | 1 + .../private/Get-ChocolateyDefaultArgument.ps1 | 18 +- source/private/Get-Downloader.ps1 | 7 +- source/private/Get-RemoteFile.ps1 | 1 + source/private/Get-RemoteString.ps1 | 3 +- source/public/Get-ChocolateyPackage.ps1 | 7 +- source/public/Install-ChocolateyPackage.ps1 | 151 +++------ source/public/Install-ChocolateySoftware.ps1 | 38 ++- source/public/Uninstall-Chocolatey.ps1 | 2 +- source/public/Uninstall-ChocolateyPackage.ps1 | 2 +- source/public/Update-ChocolateyPackage.ps1 | 2 +- tasks/Clean.BuildHelpers.build.ps1 | 34 -- tasks/DeployAll.PSDeploy.build.ps1 | 47 --- tasks/Environment.BuildHelpers.build.ps1 | 19 -- tasks/Get-MergedModule.ps1 | 49 --- tasks/IntegrationTests.pester.build.ps1 | 45 --- tasks/MergeModule.Release.build.ps1 | 80 ----- tasks/QualityTests.pester.build.ps1 | 81 ----- tasks/TestResultUpload.Appveyor.build.ps1 | 38 --- tasks/UnitTests.pester.build.ps1 | 165 ---------- tasks/Update-DscResourceFromDefinition.ps1 | 43 --- .../UpdateDscResourceFromDefinition.build.ps1 | 26 -- tasks/generateHelp.PlatyPS.build.ps1 | 52 --- .../ChocolateyIsInstalled.tests.ps1 | 48 +++ tests/kitchen/provisioning.ps1 | 19 ++ 41 files changed, 741 insertions(+), 1348 deletions(-) create mode 100644 source/Classes/001.ChocolateyReason.ps1 create mode 100644 source/Classes/002.ChocolateyPackage.ps1 create mode 100644 source/Classes/003.ChocolateySoftware.ps1 delete mode 100644 source/DscResources/ChocolateyPackage/ChocolateyPackage.psm1 delete mode 100644 source/DscResources/ChocolateyPackage/ChocolateyPackage.schema.mof delete mode 100644 source/DscResources/ChocolateyPackage/en-US/ChocolateyPackage.strings.psd1 delete mode 100644 source/DscResources/ChocolateySoftware/ChocolateySoftware.psm1 delete mode 100644 source/DscResources/ChocolateySoftware/ChocolateySoftware.schema.mof delete mode 100644 source/DscResources/ChocolateySoftware/en-US/ChocolateySoftware.strings.psd1 create mode 100644 source/Enum/Ensure.ps1 create mode 100644 source/GCPackages/ChocolateyIsInstalled/ChocolateyIsInstalled.config.ps1 create mode 100644 source/WikiSource/Home.md create mode 100644 source/prefix.ps1 delete mode 100644 tasks/Clean.BuildHelpers.build.ps1 delete mode 100644 tasks/DeployAll.PSDeploy.build.ps1 delete mode 100644 tasks/Environment.BuildHelpers.build.ps1 delete mode 100644 tasks/Get-MergedModule.ps1 delete mode 100644 tasks/IntegrationTests.pester.build.ps1 delete mode 100644 tasks/MergeModule.Release.build.ps1 delete mode 100644 tasks/QualityTests.pester.build.ps1 delete mode 100644 tasks/TestResultUpload.Appveyor.build.ps1 delete mode 100644 tasks/UnitTests.pester.build.ps1 delete mode 100644 tasks/Update-DscResourceFromDefinition.ps1 delete mode 100644 tasks/UpdateDscResourceFromDefinition.build.ps1 delete mode 100644 tasks/generateHelp.PlatyPS.build.ps1 create mode 100644 tests/kitchen/GCPackages/ChocolateyIsInstalled/ChocolateyIsInstalled.tests.ps1 create mode 100644 tests/kitchen/provisioning.ps1 diff --git a/.kitchen.yml b/.kitchen.yml index 310a61b..04409fb 100644 --- a/.kitchen.yml +++ b/.kitchen.yml @@ -1,38 +1,73 @@ --- +# Check this doc: https://github.com/test-kitchen/kitchen-azurerm +# you may set the following environment variables: +# AZURE_CLIENT_ID="your-azure-client-id-here" +# AZURE_CLIENT_SECRET="your-client-secret-here" +# AZURE_TENANT_ID="your-azure-tenant-id-here" +# (check doc https://github.com/test-kitchen/kitchen-azurerm for more details) + +driver: + name: azurerm + subscription_id: <%= ENV['AZ_SUBSCRIPTION_ID'] %> + location: 'uksouth' + machine_size: 'Standard_D2s_v3' + username: azure + password: <%= + require "securerandom" + if File.exists?("./.kitchen/pwd.txt") + ENV['MACHINE_PASS'] = File.read("./.kitchen/pwd.txt") + else + ENV['MACHINE_PASS'] = SecureRandom.base64(24) + File.open("./.kitchen/pwd.txt", "w") { |f| f.write ENV['MACHINE_PASS'] } + end + ENV['MACHINE_PASS'] + %> + provisioner: - name: dsc - dsc_local_configuration_manager_version: wmf5 - dsc_local_configuration_manager: - reboot_if_needed: true - configuration_mode_frequency_mins: 15 - debug_mode: none - configuration_script_folder: Chocolatey/examples - configuration_script: dsc_configuration.ps1 - modules_path: BuildOutput/Artefact - retry_on_exit_code: - - 35 - configuration_data_variable: configurationData - modules_from_gallery: - - xPSDesiredStateConfiguration - - name: Pester - requiredversion: 4.0.3 - repository: PSGallery - #- PackageManagementProviderResource + name: shell # defaults to bash on linux, so the shebang is important! + script: 'tests/kitchen/provisioning.ps1' verifier: name: pester - test_folder: Chocolatey/tests/integration/ + shell: powershell + bootstrap: # installs modules from nuget feed by download and unzip. + repository_url: "https://www.powershellgallery.com/api/v2" + modules: + - PackageManagement + - PowerShellGet + test_folder: ./tests/kitchen/ + copy_folders: + - output/module/Chocolatey + downloads: + "./PesterTestResults.xml": "./output/testResults/" + # kitchen_cmd.ps1: "./output/testResults/" + # ./coverage.xml: "./output/testResults/" + # # : ./testresults platforms: - - name: win2012r2_PPB + - name: winCore2022 + driver: + # image_urn: MicrosoftWindowsServer:microsoftserveroperatingsystems-previews:windows-server-2022-azure-edition-preview-core:20348.11.2105262137 + image_urn: MicrosoftWindowsServer:WindowsServer:2022-datacenter-core:latest #20348.288.2110071044 + # use_ephemeral_osdisk: true os_type: windows shell: powershell + lifecycle: + post_create: + - remote: | + $null = Install-PackageProvider -Name NuGet -Confirm:$false -Force + Install-Module PowerShellGet -Force -SkipPublisherCheck -Confirm:$false -Scope AllUsers + Set-PSRepository -Name 'PSGallery' -InstallationPolicy 'Trusted' + Remove-Module -Name PowerShellGet + transport: + name: winrm suites: - - name: chocolatey - provisioner: - configuration_script_folder: Chocolatey/examples - configuration_script: chocolateyConfig.ps1 - - name: Default - - name: Remove - + - name: GCPackages + verifier: + copy_folders: + - output/GCPackages/ + - output/module/Chocolatey # This is only used when troubleshooting interactively. + install_modules: + - Name: GuestConfiguration + AllowPrerelease: true diff --git a/CHANGELOG.md b/CHANGELOG.md index 30a27fb..a1be2cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Modified project with new Sampler template. - Invoking choco commands now always add `--no-progress` & `--limit-output` +- Changed `ChocolateySoftware` to be class-based DSC Resource. +- Changed `ChocolateyPackage` to be class-based DSC Resource. + +### Added + +- Added the `ChocolateyIsInstalled` Azure Automanage Machine Configuration package that validates that Chocolatey is installed. ### Removed diff --git a/RequiredModules.psd1 b/RequiredModules.psd1 index 58d5a3b..6e9a8ed 100644 --- a/RequiredModules.psd1 +++ b/RequiredModules.psd1 @@ -28,4 +28,11 @@ 'DscResource.AnalyzerRules' = 'latest' xDscResourceDesigner = 'latest' 'DscResource.DocGenerator' = 'latest' + + 'GuestConfiguration' = @{ + version = 'latest' + Parameters = @{ + AllowPrerelease = $true + } + } } diff --git a/build.yaml b/build.yaml index 6d827fb..3985966 100644 --- a/build.yaml +++ b/build.yaml @@ -51,6 +51,9 @@ BuildWorkflow: # - Set_PSModulePath - Invoke_HQRM_Tests_Stop_On_Fail + gcpol: + - build_guestconfiguration_packages + # Defining test task to be run when invoking `./build.ps1 -Tasks test` test: # - Set_PSModulePath @@ -152,6 +155,8 @@ TaskHeader: | GitHubConfig: GitHubFilesToAdd: - 'CHANGELOG.md' + ReleaseAssets: + - output/GCPackages/ChocoIsInstalled*.zip GitHubConfigUserName: dscbot GitHubConfigUserEmail: dsccommunity@outlook.com UpdateChangelogOnPrerelease: false diff --git a/source/Classes/001.ChocolateyReason.ps1 b/source/Classes/001.ChocolateyReason.ps1 new file mode 100644 index 0000000..c23b3bc --- /dev/null +++ b/source/Classes/001.ChocolateyReason.ps1 @@ -0,0 +1,8 @@ +class ChocolateyReason +{ + [DscProperty(NotConfigurable)] + [string] $Code + + [DscProperty(NotConfigurable)] + [string] $Phrase +} diff --git a/source/Classes/002.ChocolateyPackage.ps1 b/source/Classes/002.ChocolateyPackage.ps1 new file mode 100644 index 0000000..4a86e01 --- /dev/null +++ b/source/Classes/002.ChocolateyPackage.ps1 @@ -0,0 +1,280 @@ +using namespace System.Management.Automation + +<# + .SYNOPSIS + The `ChocolateyPackage` DSC resource is used to install or remove chocolatey + packages. + .DESCRIPTION + The ChocolateyPackage DSC Resource helps with chocolatey package management. + .PARAMETER Name + The exact name of the ChocolateyPackage to set in the desired state. + .PARAMETER Version + The version of the package to install. If not set, it will only ensure the package + is present/absent. + If set to latest, it will always make sure the latest version available on the source is + the one installed. + If set to a version it will try to compare and make sure the installed version is greater + or equal than the one desired. + .PARAMETER ChocolateyOptions + Chocolatey parameters as per the Install or Update chocolateyPackage commands. + .PARAMETER UpdateOnly + Only update the package if present. + When absent do not attempt to install. + .PARAMETER Reasons + Reason for compliance or non-compliance returned by the Get method. + .EXAMPLE + Invoke-DscResource -ModuleName Chocolatey -Name ChocolateyPackage -Method Get -Property @{ + Ensure = 'present' + Name = 'localhost' + UpdateOnly = $true + } + + This example shows how to call the resource using Invoke-DscResource. +#> +[DscResource(RunAsCredential = 'Optional')] +class ChocolateyPackage +{ + [DscProperty(Mandatory)] + [Ensure] $Ensure = 'Present' + + [DscProperty(Key)] + [String] $Name + + [DscProperty()] + [String] $Version + + [DscProperty()] + [hashtable] $ChocolateyOptions + + [DscProperty()] + [PSCredential] $Credential + + [DscProperty(NotConfigurable)] + [bool] $UpdateOnly + + [DscProperty(NotConfigurable)] + [ChocolateyReason[]] $Reasons + + [ChocolateyPackage] Get() + { + $currentState = [ChocolateyPackage]::new() + $currentState.Name = $this.Name + + if ($false -eq (Test-ChocolateyInstall)) + { + Write-Debug -Message 'Chocolatey is not installed.' + $currentState.Ensure = 'Absent' + + $currentState.Reasons += @{ + code = 'ChocolateyPackage:ChocolateyPackage:ChocolateyNotInstalled' + phrase = 'The Chocolatey software is not installed. We cannot check if a package is present using choco.' + } + + return $currentState + } + + $localPackage = Get-ChocolateyPackage -LocalOnly -Name $this.Name -Exact + + if ($null -eq $localPackage) + { + Write-Debug -Message ('Local Package ''{0}'' not found.' -f $this.Name) + $currentState.Ensure = [Ensure]::Absent + } + else + { + Write-Debug -Message ('Local Package found: {0}' -f ($localPackage | ConvertTo-Json)) + $currentState.Ensure = [ensure]::Present + $currentState.Version = $localPackage.Version + } + + if ($this.Ensure -eq 'Absent' -and $currentState.Ensure -eq $this.Ensure) + { + Write-Debug -Message ('The package named ''{0}'' is absent as expected.' -f $this.Name) + $currentState.Reasons += @{ + Code = ('ChocolateyPackage:ChocolateyPackage:Compliant') + Phrase = ('The Package ''{0}'' is not installed as desired.' -f $currentState.Name) + } + } + elseif ([string]::IsNullOrEmpty($this.Version) -and $currentState.Ensure -eq $this.Ensure) + { + Write-Debug -Message ('The package named ''{0}'' is present as expected. No version required.' -f $this.Name) + $currentState.Reasons += @{ + Code = ('ChocolateyPackage:ChocolateyPackage:Compliant') + Phrase = ('The Package ''{0}'' is installed (with version ''{1}'').' -f $currentState.Name, $currentState.Version) + } + } + elseif (-not [string]::IsNullOrEmpty($this.Version) -and $this.Version -eq $localPackage.Version -and $currentState.Ensure -eq $this.Ensure) + { + Write-Debug -Message ('The package named ''{0}'' is present with expected version.' -f $this.Name) + $currentState.Reasons += @{ + Code = ('ChocolateyPackage:ChocolateyPackage:Compliant') + Phrase = ('The Package ''{0}'' is installed with expected version ''{1}''.' -f $currentState.Name, $currentState.Version) + } + } + elseif ($currentState.Ensure -ne $this.Ensure) + { + if ($this.Ensure -eq 'Absent') + { + $currentState.Reasons += @{ + Code = ('ChocolateyPackage:ChocolateyPackage:ShouldNotBeInstalled') + Phrase = ('The Package ''{0}'' is installed with version ''{1}'' but is NOT expected to be present.' -f $currentState.Name, $currentState.Version) + } + } + else + { + $currentState.Reasons += @{ + Code = ('ChocolateyPackage:ChocolateyPackage:ShouldBeInstalled') + Phrase = ('The Package ''{0}'' is not installed but is expected to be present.' -f $currentState.Name) + } + } + } + else + { + Write-Debug -Message ('The package named ''{0}'' is ''Present'' with version ''{1}'' while we expect version ''{2}''.' -f $this.Name, $currentState.Version,$this.Version) + if ('latest' -eq $this.Version) + { + Write-Debug -Message (' Grabbing the latest version of ''{0}'' from source.' -f $this.Name) + $searchVersionParam = @{ + Exact = $true + Name = $this.Name + } + + if ($this.ChocolateyOptions -and $this.ChocolateyOptions.ContainsKey('source')) + { + Write-Debug -Message (' Searching in specified source ''{0}''.' -f $this.ChocolateyOptions['source']) + $searchVersionParam['source'] = $this.ChocolateyOptions['source'] + } + + Write-Debug -Message (' Searching with ''Get-ChocolateyPackage'' and parameters {0}' -f ($searchVersionParam | ConvertTo-Json -Depth 3)) + $refVersionPackage = Get-ChocolateyPackage @searchVersionParam + + if ($null -eq $refVersionPackage) + { + Write-Debug -Message ('The package ''{0}'' could not be found on the source repository.' -f $this.Name) + $refVersion = $null + $currentState.Reasons += @{ + Code = ('ChocolateyPackage:ChocolateyPackage:LatestPackageNotFound') + Phrase = ('The Package ''{0}'' is installed with version ''{1}'' but couldn''t be found on the source.' -f $currentState.Name, $currentState.Version, $this.Version) + } + } + else + { + $refVersion = $refVersionPackage.Version + Write-Debug -Message ('Latest version for ''{0}'' found in source is ''{1}''.' -f $this.Name, $refVersion) + } + } + else + { + $refVersion = $this.Version + } + + if ([string]::IsNullOrEmpty($refVersion)) + { + # No need to check for version because the 'LatestPackageNotFound' on source... + } + elseif ((Compare-SemVerVersion -ReferenceVersion $refVersion -DifferenceVersion $currentState.version) -in @('=', '<')) + { + $currentState.Reasons += @{ + Code = ('ChocolateyPackage:ChocolateyPackage:Compliant') + Phrase = ('The Package ''{0}'' is installed with version ''{1}'' higher or equal than the expected ''{2}'' (''{3}'').' -f $currentState.Name, $currentState.Version, $this.Version, $refVersion) + } + } + else + { + $currentState.Reasons += @{ + Code = ('ChocolateyPackage:ChocolateyPackage:BelowExpectedVersion') + Phrase = ('The Package ''{0}'' is installed with version ''{1}'' Lower than the expected ''{2}''.' -f $currentState.Name, $currentState.Version, $this.Version) + } + } + } + + return $currentState + } + + [bool] Test() + { + $currentState = $this.Get() + + if ($currentState.Reasons.Code.Where({$_ -in @('BelowExpectedVersion','ShouldBeInstalled','ShouldNotBeInstalled','')})) + { + return $false + } + else + { + return $true + } + } + + [void] Set() + { + [ChocolateyPackage] $currentState = $this.Get() + $chocoCommand = Get-Command 'Install-ChocolateyPackage' + [hashtable] $chocoCommandParams = @{ + Name = $this.Name + Confirm = $false + ErrorAction = 'Stop' + } + + switch -Regex ($currentState.Reasons.Code) + { + 'BelowExpectedVersion$' + { + Write-Debug -Message ('Upgrading package ''{0}'' to version ''{1}''.' -f $this.Name, $this.Version) + $chocoCommand = Get-Command -Name 'Update-ChocolateyPackage' -Module 'Chocolatey' + } + + 'ShouldBeInstalled$' + { + $chocoCommand = Get-Command -Name 'Install-ChocolateyPackage' -Module 'Chocolatey' + + if ('latest' -eq $this.Version -or [string]::IsNullOrEmpty($this.Version)) + { + Write-Debug -Message ('Installing the ''latest'' version of ''{0}'' from the configured or specified source.' -f $this.Name) + } + else + { + Write-Debug -Message ('Installing package ''{0}'' to version ''{1}''.' -f $this.Name, $this.Version) + $chocoCommandParams['Version'] = $this.Version + } + } + + 'ShouldNotBeInstalled$' + { + $chocoCommand = Get-Command -Name 'Uninstall-ChocolateyPackage' -Module 'Chocolatey' + $chocoCommandParams['Force'] = $true + } + } + + if ($this.UpdateOnly -and $chocoCommand.Name -eq 'Install-ChocolateyPackage') + { + Write-Verbose -Message ('Skipping install of ''{0}'' because ''UpdateOnly'' is set.' -f $this.Name) + return + } + + if ($this.Credential) + { + $chocoCommandParams['Credential'] = $this.Credential + } + + $this.ChocolateyOptions.keys.Where{$_ -notin $chocoCommandParams.Keys}.Foreach{ + if ($chocoCommand.Parameters.Keys -contains $_) + { + if ($this.ChocolateyOptions[$_] -in @('True','False')) + { + $chocoCommandParams[$_] = [bool]::Parse($this.ChocolateyOptions[$_]) + } + else + { + $chocoCommandParams[$_] = $this.ChocolateyOptions[$_] + } + } + else + { + Write-Verbose -Message (' Ignoring parameter ''{0}''. Not suported by ''{1}''.' -f $_, $chocoCommand.Name) + } + } + + Write-Verbose -Message (' Calling ''{0}'' with parameters {1}.' -f $chocoCommand.Name,($chocoCommandParams | ConvertTo-Json -Depth 3)) + &$($chocoCommand.Name) @ChocoCommandParams + } +} diff --git a/source/Classes/003.ChocolateySoftware.ps1 b/source/Classes/003.ChocolateySoftware.ps1 new file mode 100644 index 0000000..9877d26 --- /dev/null +++ b/source/Classes/003.ChocolateySoftware.ps1 @@ -0,0 +1,194 @@ +using namespace System.Management.Automation + +<# + .SYNOPSIS + The `ChocolateySoftware` DSC resource is used to install or remove choco.exe. + .DESCRIPTION + Install Chocolatey Software either from a fixed URL where the chocolatey nupkg is stored, + or from the url of a NuGet feed containing the Chocolatey Package. + A version can be specified to lookup the Package feed for a specific version, and install it. + A proxy URL and credential can be specified to fetch the Chocolatey package, or the proxy configuration + can be ignored. + .PARAMETER Ensure + Indicate whether the Chocolatey Software should be installed or not on the system. + .PARAMETER Version + Version to install if you want to be specific, this is the way to Install a pre-release version, as when not specified, + the latest non-prerelease version is looked up from the feed defined in PackageFeedUrl. + .PARAMETER ChocolateyPackageUrl + Exact URL of the chocolatey package. This can be an HTTP server, a network or local path. + This must be the .nupkg package as downloadable from here: https://chocolatey.org/packages/chocolatey + .PARAMETER PackageFeedUrl + Url of the NuGet Feed API to use for looking up the latest version of Chocolatey (available on that feed). + This is also used when searching for a specific version, doing a lookup via an Odata filter. + .PARAMETER ChocoTempDir + The temporary folder to extract the Chocolatey Binaries during install. This does not set the Chocolatey Cache dir. + .PARAMETER IgnoreProxy + Ensure the proxy is bypassed when downloading the Chocolatey Package from the URL. + .PARAMETER ProxyCredential + Credential to authenticate to the proxy, if not specified but the ProxyLocation is set, an attempt + to use the Cached credential will be made. + .PARAMETER ProxyLocation + Proxy url to use when downloading the Chocolatey Package for installation. + .EXAMPLE + Invoke-DscResource -ModuleName Chocolatey -Name ChocolateySoftware -Method Get -Property @{ + Ensure = 'Present' + } + + # This example shows how to call the resource using Invoke-DscResource. +#> +[DscResource()] +class ChocolateySoftware +{ + [DscProperty(Key)] + [Ensure] $Ensure = 'Present' + + [DscProperty()] + [String] $InstallationDirectory + + [DscProperty()] #WriteOnly + [String] $ChocolateyPackageUrl + + [DscProperty()] #WriteOnly + [String] $PackageFeedUrl + + [DscProperty()] #WriteOnly + [string] $Version + + [DscProperty()] #WriteOnly + [String] $ChocoTempDir + + [DscProperty()] #WriteOnly + [string] $ProxyLocation + + [DscProperty()] #WriteOnly + [bool] $IgnoreProxy + + [DscProperty()] #WriteOnly + [PSCredential] $ProxyCredential + + [DscProperty(NotConfigurable)] + [ChocolateyReason[]] $Reasons + + [ChocolateySoftware] Get() + { + $currentState = [ChocolateySoftware]::new() + $chocoExe = Get-Command -Name 'choco.exe' -ErrorAction 'Ignore' + + if ($null -eq $chocoExe) + { + $currentState.Ensure = 'Absent' + } + else + { + $currentState.Ensure = 'Present' + $chocoBin = Split-Path -Path $chocoExe.Path -Parent + $chocoInstallPath = Split-Path -Path $chocoBin -Parent + $currentState.InstallationDirectory = $chocoInstallPath + } + + if ($this.Ensure -eq 'Present' -and $currentState.Ensure -eq 'Absent') + { + Write-Debug -Message 'Choco not found while it should be installed.' + $currentState.Reasons += @{ + code = 'ChocolateySoftware:ChocolateySoftware:ChocoShouldBeInstalled' + phrase = 'The Chocolatey software is not installed but is expected to be.' + } + } + elseif ($this.Ensure -eq 'Absent' -and $currentState.Ensure -eq $this.Ensure) + { + Write-Debug -Message 'Choco not found as expected.' + $currentState.Reasons += @{ + code = 'ChocolateySoftware:ChocolateySoftware:Compliant' + phrase = 'Chocolatey software is absent as expected.' + } + } + elseif ($this.Ensure -eq 'Absent' -and $currentState.Ensure -eq 'Present') + { + Write-Debug -Message 'Choco found while it should be ''Absent''.' + $currentState.Reasons += @{ + code = 'ChocolateySoftware:ChocolateySoftware:ChocoShouldBeRemoved' + phrase = 'Chocolatey software is unexpectedly present and should be uninstalled.' + } + } + else + { + Write-Verbose -Message 'Choco.exe is found as expected, let''s retrieve its Install Directory.' + # Present as it should + if ([string]::IsNullOrEmpty($this.InstallationDirectory) -or $this.InstallationDirectory -eq $currentState.InstallationDirectory) + { + $currentState.Reasons += @{ + code = 'ChocolateySoftware:ChocolateySoftware:Compliant' + phrase = ('Choco.exe is correctly installed at ''{0}''.' -f $currentState.InstallationDirectory) + } + } + else + { + $currentState.Reasons += @{ + code = 'ChocolateySoftware:ChocolateySoftware:ChocoInstalledInWrongDirectory' + phrase = ('Choco.exe is **incorrectly** installed at ''{0}''. Unable to remediate.' -f $currentState.InstallationDirectory) + } + } + } + + return $currentState + } + + [bool] Test() + { + $currentState = $this.Get() + + if ($currentState.Reasons.Code.Where{$_ -notmatch 'Compliant$'}) + { + return $false + } + else + { + return $true + } + } + + [void] Set() + { + $currentState = $this.Get() + + if ($currentState.Reasons.code.Where{$_ -match 'ChocoShouldBeInstalled$'}) + { + $properties = @( + 'ChocolateyPackageUrl' + 'PackageFeedUrl' + 'Version' + 'ChocoTempDir' + 'ProxyLocation' + 'InstallationDirectory' + 'IgnoreProxy' + 'InstallationDirectory' + 'ProxyCredential' + ) + + $installChocoSoftwareParam = @{} + + $properties.Where{-not [string]::IsNullOrEmpty($this.($_))}.Foreach{ + $installChocoSoftwareParam[$_] = $this.($_) + } + + Write-Debug -Message ('Installing Chocolatey with parameters: {0}' -f ($installChocoSoftwareParam | ConvertTo-Json -Depth 2)) + Install-ChocolateySoftware @installChocoSoftwareParam + } + elseif ($currentState.Reasons.code.Where{$_ -match 'ChocoShouldBeRemoved$'}) + { + if ( -not [string]::isNullOrEmpty($this.InstallationDirectory)) + { + Write-Debug -Message ('Uninstall-Chocolatey -InstallationDir ''{0}''' -f $this.InstallationDirectory) + $null = Uninstall-Chocolatey -InstallationDir $this.InstallationDirectory + } + else + { + $null = Uninstall-Chocolatey + } + } + else + { + Write-Verbose -Message 'No ChocolateySoftware action taken.' + } + } +} diff --git a/source/DscResources/ChocolateyPackage/ChocolateyPackage.psm1 b/source/DscResources/ChocolateyPackage/ChocolateyPackage.psm1 deleted file mode 100644 index cefe30a..0000000 --- a/source/DscResources/ChocolateyPackage/ChocolateyPackage.psm1 +++ /dev/null @@ -1,300 +0,0 @@ -function Get-TargetResource -{ - [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSDSCUseVerboseMessageInDSCResource', '')] - [CmdletBinding()] - [OutputType([System.Collections.Hashtable])] - param - ( - [Parameter(Mandatory = $true)] - [ValidateSet("Present", "Absent")] - [System.String] - $Ensure, - - [Parameter(Mandatory = $true)] - [System.String] - $Name, - - [Parameter()] - [System.String] - $Version, - - [Parameter()] - [Microsoft.Management.Infrastructure.CimInstance[]] - $ChocolateyOptions - ) - - $Env:Path = [Environment]::GetEnvironmentVariable('Path', 'Machine') - Write-Verbose "Converting CIMInstance[] to hashtable" - $ChocoOptions = Convert-CimInstancesToHashtable $ChocolateyOptions - - Import-Module $PSScriptRoot\..\..\Chocolatey.psd1 -Verbose:$False - $TestParams = @{ - Name = $Name - } - if ($Version) - { - $TestParams.Add('Version', $Version) - } - foreach ($Option in $ChocoOptions.keys) - { - if (!$TestParams.ContainsKey($Option) -and - $option -in (Get-Command Test-ChocolateyPackageIsInstalled).Parameters.keys) - { - $null = $TestParams.Add($option, $ChocoOptions[$Option]) - } - } - - $InstalledPackage = Test-ChocolateyPackageIsInstalled @TestParams - - $returnValue = @{ - Ensure = @('Absent', 'Present')[[int]$InstalledPackage.VersionGreaterOrEqual] - Name = $Name - Version = $Version - } - $returnValue -} - -function Set-TargetResource -{ - [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSDSCUseVerboseMessageInDSCResource', '')] - [CmdletBinding()] - param - ( - [Parameter(Mandatory = $true)] - [ValidateSet("Present", "Absent")] - [System.String] - $Ensure, - - [Parameter(Mandatory = $true)] - [System.String] - $Name, - - [Parameter()] - [System.String] - $Version, - - [Parameter()] - [Microsoft.Management.Infrastructure.CimInstance[]] - $ChocolateyOptions, - - [Parameter()] - [System.Boolean] - $UpdateOnly, - - [Parameter()] - [System.Management.Automation.PSCredential] - $Credential - ) - - begin - { - $Env:Path = [Environment]::GetEnvironmentVariable('Path', 'Machine') - Write-Verbose "Converting CIMInstance[] to hashtable" - $ChocoOptions = Convert-CimInstancesToHashtable $ChocolateyOptions - } - - process - { - Import-Module $PSScriptRoot\..\..\Chocolatey.psd1 -verbose:$False - - $TestParams = @{ - Name = $Name - } - if ($Version) - { - $TestParams.Add('Version', $Version) - } - foreach ($Option in $ChocoOptions.keys) - { - if (!$TestParams.ContainsKey($Option) -and - $option -in (Get-Command Test-ChocolateyPackageIsInstalled).Parameters.keys) - { - $null = $TestParams.Add($option, $ChocoOptions[$Option]) - } - } - - $testResult = Test-ChocolateyPackageIsInstalled @TestParams - $ChocoCommand = switch ($Ensure) - { - 'Present' - { - if ($testResult.PackagePresent -and !$testResult.VersionGreaterOrEqual) - { - Get-Command Update-ChocolateyPackage - } - elseif (!$UpdateOnly) - { - Get-Command Install-ChocolateyPackage - } - else - { - Write-Verbose "Nothing to do: UpdateOnly : $UpdateOnly" - return - } - } - 'Absent' - { - Get-Command Uninstall-ChocolateyPackage - } - } - Write-Verbose "$($ChocoCommand.Name) ``" - Write-Verbose "`t -Name $Name)" - Write-Verbose "`t -Confirm `$False)" - Write-Verbose "`t -NoProgress `$true)" - $ChocoCommandParams = @{ - Name = $Name - Confirm = $False - NoProgress = $true - } - if ($Version -and $Version -ne 'latest') - { - $ChocoCommandParams.Add('Version', $Version) - } - if ($Credential) - { - $ChocoCommandParams.Add('Credential', $Credential) - } - - #Allow merge but no overrides - foreach ($ChocoOptionName in $ChocoOptions.Keys) - { - if ( - !$ChocoCommandParams.ContainsKey($ChocoOptionName) -and - $ChocoCommand.Parameters.ContainsKey($ChocoOptionName) - ) - { - Write-Verbose "`t -$ChocoOptionName $($ChocoOptions[$ChocoOptionName])" - $ChocoCommandParams.Add($ChocoOptionName, $( - if ($ChocoOptions[$ChocoOptionName] -in @('True', 'False')) - { - [bool]::Parse($ChocoOptions[$ChocoOptionName]) - } - else - { - $ChocoOptions[$ChocoOptionName] - } - )) - } - } - Write-Verbose "Starting the Execution..." - &$ChocoCommand @ChocoCommandParams -verbose | Write-Verbose - - $PostActionResult = Test-ChocolateyPackageIsInstalled @TestParams - if ($PostActionResult.PackagePresent -and - $PostActionResult.VersionGreaterOrEqual -and - $Ensure -eq 'Present') - { - Write-Verbose -Message "--> Package Successfully Installed" - } - elseif ((!$PostActionResult.PackagePresent -or - !$PostActionResult.VersionGreaterOrEqual) -and - $Ensure -eq 'Absent') - { - Write-Verbose -Message "--> Package Successfully Removed" - } - else - { - throw "Chocolatey Package $($ChocoCommand.verb) Failed" - } - } -} - -function Test-TargetResource -{ - [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSDSCUseVerboseMessageInDSCResource', '')] - [CmdletBinding()] - [OutputType([System.Boolean])] - param - ( - [Parameter(Mandatory = $true)] - [ValidateSet("Present", "Absent")] - [System.String] - $Ensure, - - [Parameter(Mandatory = $true)] - [System.String] - $Name, - - [Parameter()] - [System.String] - $Version, - - [Parameter()] - [Microsoft.Management.Infrastructure.CimInstance[]] - $ChocolateyOptions, - - [Parameter()] - [System.Boolean] - $UpdateOnly, - - [Parameter()] - [System.Management.Automation.PSCredential] - $Credential - ) - - Write-Verbose "Converting CIMInstance[] to hashtable" - $ChocoOptions = Convert-CimInstancesToHashtable $ChocolateyOptions - - $Env:Path = [Environment]::GetEnvironmentVariable('Path', 'Machine') - Import-Module $PSScriptRoot\..\..\Chocolatey.psd1 -verbose:$False - - $TestParams = @{ - Name = $Name - } - if ($Credential) - { - $TestParams.Add('Credential', $Credential) - } - #Not decided whether Version should be mandatory or not - if ($Version) - { - $TestParams.Add('Version', $Version) - } - foreach ($Option in $ChocoOptions.keys) - { - if (!$TestParams.ContainsKey($Option) -and - $option -in (Get-Command Test-ChocolateyPackageIsInstalled).Parameters.keys) - { - $null = $TestParams.Add($option, $ChocoOptions[$Option]) - } - } - - Write-Verbose "Testing whether we need to refresh the PS environment so chocolatey doesn't fail" - if ($null -eq $env:ChocolateyInstall) - { - write-verbose "Set ChocolateyInstall" - $env:ChocolateyInstall = Convert-Path "$((Get-Command choco).Path)\..\.." - Import-Module "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1" - refreshenv > $null - } - - $EnsureResultMap = @{ - 'Present' = $true - 'Absent' = $false - } - Write-Verbose "Testing whether the Package $Name is Installed" - return ($EnsureResultMap[$Ensure] -eq (Test-ChocolateyPackageIsInstalled @TestParams).VersionGreaterOrEqual) -} - -Export-ModuleMember -Function *-TargetResource - -#As per Dave Wyatt's : https://powershell.org/forums/topic/hashtable-as-parameter-for-custom-dsc-resource/ -function Convert-CimInstancesToHashtable -{ - [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSDSCUseVerboseMessageInDSCResource', '')] - [CmdletBinding()] - [OutputType([hashtable])] - param ( - [Parameter()] - [Microsoft.Management.Infrastructure.CimInstance[]] - $Pairs - ) - - Write-Verbose ">> Converting CIM Pairs" - $hash = @{} - foreach ($pair in $Pairs) - { - $hash[$pair.Key] = $pair.Value - } - return $hash -} diff --git a/source/DscResources/ChocolateyPackage/ChocolateyPackage.schema.mof b/source/DscResources/ChocolateyPackage/ChocolateyPackage.schema.mof deleted file mode 100644 index 87bd508..0000000 --- a/source/DscResources/ChocolateyPackage/ChocolateyPackage.schema.mof +++ /dev/null @@ -1,11 +0,0 @@ - -[ClassVersion("1.0.0.0"), FriendlyName("ChocolateyPackage")] -class ChocolateyPackage : OMI_BaseResource -{ - [Required, Description("Specifies whether the Chocolatey Package should be installed on the machine or not."), ValueMap{"Present","Absent"}, Values{"Present","Absent"}] String Ensure; - [Key, Description("Chocolatey Package Id")] String Name; - [Write, Description("Chocolatey Package Version")] String Version; - [Write, EmbeddedInstance("MSFT_KeyValuePair"), Description("Chocolatey Options")] String ChocolateyOptions[]; - [Write, Description("Only updates installed package and ignores packages not Installed.")] boolean UpdateOnly; - [Write, EmbeddedInstance("MSFT_Credential")] String Credential; -}; diff --git a/source/DscResources/ChocolateyPackage/en-US/ChocolateyPackage.strings.psd1 b/source/DscResources/ChocolateyPackage/en-US/ChocolateyPackage.strings.psd1 deleted file mode 100644 index 621eb56..0000000 --- a/source/DscResources/ChocolateyPackage/en-US/ChocolateyPackage.strings.psd1 +++ /dev/null @@ -1 +0,0 @@ -# I am not an empty file diff --git a/source/DscResources/ChocolateySoftware/ChocolateySoftware.psm1 b/source/DscResources/ChocolateySoftware/ChocolateySoftware.psm1 deleted file mode 100644 index 7ed1b32..0000000 --- a/source/DscResources/ChocolateySoftware/ChocolateySoftware.psm1 +++ /dev/null @@ -1,173 +0,0 @@ -function Get-TargetResource -{ - [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSDSCUseVerboseMessageInDSCResource', '')] - [CmdletBinding()] - [OutputType([System.Collections.Hashtable])] - param - ( - [Parameter(Mandatory = $true)] - [ValidateSet("Present", "Absent")] - [System.String] - $Ensure, - - [Parameter()] - [System.String] - $InstallationDirectory - ) - <# - ,[string] - $InstallationDirectory - #> - $Env:Path = [Environment]::GetEnvironmentVariable('Path', 'Machine') - - if ($chocoCmd = Get-Command 'choco.exe' -CommandType Application -ErrorAction SilentlyContinue) - { - $chocoBin = Split-Path -Parent $chocoCmd.Path -ErrorAction SilentlyContinue - $InstallationDirectory = (Resolve-Path ([io.path]::combine($chocoBin, '..'))).Path - } - - Write-Output ( - @{ - Ensure = if ($chocoCmd) - { - 'Present' - } - else - { - 'Absent' - } - InstallationDirectory = $InstallationDirectory - }) -} - -function Set-TargetResource -{ - [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSDSCUseVerboseMessageInDSCResource', '')] - [CmdletBinding()] - param - ( - [Parameter(Mandatory = $true)] - [ValidateSet("Present", "Absent")] - [System.String] - $Ensure, - - [Parameter()] - [System.String] - $ChocolateyPackageUrl, - - [Parameter()] - [System.String] - $PackageFeedUrl, - - [Parameter()] - [System.String] - $Version, - - [Parameter()] - [System.String] - $ChocoTempDir, - - [Parameter()] - [System.String] - $ProxyLocation, - - [Parameter()] - [System.Management.Automation.PSCredential] - $ProxyCredential, - - [Parameter()] - [System.Boolean] - $IgnoreProxy, - - [Parameter()] - [System.String] - $InstallationDirectory - ) - $Env:Path = [Environment]::GetEnvironmentVariable('Path', 'Machine') - - Import-Module $PSScriptRoot\..\..\Chocolatey.psd1 -verbose:$False - - $ChocoParams = @{} - - if ($ensure -eq 'Present') - { - $AllowedParamName = (Get-Command Install-ChocolateySoftware).Parameters.keys - foreach ($key in ($PSBoundParameters.keys | Where-Object { $_ -in $AllowedParamName })) - { - if ($PSBoundParameters[$Key]) - { - $null = $ChocoParams.add($Key, $PSBoundParameters[$Key]) - } - } - Install-ChocolateySoftware @ChocoParams - } - else - { - Uninstall-Chocolatey -InstallDir $InstallationDirectory - } -} - -function Test-TargetResource -{ - [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSDSCUseVerboseMessageInDSCResource', '')] - [CmdletBinding()] - [OutputType([System.Boolean])] - param - ( - [Parameter(Mandatory = $true)] - [ValidateSet("Present", "Absent")] - [System.String] - $Ensure, - - [Parameter()] - [System.String] - $ChocolateyPackageUrl, - - [Parameter()] - [System.String] - $PackageFeedUrl, - - [Parameter()] - [System.String] - $Version, - - [Parameter()] - [System.String] - $ChocoTempDir, - - [Parameter()] - [System.String] - $ProxyLocation, - - [Parameter()] - [System.Management.Automation.PSCredential] - $ProxyCredential, - - [Parameter()] - [System.Boolean] - $IgnoreProxy, - - [Parameter()] - [System.String] - $InstallationDirectory - ) - $Env:Path = [Environment]::GetEnvironmentVariable('Path', 'Machine') - - Import-Module $PSScriptRoot\..\..\Chocolatey.psd1 -verbose:$False - - $ChocoParams = @{} - if ($InstallDir) - { - $ChocoParams.Add('InstallDir', $InstallDir) - } - - $EnsureTestMap = @{ - 'Present' = $true; - 'Absent' = $false - } - - return ($EnsureTestMap[$Ensure] -eq (Test-ChocolateyInstall @ChocoParams)) - -} - -Export-ModuleMember -Function *-TargetResource diff --git a/source/DscResources/ChocolateySoftware/ChocolateySoftware.schema.mof b/source/DscResources/ChocolateySoftware/ChocolateySoftware.schema.mof deleted file mode 100644 index 4a54715..0000000 --- a/source/DscResources/ChocolateySoftware/ChocolateySoftware.schema.mof +++ /dev/null @@ -1,15 +0,0 @@ - -[ClassVersion("1.0.0.0"), FriendlyName("ChocolateySoftware")] -class ChocolateySoftware : OMI_BaseResource -{ - [Key, Description("Specifies whether the Chocolatey Software should be installed on the machine or not."), ValueMap{"Present","Absent"}, Values{"Present","Absent"}] String Ensure; - [Write, Description("Optional package URL to download a specific Chocolatey Software package.")] String ChocolateyPackageUrl; - [Write, Description("Url of a Nuget feed where the Chocolatey software package is available. i.e. https://chocolatey.org/api/v2")] String PackageFeedUrl; - [Write, Description("Specific version of package available on the nuget Package Feed Url provided.")] String Version; - [Write, Description("Chocolatey Temp directory to unzip downloaded package during bootstrap.")] String ChocoTempDir; - [Write, Description("Url of the proxy to use to download the Chocolatey binaries.")] String ProxyLocation; - [Write, EmbeddedInstance("MSFT_Credential"), Description("Credential to be used to authenticate to the Proxy for downloading the Chocolatey Package.")] String ProxyCredential; - [Write, Description("Ignore proxy during download.")] Boolean IgnoreProxy; - [Write, Description("Path where Chocolatey should be installed, default to `$Env:ChocolateyInstall or C:\\ProgramData\\Chocolatey")] String InstallationDirectory; -}; - diff --git a/source/DscResources/ChocolateySoftware/en-US/ChocolateySoftware.strings.psd1 b/source/DscResources/ChocolateySoftware/en-US/ChocolateySoftware.strings.psd1 deleted file mode 100644 index 621eb56..0000000 --- a/source/DscResources/ChocolateySoftware/en-US/ChocolateySoftware.strings.psd1 +++ /dev/null @@ -1 +0,0 @@ -# I am not an empty file diff --git a/source/Enum/Ensure.ps1 b/source/Enum/Ensure.ps1 new file mode 100644 index 0000000..d15ae8e --- /dev/null +++ b/source/Enum/Ensure.ps1 @@ -0,0 +1,5 @@ +enum Ensure +{ + Absent + Present +} diff --git a/source/GCPackages/ChocolateyIsInstalled/ChocolateyIsInstalled.config.ps1 b/source/GCPackages/ChocolateyIsInstalled/ChocolateyIsInstalled.config.ps1 new file mode 100644 index 0000000..398a507 --- /dev/null +++ b/source/GCPackages/ChocolateyIsInstalled/ChocolateyIsInstalled.config.ps1 @@ -0,0 +1,15 @@ +Configuration ChocolateyIsInstalled { + Import-DscResource -ModuleName Chocolatey + + node ChocolateyIsInstalled { + ChocolateySoftware chocoSoftwareInstalled { + Ensure = 'Present' + } + + ChocolateyPackage chocoSoftwareInstalled { + Ensure = 'Present' + Name = 'chocolatey' + # Version = 'latest' + } + } +} diff --git a/source/WikiSource/Home.md b/source/WikiSource/Home.md new file mode 100644 index 0000000..f072434 --- /dev/null +++ b/source/WikiSource/Home.md @@ -0,0 +1 @@ +# Chocolatey PowerShell module diff --git a/source/prefix.ps1 b/source/prefix.ps1 new file mode 100644 index 0000000..46b925b --- /dev/null +++ b/source/prefix.ps1 @@ -0,0 +1 @@ +$Env:Path = [Environment]::GetEnvironmentVariable('Path', 'Machine') diff --git a/source/private/Get-ChocolateyDefaultArgument.ps1 b/source/private/Get-ChocolateyDefaultArgument.ps1 index 5ae2370..ed9737b 100644 --- a/source/private/Get-ChocolateyDefaultArgument.ps1 +++ b/source/private/Get-ChocolateyDefaultArgument.ps1 @@ -238,7 +238,7 @@ Should install arguments be used exclusively without appending to current packag #> function Get-ChocolateyDefaultArgument { - [CmdletBinding()] + [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'None')] [OutputType([collections.generic.list[string]])] param ( @@ -473,7 +473,7 @@ function Get-ChocolateyDefaultArgument process { [collections.generic.list[string]] $ChocoArguments = [collections.generic.list[string]]::new() - $ChocoArguments = switch ($PSBoundParameters.Keys) + $Arguments = switch ($PSBoundParameters.Keys) { 'Value' { @@ -789,8 +789,16 @@ function Get-ChocolateyDefaultArgument } } - $null = $ChocoArguments.Add('--no-progress') - $null = $ChocoArguments.Add('--limit-output') - return $ChocoArguments + + if ($PSCmdlet.ShouldProcess('Returning Choco arguments','return')) + { + $Arguments.Foreach{ + $null = $ChocoArguments.Add($_) + } + + $null = $ChocoArguments.Add('--no-progress') + $null = $ChocoArguments.Add('--limit-output') + return $ChocoArguments + } } } diff --git a/source/private/Get-Downloader.ps1 b/source/private/Get-Downloader.ps1 index 807c76f..f32faf3 100644 --- a/source/private/Get-Downloader.ps1 +++ b/source/private/Get-Downloader.ps1 @@ -25,10 +25,9 @@ function Get-Downloader { [CmdletBinding()] + [OutputType([System.Net.WebClient])] param ( - [Parameter( - Mandatory = $true - )] + [Parameter(Mandatory = $true)] [System.String] $url, @@ -46,7 +45,7 @@ function Get-Downloader $IgnoreProxy ) - $downloader = new-object System.Net.WebClient + $downloader = [System.Net.WebClient]::new() $defaultCreds = [System.Net.CredentialCache]::DefaultCredentials if ($defaultCreds -ne $null) diff --git a/source/private/Get-RemoteFile.ps1 b/source/private/Get-RemoteFile.ps1 index 871769a..14cbbe2 100644 --- a/source/private/Get-RemoteFile.ps1 +++ b/source/private/Get-RemoteFile.ps1 @@ -60,6 +60,7 @@ function Get-RemoteFile Write-Debug "`tWith $key :: $($PSBoundParameters[$key])" $null = $downloaderParams.Add($key , $PSBoundParameters[$key]) } + $downloader = Get-Downloader @downloaderParams $downloader.DownloadFile($url, $file) } diff --git a/source/private/Get-RemoteString.ps1 b/source/private/Get-RemoteString.ps1 index 1cc6b27..e75b0ff 100644 --- a/source/private/Get-RemoteString.ps1 +++ b/source/private/Get-RemoteString.ps1 @@ -28,7 +28,8 @@ function Get-RemoteString { [CmdletBinding()] - param ( + param + ( [Parameter()] [System.String] $url, diff --git a/source/public/Get-ChocolateyPackage.ps1 b/source/public/Get-ChocolateyPackage.ps1 index 366527a..398502b 100644 --- a/source/public/Get-ChocolateyPackage.ps1 +++ b/source/public/Get-ChocolateyPackage.ps1 @@ -165,14 +165,15 @@ function Get-ChocolateyPackage $CachePath = Join-Path -Path $CacheFolder -ChildPath 'GetChocolateyPackageCache.xml' try { - if (!(Test-Path $CacheFolder)) + if (-not (Test-Path -Path $CacheFolder)) { $null = New-Item -Type Directory -Path $CacheFolder -Force -ErrorAction Stop } - if (Test-Path $CachePath) + if (Test-Path -Path $CachePath) { $CachedFile = Get-Item $CachePath } + [io.file]::OpenWrite($CachePath).close() $CacheAvailable = $true } @@ -202,7 +203,7 @@ function Get-ChocolateyPackage { try { - $null = $UnfilteredResults | Export-Clixml -Path $CacheFile -Force -ErrorAction Stop + $null = $UnfilteredResults | Export-Clixml -Path $CacheFile -Force -ErrorAction 'Stop' Write-Debug "Unfiltered list cached at $CacheFile." } catch diff --git a/source/public/Install-ChocolateyPackage.ps1 b/source/public/Install-ChocolateyPackage.ps1 index 561b4ba..ca2a0dc 100644 --- a/source/public/Install-ChocolateyPackage.ps1 +++ b/source/public/Install-ChocolateyPackage.ps1 @@ -155,197 +155,131 @@ #> function Install-ChocolateyPackage { - [CmdletBinding( - SupportsShouldProcess = $true, - ConfirmImpact = 'High' - )] - param ( - [Parameter( - Mandatory = $true - , ValueFromPipeline - , ValueFromPipelineByPropertyName - )] + [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')] + param + ( + [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [System.String[]] $Name, - [Parameter( - , ValueFromPipelineByPropertyName - )] + [Parameter(ValueFromPipelineByPropertyName = $true)] [ValidateNotNullOrEmpty()] [System.String] $Version, - [Parameter( - ValueFromPipelineByPropertyName - )] + [Parameter(ValueFromPipelineByPropertyName = $true)] $Source, - [Parameter( - ValueFromPipelineByPropertyName - )] + [Parameter(ValueFromPipelineByPropertyName = $true)] [PSCredential] $Credential, - [Parameter( - ValueFromPipelineByPropertyName - )] + [Parameter(ValueFromPipelineByPropertyName = $true)] [switch] $Force, - [Parameter( - ValueFromPipelineByPropertyName - )] + [Parameter(ValueFromPipelineByPropertyName = $true)] [System.String] $CacheLocation, - [Parameter( - ValueFromPipelineByPropertyName - )] + [Parameter(ValueFromPipelineByPropertyName = $true)] [switch] $NoProgress, - [Parameter( - ValueFromPipelineByPropertyName - )] + [Parameter(ValueFromPipelineByPropertyName = $true)] [switch] $AcceptLicense, - [Parameter( - ValueFromPipelineByPropertyName - )] + [Parameter(ValueFromPipelineByPropertyName = $true)] [int] $Timeout, - [Parameter( - ValueFromPipelineByPropertyName - )] + [Parameter(ValueFromPipelineByPropertyName = $true)] [switch] $x86, - [Parameter( - ValueFromPipelineByPropertyName - )] + [Parameter(ValueFromPipelineByPropertyName = $true)] [System.String] $InstallArguments, - [Parameter( - ValueFromPipelineByPropertyName - )] + [Parameter(ValueFromPipelineByPropertyName = $true)] [System.String] $InstallArgumentsSensitive, - [Parameter( - ValueFromPipelineByPropertyName - )] + [Parameter(ValueFromPipelineByPropertyName = $true)] [System.String] $PackageParameters, - [Parameter( - ValueFromPipelineByPropertyName - )] + [Parameter(ValueFromPipelineByPropertyName = $true)] [System.String] $PackageParametersSensitive, - [Parameter( - ValueFromPipelineByPropertyName - )] + [Parameter(ValueFromPipelineByPropertyName = $true)] [switch] $OverrideArguments, - [Parameter( - ValueFromPipelineByPropertyName - )] + [Parameter(ValueFromPipelineByPropertyName = $true)] [switch] $NotSilent, - [Parameter( - ValueFromPipelineByPropertyName - )] + [Parameter(ValueFromPipelineByPropertyName = $true)] [switch] $ApplyArgsToDependencies, - [Parameter( - ValueFromPipelineByPropertyName - )] + [Parameter(ValueFromPipelineByPropertyName = $true)] [switch] $AllowDowngrade, - [Parameter( - ValueFromPipelineByPropertyName - )] + [Parameter(ValueFromPipelineByPropertyName = $true)] [switch] $IgnoreDependencies, - [Parameter( - ValueFromPipelineByPropertyName - )] + [Parameter(ValueFromPipelineByPropertyName = $true)] [switch] $ForceDependencies, - [Parameter( - ValueFromPipelineByPropertyName - )] + [Parameter(ValueFromPipelineByPropertyName = $true)] [switch] $SkipPowerShell, - [Parameter( - ValueFromPipelineByPropertyName - )] + [Parameter(ValueFromPipelineByPropertyName = $true)] [switch] $IgnoreChecksum, - [Parameter( - ValueFromPipelineByPropertyName - )] + [Parameter(ValueFromPipelineByPropertyName = $true)] [switch] $AllowEmptyChecksum, - [Parameter( - ValueFromPipelineByPropertyName - )] + [Parameter(ValueFromPipelineByPropertyName = $true)] [switch] $ignorePackageCodes, - [Parameter( - ValueFromPipelineByPropertyName - )] + [Parameter(ValueFromPipelineByPropertyName = $true)] [switch] $UsePackageCodes, - [Parameter( - ValueFromPipelineByPropertyName - )] + [Parameter(ValueFromPipelineByPropertyName = $true)] [switch] $StopOnFirstFailure, - [Parameter( - ValueFromPipelineByPropertyName - )] + [Parameter(ValueFromPipelineByPropertyName = $true)] [switch] $SkipCache, - [Parameter( - ValueFromPipelineByPropertyName - )] + [Parameter(ValueFromPipelineByPropertyName = $true)] [switch] $UseDownloadCache, - [Parameter( - ValueFromPipelineByPropertyName - )] + [Parameter(ValueFromPipelineByPropertyName = $true)] [switch] $SkipVirusCheck, - [Parameter( - ValueFromPipelineByPropertyName - )] + [Parameter(ValueFromPipelineByPropertyName = $true)] [switch] $VirusCheck, - [Parameter( - ValueFromPipelineByPropertyName - )] + [Parameter(ValueFromPipelineByPropertyName = $true)] [ValidateNotNullOrEmpty()] [int] $VirusPositive @@ -354,21 +288,26 @@ function Install-ChocolateyPackage begin { $null = $PSboundParameters.remove('Name') - if (-not ($chocoCmd = Get-Command 'choco.exe' -CommandType Application -ErrorAction SilentlyContinue)) + if (-not ($chocoCmd = Get-Command 'choco.exe' -CommandType 'Application' -ErrorAction 'SilentlyContinue')) { throw "Chocolatey Software not found." } + $CachePath = [io.path]::Combine($Env:ChocolateyInstall, 'cache', 'GetChocolateyPackageCache.xml') - if ( (Test-Path $CachePath)) + if ( (Test-Path -Path $CachePath)) { - $null = Remove-Item $CachePath -ErrorAction SilentlyContinue + Write-Debug -Message 'Removing cache begin of Install-ChocolateyPackage' + $null = Remove-Item -Path $CachePath -ErrorAction SilentlyContinue -Force -Confirm:$false + Write-Debug -Message 'Removed' } } + process { foreach ($PackageName in $Name) { $ChocoArguments = @('install', $PackageName) + $ChocoArguments += Get-ChocolateyDefaultArgument @PSBoundParameters Write-Verbose "choco $($ChocoArguments -join ' ')" @@ -376,11 +315,7 @@ function Install-ChocolateyPackage { #Impact confirmed, go choco go! $ChocoArguments += '-y' - $ChocoOut = &$chocoCmd $ChocoArguments - if ($ChocoOut) - { - Write-Output $ChocoOut - } + &$chocoCmd $ChocoArguments } } } diff --git a/source/public/Install-ChocolateySoftware.ps1 b/source/public/Install-ChocolateySoftware.ps1 index bc7a717..36323a1 100644 --- a/source/public/Install-ChocolateySoftware.ps1 +++ b/source/public/Install-ChocolateySoftware.ps1 @@ -152,47 +152,51 @@ function Install-ChocolateySoftware { $url = 'https://chocolatey.org/api/v2' } + Write-Verbose "Getting latest version of the Chocolatey package for download." $url = "$url/Packages()?`$filter=((Id%20eq%20%27chocolatey%27)%20and%20(not%20IsPrerelease))%20and%20IsLatestVersion" - Write-Debug "Retrieving Binary URL from Package Metadata: $url" + Write-Debug -Message ('Retrieving Binary URL from Package Metadata: {0}' -f $url) $GetRemoteStringParams = @{ url = $url } - $GetRemoteStringParamsName = (get-command Get-RemoteString).parameters.keys + + [string[]]$getRemoteStringParamsName = (Get-Command -Name Get-RemoteString).parameters.keys $KeysForRemoteString = $PSBoundParameters.keys | Where-Object { $_ -in $GetRemoteStringParamsName } foreach ($key in $KeysForRemoteString ) { - Write-Debug "`tWith $key :: $($PSBoundParameters[$key])" - $null = $GetRemoteStringParams.Add($key , $PSBoundParameters[$key]) + Write-Debug -Message "`tWith $key :: $($PSBoundParameters[$key])" + $GetRemoteStringParams[$key] =$PSBoundParameters[$key] } + [xml]$result = Get-RemoteString @GetRemoteStringParams Write-Debug "New URL for nupkg: $url" $url = $result.feed.entry.content.src } } + 'FromPackageUrl' { #ignores version - Write-Verbose "Downloading Chocolatey from : $ChocolateyPackageUrl" + Write-Verbose -Message "Downloading Chocolatey from : $ChocolateyPackageUrl" $url = $ChocolateyPackageUrl } } - if ($null -eq $env:TEMP) + if ([string]::IsNullOrEmpty($env:TEMP)) { $env:TEMP = Join-Path $Env:SYSTEMDRIVE 'temp' } $tempDir = [io.path]::Combine($Env:TEMP, 'chocolatey', 'chocInstall') - if (![System.IO.Directory]::Exists($tempDir)) + if (-not [System.IO.Directory]::Exists($tempDir)) { $null = New-Item -path $tempDir -ItemType Directory } - $file = Join-Path $tempDir "chocolatey.zip" + $file = Join-Path $tempDir "chocolatey.zip" # Download the Chocolatey package - Write-Verbose "Getting Chocolatey from $url." + Write-Verbose -Message "Getting Chocolatey from $url." $GetRemoteFileParams = @{ url = $url file = $file @@ -243,17 +247,17 @@ function Install-ChocolateySoftware Write-Verbose 'Ensuring chocolatey commands are on the path.' $chocoPath = [Environment]::GetEnvironmentVariable('ChocolateyInstall') - if ($chocoPath -eq $null -or $chocoPath -eq '') + if ([string]::IsNullOrEmpty($chocoPath)) { $chocoPath = "$env:ALLUSERSPROFILE\Chocolatey" } - if (!(Test-Path ($chocoPath))) + if (-not (Test-Path -Path $chocoPath)) { $chocoPath = "$env:SYSTEMDRIVE\ProgramData\Chocolatey" } - $chocoExePath = Join-Path $chocoPath 'bin' + $chocoExePath = Join-Path -Path $chocoPath -ChildPath 'bin' if ($($env:Path).ToLower().Contains($($chocoExePath).ToLower()) -eq $false) { @@ -261,13 +265,13 @@ function Install-ChocolateySoftware } Write-Verbose 'Ensuring chocolatey.nupkg is in the lib folder' - $chocoPkgDir = Join-Path $chocoPath 'lib\chocolatey' - $nupkg = Join-Path $chocoPkgDir 'chocolatey.nupkg' + $chocoPkgDir = Join-Path -Path $chocoPath -ChildPath 'lib\chocolatey' + $nupkg = Join-Path -Path $chocoPkgDir -ChildPath 'chocolatey.nupkg' $null = [System.IO.Directory]::CreateDirectory($chocoPkgDir) - Copy-Item "$file" "$nupkg" -Force -ErrorAction SilentlyContinue + Copy-Item -Path "$file" -Destination "$nupkg" -Force -ErrorAction SilentlyContinue - if ($ChocoVersion = & "$chocoPath\choco.exe" -v) + if ($ChocoVersion = & "$chocoPath\choco.exe" @('-v')) { - Write-Verbose "Installed Chocolatey Version: $ChocoVersion" + Write-Verbose ('Installed Chocolatey Version: {0}'-f $ChocoVersion) } } diff --git a/source/public/Uninstall-Chocolatey.ps1 b/source/public/Uninstall-Chocolatey.ps1 index 803a396..368fc1c 100644 --- a/source/public/Uninstall-Chocolatey.ps1 +++ b/source/public/Uninstall-Chocolatey.ps1 @@ -83,7 +83,7 @@ function Uninstall-Chocolatey if ($Pscmdlet.ShouldProcess('Chocofiles')) { - $FilesToRemove | Sort-Object -Descending FullName | remove-item -Force -recurse -ErrorAction SilentlyContinue + $FilesToRemove | Sort-Object -Descending FullName | remove-item -Force -recurse -ErrorAction 'SilentlyContinue' -Confirm:$false } Write-Verbose "Removing $InstallDir from the Path and the ChocolateyInstall Environment variable." diff --git a/source/public/Uninstall-ChocolateyPackage.ps1 b/source/public/Uninstall-ChocolateyPackage.ps1 index d493282..6546cc1 100644 --- a/source/public/Uninstall-ChocolateyPackage.ps1 +++ b/source/public/Uninstall-ChocolateyPackage.ps1 @@ -273,7 +273,7 @@ function Uninstall-ChocolateyPackage $CachePath = [io.path]::Combine($Env:ChocolateyInstall, 'cache', 'GetChocolateyPackageCache.xml') if ( (Test-Path $CachePath)) { - $null = Remove-Item $CachePath -ErrorAction SilentlyContinue + $null = Remove-Item -Path $CachePath -ErrorAction 'SilentlyContinue' -Confirm:$false } } process diff --git a/source/public/Update-ChocolateyPackage.ps1 b/source/public/Update-ChocolateyPackage.ps1 index 3133246..ebc79d4 100644 --- a/source/public/Update-ChocolateyPackage.ps1 +++ b/source/public/Update-ChocolateyPackage.ps1 @@ -335,7 +335,7 @@ function Update-ChocolateyPackage $CachePath = [io.path]::Combine($Env:ChocolateyInstall, 'cache', 'GetChocolateyPackageCache.xml') if ( (Test-Path $CachePath)) { - $null = Remove-Item $CachePath -ErrorAction SilentlyContinue + $null = Remove-Item -Path $CachePath -ErrorAction 'SilentlyContinue' -Confirm:$false } } diff --git a/tasks/Clean.BuildHelpers.build.ps1 b/tasks/Clean.BuildHelpers.build.ps1 deleted file mode 100644 index ee3cc65..0000000 --- a/tasks/Clean.BuildHelpers.build.ps1 +++ /dev/null @@ -1,34 +0,0 @@ -Param ( - - [io.DirectoryInfo] - $ProjectPath = (property ProjectPath (Join-Path $PSScriptRoot '../..' -Resolve -ErrorAction SilentlyContinue)), - - [string] - $BuildOutput = (property BuildOutput 'C:\BuildOutput'), - - [string] - $LineSeparation = (property LineSeparation ('-' * 78)) -) - -task Clean { - $LineSeparation - "`t`t`t CLEAN UP" - $LineSeparation - - if (![io.path]::IsPathRooted($BuildOutput)) { - $BuildOutput = Join-Path -Path $ProjectPath.FullName -ChildPath $BuildOutput - } - if (Test-Path $BuildOutput) { - "Removing $BuildOutput\*" - Gci .\BuildOutput\ -Exclude modules | Remove-Item -Force -Recurse - } - -} - -task CleanModule { - if (![io.path]::IsPathRooted($BuildOutput)) { - $BuildOutput = Join-Path -Path $ProjectPath.FullName -ChildPath $BuildOutput - } - "Removing $BuildOutput\*" - Gci .\BuildOutput\ | Remove-Item -Force -Recurse -Verbose -ErrorAction Stop -} \ No newline at end of file diff --git a/tasks/DeployAll.PSDeploy.build.ps1 b/tasks/DeployAll.PSDeploy.build.ps1 deleted file mode 100644 index c2aec3b..0000000 --- a/tasks/DeployAll.PSDeploy.build.ps1 +++ /dev/null @@ -1,47 +0,0 @@ -Param ( - [io.DirectoryInfo] - $ProjectPath = (property ProjectPath (Join-Path $PSScriptRoot '../..' -Resolve -ErrorAction SilentlyContinue)), - - [string] - $BuildOutput = (property BuildOutput 'C:\BuildOutput'), - - [string] - $ProjectName = (property ProjectName (Split-Path -Leaf (Join-Path $PSScriptRoot '../..')) ), - - [string] - $PesterOutputFormat = (property PesterOutputFormat 'NUnitXml'), - - [string] - $APPVEYOR_JOB_ID = $(try {property APPVEYOR_JOB_ID} catch {}), - - $DeploymentTags = $(try {property DeploymentTags} catch {}), - - $DeployConfig = (property DeployConfig 'Deploy.PSDeploy.ps1') -) - -task DeployAll { - $LineSeparation - "`t`t`t DEPLOYMENT " - $LineSeparation - - if (![io.path]::IsPathRooted($BuildOutput)) { - $BuildOutput = Join-Path -Path $ProjectPath.FullName -ChildPath $BuildOutput - } - - $DeployFile = [io.path]::Combine($ProjectPath, $DeployConfig) - - "Deploying Module based on $DeployConfig config" - - $InvokePSDeployArgs = @{ - Path = $DeployFile - Force = $true - Verbose = $true - } - - if($DeploymentTags) { - $null = $InvokePSDeployArgs.Add('Tags',$DeploymentTags) - } - - Import-Module PSDeploy - Invoke-PSDeploy @InvokePSDeployArgs -} \ No newline at end of file diff --git a/tasks/Environment.BuildHelpers.build.ps1 b/tasks/Environment.BuildHelpers.build.ps1 deleted file mode 100644 index cb2b460..0000000 --- a/tasks/Environment.BuildHelpers.build.ps1 +++ /dev/null @@ -1,19 +0,0 @@ -Param ( - [string] - $LineSeparation = (property LineSeparation ('-' * 78)), - - [string] - $VariableNamePrefix = $(try {property VariableNamePrefix} catch {''}), - - [switch] - $ForceEnvironmentVariables = $(try {property ForceEnvironmentVariables} catch {$false}) -) - -task SetBuildEnvironment { - $LineSeparation - 'Set-BuildEnvironment' - Set-BuildEnvironment -variableNamePrefix $VariableNamePrefix -ErrorVariable err -ErrorAction SilentlyContinue -Force:$ForceEnvironmentVariables -Verbose - foreach ($e in $err) { - Write-Host $e - } -} \ No newline at end of file diff --git a/tasks/Get-MergedModule.ps1 b/tasks/Get-MergedModule.ps1 deleted file mode 100644 index 2ccc63f..0000000 --- a/tasks/Get-MergedModule.ps1 +++ /dev/null @@ -1,49 +0,0 @@ -function Get-MergedModule { - param ( - [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)] - [String]$Name, - - [Parameter(ValueFromPipelineByPropertyName)] - [io.DirectoryInfo]$SourceFolder, - - [Parameter(ValueFromPipelineByPropertyName)] - [ScriptBlock]$Order, - - [String]$Separator = "`n`n", - - [switch] - $DeleteSource - ) - - begin { - $usingList = New-Object System.Collections.Generic.List[String] - $merge = New-Object System.Text.StringBuilder - $ListOfFileToDelete = New-Object System.Collections.Generic.List[String] - } - - process { - Write-Verbose "Processing $Name" - $FilePath = [io.path]::Combine($SourceFolder,$Name) - Get-ChildItem $FilePath -Filter *.ps1 -Recurse | Sort-Object $Order | ForEach-Object { - $content = $_ | Get-Content | ForEach-Object { - if ($_ -match '^using') { - $usingList.Add($_) - } else { - $_.TrimEnd() - } - } | Out-String - $null = $merge.AppendFormat('{0}{1}', $content.Trim(), $Separator) - $ListOfFileToDelete.Add($_.FullName) - } - } - - end { - $null = $merge.Insert(0, ($usingList | Sort-Object | Get-Unique | Out-String)) - if ($DeleteSource) { - $ListOfFileToDelete | Remove-Item -Confirm:$false - } - $merge.ToString() - } -} - -#Courtesy of Chris Dent https://github.com/indented-automation/ diff --git a/tasks/IntegrationTests.pester.build.ps1 b/tasks/IntegrationTests.pester.build.ps1 deleted file mode 100644 index e3e9fbe..0000000 --- a/tasks/IntegrationTests.pester.build.ps1 +++ /dev/null @@ -1,45 +0,0 @@ -#Requires -Modules Pester -Param ( - [io.DirectoryInfo] - $ProjectPath = (property ProjectPath (Join-Path $PSScriptRoot '../..' -Resolve -ErrorAction SilentlyContinue)), - - [string] - $ProjectName = (property ProjectName (Split-Path -Leaf (Join-Path $PSScriptRoot '../..')) ), - - [string] - $RelativePathToIntegrationTests = (property RelativePathToIntegrationTests 'tests/Integration'), - - [string] - $LineSeparation = (property LineSeparation ('-' * 78)) -) - -task IntegrationTests { - $LineSeparation - "`t`t`t RUNNING INTEGRATION TESTS" - $LineSeparation - "`tProject Path = $ProjectPath" - "`tProject Name = $ProjectName" - "`tIntegration Tests = $RelativePathToIntegrationTests" - $IntegrationTestPath = [io.DirectoryInfo][system.io.path]::Combine($ProjectPath,$ProjectName,$RelativePathToIntegrationTests) - "`tIntegration Tests = $IntegrationTestPath" - - if (!$IntegrationTestPath.Exists -and - ( #Try a module structure where the - ($IntegrationTestPath = [io.DirectoryInfo][system.io.path]::Combine($ProjectPath,$RelativePathToIntegrationTests)) -and - !$IntegrationTestPath.Exists - ) - ) - { - Write-Warning ('Integration tests Path Not found {0}' -f $IntegrationTestPath) - } - else { - "`tIntegrationTest Path: $IntegrationTestPath" - '' - Push-Location $IntegrationTestPath - - Import-module Pester - Invoke-Pester -ErrorAction Stop - - Pop-Location - } -} \ No newline at end of file diff --git a/tasks/MergeModule.Release.build.ps1 b/tasks/MergeModule.Release.build.ps1 deleted file mode 100644 index 6c842fd..0000000 --- a/tasks/MergeModule.Release.build.ps1 +++ /dev/null @@ -1,80 +0,0 @@ -Param ( - [io.DirectoryInfo] - $ProjectPath = (property ProjectPath (Join-Path $PSScriptRoot '../..' -Resolve -ErrorAction SilentlyContinue)), - - [string] - $ProjectName = (property ProjectName (Split-Path -Leaf (Join-Path $PSScriptRoot '../..')) ), - - [string] - $SourceFolder = $ProjectName, - - [string] - $BuildOutput = (property BuildOutput 'C:\BuildOutput'), - - [string] - $ModuleVersion = (property ModuleVersion $env:APPVEYOR_BUILD_VERSION), - - $MergeList = (property MergeList @('enum*','class*','priv*','pub*') ), - - [string] - $LineSeparation = (property LineSeparation ('-' * 78)) - -) - -Task CopySourceToModuleOut { - $LineSeparation - "`t`t`t COPY SOURCE TO BUILD OUTPUT" - $LineSeparation - - if (![io.path]::IsPathRooted($BuildOutput)) { - $BuildOutput = Join-Path -Path $ProjectPath.FullName -ChildPath $BuildOutput - } - $BuiltModuleFolder = [io.Path]::Combine($BuildOutput,$ProjectName) - "Copying $ProjectPath\$SourceFolder To $BuiltModuleFolder\" - Copy-Item -Path "$ProjectPath\$SourceFolder" -Destination "$BuiltModuleFolder\" -Recurse -} - -Task MergeFilesToPSM1 { - $LineSeparation - "`t`t`t MERGE TO PSM1" - $LineSeparation - if (![io.path]::IsPathRooted($BuildOutput)) { - $BuildOutput = Join-Path -Path $ProjectPath.FullName -ChildPath $BuildOutput - } - $BuiltModuleFolder = [io.Path]::Combine($BuildOutput,$ProjectName) - - # Merge individual PS1 files into a single PSM1, and delete merged files - $OutModulePSM1 = [io.path]::Combine($BuiltModuleFolder,"$ProjectName.psm1") - "Merging to $OutModulePSM1" - $MergeList | Get-MergedModule -DeleteSource -SourceFolder $BuiltModuleFolder | Out-File $OutModulePSM1 -Force -} - -Task CleanOutputEmptyFolders { - $LineSeparation - "`t`t`t REMOVE EMPTY FOLDERS" - $LineSeparation - if (![io.path]::IsPathRooted($BuildOutput)) { - $BuildOutput = Join-Path -Path $ProjectPath.FullName -ChildPath $BuildOutput - } - - Get-ChildItem $BuildOutput -Recurse -Force | Sort-Object -Property FullName -Descending | Where-Object { - $_.PSIsContainer -and - $_.GetFiles().count -eq 0 -and - $_.GetDirectories().Count -eq 0 - } | Remove-Item -} - -Task UpdateModuleManifest { - $LineSeparation - "`t`t`t UPDATE MODULE MANIFEST" - $LineSeparation - - if (![io.path]::IsPathRooted($BuildOutput)) { - $BuildOutput = Join-Path -Path $ProjectPath.FullName -ChildPath $BuildOutput - } - $BuiltModule = [io.path]::Combine($BuildOutput,$ProjectName,"$ProjectName.psd1") - Set-ModuleFunctions -Path $BuiltModule - if($ModuleVersion) { - Update-Metadata -path $BuiltModule -PropertyName ModuleVersion -Value $ModuleVersion - } -} \ No newline at end of file diff --git a/tasks/QualityTests.pester.build.ps1 b/tasks/QualityTests.pester.build.ps1 deleted file mode 100644 index bc2ffa7..0000000 --- a/tasks/QualityTests.pester.build.ps1 +++ /dev/null @@ -1,81 +0,0 @@ -#Requires -Modules Pester -Param ( - [io.DirectoryInfo] - $ProjectPath = (property ProjectPath (Join-Path $PSScriptRoot '../..' -Resolve -ErrorAction SilentlyContinue)), - - [System.String] - $BuildOutput = (property BuildOutput 'C:\BuildOutput'), - - [System.String] - $ProjectName = (property ProjectName (Split-Path -Leaf (Join-Path $PSScriptRoot '../..')) ), - - [System.String] - $PesterOutputFormat = (property PesterOutputFormat 'NUnitXml'), - - [System.String] - $RelativePathToQualityTests = (property RelativePathToQualityTests 'tests/QA'), - - [System.String] - $PesterOutputSubFolder = (property PesterOutputSubFolder 'PesterOut'), - - [System.String] - $LineSeparation = (property LineSeparation ('-' * 78)) -) - -task QualityTests { - $LineSeparation - "`t`t`t RUNNING Quality TESTS" - $LineSeparation - "`tProject Path = $ProjectPath" - "`tProject Name = $ProjectName" - "`tQuality Tests = $RelativePathToQualityTests" - - $QualityTestPath = [io.DirectoryInfo][system.io.path]::Combine($ProjectPath,$ProjectName,$RelativePathToQualityTests) - - if (!$QualityTestPath.Exists -and - ( #Try a module structure where the - ($QualityTestPath = [io.DirectoryInfo][system.io.path]::Combine($ProjectPath,$RelativePathToQualityTests)) -and - !$QualityTestPath.Exists - ) - ) - { - Write-Warning ('Cannot Execute Quality tests, Path Not found {0}' -f $QualityTestPath) - return - } - - "`tQualityTest Path: $QualityTestPath" - if (![io.path]::IsPathRooted($BuildOutput)) { - $BuildOutput = Join-Path -Path $ProjectPath.FullName -ChildPath $BuildOutput - } - # $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath(".\nonexist\foo.txt") - $PSVersion = 'PSv{0}.{1}' -f $PSVersionTable.PSVersion.Major, $PSVersionTable.PSVersion.Minor - $Timestamp = Get-date -uformat "%Y%m%d-%H%M%S" - $TestResultFileName = "QA_$PSVersion`_$TimeStamp.xml" - $TestResultFile = [system.io.path]::Combine($BuildOutput,'testResults','QA',$PesterOutputFormat,$TestResultFileName) - $TestResultFileParentFolder = Split-Path $TestResultFile -Parent - $PesterOutFilePath = [system.io.path]::Combine($BuildOutput,'testResults','QA',$PesterOutputSubFolder,$TestResultFileName) - $PesterOutParentFolder = Split-Path $PesterOutFilePath -Parent - - if (!(Test-Path $PesterOutParentFolder)) { - Write-Verbose "CREATING Pester Results Output Folder $PesterOutParentFolder" - $null = mkdir $PesterOutParentFolder -Force - } - - if (!(Test-Path $TestResultFileParentFolder)) { - Write-Verbose "CREATING Test Results Output Folder $TestResultFileParentFolder" - $null = mkdir $TestResultFileParentFolder -Force - } - - Push-Location $QualityTestPath - - Import-module Pester - $script:QualityTestResults = Invoke-Pester -ErrorAction Stop -OutputFormat NUnitXml -OutputFile $TestResultFile -PassThru - $null = $script:QualityTestResults | Export-Clixml -Path $PesterOutFilePath -Force - Pop-Location -} - -task FailBuildIfFailedQualityTest -If ($CodeCoverageThreshold -ne 0) { - assert ($script:QualityTestResults.FailedCount -eq 0) ('Failed {0} Quality tests. Aborting Build' -f $script:QualityTestResults.FailedCount) -} - -task QualityTestsStopOnFail QualityTests,FailBuildIfFailedQualityTest \ No newline at end of file diff --git a/tasks/TestResultUpload.Appveyor.build.ps1 b/tasks/TestResultUpload.Appveyor.build.ps1 deleted file mode 100644 index 6d85177..0000000 --- a/tasks/TestResultUpload.Appveyor.build.ps1 +++ /dev/null @@ -1,38 +0,0 @@ -Param ( - [io.DirectoryInfo] - $ProjectPath = (property ProjectPath (Join-Path $PSScriptRoot '../..' -Resolve -ErrorAction SilentlyContinue)), - - [string] - $BuildOutput = (property BuildOutput 'C:\BuildOutput'), - - [string] - $ProjectName = (property ProjectName (Split-Path -Leaf (Join-Path $PSScriptRoot '../..')) ), - - [string] - $PesterOutputFormat = (property PesterOutputFormat 'NUnitXml'), - - [string] - $APPVEYOR_JOB_ID = $(try {property APPVEYOR_JOB_ID} catch {}) -) - -task UploadUnitTestResultsToAppVeyor -If {(property BuildSystem 'unknown') -eq 'AppVeyor'} { - - if (![io.path]::IsPathRooted($BuildOutput)) { - $BuildOutput = Join-Path -Path $ProjectPath.FullName -ChildPath $BuildOutput - } - - $TestOutputPath = [system.io.path]::Combine($BuildOutput,'testResults','unit',$PesterOutputFormat) - $TestResultFiles = Get-ChildItem -Path $TestOutputPath -Filter *.xml - $TestResultFiles | Add-TestResultToAppveyor -} - -task UploadUnit2TestResultsToAppVeyor -If {(property BuildSystem 'unknown') -eq 'AppVeyor'} { - - if (![io.path]::IsPathRooted($BuildOutput)) { - $BuildOutput = Join-Path -Path $ProjectPath.FullName -ChildPath $BuildOutput - } - - $TestOutputPath = [system.io.path]::Combine($BuildOutput,'testResults','unit2',$PesterOutputFormat) - $TestResultFiles = Get-ChildItem -Path $TestOutputPath -Filter *.xml - $TestResultFiles | Add-TestResultToAppveyor -} \ No newline at end of file diff --git a/tasks/UnitTests.pester.build.ps1 b/tasks/UnitTests.pester.build.ps1 deleted file mode 100644 index 614639c..0000000 --- a/tasks/UnitTests.pester.build.ps1 +++ /dev/null @@ -1,165 +0,0 @@ -#Requires -Modules Pester -Param ( - [io.DirectoryInfo] - $ProjectPath = (property ProjectPath (Join-Path $PSScriptRoot '../..' -Resolve -ErrorAction SilentlyContinue)), - - [System.String] - $BuildOutput = (property BuildOutput 'C:\BuildOutput'), - - [System.Boolean] - $TestFromBuildOutput = $true, - - [System.String] - $ProjectName = (property ProjectName (Split-Path -Leaf (Join-Path $PSScriptRoot '../..')) ), - - [System.String] - $PesterOutputFormat = (property PesterOutputFormat 'NUnitXml'), - - [System.String] - $PathToUnitTests = (property PathToUnitTests 'tests/Unit'), - - [System.String] - $PesterOutputSubFolder = (property PesterOutputSubFolder 'PesterOut'), - - [Int] - [ValidateRange(0,100)] - $CodeCoverageThreshold = (property CodeCoverageThreshold 90), - - [System.String] - $LineSeparation = (property LineSeparation ('-' * 78)) -) - -task UnitTests { - $LineSeparation - "`t`t`t RUNNING UNIT TESTS" - $LineSeparation - "`tProject Path = $ProjectPath" - "`tProject Name = $ProjectName" - "`tUnit Tests = $PathToUnitTests" - "`tResult Folder= $BuildOutput\Unit\" - if ($TestFromBuildOutput) { - "`tTesting against compiled Module: $BuildOutput\$ProjectName" - } - else { - "`tTesting against Source Code: $BuildOutput\$ProjectPath" - } - - #Resolving the Unit Tests path based on 2 possible Path: - # ProjectPath\ProjectName\tests\Unit (my way, I like to ship tests with Modules) - # or ProjectPath\tests\Unit (Warren's way: http://ramblingcookiemonster.github.io/Building-A-PowerShell-Module/) - $UnitTestPath = [io.DirectoryInfo][system.io.path]::Combine($ProjectPath,$ProjectName,$PathToUnitTests) - - if (!$UnitTestPath.Exists -and - ( #Try a module structure where the tests are outside of the Source directory - ($UnitTestPath = [io.DirectoryInfo][system.io.path]::Combine($ProjectPath,$PathToUnitTests)) -and - !$UnitTestPath.Exists - ) - ) - { - Write-Warning ('Cannot Execute Unit tests, Path Not found {0}' -f $UnitTestPath) - return - } - - "`tUnitTest Path: $UnitTestPath" - '' - - if (![io.path]::IsPathRooted($BuildOutput)) { - $BuildOutput = Join-Path -Path $ProjectPath.FullName -ChildPath $BuildOutput - } - - $PSVersion = 'PSv{0}.{1}' -f $PSVersionTable.PSVersion.Major, $PSVersionTable.PSVersion.Minor - $Timestamp = Get-date -uformat "%Y%m%d-%H%M%S" - $TestResultFileName = "Unit_$PSVersion`_$TimeStamp.xml" - $TestResultFile = [system.io.path]::Combine($BuildOutput,'testResults','unit',$PesterOutputFormat,$TestResultFileName) - $TestResultFileParentFolder = Split-Path $TestResultFile -Parent - $PesterOutFilePath = [system.io.path]::Combine($BuildOutput,'testResults','unit',$PesterOutputSubFolder,$TestResultFileName) - $PesterOutParentFolder = Split-Path $PesterOutFilePath -Parent - - if (!(Test-Path $PesterOutParentFolder)) { - Write-Verbose "CREATING Pester Results Output Folder $PesterOutParentFolder" - $null = mkdir $PesterOutParentFolder -Force - } - - if (!(Test-Path $TestResultFileParentFolder)) { - Write-Verbose "CREATING Test Results Output Folder $TestResultFileParentFolder" - $null = mkdir $TestResultFileParentFolder -Force - } - - Push-Location $UnitTestPath - if ($TestFromBuildOutput) { - $ListOfTestedFile = Get-ChildItem -Recurse "$BuildOutput\$ProjectName" -include *.ps1,*.psm1 -Exclude *.tests.ps1 - } - else { - $ListOfTestedFile = Get-ChildItem | Foreach-Object { - $fileName = $_.BaseName -replace '\.tests' - "$ProjectPath\$ProjectName\*\$fileName.ps1" - } - } - - $ListOfTestedFile | ForEach-Object { Write-Verbose $_} - "Number of tested files: $($ListOfTestedFile.Count)" - $PesterParams = @{ - ErrorAction = 'Stop' - OutputFormat = $PesterOutputFormat - OutputFile = $TestResultFile - CodeCoverage = $ListOfTestedFile - PassThru = $true - } - Import-module Pester - if ($TestFromBuildOutput) { - Import-Module -Force ("$BuildOutput\$ProjectName" -replace '\\$') - } - else { - Import-Module -Force ("$ProjectPath\$ProjectName" -replace '\\$') - } - - $script:UnitTestResults = Invoke-Pester @PesterParams - $null = $script:UnitTestResults | Export-Clixml -Path $PesterOutFilePath -Force - Pop-Location -} - -task FailBuildIfFailedUnitTest -If ($CodeCoverageThreshold -ne 0) { - assert ($script:UnitTestResults.FailedCount -eq 0) ('Failed {0} Unit tests. Aborting Build' -f $script:UnitTestResults.FailedCount) -} - -task FailIfLastCodeConverageUnderThreshold { - $LineSeparation - "`t`t`t LOADING LAST CODE COVERAGE From FILE" - $LineSeparation - "`tProject Path = $ProjectPath" - "`tProject Name = $ProjectName" - "`tUnit Tests = $PathToUnitTests" - "`tResult Folder = $BuildOutput\Unit\" - "`tMin Coverage = $CodeCoverageThreshold %" - '' - - if (![io.path]::IsPathRooted($BuildOutput)) { - $BuildOutput = Join-Path -Path $ProjectPath.FullName -ChildPath $BuildOutput - } - - $TestResultFileName = "Unit_*.xml" - $PesterOutPath = [system.io.path]::Combine($BuildOutput,'testResults','unit',$PesterOutputSubFolder,$TestResultFileName) - if (-Not (Test-Path $PesterOutPath)) { - if ( $CodeCoverageThreshold -eq 0 ) { - Write-Host "Code Coverage SUCCESS with value of 0%. No Pester output found." -ForegroundColor Magenta - return - } - else { - throw "No command were tested. Threshold of $CodeCoverageThreshold % not met" - } - } - $PesterOutPath - $PesterOutFile = Get-ChildItem -Path $PesterOutPath | Sort-Object -Descending | Select-Object -first 1 - $PesterObject = Import-Clixml -Path $PesterOutFile.FullName - if ($PesterObject.CodeCoverage.NumberOfCommandsAnalyzed) { - $coverage = $PesterObject.CodeCoverage.NumberOfCommandsExecuted / $PesterObject.CodeCoverage.NumberOfCommandsAnalyzed - if ($coverage -lt $CodeCoverageThreshold/100) { - throw "The Code Coverage FAILURE: ($($Coverage*100) %) is under the threshold of $CodeCoverageThreshold %." - } - else { - Write-Host "Code Coverage SUCCESS with value of $($coverage*100) %" -ForegroundColor Green - } - } -} - -task UnitTestsStopOnFail UnitTests,FailBuildIfFailedUnitTest,FailIfLastCodeConverageUnderThreshold diff --git a/tasks/Update-DscResourceFromDefinition.ps1 b/tasks/Update-DscResourceFromDefinition.ps1 deleted file mode 100644 index 73afa71..0000000 --- a/tasks/Update-DscResourceFromDefinition.ps1 +++ /dev/null @@ -1,43 +0,0 @@ -function Update-DscResourceFromObjectMetadata { - [CmdletBinding()] - param ( - [Parameter(ValueFromPipelineByPropertyName)] - [io.DirectoryInfo]$SourceFolder, - - [PSCustomObject] - $DscResourceMetadata = (Get-Content -Raw "$((Resolve-Path $SourceFolder).Path)\DscResources\DSCResourcesDefinitions.json"| ConvertFrom-Json) - ) - - if (![io.path]::IsPathRooted($SourceFolder)) { - $SourceFolder = (Resolve-Path $SourceFolder).Path - } - foreach ($Resource in $DscResourceMetadata) - { - $DscProperties = @() - $ResourceName = $Resource.psobject.Properties.Name - Write-Verbose "Preparing $ResourceName" - foreach ($DscProperty in $Resource.($ResourceName)) { - $resourceParams = @{} - $DscProperty.psobject.properties | % { $resourceParams[$_.Name] = $_.value } - $DscProperties += New-xDscResourceProperty @resourceParams - } - - if (Test-Path "$SourceFolder\DscResources\$ResourceName") { - $DscResourceParams = @{ - Property = $DscProperties - Path = "$SourceFolder\DscResources\$ResourceName" - FriendlyName = $ResourceName - } - Update-xDscResource @DscResourceParams -Force - } - else { - $DscResourceParams = @{ - Name = $ResourceName - Property = $DscProperties - Path = "$SourceFolder\" - FriendlyName = $ResourceName - } - New-xDscResource @DscResourceParams - } - } -} diff --git a/tasks/UpdateDscResourceFromDefinition.build.ps1 b/tasks/UpdateDscResourceFromDefinition.build.ps1 deleted file mode 100644 index f1d2b4c..0000000 --- a/tasks/UpdateDscResourceFromDefinition.build.ps1 +++ /dev/null @@ -1,26 +0,0 @@ -Param ( - - [io.DirectoryInfo] - $ProjectPath = (property ProjectPath (Join-Path $PSScriptRoot '../..' -Resolve -ErrorAction SilentlyContinue)), - - [string] - $ProjectName = (property ProjectName (Split-Path -Leaf (Join-Path $PSScriptRoot '../..')) ), - - [string] - $LineSeparation = (property LineSeparation ('-' * 78)) -) - -task UpdateDscResource { - $LineSeparation - "`t`t`t UPDATING DSC SCRIPT RESOURCE SCHEMAS" - $LineSeparation - . $PSScriptRoot\Update-DscResourceFromDefinition.ps1 - - $SourceFolder = Join-Path -Path $ProjectPath.FullName -ChildPath $ProjectName - - if (Test-Path $SourceFolder) { - Update-DscResourceFromObjectMetadata -SourceFolder $SourceFolder - } -} - -task updateDscSchema UpdateDscResource \ No newline at end of file diff --git a/tasks/generateHelp.PlatyPS.build.ps1 b/tasks/generateHelp.PlatyPS.build.ps1 deleted file mode 100644 index c2dd37b..0000000 --- a/tasks/generateHelp.PlatyPS.build.ps1 +++ /dev/null @@ -1,52 +0,0 @@ -Param ( - [io.DirectoryInfo] - $ProjectPath = (property ProjectPath (Join-Path $PSScriptRoot '../..' -Resolve -ErrorAction SilentlyContinue)), - - [string] - $ProjectName = (property ProjectName (Split-Path -Leaf (Join-Path $PSScriptRoot '../..')) ), - - [string] - $SourceFolder = $ProjectName, - - [string] - $HelpFolder = (property HelpFolder 'docs'), - - [string] - $BuildOutput = (property BuildOutput 'C:\BuildOutput'), - - [cultureinfo] - $HelpCultureInfo = 'en-US', - - [string] - $LineSeparation = (property LineSeparation ('-' * 78)) - -) - -Task UpdateHelp{ - $LineSeparation - "`t`t`t UPDATE HELP MARKDOWN FILE" - $LineSeparation - - if (![io.path]::IsPathRooted($BuildOutput)) { - $BuildOutput = Join-Path -Path $ProjectPath.FullName -ChildPath $BuildOutput - } - $HelpFolder = [io.Path]::Combine($ProjectPath,$SourceFolder,$HelpFolder) - - import-module -Force ([io.DirectoryInfo][io.Path]::Combine($ProjectPath,$SourceFolder,"$ProjectName.psd1")).ToString() - Update-MarkdownHelpModule -Path $HelpFolder -} - - -Task GenerateMamlFromMd { - $LineSeparation - "`t`t`t GENERATE MAML IN BUILD OUTPUT" - $LineSeparation - - if (![io.path]::IsPathRooted($BuildOutput)) { - $BuildOutput = Join-Path -Path $ProjectPath.FullName -ChildPath $BuildOutput - } - $BuiltModuleFolder = [io.Path]::Combine($BuildOutput,$ProjectName) - - New-ExternalHelp -Path "$ProjectPath\$SourceFolder\$HelpFolder" -OutputPath "$BuiltModuleFolder\$HelpCultureInfo" -Force - -} \ No newline at end of file diff --git a/tests/kitchen/GCPackages/ChocolateyIsInstalled/ChocolateyIsInstalled.tests.ps1 b/tests/kitchen/GCPackages/ChocolateyIsInstalled/ChocolateyIsInstalled.tests.ps1 new file mode 100644 index 0000000..3e10fb5 --- /dev/null +++ b/tests/kitchen/GCPackages/ChocolateyIsInstalled/ChocolateyIsInstalled.tests.ps1 @@ -0,0 +1,48 @@ +BeforeAll { + $ProgressPreference = 'SilentlyContinue' + $ErrorActionPreference = 'Continue' + $ModulePath = (Join-Path -Path (Join-Path $Env:TEMP 'verifier') -ChildPath 'modules') + $packageZipPath = Join-Path -Path $ModulePath -ChildPath 'GCPackages/ChocolateyIsInstalled*.zip' + $packageZip = Get-Item -Path $packageZipPath -errorAction SilentlyContinue + + Import-Module (Join-Path -Path $ModulePath -ChildPath 'Chocolatey') -Verbose:$false + if (Test-ChocolateyInstall) + { + Uninstall-Chocolatey -ErrorAction 'SilentlyContinue' + } +} +Describe 'Test ChocolateyIsInstalled Package' { + it 'Package should be available' { + + Test-Path -Path $packageZip | Should -be $true -because (gci (split-path -parent $packageZipPath)) + { Import-Module -Name 'GuestConfiguration' -ErrorAction 'Stop' } | Should -not -Throw + Test-Path -Path $packageZip | Should -be $true + } + + it 'Gets the ChocolateyIsInstalled Package Compliance Status (non-compliant)' { + + $result = $null + $result = Get-GuestConfigurationPackageComplianceStatus -Path $packageZip + $result.Resources.Reasons | Should -not -BeNullOrEmpty + $result.complianceStatus | Should -be $false + } + + it 'Remediates the ChocolateyIsInstalled Package (and return compliant)' { + $result = Start-GuestConfigurationPackageRemediation -Path $packageZip + $result.complianceStatus | Should -be $true + } + + # it 'Gets the non-compliant InstalledApplicationLinux @(''powershell-preview'',''somethingNotInstalled'') Package Compliance Status (with params)' { + + # # $result = $null + # # $result = Get-GuestConfigurationPackageComplianceStatus -Path $packageZip -Parameter @{ + # # ResourceType = "GC_InstalledApplicationLinux" + # # ResourceId = "InstalledApplicationLinux" + # # ResourcePropertyName = "AttributesYmlContent" + # # ResourcePropertyValue = "powershell;somethingNotInstalled" + # # } + + # # $result.Resources.Reasons | Should -not -BeNullOrEmpty + # # $result.complianceStatus | Should -be $false + # } +} diff --git a/tests/kitchen/provisioning.ps1 b/tests/kitchen/provisioning.ps1 new file mode 100644 index 0000000..0b33563 --- /dev/null +++ b/tests/kitchen/provisioning.ps1 @@ -0,0 +1,19 @@ +#!/usr/bin/env pwsh +$ProgressPreference = 'SilentlyContinue' + +$PSVersionTable + +$psget = Import-Module -Name PowerShellget -PassThru +$psget.ModuleBase + +if ((Get-PSRepository -Name PSGallery).InstallationPolicy -ne 'Trusted') +{ + Set-PSRepository -Name PSGallery -InstallationPolicy Trusted +} + +# Install-Module -Name PSDesiredStateConfiguration -RequiredVersion 2.0.6 -Confirm:$false +# Import-Module -Name PSDesiredStateConfiguration -MinimumVersion 2.0 -PassThru +# Enable-ExperimentalFeature -Name PSDesiredStateConfiguration.InvokeDscResource -Confirm:$false + +Install-Module -Name GuestConfiguration -AllowPrerelease +Import-Module -Name GuestConfiguration