From 83aaeb34d85aabbe319795df7108ef3f2c38d729 Mon Sep 17 00:00:00 2001 From: Thomas Meckel Date: Mon, 4 Mar 2019 15:29:44 +0100 Subject: [PATCH 01/32] First implementation that passed unit tests of fix for issue 463 --- .../MSFT_xDSCWebService.psm1 | 66 ++++-- .../MSFT_xDSCWebService.schema.mof | 1 + .../MSFT_xDSCWebService/PSWSIISEndpoint.psm1 | 196 ++++++++++++++---- Tests/Unit/MSFT_xDSCWebService.Tests.ps1 | 14 +- 4 files changed, 209 insertions(+), 68 deletions(-) diff --git a/DSCResources/MSFT_xDSCWebService/MSFT_xDSCWebService.psm1 b/DSCResources/MSFT_xDSCWebService/MSFT_xDSCWebService.psm1 index 96ddfdd3b..3b57470ab 100644 --- a/DSCResources/MSFT_xDSCWebService/MSFT_xDSCWebService.psm1 +++ b/DSCResources/MSFT_xDSCWebService/MSFT_xDSCWebService.psm1 @@ -33,6 +33,11 @@ function Get-TargetResource [System.String] $EndpointName, + [Parameter()] + [ValidateNotNullOrEmpty()] + [System.String] + $ApplicationPoolName = $DefaultAppPoolName, + # Thumbprint of the Certificate in CERT:\LocalMachine\MY\ for Pull Server [Parameter(ParameterSetName = 'CertificateThumbPrint')] [ValidateNotNullOrEmpty()] @@ -143,6 +148,7 @@ function Get-TargetResource } $ConfigureFirewall = Test-PullServerFirewallConfiguration -Port $iisPort + $ApplicationPoolName = $webSite.applicationPool } else { @@ -151,21 +157,22 @@ function Get-TargetResource } $output = @{ - EndpointName = $EndpointName - Port = $iisPort - PhysicalPath = $website.physicalPath - State = $webSite.state - DatabasePath = $databasePath - ModulePath = $modulePath - ConfigurationPath = $configurationPath - DSCServerUrl = $serverUrl - Ensure = $Ensure - RegistrationKeyPath = $registrationKeyPath - AcceptSelfSignedCertificates = $acceptSelfSignedCertificates - UseSecurityBestPractices = $UseSecurityBestPractices - DisableSecurityBestPractices = $DisableSecurityBestPractices - Enable32BitAppOnWin64 = $Enable32BitAppOnWin64 - ConfigureFirewall = $ConfigureFirewall + EndpointName = $EndpointName + ApplicationPoolName = $ApplicationPoolName + Port = $iisPort + PhysicalPath = $website.physicalPath + State = $webSite.state + DatabasePath = $databasePath + ModulePath = $modulePath + ConfigurationPath = $configurationPath + DSCServerUrl = $serverUrl + Ensure = $Ensure + RegistrationKeyPath = $registrationKeyPath + AcceptSelfSignedCertificates = $acceptSelfSignedCertificates + UseSecurityBestPractices = $UseSecurityBestPractices + DisableSecurityBestPractices = $DisableSecurityBestPractices + Enable32BitAppOnWin64 = $Enable32BitAppOnWin64 + ConfigureFirewall = $ConfigureFirewall } if ($CertificateThumbPrint -eq 'AllowUnencryptedTraffic') @@ -210,6 +217,11 @@ function Set-TargetResource [System.String] $EndpointName, + [Parameter()] + [ValidateNotNullOrEmpty()] + [System.String] + $ApplicationPoolName = $DefaultAppPoolName, + # Port number of the DSC Pull Server IIS Endpoint [Parameter()] [ValidateRange(1, 65535)] @@ -396,6 +408,7 @@ function Set-TargetResource -path $PhysicalPath ` -cfgfile $webConfigFileName ` -port $Port ` + -appPool $ApplicationPoolName ` -applicationPoolIdentityType LocalSystem ` -app $EndpointName ` -svc $svcFileName ` @@ -510,6 +523,11 @@ function Test-TargetResource [System.String] $EndpointName, + [Parameter()] + [ValidateNotNullOrEmpty()] + [System.String] + $ApplicationPoolName = $DefaultAppPoolName, + # Port number of the DSC Pull Server IIS Endpoint [Parameter()] [System.UInt32] @@ -620,20 +638,20 @@ function Test-TargetResource :WebSiteTests Do { - Write-Verbose -Message "Check Ensure" - if(($Ensure -eq "Present" -and $null -eq $website)) + Write-Verbose -Message 'Check Ensure' + if(($Ensure -eq 'Present' -and $null -eq $website)) { $desiredConfigurationMatch = $false Write-Verbose -Message "The Website $EndpointName is not present" break } - if(($Ensure -eq "Absent" -and $null -ne $website)) + if(($Ensure -eq 'Absent' -and $null -ne $website)) { $desiredConfigurationMatch = $false Write-Verbose -Message "The Website $EndpointName is present but should not be" break } - if(($Ensure -eq "Absent" -and $null -eq $website)) + if(($Ensure -eq 'Absent' -and $null -eq $website)) { $desiredConfigurationMatch = $true Write-Verbose -Message "The Website $EndpointName is not present as requested" @@ -641,7 +659,7 @@ function Test-TargetResource } # the other case is: Ensure and exist, we continue with more checks - Write-Verbose -Message "Check Port" + Write-Verbose -Message 'Check Port' $actualPort = $website.bindings.Collection[0].bindingInformation.Split(":")[1] if ($Port -ne $actualPort) { @@ -650,6 +668,14 @@ function Test-TargetResource break } + Write-Verbose -Message 'Check Application Pool' + if ($ApplicationPoolName -ne $website.applicationPool) + { + $desiredConfigurationMatch = $false + Write-Verbose -Message "Currently bound application pool [$($website.applicationPool)] does not match the desired state [$ApplicationPoolName]." + break + } + Write-Verbose -Message 'Check Binding' $actualCertificateHash = $website.bindings.Collection[0].certificateHash $websiteProtocol = $website.bindings.collection[0].Protocol diff --git a/DSCResources/MSFT_xDSCWebService/MSFT_xDSCWebService.schema.mof b/DSCResources/MSFT_xDSCWebService/MSFT_xDSCWebService.schema.mof index cc9c863f8..4907c292d 100644 --- a/DSCResources/MSFT_xDSCWebService/MSFT_xDSCWebService.schema.mof +++ b/DSCResources/MSFT_xDSCWebService/MSFT_xDSCWebService.schema.mof @@ -10,6 +10,7 @@ class MSFT_xDSCWebService : OMI_BaseResource [write] string PhysicalPath; [write,ValueMap{"Present", "Absent"},Values{"Present", "Absent"}] string Ensure; [write,ValueMap{"Started","Stopped"},Values{"Started", "Stopped"}] string State; + [write] string ApplicationPoolName; [write] string DatabasePath; [write] string ModulePath; [write] string ConfigurationPath; diff --git a/DSCResources/MSFT_xDSCWebService/PSWSIISEndpoint.psm1 b/DSCResources/MSFT_xDSCWebService/PSWSIISEndpoint.psm1 index 8520018cc..20edf72b7 100644 --- a/DSCResources/MSFT_xDSCWebService/PSWSIISEndpoint.psm1 +++ b/DSCResources/MSFT_xDSCWebService/PSWSIISEndpoint.psm1 @@ -1,6 +1,8 @@ # This module file contains a utility to perform PSWS IIS Endpoint setup # Module exports New-PSWSEndpoint function to perform the endpoint setup +New-Variable -Name DefaultAppPoolName -Value 'PSWS' -Option ReadOnly -Force -Scope Script + <# .SYNOPSIS Validate supplied configuration to setup the PSWS Endpoint Function @@ -12,6 +14,10 @@ function Initialize-Endpoint [CmdletBinding()] param ( + [Parameter()] + [System.String] + $appPool, + [Parameter()] [System.String] $site, @@ -101,14 +107,13 @@ function Initialize-Endpoint Test-IISInstall - $appPool = 'PSWS' + # First remove the site so that the binding count on the application pool is reduced + Write-Verbose -Message "Remove the site [$site] if it already exists" + Update-Site -siteName $site -siteAction Remove - Write-Verbose -Message 'Delete the App Pool if it exists' + Write-Verbose -Message "Delete App Pool [$appPool] if it exists" Remove-AppPool -apppool $appPool - Write-Verbose -Message 'Remove the site if it already exists' - Update-Site -siteName $site -siteAction Remove - # Check for existing binding, there should be no binding with the same port $allWebBindingsOnPort = Get-WebBinding | Where-Object -FilterScript { $_.BindingInformation -eq "*:$($port):" @@ -127,9 +132,25 @@ function Initialize-Endpoint } } - Copy-PSWSConfigurationToIISEndpointFolder -path $path -cfgfile $cfgfile -svc $svc -mof $mof -dispatch $dispatch -asax $asax -dependentBinaries $dependentBinaries -language $language -dependentMUIFiles $dependentMUIFiles -psFiles $psFiles - - New-IISWebSite -site $site -path $path -port $port -app $app -apppool $appPool -applicationPoolIdentityType $applicationPoolIdentityType -certificateThumbPrint $certificateThumbPrint -enable32BitAppOnWin64 $enable32BitAppOnWin64 + Copy-PSWSConfigurationToIISEndpointFolder -path $path ` + -cfgfile $cfgfile ` + -svc $svc ` + -mof $mof ` + -dispatch $dispatch ` + -asax $asax ` + -dependentBinaries $dependentBinaries ` + -language $language ` + -dependentMUIFiles $dependentMUIFiles ` + -psFiles $psFiles + + New-IISWebSite -site $site ` + -path $path + -port $port ` + -app $app ` + -apppool $appPool ` + -applicationPoolIdentityType $applicationPoolIdentityType ` + -certificateThumbPrint $certificateThumbPrint ` + -enable32BitAppOnWin64 $enable32BitAppOnWin64 } <# @@ -230,6 +251,40 @@ function Update-Site } } +<# + .SYNOPSIS + Returns the list of bound sites and applications for a given IIS Application pool + + .PARAMETER appPool + The application pool name +#> +function Get-AppPoolBinding +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $appPool + ) + + if (Test-Path -Path "IIS:\AppPools\$appPool") + { + $sites = Get-WebConfigurationProperty ` + -Filter "/system.applicationHost/sites/site/application[@applicationPool=`'$appPool`'and @path='/']/parent::*" ` + -PSPath 'machine/webroot/apphost' ` + -Name name + $apps = Get-WebConfigurationProperty ` + -Filter "/system.applicationHost/sites/site/application[@applicationPool=`'$appPool`'and @path!='/']" ` + -PSPath 'machine/webroot/apphost' ` + -Name path + $sites, $apps | ForEach-Object { + $_.Value + } + } +} + <# .SYNOPSIS Delete the given IIS Application Pool. This is required to cleanup any @@ -240,15 +295,31 @@ function Remove-AppPool [CmdletBinding()] param ( - [Parameter()] + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] [System.String] $appPool ) - # Without this tests we may get a breaking error here, despite SilentlyContinue - if (Test-Path -Path "IIS:\AppPools\$appPool") + if ($DefaultAppPoolName -eq $appPool) + { + # Without this tests we may get a breaking error here, despite SilentlyContinue + if (Test-Path -Path "IIS:\AppPools\$appPool") + { + $cntBindings = (Get-AppPoolBinding -apppool $appPool | Measure-Object).Count + if (0 -ge $cntBindings) + { + Remove-WebAppPool -Name $appPool -ErrorAction SilentlyContinue + } + else + { + Write-Verbose -Message "Unable to delete application pool [$appPool] because it's still bound to a site or application" + } + } + } + else { - Remove-WebAppPool -Name $appPool -ErrorAction SilentlyContinue + Write-Verbose -Message "ApplicationPool name is different from built in name [$DefaultAppPoolName]. Unable to delete." } } @@ -406,33 +477,50 @@ function New-IISWebSite $siteID = New-SiteID - Write-Verbose -Message 'Adding App Pool' - $null = New-WebAppPool -Name $appPool - - Write-Verbose -Message 'Set App Pool Properties' - $appPoolIdentity = 4 - if ($applicationPoolIdentityType) + if (Test-Path IIS:\AppPools\$appPool) { - # LocalSystem = 0, LocalService = 1, NetworkService = 2, SpecificUser = 3, ApplicationPoolIdentity = 4 - if ($applicationPoolIdentityType -eq 'LocalSystem') - { - $appPoolIdentity = 0 - } - elseif ($applicationPoolIdentityType -eq 'LocalService') - { - $appPoolIdentity = 1 - } - elseif ($applicationPoolIdentityType -eq 'NetworkService') + Write-Verbose -Message "Application Pool $appPool already exists" + } + else + { + Write-Verbose -Message "Adding App Pool [$appPool]" + $null = New-WebAppPool -Name $appPool + + Write-Verbose -Message 'Set App Pool Properties' + $appPoolIdentity = 4 + if ($applicationPoolIdentityType) { - $appPoolIdentity = 2 + # LocalSystem = 0, LocalService = 1, NetworkService = 2, SpecificUser = 3, ApplicationPoolIdentity = 4 + switch ($applicationPoolIdentityType) + { + 'LocalSystem' + { + $appPoolIdentity = 0 + } + 'LocalService' + { + $appPoolIdentity = 1 + } + 'NetworkService' + { + $appPoolIdentity = 2 + } + 'ApplicationPoolIdentity' + { + $appPoolIdentity = 4 + } + default { + throw "Invalid value [$applicationPoolIdentityType] for parameter -applicationPoolIdentityType" + } + } } - } - $appPoolItem = Get-Item IIS:\AppPools\$appPool - $appPoolItem.managedRuntimeVersion = 'v4.0' - $appPoolItem.enable32BitAppOnWin64 = $enable32BitAppOnWin64 - $appPoolItem.processModel.identityType = $appPoolIdentity - $appPoolItem | Set-Item + $appPoolItem = Get-Item IIS:\AppPools\$appPool + $appPoolItem.managedRuntimeVersion = 'v4.0' + $appPoolItem.enable32BitAppOnWin64 = $enable32BitAppOnWin64 + $appPoolItem.processModel.identityType = $appPoolIdentity + $appPoolItem | Set-Item + } Write-Verbose -Message 'Add and Set Site Properties' if ($certificateThumbPrint -eq 'AllowUnencryptedTraffic') @@ -529,6 +617,11 @@ function New-PSWSEndpoint [System.String] $app = 'PSWS', + # IIS Application Name for the Site + [Parameter()] + [System.String] + $appPool, + # IIS App Pool Identity Type - must be one of LocalService, LocalSystem, NetworkService, ApplicationPoolIdentity [Parameter()] [ValidateSet('LocalService', 'LocalSystem', 'NetworkService', 'ApplicationPoolIdentity')] @@ -603,6 +696,11 @@ function New-PSWSEndpoint $Enable32BitAppOnWin64 = $false ) + if (-not $appPool) + { + $appPool = $DefaultAppPoolName + } + $script:wevtutil = "$env:windir\system32\Wevtutil.exe" $svcName = Split-Path $svc -Leaf @@ -616,12 +714,24 @@ function New-PSWSEndpoint $cimInstance = Get-CimInstance -ClassName Win32_ComputerSystem -Verbose:$false Write-Verbose ("Setting up endpoint at - $protocol//" + $cimInstance.Name + ':' + $port + '/' + $svcName) - Initialize-Endpoint -site $site -path $path -cfgfile $cfgfile -port $port -app $app ` - -applicationPoolIdentityType $applicationPoolIdentityType -svc $svc -mof $mof ` - -dispatch $dispatch -asax $asax -dependentBinaries $dependentBinaries ` - -language $language -dependentMUIFiles $dependentMUIFiles -psFiles $psFiles ` - -removeSiteFiles $removeSiteFiles -certificateThumbPrint $certificateThumbPrint ` - -enable32BitAppOnWin64 $Enable32BitAppOnWin64 + Initialize-Endpoint ` + -site $site ` + -path $path ` + -cfgfile $cfgfile ` + -port $port ` + -app $app ` + -applicationPoolIdentityType $applicationPoolIdentityType ` + -svc $svc ` + -mof $mof ` + -dispatch $dispatch ` + -asax $asax ` + -dependentBinaries $dependentBinaries ` + -language $language ` + -dependentMUIFiles $dependentMUIFiles ` + -psFiles $psFiles ` + -removeSiteFiles $removeSiteFiles ` + -certificateThumbPrint $certificateThumbPrint ` + -enable32BitAppOnWin64 $Enable32BitAppOnWin64 if ($EnablePSWSETW) { @@ -867,4 +977,6 @@ function Set-BindingRedirectSettingInWebConfig } } -Export-ModuleMember -function New-PSWSEndpoint, Set-AppSettingsInWebconfig, Set-BindingRedirectSettingInWebConfig, Remove-PSWSEndpoint +Export-ModuleMember ` + -Function New-PSWSEndpoint, Set-AppSettingsInWebconfig, Set-BindingRedirectSettingInWebConfig, Remove-PSWSEndpoint ` + -Variable DefaultAppPoolName diff --git a/Tests/Unit/MSFT_xDSCWebService.Tests.ps1 b/Tests/Unit/MSFT_xDSCWebService.Tests.ps1 index e99e0ebed..1590b31c1 100644 --- a/Tests/Unit/MSFT_xDSCWebService.Tests.ps1 +++ b/Tests/Unit/MSFT_xDSCWebService.Tests.ps1 @@ -52,7 +52,7 @@ try } $websiteDataHTTP = [System.Management.Automation.PSObject] @{ - bindings = [System.Management.Automation.PSObject] @{ + bindings = [System.Management.Automation.PSObject] @{ collection = @( @{ protocol = 'http' @@ -61,12 +61,13 @@ try } ) } - physicalPath = 'TestDrive:\inetpub\PesterTestSite' - state = 'Started' + physicalPath = 'TestDrive:\inetpub\PesterTestSite' + state = 'Started' + applicationPool = 'PSWS' } $websiteDataHTTPS = [System.Management.Automation.PSObject] @{ - bindings = [System.Management.Automation.PSObject] @{ + bindings = [System.Management.Automation.PSObject] @{ collection = @( @{ protocol = 'https' @@ -75,8 +76,9 @@ try } ) } - physicalPath = 'TestDrive:\inetpub\PesterTestSite' - state = 'Started' + physicalPath = 'TestDrive:\inetpub\PesterTestSite' + state = 'Started' + applicationPool = 'PSWS' } $certificateData = @( From 03c1d58eed03bd2359b34cd6ee433a007645c730 Mon Sep 17 00:00:00 2001 From: Thomas Meckel Date: Thu, 7 Mar 2019 09:53:54 +0100 Subject: [PATCH 02/32] Various adjustments to the MSFT_xDSCWebService resource after reviewing the code --- .../MSFT_xDSCWebService.psm1 | 73 +++++++++++-------- .../MSFT_xDSCWebService.schema.mof | 2 +- .../MSFT_xDSCWebService/PSWSIISEndpoint.psm1 | 60 +++++++-------- .../en-US/MSFT_xDSCWebService.psd1 | 1 + 4 files changed, 73 insertions(+), 63 deletions(-) diff --git a/DSCResources/MSFT_xDSCWebService/MSFT_xDSCWebService.psm1 b/DSCResources/MSFT_xDSCWebService/MSFT_xDSCWebService.psm1 index 3b57470ab..ef3ea90a7 100644 --- a/DSCResources/MSFT_xDSCWebService/MSFT_xDSCWebService.psm1 +++ b/DSCResources/MSFT_xDSCWebService/MSFT_xDSCWebService.psm1 @@ -252,14 +252,14 @@ function Set-TargetResource $CertificateTemplateName = 'WebServer', [Parameter()] - [ValidateSet("Present", "Absent")] + [ValidateSet('Present', 'Absent')] [System.String] - $Ensure = "Present", + $Ensure = 'Present', [Parameter()] - [ValidateSet("Started", "Stopped")] + [ValidateSet('Started', 'Stopped')] [System.String] - $State = "Started", + $State = 'Started', # Location on the disk where the database is stored [Parameter()] @@ -305,7 +305,7 @@ function Set-TargetResource # Exceptions of security best practices [Parameter()] - [ValidateSet("SecureTLSProtocols")] + [ValidateSet('SecureTLSProtocols')] [System.String[]] $DisableSecurityBestPractices, @@ -333,7 +333,7 @@ function Set-TargetResource } # Check parameter values - if ($UseSecurityBestPractices -and ($CertificateThumbPrint -eq "AllowUnencryptedTraffic")) + if ($UseSecurityBestPractices -and ($CertificateThumbPrint -eq 'AllowUnencryptedTraffic')) { throw $LocalizedData.ThrowUseSecurityBestPractice } @@ -342,12 +342,21 @@ function Set-TargetResource { Write-Warning -Message $LocalizedData.ConfigFirewallDeprecated } + # If the Pull Server Site should be bound to the non default AppPool + # ensure that the AppPool already exists + if ('Present' -eq $Ensure ` + -and $ApplicationPoolName -ne $DefaultAppPoolName ` + -and (-not (Test-Path -Path "IIS:\AppPools\$ApplicationPoolName"))) + { + throw ($LocalizedData.ThrowApplicationPoolNotFound -f $ApplicationPoolName) + } + # Initialize with default values $pathPullServer = "$pshome\modules\PSDesiredStateConfiguration\PullServer" - $jet4provider = "System.Data.OleDb" - $jet4database = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=$DatabasePath\Devices.mdb;" - $eseprovider = "ESENT" + $jet4provider = 'System.Data.OleDb' + $jet4database = 'Provider=Microsoft.Jet.OLEDB.4.0;Data Source=$DatabasePath\Devices.mdb;' + $eseprovider = 'ESENT' $esedatabase = "$DatabasePath\Devices.edb" $cultureInfo = Get-Culture @@ -355,7 +364,7 @@ function Set-TargetResource $language = $cultureInfo.TwoLetterISOLanguageName # the two letter iso languagename is not actually implemented in the source path, it's always 'en' - if (-not (Test-Path -Path $pathPullServer\$languagePath\Microsoft.Powershell.DesiredStateConfiguration.Service.Resources.dll)) + if (-not (Test-Path -Path "$pathPullServer\$languagePath\Microsoft.Powershell.DesiredStateConfiguration.Service.Resources.dll")) { $languagePath = 'en' } @@ -439,9 +448,9 @@ function Set-TargetResource if($SqlProvider) { - Write-Verbose -Message "Set values into the web.config that define the SQL Connection " - PSWSIISEndpoint\Set-AppSettingsInWebconfig -path $PhysicalPath -key "dbprovider" -value $jet4provider - PSWSIISEndpoint\Set-AppSettingsInWebconfig -path $PhysicalPath -key "dbconnectionstr"-value $SqlConnectionString + Write-Verbose -Message 'Set values into the web.config that define the SQL Connection' + PSWSIISEndpoint\Set-AppSettingsInWebconfig -path $PhysicalPath -key 'dbprovider' -value $jet4provider + PSWSIISEndpoint\Set-AppSettingsInWebconfig -path $PhysicalPath -key 'dbconnectionst' -value $SqlConnectionString if ($isBlue) { Set-BindingRedirectSettingInWebConfig -path $PhysicalPath @@ -449,9 +458,9 @@ function Set-TargetResource } elseif ($isBlue) { - Write-Verbose -Message "Set values into the web.config that define the repository for BLUE OS" - PSWSIISEndpoint\Set-AppSettingsInWebconfig -path $PhysicalPath -key "dbprovider" -value $eseprovider - PSWSIISEndpoint\Set-AppSettingsInWebconfig -path $PhysicalPath -key "dbconnectionstr"-value $esedatabase + Write-Verbose -Message 'Set values into the web.config that define the repository for BLUE OS' + PSWSIISEndpoint\Set-AppSettingsInWebconfig -path $PhysicalPath -key 'dbprovider' -value $eseprovider + PSWSIISEndpoint\Set-AppSettingsInWebconfig -path $PhysicalPath -key 'dbconnectionstr' -value $esedatabase Set-BindingRedirectSettingInWebConfig -path $PhysicalPath } @@ -459,40 +468,40 @@ function Set-TargetResource { if($isDownlevelOfBlue) { - Write-Verbose -Message "Set values into the web.config that define the repository for non-BLUE Downlevel OS" - $repository = Join-Path -Path "$DatabasePath" -ChildPath "Devices.mdb" + Write-Verbose -Message 'Set values into the web.config that define the repository for non-BLUE Downlevel OS' + $repository = Join-Path -Path "$DatabasePath" -ChildPath 'Devices.mdb' Copy-Item -Path "$pathPullServer\Devices.mdb" -Destination $repository -Force - PSWSIISEndpoint\Set-AppSettingsInWebconfig -path $PhysicalPath -key "dbprovider" -value $jet4provider - PSWSIISEndpoint\Set-AppSettingsInWebconfig -path $PhysicalPath -key "dbconnectionstr" -value $jet4database + PSWSIISEndpoint\Set-AppSettingsInWebconfig -path $PhysicalPath -key 'dbprovider' -value $jet4provider + PSWSIISEndpoint\Set-AppSettingsInWebconfig -path $PhysicalPath -key 'dbconnectionstr' -value $jet4database } else { - Write-Verbose -Message "Set values into the web.config that define the repository later than BLUE OS" - Write-Verbose -Message "Only ESENT is supported on Windows Server 2016" + Write-Verbose -Message 'Set values into the web.config that define the repository later than BLUE OS' + Write-Verbose -Message 'Only ESENT is supported on Windows Server 2016' - PSWSIISEndpoint\Set-AppSettingsInWebconfig -path $PhysicalPath -key "dbprovider" -value $eseprovider - PSWSIISEndpoint\Set-AppSettingsInWebconfig -path $PhysicalPath -key "dbconnectionstr"-value $esedatabase + PSWSIISEndpoint\Set-AppSettingsInWebconfig -path $PhysicalPath -key 'dbprovider' -value $eseprovider + PSWSIISEndpoint\Set-AppSettingsInWebconfig -path $PhysicalPath -key 'dbconnectionstr' -value $esedatabase } } - Write-Verbose -Message "Pull Server: Set values into the web.config that indicate the location of repository, configuration, modules" + Write-Verbose -Message 'Pull Server: Set values into the web.config that indicate the location of repository, configuration, modules' # Create the application data directory calculated above - $null = New-Item -path $DatabasePath -itemType "directory" -Force + $null = New-Item -path $DatabasePath -itemType 'directory' -Force - $null = New-Item -path "$ConfigurationPath" -itemType "directory" -Force + $null = New-Item -path "$ConfigurationPath" -itemType 'directory' -Force - PSWSIISEndpoint\Set-AppSettingsInWebconfig -path $PhysicalPath -key "ConfigurationPath" -value $configurationPath + PSWSIISEndpoint\Set-AppSettingsInWebconfig -path $PhysicalPath -key 'ConfigurationPath' -value $configurationPath - $null = New-Item -path "$ModulePath" -itemType "directory" -Force + $null = New-Item -path "$ModulePath" -itemType 'directory' -Force - PSWSIISEndpoint\Set-AppSettingsInWebconfig -path $PhysicalPath -key "ModulePath" -value $ModulePath + PSWSIISEndpoint\Set-AppSettingsInWebconfig -path $PhysicalPath -key 'ModulePath' -value $ModulePath - $null = New-Item -path "$RegistrationKeyPath" -itemType "directory" -Force + $null = New-Item -path "$RegistrationKeyPath" -itemType 'directory' -Force - PSWSIISEndpoint\Set-AppSettingsInWebconfig -path $PhysicalPath -key "RegistrationKeyPath" -value $registrationKeyPath + PSWSIISEndpoint\Set-AppSettingsInWebconfig -path $PhysicalPath -key 'RegistrationKeyPath' -value $registrationKeyPath if($AcceptSelfSignedCertificates) { diff --git a/DSCResources/MSFT_xDSCWebService/MSFT_xDSCWebService.schema.mof b/DSCResources/MSFT_xDSCWebService/MSFT_xDSCWebService.schema.mof index 4907c292d..449ab3580 100644 --- a/DSCResources/MSFT_xDSCWebService/MSFT_xDSCWebService.schema.mof +++ b/DSCResources/MSFT_xDSCWebService/MSFT_xDSCWebService.schema.mof @@ -10,7 +10,7 @@ class MSFT_xDSCWebService : OMI_BaseResource [write] string PhysicalPath; [write,ValueMap{"Present", "Absent"},Values{"Present", "Absent"}] string Ensure; [write,ValueMap{"Started","Stopped"},Values{"Started", "Stopped"}] string State; - [write] string ApplicationPoolName; + [write, Description("The IIS ApplicationPool to use for the Pull Server. If not specified a pool with name 'PSWS' will be created.")] string ApplicationPoolName; [write] string DatabasePath; [write] string ModulePath; [write] string ConfigurationPath; diff --git a/DSCResources/MSFT_xDSCWebService/PSWSIISEndpoint.psm1 b/DSCResources/MSFT_xDSCWebService/PSWSIISEndpoint.psm1 index 20edf72b7..5f4c933d7 100644 --- a/DSCResources/MSFT_xDSCWebService/PSWSIISEndpoint.psm1 +++ b/DSCResources/MSFT_xDSCWebService/PSWSIISEndpoint.psm1 @@ -485,43 +485,43 @@ function New-IISWebSite { Write-Verbose -Message "Adding App Pool [$appPool]" $null = New-WebAppPool -Name $appPool + } - Write-Verbose -Message 'Set App Pool Properties' - $appPoolIdentity = 4 - if ($applicationPoolIdentityType) + Write-Verbose -Message 'Set App Pool Properties' + $appPoolIdentity = 4 + if ($applicationPoolIdentityType) + { + # LocalSystem = 0, LocalService = 1, NetworkService = 2, SpecificUser = 3, ApplicationPoolIdentity = 4 + switch ($applicationPoolIdentityType) { - # LocalSystem = 0, LocalService = 1, NetworkService = 2, SpecificUser = 3, ApplicationPoolIdentity = 4 - switch ($applicationPoolIdentityType) + 'LocalSystem' + { + $appPoolIdentity = 0 + } + 'LocalService' + { + $appPoolIdentity = 1 + } + 'NetworkService' { - 'LocalSystem' - { - $appPoolIdentity = 0 - } - 'LocalService' - { - $appPoolIdentity = 1 - } - 'NetworkService' - { - $appPoolIdentity = 2 - } - 'ApplicationPoolIdentity' - { - $appPoolIdentity = 4 - } - default { - throw "Invalid value [$applicationPoolIdentityType] for parameter -applicationPoolIdentityType" - } + $appPoolIdentity = 2 + } + 'ApplicationPoolIdentity' + { + $appPoolIdentity = 4 + } + default { + throw "Invalid value [$applicationPoolIdentityType] for parameter -applicationPoolIdentityType" } } - - $appPoolItem = Get-Item IIS:\AppPools\$appPool - $appPoolItem.managedRuntimeVersion = 'v4.0' - $appPoolItem.enable32BitAppOnWin64 = $enable32BitAppOnWin64 - $appPoolItem.processModel.identityType = $appPoolIdentity - $appPoolItem | Set-Item } + $appPoolItem = Get-Item IIS:\AppPools\$appPool + $appPoolItem.managedRuntimeVersion = 'v4.0' + $appPoolItem.enable32BitAppOnWin64 = $enable32BitAppOnWin64 + $appPoolItem.processModel.identityType = $appPoolIdentity + $appPoolItem | Set-Item + Write-Verbose -Message 'Add and Set Site Properties' if ($certificateThumbPrint -eq 'AllowUnencryptedTraffic') { diff --git a/DSCResources/MSFT_xDSCWebService/en-US/MSFT_xDSCWebService.psd1 b/DSCResources/MSFT_xDSCWebService/en-US/MSFT_xDSCWebService.psd1 index ed91aeded..28cdc704e 100644 --- a/DSCResources/MSFT_xDSCWebService/en-US/MSFT_xDSCWebService.psd1 +++ b/DSCResources/MSFT_xDSCWebService/en-US/MSFT_xDSCWebService.psd1 @@ -7,4 +7,5 @@ ConvertFrom-StringData -StringData @' IISInstallationPathNotFound = IIS installation path not found IISWebAdministrationAssemblyNotFound = IIS version of Microsoft.Web.Administration.dll not found ConfigFirewallDeprecated = The support for configuring firewall rules is deprecated. Please set ConfigureFirewall to false and use the Firewall resource from NetworkingDSC module to configure required firewall rules. + ThrowApplicationPoolNotFound = IIS Application pool "{0}" not found. '@ From 36b08e6699574353d219c022e0ae6df69a6f753f Mon Sep 17 00:00:00 2001 From: Thomas Meckel Date: Thu, 14 Mar 2019 19:34:34 +0100 Subject: [PATCH 03/32] Fixed bugs after integration tests --- .../MSFT_xDSCWebService.psm1 | 38 +++--- .../MSFT_xDSCWebService/PSWSIISEndpoint.psm1 | 111 +++++++++++------- 2 files changed, 89 insertions(+), 60 deletions(-) diff --git a/DSCResources/MSFT_xDSCWebService/MSFT_xDSCWebService.psm1 b/DSCResources/MSFT_xDSCWebService/MSFT_xDSCWebService.psm1 index ef3ea90a7..4d46332c9 100644 --- a/DSCResources/MSFT_xDSCWebService/MSFT_xDSCWebService.psm1 +++ b/DSCResources/MSFT_xDSCWebService/MSFT_xDSCWebService.psm1 @@ -401,7 +401,7 @@ function Set-TargetResource } # there is a web site, but there shouldn't be one - Write-Verbose -Message "Removing web site $EndpointName" + Write-Verbose -Message "Removing web site [$EndpointName]" PSWSIISEndpoint\Remove-PSWSEndpoint -SiteName $EndpointName $portList | ForEach-Object -Process { Remove-PullServerFirewallConfiguration -Port $_ } @@ -413,23 +413,25 @@ function Set-TargetResource # =========================================================== Write-Verbose -Message "Create the IIS endpoint" - PSWSIISEndpoint\New-PSWSEndpoint -site $EndpointName ` - -path $PhysicalPath ` - -cfgfile $webConfigFileName ` - -port $Port ` - -appPool $ApplicationPoolName ` - -applicationPoolIdentityType LocalSystem ` - -app $EndpointName ` - -svc $svcFileName ` - -mof $pswsMofFileName ` - -dispatch $pswsDispatchFileName ` - -asax "$pathPullServer\Global.asax" ` - -dependentBinaries "$pathPullServer\Microsoft.Powershell.DesiredStateConfiguration.Service.dll" ` - -language $language ` - -dependentMUIFiles "$pathPullServer\$languagePath\Microsoft.Powershell.DesiredStateConfiguration.Service.Resources.dll" ` - -certificateThumbPrint $certificateThumbPrint ` - -Enable32BitAppOnWin64 $Enable32BitAppOnWin64 ` - -Verbose + PSWSIISEndpoint\New-PSWSEndpoint ` + -site $EndpointName ` + -path $PhysicalPath ` + -cfgfile $webConfigFileName ` + -port $Port ` + -appPool $ApplicationPoolName ` + -applicationPoolIdentityType LocalSystem ` + -app $EndpointName ` + -svc $svcFileName ` + -mof $pswsMofFileName ` + -dispatch $pswsDispatchFileName ` + -asax "$pathPullServer\Global.asax" ` + -dependentBinaries "$pathPullServer\Microsoft.Powershell.DesiredStateConfiguration.Service.dll" ` + -language $language ` + -dependentMUIFiles "$pathPullServer\$languagePath\Microsoft.Powershell.DesiredStateConfiguration.Service.Resources.dll" ` + -certificateThumbPrint $certificateThumbPrint ` + -EnableFirewallException $true ` + -Enable32BitAppOnWin64 $Enable32BitAppOnWin64 ` + -Verbose if ($ConfigureFirewall) { diff --git a/DSCResources/MSFT_xDSCWebService/PSWSIISEndpoint.psm1 b/DSCResources/MSFT_xDSCWebService/PSWSIISEndpoint.psm1 index 5f4c933d7..e1096826b 100644 --- a/DSCResources/MSFT_xDSCWebService/PSWSIISEndpoint.psm1 +++ b/DSCResources/MSFT_xDSCWebService/PSWSIISEndpoint.psm1 @@ -15,14 +15,17 @@ function Initialize-Endpoint param ( [Parameter()] + [ValidateNotNullOrEmpty()] [System.String] $appPool, [Parameter()] + [ValidateNotNullOrEmpty()] [System.String] $site, [Parameter()] + [ValidateNotNullOrEmpty()] [System.String] $path, @@ -36,10 +39,12 @@ function Initialize-Endpoint $port, [Parameter()] + [ValidateNotNullOrEmpty()] [System.String] $app, [Parameter()] + [ValidateNotNullOrEmpty()] [System.String] $applicationPoolIdentityType, @@ -54,6 +59,7 @@ function Initialize-Endpoint $mof, [Parameter()] + [ValidateNotNullOrEmpty()] [System.String] $dispatch, @@ -63,14 +69,17 @@ function Initialize-Endpoint $asax, [Parameter()] + [ValidateNotNullOrEmpty()] [System.String[]] $dependentBinaries, [Parameter()] + [ValidateNotNullOrEmpty()] [System.String] $language, [Parameter()] + [ValidateNotNullOrEmpty()] [System.String[]] $dependentMUIFiles, @@ -144,7 +153,7 @@ function Initialize-Endpoint -psFiles $psFiles New-IISWebSite -site $site ` - -path $path + -path $path ` -port $port ` -app $app ` -apppool $appPool ` @@ -442,23 +451,27 @@ function New-IISWebSite [CmdletBinding()] param ( - [Parameter()] + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] [System.String] $site, - [Parameter()] + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] [System.String] $path, - [Parameter()] + [Parameter(Mandatory = $true)] [System.Int32] $port, - [Parameter()] + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] [System.String] $app, - [Parameter()] + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] [System.String] $appPool, @@ -466,7 +479,8 @@ function New-IISWebSite [System.String] $applicationPoolIdentityType, - [Parameter()] + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] [System.String] $certificateThumbPrint, @@ -479,48 +493,49 @@ function New-IISWebSite if (Test-Path IIS:\AppPools\$appPool) { - Write-Verbose -Message "Application Pool $appPool already exists" + Write-Verbose -Message "Application Pool [$appPool] already exists" } else { Write-Verbose -Message "Adding App Pool [$appPool]" $null = New-WebAppPool -Name $appPool - } - Write-Verbose -Message 'Set App Pool Properties' - $appPoolIdentity = 4 - if ($applicationPoolIdentityType) - { - # LocalSystem = 0, LocalService = 1, NetworkService = 2, SpecificUser = 3, ApplicationPoolIdentity = 4 - switch ($applicationPoolIdentityType) + Write-Verbose -Message 'Set App Pool Properties' + $appPoolIdentity = 4 + if ($applicationPoolIdentityType) { - 'LocalSystem' - { - $appPoolIdentity = 0 - } - 'LocalService' - { - $appPoolIdentity = 1 - } - 'NetworkService' + # LocalSystem = 0, LocalService = 1, NetworkService = 2, SpecificUser = 3, ApplicationPoolIdentity = 4 + switch ($applicationPoolIdentityType) { - $appPoolIdentity = 2 - } - 'ApplicationPoolIdentity' - { - $appPoolIdentity = 4 - } - default { - throw "Invalid value [$applicationPoolIdentityType] for parameter -applicationPoolIdentityType" + 'LocalSystem' + { + $appPoolIdentity = 0 + } + 'LocalService' + { + $appPoolIdentity = 1 + } + 'NetworkService' + { + $appPoolIdentity = 2 + } + 'ApplicationPoolIdentity' + { + $appPoolIdentity = 4 + } + default { + throw "Invalid value [$applicationPoolIdentityType] for parameter -applicationPoolIdentityType" + } } } - } - $appPoolItem = Get-Item IIS:\AppPools\$appPool - $appPoolItem.managedRuntimeVersion = 'v4.0' - $appPoolItem.enable32BitAppOnWin64 = $enable32BitAppOnWin64 - $appPoolItem.processModel.identityType = $appPoolIdentity - $appPoolItem | Set-Item + $appPoolItem = Get-Item IIS:\AppPools\$appPool + $appPoolItem.managedRuntimeVersion = 'v4.0' + $appPoolItem.enable32BitAppOnWin64 = $enable32BitAppOnWin64 + $appPoolItem.processModel.identityType = $appPoolIdentity + $appPoolItem | Set-Item + + } Write-Verbose -Message 'Add and Set Site Properties' if ($certificateThumbPrint -eq 'AllowUnencryptedTraffic') @@ -594,16 +609,19 @@ function New-PSWSEndpoint ( # Unique Name of the IIS Site [Parameter()] + [ValidateNotNullOrEmpty()] [System.String] $site = 'PSWS', # Physical path for the IIS Endpoint on the machine (under inetpub) [Parameter()] + [ValidateNotNullOrEmpty()] [System.String] $path = "$env:SystemDrive\inetpub\PSWS", # Web.config file [Parameter()] + [ValidateNotNullOrEmpty()] [System.String] $cfgfile = 'web.config', @@ -614,6 +632,7 @@ function New-PSWSEndpoint # IIS Application Name for the Site [Parameter()] + [ValidateNotNullOrEmpty()] [System.String] $app = 'PSWS', @@ -630,6 +649,7 @@ function New-PSWSEndpoint # WCF Service SVC file [Parameter()] + [ValidateNotNullOrEmpty()] [System.String] $svc = 'PSWS.svc', @@ -671,7 +691,6 @@ function New-PSWSEndpoint # Any dependent PowerShell Scipts/Modules that need to be deployed to the IIS endpoint application root [Parameter()] - [ValidateNotNullOrEmpty()] [System.String[]] $psFiles, @@ -715,6 +734,7 @@ function New-PSWSEndpoint Write-Verbose ("Setting up endpoint at - $protocol//" + $cimInstance.Name + ':' + $port + '/' + $svcName) Initialize-Endpoint ` + -appPool $appPool ` -site $site ` -path $path ` -cfgfile $cfgfile ` @@ -783,14 +803,21 @@ function Remove-PSWSEndpoint # Get the path so we can delete the files $filePath = $site.PhysicalPath - # Remove the actual site. - Remove-Website -Name $siteName + # If the website isn't stopped before removal the directories and files might be locked + # and thus cannot be removed by Remove-WebSite + Write-Verbose -Message "Stopping website $siteName" + Stop-Website -Name $siteName + <# There may be running requests, wait a little I had an issue where the files were still in use when I tried to delete them #> - Start-Sleep -Milliseconds 2000 + Write-Verbose -Message 'Waiting for IIS to stop website' + Start-Sleep -Milliseconds 1000 + + # Remove the actual site. + Remove-Website -Name $siteName # Remove the files for the site If (Test-Path -Path $filePath) From bd11178482e81387176789ed8152328c5f722df2 Mon Sep 17 00:00:00 2001 From: Thomas Meckel Date: Tue, 9 Apr 2019 07:49:20 +0200 Subject: [PATCH 04/32] xDSCWebService: Merged integration tests from branch issue-536 --- DSCResources/MSFT_xDSCWebService/MSFT_xDSCWebService.psm1 | 1 - 1 file changed, 1 deletion(-) diff --git a/DSCResources/MSFT_xDSCWebService/MSFT_xDSCWebService.psm1 b/DSCResources/MSFT_xDSCWebService/MSFT_xDSCWebService.psm1 index 4d46332c9..ab7261c66 100644 --- a/DSCResources/MSFT_xDSCWebService/MSFT_xDSCWebService.psm1 +++ b/DSCResources/MSFT_xDSCWebService/MSFT_xDSCWebService.psm1 @@ -429,7 +429,6 @@ function Set-TargetResource -language $language ` -dependentMUIFiles "$pathPullServer\$languagePath\Microsoft.Powershell.DesiredStateConfiguration.Service.Resources.dll" ` -certificateThumbPrint $certificateThumbPrint ` - -EnableFirewallException $true ` -Enable32BitAppOnWin64 $Enable32BitAppOnWin64 ` -Verbose From 00f8a0e177e3f77e942ace81511ee1e597b179eb Mon Sep 17 00:00:00 2001 From: Thomas Meckel Date: Fri, 3 May 2019 21:43:20 +0200 Subject: [PATCH 05/32] * Fixed bug in handling of application pools and sites * Fixed firewall handling * Restructured unit tests for MSFT_xDSCWebService so that it can be tested more thoroughly in the future --- .../MSFT_xDSCWebService/Firewall.psm1 | 20 +- .../MSFT_xDSCWebService.psm1 | 23 +- .../MSFT_xDSCWebService/PSWSIISEndpoint.psm1 | 95 +++-- Tests/Unit/MSFT_xDSCWebService.Tests.ps1 | 352 ++++++++++++++++-- 4 files changed, 397 insertions(+), 93 deletions(-) diff --git a/DSCResources/MSFT_xDSCWebService/Firewall.psm1 b/DSCResources/MSFT_xDSCWebService/Firewall.psm1 index 2b29d21ea..d6bd3a2a5 100644 --- a/DSCResources/MSFT_xDSCWebService/Firewall.psm1 +++ b/DSCResources/MSFT_xDSCWebService/Firewall.psm1 @@ -1,5 +1,5 @@ # Name and description for the Firewall rules. Used in multiple locations -New-Variable -Name fireWallRuleDisplayName -Value 'Desired State Configuration - Pull Server Port:{0}' -Option ReadOnly -Scope Script -Force +New-Variable -Name fireWallRuleDisplayName -Value 'DSCPullServer_IIS_Port:{0}' -Option ReadOnly -Scope Script -Force New-Variable -Name netsh -Value "$env:windir\system32\netsh.exe" -Option ReadOnly -Scope Script -Force <# .SYNOPSIS @@ -22,11 +22,13 @@ function Add-PullServerFirewallConfiguration Write-Verbose -Message 'Disable Inbound Firewall Notification' $null = & $script:netsh advfirewall set currentprofile settings inboundusernotification disable + $ruleName = $FireWallRuleDisplayName -f $port + # Remove all existing rules with that displayName - $null = & $script:netsh advfirewall firewall delete rule name=DSCPullServer_IIS_Port protocol=tcp localport=$Port + $null = & $script:netsh advfirewall firewall delete rule name=$ruleName protocol=tcp localport=$Port Write-Verbose -Message "Add Firewall Rule for port $Port" - $null = & $script:netsh advfirewall firewall add rule name=DSCPullServer_IIS_Port dir=in action=allow protocol=TCP localport=$Port + $null = & $script:netsh advfirewall firewall add rule name=$ruleName dir=in action=allow protocol=TCP localport=$Port } <# @@ -51,14 +53,17 @@ function Remove-PullServerFirewallConfiguration { # remove all existing rules with that displayName Write-Verbose -Message "Delete Firewall Rule for port $Port" - $null = & $script:netsh advfirewall firewall delete rule name=DSCPullServer_IIS_Port protocol=tcp localport=$Port + $ruleName = $FireWallRuleDisplayName -f $port # backwards compatibility with old code if (Get-Command -Name Get-NetFirewallRule -CommandType Cmdlet -ErrorAction:SilentlyContinue) { # Remove all rules with that name - $ruleName = ($($FireWallRuleDisplayName) -f $port) - Get-NetFirewallRule | Where-Object -Property DisplayName -eq -Value "$ruleName" | Remove-NetFirewallRule + Get-NetFirewallRule -DisplayName $ruleName | Remove-NetFirewallRule + } + else + { + $null = & $script:netsh advfirewall firewall delete rule name=$ruleName protocol=tcp localport=$Port } } else @@ -88,7 +93,8 @@ function Test-PullServerFirewallConfiguration # Remove all existing rules with that displayName Write-Verbose -Message "Testing Firewall Rule for port $Port" - $result = & $script:netsh advfirewall firewall show rule name=DSCPullServer_IIS_Port | Select-String -Pattern "LocalPort:\s*$Port" + $ruleName = $FireWallRuleDisplayName -f $port + $result = & $script:netsh advfirewall firewall show rule name=$ruleName | Select-String -Pattern "LocalPort:\s*$Port" return -not [string]::IsNullOrWhiteSpace($result) } diff --git a/DSCResources/MSFT_xDSCWebService/MSFT_xDSCWebService.psm1 b/DSCResources/MSFT_xDSCWebService/MSFT_xDSCWebService.psm1 index ab7261c66..d8da079d4 100644 --- a/DSCResources/MSFT_xDSCWebService/MSFT_xDSCWebService.psm1 +++ b/DSCResources/MSFT_xDSCWebService/MSFT_xDSCWebService.psm1 @@ -430,17 +430,22 @@ function Set-TargetResource -dependentMUIFiles "$pathPullServer\$languagePath\Microsoft.Powershell.DesiredStateConfiguration.Service.Resources.dll" ` -certificateThumbPrint $certificateThumbPrint ` -Enable32BitAppOnWin64 $Enable32BitAppOnWin64 ` - -Verbose - if ($ConfigureFirewall) + switch ($Ensure) { - Write-Verbose -Message "Enabling firewall exception for port $port" - Add-PullServerFirewallConfiguration -Port $port - } - else - { - Write-Verbose -Message "Disabling firewall exception for port $port" - Remove-PullServerFirewallConfiguration -Port $port + 'Present' + { + if ($ConfigureFirewall) + { + Write-Verbose -Message "Enabling firewall exception for port $port" + Add-PullServerFirewallConfiguration -Port $port + } + } + 'Absent' + { + Write-Verbose -Message "Disabling firewall exception for port $port" + Remove-PullServerFirewallConfiguration -Port $port + } } Update-LocationTagInApplicationHostConfigForAuthentication -WebSite $EndpointName -Authentication "anonymous" diff --git a/DSCResources/MSFT_xDSCWebService/PSWSIISEndpoint.psm1 b/DSCResources/MSFT_xDSCWebService/PSWSIISEndpoint.psm1 index e1096826b..f1fa6342b 100644 --- a/DSCResources/MSFT_xDSCWebService/PSWSIISEndpoint.psm1 +++ b/DSCResources/MSFT_xDSCWebService/PSWSIISEndpoint.psm1 @@ -117,11 +117,9 @@ function Initialize-Endpoint Test-IISInstall # First remove the site so that the binding count on the application pool is reduced - Write-Verbose -Message "Remove the site [$site] if it already exists" Update-Site -siteName $site -siteAction Remove - Write-Verbose -Message "Delete App Pool [$appPool] if it exists" - Remove-AppPool -apppool $appPool + Remove-AppPool -appPool $appPool # Check for existing binding, there should be no binding with the same port $allWebBindingsOnPort = Get-WebBinding | Where-Object -FilterScript { @@ -235,29 +233,59 @@ function Update-Site [Parameter(ParameterSetName = 'SiteName', Mandatory = $true, Position = 1)] [Parameter(ParameterSetName = 'Site', Mandatory = $true, Position = 1)] [System.String] + [ValidateSet('Start', 'Stop', 'Remove')] $siteAction ) - [System.String] $name = $null - - if ($PSCmdlet.ParameterSetName -eq 'SiteName') - { - $name = $siteName - } - elseif ($PSCmdlet.ParameterSetName -eq 'Site') + if ('SiteName' -eq $PSCmdlet.ParameterSetName) { - $name = $site.Name + $site = Get-Website -Name $siteName } - - if (Test-ForIISSite -siteName $name) + if ($site) { switch ($siteAction) { - 'Start' {Start-Website -Name "$name"} - 'Stop' {Stop-Website -Name "$name" -ErrorAction SilentlyContinue} - 'Remove' {Remove-Website -Name "$name"} + 'Start' + { + Write-Verbose -Message "Starting IIS Website [$($site.name)]" + Start-Website -Name $site.name + } + 'Stop' + { + if ('Started' -eq $site.state) + { + Write-Verbose -Message "Stopping WebSite $($site.name)" + $website = Stop-Website -Name $site.name -Passthru + if ('Started' -eq $website.state) + { + Write-Error -Message "Unable to stop WebSite $($site.name)" -ErrorAction:Stop + } + + <# + There may be running requests, wait a little + I had an issue where the files were still in use + when I tried to delete them + #> + Write-Verbose -Message 'Waiting for IIS to stop website' + Start-Sleep -Milliseconds 1000 + } + else + { + Write-Verbose -Message "IIS Website [$($site.name)] already stopped" + } + } + 'Remove' + { + Update-Site -site $site -siteAction Stop + Write-Verbose -Message "Removing IIS Website [$($site.name)]" + Remove-Website -Name $site.name + } } } + else + { + Write-Verbose -Message "IIS Website [$siteName] not found" + } } <# @@ -787,37 +815,13 @@ function Remove-PSWSEndpoint $site = Get-Website -Name $siteName if ($site) { - if ('Started' -eq $site.state) - { - Write-Verbose -Message "Stopping WebSite $($site.name)" - $website = Stop-Website -Name $site.name -Passthru - if ('Started' -eq $website.state) - { - Write-Error -Message "Unable to stop WebSite $($site.name)" -ErrorAction:Stop - } - } - # And the pool it is using $pool = $site.applicationPool - # Get the path so we can delete the files $filePath = $site.PhysicalPath - # If the website isn't stopped before removal the directories and files might be locked - # and thus cannot be removed by Remove-WebSite - Write-Verbose -Message "Stopping website $siteName" - Stop-Website -Name $siteName - - <# - There may be running requests, wait a little - I had an issue where the files were still in use - when I tried to delete them - #> - Write-Verbose -Message 'Waiting for IIS to stop website' - Start-Sleep -Milliseconds 1000 - # Remove the actual site. - Remove-Website -Name $siteName + Update-Site -site $site -siteAction Remove # Remove the files for the site If (Test-Path -Path $filePath) @@ -826,14 +830,7 @@ function Remove-PSWSEndpoint Remove-Item -Path $filePath -Force } - # Find out whether any other site is using this pool - $filter = "/system.applicationHost/sites/site/application[@applicationPool='" + $pool + "']" - $apps = (Get-WebConfigurationProperty -Filter $filter -PSPath 'machine/webroot/apphost' -name path).ItemXPath - if (-not $apps -or $apps.count -eq 1) - { - # If we are the only site in the pool, remove the pool as well. - Remove-WebAppPool -Name $pool - } + Remove-AppPool -appPool $pool } else { diff --git a/Tests/Unit/MSFT_xDSCWebService.Tests.ps1 b/Tests/Unit/MSFT_xDSCWebService.Tests.ps1 index 1590b31c1..f40cde1a3 100644 --- a/Tests/Unit/MSFT_xDSCWebService.Tests.ps1 +++ b/Tests/Unit/MSFT_xDSCWebService.Tests.ps1 @@ -39,6 +39,7 @@ try CertificateThumbPrint = 'AllowUnencryptedTraffic' EndpointName = 'PesterTestSite' UseSecurityBestPractices = $false + ConfigureFirewall = $false } $serviceData = @{ @@ -58,6 +59,11 @@ try protocol = 'http' bindingInformation = '*:8080:' certificateHash = '' + }, + @{ + protocol = 'http' + bindingInformation = '*:8090:' + certificateHash = '' } ) } @@ -151,7 +157,6 @@ try <# Create dummy functions so that Pester is able to mock them #> function Get-Website {} function Get-WebBinding {} - function Stop-Website {} $webConfigPath = 'TestDrive:\inetpub\PesterTestSite\Web.config' @@ -175,8 +180,8 @@ try } #region Mocks - Mock -CommandName Get-WebSite -MockWith {return $websiteDataHTTP} - Mock -CommandName Get-WebBinding -MockWith {return @{CertificateHash = $websiteDataHTTPS.bindings.collection[0].certificateHash}} + Mock -CommandName Get-WebSite -MockWith { return $websiteDataHTTP } + Mock -CommandName Get-WebBinding -MockWith { return @{ CertificateHash = $websiteDataHTTPS.bindings.collection[0].certificateHash } } Mock -CommandName Get-ChildItem -ParameterFilter {$Path -eq $websiteDataHTTP.physicalPath -and $Filter -eq '*.svc'} -MockWith {return @{Name = $serviceData.ServiceName}} Mock -CommandName Get-WebConfigAppSetting -ParameterFilter {$AppSettingName -eq 'ModulePath'} -MockWith {return $serviceData.ModulePath} Mock -CommandName Get-WebConfigAppSetting -ParameterFilter {$AppSettingName -eq 'ConfigurationPath'} -MockWith {return $serviceData.ConfigurationPath} @@ -466,6 +471,12 @@ try function Get-Website {} function Get-WebBinding {} function Stop-Website {} + function New-WebAppPool {} + function Remove-WebAppPool {} + function New-WebSite {} + function Start-Website {} + function Get-WebConfigurationProperty {} + function Remove-Website {} #region Mocks Mock -CommandName Get-Command -ParameterFilter {$Name -eq '.\appcmd.exe'} -MockWith { @@ -491,37 +502,35 @@ try } } Mock -CommandName Get-OSVersion -MockWith {@{Major = 6; Minor = 3}} - Mock -CommandName Get-Website #endregion Context -Name 'DSC Service is not installed and Ensure is Absent' -Fixture { #region Mocks Mock -CommandName Test-Path -ParameterFilter { $LiteralPath -like "IIS:\Sites\*" } -MockWith { $false } Mock -CommandName Remove-PSWSEndpoint + Mock -CommandName Remove-PullServerFirewallConfiguration #endregion It 'Should call expected mocks' { Set-TargetResource @testParameters -Ensure Absent - Assert-MockCalled -Exactly -Times 0 -CommandName Get-Website Assert-MockCalled -Exactly -Times 1 -CommandName Get-OSVersion Assert-MockCalled -Exactly -Times 1 -CommandName Test-Path Assert-MockCalled -Exactly -Times 0 -CommandName Remove-PSWSEndpoint Assert-MockCalled -Exactly -Times 0 -CommandName Get-Command + Assert-MockCalled -Exactly -Times 0 -CommandName Remove-PullServerFirewallConfiguration } } Context -Name 'DSC Service is installed and Ensure is Absent' -Fixture { #region Mocks Mock -CommandName Test-Path -ParameterFilter { $LiteralPath -like "IIS:\Sites\*" } -MockWith { $LiteralPath -eq "IIS:\Sites\$($testParameters.EndpointName)" } - Mock -CommandName Get-Website -MockWith { return $websiteDataHTTP } Mock -CommandName Remove-PSWSEndpoint #endregion It 'Should call expected mocks' { Set-TargetResource @testParameters -Ensure Absent - Assert-MockCalled -Exactly -Times 0 -CommandName Get-Website Assert-MockCalled -Exactly -Times 1 -CommandName Get-OSVersion Assert-MockCalled -Exactly -Times 0 -CommandName Get-Command Assert-MockCalled -Exactly -Times 1 -CommandName Test-Path @@ -529,10 +538,9 @@ try } } - #region Mocks - Mock -CommandName Get-Culture -MockWith {@{TwoLetterISOLanguageName = 'en'}} - Mock -CommandName Test-Path -MockWith {$true} - Mock -CommandName New-PSWSEndpoint + #region MSFT_xDSCWebService Mocks + Mock -CommandName Get-Culture -MockWith { @{TwoLetterISOLanguageName = 'en'} } + Mock -CommandName Test-Path -MockWith { $true } Mock -CommandName Update-LocationTagInApplicationHostConfigForAuthentication Mock -CommandName Set-AppSettingsInWebconfig Mock -CommandName Set-BindingRedirectSettingInWebConfig @@ -540,6 +548,76 @@ try Mock -CommandName Test-FilesDiffer -MockWith { $false } #endregion + #region PSWSIISEndpoint Mocks + Mock -CommandName Get-WebConfigurationProperty -ModuleName PSWSIISEndpoint + Mock -CommandName Test-Path -MockWith { $true } -ModuleName PSWSIISEndpoint + Mock -CommandName Test-IISInstall -ModuleName PSWSIISEndpoint + Mock -CommandName Remove-WebAppPool -ModuleName PSWSIISEndpoint + Mock -CommandName Remove-Item -ModuleName PSWSIISEndpoint + Mock -CommandName Copy-PSWSConfigurationToIISEndpointFolder -ModuleName PSWSIISEndpoint + Mock -CommandName New-WebAppPool -ModuleName PSWSIISEndpoint + Mock -CommandName Set-Item -ModuleName PSWSIISEndpoint + Mock -CommandName Get-Item -ParameterFilter { $Path -like 'IIS:\AppPools*' } -MockWith { + [PSCustomObject]@{ + name = Split-Path -Path $Path -Leaf + managedRuntimeVersion = 'v4.0' + enable32BitAppOnWin64 = $false + processModel = [PSCustomObject]@{ + identityType = 4 + } + } + } -ModuleName PSWSIISEndpoint + Mock -CommandName Get-ChildItem -ParameterFilter { $Path -eq 'Cert:\LocalMachine\My\' } -MockWith { + ##### + # we cannot use the existing certificate definitions from $certificateData because the + # mock runs in a different module and thus the variable does not exist + ##### + [System.Management.Automation.PSObject] @{ + Thumbprint = 'AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTT' + Subject = 'PesterTestDuplicateCertificate' + Extensions = [System.Array] @( + [System.Management.Automation.PSObject] @{ + Oid = [System.Management.Automation.PSObject] @{ + FriendlyName = 'Certificate Template Name' + Value = '1.3.6.1.4.1.311.20.2' + } + } + [System.Management.Automation.PSObject] @{} + ) + NotAfter = Get-Date + } + } -ModuleName PSWSIISEndpoint + Mock -CommandName Get-Item -ParameterFilter { $Path -eq 'CERT:\LocalMachine\MY\AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTT' } -MockWith { + ##### + # we cannot use the existing certificate definitions from $certificateData because the + # mock runs in a different module and thus the variable does not exist + ##### + [System.Management.Automation.PSObject] @{ + Thumbprint = 'AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTT' + Subject = 'PesterTestDuplicateCertificate' + Extensions = [System.Array] @( + [System.Management.Automation.PSObject] @{ + Oid = [System.Management.Automation.PSObject] @{ + FriendlyName = 'Certificate Template Name' + Value = '1.3.6.1.4.1.311.20.2' + } + } + [System.Management.Automation.PSObject] @{} + ) + NotAfter = Get-Date + } + } -ModuleName PSWSIISEndpoint + Mock -CommandName New-WebSite -ModuleName PSWSIISEndpoint + Mock -CommandName New-SiteID -ModuleName PSWSIISEndpoint -MockWith { + Get-Random -Maximum 10000 -Minimum 1 + } + Mock -CommandName New-Item -ParameterFilter { $Path -like 'IIS:*' } -ModuleName PSWSIISEndpoint + Mock -CommandName Remove-Item -ModuleName PSWSIISEndpoint + Mock -CommandName Get-WebBinding -ModuleName PSWSIISEndpoint + Mock -CommandName Remove-Website -ModuleName PSWSIISEndpoint + Mock -CommandName Start-Website -ModuleName PSWSIISEndpoint + #endregion + Context -Name 'Ensure is Present' -Fixture { $setTargetPaths = @{ DatabasePath = 'TestDrive:\Database' @@ -549,14 +627,17 @@ try } It 'Should call expected mocks' { + Mock -CommandName Get-Website -MockWith { } -ModuleName PSWSIISEndpoint + Mock -CommandName Add-PullServerFirewallConfiguration + Set-TargetResource @testParameters @setTargetPaths -Ensure Present Assert-MockCalled -Exactly -Times 3 -CommandName Get-Command Assert-MockCalled -Exactly -Times 1 -CommandName Get-Culture - Assert-MockCalled -Exactly -Times 0 -CommandName Get-Website + Assert-MockCalled -Exactly -Times 2 -CommandName Get-Website -ModuleName PSWSIISEndpoint Assert-MockCalled -Exactly -Times 2 -CommandName Test-Path Assert-MockCalled -Exactly -Times 1 -CommandName Get-OSVersion - Assert-MockCalled -Exactly -Times 1 -CommandName New-PSWSEndpoint + Assert-MockCalled -Exactly -Times 0 -CommandName Add-PullServerFirewallConfiguration Assert-MockCalled -Exactly -Times 3 -CommandName Update-LocationTagInApplicationHostConfigForAuthentication Assert-MockCalled -Exactly -Times 5 -CommandName Set-AppSettingsInWebconfig Assert-MockCalled -Exactly -Times 1 -CommandName Set-BindingRedirectSettingInWebConfig @@ -597,14 +678,22 @@ try } It 'Should call expected mocks' { + + Mock -CommandName Get-Website -MockWith { + [PSCustomObject]@{ + Name = $Name + State = 'Stopped' + applicationPool = 'PSWS' + physicalPath = 'TestDrive:\inetpub\PesterTestSite' + } + } -ModuleName PSWSIISEndpoint + Set-TargetResource @testParameters @setTargetPaths -Ensure Present Assert-MockCalled -Exactly -Times 3 -CommandName Get-Command Assert-MockCalled -Exactly -Times 1 -CommandName Get-Culture - Assert-MockCalled -Exactly -Times 0 -CommandName Get-Website Assert-MockCalled -Exactly -Times 2 -CommandName Test-Path Assert-MockCalled -Exactly -Times 1 -CommandName Get-OSVersion - Assert-MockCalled -Exactly -Times 1 -CommandName New-PSWSEndpoint Assert-MockCalled -Exactly -Times 3 -CommandName Update-LocationTagInApplicationHostConfigForAuthentication Assert-MockCalled -Exactly -Times 5 -CommandName Set-AppSettingsInWebconfig Assert-MockCalled -Exactly -Times 0 -CommandName Set-BindingRedirectSettingInWebConfig @@ -626,14 +715,22 @@ try } It 'Should call expected mocks' { + Mock -CommandName Get-Website -MockWith { + [PSCustomObject]@{ + Name = $Name + State = 'Stopped' + applicationPool = 'PSWS' + physicalPath = 'TestDrive:\inetpub\PesterTestSite' + } + } -ModuleName PSWSIISEndpoint + Set-TargetResource @testParameters @setTargetPaths -Ensure Present Assert-MockCalled -Exactly -Times 3 -CommandName Get-Command Assert-MockCalled -Exactly -Times 1 -CommandName Get-Culture - Assert-MockCalled -Exactly -Times 0 -CommandName Get-Website + Assert-MockCalled -Exactly -Times 2 -CommandName Get-Website -ModuleName PSWSIISEndpoint Assert-MockCalled -Exactly -Times 2 -CommandName Test-Path Assert-MockCalled -Exactly -Times 1 -CommandName Get-OSVersion - Assert-MockCalled -Exactly -Times 1 -CommandName New-PSWSEndpoint Assert-MockCalled -Exactly -Times 3 -CommandName Update-LocationTagInApplicationHostConfigForAuthentication Assert-MockCalled -Exactly -Times 5 -CommandName Set-AppSettingsInWebconfig Assert-MockCalled -Exactly -Times 0 -CommandName Set-BindingRedirectSettingInWebConfig @@ -650,14 +747,22 @@ try } It 'Should call expected mocks' { + Mock -CommandName Get-Website -MockWith { + [PSCustomObject]@{ + Name = $Name + State = 'Stopped' + applicationPool = 'PSWS' + physicalPath = 'TestDrive:\inetpub\PesterTestSite' + } + } -ModuleName PSWSIISEndpoint + Set-TargetResource @testParameters @setTargetPaths -Ensure Present -Enable32BitAppOnWin64 $true Assert-MockCalled -Exactly -Times 3 -CommandName Get-Command Assert-MockCalled -Exactly -Times 1 -CommandName Get-Culture - Assert-MockCalled -Exactly -Times 0 -CommandName Get-Website + Assert-MockCalled -Exactly -Times 2 -CommandName Get-Website -ModuleName PSWSIISEndpoint Assert-MockCalled -Exactly -Times 2 -CommandName Test-Path Assert-MockCalled -Exactly -Times 1 -CommandName Get-OSVersion - Assert-MockCalled -Exactly -Times 1 -CommandName New-PSWSEndpoint Assert-MockCalled -Exactly -Times 3 -CommandName Update-LocationTagInApplicationHostConfigForAuthentication Assert-MockCalled -Exactly -Times 5 -CommandName Set-AppSettingsInWebconfig Assert-MockCalled -Exactly -Times 1 -CommandName Set-BindingRedirectSettingInWebConfig @@ -675,14 +780,22 @@ try It 'Should call expected mocks' { + Mock -CommandName Get-Website -MockWith { + [PSCustomObject]@{ + Name = $Name + State = 'Stopped' + applicationPool = 'PSWS' + physicalPath = 'TestDrive:\inetpub\PesterTestSite' + } + } -ModuleName PSWSIISEndpoint + Set-TargetResource @testParameters @setTargetPaths -Ensure Present -AcceptSelfSignedCertificates $false Assert-MockCalled -Exactly -Times 1 -CommandName Get-Command Assert-MockCalled -Exactly -Times 1 -CommandName Get-Culture - Assert-MockCalled -Exactly -Times 0 -CommandName Get-Website + Assert-MockCalled -Exactly -Times 2 -CommandName Get-Website -ModuleName PSWSIISEndpoint Assert-MockCalled -Exactly -Times 1 -CommandName Test-Path Assert-MockCalled -Exactly -Times 1 -CommandName Get-OSVersion - Assert-MockCalled -Exactly -Times 1 -CommandName New-PSWSEndpoint Assert-MockCalled -Exactly -Times 3 -CommandName Update-LocationTagInApplicationHostConfigForAuthentication Assert-MockCalled -Exactly -Times 5 -CommandName Set-AppSettingsInWebconfig Assert-MockCalled -Exactly -Times 1 -CommandName Set-BindingRedirectSettingInWebConfig @@ -716,6 +829,15 @@ try } It 'Should call expected mocks' { + Mock -CommandName Get-Website -MockWith { + [PSCustomObject]@{ + Name = $Name + State = 'Stopped' + applicationPool = 'PSWS' + physicalPath = 'TestDrive:\inetpub\PesterTestSite' + } + } -ModuleName PSWSIISEndpoint + Set-TargetResource @altTestParameters @setTargetPaths -Ensure Present -CertificateSubject 'PesterTestCertificate' Assert-MockCalled -Exactly -Times 1 -CommandName Find-CertificateThumbprintWithSubjectAndTemplateName @@ -739,6 +861,15 @@ try } It 'Should not throw an error' { + Mock -CommandName Get-Website -MockWith { + [PSCustomObject]@{ + Name = $Name + State = 'Stopped' + applicationPool = 'PSWS' + physicalPath = 'TestDrive:\inetpub\PesterTestSite' + } + } -ModuleName PSWSIISEndpoint + {Set-TargetResource @altTestParameters @setTargetPaths -Ensure Present} | Should -Not -throw } @@ -756,6 +887,175 @@ try {Set-TargetResource @altTestParameters} | Should -Throw } } + + Context -Name 'Verify Firewall handling' -Fixture { + + $setTargetPaths = @{ + DatabasePath = 'TestDrive:\Database' + ConfigurationPath = 'TestDrive:\Configuration' + ModulePath = 'TestDrive:\Module' + RegistrationKeyPath = 'TestDrive:\RegistrationKey' + } + + Mock -CommandName Remove-PSWSEndpoint + + It 'Should not create any firewall rules if disabled' { + $altTestParameters = $testParameters.Clone() + $altTestParameters.Ensure = 'Present' + $altTestParameters.ConfigureFirewall = $false + + Mock -CommandName Add-PullServerFirewallConfiguration + Mock -CommandName Get-Website -MockWith { $null } -ModuleName PSWSIISEndpoint + + Set-TargetResource @altTestParameters @setTargetPaths + + Assert-MockCalled -Exactly -Times 0 -CommandName Add-PullServerFirewallConfiguration + } + + It 'Should create firewall rules when enabled' { + $altTestParameters = $testParameters.Clone() + $altTestParameters.Ensure = 'Present' + $altTestParameters.ConfigureFirewall = $true + + Mock -CommandName Add-PullServerFirewallConfiguration + Mock -CommandName Get-Website -MockWith { $null } -ModuleName PSWSIISEndpoint + + Set-TargetResource @altTestParameters @setTargetPaths + Assert-MockCalled -Exactly -Times 1 -CommandName Add-PullServerFirewallConfiguration + } + + It 'Should always delete firewall rules which match the display internal name and port' { + $altTestParameters = $testParameters.Clone() + $altTestParameters.Ensure = 'Absent' + $altTestParameters.ConfigureFirewall = $true + + Mock -CommandName Get-Website -MockWith { + [PSCustomObject]@{ + Name = $Name + State = 'Stopped' + applicationPool = 'PSWS' + physicalPath = 'TestDrive:\inetpub\PesterTestSite' + } + } -ModuleName PSWSIISEndpoint + Mock -CommandName Get-WebBinding -MockWith { + [PSCustomObject]@{ + protocol = 'http' + bindingInformation = '*:8080:' + } + [PSCustomObject]@{ + protocol = 'http' + bindingInformation = '*:8090:' + } + [PSCustomObject]@{ + protocol = 'http' + bindingInformation = 'http://test.local/DSCPullServer:8010:' + } + } + Mock -CommandName Test-PullServerFirewallConfiguration -MockWith { $true } -ModuleName Firewall + Mock -CommandName Get-Command -ParameterFilter { $Name -eq 'Get-NetFirewallRule' } -MockWith { $true } -ModuleName Firewall + Mock -CommandName Get-NetFirewallRule -MockWith { + if ($DisplayName -notlike 'DSCPullServer_IIS_Port:*') + { + throw "Invalid DisplayName filter [$DisplayName] for Get-NetFirewallRule" + } + } -ModuleName Firewall + + Set-TargetResource @altTestParameters @setTargetPaths + + Assert-MockCalled -Exactly -Times 3 -CommandName Test-PullServerFirewallConfiguration -ModuleName Firewall + Assert-MockCalled -Exactly -Times 3 -CommandName Get-NetFirewallRule -ModuleName Firewall + } + } + + Context -Name 'Verify Application Pool handling' -Fixture { + + $setTargetPaths = @{ + DatabasePath = 'TestDrive:\Database' + ConfigurationPath = 'TestDrive:\Configuration' + ModulePath = 'TestDrive:\Module' + RegistrationKeyPath = 'TestDrive:\RegistrationKey' + } + + It 'Ensure is Absent - An AppPool still bound by an application should not be deleted' { + $altTestParameters = $testParameters.Clone() + $altTestParameters.Ensure = 'Absent' + + Mock -CommandName Get-Website -MockWith { + [PSCustomObject]@{ + Name = $Name + State = 'Stopped' + applicationPool = 'PSWS' + physicalPath = 'TestDrive:\inetpub\PesterTestSite' + } + } -ModuleName PSWSIISEndpoint + + Mock -CommandName Get-ChildItem ` + -ParameterFilter { $Path -eq 'TestDrive:\inetpub\PesterTestSite' } ` + -ModuleName PSWSIISEndpoint + + Mock -CommandName Get-AppPoolBinding ` + -MockWith { "Default Web Site"} ` + -ModuleName PSWSIISEndpoint + + Set-TargetResource @altTestParameters @setTargetPaths + + Assert-MockCalled -Exactly -Times 0 -CommandName Remove-WebAppPool -ModuleName PSWSIISEndpoint + } + + It 'Ensure is Present - No standard AppPool that does not exist should throw' { + $altTestParameters = $testParameters.Clone() + $altTestParameters.Ensure = 'Present' + $altTestParameters.ApplicationPoolName = 'NonExistingAppPool' + + Mock -CommandName Test-Path -ParameterFilter { $Path -eq 'IIS:\AppPools\NonExistingAppPool' } -MockWith { + $false + } + + { Set-TargetResource @altTestParameters @setTargetPaths } | Should -Throw + Assert-MockCalled -Exactly -Times 1 -CommandName Test-Path -Scope It + } + + It 'Ensure is Present - No standard AppPool will be created if an external AppPool is specified' { + $altTestParameters = $testParameters.Clone() + $altTestParameters.Ensure = 'Present' + $altTestParameters.ApplicationPoolName = 'PullServer AppPool' + + Mock -CommandName Test-Path -ParameterFilter { $Path -eq 'IIS:\AppPools\PullServer AppPool' } -MockWith { + $true + } + Mock -CommandName New-WebAppPool -ModuleName PSWSIISEndpoint + + Set-TargetResource @altTestParameters @setTargetPaths + + Assert-MockCalled -Exactly -Times 0 -CommandName New-WebAppPool -ModuleName PSWSIISEndpoint + } + + It 'Ensure is Absent - An externally defined AppPool should not be deleted' { + $altTestParameters = $testParameters.Clone() + $altTestParameters.Ensure = 'Absent' + $altTestParameters.ApplicationPoolName = 'PullServer AppPool' + + Mock -CommandName Get-Website -MockWith { + [PSCustomObject]@{ + Name = $Name + State = 'Stopped' + applicationPool = 'PullServer AppPool' + physicalPath = 'TestDrive:\inetpub\PesterTestSite' + } + } -ModuleName PSWSIISEndpoint + Mock -CommandName Test-Path -ParameterFilter { $Path -eq 'IIS:\AppPools\PullServer AppPool' } -MockWith { + $true + } -ModuleName PSWSIISEndpoint + Mock -CommandName Get-AppPoolBinding -MockWith { $null } -ModuleName PSWSIISEndpoint + Mock -CommandName Remove-WebAppPool -ModuleName PSWSIISEndpoint + + Set-TargetResource @altTestParameters @setTargetPaths + + Assert-MockCalled -Exactly -Times 0 -CommandName Test-Path -ModuleName PSWSIISEndpoint -ParameterFilter { $Path -eq 'IIS:\AppPools\PullServer AppPool' } -Scope It + Assert-MockCalled -Exactly -Times 0 -CommandName Get-AppPoolBinding -ModuleName PSWSIISEndpoint -Scope It + Assert-MockCalled -Exactly -Times 0 -CommandName Remove-WebAppPool -ModuleName PSWSIISEndpoint -Scope It + } + } } Describe -Name "$dscResourceName\Test-TargetResource" -Fixture { @@ -780,7 +1080,7 @@ try #endregion Context -Name 'DSC Service is not installed' -Fixture { - Mock -CommandName Get-Website + #Mock -CommandName Get-Website It 'Should return $true when Ensure is Absent' { Test-TargetResource @testParameters -Ensure Absent | Should -Be $true @@ -792,7 +1092,7 @@ try Context -Name 'DSC Web Service is installed as HTTP' -Fixture { Mock -CommandName Get-Website -MockWith {$WebsiteDataHTTP} - Mock -CommandName Test-PullServerFirewallConfiguration -MockWith { $true } + Mock -CommandName Test-PullServerFirewallConfiguration -MockWith { $false } It 'Should return $false when Ensure is Absent' { Test-TargetResource @testParameters -Ensure Absent | Should -Be $false @@ -967,7 +1267,7 @@ try Context -Name 'DSC Web Service is installed as HTTPS' -Fixture { #region Mocks Mock -CommandName Get-Website -MockWith {$websiteDataHTTPS} - Mock -CommandName Test-PullServerFirewallConfiguration -MockWith { $true } + Mock -CommandName Test-PullServerFirewallConfiguration -MockWith { $false } #endregion It 'Should return $false if Certificate Thumbprint is set to AllowUnencryptedTraffic' { @@ -1127,7 +1427,6 @@ try function Get-Website {} function Get-WebBinding {} - function Stop-Website {} $webConfigPath = 'TestDrive:\Web.config' @@ -1150,7 +1449,6 @@ try function Get-Website {} function Get-WebBinding {} - function Stop-Website {} $webConfigPath = 'TestDrive:\Web.config' @@ -1168,7 +1466,6 @@ try function Get-Website {} function Get-WebBinding {} - function Stop-Website {} $appHostConfigSection = [System.Management.Automation.PSObject] @{OverrideMode = ''} @@ -1192,7 +1489,6 @@ try function Get-Website {} function Get-WebBinding {} - function Stop-Website {} Mock -CommandName Get-ChildItem -MockWith {,@($certificateData)} From 8ef31c4b851d8429392162a0f9019f52426869ac Mon Sep 17 00:00:00 2001 From: Thomas Meckel Date: Sat, 4 May 2019 10:35:51 +0200 Subject: [PATCH 06/32] Reverted naming of PullServer firewall rule naming scheme --- DSCResources/MSFT_xDSCWebService/Firewall.psm1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DSCResources/MSFT_xDSCWebService/Firewall.psm1 b/DSCResources/MSFT_xDSCWebService/Firewall.psm1 index d6bd3a2a5..c58cd6243 100644 --- a/DSCResources/MSFT_xDSCWebService/Firewall.psm1 +++ b/DSCResources/MSFT_xDSCWebService/Firewall.psm1 @@ -1,5 +1,5 @@ # Name and description for the Firewall rules. Used in multiple locations -New-Variable -Name fireWallRuleDisplayName -Value 'DSCPullServer_IIS_Port:{0}' -Option ReadOnly -Scope Script -Force +New-Variable -Name fireWallRuleDisplayName -Value 'DSCPullServer_IIS_Port' -Option ReadOnly -Scope Script -Force New-Variable -Name netsh -Value "$env:windir\system32\netsh.exe" -Option ReadOnly -Scope Script -Force <# .SYNOPSIS From d5225db31b06eb0772bfaf501d033252e1404f8e Mon Sep 17 00:00:00 2001 From: Thomas Meckel Date: Sat, 4 May 2019 11:12:22 +0200 Subject: [PATCH 07/32] Corrected firewall display name filter in DSC PullServer unit test --- Tests/Unit/MSFT_xDSCWebService.Tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/Unit/MSFT_xDSCWebService.Tests.ps1 b/Tests/Unit/MSFT_xDSCWebService.Tests.ps1 index f40cde1a3..f75c742c3 100644 --- a/Tests/Unit/MSFT_xDSCWebService.Tests.ps1 +++ b/Tests/Unit/MSFT_xDSCWebService.Tests.ps1 @@ -954,7 +954,7 @@ try Mock -CommandName Test-PullServerFirewallConfiguration -MockWith { $true } -ModuleName Firewall Mock -CommandName Get-Command -ParameterFilter { $Name -eq 'Get-NetFirewallRule' } -MockWith { $true } -ModuleName Firewall Mock -CommandName Get-NetFirewallRule -MockWith { - if ($DisplayName -notlike 'DSCPullServer_IIS_Port:*') + if ($DisplayName -notlike 'DSCPullServer_IIS_Port*') { throw "Invalid DisplayName filter [$DisplayName] for Get-NetFirewallRule" } From 1fc7538a41b0ad14bab7f173ae0393787e019bf5 Mon Sep 17 00:00:00 2001 From: Thomas Meckel Date: Sat, 4 May 2019 12:07:02 +0200 Subject: [PATCH 08/32] Added DSC PullServer integration test to ensure clean removal of a deployed PullServer --- .../MSFT_xDSCWebService.Integration.tests.ps1 | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 b/Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 index 59a9b929d..e30f3f4f4 100644 --- a/Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 +++ b/Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 @@ -240,6 +240,23 @@ try } } + Context 'Verify clean removal' { + + BeforeAll { + Invoke-CommonResourceTesting -ConfigurationName $ensureAbsentConfigurationName + } + + Invoke-CommonResourceTesting -ConfigurationName 'MSFT_xDSCWebService_PullTestWithSecurityBestPractices_Config' + + Test-DSCPullServer -WebsiteName 'PSDSCPullServer' -ResourceState 'Present' -WebsiteState 'Started' + Test-DSCPullServerFirewallRule -RuleName 'DSCPullServer_IIS_Port' -State 'Present' + + Invoke-CommonResourceTesting -ConfigurationName $ensureAbsentConfigurationName + + Test-DSCPullServer -WebsiteName 'PSDSCPullServer' -ResourceState 'Absent' -WebsiteState 'DoesNotExist' + Test-DSCPullServerFirewallRule -RuleName 'DSCPullServer_IIS_Port' -State 'Absent' + } + Context 'No firewall configuration' { $configurationName = 'MSFT_xDSCWebService_PullTestWithoutFirewall_Config' From 6fa47bc58b42f4b10632c88d9e0b1ec5d23db413 Mon Sep 17 00:00:00 2001 From: Thomas Meckel Date: Sat, 4 May 2019 13:25:10 +0200 Subject: [PATCH 09/32] Corrected Test-DSCPullServer --- .../MSFT_xDSCWebService.Integration.tests.ps1 | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 b/Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 index e30f3f4f4..e1325f876 100644 --- a/Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 +++ b/Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 @@ -116,13 +116,13 @@ function Test-DSCPullServer $WebsiteState ) - It 'Should create a web.config file at the web site root' { - Test-Path -Path (Join-Path -Path $ConfigurationData.AllNodes.PhysicalPath -ChildPath 'web.config') | Should -Be $true - } - switch ($ResourceState) { 'Present' { + It 'Should create a web.config file at the web site root' { + Test-Path -Path (Join-Path -Path $ConfigurationData.AllNodes.PhysicalPath -ChildPath 'web.config') | Should -Be $true + } + It ("Should exist a WebSite called $WebsiteName") { Get-WebSite -Name $WebsiteName | Should Not Be $null } @@ -212,6 +212,14 @@ try #region Integration Tests $configurationFile = Join-Path -Path $PSScriptRoot -ChildPath "$($script:dcsResourceName).config.ps1" + $requiredModules = Get-ResourceModulesInConfiguration -ConfigurationPath $exampleToValidate.FullName | + Where-Object -Property Name -ne $script:dscModuleName + + if ($requiredModules) + { + Install-DependentModule -Module $requiredModules + } + . $configurationFile Describe "$($script:dcsResourceName)_Integration" { From b794ec5de9a4f1269cfdbee29e60104cf8afab04 Mon Sep 17 00:00:00 2001 From: Thomas Meckel Date: Sat, 4 May 2019 13:26:06 +0200 Subject: [PATCH 10/32] Added additional integration test DSC configuration to MSFT_xDSCWebService --- .../MSFT_xDSCWebService.config.ps1 | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/Tests/Integration/MSFT_xDSCWebService.config.ps1 b/Tests/Integration/MSFT_xDSCWebService.config.ps1 index 5e9f78451..27ee7ce08 100644 --- a/Tests/Integration/MSFT_xDSCWebService.config.ps1 +++ b/Tests/Integration/MSFT_xDSCWebService.config.ps1 @@ -142,3 +142,90 @@ Configuration MSFT_xDSCWebService_PullTestWithoutFirewall_Config } } } + +<# + .SYNOPSIS + Sets up a DSC pull server without firewall exceptions +#> +Configuration MSFT_xDSCWebService_PullTestWithExternalFirewall_Config +{ + Import-DscResource -ModuleName 'xPSDesiredStateConfiguration' + Import-DscResource -ModuleName 'NetworkingDsc' + + node $AllNodes.NodeName + { + + xDSCWebService Integration_Test + { + Ensure = 'Present' + AcceptSelfSignedCertificates = $true + CertificateThumbPrint = $Node.CertificateThumbprint + ConfigurationPath = $Node.ConfigurationPath + Enable32BitAppOnWin64 = $false + EndpointName = $Node.EndpointName + ModulePath = $Node.ModulePath + Port = $Node.Port + PhysicalPath = $Node.PhysicalPath + RegistrationKeyPath = $Node.RegistrationKeyPath + State = 'Started' + UseSecurityBestPractices = $true + ConfigureFirewall = $false + DependsOn = '[Firewall]DSC-FirewallRule' + } + + Firewall PSDSCPullServerRule + { + Ensure = 'Present' + Name = "DSC_PullServer_$Port" + DisplayName = "DSC PullServer $Port" + Group = 'DSC PullServer' + Enabled = $true + Action = 'Allow' + Direction = 'InBound' + LocalPort = $Node.Port + Protocol = 'TCP' + DependsOn = '[xDscWebService]Integration_Test' + } + + } +} + +<# + .SYNOPSIS + Sets up a DSC pull server with an separately defined application pool +#> +Configuration MSFT_xDSCWebService_PullTestExternalAppPool_Config +{ + Import-DscResource -ModuleName 'xPSDesiredStateConfiguration' + Import-DscResource -ModuleName 'xWebAdministration' + + node $AllNodes.NodeName + { + + xWebAppPool PSDSCPullServerPool + { + Ensure = 'Present' + Name = "PSDSCPullServer_$($Node.EndpointName)" + IdentityType = 'NetworkService' + } + + xDSCWebService Integration_Test + { + Ensure = 'Present' + AcceptSelfSignedCertificates = $true + CertificateThumbPrint = $Node.CertificateThumbprint + ConfigurationPath = $Node.ConfigurationPath + Enable32BitAppOnWin64 = $false + EndpointName = $Node.EndpointName + ModulePath = $Node.ModulePath + Port = $Node.Port + PhysicalPath = $Node.PhysicalPath + RegistrationKeyPath = $Node.RegistrationKeyPath + State = 'Started' + UseSecurityBestPractices = $true + ConfigureFirewall = $false + DependsOn = '[xWebAppPool]PSDSCPullServerPool' + } + + } +} From 5f09edc1b33ce13f9f969b2b68b479a8f4726330 Mon Sep 17 00:00:00 2001 From: Thomas Meckel Date: Sat, 4 May 2019 14:37:54 +0200 Subject: [PATCH 11/32] Corrected test initialization in MSFT_xDSCWebService.Integration.tests.ps1 --- Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 b/Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 index e1325f876..15438934b 100644 --- a/Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 +++ b/Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 @@ -212,7 +212,7 @@ try #region Integration Tests $configurationFile = Join-Path -Path $PSScriptRoot -ChildPath "$($script:dcsResourceName).config.ps1" - $requiredModules = Get-ResourceModulesInConfiguration -ConfigurationPath $exampleToValidate.FullName | + $requiredModules = Get-ResourceModulesInConfiguration -ConfigurationPath $configurationFile.FullName | Where-Object -Property Name -ne $script:dscModuleName if ($requiredModules) From 2d5efad3a788d308e37baf63e91ff40f00929ded Mon Sep 17 00:00:00 2001 From: Thomas Meckel Date: Sat, 4 May 2019 14:53:14 +0200 Subject: [PATCH 12/32] Corrcted path reference in MSFT_xDSCWebService.Integration.tests.ps1 --- Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 b/Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 index 15438934b..c9fb5c031 100644 --- a/Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 +++ b/Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 @@ -212,7 +212,7 @@ try #region Integration Tests $configurationFile = Join-Path -Path $PSScriptRoot -ChildPath "$($script:dcsResourceName).config.ps1" - $requiredModules = Get-ResourceModulesInConfiguration -ConfigurationPath $configurationFile.FullName | + $requiredModules = Get-ResourceModulesInConfiguration -ConfigurationPath $configurationFile | Where-Object -Property Name -ne $script:dscModuleName if ($requiredModules) From 023dc3a71fb626ec2de7caafe969057c33830aab Mon Sep 17 00:00:00 2001 From: Thomas Meckel Date: Sat, 4 May 2019 16:14:06 +0200 Subject: [PATCH 13/32] MSFT_xDSCWebService: Added new integration tests for firewall and applicaiton pool handling --- .../MSFT_xDSCWebService.Integration.tests.ps1 | 95 ++++++++++++++++++- .../MSFT_xDSCWebService.config.ps1 | 16 ++-- 2 files changed, 102 insertions(+), 9 deletions(-) diff --git a/Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 b/Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 index c9fb5c031..39a2750ea 100644 --- a/Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 +++ b/Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 @@ -81,6 +81,45 @@ function Invoke-CommonResourceTesting } } +<# + .SYNOPSIS + Tests if the specified IIS application pool is absent or present + + .PARAMETER ApplicationPoolName + name of the IIS application pool + + .PARAMETER ResourceState + state of the IIS application pool +#> +function Test-IISApplicationPool +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $ApplicationPoolName, + + [Parameter(Mandatory = $true)] + [ValidateSet('Present', 'Absent')] + [System.String] + $ResourceState + ) + + $appPoolPath = Join-Path -Path 'IIS:\AppPools' -ChildPath $ApplicationPoolName + + switch ($ResourceState) + { + 'Present' { + Test-Path -Path $appPoolPath + } + 'Absent' { + -not (Test-Path -Path $appPoolPath) + } + } +} + <# .SYNOPSIS Performs common tests to ensure that the DSC pull server was properly @@ -94,6 +133,9 @@ function Invoke-CommonResourceTesting .PARAMETER WebsiteState State of the website + + .PARAMETER ApplicationPoolName + name of the IIS application pool #> function Test-DSCPullServer { @@ -113,7 +155,12 @@ function Test-DSCPullServer [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] - $WebsiteState + $WebsiteState, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [System.String] + $ApplicationPoolName = 'PSWS' ) switch ($ResourceState) @@ -132,6 +179,16 @@ function Test-DSCPullServer $website | Should Not Be $null $website.state | Should BeExactly $WebsiteState } + + It "IIS Application pool $ApplicationPoolName should exist" { + Test-IISApplicationPool -ApplicationPoolName $ApplicationPoolName -ResourceState 'Present' | Should -Be $true + } + + It "WebSite $WebsiteName should be bound to IIS applicaiton pool $ApplicationPoolName" { + $website = Get-WebSite -Name $WebsiteName + $website | Should Not Be $null + $website.applicationPool | Should BeExactly $ApplicationPoolName + } } 'Absent' { @@ -261,7 +318,7 @@ try Invoke-CommonResourceTesting -ConfigurationName $ensureAbsentConfigurationName - Test-DSCPullServer -WebsiteName 'PSDSCPullServer' -ResourceState 'Absent' -WebsiteState 'DoesNotExist' + Test-DSCPullServer -WebsiteName 'PSDSCPullServer' -ResourceState 'Absent' -WebsiteState 'Absent' Test-DSCPullServerFirewallRule -RuleName 'DSCPullServer_IIS_Port' -State 'Absent' } @@ -281,6 +338,40 @@ try Test-DSCPullServer -WebsiteName 'PSDSCPullServer' -ResourceState 'Present' -WebsiteState 'Started' Test-DSCPullServerFirewallRule -RuleName 'DSCPullServer_IIS_Port' -State 'Absent' } + + Context 'Separate firewall role definition' { + + BeforeAll { + Invoke-CommonResourceTesting -ConfigurationName $ensureAbsentConfigurationName + } + + Invoke-CommonResourceTesting -ConfigurationName 'MSFT_xDSCWebService_PullTestWithSeparateFirewallRule_Config' + Test-DSCPullServer -WebsiteName 'PSDSCPullServer' -ResourceState 'Present' -WebsiteState 'Started' + Test-DSCPullServerFirewallRule -RuleName 'DSCPullServer_IIS_Port' -State 'Absent' + Test-DSCPullServerFirewallRule -RuleName 'DSC_PullServer_8080' -State 'Present' + + + Invoke-CommonResourceTesting -ConfigurationName $ensureAbsentConfigurationName + Test-DSCPullServer -WebsiteName 'PSDSCPullServer' -ResourceState 'Absent' -WebsiteState 'Absent' + Test-DSCPullServerFirewallRule -RuleName 'DSCPullServer_IIS_Port' -State 'Absent' + Test-DSCPullServerFirewallRule -RuleName 'DSC_PullServer_8080' -State 'Present' + } + + Context 'Separate IIS application pool definition' { + + BeforeAll { + Invoke-CommonResourceTesting -ConfigurationName $ensureAbsentConfigurationName + } + + Invoke-CommonResourceTesting -ConfigurationName 'MSFT_xDSCWebService_PullTestSeparateAppPool_Config' + Test-DSCPullServer -WebsiteName 'PSDSCPullServer' -ResourceState 'Present' -WebsiteState 'Started' -ApplicationPoolName 'PSDSCPullServer_PSDSCPullServer' + Test-DSCPullServerFirewallRule -RuleName 'DSCPullServer_IIS_Port' -State 'Absent' + + Invoke-CommonResourceTesting -ConfigurationName $ensureAbsentConfigurationName + Test-DSCPullServer -WebsiteName 'PSDSCPullServer' -ResourceState 'Absent' -WebsiteState 'Absent' + Test-DSCPullServerFirewallRule -RuleName 'DSCPullServer_IIS_Port' -State 'Absent' + Test-IISApplicationPool -ApplicationPoolName 'PSDSCPullServer_PSDSCPullServer' -State 'Present' + } } #endregion diff --git a/Tests/Integration/MSFT_xDSCWebService.config.ps1 b/Tests/Integration/MSFT_xDSCWebService.config.ps1 index 27ee7ce08..ea7f2ff44 100644 --- a/Tests/Integration/MSFT_xDSCWebService.config.ps1 +++ b/Tests/Integration/MSFT_xDSCWebService.config.ps1 @@ -145,9 +145,9 @@ Configuration MSFT_xDSCWebService_PullTestWithoutFirewall_Config <# .SYNOPSIS - Sets up a DSC pull server without firewall exceptions + Sets up a DSC pull server with a separate firewall rule definition #> -Configuration MSFT_xDSCWebService_PullTestWithExternalFirewall_Config +Configuration MSFT_xDSCWebService_PullTestWithSeparateFirewallRule_Config { Import-DscResource -ModuleName 'xPSDesiredStateConfiguration' Import-DscResource -ModuleName 'NetworkingDsc' @@ -170,14 +170,13 @@ Configuration MSFT_xDSCWebService_PullTestWithExternalFirewall_Config State = 'Started' UseSecurityBestPractices = $true ConfigureFirewall = $false - DependsOn = '[Firewall]DSC-FirewallRule' } Firewall PSDSCPullServerRule { Ensure = 'Present' - Name = "DSC_PullServer_$Port" - DisplayName = "DSC PullServer $Port" + Name = "DSC_PullServer_$($Node.Port)" + DisplayName = "DSC PullServer $($Node.Port)" Group = 'DSC PullServer' Enabled = $true Action = 'Allow' @@ -194,7 +193,7 @@ Configuration MSFT_xDSCWebService_PullTestWithExternalFirewall_Config .SYNOPSIS Sets up a DSC pull server with an separately defined application pool #> -Configuration MSFT_xDSCWebService_PullTestExternalAppPool_Config +Configuration MSFT_xDSCWebService_PullTestSeparateAppPool_Config { Import-DscResource -ModuleName 'xPSDesiredStateConfiguration' Import-DscResource -ModuleName 'xWebAdministration' @@ -202,10 +201,12 @@ Configuration MSFT_xDSCWebService_PullTestExternalAppPool_Config node $AllNodes.NodeName { + $ApplicationPool_Name = "PSDSCPullServer_$($Node.EndpointName)" + xWebAppPool PSDSCPullServerPool { Ensure = 'Present' - Name = "PSDSCPullServer_$($Node.EndpointName)" + Name = $ApplicationPool_Name IdentityType = 'NetworkService' } @@ -216,6 +217,7 @@ Configuration MSFT_xDSCWebService_PullTestExternalAppPool_Config CertificateThumbPrint = $Node.CertificateThumbprint ConfigurationPath = $Node.ConfigurationPath Enable32BitAppOnWin64 = $false + ApplicationPoolName = $ApplicationPool_Name EndpointName = $Node.EndpointName ModulePath = $Node.ModulePath Port = $Node.Port From 421632adf89f947e6bd94b10e5161ea7455ca60c Mon Sep 17 00:00:00 2001 From: Thomas Meckel Date: Sat, 4 May 2019 16:37:12 +0200 Subject: [PATCH 14/32] MSFT_xDSCWebService: fixed bugs in new integration tests --- .../MSFT_xDSCWebService.Integration.tests.ps1 | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 b/Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 index 39a2750ea..e4b8651f9 100644 --- a/Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 +++ b/Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 @@ -206,7 +206,7 @@ function Test-DSCPullServer .PARAMETER RuleName name of the firewall rule - .PARAMETER State + .PARAMETER ResourceState state of the rule #> function Test-DSCPullServerFirewallRule @@ -222,18 +222,18 @@ function Test-DSCPullServerFirewallRule [Parameter(Mandatory = $true)] [ValidateSet('Present', 'Absent')] [System.String] - $State + $ResourceState ) - Write-Verbose -Message "Test-DSCPullServerFirewallRule $RuleName for state $State." + Write-Verbose -Message "Test-DSCPullServerFirewallRule $RuleName for state $ResourceState." $expectedRuleCount = 0 - if ('Present' -eq $State) + if ('Present' -eq $ResourceState) { $expectedRuleCount = 1 } - It ("Should $(if ('Present' -eq $State) { '' } else { 'not ' })create a firewall rule $RuleName for the chosen port") { + It ("Should $(if ('Present' -eq $ResourceState) { '' } else { 'not ' })create a firewall rule $RuleName for the chosen port") { $ruleCnt = (Get-NetFirewallRule | Where-Object -FilterScript { $_.DisplayName -eq $RuleName #'DSCPullServer_IIS_Port' } | Measure-Object).Count @@ -301,7 +301,7 @@ try Invoke-CommonResourceTesting -ConfigurationName $configurationName Test-DSCPullServer -WebsiteName 'PSDSCPullServer' -ResourceState 'Present' -WebsiteState 'Started' - Test-DSCPullServerFirewallRule -RuleName 'DSCPullServer_IIS_Port' -State 'Present' + Test-DSCPullServerFirewallRule -RuleName 'DSCPullServer_IIS_Port' -ResourceState 'Present' } } @@ -314,12 +314,12 @@ try Invoke-CommonResourceTesting -ConfigurationName 'MSFT_xDSCWebService_PullTestWithSecurityBestPractices_Config' Test-DSCPullServer -WebsiteName 'PSDSCPullServer' -ResourceState 'Present' -WebsiteState 'Started' - Test-DSCPullServerFirewallRule -RuleName 'DSCPullServer_IIS_Port' -State 'Present' + Test-DSCPullServerFirewallRule -RuleName 'DSCPullServer_IIS_Port' -ResourceState 'Present' Invoke-CommonResourceTesting -ConfigurationName $ensureAbsentConfigurationName Test-DSCPullServer -WebsiteName 'PSDSCPullServer' -ResourceState 'Absent' -WebsiteState 'Absent' - Test-DSCPullServerFirewallRule -RuleName 'DSCPullServer_IIS_Port' -State 'Absent' + Test-DSCPullServerFirewallRule -RuleName 'DSCPullServer_IIS_Port' -ResourceState 'Absent' } Context 'No firewall configuration' { @@ -336,7 +336,7 @@ try Invoke-CommonResourceTesting -ConfigurationName $configurationName Test-DSCPullServer -WebsiteName 'PSDSCPullServer' -ResourceState 'Present' -WebsiteState 'Started' - Test-DSCPullServerFirewallRule -RuleName 'DSCPullServer_IIS_Port' -State 'Absent' + Test-DSCPullServerFirewallRule -RuleName 'DSCPullServer_IIS_Port' -ResourceState 'Absent' } Context 'Separate firewall role definition' { @@ -347,14 +347,14 @@ try Invoke-CommonResourceTesting -ConfigurationName 'MSFT_xDSCWebService_PullTestWithSeparateFirewallRule_Config' Test-DSCPullServer -WebsiteName 'PSDSCPullServer' -ResourceState 'Present' -WebsiteState 'Started' - Test-DSCPullServerFirewallRule -RuleName 'DSCPullServer_IIS_Port' -State 'Absent' - Test-DSCPullServerFirewallRule -RuleName 'DSC_PullServer_8080' -State 'Present' + Test-DSCPullServerFirewallRule -RuleName 'DSCPullServer_IIS_Port' -ResourceState 'Absent' + Test-DSCPullServerFirewallRule -RuleName 'DSC PullServer 8080' -ResourceState 'Present' Invoke-CommonResourceTesting -ConfigurationName $ensureAbsentConfigurationName Test-DSCPullServer -WebsiteName 'PSDSCPullServer' -ResourceState 'Absent' -WebsiteState 'Absent' - Test-DSCPullServerFirewallRule -RuleName 'DSCPullServer_IIS_Port' -State 'Absent' - Test-DSCPullServerFirewallRule -RuleName 'DSC_PullServer_8080' -State 'Present' + Test-DSCPullServerFirewallRule -RuleName 'DSCPullServer_IIS_Port' -ResourceState 'Absent' + Test-DSCPullServerFirewallRule -RuleName 'DSC PullServer 8080' -ResourceState 'Present' } Context 'Separate IIS application pool definition' { @@ -365,12 +365,12 @@ try Invoke-CommonResourceTesting -ConfigurationName 'MSFT_xDSCWebService_PullTestSeparateAppPool_Config' Test-DSCPullServer -WebsiteName 'PSDSCPullServer' -ResourceState 'Present' -WebsiteState 'Started' -ApplicationPoolName 'PSDSCPullServer_PSDSCPullServer' - Test-DSCPullServerFirewallRule -RuleName 'DSCPullServer_IIS_Port' -State 'Absent' + Test-DSCPullServerFirewallRule -RuleName 'DSCPullServer_IIS_Port' -ResourceState 'Absent' Invoke-CommonResourceTesting -ConfigurationName $ensureAbsentConfigurationName Test-DSCPullServer -WebsiteName 'PSDSCPullServer' -ResourceState 'Absent' -WebsiteState 'Absent' - Test-DSCPullServerFirewallRule -RuleName 'DSCPullServer_IIS_Port' -State 'Absent' - Test-IISApplicationPool -ApplicationPoolName 'PSDSCPullServer_PSDSCPullServer' -State 'Present' + Test-DSCPullServerFirewallRule -RuleName 'DSCPullServer_IIS_Port' -ResourceState 'Absent' + Test-IISApplicationPool -ApplicationPoolName 'PSDSCPullServer_PSDSCPullServer' -ResourceState 'Present' } } #endregion From a4ec2275a09a7315e08d448468eb036b49d9b813 Mon Sep 17 00:00:00 2001 From: Thomas Meckel Date: Sat, 4 May 2019 18:16:05 +0200 Subject: [PATCH 15/32] MSFT_xDSCWebService: corrected integration test --- Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 b/Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 index e4b8651f9..8547d5a7f 100644 --- a/Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 +++ b/Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 @@ -370,7 +370,11 @@ try Invoke-CommonResourceTesting -ConfigurationName $ensureAbsentConfigurationName Test-DSCPullServer -WebsiteName 'PSDSCPullServer' -ResourceState 'Absent' -WebsiteState 'Absent' Test-DSCPullServerFirewallRule -RuleName 'DSCPullServer_IIS_Port' -ResourceState 'Absent' - Test-IISApplicationPool -ApplicationPoolName 'PSDSCPullServer_PSDSCPullServer' -ResourceState 'Present' + + It "Separately created IIS Application pool should still exist after cleanup" { + Test-IISApplicationPool -ApplicationPoolName 'PSDSCPullServer_PSDSCPullServer' -ResourceState 'Present' | Should -Be $true + } + } } #endregion From 6a034e04b347a47ebba9241949aacc4110d866bc Mon Sep 17 00:00:00 2001 From: Thomas Meckel Date: Sat, 4 May 2019 18:16:29 +0200 Subject: [PATCH 16/32] MSFT_xDSCWebService: added Changelog, Readme documentation and a new sample --- CHANGELOG.md | 3 + Examples/Sample_xDscWebServiceAppPool.ps1 | 226 ++++++++++++++++++++++ README.md | 15 ++ 3 files changed, 244 insertions(+) create mode 100644 Examples/Sample_xDscWebServiceAppPool.ps1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 4aebbda26..d2e4746a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,9 @@ #536](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/536) and starts the deprecation process for configuring a windows firewall (exception) rule using xDSCWebService + - Fixes [issue + #463](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/463) + and fixes some bugs introduced with the new firewall rule handling ## 8.6.0.0 diff --git a/Examples/Sample_xDscWebServiceAppPool.ps1 b/Examples/Sample_xDscWebServiceAppPool.ps1 new file mode 100644 index 000000000..cafbcf93f --- /dev/null +++ b/Examples/Sample_xDscWebServiceAppPool.ps1 @@ -0,0 +1,226 @@ +<#PSScriptInfo +.VERSION 1.0.0 +.GUID c0a8626d-0f4f-469d-8f20-b79f860edc09 +.AUTHOR Microsoft Corporation +.COMPANYNAME Microsoft Corporation +.COPYRIGHT +.TAGS DSCConfiguration +.LICENSEURI https://github.com/PowerShell/xPSDesiredStateConfiguration/blob/master/LICENSE +.PROJECTURI https://github.com/PowerShell/xPSDesiredStateConfiguration +.ICONURI +.EXTERNALMODULEDEPENDENCIES NetworkingDsc, xPSDesiredStateConfiguration +.REQUIREDSCRIPTS +.EXTERNALSCRIPTDEPENDENCIES +#> +<# + .SYNOPSIS + Configures a DSC Pull Server with an separately configured IIS Application Pool + + .DESCRIPTION + The Sample_xDscWebServiceRegistration configuration sets up a DSC pull + server that is capable for client nodes to register with it and + retrieve configuration documents with configuration names instead of + configuration id. + + Prerequisite: 1 - Install a certificate in 'CERT:\LocalMachine\MY\' store + For testing environments, you could use a self-signed + certificate. (New-SelfSignedCertificate cmdlet could + generate one for you). For production environments, you + will need a certificate signed by valid CA. Registration + only works over https protocols. So to use registration + feature, a secure pull server setup with certificate is + necessary. + 2 - To configure a Firewall Rule (Exception) to allow external + connections the [NetworkingDsc](https://github.com/PowerShell/NetworkingDsc) + DSC module is required. + + .PARAMETER NodeName + The name of the node being configured as a DSC Pull Server. + + .PARAMETER CertificateThumbPrint + Certificate thumbprint for creating an HTTPS endpoint. Use + "AllowUnencryptedTraffic" for setting up a non SSL based endpoint. + + .PARAMETER RegistrationKey + This key will be used by client nodes as a shared key to authenticate + during registration. This should be a string with enough entropy + (randomness) to protect the registration of clients to the pull server. + The example creates a new GUID for the registration key. + + .PARAMETER Port + The TCP port on which the Pull Server will listen for connections + + .PARAMETER ApplicationPoolName + The IIS Application Pool to use with the new Pull Server + + .EXAMPLE + $thumbprint = (New-SelfSignedCertificate -DnsName $env:COMPUTERNAME -CertStoreLocation Cert:\LocalMachine\My).Thumbprint + $registrationKey = [System.Guid]::NewGuid() + + Sample_xDscWebServiceRegistration -RegistrationKey $registrationkey -CertificateThumbPrint $thumbprint +#> +Configuration Sample_xDscWebServiceRegistration +{ + param + ( + [Parameter()] + [System.String[]] + $NodeName = 'localhost', + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $CertificateThumbPrint, + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $RegistrationKey, + + [Parameter()] + [ValidateRange(1, 65535)] + [System.UInt16] + $Port = 8080, + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $ApplicationPoolName + ) + + Import-DscResource -ModuleName 'NetworkingDsc' + Import-DSCResource -ModuleName 'xPSDesiredStateConfiguration' + Import-DscResource -ModuleName 'xWebAdministration' + + Node $NodeName + { + WindowsFeature DSCServiceFeature + { + Ensure = 'Present' + Name = 'DSC-Service' + } + + xWebAppPool PSDSCPullServerPool + { + Ensure = 'Present' + Name = $ApplicationPoolName + IdentityType = 'NetworkService' + } + + xDscWebService PSDSCPullServer + { + Ensure = 'Present' + EndpointName = 'PSDSCPullServer' + ApplicationPoolName = $ApplicationPoolName + Port = $Port + PhysicalPath = "$env:SystemDrive\inetpub\PSDSCPullServer" + CertificateThumbPrint = $CertificateThumbPrint + ModulePath = "$env:PROGRAMFILES\WindowsPowerShell\DscService\Modules" + ConfigurationPath = "$env:PROGRAMFILES\WindowsPowerShell\DscService\Configuration" + State = 'Started' + DependsOn = '[WindowsFeature]DSCServiceFeature' + RegistrationKeyPath = "$env:PROGRAMFILES\WindowsPowerShell\DscService" + AcceptSelfSignedCertificates = $true + Enable32BitAppOnWin64 = $false + UseSecurityBestPractices = $true + ConfigureFirewall = $false + DependsOn = '[xWebAppPool]PSDSCPullServerPool' + } + + File RegistrationKeyFile + { + Ensure = 'Present' + Type = 'File' + DestinationPath = "$env:ProgramFiles\WindowsPowerShell\DscService\RegistrationKeys.txt" + Contents = $RegistrationKey + } + + Firewall PSDSCPullServerRule + { + Ensure = 'Present' + Name = "DSC_PullServer_$Port" + DisplayName = "DSC PullServer $Port" + Group = 'DSC PullServer' + Enabled = $true + Action = 'Allow' + Direction = 'InBound' + LocalPort = $Port + Protocol = 'TCP' + DependsOn = '[xDscWebService]PSDSCPullServer' + } + } +} + +<# + .SYNOPSIS + The Sample_MetaConfigurationToRegisterWithSecurePullServer registers + a DSC client node with the pull server. + + .PARAMETER NodeName + The name of the node being configured as a DSC Pull Server. + + .PARAMETER RegistrationKey + This key will be used by client nodes as a shared key to authenticate + during registration. This should be a string with enough entropy + (randomness) to protect the registration of clients to the pull server. + The example creates a new GUID for the registration key. + + .PARAMETER ServerName + The HostName to use when configuring the Pull Server URL on the DSC + client. + + .PARAMETER Port + The port on which the PullServer is listening for connections + + .EXAMPLE + $registrationKey = [System.Guid]::NewGuid() + + Sample_MetaConfigurationToRegisterWithSecurePullServer -RegistrationKey $registrationKey +#> +[DSCLocalConfigurationManager()] +Configuration Sample_MetaConfigurationToRegisterWithSecurePullServer +{ + param + ( + [Parameter()] + [ValidateNotNullOrEmpty()] + [System.String] + $NodeName = 'localhost', + + [Parameter()] + [Parameter(Mandatory = $true)] + [System.String] + $RegistrationKey, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [System.String] + $ServerName = 'localhost', + + [Parameter()] + [ValidateRange(1, 65535)] + [System.UInt16] + $Port = 8080 + ) + + Node $NodeName + { + Settings + { + RefreshMode = 'Pull' + } + + ConfigurationRepositoryWeb CONTOSO-PullSrv + { + ServerURL = "https://$ServerName`:$Port/PSDSCPullServer.svc" + RegistrationKey = $RegistrationKey + ConfigurationNames = @('ClientConfig') + } + + ReportServerWeb CONTOSO-PullSrv + { + ServerURL = "https://$ServerName`:$Port/PSDSCPullServer.svc" + RegistrationKey = $RegistrationKey + } + } +} diff --git a/README.md b/README.md index 2f17865e0..50385b559 100644 --- a/README.md +++ b/README.md @@ -166,6 +166,12 @@ None endpoint. Use "AllowUnencryptedTraffic" for setting up a non SSL based endpoint. * **Port**: Port for web service. +* **ApplicationPoolName**: The name of IIS ApplicationPool to use for the Pull + Server. If not specified a pool with name 'PSWS' will be created and bound to + the Pull Server instance. Preferably the new application pool is created by + using the __xWebAppPool__ resource from the + [xWebAdministration](https://github.com/PowerShell/xWebAdministration) DSC + module * **PhysicalPath**: Folder location where the content of the web service resides. * **Ensure**: Ensures that the web service is **Present** or **Absent** @@ -209,6 +215,15 @@ All users are requested to adjust existing configurations so that the is created by using the **Firewall** resource from the [NetworkingDsc](https://github.com/PowerShell/NetworkingDsc) module. +#### Examples + +* [A Pull Server using a SQL Server as backend](./Examples/Sample_xDscWebServiceRegistration_UseSQLProvider.ps1) +* [A Pull Server default configuration](./Examples/Sample_xDscWebServiceRegistration_Win2k12and2k12R2.ps1) +* [A Pull Server with an enhanced security configuration and a firewall rule](./Examples/Sample_xDscWebServiceRegistration.ps1) +* [A Pull Server using the security best practice configuration](./Examples/Sample_xDscWebServiceRegistrationWithSecurityBestPractices.ps1) +* [Removing a Pull Server instance](./Examples/Sample_xDscWebServiceRemoval.ps1) +* [A Pull Server with a separately defined IIS Application Pool](./Examples/Sample_xDscWebServiceAppPool.ps1) + ### xGroup Provides a mechanism to manage local groups on the target node. From 4aba9349e1de5f35c5259017d2b37fa92a75b9cd Mon Sep 17 00:00:00 2001 From: Thomas Meckel Date: Sat, 4 May 2019 18:28:50 +0200 Subject: [PATCH 17/32] MSFT_xDSCWebService: correct an error in Sample_xDscWebServiceAppPool.ps1 --- Examples/Sample_xDscWebServiceAppPool.ps1 | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Examples/Sample_xDscWebServiceAppPool.ps1 b/Examples/Sample_xDscWebServiceAppPool.ps1 index cafbcf93f..07a1a4df9 100644 --- a/Examples/Sample_xDscWebServiceAppPool.ps1 +++ b/Examples/Sample_xDscWebServiceAppPool.ps1 @@ -118,13 +118,12 @@ Configuration Sample_xDscWebServiceRegistration ModulePath = "$env:PROGRAMFILES\WindowsPowerShell\DscService\Modules" ConfigurationPath = "$env:PROGRAMFILES\WindowsPowerShell\DscService\Configuration" State = 'Started' - DependsOn = '[WindowsFeature]DSCServiceFeature' RegistrationKeyPath = "$env:PROGRAMFILES\WindowsPowerShell\DscService" AcceptSelfSignedCertificates = $true Enable32BitAppOnWin64 = $false UseSecurityBestPractices = $true ConfigureFirewall = $false - DependsOn = '[xWebAppPool]PSDSCPullServerPool' + DependsOn = '[WindowsFeature]DSCServiceFeature', '[xWebAppPool]PSDSCPullServerPool' } File RegistrationKeyFile From ae102b33d535790d45f4da00c64c755e695921d9 Mon Sep 17 00:00:00 2001 From: Thomas Meckel Date: Sat, 4 May 2019 18:33:41 +0200 Subject: [PATCH 18/32] MSFT_xDSCWebService: corrected a meta test error in Sample_xDscWebServiceAppPool.ps1 --- Examples/Sample_xDscWebServiceAppPool.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Examples/Sample_xDscWebServiceAppPool.ps1 b/Examples/Sample_xDscWebServiceAppPool.ps1 index 07a1a4df9..61d6b3e80 100644 --- a/Examples/Sample_xDscWebServiceAppPool.ps1 +++ b/Examples/Sample_xDscWebServiceAppPool.ps1 @@ -59,7 +59,7 @@ Sample_xDscWebServiceRegistration -RegistrationKey $registrationkey -CertificateThumbPrint $thumbprint #> -Configuration Sample_xDscWebServiceRegistration +Configuration Sample_xDscWebServiceAppPool { param ( From de65a11e71449ea457fc1a88824bbc87551afafd Mon Sep 17 00:00:00 2001 From: Thomas Meckel Date: Sat, 4 May 2019 18:36:40 +0200 Subject: [PATCH 19/32] MSFT_xDSCWebService: corrected the PSScriptInfo and description of Sample_xDscWebServiceAppPool.ps1 --- Examples/Sample_xDscWebServiceAppPool.ps1 | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Examples/Sample_xDscWebServiceAppPool.ps1 b/Examples/Sample_xDscWebServiceAppPool.ps1 index 61d6b3e80..a95fe660f 100644 --- a/Examples/Sample_xDscWebServiceAppPool.ps1 +++ b/Examples/Sample_xDscWebServiceAppPool.ps1 @@ -1,6 +1,6 @@ <#PSScriptInfo .VERSION 1.0.0 -.GUID c0a8626d-0f4f-469d-8f20-b79f860edc09 +.GUID 4321b681-da05-4486-a7db-1ce4842d40c5 .AUTHOR Microsoft Corporation .COMPANYNAME Microsoft Corporation .COPYRIGHT @@ -8,7 +8,7 @@ .LICENSEURI https://github.com/PowerShell/xPSDesiredStateConfiguration/blob/master/LICENSE .PROJECTURI https://github.com/PowerShell/xPSDesiredStateConfiguration .ICONURI -.EXTERNALMODULEDEPENDENCIES NetworkingDsc, xPSDesiredStateConfiguration +.EXTERNALMODULEDEPENDENCIES NetworkingDsc, xPSDesiredStateConfiguration, xWebAdministration .REQUIREDSCRIPTS .EXTERNALSCRIPTDEPENDENCIES #> @@ -33,6 +33,8 @@ 2 - To configure a Firewall Rule (Exception) to allow external connections the [NetworkingDsc](https://github.com/PowerShell/NetworkingDsc) DSC module is required. + 3 - The [xWebAdministration](https://github.com/PowerShell/xWebAdministration) + DSC module is required to configure the IIS Application Pool .PARAMETER NodeName The name of the node being configured as a DSC Pull Server. From 22fce06caf430148a0306da588c3214aab488f99 Mon Sep 17 00:00:00 2001 From: Thomas Meckel Date: Sun, 5 May 2019 18:59:30 +0200 Subject: [PATCH 20/32] Changes after 1st review --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 50385b559..42246c4dc 100644 --- a/README.md +++ b/README.md @@ -171,7 +171,7 @@ None the Pull Server instance. Preferably the new application pool is created by using the __xWebAppPool__ resource from the [xWebAdministration](https://github.com/PowerShell/xWebAdministration) DSC - module + module. * **PhysicalPath**: Folder location where the content of the web service resides. * **Ensure**: Ensures that the web service is **Present** or **Absent** From 72d57bd6c66544353f5d8ae7d207989a6baeb58f Mon Sep 17 00:00:00 2001 From: Thomas Meckel Date: Fri, 10 May 2019 21:10:16 +0200 Subject: [PATCH 21/32] Changes after 2nd review --- .../MSFT_xDSCWebService.psm1 | 8 +- .../MSFT_xDSCWebService/PSWSIISEndpoint.psm1 | 14 ++-- Examples/Sample_xDscWebServiceAppPool.ps1 | 74 ------------------ .../Sample_xDscWebServiceRegistration.ps1 | 74 ------------------ ...eRegistrationWithSecurityBestPractices.ps1 | 66 ---------------- ...cWebServiceRegistration_UseSQLProvider.ps1 | 71 ------------------ ...ebServiceRegistration_Win2k12and2k12R2.ps1 | 71 ------------------ .../Sample_xDscWebService_ClientConfig.ps1 | 75 +++++++++++++++++++ README.md | 6 ++ .../MSFT_xDSCWebService.Integration.tests.ps1 | 22 +++--- Tests/Unit/MSFT_xDSCWebService.Tests.ps1 | 4 +- 11 files changed, 107 insertions(+), 378 deletions(-) create mode 100644 Examples/Sample_xDscWebService_ClientConfig.ps1 diff --git a/DSCResources/MSFT_xDSCWebService/MSFT_xDSCWebService.psm1 b/DSCResources/MSFT_xDSCWebService/MSFT_xDSCWebService.psm1 index d8da079d4..2e9df19a7 100644 --- a/DSCResources/MSFT_xDSCWebService/MSFT_xDSCWebService.psm1 +++ b/DSCResources/MSFT_xDSCWebService/MSFT_xDSCWebService.psm1 @@ -36,7 +36,7 @@ function Get-TargetResource [Parameter()] [ValidateNotNullOrEmpty()] [System.String] - $ApplicationPoolName = $DefaultAppPoolName, + $ApplicationPoolName = $DscWebServiceDefaultAppPoolName, # Thumbprint of the Certificate in CERT:\LocalMachine\MY\ for Pull Server [Parameter(ParameterSetName = 'CertificateThumbPrint')] @@ -220,7 +220,7 @@ function Set-TargetResource [Parameter()] [ValidateNotNullOrEmpty()] [System.String] - $ApplicationPoolName = $DefaultAppPoolName, + $ApplicationPoolName = $DscWebServiceDefaultAppPoolName, # Port number of the DSC Pull Server IIS Endpoint [Parameter()] @@ -345,7 +345,7 @@ function Set-TargetResource # If the Pull Server Site should be bound to the non default AppPool # ensure that the AppPool already exists if ('Present' -eq $Ensure ` - -and $ApplicationPoolName -ne $DefaultAppPoolName ` + -and $ApplicationPoolName -ne $DscWebServiceDefaultAppPoolName ` -and (-not (Test-Path -Path "IIS:\AppPools\$ApplicationPoolName"))) { throw ($LocalizedData.ThrowApplicationPoolNotFound -f $ApplicationPoolName) @@ -541,7 +541,7 @@ function Test-TargetResource [Parameter()] [ValidateNotNullOrEmpty()] [System.String] - $ApplicationPoolName = $DefaultAppPoolName, + $ApplicationPoolName = $DscWebServiceDefaultAppPoolName, # Port number of the DSC Pull Server IIS Endpoint [Parameter()] diff --git a/DSCResources/MSFT_xDSCWebService/PSWSIISEndpoint.psm1 b/DSCResources/MSFT_xDSCWebService/PSWSIISEndpoint.psm1 index f1fa6342b..479db6e0b 100644 --- a/DSCResources/MSFT_xDSCWebService/PSWSIISEndpoint.psm1 +++ b/DSCResources/MSFT_xDSCWebService/PSWSIISEndpoint.psm1 @@ -1,7 +1,7 @@ # This module file contains a utility to perform PSWS IIS Endpoint setup # Module exports New-PSWSEndpoint function to perform the endpoint setup -New-Variable -Name DefaultAppPoolName -Value 'PSWS' -Option ReadOnly -Force -Scope Script +New-Variable -Name DscWebServiceDefaultAppPoolName -Value 'PSWS' -Option ReadOnly -Force -Scope Script <# .SYNOPSIS @@ -258,7 +258,7 @@ function Update-Site $website = Stop-Website -Name $site.name -Passthru if ('Started' -eq $website.state) { - Write-Error -Message "Unable to stop WebSite $($site.name)" -ErrorAction:Stop + throw "Unable to stop WebSite $($site.name)" } <# @@ -338,7 +338,7 @@ function Remove-AppPool $appPool ) - if ($DefaultAppPoolName -eq $appPool) + if ($DscWebServiceDefaultAppPoolName -eq $appPool) { # Without this tests we may get a breaking error here, despite SilentlyContinue if (Test-Path -Path "IIS:\AppPools\$appPool") @@ -350,13 +350,13 @@ function Remove-AppPool } else { - Write-Verbose -Message "Unable to delete application pool [$appPool] because it's still bound to a site or application" + Write-Verbose -Message "Application pool [$appPool] can't be deleted because it's still bound to a site or application" } } } else { - Write-Verbose -Message "ApplicationPool name is different from built in name [$DefaultAppPoolName]. Unable to delete." + Write-Verbose -Message "ApplicationPool can't be deleted because the name is different from built-in name [$DscWebServiceDefaultAppPoolName]." } } @@ -745,7 +745,7 @@ function New-PSWSEndpoint if (-not $appPool) { - $appPool = $DefaultAppPoolName + $appPool = $DscWebServiceDefaultAppPoolName } $script:wevtutil = "$env:windir\system32\Wevtutil.exe" @@ -1003,4 +1003,4 @@ function Set-BindingRedirectSettingInWebConfig Export-ModuleMember ` -Function New-PSWSEndpoint, Set-AppSettingsInWebconfig, Set-BindingRedirectSettingInWebConfig, Remove-PSWSEndpoint ` - -Variable DefaultAppPoolName + -Variable DscWebServiceDefaultAppPoolName diff --git a/Examples/Sample_xDscWebServiceAppPool.ps1 b/Examples/Sample_xDscWebServiceAppPool.ps1 index a95fe660f..e868c4d9b 100644 --- a/Examples/Sample_xDscWebServiceAppPool.ps1 +++ b/Examples/Sample_xDscWebServiceAppPool.ps1 @@ -151,77 +151,3 @@ Configuration Sample_xDscWebServiceAppPool } } } - -<# - .SYNOPSIS - The Sample_MetaConfigurationToRegisterWithSecurePullServer registers - a DSC client node with the pull server. - - .PARAMETER NodeName - The name of the node being configured as a DSC Pull Server. - - .PARAMETER RegistrationKey - This key will be used by client nodes as a shared key to authenticate - during registration. This should be a string with enough entropy - (randomness) to protect the registration of clients to the pull server. - The example creates a new GUID for the registration key. - - .PARAMETER ServerName - The HostName to use when configuring the Pull Server URL on the DSC - client. - - .PARAMETER Port - The port on which the PullServer is listening for connections - - .EXAMPLE - $registrationKey = [System.Guid]::NewGuid() - - Sample_MetaConfigurationToRegisterWithSecurePullServer -RegistrationKey $registrationKey -#> -[DSCLocalConfigurationManager()] -Configuration Sample_MetaConfigurationToRegisterWithSecurePullServer -{ - param - ( - [Parameter()] - [ValidateNotNullOrEmpty()] - [System.String] - $NodeName = 'localhost', - - [Parameter()] - [Parameter(Mandatory = $true)] - [System.String] - $RegistrationKey, - - [Parameter()] - [ValidateNotNullOrEmpty()] - [System.String] - $ServerName = 'localhost', - - [Parameter()] - [ValidateRange(1, 65535)] - [System.UInt16] - $Port = 8080 - ) - - Node $NodeName - { - Settings - { - RefreshMode = 'Pull' - } - - ConfigurationRepositoryWeb CONTOSO-PullSrv - { - ServerURL = "https://$ServerName`:$Port/PSDSCPullServer.svc" - RegistrationKey = $RegistrationKey - ConfigurationNames = @('ClientConfig') - } - - ReportServerWeb CONTOSO-PullSrv - { - ServerURL = "https://$ServerName`:$Port/PSDSCPullServer.svc" - RegistrationKey = $RegistrationKey - } - } -} diff --git a/Examples/Sample_xDscWebServiceRegistration.ps1 b/Examples/Sample_xDscWebServiceRegistration.ps1 index 259cade4a..9208c95a6 100644 --- a/Examples/Sample_xDscWebServiceRegistration.ps1 +++ b/Examples/Sample_xDscWebServiceRegistration.ps1 @@ -133,77 +133,3 @@ Configuration Sample_xDscWebServiceRegistration } } } - -<# - .SYNOPSIS - The Sample_MetaConfigurationToRegisterWithSecurePullServer registers - a DSC client node with the pull server. - - .PARAMETER NodeName - The name of the node being configured as a DSC Pull Server. - - .PARAMETER RegistrationKey - This key will be used by client nodes as a shared key to authenticate - during registration. This should be a string with enough entropy - (randomness) to protect the registration of clients to the pull server. - The example creates a new GUID for the registration key. - - .PARAMETER ServerName - The HostName to use when configuring the Pull Server URL on the DSC - client. - - .PARAMETER Port - The port on which the PullServer is listening for connections - - .EXAMPLE - $registrationKey = [System.Guid]::NewGuid() - - Sample_MetaConfigurationToRegisterWithSecurePullServer -RegistrationKey $registrationKey -#> -[DSCLocalConfigurationManager()] -Configuration Sample_MetaConfigurationToRegisterWithSecurePullServer -{ - param - ( - [Parameter()] - [ValidateNotNullOrEmpty()] - [System.String] - $NodeName = 'localhost', - - [Parameter()] - [Parameter(Mandatory = $true)] - [System.String] - $RegistrationKey, - - [Parameter()] - [ValidateNotNullOrEmpty()] - [System.String] - $ServerName = 'localhost', - - [Parameter()] - [ValidateRange(1, 65535)] - [System.UInt16] - $Port = 8080 - ) - - Node $NodeName - { - Settings - { - RefreshMode = 'Pull' - } - - ConfigurationRepositoryWeb CONTOSO-PullSrv - { - ServerURL = "https://$ServerName`:$Port/PSDSCPullServer.svc" - RegistrationKey = $RegistrationKey - ConfigurationNames = @('ClientConfig') - } - - ReportServerWeb CONTOSO-PullSrv - { - ServerURL = "https://$ServerName`:$Port/PSDSCPullServer.svc" - RegistrationKey = $RegistrationKey - } - } -} diff --git a/Examples/Sample_xDscWebServiceRegistrationWithSecurityBestPractices.ps1 b/Examples/Sample_xDscWebServiceRegistrationWithSecurityBestPractices.ps1 index a082e71e3..dee5f5f3e 100644 --- a/Examples/Sample_xDscWebServiceRegistrationWithSecurityBestPractices.ps1 +++ b/Examples/Sample_xDscWebServiceRegistrationWithSecurityBestPractices.ps1 @@ -136,69 +136,3 @@ Configuration Sample_xDscWebServiceRegistrationWithSecurityBestPractices } } } - -<# - .SYNOPSIS - The Sample_MetaConfigurationToRegisterWithSecurePullServer registers - a DSC client node with the pull server. - - .PARAMETER NodeName - The name of the node being configured as a DSC Pull Server. - - .PARAMETER RegistrationKey - This key will be used by client nodes as a shared key to authenticate - during registration. This should be a string with enough entropy - (randomness) to protect the registration of clients to the pull server. - The example creates a new GUID for the registration key. - - .PARAMETER ServerName - The HostName to use when configuring the Pull Server URL on the DSC - client. - - .EXAMPLE - $registrationKey = [System.Guid]::NewGuid() - - Sample_MetaConfigurationToRegisterWithSecurePullServer -RegistrationKey $registrationKey -#> -[DSCLocalConfigurationManager()] -Configuration Sample_MetaConfigurationToRegisterWithSecurePullServer -{ - param - ( - [Parameter()] - [ValidateNotNullOrEmpty()] - [System.String] - $NodeName = 'localhost', - - [Parameter()] - [ValidateNotNullOrEmpty()] - [System.String] - $RegistrationKey, - - [Parameter()] - [ValidateNotNullOrEmpty()] - [System.String] - $ServerName = 'localhost' - ) - - Node $NodeName - { - Settings - { - RefreshMode = 'Pull' - } - - ConfigurationRepositoryWeb CONTOSO-PullSrv - { - ServerURL = "https://$ServerName`:8080/PSDSCPullServer.svc" - RegistrationKey = $RegistrationKey - ConfigurationNames = @('ClientConfig') - } - - ReportServerWeb CONTOSO-PullSrv - { - ServerURL = "https://$ServerName`:8080/PSDSCPullServer.svc" - RegistrationKey = $RegistrationKey - } - } -} diff --git a/Examples/Sample_xDscWebServiceRegistration_UseSQLProvider.ps1 b/Examples/Sample_xDscWebServiceRegistration_UseSQLProvider.ps1 index 7df76c005..9e7e9f42e 100644 --- a/Examples/Sample_xDscWebServiceRegistration_UseSQLProvider.ps1 +++ b/Examples/Sample_xDscWebServiceRegistration_UseSQLProvider.ps1 @@ -134,74 +134,3 @@ Configuration Sample_xDscWebServiceRegistration_UseSQLProvider } } - -<# - .SYNOPSIS - The Sample_MetaConfigurationToRegisterWithSecurePullServer registers - a DSC client node with the pull server. - - .PARAMETER NodeName - The name of the node being configured as a DSC Pull Server. - - .PARAMETER RegistrationKey - This key will be used by client nodes as a shared key to authenticate - during registration. This should be a string with enough entropy - (randomness) to protect the registration of clients to the pull server. - The example creates a new GUID for the registration key. - - .PARAMETER ServerName - The HostName to use when configuring the Pull Server URL on the DSC - client. - - .EXAMPLE - $registrationKey = [System.Guid]::NewGuid() - - Sample_MetaConfigurationToRegisterWithSecurePullServer -RegistrationKey $registrationKey -#> -[DSCLocalConfigurationManager()] -Configuration Sample_MetaConfigurationToRegisterWithSecurePullServer -{ - param - ( - [Parameter()] - [ValidateNotNullOrEmpty()] - [System.String] - $NodeName = 'localhost', - - [Parameter()] - [Parameter(Mandatory = $true)] - [System.String] - $RegistrationKey, - - [Parameter()] - [ValidateNotNullOrEmpty()] - [System.String] - $ServerName = 'localhost', - - [Parameter()] - [ValidateRange(1, 65535)] - [System.UInt16] - $Port = 8080 - ) - - Node $NodeName - { - Settings - { - RefreshMode = 'Pull' - } - - ConfigurationRepositoryWeb CONTOSO-PullSrv - { - ServerURL = "https://$ServerName`:$Port/PSDSCPullServer.svc" - RegistrationKey = $RegistrationKey - ConfigurationNames = @('ClientConfig') - } - - ReportServerWeb CONTOSO-PullSrv - { - ServerURL = "https://$ServerName`:$Port/PSDSCPullServer.svc" - RegistrationKey = $RegistrationKey - } - } -} diff --git a/Examples/Sample_xDscWebServiceRegistration_Win2k12and2k12R2.ps1 b/Examples/Sample_xDscWebServiceRegistration_Win2k12and2k12R2.ps1 index de9f14db6..3ba4ac0c8 100644 --- a/Examples/Sample_xDscWebServiceRegistration_Win2k12and2k12R2.ps1 +++ b/Examples/Sample_xDscWebServiceRegistration_Win2k12and2k12R2.ps1 @@ -117,74 +117,3 @@ Configuration Sample_xDscWebServiceRegistration_Win2k12and2k12R2 } } } - -<# - .SYNOPSIS - The Sample_MetaConfigurationToRegisterWithSecurePullServer registers - a DSC client node with the pull server. - - .PARAMETER NodeName - The name of the node being configured as a DSC Pull Server. - - .PARAMETER RegistrationKey - This key will be used by client nodes as a shared key to authenticate - during registration. This should be a string with enough entropy - (randomness) to protect the registration of clients to the pull server. - The example creates a new GUID for the registration key. - - .PARAMETER ServerName - The HostName to use when configuring the Pull Server URL on the DSC - client. - - .EXAMPLE - $registrationKey = [System.Guid]::NewGuid() - - Sample_MetaConfigurationToRegisterWithSecurePullServer -RegistrationKey $registrationKey -#> -[DSCLocalConfigurationManager()] -Configuration Sample_MetaConfigurationToRegisterWithSecurePullServer -{ - param - ( - [Parameter()] - [ValidateNotNullOrEmpty()] - [System.String] - $NodeName = 'localhost', - - [Parameter()] - [Parameter(Mandatory = $true)] - [System.String] - $RegistrationKey, - - [Parameter()] - [ValidateNotNullOrEmpty()] - [System.String] - $ServerName = 'localhost', - - [Parameter()] - [ValidateRange(1, 65535)] - [System.UInt16] - $Port = 8080 - ) - - Node $NodeName - { - Settings - { - RefreshMode = 'Pull' - } - - ConfigurationRepositoryWeb CONTOSO-PullSrv - { - ServerURL = "https://$ServerName`:$Port/PSDSCPullServer.svc" - RegistrationKey = $RegistrationKey - ConfigurationNames = @('ClientConfig') - } - - ReportServerWeb CONTOSO-PullSrv - { - ServerURL = "https://$ServerName`:$Port/PSDSCPullServer.svc" - RegistrationKey = $RegistrationKey - } - } -} diff --git a/Examples/Sample_xDscWebService_ClientConfig.ps1 b/Examples/Sample_xDscWebService_ClientConfig.ps1 new file mode 100644 index 000000000..2f45703ec --- /dev/null +++ b/Examples/Sample_xDscWebService_ClientConfig.ps1 @@ -0,0 +1,75 @@ +<# + .SYNOPSIS + The Sample_MetaConfigurationToRegisterWithSecurePullServer registers + a DSC client node with the pull server. + + .PARAMETER NodeName + The name of the node being configured as a DSC Pull Server. + + .PARAMETER RegistrationKey + This key will be used by client nodes as a shared key to authenticate + during registration. This should be a string with enough entropy + (randomness) to protect the registration of clients to the pull server. + The example creates a new GUID for the registration key. + + .PARAMETER ServerName + The HostName to use when configuring the Pull Server URL on the DSC + client. + + .PARAMETER Port + The port on which the PullServer is listening for connections + + .EXAMPLE + $registrationKey = [System.Guid]::NewGuid() + + Sample_MetaConfigurationToRegisterWithSecurePullServer -RegistrationKey $registrationKey +#> +[DSCLocalConfigurationManager()] +Configuration Sample_MetaConfigurationToRegisterWithSecurePullServer +{ + param + ( + [Parameter()] + [ValidateNotNullOrEmpty()] + [System.String] + $NodeName = 'localhost', + + [Parameter()] + [Parameter(Mandatory = $true)] + [System.String] + $RegistrationKey, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [System.String] + $ServerName = 'localhost', + + [Parameter()] + [ValidateRange(1, 65535)] + [System.UInt16] + $Port = 8080 + ) + + Import-DSCResource -ModuleName 'xPSDesiredStateConfiguration' + + Node $NodeName + { + Settings + { + RefreshMode = 'Pull' + } + + ConfigurationRepositoryWeb CONTOSO-PullSrv + { + ServerURL = "https://$ServerName`:$Port/PSDSCPullServer.svc" + RegistrationKey = $RegistrationKey + ConfigurationNames = @('ClientConfig') + } + + ReportServerWeb CONTOSO-PullSrv + { + ServerURL = "https://$ServerName`:$Port/PSDSCPullServer.svc" + RegistrationKey = $RegistrationKey + } + } +} diff --git a/README.md b/README.md index 50385b559..074914c05 100644 --- a/README.md +++ b/README.md @@ -217,6 +217,8 @@ is created by using the **Firewall** resource from the #### Examples +_Pull Server_ + * [A Pull Server using a SQL Server as backend](./Examples/Sample_xDscWebServiceRegistration_UseSQLProvider.ps1) * [A Pull Server default configuration](./Examples/Sample_xDscWebServiceRegistration_Win2k12and2k12R2.ps1) * [A Pull Server with an enhanced security configuration and a firewall rule](./Examples/Sample_xDscWebServiceRegistration.ps1) @@ -224,6 +226,10 @@ is created by using the **Firewall** resource from the * [Removing a Pull Server instance](./Examples/Sample_xDscWebServiceRemoval.ps1) * [A Pull Server with a separately defined IIS Application Pool](./Examples/Sample_xDscWebServiceAppPool.ps1) +_Client_ + +* [Common Client (Server) Configuration](./Examples/Sample_xDscWebService_ClientConfig.ps1) + ### xGroup Provides a mechanism to manage local groups on the target node. diff --git a/Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 b/Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 index 8547d5a7f..5606d5621 100644 --- a/Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 +++ b/Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 @@ -30,6 +30,19 @@ if (Test-SkipContinuousIntegrationTask -Type 'Integration') return } +if ($env:CI -eq $false) +{ + # Install modules + $configurationFile = Join-Path -Path $PSScriptRoot -ChildPath "$($script:dcsResourceName).config.ps1" + $requiredModules = Get-ResourceModulesInConfiguration -ConfigurationPath $configurationFile | + Where-Object -Property Name -ne $script:dscModuleName + + if ($requiredModules) + { + Install-DependentModule -Module $requiredModules + } +} + <# .SYNOPSIS Performs common DSC integration tests including compiling, setting, @@ -268,15 +281,6 @@ try Start-Service -Name w3svc -ErrorAction Stop #region Integration Tests - $configurationFile = Join-Path -Path $PSScriptRoot -ChildPath "$($script:dcsResourceName).config.ps1" - $requiredModules = Get-ResourceModulesInConfiguration -ConfigurationPath $configurationFile | - Where-Object -Property Name -ne $script:dscModuleName - - if ($requiredModules) - { - Install-DependentModule -Module $requiredModules - } - . $configurationFile Describe "$($script:dcsResourceName)_Integration" { diff --git a/Tests/Unit/MSFT_xDSCWebService.Tests.ps1 b/Tests/Unit/MSFT_xDSCWebService.Tests.ps1 index f75c742c3..0eea716b6 100644 --- a/Tests/Unit/MSFT_xDSCWebService.Tests.ps1 +++ b/Tests/Unit/MSFT_xDSCWebService.Tests.ps1 @@ -164,7 +164,7 @@ try $null = New-Item -Path $webConfigPath -Value $webConfig Context -Name 'DSC Web Service is not installed' -Fixture { - Mock -CommandName Get-WebSite -MockWith {} + Mock -CommandName Get-WebSite $script:result = $null @@ -627,7 +627,7 @@ try } It 'Should call expected mocks' { - Mock -CommandName Get-Website -MockWith { } -ModuleName PSWSIISEndpoint + Mock -CommandName Get-Website -ModuleName PSWSIISEndpoint Mock -CommandName Add-PullServerFirewallConfiguration Set-TargetResource @testParameters @setTargetPaths -Ensure Present From 3d962bf43d0d94e98ffb538c17bf8637457d34f2 Mon Sep 17 00:00:00 2001 From: Thomas Meckel Date: Fri, 10 May 2019 21:27:30 +0200 Subject: [PATCH 22/32] Changed new Sample_MetaConfigurationToRegisterWithSecurePullServer.ps1 according to meta tests --- ... Sample_MetaConfigurationToRegisterWithSecurePullServer.ps1} | 0 README.md | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename Examples/{Sample_xDscWebService_ClientConfig.ps1 => Sample_MetaConfigurationToRegisterWithSecurePullServer.ps1} (100%) diff --git a/Examples/Sample_xDscWebService_ClientConfig.ps1 b/Examples/Sample_MetaConfigurationToRegisterWithSecurePullServer.ps1 similarity index 100% rename from Examples/Sample_xDscWebService_ClientConfig.ps1 rename to Examples/Sample_MetaConfigurationToRegisterWithSecurePullServer.ps1 diff --git a/README.md b/README.md index 17157ca78..3515c4460 100644 --- a/README.md +++ b/README.md @@ -228,7 +228,7 @@ _Pull Server_ _Client_ -* [Common Client (Server) Configuration](./Examples/Sample_xDscWebService_ClientConfig.ps1) +* [Common Client (Server) Configuration](./Examples/Sample_MetaConfigurationToRegisterWithSecurePullServer.ps1) ### xGroup From 62c17c37a4b660e175e55581188f340955c7f6eb Mon Sep 17 00:00:00 2001 From: Thomas Meckel Date: Sat, 11 May 2019 11:38:28 +0200 Subject: [PATCH 23/32] Corrected some markdown meta test errors --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3515c4460..df452a309 100644 --- a/README.md +++ b/README.md @@ -217,7 +217,7 @@ is created by using the **Firewall** resource from the #### Examples -_Pull Server_ +##### Pull Server * [A Pull Server using a SQL Server as backend](./Examples/Sample_xDscWebServiceRegistration_UseSQLProvider.ps1) * [A Pull Server default configuration](./Examples/Sample_xDscWebServiceRegistration_Win2k12and2k12R2.ps1) @@ -226,7 +226,7 @@ _Pull Server_ * [Removing a Pull Server instance](./Examples/Sample_xDscWebServiceRemoval.ps1) * [A Pull Server with a separately defined IIS Application Pool](./Examples/Sample_xDscWebServiceAppPool.ps1) -_Client_ +##### Client * [Common Client (Server) Configuration](./Examples/Sample_MetaConfigurationToRegisterWithSecurePullServer.ps1) From 871d78eefe1b75fc55a8ad38186b3d02ba2f2313 Mon Sep 17 00:00:00 2001 From: Thomas Meckel Date: Sat, 11 May 2019 11:49:36 +0200 Subject: [PATCH 24/32] Renamed and corrected Examples\Sample_xDscWebService_Client.ps1 --- ...er.ps1 => Sample_xDscWebService_Client.ps1} | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) rename Examples/{Sample_MetaConfigurationToRegisterWithSecurePullServer.ps1 => Sample_xDscWebService_Client.ps1} (80%) diff --git a/Examples/Sample_MetaConfigurationToRegisterWithSecurePullServer.ps1 b/Examples/Sample_xDscWebService_Client.ps1 similarity index 80% rename from Examples/Sample_MetaConfigurationToRegisterWithSecurePullServer.ps1 rename to Examples/Sample_xDscWebService_Client.ps1 index 2f45703ec..892633890 100644 --- a/Examples/Sample_MetaConfigurationToRegisterWithSecurePullServer.ps1 +++ b/Examples/Sample_xDscWebService_Client.ps1 @@ -1,3 +1,17 @@ +<#PSScriptInfo +.VERSION 1.0.0 +.GUID 119f0689-7410-4b2d-a805-d5df9f582cad +.AUTHOR Microsoft Corporation +.COMPANYNAME Microsoft Corporation +.COPYRIGHT +.TAGS DSCConfiguration +.LICENSEURI https://github.com/PowerShell/xPSDesiredStateConfiguration/blob/master/LICENSE +.PROJECTURI https://github.com/PowerShell/xPSDesiredStateConfiguration +.ICONURI +.EXTERNALMODULEDEPENDENCIES xPSDesiredStateConfiguration +.REQUIREDSCRIPTS +.EXTERNALSCRIPTDEPENDENCIES +#> <# .SYNOPSIS The Sample_MetaConfigurationToRegisterWithSecurePullServer registers @@ -25,7 +39,7 @@ Sample_MetaConfigurationToRegisterWithSecurePullServer -RegistrationKey $registrationKey #> [DSCLocalConfigurationManager()] -Configuration Sample_MetaConfigurationToRegisterWithSecurePullServer +Configuration Sample_xDscWebService_Client { param ( @@ -50,8 +64,6 @@ Configuration Sample_MetaConfigurationToRegisterWithSecurePullServer $Port = 8080 ) - Import-DSCResource -ModuleName 'xPSDesiredStateConfiguration' - Node $NodeName { Settings From 9418189b6608f51073e08976c36ef7e7fa3532e1 Mon Sep 17 00:00:00 2001 From: Thomas Meckel Date: Sat, 11 May 2019 12:00:14 +0200 Subject: [PATCH 25/32] Corrected link to xDSCWebService client example --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index df452a309..b2eb5908d 100644 --- a/README.md +++ b/README.md @@ -228,7 +228,7 @@ is created by using the **Firewall** resource from the ##### Client -* [Common Client (Server) Configuration](./Examples/Sample_MetaConfigurationToRegisterWithSecurePullServer.ps1) +* [Common Client (Server) Configuration](./Examples/Sample_xDscWebService_Client.ps1) ### xGroup From cc2a8c3deab4e821f97afc2bc2d44177991254ba Mon Sep 17 00:00:00 2001 From: Thomas Meckel Date: Sat, 11 May 2019 12:20:09 +0200 Subject: [PATCH 26/32] Corrected Examples\Sample_xDscWebService_Client.ps1 --- Examples/Sample_xDscWebService_Client.ps1 | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Examples/Sample_xDscWebService_Client.ps1 b/Examples/Sample_xDscWebService_Client.ps1 index 892633890..733d270dd 100644 --- a/Examples/Sample_xDscWebService_Client.ps1 +++ b/Examples/Sample_xDscWebService_Client.ps1 @@ -8,13 +8,13 @@ .LICENSEURI https://github.com/PowerShell/xPSDesiredStateConfiguration/blob/master/LICENSE .PROJECTURI https://github.com/PowerShell/xPSDesiredStateConfiguration .ICONURI -.EXTERNALMODULEDEPENDENCIES xPSDesiredStateConfiguration +.EXTERNALMODULEDEPENDENCIES .REQUIREDSCRIPTS .EXTERNALSCRIPTDEPENDENCIES #> <# .SYNOPSIS - The Sample_MetaConfigurationToRegisterWithSecurePullServer registers + The Sample_xDscWebService_Client registers a DSC client node with the pull server. .PARAMETER NodeName @@ -36,7 +36,7 @@ .EXAMPLE $registrationKey = [System.Guid]::NewGuid() - Sample_MetaConfigurationToRegisterWithSecurePullServer -RegistrationKey $registrationKey + Sample_xDscWebService_Client -RegistrationKey $registrationKey #> [DSCLocalConfigurationManager()] Configuration Sample_xDscWebService_Client @@ -48,7 +48,6 @@ Configuration Sample_xDscWebService_Client [System.String] $NodeName = 'localhost', - [Parameter()] [Parameter(Mandatory = $true)] [System.String] $RegistrationKey, From 25c54c65caa6503d6835591db4cf5eef3ee1c462 Mon Sep 17 00:00:00 2001 From: Thomas Meckel Date: Sat, 11 May 2019 13:39:43 +0200 Subject: [PATCH 27/32] Corrected Tests\Integration\MSFT_xDSCWebService.Integration.tests.ps1 --- Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 b/Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 index 5606d5621..63d8b8d42 100644 --- a/Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 +++ b/Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 @@ -30,10 +30,11 @@ if (Test-SkipContinuousIntegrationTask -Type 'Integration') return } +$configurationFile = Join-Path -Path $PSScriptRoot -ChildPath "$($script:dcsResourceName).config.ps1" + if ($env:CI -eq $false) { # Install modules - $configurationFile = Join-Path -Path $PSScriptRoot -ChildPath "$($script:dcsResourceName).config.ps1" $requiredModules = Get-ResourceModulesInConfiguration -ConfigurationPath $configurationFile | Where-Object -Property Name -ne $script:dscModuleName From 92c27570d2bba022903326e085bc1aadbee55e19 Mon Sep 17 00:00:00 2001 From: Thomas Meckel Date: Sun, 12 May 2019 12:15:05 +0200 Subject: [PATCH 28/32] xDSCWebService: removed formatting for firewall rule name --- DSCResources/MSFT_xDSCWebService/Firewall.psm1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DSCResources/MSFT_xDSCWebService/Firewall.psm1 b/DSCResources/MSFT_xDSCWebService/Firewall.psm1 index c58cd6243..41d4a0485 100644 --- a/DSCResources/MSFT_xDSCWebService/Firewall.psm1 +++ b/DSCResources/MSFT_xDSCWebService/Firewall.psm1 @@ -1,5 +1,5 @@ # Name and description for the Firewall rules. Used in multiple locations -New-Variable -Name fireWallRuleDisplayName -Value 'DSCPullServer_IIS_Port' -Option ReadOnly -Scope Script -Force +New-Variable -Name FireWallRuleDisplayName -Value 'DSCPullServer_IIS_Port' -Option ReadOnly -Scope Script -Force New-Variable -Name netsh -Value "$env:windir\system32\netsh.exe" -Option ReadOnly -Scope Script -Force <# .SYNOPSIS @@ -22,7 +22,7 @@ function Add-PullServerFirewallConfiguration Write-Verbose -Message 'Disable Inbound Firewall Notification' $null = & $script:netsh advfirewall set currentprofile settings inboundusernotification disable - $ruleName = $FireWallRuleDisplayName -f $port + $ruleName = $FireWallRuleDisplayName # Remove all existing rules with that displayName $null = & $script:netsh advfirewall firewall delete rule name=$ruleName protocol=tcp localport=$Port From 3f2445c707ba5f5025b839108fe2b78919ae3447 Mon Sep 17 00:00:00 2001 From: Thomas Meckel Date: Sun, 12 May 2019 12:15:31 +0200 Subject: [PATCH 29/32] xDSCWebService: Replaces Write-Error with throw --- .../PullServerSetupTests.ps1 | 2 +- DSCResources/MSFT_xDSCWebService/MSFT_xDSCWebService.psm1 | 2 +- Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DSCPullServerSetup/PullServerDeploymentVerificationTest/PullServerSetupTests.ps1 b/DSCPullServerSetup/PullServerDeploymentVerificationTest/PullServerSetupTests.ps1 index ab4acc8a1..11291b369 100644 --- a/DSCPullServerSetup/PullServerDeploymentVerificationTest/PullServerSetupTests.ps1 +++ b/DSCPullServerSetup/PullServerDeploymentVerificationTest/PullServerSetupTests.ps1 @@ -30,7 +30,7 @@ Describe PullServerInstallationTests { # Skip all tests if web.config is not found if (-not (Test-Path $DscWebConfigPath)){ - Write-Error 'No pullserver web.config found.' -ErrorAction Stop + throw 'No pullserver web.config found.' } # Get web.config content as XML diff --git a/DSCResources/MSFT_xDSCWebService/MSFT_xDSCWebService.psm1 b/DSCResources/MSFT_xDSCWebService/MSFT_xDSCWebService.psm1 index 2e9df19a7..82db7cdf5 100644 --- a/DSCResources/MSFT_xDSCWebService/MSFT_xDSCWebService.psm1 +++ b/DSCResources/MSFT_xDSCWebService/MSFT_xDSCWebService.psm1 @@ -1478,7 +1478,7 @@ function Test-IISSelfSignedModuleEnabled } else { - Write-Error -Message "Website [$EndpointName] not found" + throw "Website [$EndpointName] not found" } } diff --git a/Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 b/Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 index 63d8b8d42..95c949c3b 100644 --- a/Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 +++ b/Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 @@ -14,7 +14,7 @@ if ( (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCR # Ensure that Powershell Module 'WebAdministration' is available if (-not (Install-WindowsFeatureAndVerify -Name Web-Mgmt-Tools)) { - Write-Error -Message 'Failed to verify for required Windows Feature. Unable to continue ...' -ErrorAction:Stop + throw 'Failed to verify for required Windows Feature. Unable to continue ...' } Import-Module -Name WebAdministration -ErrorAction:Stop -Force Import-Module -Name (Join-Path -Path $script:moduleRoot -ChildPath (Join-Path -Path 'DSCResource.Tests' -ChildPath 'TestHelper.psm1')) -ErrorAction:Stop -Force From 52e7b767cec4248df947fe97143d39f71ca3968d Mon Sep 17 00:00:00 2001 From: Thomas Meckel Date: Sun, 12 May 2019 14:52:37 +0200 Subject: [PATCH 30/32] xDSCWebService: renamed Sample_xDscWebServiceAppPool.ps1 to Sample_xDscWebService_Preferred.ps1 --- ...ServiceAppPool.ps1 => Sample_xDscWebService_Preferred.ps1} | 4 ++-- README.md | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) rename Examples/{Sample_xDscWebServiceAppPool.ps1 => Sample_xDscWebService_Preferred.ps1} (97%) diff --git a/Examples/Sample_xDscWebServiceAppPool.ps1 b/Examples/Sample_xDscWebService_Preferred.ps1 similarity index 97% rename from Examples/Sample_xDscWebServiceAppPool.ps1 rename to Examples/Sample_xDscWebService_Preferred.ps1 index e868c4d9b..430e75e35 100644 --- a/Examples/Sample_xDscWebServiceAppPool.ps1 +++ b/Examples/Sample_xDscWebService_Preferred.ps1 @@ -59,9 +59,9 @@ $thumbprint = (New-SelfSignedCertificate -DnsName $env:COMPUTERNAME -CertStoreLocation Cert:\LocalMachine\My).Thumbprint $registrationKey = [System.Guid]::NewGuid() - Sample_xDscWebServiceRegistration -RegistrationKey $registrationkey -CertificateThumbPrint $thumbprint + Sample_xDscWebService_Preferred -RegistrationKey $registrationkey -CertificateThumbPrint $thumbprint #> -Configuration Sample_xDscWebServiceAppPool +Configuration Sample_xDscWebService_Preferred { param ( diff --git a/README.md b/README.md index b2eb5908d..d8a43fce7 100644 --- a/README.md +++ b/README.md @@ -219,12 +219,13 @@ is created by using the **Firewall** resource from the ##### Pull Server +* [A Pull Server with a separately defined IIS Application Pool and firewall + rule](./Examples/Sample_xDscWebService_Preferred.ps1) * [A Pull Server using a SQL Server as backend](./Examples/Sample_xDscWebServiceRegistration_UseSQLProvider.ps1) * [A Pull Server default configuration](./Examples/Sample_xDscWebServiceRegistration_Win2k12and2k12R2.ps1) * [A Pull Server with an enhanced security configuration and a firewall rule](./Examples/Sample_xDscWebServiceRegistration.ps1) * [A Pull Server using the security best practice configuration](./Examples/Sample_xDscWebServiceRegistrationWithSecurityBestPractices.ps1) * [Removing a Pull Server instance](./Examples/Sample_xDscWebServiceRemoval.ps1) -* [A Pull Server with a separately defined IIS Application Pool](./Examples/Sample_xDscWebServiceAppPool.ps1) ##### Client From a8b1e3f098eaa3dba5d059edebbd7593c7de7f45 Mon Sep 17 00:00:00 2001 From: Thomas Meckel Date: Sun, 12 May 2019 20:30:31 +0200 Subject: [PATCH 31/32] xDSCWebService: removed some more formatting stuff from DSCResources\MSFT_xDSCWebService\Firewall.psm1 --- DSCResources/MSFT_xDSCWebService/Firewall.psm1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DSCResources/MSFT_xDSCWebService/Firewall.psm1 b/DSCResources/MSFT_xDSCWebService/Firewall.psm1 index 41d4a0485..45f0658aa 100644 --- a/DSCResources/MSFT_xDSCWebService/Firewall.psm1 +++ b/DSCResources/MSFT_xDSCWebService/Firewall.psm1 @@ -53,7 +53,7 @@ function Remove-PullServerFirewallConfiguration { # remove all existing rules with that displayName Write-Verbose -Message "Delete Firewall Rule for port $Port" - $ruleName = $FireWallRuleDisplayName -f $port + $ruleName = $FireWallRuleDisplayName # backwards compatibility with old code if (Get-Command -Name Get-NetFirewallRule -CommandType Cmdlet -ErrorAction:SilentlyContinue) @@ -93,7 +93,7 @@ function Test-PullServerFirewallConfiguration # Remove all existing rules with that displayName Write-Verbose -Message "Testing Firewall Rule for port $Port" - $ruleName = $FireWallRuleDisplayName -f $port + $ruleName = $FireWallRuleDisplayName $result = & $script:netsh advfirewall firewall show rule name=$ruleName | Select-String -Pattern "LocalPort:\s*$Port" return -not [string]::IsNullOrWhiteSpace($result) } From 1118a4b35206de0eee48af8236639df1cc0acbf5 Mon Sep 17 00:00:00 2001 From: Thomas Meckel Date: Mon, 13 May 2019 20:18:28 +0200 Subject: [PATCH 32/32] Applied changes after addtional review --- .../PullServerSetupTests.ps1 | 2 +- .../MSFT_xDSCWebService.psm1 | 85 +++++++++++-------- .../MSFT_xDSCWebService/PSWSIISEndpoint.psm1 | 67 +++++++++------ 3 files changed, 89 insertions(+), 65 deletions(-) diff --git a/DSCPullServerSetup/PullServerDeploymentVerificationTest/PullServerSetupTests.ps1 b/DSCPullServerSetup/PullServerDeploymentVerificationTest/PullServerSetupTests.ps1 index 11291b369..52b9e48c2 100644 --- a/DSCPullServerSetup/PullServerDeploymentVerificationTest/PullServerSetupTests.ps1 +++ b/DSCPullServerSetup/PullServerDeploymentVerificationTest/PullServerSetupTests.ps1 @@ -29,7 +29,7 @@ Describe PullServerInstallationTests { $DscWebConfigPath = Join-Path -Path $env:SystemDrive -ChildPath $DscWebConfigChildPath # Skip all tests if web.config is not found - if (-not (Test-Path $DscWebConfigPath)){ + if (-not (Test-Path -Path $DscWebConfigPath)){ throw 'No pullserver web.config found.' } diff --git a/DSCResources/MSFT_xDSCWebService/MSFT_xDSCWebService.psm1 b/DSCResources/MSFT_xDSCWebService/MSFT_xDSCWebService.psm1 index 82db7cdf5..3a2b05202 100644 --- a/DSCResources/MSFT_xDSCWebService/MSFT_xDSCWebService.psm1 +++ b/DSCResources/MSFT_xDSCWebService/MSFT_xDSCWebService.psm1 @@ -78,8 +78,10 @@ function Get-TargetResource $ConfigureFirewall = $true ) - # If Certificate Subject is not specified then a value for CertificateThumbprint must be explicitly set instead. - # The Mof schema doesn't allow for a mandatory parameter in a parameter set. + <# + If Certificate Subject is not specified then a value for CertificateThumbprint must be explicitly set instead. + The Mof schema doesn't allow for a mandatory parameter in a parameter set. + #> if ($PScmdlet.ParameterSetName -eq 'CertificateThumbPrint' -and $PSBoundParameters.ContainsKey('CertificateThumbPrint') -ne $true) { throw $LocalizedData.ThrowCertificateThumbprint @@ -125,6 +127,7 @@ function Get-TargetResource $urlPrefix = $website.bindings.Collection[0].protocol + "://" $ipProperties = [System.Net.NetworkInformation.IPGlobalProperties]::GetIPGlobalProperties() + if ($ipProperties.DomainName) { $fqdn = '{0}.{1}' -f $ipProperties.HostName, $ipProperties.DomainName @@ -342,8 +345,11 @@ function Set-TargetResource { Write-Warning -Message $LocalizedData.ConfigFirewallDeprecated } - # If the Pull Server Site should be bound to the non default AppPool - # ensure that the AppPool already exists + + <# + If the Pull Server Site should be bound to the non default AppPool + ensure that the AppPool already exists + #> if ('Present' -eq $Ensure ` -and $ApplicationPoolName -ne $DscWebServiceDefaultAppPoolName ` -and (-not (Test-Path -Path "IIS:\AppPools\$ApplicationPoolName"))) @@ -363,7 +369,7 @@ function Set-TargetResource $languagePath = $cultureInfo.IetfLanguageTag $language = $cultureInfo.TwoLetterISOLanguageName - # the two letter iso languagename is not actually implemented in the source path, it's always 'en' + # The two letter iso languagename is not actually implemented in the source path, it's always 'en' if (-not (Test-Path -Path "$pathPullServer\$languagePath\Microsoft.Powershell.DesiredStateConfiguration.Service.Resources.dll")) { $languagePath = 'en' @@ -389,7 +395,6 @@ function Set-TargetResource $pswsMofFileName = "$pathPullServer\PSDSCPullServer.mof" $pswsDispatchFileName = "$pathPullServer\PSDSCPullServer.xml" - # ============ Absent block to remove existing site ========= if(($Ensure -eq "Absent")) { if(Test-Path -LiteralPath "IIS:\Sites\$EndpointName") @@ -400,22 +405,21 @@ function Set-TargetResource [System.Text.RegularExpressions.Regex]::Match($_.bindingInformation,':(\d+):').Groups[1].Value } - # there is a web site, but there shouldn't be one + # There is a web site, but there shouldn't be one Write-Verbose -Message "Removing web site [$EndpointName]" PSWSIISEndpoint\Remove-PSWSEndpoint -SiteName $EndpointName $portList | ForEach-Object -Process { Remove-PullServerFirewallConfiguration -Port $_ } } - # we are done here, all stuff below is for 'Present' + # We are done here, all stuff below is for 'Present' return } - # =========================================================== - Write-Verbose -Message "Create the IIS endpoint" + Write-Verbose -Message 'Create the IIS endpoint' PSWSIISEndpoint\New-PSWSEndpoint ` -site $EndpointName ` - -path $PhysicalPath ` + -Path $PhysicalPath ` -cfgfile $webConfigFileName ` -port $Port ` -appPool $ApplicationPoolName ` @@ -441,6 +445,7 @@ function Set-TargetResource Add-PullServerFirewallConfiguration -Port $port } } + 'Absent' { Write-Verbose -Message "Disabling firewall exception for port $port" @@ -452,42 +457,42 @@ function Set-TargetResource Update-LocationTagInApplicationHostConfigForAuthentication -WebSite $EndpointName -Authentication "basic" Update-LocationTagInApplicationHostConfigForAuthentication -WebSite $EndpointName -Authentication "windows" - if($SqlProvider) + if ($SqlProvider) { Write-Verbose -Message 'Set values into the web.config that define the SQL Connection' - PSWSIISEndpoint\Set-AppSettingsInWebconfig -path $PhysicalPath -key 'dbprovider' -value $jet4provider - PSWSIISEndpoint\Set-AppSettingsInWebconfig -path $PhysicalPath -key 'dbconnectionst' -value $SqlConnectionString + PSWSIISEndpoint\Set-AppSettingsInWebconfig -Path $PhysicalPath -Key 'dbprovider' -Value $jet4provider + PSWSIISEndpoint\Set-AppSettingsInWebconfig -Path $PhysicalPath -Key 'dbconnectionst' -Value $SqlConnectionString if ($isBlue) { - Set-BindingRedirectSettingInWebConfig -path $PhysicalPath + Set-BindingRedirectSettingInWebConfig -Path $PhysicalPath } } elseif ($isBlue) { Write-Verbose -Message 'Set values into the web.config that define the repository for BLUE OS' - PSWSIISEndpoint\Set-AppSettingsInWebconfig -path $PhysicalPath -key 'dbprovider' -value $eseprovider - PSWSIISEndpoint\Set-AppSettingsInWebconfig -path $PhysicalPath -key 'dbconnectionstr' -value $esedatabase + PSWSIISEndpoint\Set-AppSettingsInWebconfig -Path $PhysicalPath -Key 'dbprovider' -Value $eseprovider + PSWSIISEndpoint\Set-AppSettingsInWebconfig -Path $PhysicalPath -Key 'dbconnectionstr' -Value $esedatabase - Set-BindingRedirectSettingInWebConfig -path $PhysicalPath + Set-BindingRedirectSettingInWebConfig -Path $PhysicalPath } else { - if($isDownlevelOfBlue) + if ($isDownlevelOfBlue) { Write-Verbose -Message 'Set values into the web.config that define the repository for non-BLUE Downlevel OS' - $repository = Join-Path -Path "$DatabasePath" -ChildPath 'Devices.mdb' + $repository = Join-Path -Path $DatabasePath -ChildPath 'Devices.mdb' Copy-Item -Path "$pathPullServer\Devices.mdb" -Destination $repository -Force - PSWSIISEndpoint\Set-AppSettingsInWebconfig -path $PhysicalPath -key 'dbprovider' -value $jet4provider - PSWSIISEndpoint\Set-AppSettingsInWebconfig -path $PhysicalPath -key 'dbconnectionstr' -value $jet4database + PSWSIISEndpoint\Set-AppSettingsInWebconfig -Path $PhysicalPath -Key 'dbprovider' -Value $jet4provider + PSWSIISEndpoint\Set-AppSettingsInWebconfig -Path $PhysicalPath -Key 'dbconnectionstr' -Value $jet4database } else { Write-Verbose -Message 'Set values into the web.config that define the repository later than BLUE OS' Write-Verbose -Message 'Only ESENT is supported on Windows Server 2016' - PSWSIISEndpoint\Set-AppSettingsInWebconfig -path $PhysicalPath -key 'dbprovider' -value $eseprovider - PSWSIISEndpoint\Set-AppSettingsInWebconfig -path $PhysicalPath -key 'dbconnectionstr' -value $esedatabase + PSWSIISEndpoint\Set-AppSettingsInWebconfig -Path $PhysicalPath -Key 'dbprovider' -Value $eseprovider + PSWSIISEndpoint\Set-AppSettingsInWebconfig -Path $PhysicalPath -Key 'dbconnectionstr' -Value $esedatabase } } @@ -495,21 +500,21 @@ function Set-TargetResource Write-Verbose -Message 'Pull Server: Set values into the web.config that indicate the location of repository, configuration, modules' # Create the application data directory calculated above - $null = New-Item -path $DatabasePath -itemType 'directory' -Force + $null = New-Item -Path $DatabasePath -ItemType 'directory' -Force - $null = New-Item -path "$ConfigurationPath" -itemType 'directory' -Force + $null = New-Item -Path $ConfigurationPath -ItemType 'directory' -Force - PSWSIISEndpoint\Set-AppSettingsInWebconfig -path $PhysicalPath -key 'ConfigurationPath' -value $configurationPath + PSWSIISEndpoint\Set-AppSettingsInWebconfig -Path $PhysicalPath -Key 'ConfigurationPath' -Value $configurationPath - $null = New-Item -path "$ModulePath" -itemType 'directory' -Force + $null = New-Item -Path $ModulePath -ItemType 'directory' -Force - PSWSIISEndpoint\Set-AppSettingsInWebconfig -path $PhysicalPath -key 'ModulePath' -value $ModulePath + PSWSIISEndpoint\Set-AppSettingsInWebconfig -Path $PhysicalPath -Key 'ModulePath' -Value $ModulePath - $null = New-Item -path "$RegistrationKeyPath" -itemType 'directory' -Force + $null = New-Item -Path $RegistrationKeyPath -ItemType 'directory' -Force - PSWSIISEndpoint\Set-AppSettingsInWebconfig -path $PhysicalPath -key 'RegistrationKeyPath' -value $registrationKeyPath + PSWSIISEndpoint\Set-AppSettingsInWebconfig -Path $PhysicalPath -Key 'RegistrationKeyPath' -Value $registrationKeyPath - if($AcceptSelfSignedCertificates) + if ($AcceptSelfSignedCertificates) { Write-Verbose -Message 'Accepting self signed certificates from incoming hosts' Enable-IISSelfSignedModule -EndpointName $EndpointName -Enable32BitAppOnWin64:$Enable32BitAppOnWin64 @@ -519,7 +524,7 @@ function Set-TargetResource Disable-IISSelfSignedModule -EndpointName $EndpointName } - if($UseSecurityBestPractices) + if ($UseSecurityBestPractices) { UseSecurityBestPractices\Set-UseSecurityBestPractice -DisableSecurityBestPractices $DisableSecurityBestPractices } @@ -654,28 +659,33 @@ function Test-TargetResource :WebSiteTests Do { Write-Verbose -Message 'Check Ensure' - if(($Ensure -eq 'Present' -and $null -eq $website)) + + if (($Ensure -eq 'Present' -and $null -eq $website)) { $desiredConfigurationMatch = $false Write-Verbose -Message "The Website $EndpointName is not present" break } - if(($Ensure -eq 'Absent' -and $null -ne $website)) + + if (($Ensure -eq 'Absent' -and $null -ne $website)) { $desiredConfigurationMatch = $false Write-Verbose -Message "The Website $EndpointName is present but should not be" break } - if(($Ensure -eq 'Absent' -and $null -eq $website)) + + if (($Ensure -eq 'Absent' -and $null -eq $website)) { $desiredConfigurationMatch = $true Write-Verbose -Message "The Website $EndpointName is not present as requested" break } - # the other case is: Ensure and exist, we continue with more checks + + # The other case is: Ensure and exist, we continue with more checks Write-Verbose -Message 'Check Port' $actualPort = $website.bindings.Collection[0].bindingInformation.Split(":")[1] + if ($Port -ne $actualPort) { $desiredConfigurationMatch = $false @@ -684,6 +694,7 @@ function Test-TargetResource } Write-Verbose -Message 'Check Application Pool' + if ($ApplicationPoolName -ne $website.applicationPool) { $desiredConfigurationMatch = $false diff --git a/DSCResources/MSFT_xDSCWebService/PSWSIISEndpoint.psm1 b/DSCResources/MSFT_xDSCWebService/PSWSIISEndpoint.psm1 index 479db6e0b..32ecb294c 100644 --- a/DSCResources/MSFT_xDSCWebService/PSWSIISEndpoint.psm1 +++ b/DSCResources/MSFT_xDSCWebService/PSWSIISEndpoint.psm1 @@ -241,6 +241,7 @@ function Update-Site { $site = Get-Website -Name $siteName } + if ($site) { switch ($siteAction) @@ -250,21 +251,23 @@ function Update-Site Write-Verbose -Message "Starting IIS Website [$($site.name)]" Start-Website -Name $site.name } + 'Stop' { if ('Started' -eq $site.state) { Write-Verbose -Message "Stopping WebSite $($site.name)" $website = Stop-Website -Name $site.name -Passthru + if ('Started' -eq $website.state) { throw "Unable to stop WebSite $($site.name)" } <# - There may be running requests, wait a little - I had an issue where the files were still in use - when I tried to delete them + There may be running requests, wait a little + I had an issue where the files were still in use + when I tried to delete them #> Write-Verbose -Message 'Waiting for IIS to stop website' Start-Sleep -Milliseconds 1000 @@ -274,6 +277,7 @@ function Update-Site Write-Verbose -Message "IIS Website [$($site.name)] already stopped" } } + 'Remove' { Update-Site -site $site -siteAction Stop @@ -303,17 +307,17 @@ function Get-AppPoolBinding [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] - $appPool + $AppPool ) - if (Test-Path -Path "IIS:\AppPools\$appPool") + if (Test-Path -Path "IIS:\AppPools\$AppPool") { $sites = Get-WebConfigurationProperty ` - -Filter "/system.applicationHost/sites/site/application[@applicationPool=`'$appPool`'and @path='/']/parent::*" ` + -Filter "/system.applicationHost/sites/site/application[@applicationPool=`'$AppPool`'and @path='/']/parent::*" ` -PSPath 'machine/webroot/apphost' ` -Name name $apps = Get-WebConfigurationProperty ` - -Filter "/system.applicationHost/sites/site/application[@applicationPool=`'$appPool`'and @path!='/']" ` + -Filter "/system.applicationHost/sites/site/application[@applicationPool=`'$AppPool`'and @path!='/']" ` -PSPath 'machine/webroot/apphost' ` -Name path $sites, $apps | ForEach-Object { @@ -335,22 +339,23 @@ function Remove-AppPool [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] - $appPool + $AppPool ) - if ($DscWebServiceDefaultAppPoolName -eq $appPool) + if ($DscWebServiceDefaultAppPoolName -eq $AppPool) { # Without this tests we may get a breaking error here, despite SilentlyContinue - if (Test-Path -Path "IIS:\AppPools\$appPool") + if (Test-Path -Path "IIS:\AppPools\$AppPool") { - $cntBindings = (Get-AppPoolBinding -apppool $appPool | Measure-Object).Count - if (0 -ge $cntBindings) + $bindingCount = (Get-AppPoolBinding -AppPool $AppPool | Measure-Object).Count + + if (0 -ge $bindingCount) { - Remove-WebAppPool -Name $appPool -ErrorAction SilentlyContinue + Remove-WebAppPool -Name $AppPool -ErrorAction SilentlyContinue } else { - Write-Verbose -Message "Application pool [$appPool] can't be deleted because it's still bound to a site or application" + Write-Verbose -Message "Application pool [$AppPool] can't be deleted because it's still bound to a site or application" } } } @@ -530,6 +535,7 @@ function New-IISWebSite Write-Verbose -Message 'Set App Pool Properties' $appPoolIdentity = 4 + if ($applicationPoolIdentityType) { # LocalSystem = 0, LocalService = 1, NetworkService = 2, SpecificUser = 3, ApplicationPoolIdentity = 4 @@ -539,25 +545,29 @@ function New-IISWebSite { $appPoolIdentity = 0 } + 'LocalService' { $appPoolIdentity = 1 } + 'NetworkService' { $appPoolIdentity = 2 } + 'ApplicationPoolIdentity' { $appPoolIdentity = 4 } + default { throw "Invalid value [$applicationPoolIdentityType] for parameter -applicationPoolIdentityType" } } } - $appPoolItem = Get-Item IIS:\AppPools\$appPool + $appPoolItem = Get-Item -Path IIS:\AppPools\$appPool $appPoolItem.managedRuntimeVersion = 'v4.0' $appPoolItem.enable32BitAppOnWin64 = $enable32BitAppOnWin64 $appPoolItem.processModel.identityType = $appPoolIdentity @@ -566,6 +576,7 @@ function New-IISWebSite } Write-Verbose -Message 'Add and Set Site Properties' + if ($certificateThumbPrint -eq 'AllowUnencryptedTraffic') { $null = New-WebSite -Name $site -Id $siteID -Port $port -IPAddress "*" -PhysicalPath $path -ApplicationPool $appPool @@ -752,6 +763,7 @@ function New-PSWSEndpoint $svcName = Split-Path $svc -Leaf $protocol = 'https:' + if ($certificateThumbPrint -eq 'AllowUnencryptedTraffic') { $protocol = 'http:' @@ -760,7 +772,7 @@ function New-PSWSEndpoint # Get Machine Name $cimInstance = Get-CimInstance -ClassName Win32_ComputerSystem -Verbose:$false - Write-Verbose ("Setting up endpoint at - $protocol//" + $cimInstance.Name + ':' + $port + '/' + $svcName) + Write-Verbose -Message "Setting up endpoint at - $protocol//$($cimInstance.Name):$port/$svcName" Initialize-Endpoint ` -appPool $appPool ` -site $site ` @@ -813,6 +825,7 @@ function Remove-PSWSEndpoint # Get the site to remove $site = Get-Website -Name $siteName + if ($site) { # And the pool it is using @@ -824,7 +837,7 @@ function Remove-PSWSEndpoint Update-Site -site $site -siteAction Remove # Remove the files for the site - If (Test-Path -Path $filePath) + if (Test-Path -Path $filePath) { Get-ChildItem -Path $filePath -Recurse | Remove-Item -Recurse -Force Remove-Item -Path $filePath -Force @@ -855,22 +868,22 @@ function Set-AppSettingsInWebconfig [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] - $path, + $Path, # Key to add/update [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] - $key, + $Key, # Value [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] - $value + $Value ) - $webconfig = Join-Path $path 'web.config' + $webconfig = Join-Path -Path $Path -ChildPath 'web.config' [System.Boolean] $Found = $false if (Test-Path -Path $webconfig) @@ -878,24 +891,24 @@ function Set-AppSettingsInWebconfig $xml = [System.Xml.XmlDocument] (Get-Content -Path $webconfig) $root = $xml.get_DocumentElement() - foreach( $item in $root.appSettings.add) + foreach ($item in $root.appSettings.add) { - if( $item.key -eq $key ) + if ($item.key -eq $Key) { - $item.value = $value; + $item.value = $Value; $Found = $true; } } - if( -not $Found) + if (-not $Found) { $newElement = $xml.CreateElement('add') $nameAtt1 = $xml.CreateAttribute('key') - $nameAtt1.psbase.value = $key; + $nameAtt1.psbase.value = $Key; $null = $newElement.SetAttributeNode($nameAtt1) $nameAtt2 = $xml.CreateAttribute('value') - $nameAtt2.psbase.value = $value; + $nameAtt2.psbase.value = $Value; $null = $newElement.SetAttributeNode($nameAtt2) $null = $xml.configuration['appSettings'].AppendChild($newElement)