diff --git a/DSCResources/MSFT_xWebConfigProperty/MSFT_xWebConfigProperty.psm1 b/DSCResources/MSFT_xWebConfigProperty/MSFT_xWebConfigProperty.psm1 index 5ec082c9a..38636d806 100644 --- a/DSCResources/MSFT_xWebConfigProperty/MSFT_xWebConfigProperty.psm1 +++ b/DSCResources/MSFT_xWebConfigProperty/MSFT_xWebConfigProperty.psm1 @@ -21,6 +21,9 @@ data LocalizedData .PARAMETER Filter Required. Filter used to locate property to update. +.PARAMETER Location + Required. Location tag to use for property. + .PARAMETER PropertyName Required. Name of the property to update. #> @@ -40,6 +43,11 @@ function Get-TargetResource [string] $Filter, + [Parameter(Mandatory = $true)] + [AllowEmptyString()] + [string] + $Location, + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] @@ -47,34 +55,33 @@ function Get-TargetResource ) # Retrieve the value of the existing property if present. Write-Verbose ` - -Message ($LocalizedData.VerboseTargetCheckingTarget -f $PropertyName, $Filter, $WebsitePath ) + -Message ($LocalizedData.VerboseTargetCheckingTarget -f $PropertyName, $Filter, $WebsitePath) - $existingValue = Get-ItemValue ` - -WebsitePath $WebsitePath ` + $existingValue = Get-ItemValue -WebsitePath $WebsitePath ` -Filter $Filter ` + -Location $Location ` -PropertyName $PropertyName $result = @{ WebsitePath = $WebsitePath Filter = $Filter + Location = $Location PropertyName = $PropertyName Ensure = 'Present' Value = $existingValue } - if (-not($existingValue)) + if ( -not($existingValue) ) { # Property was not found. - Write-Verbose ` - -Message ($LocalizedData.VerboseTargetPropertyNotFound -f $PropertyName ) + Write-Verbose -Message ($LocalizedData.VerboseTargetPropertyNotFound -f $PropertyName) $result.Ensure = 'Absent' } else { # Property was found. - Write-Verbose ` - -Message ($LocalizedData.VerboseTargetPropertyFound -f $PropertyName ) + Write-Verbose -Message ($LocalizedData.VerboseTargetPropertyFound -f $PropertyName) } return $result @@ -90,6 +97,9 @@ function Get-TargetResource .PARAMETER Filter Required. Filter used to locate property to update. +.PARAMETER Location + Required. Location tag to use for property. + .PARAMETER PropertyName Required. Name of the property to update. @@ -114,6 +124,11 @@ function Set-TargetResource [string] $Filter, + [Parameter(Mandatory = $true)] + [AllowEmptyString()] + [string] + $Location, + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] @@ -131,10 +146,12 @@ function Set-TargetResource if ($Ensure -eq 'Present') { # Property needs to be updated. - Write-Verbose ` - -Message ($LocalizedData.VerboseSetTargetEditItem -f $PropertyName ) + Write-Verbose -Message ($LocalizedData.VerboseSetTargetEditItem -f $PropertyName) - $propertyType = Get-ItemPropertyType -WebsitePath $WebsitePath -Filter $Filter -PropertyName $PropertyName + $propertyType = Get-ItemPropertyType -WebsitePath $WebsitePath ` + -Filter $Filter ` + -Location $Location ` + -PropertyName $PropertyName if ($propertyType -match 'Int32|Int64') { @@ -145,23 +162,22 @@ function Set-TargetResource $setValue = $Value } - Set-WebConfigurationProperty ` + Set-WebConfigurationProperty -PSPath $WebsitePath ` -Filter $Filter ` - -PSPath $WebsitePath ` + -Location $Location ` -Name $PropertyName ` -Value $setValue ` - -WarningAction Stop + -WarningAction "Stop" } else { # Property needs to be removed. - Write-Verbose ` - -Message ($LocalizedData.VerboseSetTargetRemoveItem -f $PropertyName ) + Write-Verbose -Message ($LocalizedData.VerboseSetTargetRemoveItem -f $PropertyName) - Clear-WebConfiguration ` - -Filter "$($Filter)/@$($PropertyName)" ` - -PSPath $WebsitePath ` - -WarningAction Stop + Clear-WebConfiguration -PSPath $WebsitePath ` + -Filter "$($Filter)/@$($PropertyName)" ` + -Location $Location ` + -WarningAction "Stop" } } @@ -175,6 +191,9 @@ function Set-TargetResource .PARAMETER Filter Required. Filter used to locate property to update. +.PARAMETER Location + Required. Location tag to use for property. + .PARAMETER PropertyName Required. Name of the property to update. @@ -200,6 +219,11 @@ function Test-TargetResource [string] $Filter, + [Parameter(Mandatory = $true)] + [AllowEmptyString()] + [string] + $Location, + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] @@ -215,21 +239,19 @@ function Test-TargetResource $Ensure = 'Present' ) # Retrieve the value of the existing property if present. - Write-Verbose ` - -Message ($LocalizedData.VerboseTargetCheckingTarget -f $PropertyName, $Filter, $WebsitePath ) + Write-Verbose -Message ($LocalizedData.VerboseTargetCheckingTarget -f $PropertyName, $Filter, $WebsitePath) - $targetResource = Get-TargetResource ` - -WebsitePath $WebsitePath ` + $targetResource = Get-TargetResource -WebsitePath $WebsitePath ` -Filter $Filter ` - -PropertyName $PropertyName + -PropertyName $PropertyName ` + -Location $Location if ($Ensure -eq 'Present') { if ( ($null -eq $targetResource.Value) -or ($targetResource.Value.ToString() -ne $Value) ) { # Property was not found or didn't have expected value. - Write-Verbose ` - -Message ($LocalizedData.VerboseTargetPropertyNotFound -f $PropertyName ) + Write-Verbose -Message ($LocalizedData.VerboseTargetPropertyNotFound -f $PropertyName) return $false } @@ -239,15 +261,13 @@ function Test-TargetResource if ( ($null -ne $targetResource.Value) -and ($targetResource.Value.ToString().Length -ne 0 ) ) { # Property was found. - Write-Verbose ` - -Message ($LocalizedData.VerboseTargetPropertyWasFound -f $PropertyName ) + Write-Verbose -Message ($LocalizedData.VerboseTargetPropertyWasFound -f $PropertyName) return $false } } - Write-Verbose ` - -Message ($LocalizedData.VerboseTargetPropertyWasFound -f $PropertyName) + Write-Verbose -Message ($LocalizedData.VerboseTargetPropertyWasFound -f $PropertyName) return $true } @@ -264,6 +284,9 @@ function Test-TargetResource .PARAMETER Filter Required. Filter used to locate property to retrieve. +.PARAMETER Location + Optional. Location tag to use for property. + .PARAMETER PropertyName Required. Name of the property to retrieve. #> @@ -283,16 +306,21 @@ function Get-ItemValue [string] $Filter, + [Parameter(Mandatory = $false)] + [string] + $Location, + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $PropertyName ) + # Retrieve the value of the specified property if present. - $value = Get-WebConfigurationProperty ` - -PSPath $WebsitePath ` + $value = Get-WebConfigurationProperty -PSPath $WebsitePath ` -Filter $Filter ` - -Name $PropertyName + -Name $PropertyName ` + -Location $Location # Return the value of the property if located. if ($value -is [Microsoft.IIs.PowerShell.Framework.ConfigurationAttribute]) @@ -307,13 +335,16 @@ function Get-ItemValue Gets the current data type of the property. .PARAMETER WebsitePath - Path to website location (IIS or WebAdministration format). + Required. Path to website location (IIS or WebAdministration format). .PARAMETER Filter - Filter used to locate property to retrieve. + Required. Filter used to locate property to retrieve. + +.PARAMETER Location + Optional. Location tag to use for property. .PARAMETER PropertyName - Name of the property to retrieve. + Required. Name of the property to retrieve. #> function Get-ItemPropertyType { @@ -331,15 +362,22 @@ function Get-ItemPropertyType [string] $Filter, + [Parameter(Mandatory = $false)] + [string] + $Location, + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $PropertyName ) - $webConfiguration = Get-WebConfiguration -Filter $Filter -PsPath $WebsitePath - $property = $webConfiguration.Schema.AttributeSchemas | Where-Object -FilterScript {$_.Name -eq $propertyName} + $webConfiguration = Get-WebConfiguration -Filter $Filter ` + -PsPath $WebsitePath ` + -Location $Location + + $property = $webConfiguration.Schema.AttributeSchemas | Where-Object -FilterScript { $_.Name -eq $propertyName } return $property.ClrType.Name } diff --git a/DSCResources/MSFT_xWebConfigProperty/MSFT_xWebConfigProperty.schema.mof b/DSCResources/MSFT_xWebConfigProperty/MSFT_xWebConfigProperty.schema.mof index d2a7576a9..1ed4cfa88 100644 --- a/DSCResources/MSFT_xWebConfigProperty/MSFT_xWebConfigProperty.schema.mof +++ b/DSCResources/MSFT_xWebConfigProperty/MSFT_xWebConfigProperty.schema.mof @@ -4,6 +4,7 @@ class MSFT_xWebConfigProperty : OMI_BaseResource [Key, Description("Path to website location (IIS or WebAdministration format).")] String WebsitePath; [Key, Description("Filter used to locate property to update.")] String Filter; [Key, Description("Name of the property to update.")] String PropertyName; + [Key, Description("Location is used to update locked sections in the root config.")] String Location; [Write, Description("Indicates if the property and value should be present or absent. Defaults to Present."), ValueMap{"Present","Absent"}, Values{"Present","Absent"}] String Ensure; [Write, Description("Value of the property to update.")] String Value; }; diff --git a/Examples/Sample_xWebConfigProperty_Add.ps1 b/Examples/Sample_xWebConfigProperty_Add.ps1 index e8a350583..69717d67e 100644 --- a/Examples/Sample_xWebConfigProperty_Add.ps1 +++ b/Examples/Sample_xWebConfigProperty_Add.ps1 @@ -25,6 +25,7 @@ Configuration Sample_xWebConfigProperty_Add { WebsitePath = 'IIS:\Sites\Default Web Site' Filter = 'system.webServer/directoryBrowse' + Location = '' PropertyName = 'enabled' Value = 'false' Ensure = 'Present' diff --git a/Examples/Sample_xWebConfigProperty_Remove.ps1 b/Examples/Sample_xWebConfigProperty_Remove.ps1 index 3f78a9ee7..f5a4fabf6 100644 --- a/Examples/Sample_xWebConfigProperty_Remove.ps1 +++ b/Examples/Sample_xWebConfigProperty_Remove.ps1 @@ -25,6 +25,7 @@ Configuration Sample_xWebConfigProperty_Remove { WebsitePath = 'IIS:\Sites\Default Web Site' Filter = 'system.webServer/directoryBrowse' + Location = '' PropertyName = 'enabled' Ensure = 'Absent' } diff --git a/README.md b/README.md index 3aaf20e3e..b3322869b 100644 --- a/README.md +++ b/README.md @@ -261,6 +261,7 @@ Ensures the value of an identified property in the web.config file. * **WebsitePath**: Path to website location (IIS or WebAdministration format). * **Filter**: Filter used to locate property to update. * **PropertyName**: Name of the property to update. +* **Location**: Name of the location to update. * **Value**: Value of the property to update. * **Ensure**: Indicates if the property and value should be present or absent. Defaults to 'Present'. { *Present* | Absent } @@ -317,6 +318,7 @@ This resource manages the IIS configuration section locking (overrideMode) to co ### Unreleased +* Added new parameter 'Location' to **xWebConfigProperty** extending functionality to allow writing of locked sections in ApplicationHost.Config * xWebSite: Full path is used to get list of default documents ### 2.4.0.0 @@ -325,6 +327,8 @@ This resource manages the IIS configuration section locking (overrideMode) to co ### 2.3.0.0 +* Added new reosurce xWebConfigProperty extening functionality provided by xWebConfigProperty to allow writing of locked sections in ApplicationHost.Config +* Added new reosurce xWebConfigProperty extening functionality provided by xWebConfigProperty to allow writing of locked sections in ApplicationHost.Config * Update appveyor.yml to use the default template. * Added default template file .gitattributes, and added default settings for Visual Studio Code. diff --git a/Tests/Integration/MSFT_xWebConfigProperty.Integration.Tests.ps1 b/Tests/Integration/MSFT_xWebConfigProperty.Integration.Tests.ps1 index 1cc1e28c3..f1d115da4 100644 --- a/Tests/Integration/MSFT_xWebConfigProperty.Integration.Tests.ps1 +++ b/Tests/Integration/MSFT_xWebConfigProperty.Integration.Tests.ps1 @@ -47,6 +47,7 @@ try NodeName = 'localhost' WebsitePath = "IIS:\Sites\$($websiteName)" Filter = 'system.webServer/directoryBrowse' + Location = '' PropertyName = 'enabled' AddValue = $true UpdateValue = $false diff --git a/Tests/Integration/MSFT_xWebConfigProperty.config.ps1 b/Tests/Integration/MSFT_xWebConfigProperty.config.ps1 index d8b5e2500..8058bd844 100644 --- a/Tests/Integration/MSFT_xWebConfigProperty.config.ps1 +++ b/Tests/Integration/MSFT_xWebConfigProperty.config.ps1 @@ -8,6 +8,7 @@ Configuration MSFT_xWebConfigProperty_Add { WebsitePath = $Node.WebsitePath Filter = $Node.Filter + Location = $Node.Location PropertyName = $Node.PropertyName Value = $Node.AddValue Ensure = 'Present' @@ -25,6 +26,7 @@ Configuration MSFT_xWebConfigProperty_Update { WebsitePath = $Node.WebsitePath Filter = $Node.Filter + Location = $Node.Location PropertyName = $Node.PropertyName Value = $Node.UpdateValue Ensure = 'Present' @@ -42,6 +44,7 @@ Configuration MSFT_xWebConfigProperty_Integer { WebsitePath = $Node.WebsitePath Filter = $Node.IntegerFilter + Location = $Node.Location PropertyName = $Node.IntergerPropertyName Value = $Node.IntegerValue Ensure = 'Present' @@ -59,6 +62,7 @@ Configuration MSFT_xWebConfigProperty_Remove { WebsitePath = $Node.WebsitePath Filter = $Node.Filter + Location = $Node.Location PropertyName = $Node.PropertyName Ensure = 'Absent' } diff --git a/Tests/Unit/MSFT_xWebConfigProperty.tests.ps1 b/Tests/Unit/MSFT_xWebConfigProperty.tests.ps1 index 324968285..e870c88c9 100644 --- a/Tests/Unit/MSFT_xWebConfigProperty.tests.ps1 +++ b/Tests/Unit/MSFT_xWebConfigProperty.tests.ps1 @@ -33,27 +33,44 @@ try $script:DSCModuleName = 'xWebAdministration' $script:DSCResourceName = 'MSFT_xWebConfigProperty' - $script:presentParameters = @{ + $script:presentParametersEmptyLocation = @{ WebsitePath = 'MACHINE/WEBROOT/APPHOST' Filter = 'system.webServer/advancedLogging/server' + Location = '' PropertyName = 'enabled' Value = 'true' Ensure = 'Present' } - - $script:absentParameters = @{ + $script:presentParametersPresentLocation = @{ + WebsitePath = 'MACHINE/WEBROOT/APPHOST' + Filter = 'system.webServer/asp/session' + Location = 'Default Web Site' + PropertyName = 'keepSessionIdSecure' + Value = 'true' + Ensure = 'Present' + } + $script:absentParametersEmptyLocation = @{ WebsitePath = 'MACHINE/WEBROOT/APPHOST' Filter = 'system.webServer/advancedLogging/server' + Location = '' PropertyName = 'enabled' Ensure = 'Absent' } + $script:absentParametersPresentLocation = @{ + WebsitePath = 'MACHINE/WEBROOT/APPHOST' + Filter = 'system.webServer/asp/session' + Location = 'Default Web Site' + PropertyName = 'keepSessionIdSecure' + Ensure = 'Absent' + } #region Function Get-TargetResource Describe "$($script:DSCResourceName)\Get-TargetResource" { - Context 'Value is absent' { + Context 'Value is absent with empty location' { $parameters = @{ WebsitePath = 'MACHINE/WEBROOT/APPHOST' Filter = 'system.webServer/advancedLogging/server' + Location = '' PropertyName = 'enabled' } @@ -74,10 +91,36 @@ try } } - Context 'Value is present' { + Context 'Value is absent with present location' { + $parameters = @{ + WebsitePath = 'MACHINE/WEBROOT/APPHOST' + Filter = 'system.webServer/security/access' + Location = 'Default Web Site' + PropertyName = 'keepSessionIdSecure' + } + + Mock -CommandName Get-ItemValue -ModuleName $script:DSCResourceName -MockWith { + return $null + } + + $result = Get-TargetResource @parameters + + It 'Should return the correct values' { + $result.Ensure | Should -Be 'Absent' + $result.PropertyName | Should -Be 'keepSessionIdSecure' + $result.Value | Should -Be $null + } + + It 'Should have called Get-ItemValue the correct amount of times' { + Assert-MockCalled -CommandName Get-ItemValue -Times 1 -Exactly + } + } + + Context 'Value is present with empty location' { $parameters = @{ WebsitePath = 'MACHINE/WEBROOT/APPHOST' Filter = 'system.webServer/advancedLogging/server' + Location = '' PropertyName = 'enabled' } @@ -97,6 +140,31 @@ try Assert-MockCalled -CommandName Get-ItemValue -Times 1 -Exactly } } + + Context 'Value is present with present location' { + $parameters = @{ + WebsitePath = 'MACHINE/WEBROOT/APPHOST' + Filter = 'system.webServer/asp/session' + Location = 'Default Web Site' + PropertyName = 'keepSessionIdSecure' + } + + Mock -CommandName Get-ItemValue -ModuleName $script:DSCResourceName -MockWith { + return 'true' + } + + $result = Get-TargetResource @parameters + + It 'Should return the correct values' { + $result.Ensure | Should -Be 'Present' + $result.PropertyName | Should -Be 'keepSessionIdSecure' + $result.Value | Should -Be 'true' + } + + It 'Should have called Get-ItemValue the correct amount of times' { + Assert-MockCalled -CommandName Get-ItemValue -Times 1 -Exactly + } + } } #endregion Function Get-TargetResource @@ -107,7 +175,19 @@ try return $null } - $result = Test-TargetResource @script:presentParameters + $result = Test-TargetResource @script:presentParametersEmptyLocation + + It 'Should return false' { + $result | Should -Be $false + } + } + + Context 'Ensure is present but value is null at location' { + Mock -CommandName Get-ItemValue -ModuleName $script:DSCResourceName -MockWith { + return $null + } + + $result = Test-TargetResource @script:presentParametersPresentLocation It 'Should return false' { $result | Should -Be $false @@ -119,7 +199,19 @@ try return [System.String]::Empty } - $result = Test-TargetResource @script:presentParameters + $result = Test-TargetResource @script:presentParametersEmptyLocation + + It 'Should return false' { + $result | Should -Be $false + } + } + + Context 'Ensure is present but value is an empty string at location' { + Mock -CommandName Get-ItemValue -ModuleName $script:DSCResourceName -MockWith { + return [System.String]::Empty + } + + $result = Test-TargetResource @script:presentParametersPresentLocation It 'Should return false' { $result | Should -Be $false @@ -131,7 +223,19 @@ try return 'false' } - $result = Test-TargetResource @script:presentParameters + $result = Test-TargetResource @script:presentParametersEmptyLocation + + It 'Should return false' { + $result | Should -Be $false + } + } + + Context 'Ensure is present but value is wrong at location' { + Mock -CommandName Get-ItemValue -ModuleName $script:DSCResourceName -MockWith { + return 'false' + } + + $result = Test-TargetResource @script:presentParametersPresentLocation It 'Should return false' { $result | Should -Be $false @@ -143,7 +247,19 @@ try return 'true' } - $result = Test-TargetResource @script:presentParameters + $result = Test-TargetResource @script:presentParametersEmptyLocation + + It 'Should return true' { + $result | Should -Be $true + } + } + + Context 'Ensure is present and the value is the same at location' { + Mock -CommandName Get-ItemValue -ModuleName $script:DSCResourceName -MockWith { + return 'true' + } + + $result = Test-TargetResource @script:presentParametersPresentLocation It 'Should return true' { $result | Should -Be $true @@ -155,7 +271,19 @@ try return 'true' } - $result = Test-TargetResource @script:absentParameters + $result = Test-TargetResource @script:absentParametersEmptyLocation + + It 'Should return false' { + $result | Should -Be $false + } + } + + Context 'Ensure is absent but value is not null at location' { + Mock -CommandName Get-ItemValue -ModuleName $script:DSCResourceName -MockWith { + return 'true' + } + + $result = Test-TargetResource @script:absentParametersPresentLocation It 'Should return false' { $result | Should -Be $false @@ -167,7 +295,19 @@ try return $null } - $result = Test-TargetResource @script:absentParameters + $result = Test-TargetResource @script:absentParametersEmptyLocation + + It 'Should return true' { + $result | Should -Be $true + } + } + + Context 'Ensure is absent and value is null at location' { + Mock -CommandName Get-ItemValue -ModuleName $script:DSCResourceName -MockWith { + return $null + } + + $result = Test-TargetResource @script:absentParametersPresentLocation It 'Should return true' { $result | Should -Be $true @@ -183,7 +323,21 @@ try Mock -CommandName Convert-PropertyValue Mock -CommandName Set-WebConfigurationProperty - Set-TargetResource @script:presentParameters + Set-TargetResource @script:presentParametersEmptyLocation + + It 'Should call the right Mocks' { + Assert-MockCalled -CommandName Get-ItemPropertyType -Times 1 -Exactly + Assert-MockCalled -CommandName Convert-PropertyValue -Times 0 -Exactly + Assert-MockCalled -CommandName Set-WebConfigurationProperty -Times 1 -Exactly + } + } + + Context 'Ensure is present - String Value at location' { + Mock -CommandName Get-ItemPropertyType -MockWith { return 'String' } + Mock -CommandName Convert-PropertyValue + Mock -CommandName Set-WebConfigurationProperty + + Set-TargetResource @script:presentParametersPresentLocation It 'Should call the right Mocks' { Assert-MockCalled -CommandName Get-ItemPropertyType -Times 1 -Exactly @@ -197,7 +351,21 @@ try Mock -CommandName Convert-PropertyValue -MockWith { return '32' } Mock -CommandName Set-WebConfigurationProperty - Set-TargetResource @script:presentParameters + Set-TargetResource @script:presentParametersEmptyLocation + + It 'Should call the right Mocks' { + Assert-MockCalled -CommandName Get-ItemPropertyType -Times 1 -Exactly + Assert-MockCalled -CommandName Convert-PropertyValue -Times 1 -Exactly + Assert-MockCalled -CommandName Set-WebConfigurationProperty -Times 1 -Exactly + } + } + + Context 'Ensure is present - Integer Value at location' { + Mock -CommandName Get-ItemPropertyType -MockWith { return 'Int32' } + Mock -CommandName Convert-PropertyValue -MockWith { return '32' } + Mock -CommandName Set-WebConfigurationProperty + + Set-TargetResource @script:presentParametersPresentLocation It 'Should call the right Mocks' { Assert-MockCalled -CommandName Get-ItemPropertyType -Times 1 -Exactly @@ -209,7 +377,17 @@ try Context 'Ensure is absent' { Mock -CommandName Clear-WebConfiguration - Set-TargetResource @script:absentParameters + Set-TargetResource @script:absentParametersEmptyLocation + + It 'Should call the right Mocks' { + Assert-MockCalled -CommandName Clear-WebConfiguration -Times 1 -Exactly + } + } + + Context 'Ensure is absent at location' { + Mock -CommandName Clear-WebConfiguration + + Set-TargetResource @script:absentParametersPresentLocation It 'Should call the right Mocks' { Assert-MockCalled -CommandName Clear-WebConfiguration -Times 1 -Exactly