diff --git a/DSCResources/cChocoConfig/cChocoConfig.psm1 b/DSCResources/cChocoConfig/cChocoConfig.psm1 new file mode 100644 index 0000000..6810a89 --- /dev/null +++ b/DSCResources/cChocoConfig/cChocoConfig.psm1 @@ -0,0 +1,180 @@ +# Copyright (c) 2017 Chocolatey Software, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +<# + .SYNOPSIS + DSC Resource to set/unset chocolatey config settings + + .NOTES + This resource supports querying the XML file directly. This is faster but not recommended as it does not respect the chocolatey api. + Use this feature if you will not be upgrading chocolatey as updates may change the xml format and break this resource! +#> +function Get-TargetResource { + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] + param ( + [Parameter(Mandatory)] + [System.String]$Key, + + [Parameter()] + [System.String]$Value, + + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String]$Ensure = 'Present', + + [Parameter()] + [System.Boolean]$QueryXML + ) + + $return = @{ + 'Key' = $Key + 'Value' = $Value + 'Ensure' = $Ensure + 'QueryXML' = $QueryXML + } + + if ($Ensure -eq 'Absent') { + # if value and absent specified remove value as it can be confusing output + $return['Value'] = $null + } + + return $return +} + +function Set-TargetResource { + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [System.String]$Key, + + [Parameter()] + [System.String]$Value, + + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String]$Ensure = 'Present', + + [Parameter()] + [System.Boolean]$QueryXML + ) + + # validate value is given when ensure present + if ($Ensure -eq 'Present' -and -not $PSBoundParameters.ContainsKey('Value')) { + throw "Missing parameter 'Value' when ensuring config is present!" + } + + # query config to determine current setting + $ChocoConfig = @{ + 'Key' = $Key + 'QueryXML' = $QueryXML + } + $CurrentValue = Get-ChocoConfig @ChocoConfig + + # make it so + if ($Ensure -eq 'Absent' -and -not [String]::IsNullOrEmpty($CurrentValue)) { + Write-Verbose "Unsetting $Key..." + & choco config unset $Key + } + else { + Write-Verbose "Setting $Key to '$Value'..." + & choco config set --name $Key --value $Value + } + +} + +function Test-TargetResource { + [CmdletBinding()] + [OutputType([System.Boolean])] + param ( + [Parameter(Mandatory)] + [System.String]$Key, + + [Parameter()] + [System.String]$Value, + + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String]$Ensure = 'Present', + + [Parameter()] + [System.Boolean]$QueryXML + ) + + # validate value is given when ensure present + if ($Ensure -eq 'Present' -and -not $PSBoundParameters.ContainsKey('Value')) { + throw "Missing parameter 'Value' when ensuring config is present!" + } + + # query config to determine current setting + $ChocoConfig = @{ + 'Key' = $Key + 'QueryXML' = $QueryXML + } + $CurrentValue = Get-ChocoConfig @ChocoConfig + + # determine if in desired state + if ($Ensure -eq 'Absent') { + if ([String]::IsNullOrEmpty($CurrentValue)) { + Write-Verbose "$Key is in desired state" + return $true + } + else { + Write-Verbose "$key not in desired state: value should be unset but was '$CurrentValue'" + return $false + } + } + else { + if ($Value -eq $CurrentValue) { + Write-Verbose "$Key is in desired state" + return $true + } + else { + Write-Verbose "$key not in desired state: value should be '$Value' but was '$CurrentValue'" + return $false + } + } +} + +function Get-ChocoConfig { + [CmdletBinding()] + [OutputType([System.String])] + param ( + [Parameter(Mandatory)] + [System.String]$Key, + + [Parameter()] + [Switch]$QueryXML + ) + + if ($QueryXML.IsPresent) { + Write-Verbose "Querying chco config via chocolatey.config xml..." + $ConfigXmlPath = Join-Path $env:ChocolateyInstall -ChildPath "config\chocolatey.config" + try { + $ConfigXml = [xml](Get-Content -Path $ConfigXmlPath) + # xpath is case sensitive so we must convert all to lower case + $XPath = "/chocolatey/config/add[translate(@key, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz') = '$($Key.ToLower())']" + $ConfigValue = $ConfigXml.SelectSingleNode($XPath).value + } + catch { + $PSCmdlet.ThrowTerminatingError($_) + } + } + else { + Write-Verbose "Querying choco config via CLI" + $ConfigValue = & choco config get $Key -r + } + + return $ConfigValue +} diff --git a/DSCResources/cChocoConfig/cChocoConfig.schema.mof b/DSCResources/cChocoConfig/cChocoConfig.schema.mof new file mode 100644 index 0000000..53969ab --- /dev/null +++ b/DSCResources/cChocoConfig/cChocoConfig.schema.mof @@ -0,0 +1,8 @@ +[ClassVersion("1.0.0.0"), FriendlyName("cChocoConfig")] +class cChocoConfig : OMI_BaseResource +{ + [Key] String Key; + [Write] String Value; + [Write,ValueMap{"Present", "Absent"},Values{"Present", "Absent"}] String Ensure; + [Write] Boolean QueryXML; +}; diff --git a/Examples/cChocoConfigExample.ps1 b/Examples/cChocoConfigExample.ps1 new file mode 100644 index 0000000..2ae38d3 --- /dev/null +++ b/Examples/cChocoConfigExample.ps1 @@ -0,0 +1,39 @@ +# Copyright (c) 2017 Chocolatey Software, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +configuration ChocoConfig { + + Import-DscResource -ModuleName cChoco + + Node 'localhost' { + + cChocoConfig webRequestTimeoutSeconds { + Key = "webRequestTimeoutSeconds" + Ensure = 'Present' + Value = 30 + } + + cChocoConfig proxy { + Key = "proxy" + Ensure = 'Absent' + } + } + +} + + +$config = ChocoConfig + +Start-DscConfiguration -Path $config.psparentpath -Wait -Verbose -Force diff --git a/Tests/cChocoConfig_Tests.ps1 b/Tests/cChocoConfig_Tests.ps1 new file mode 100644 index 0000000..8d69189 --- /dev/null +++ b/Tests/cChocoConfig_Tests.ps1 @@ -0,0 +1,163 @@ +# Copyright (c) 2017 Chocolatey Software, Inc. +# Copyright (c) 2013 - 2017 Lawrence Gripper & original authors/contributors from https://github.com/chocolatey/cChoco +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +#----------------------------------------# +# Pester tests for cChocoPackageInstall # +#----------------------------------------# +$ResourceName = ((Split-Path -Path $MyInvocation.MyCommand.Path -Leaf) -split '_')[0] +$ResourceFile = (Get-DscResource -Name $ResourceName).Path + +$TestsPath = (split-path -path $MyInvocation.MyCommand.Path -Parent) +$ResourceFile = Get-ChildItem -Recurse $TestsPath\.. -File | Where-Object {$_.name -eq "$ResourceName.psm1"} + +Import-Module -Name $ResourceFile.FullName + +Describe -Name "Testing $ResourceName loaded from $ResourceFile" -Fixture { + $MyProxyUrl = 'http://foo' + Context -Name "Proxy config is set to '$MyProxyUrl'" -Fixture { + Mock -CommandName 'Get-ChocoConfig' -ModuleName 'cChocoConfig' -MockWith { + return 'http://foo' + } + + # returns true if config matches supplied value + $Scenario1 = @{ + Key = 'Proxy' + Ensure = 'Present' + Value = $MyProxyUrl + } + It -name "Test-TargetResource -ensure 'Present' -Value '$MyProxyUrl' should return True" -test { + Test-TargetResource @Scenario1 | Should Be $true + } + + # returns true if config matches supplied value (via xml) + $Scenario2 = @{ + Key = 'Proxy' + Ensure = 'Present' + Value = $MyProxyUrl + QueryXML = $true + } + It -name "Test-TargetResource -ensure 'Present' -Value '$MyProxyUrl' -QueryXML `$true should return True" -test { + Test-TargetResource @Scenario2 | Should Be $true + } + + # returns false if config does not match supplied value + $Scenario3 = @{ + Key = 'Proxy' + Ensure = 'Present' + Value = 'http://some/other/url' + } + It -name "Test-TargetResource -ensure 'Present' -Value 'http://some/other/url' should return False" -test { + Test-TargetResource @Scenario3 | Should Be $false + } + + # returns false if config does not match supplied value (via xml) + $Scenario4 = @{ + Key = 'Proxy' + Ensure = 'Present' + Value = 'http://some/other/url' + QueryXML = $true + } + It -name "Test-TargetResource -ensure 'Present' -Value 'http://some/other/url' -QueryXML `$true should return False" -test { + Test-TargetResource @Scenario4 | Should Be $false + } + + # returns false if ensure absent + $Scenario5 = @{ + Key = 'Proxy' + Ensure = 'Absent' + } + It -name "Test-TargetResource -ensure 'Absent' should return False" -test { + Test-TargetResource @Scenario5 | Should Be $false + } + + # returns false if ensure absent (via xml) + $Scenario6 = @{ + Key = 'Proxy' + Ensure = 'Absent' + QueryXML = $true + } + It -name "Test-TargetResource -ensure 'Absent' -QueryXML `$true should return False" -test { + Test-TargetResource @Scenario6 | Should Be $false + } + + # throws when ensure present but no value given + $Scenario7 = @{ + Key = 'Proxy' + Ensure = 'Present' + } + It -name "Test-TargetResource -ensure 'Present' should throw" -test { + {Test-TargetResource @Scenario7} | Should -Throw "Missing parameter 'Value' when ensuring config is present!" + } + + # throws when ensure present but no value given (via xml) + $Scenario8 = @{ + Key = 'Proxy' + Ensure = 'Present' + QueryXML = $true + } + It -name "Test-TargetResource -ensure 'Present' -QueryXML `$true should throw" -test { + {Test-TargetResource @Scenario8} | Should -Throw "Missing parameter 'Value' when ensuring config is present!" + } + } + + Context -Name "Proxy config is not set" -Fixture { + Mock -CommandName 'Get-ChocoConfig' -ModuleName 'cChocoConfig' -MockWith { + return '' + } + + # returns false if value supplied + $Scenario1 = @{ + Key = 'Proxy' + Ensure = 'Present' + Value = $MyProxyUrl + } + It -name "Test-TargetResource -ensure 'Present' -Value '$MyProxyUrl' should return False" -test { + Test-TargetResource @Scenario1 | Should Be $false + } + + # returns true if value supplied (via xml) + $Scenario2 = @{ + Key = 'Proxy' + Ensure = 'Present' + Value = $MyProxyUrl + QueryXML = $true + } + It -name "Test-TargetResource -ensure 'Present' -Value '$MyProxyUrl' -QueryXML `$true should return False" -test { + Test-TargetResource @Scenario2 | Should Be $false + } + + # returns true if ensure absent + $Scenario5 = @{ + Key = 'Proxy' + Ensure = 'Absent' + } + It -name "Test-TargetResource -ensure 'Absent' should return True" -test { + Test-TargetResource @Scenario5 | Should Be $true + } + + # returns true if ensure absent (via xml) + $Scenario6 = @{ + Key = 'Proxy' + Ensure = 'Absent' + QueryXML = $true + } + It -name "Test-TargetResource -ensure 'Absent' -QueryXML `$true should return True" -test { + Test-TargetResource @Scenario6 | Should Be $true + } + } +} + +#Clean-up +Remove-Module cChocoConfig