From 21129fcb42e0de81bea39ec2c7c24d5f27024479 Mon Sep 17 00:00:00 2001 From: Chris Gardner Date: Tue, 2 May 2017 21:31:32 +0100 Subject: [PATCH 01/15] Adding helper function to find certificate --- DSCResources/Helper.psm1 | 149 ++++++++ Tests/TestHelper/CommonTestHelper.psm1 | 135 +++++++ Tests/Unit/Helper.tests.ps1 | 480 +++++++++++++++++++++++++ 3 files changed, 764 insertions(+) create mode 100644 Tests/TestHelper/CommonTestHelper.psm1 create mode 100644 Tests/Unit/Helper.tests.ps1 diff --git a/DSCResources/Helper.psm1 b/DSCResources/Helper.psm1 index 2f87aaf7d..3dc05ab95 100644 --- a/DSCResources/Helper.psm1 +++ b/DSCResources/Helper.psm1 @@ -61,3 +61,152 @@ function Assert-Module -ErrorCategory ObjectNotFound } } + +<# + .SYNOPSIS + Locates one or more certificates using the passed certificate selector parameters. + + If more than one certificate is found matching the selector criteria, they will be + returned in order of descending expiration date. + + .PARAMETER Thumbprint + The thumbprint of the certificate to find. + + .PARAMETER FriendlyName + The friendly name of the certificate to find. + + .PARAMETER Subject + The subject of the certificate to find. + + .PARAMETER DNSName + The subject alternative name of the certificate to export must contain these values. + + .PARAMETER Issuer + The issuer of the certiicate to find. + + .PARAMETER KeyUsage + The key usage of the certificate to find must contain these values. + + .PARAMETER EnhancedKeyUsage + The enhanced key usage of the certificate to find must contain these values. + + .PARAMETER Store + The Windows Certificate Store Name to search for the certificate in. + Defaults to 'My'. + + .PARAMETER AllowExpired + Allows expired certificates to be returned. + +#> +function Find-Certificate +{ + [CmdletBinding()] + [OutputType([System.Security.Cryptography.X509Certificates.X509Certificate2[]])] + param + ( + [Parameter()] + [String] + $Thumbprint, + + [Parameter()] + [String] + $FriendlyName, + + [Parameter()] + [String] + $Subject, + + [Parameter()] + [String[]] + $DNSName, + + [Parameter()] + [String] + $Issuer, + + [Parameter()] + [String[]] + $KeyUsage, + + [Parameter()] + [String[]] + $EnhancedKeyUsage, + + [Parameter()] + [String] + $Store = 'My', + + [Parameter()] + [Boolean] + $AllowExpired = $false + ) + + $certPath = Join-Path -Path 'Cert:\LocalMachine' -ChildPath $Store + + if (-not (Test-Path -Path $certPath)) + { + # The Certificte Path is not valid + New-InvalidArgumentError ` + -ErrorId 'CannotFindCertificatePath' ` + -ErrorMessage ($LocalizedData.CertificatePathError -f $certPath) + } # if + + # Assemble the filter to use to select the certificate + $certFilters = @() + if ($PSBoundParameters.ContainsKey('Thumbprint')) + { + $certFilters += @('($_.Thumbprint -eq $Thumbprint)') + } # if + + if ($PSBoundParameters.ContainsKey('FriendlyName')) + { + $certFilters += @('($_.FriendlyName -eq $FriendlyName)') + } # if + + if ($PSBoundParameters.ContainsKey('Subject')) + { + $certFilters += @('($_.Subject -eq $Subject)') + } # if + + if ($PSBoundParameters.ContainsKey('Issuer')) + { + $certFilters += @('($_.Issuer -eq $Issuer)') + } # if + + if (-not $AllowExpired) + { + $certFilters += @('(((Get-Date) -le $_.NotAfter) -and ((Get-Date) -ge $_.NotBefore))') + } # if + + if ($PSBoundParameters.ContainsKey('DNSName')) + { + $certFilters += @('(@(Compare-Object -ReferenceObject $_.DNSNameList.Unicode -DifferenceObject $DNSName | Where-Object -Property SideIndicator -eq "=>").Count -eq 0)') + } # if + + if ($PSBoundParameters.ContainsKey('KeyUsage')) + { + $certFilters += @('(@(Compare-Object -ReferenceObject ($_.Extensions.KeyUsages -split ", ") -DifferenceObject $KeyUsage | Where-Object -Property SideIndicator -eq "=>").Count -eq 0)') + } # if + + if ($PSBoundParameters.ContainsKey('EnhancedKeyUsage')) + { + $certFilters += @('(@(Compare-Object -ReferenceObject ($_.EnhancedKeyUsageList.FriendlyName) -DifferenceObject $EnhancedKeyUsage | Where-Object -Property SideIndicator -eq "=>").Count -eq 0)') + } # if + + # Join all the filters together + $certFilterScript = '(' + ($certFilters -join ' -and ') + ')' + + Write-Verbose -Message ($LocalizedData.SearchingForCertificateUsingFilters ` + -f $store,$certFilterScript) + + $certs = Get-ChildItem -Path $certPath | + Where-Object -FilterScript ([ScriptBlock]::Create($certFilterScript)) + + # Sort the certificates + if ($certs.count -gt 1) + { + $certs = $certs | Sort-Object -Descending -Property 'NotAfter' + } # if + + return $certs +} # end function Find-Certificate diff --git a/Tests/TestHelper/CommonTestHelper.psm1 b/Tests/TestHelper/CommonTestHelper.psm1 new file mode 100644 index 000000000..ae3c83979 --- /dev/null +++ b/Tests/TestHelper/CommonTestHelper.psm1 @@ -0,0 +1,135 @@ +<# + .SYNOPSIS + Returns an invalid argument exception object + + .PARAMETER Message + The message explaining why this error is being thrown + + .PARAMETER ArgumentName + The name of the invalid argument that is causing this error to be thrown +#> +function Get-InvalidArgumentRecord +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [String] + $Message, + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [String] + $ArgumentName + ) + + $argumentException = New-Object -TypeName 'ArgumentException' -ArgumentList @( $Message, + $ArgumentName ) + $newObjectParams = @{ + TypeName = 'System.Management.Automation.ErrorRecord' + ArgumentList = @( $argumentException, $ArgumentName, 'InvalidArgument', $null ) + } + return New-Object @newObjectParams +} + +<# + .SYNOPSIS + Returns an invalid operation exception object + + .PARAMETER Message + The message explaining why this error is being thrown + + .PARAMETER ErrorRecord + The error record containing the exception that is causing this terminating error +#> +function Get-InvalidOperationRecord +{ + [CmdletBinding()] + param + ( + [ValidateNotNullOrEmpty()] + [String] + $Message, + + [ValidateNotNull()] + [System.Management.Automation.ErrorRecord] + $ErrorRecord + ) + + if ($null -eq $Message) + { + $invalidOperationException = New-Object -TypeName 'InvalidOperationException' + } + elseif ($null -eq $ErrorRecord) + { + $invalidOperationException = + New-Object -TypeName 'InvalidOperationException' -ArgumentList @( $Message ) + } + else + { + $invalidOperationException = + New-Object -TypeName 'InvalidOperationException' -ArgumentList @( $Message, + $ErrorRecord.Exception ) + } + + $newObjectParams = @{ + TypeName = 'System.Management.Automation.ErrorRecord' + ArgumentList = @( $invalidOperationException.ToString(), 'MachineStateIncorrect', + 'InvalidOperation', $null ) + } + return New-Object @newObjectParams +} + +<# + .SYNOPSIS + Some tests require a self-signed certificate to be created. However, the + New-SelfSignedCertificate cmdlet built into Windows Server 2012 R2 is too + limited to work for this process. + + Therefore an alternate method of creating self-signed certificates to meet the + reqirements. A script on Microsoft Script Center can be used for this but must + be downloaded: + https://gallery.technet.microsoft.com/scriptcenter/Self-signed-certificate-5920a7c6 + + This cmdlet will install the script if it is not available and dot source it. + + .PARAMETER OutputPath + The path to download the script to. If not provided will default to the current + users temp folder. + + .OUTPUTS + The path to the script that was downloaded. +#> +function Install-NewSelfSignedCertificateExScript +{ + [CmdletBinding()] + [OutputType([String])] + param + ( + [Parameter()] + [String] + $OutputPath = $env:Temp + ) + + $newSelfSignedCertURL = 'https://gallery.technet.microsoft.com/scriptcenter/Self-signed-certificate-5920a7c6/file/101251/2/New-SelfSignedCertificateEx.zip' + $newSelfSignedCertZip = Split-Path -Path $newSelfSignedCertURL -Leaf + $newSelfSignedCertZipPath = Join-Path -Path $OutputPath -ChildPath $newSelfSignedCertZip + $newSelfSignedCertScriptPath = Join-Path -Path $OutputPath -ChildPath 'New-SelfSignedCertificateEx.ps1' + if (-not (Test-Path -Path $newSelfSignedCertScriptPath)) + { + if (Test-Path -Path $newSelfSignedCertZip) + { + Remove-Item -Path $newSelfSignedCertZipPath -Force + } + Invoke-WebRequest -Uri $newSelfSignedCertURL -OutFile $newSelfSignedCertZipPath + Add-Type -AssemblyName System.IO.Compression.FileSystem + [System.IO.Compression.ZipFile]::ExtractToDirectory($newSelfSignedCertZipPath, $OutputPath) + } # if + return $newSelfSignedCertScriptPath +} # end function Install-NewSelfSignedCertificateExScript + +Export-ModuleMember -Function ` + Install-NewSelfSignedCertificateExScript, ` + Get-InvalidArgumentRecord, ` + Get-InvalidOperationRecord \ No newline at end of file diff --git a/Tests/Unit/Helper.tests.ps1 b/Tests/Unit/Helper.tests.ps1 new file mode 100644 index 000000000..c24ed90fc --- /dev/null +++ b/Tests/Unit/Helper.tests.ps1 @@ -0,0 +1,480 @@ +$script:ModuleName = 'Helper' +$script:DSCModuleName = 'xWebAdministration' + +#region HEADER +$script:moduleRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) + if ( (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests'))) -or ` + (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1'))) ) +{ + & git @('clone','https://github.com/PowerShell/DscResource.Tests.git',(Join-Path -Path $script:moduleRoot -ChildPath '\DSCResource.Tests\')) +} + +Import-Module (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1') -Force + +Import-Module (Join-Path -Path $script:moduleRoot -ChildPath 'Tests\TestHelper\CommonTestHelper.psm1') +Import-Module (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResources\Helper.psm1') +#endregion + +# Begin Testing +try +{ + InModuleScope $script:ModuleName { + + Describe "$DSCResourceName\Find-Certificate" { + + # Download and dot source the New-SelfSignedCertificateEx script + . (Install-NewSelfSignedCertificateExScript) + + # Generate the Valid certificate for testing but remove it from the store straight away + $certDNSNames = @('www.fabrikam.com', 'www.contoso.com') + $certDNSNamesReverse = @('www.contoso.com', 'www.fabrikam.com') + $certDNSNamesNoMatch = $certDNSNames + @('www.nothere.com') + $certKeyUsage = @('DigitalSignature','DataEncipherment') + $certKeyUsageReverse = @('DataEncipherment','DigitalSignature') + $certKeyUsageNoMatch = $certKeyUsage + @('KeyEncipherment') + $certEKU = @('Server Authentication','Client authentication') + $certEKUReverse = @('Client authentication','Server Authentication') + $certEKUNoMatch = $certEKU + @('Encrypting File System') + $certSubject = 'CN=contoso, DC=com' + $certFriendlyName = 'Contoso Test Cert' + $validCert = New-SelfSignedCertificateEx ` + -Subject $certSubject ` + -KeyUsage $certKeyUsage ` + -KeySpec 'Exchange' ` + -EKU $certEKU ` + -SubjectAlternativeName $certDNSNames ` + -FriendlyName $certFriendlyName ` + -StoreLocation 'CurrentUser' ` + -Exportable + # Pull the generated certificate from the store so we have the friendlyname + $validThumbprint = $validCert.Thumbprint + $validCert = Get-Item -Path "cert:\CurrentUser\My\$validThumbprint" + Remove-Item -Path $validCert.PSPath -Force + + # Generate the Expired certificate for testing but remove it from the store straight away + $expiredCert = New-SelfSignedCertificateEx ` + -Subject $certSubject ` + -KeyUsage $certKeyUsage ` + -KeySpec 'Exchange' ` + -EKU $certEKU ` + -SubjectAlternativeName $certDNSNames ` + -FriendlyName $certFriendlyName ` + -NotBefore ((Get-Date) - (New-TimeSpan -Days 2)) ` + -NotAfter ((Get-Date) - (New-TimeSpan -Days 1)) ` + -StoreLocation 'CurrentUser' ` + -Exportable + # Pull the generated certificate from the store so we have the friendlyname + $expiredThumbprint = $expiredCert.Thumbprint + $expiredCert = Get-Item -Path "cert:\CurrentUser\My\$expiredThumbprint" + Remove-Item -Path $expiredCert.PSPath -Force + + $nocertThumbprint = '1111111111111111111111111111111111111111' + + # Dynamic mock content for Get-ChildItem + $mockGetChildItem = { + switch ( $Path ) + { + 'cert:\LocalMachine\My' + { + return @( $validCert ) + } + + 'cert:\LocalMachine\NoCert' + { + return @() + } + + 'cert:\LocalMachine\TwoCerts' + { + return @( $expiredCert, $validCert ) + } + + 'cert:\LocalMachine\Expired' + { + return @( $expiredCert ) + } + + default + { + throw 'mock called with unexpected value {0}' -f $Path + } + } + } + + BeforeEach { + Mock ` + -CommandName Test-Path ` + -MockWith { $true } + + Mock ` + -CommandName Get-ChildItem ` + -MockWith $mockGetChildItem + } + + Context 'Thumbprint only is passed and matching certificate exists' { + It 'should not throw exception' { + { $script:result = Find-Certificate -Thumbprint $validThumbprint } | Should Not Throw + } + + It 'should return expected certificate' { + $script:result.Thumbprint | Should Be $validThumbprint + } + + It 'should call expected mocks' { + Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 + Assert-MockCalled -CommandName Get-ChildItem -Exactly -Times 1 + } + } + + Context 'Thumbprint only is passed and matching certificate does not exist' { + It 'should not throw exception' { + { $script:result = Find-Certificate -Thumbprint $nocertThumbprint } | Should Not Throw + } + + It 'should return null' { + $script:result | Should BeNullOrEmpty + } + + It 'should call expected mocks' { + Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 + Assert-MockCalled -CommandName Get-ChildItem -Exactly -Times 1 + } + } + + Context 'FriendlyName only is passed and matching certificate exists' { + It 'should not throw exception' { + { $script:result = Find-Certificate -FriendlyName $certFriendlyName } | Should Not Throw + } + + It 'should return expected certificate' { + $script:result.Thumbprint | Should Be $validThumbprint + } + + It 'should call expected mocks' { + Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 + Assert-MockCalled -CommandName Get-ChildItem -Exactly -Times 1 + } + } + + Context 'FriendlyName only is passed and matching certificate does not exist' { + It 'should not throw exception' { + { $script:result = Find-Certificate -FriendlyName 'Does Not Exist' } | Should Not Throw + } + + It 'should return null' { + $script:result | Should BeNullOrEmpty + } + + It 'should call expected mocks' { + Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 + Assert-MockCalled -CommandName Get-ChildItem -Exactly -Times 1 + } + } + + Context 'Subject only is passed and matching certificate exists' { + It 'should not throw exception' { + { $script:result = Find-Certificate -Subject $certSubject } | Should Not Throw + } + + It 'should return expected certificate' { + $script:result.Thumbprint | Should Be $validThumbprint + } + + It 'should call expected mocks' { + Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 + Assert-MockCalled -CommandName Get-ChildItem -Exactly -Times 1 + } + } + + Context 'Subject only is passed and matching certificate does not exist' { + It 'should not throw exception' { + { $script:result = Find-Certificate -Subject 'CN=Does Not Exist' } | Should Not Throw + } + + It 'should return null' { + $script:result | Should BeNullOrEmpty + } + + It 'should call expected mocks' { + Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 + Assert-MockCalled -CommandName Get-ChildItem -Exactly -Times 1 + } + } + + Context 'Issuer only is passed and matching certificate exists' { + It 'should not throw exception' { + { $script:result = Find-Certificate -Issuer $certSubject } | Should Not Throw + } + + It 'should return expected certificate' { + $script:result.Thumbprint | Should Be $validThumbprint + } + + It 'should call expected mocks' { + Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 + Assert-MockCalled -CommandName Get-ChildItem -Exactly -Times 1 + } + } + + Context 'Issuer only is passed and matching certificate does not exist' { + It 'should not throw exception' { + { $script:result = Find-Certificate -Issuer 'CN=Does Not Exist' } | Should Not Throw + } + + It 'should return null' { + $script:result | Should BeNullOrEmpty + } + + It 'should call expected mocks' { + Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 + Assert-MockCalled -CommandName Get-ChildItem -Exactly -Times 1 + } + } + + Context 'DNSName only is passed and matching certificate exists' { + It 'should not throw exception' { + { $script:result = Find-Certificate -DnsName $certDNSNames } | Should Not Throw + } + + It 'should return expected certificate' { + $script:result.Thumbprint | Should Be $validThumbprint + } + + It 'should call expected mocks' { + Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 + Assert-MockCalled -CommandName Get-ChildItem -Exactly -Times 1 + } + } + + Context 'DNSName only is passed in reversed order and matching certificate exists' { + It 'should not throw exception' { + { $script:result = Find-Certificate -DnsName $certDNSNamesReverse } | Should Not Throw + } + + It 'should return expected certificate' { + $script:result.Thumbprint | Should Be $validThumbprint + } + + It 'should call expected mocks' { + Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 + Assert-MockCalled -CommandName Get-ChildItem -Exactly -Times 1 + } + } + + Context 'DNSName only is passed with only one matching DNS name and matching certificate exists' { + It 'should not throw exception' { + { $script:result = Find-Certificate -DnsName $certDNSNames[0] } | Should Not Throw + } + + It 'should return expected certificate' { + $script:result.Thumbprint | Should Be $validThumbprint + } + + It 'should call expected mocks' { + Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 + Assert-MockCalled -CommandName Get-ChildItem -Exactly -Times 1 + } + } + + Context 'DNSName only is passed but an entry is missing and matching certificate does not exist' { + It 'should not throw exception' { + { $script:result = Find-Certificate -DnsName $certDNSNamesNoMatch } | Should Not Throw + } + + It 'should return null' { + $script:result | Should BeNullOrEmpty + } + + It 'should call expected mocks' { + Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 + Assert-MockCalled -CommandName Get-ChildItem -Exactly -Times 1 + } + } + + Context 'KeyUsage only is passed and matching certificate exists' { + It 'should not throw exception' { + { $script:result = Find-Certificate -KeyUsage $certKeyUsage } | Should Not Throw + } + + It 'should return expected certificate' { + $script:result.Thumbprint | Should Be $validThumbprint + } + + It 'should call expected mocks' { + Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 + Assert-MockCalled -CommandName Get-ChildItem -Exactly -Times 1 + } + } + + Context 'KeyUsage only is passed in reversed order and matching certificate exists' { + It 'should not throw exception' { + { $script:result = Find-Certificate -KeyUsage $certKeyUsageReverse } | Should Not Throw + } + + It 'should return expected certificate' { + $script:result.Thumbprint | Should Be $validThumbprint + } + + It 'should call expected mocks' { + Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 + Assert-MockCalled -CommandName Get-ChildItem -Exactly -Times 1 + } + } + + Context 'KeyUsage only is passed with only one matching DNS name and matching certificate exists' { + It 'should not throw exception' { + { $script:result = Find-Certificate -KeyUsage $certKeyUsage[0] } | Should Not Throw + } + + It 'should return expected certificate' { + $script:result.Thumbprint | Should Be $validThumbprint + } + + It 'should call expected mocks' { + Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 + Assert-MockCalled -CommandName Get-ChildItem -Exactly -Times 1 + } + } + + Context 'KeyUsage only is passed but an entry is missing and matching certificate does not exist' { + It 'should not throw exception' { + { $script:result = Find-Certificate -KeyUsage $certKeyUsageNoMatch } | Should Not Throw + } + + It 'should return null' { + $script:result | Should BeNullOrEmpty + } + + It 'should call expected mocks' { + Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 + Assert-MockCalled -CommandName Get-ChildItem -Exactly -Times 1 + } + } + + Context 'EnhancedKeyUsage only is passed and matching certificate exists' { + It 'should not throw exception' { + { $script:result = Find-Certificate -EnhancedKeyUsage $certEKU } | Should Not Throw + } + + It 'should return expected certificate' { + $script:result.Thumbprint | Should Be $validThumbprint + } + + It 'should call expected mocks' { + Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 + Assert-MockCalled -CommandName Get-ChildItem -Exactly -Times 1 + } + } + + Context 'EnhancedKeyUsage only is passed in reversed order and matching certificate exists' { + It 'should not throw exception' { + { $script:result = Find-Certificate -EnhancedKeyUsage $certEKUReverse } | Should Not Throw + } + + It 'should return expected certificate' { + $script:result.Thumbprint | Should Be $validThumbprint + } + + It 'should call expected mocks' { + Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 + Assert-MockCalled -CommandName Get-ChildItem -Exactly -Times 1 + } + } + + Context 'EnhancedKeyUsage only is passed with only one matching DNS name and matching certificate exists' { + It 'should not throw exception' { + { $script:result = Find-Certificate -EnhancedKeyUsage $certEKU[0] } | Should Not Throw + } + + It 'should return expected certificate' { + $script:result.Thumbprint | Should Be $validThumbprint + } + + It 'should call expected mocks' { + Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 + Assert-MockCalled -CommandName Get-ChildItem -Exactly -Times 1 + } + } + + Context 'EnhancedKeyUsage only is passed but an entry is missing and matching certificate does not exist' { + It 'should not throw exception' { + { $script:result = Find-Certificate -EnhancedKeyUsage $certEKUNoMatch } | Should Not Throw + } + + It 'should return null' { + $script:result | Should BeNullOrEmpty + } + + It 'should call expected mocks' { + Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 + Assert-MockCalled -CommandName Get-ChildItem -Exactly -Times 1 + } + } + + Context 'Thumbprint only is passed and matching certificate does not exist in the store' { + It 'should not throw exception' { + { $script:result = Find-Certificate -Thumbprint $validThumbprint -Store 'NoCert'} | Should Not Throw + } + + It 'should return null' { + $script:result | Should BeNullOrEmpty + } + + It 'should call expected mocks' { + Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 + Assert-MockCalled -CommandName Get-ChildItem -Exactly -Times 1 + } + } + + Context 'FriendlyName only is passed and both valid and expired certificates exist' { + It 'should not throw exception' { + { $script:result = Find-Certificate -FriendlyName $certFriendlyName -Store 'TwoCerts' } | Should Not Throw + } + + It 'should return expected certificate' { + $script:result.Thumbprint | Should Be $validThumbprint + } + + It 'should call expected mocks' { + Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 + Assert-MockCalled -CommandName Get-ChildItem -Exactly -Times 1 + } + } + + Context 'FriendlyName only is passed and only expired certificates exist' { + It 'should not throw exception' { + { $script:result = Find-Certificate -FriendlyName $certFriendlyName -Store 'Expired' } | Should Not Throw + } + + It 'should return expected certificate' { + $script:result | Should BeNullOrEmpty + } + + It 'should call expected mocks' { + Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 + Assert-MockCalled -CommandName Get-ChildItem -Exactly -Times 1 + } + } + + Context 'FriendlyName only is passed and only expired certificates exist but allowexpired passed' { + It 'should not throw exception' { + { $script:result = Find-Certificate -FriendlyName $certFriendlyName -Store 'Expired' -AllowExpired:$true } | Should Not Throw + } + + It 'should return expected certificate' { + $script:result.Thumbprint | Should Be $expiredThumbprint + } + + It 'should call expected mocks' { + Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 + Assert-MockCalled -CommandName Get-ChildItem -Exactly -Times 1 + } + } + } + } +} +finally +{ + #region FOOTER + #endregion +} \ No newline at end of file From 6f40f3233ac7dcfbb3ca7b908bc01dd5273fbb80 Mon Sep 17 00:00:00 2001 From: Chris Gardner Date: Tue, 2 May 2017 21:53:26 +0100 Subject: [PATCH 02/15] Adding support for finding cert by subject. Added Find-Certificate as helper (thanks to @PlagueHO) and incorporated into ConvertTo-WebBinding. --- DSCResources/MSFT_xWebsite/MSFT_xWebsite.psm1 | 51 +++++++++-- Tests/Unit/MSFT_xWebsite.Tests.ps1 | 85 ++++++++++++++++++- 2 files changed, 129 insertions(+), 7 deletions(-) diff --git a/DSCResources/MSFT_xWebsite/MSFT_xWebsite.psm1 b/DSCResources/MSFT_xWebsite/MSFT_xWebsite.psm1 index 80a950b8a..af0acf779 100644 --- a/DSCResources/MSFT_xWebsite/MSFT_xWebsite.psm1 +++ b/DSCResources/MSFT_xWebsite/MSFT_xWebsite.psm1 @@ -23,6 +23,7 @@ data LocalizedData ErrorWebBindingMissingBindingInformation = The BindingInformation property is required for bindings of type "{0}". ErrorWebBindingMissingCertificateThumbprint = The CertificateThumbprint property is required for bindings of type "{0}". ErrorWebBindingMissingSniHostName = The HostName property is required for use with Server Name Indication. + ErrorWebBindingInvalidCertificateSubject = The Subject "{0}" provided is not found on this host in store "{1}" ErrorWebsitePreloadFailure = Failure to set Preload on Website "{0}". Error: "{1}". ErrorWebsiteAutoStartFailure = Failure to set AutoStart on Website "{0}". Error: "{1}". ErrorWebsiteAutoStartProviderFailure = Failure to set AutoStartProvider on Website "{0}". Error: "{1}". @@ -1450,11 +1451,24 @@ function ConvertTo-WebBinding { if ([String]::IsNullOrEmpty($binding.CertificateThumbprint)) { - $errorMessage = $LocalizedData.ErrorWebBindingMissingCertificateThumbprint ` - -f $binding.Protocol - New-TerminatingError -ErrorId 'WebBindingMissingCertificateThumbprint' ` - -ErrorMessage $errorMessage ` - -ErrorCategory 'InvalidArgument' + If ($Binding.CertificateSubject) + { + if ($binding.CertificateSubject.substring(0,3) -ne 'CN=') + { + $binding.CertificateSubject = "CN=$($Binding.CertificateSubject)" + } + $FindCertificateSplat = @{ + Subject = $Binding.CertificateSubject + } + } + else + { + $errorMessage = $LocalizedData.ErrorWebBindingMissingCertificateThumbprint ` + -f $binding.Protocol + New-TerminatingError -ErrorId 'WebBindingMissingCertificateThumbprint' ` + -ErrorMessage $errorMessage ` + -ErrorCategory 'InvalidArgument' + } } if ([String]::IsNullOrEmpty($binding.CertificateStoreName)) @@ -1469,8 +1483,33 @@ function ConvertTo-WebBinding $certificateStoreName = $binding.CertificateStoreName } + if ($FindCertificateSplat) + { + $FindCertificateSplat.Add('Store',$CertificateStoreName) + $Certificate = Find-Certificate @FindCertificateSplat + if ($Certificate) + { + $certificateHash = $Certificate.Thumbprint + } + else + { + $errorMessage = $LocalizedData.ErrorWebBindingInvalidCertificateSubject ` + -f $binding.CertificateSubject, $binding.CertificateStoreName + New-TerminatingError -ErrorId 'WebBindingInvalidCertificateSubject' ` + -ErrorMessage $errorMessage ` + -ErrorCategory 'InvalidArgument' + } + } + # Remove the Left-to-Right Mark character - $certificateHash = $binding.CertificateThumbprint -replace '^\u200E' + if ($certificateHash) + { + $certificateHash = $certificateHash -replace '^\u200E' + } + else + { + $certificateHash = $binding.CertificateThumbprint -replace '^\u200E' + } $outputObject.Add('certificateHash', [String]$certificateHash) $outputObject.Add('certificateStoreName', [String]$certificateStoreName) diff --git a/Tests/Unit/MSFT_xWebsite.Tests.ps1 b/Tests/Unit/MSFT_xWebsite.Tests.ps1 index 47f3c9267..6ca6f84b9 100644 --- a/Tests/Unit/MSFT_xWebsite.Tests.ps1 +++ b/Tests/Unit/MSFT_xWebsite.Tests.ps1 @@ -1874,7 +1874,7 @@ try } } - Describe "$script:DSCResourceName\ConvertTo-WebBinding" { + Describe "$script:DSCResourceName\ConvertTo-WebBinding" -Tag 'ConvertTo' { Context 'Expected behaviour' { $MockBindingInfo = @( New-CimInstance ` @@ -2053,7 +2053,90 @@ try $ErrorRecord = New-Object ` -TypeName System.Management.Automation.ErrorRecord ` -ArgumentList $Exception, $ErrorId, $ErrorCategory, $null + { ConvertTo-WebBinding -InputObject $MockBindingInfo } | Should Throw $ErrorRecord + } + } + + Context 'Protocol is HTTPS and CertificateSubject is specified' { + $MockBindingInfo = @( + New-CimInstance -ClassName MSFT_xWebBindingInformation ` + -Namespace root/microsoft/Windows/DesiredStateConfiguration ` + -Property @{ + Protocol = 'https' + CertificateSubject = 'TestCertificate' + } -ClientOnly + ) + + Mock Find-Certificate -MockWith { + return [PSCustomObject]@{ + Thumbprint = 'C65CE51E20C523DEDCE979B9922A0294602D9D5C' + } + } + + It 'should not throw an error' { + { ConvertTo-WebBinding -InputObject $MockBindingInfo } | Should Not Throw + } + It 'should return the correct thumbprint' { + $Result = ConvertTo-WebBinding -InputObject $MockBindingInfo + $Result.certificateHash | Should Be 'C65CE51E20C523DEDCE979B9922A0294602D9D5C' + } + It 'Should call Find-Certificate mock' { + Assert-MockCalled -CommandName Find-Certificate -Times 1 + } + } + + Context 'Protocol is HTTPS and full CN of CertificateSubject is specified' { + $MockBindingInfo = @( + New-CimInstance -ClassName MSFT_xWebBindingInformation ` + -Namespace root/microsoft/Windows/DesiredStateConfiguration ` + -Property @{ + Protocol = 'https' + CertificateSubject = 'CN=TestCertificate' + } -ClientOnly + ) + Mock Find-Certificate -MockWith { + return [PSCustomObject]@{ + Thumbprint = 'C65CE51E20C523DEDCE979B9922A0294602D9D5C' + } + } + + It 'should not throw an error' { + { ConvertTo-WebBinding -InputObject $MockBindingInfo } | Should Not Throw + } + It 'should return the correct thumbprint' { + $Result = ConvertTo-WebBinding -InputObject $MockBindingInfo + $Result.certificateHash | Should Be 'C65CE51E20C523DEDCE979B9922A0294602D9D5C' + } + It 'Should call Find-Certificate mock' { + Assert-MockCalled -CommandName Find-Certificate -Times 1 + } + } + + Context 'Protocol is HTTPS and invalid CertificateSubject is specified' { + $MockBindingInfo = @( + New-CimInstance -ClassName MSFT_xWebBindingInformation ` + -Namespace root/microsoft/Windows/DesiredStateConfiguration ` + -Property @{ + Protocol = 'https' + CertificateSubject = 'TestCertificate' + CertificateStoreName = 'MY' + } -ClientOnly + ) + + Mock Find-Certificate + + It 'should throw the correct error' { + $CertificateSubject = "CN=$($MockBindingInfo.CertificateSubject)" + $ErrorId = 'WebBindingInvalidCertificateSubject' + $ErrorCategory = [System.Management.Automation.ErrorCategory]::InvalidArgument + $ErrorMessage = $LocalizedData.ErrorWebBindingInvalidCertificateSubject -f $CertificateSubject, $MockBindingInfo.CertificateStoreName + $Exception = New-Object ` + -TypeName System.InvalidOperationException ` + -ArgumentList $ErrorMessage + $ErrorRecord = New-Object ` + -TypeName System.Management.Automation.ErrorRecord ` + -ArgumentList $Exception, $ErrorId, $ErrorCategory, $null { ConvertTo-WebBinding -InputObject $MockBindingInfo } | Should Throw $ErrorRecord } } From 3a134d96ee0f503095d560bcb569e99cddf16f68 Mon Sep 17 00:00:00 2001 From: Chris Gardner Date: Tue, 2 May 2017 22:01:29 +0100 Subject: [PATCH 03/15] Adding additional line as required --- Tests/TestHelper/CommonTestHelper.psm1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Tests/TestHelper/CommonTestHelper.psm1 b/Tests/TestHelper/CommonTestHelper.psm1 index ae3c83979..c89c98b82 100644 --- a/Tests/TestHelper/CommonTestHelper.psm1 +++ b/Tests/TestHelper/CommonTestHelper.psm1 @@ -132,4 +132,5 @@ function Install-NewSelfSignedCertificateExScript Export-ModuleMember -Function ` Install-NewSelfSignedCertificateExScript, ` Get-InvalidArgumentRecord, ` - Get-InvalidOperationRecord \ No newline at end of file + Get-InvalidOperationRecord + \ No newline at end of file From e499e89e8edf74e658d9047fa3ca224aa5b1463b Mon Sep 17 00:00:00 2001 From: Chris Gardner Date: Tue, 2 May 2017 22:09:37 +0100 Subject: [PATCH 04/15] Adding new line using helper function --- Tests/TestHelper/CommonTestHelper.psm1 | 1 - 1 file changed, 1 deletion(-) diff --git a/Tests/TestHelper/CommonTestHelper.psm1 b/Tests/TestHelper/CommonTestHelper.psm1 index c89c98b82..8dd376e4d 100644 --- a/Tests/TestHelper/CommonTestHelper.psm1 +++ b/Tests/TestHelper/CommonTestHelper.psm1 @@ -133,4 +133,3 @@ Export-ModuleMember -Function ` Install-NewSelfSignedCertificateExScript, ` Get-InvalidArgumentRecord, ` Get-InvalidOperationRecord - \ No newline at end of file From 25e427b776c1a7286fc7e47662a9867c2e6a2f31 Mon Sep 17 00:00:00 2001 From: Chris Gardner Date: Tue, 2 May 2017 22:11:35 +0100 Subject: [PATCH 05/15] Adding missing new lines --- Tests/Unit/Helper.tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/Unit/Helper.tests.ps1 b/Tests/Unit/Helper.tests.ps1 index c24ed90fc..31e66846a 100644 --- a/Tests/Unit/Helper.tests.ps1 +++ b/Tests/Unit/Helper.tests.ps1 @@ -477,4 +477,4 @@ finally { #region FOOTER #endregion -} \ No newline at end of file +} From a9ff64e8be38d096718b940930d22f52b367dd0f Mon Sep 17 00:00:00 2001 From: Chris Gardner Date: Tue, 2 May 2017 22:19:44 +0100 Subject: [PATCH 06/15] Updating schema with new property for WebBindingInformation --- DSCResources/MSFT_xWebsite/MSFT_xWebsite.schema.mof | 1 + 1 file changed, 1 insertion(+) diff --git a/DSCResources/MSFT_xWebsite/MSFT_xWebsite.schema.mof b/DSCResources/MSFT_xWebsite/MSFT_xWebsite.schema.mof index fc452e3e4..05a148256 100644 --- a/DSCResources/MSFT_xWebsite/MSFT_xWebsite.schema.mof +++ b/DSCResources/MSFT_xWebsite/MSFT_xWebsite.schema.mof @@ -7,6 +7,7 @@ class MSFT_xWebBindingInformation [Write] UInt16 Port; [Write] String HostName; [Write] String CertificateThumbprint; + [Write] String CertificateSubject; [Write,ValueMap{"My", "WebHosting"},Values{"My", "WebHosting"}] String CertificateStoreName; [Write,ValueMap{"0","1","2","3"},Values{"0","1","2","3"}] String SslFlags; }; From 5884bbbd0876383eea985e64f46e1e43d55028a9 Mon Sep 17 00:00:00 2001 From: Chris Gardner Date: Tue, 2 May 2017 22:28:34 +0100 Subject: [PATCH 07/15] Updating ReadMe --- README.md | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/README.md b/README.md index 37a0ec3a7..c4aa71761 100644 --- a/README.md +++ b/README.md @@ -142,6 +142,7 @@ Please check out common DSC Resources [contributing guidelines](https://github.c * **Port**: The port of the binding. The value must be a positive integer between `1` and `65535`. This property is only applicable for `http` (the default value is `80`) and `https` (the default value is `443`) bindings. * **HostName**: The host name of the binding. This property is only applicable for `http` and `https` bindings. * **CertificateThumbprint**: The thumbprint of the certificate. This property is only applicable for `https` bindings. + * **CertificateSubject**: The subject of the certificate if the thumbprint isn't known. This property is only applicable for `https` bindings. * **CertificateStoreName**: The name of the certificate store where the certificate is located. This property is only applicable for `https` bindings. The acceptable values for this property are: `My`, `WebHosting`. The default value is `My`. * **SslFlags**: The type of binding used for Secure Sockets Layer (SSL) certificates. This property is supported in IIS 8.0 or later, and is only applicable for `https` bindings. The acceptable values for this property are: * **0**: The default value. The secure connection be made using an IP/Port combination. Only one certificate can be bound to a combination of IP address and the port. @@ -707,6 +708,91 @@ Configuration Sample_xWebsite_NewWebsite } ``` +When specifying a HTTPS web binding you can also specify a certifcate subject, for cases where the certificate +is being generated by the same configuration using something like xCertReq. + +```powershell +Configuration Sample_xWebsite_NewWebsite +{ + param + ( + # Target nodes to apply the configuration + [string[]]$NodeName = 'localhost', + # Name of the website to create + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [String]$WebSiteName, + # Source Path for Website content + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [String]$SourcePath, + # Destination path for Website content + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [String]$DestinationPath + ) + + # Import the module that defines custom resources + Import-DscResource -Module xWebAdministration + Node $NodeName + { + # Install the IIS role + WindowsFeature IIS + { + Ensure = "Present" + Name = "Web-Server" + } + + # Install the ASP .NET 4.5 role + WindowsFeature AspNet45 + { + Ensure = "Present" + Name = "Web-Asp-Net45" + } + + # Stop the default website + xWebsite DefaultSite + { + Ensure = "Present" + Name = "Default Web Site" + State = "Stopped" + PhysicalPath = "C:\inetpub\wwwroot" + DependsOn = "[WindowsFeature]IIS" + } + + # Copy the website content + File WebContent + { + Ensure = "Present" + SourcePath = $SourcePath + DestinationPath = $DestinationPath + Recurse = $true + Type = "Directory" + DependsOn = "[WindowsFeature]AspNet45" + } + + # Create the new Website with HTTPS + xWebsite NewWebsite + { + Ensure = "Present" + Name = $WebSiteName + State = "Started" + PhysicalPath = $DestinationPath + BindingInfo = @( + MSFT_xWebBindingInformation + { + Protocol = "HTTPS" + Port = 8444 + CertificateSubject = "CN=CertificateSubject" + CertificateStoreName = "MY" + } + ) + DependsOn = "[File]WebContent" + } + } +} +``` + ### Creating the default website using configuration data In this example, we’ve moved the parameters used to generate the website into a configuration data file. From 96fb1997e96435f303a6fa3ac621bd99a9dd28ae Mon Sep 17 00:00:00 2001 From: Chris Gardner Date: Wed, 3 May 2017 12:13:26 +0100 Subject: [PATCH 08/15] Update Integration test configuration --- Tests/Integration/MSFT_xWebsite.config.ps1 | 22 ++++++++++++++++++++- Tests/Integration/MSFT_xWebsite.config.psd1 | 1 + 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/Tests/Integration/MSFT_xWebsite.config.ps1 b/Tests/Integration/MSFT_xWebsite.config.ps1 index 588a4bb87..360b00a33 100644 --- a/Tests/Integration/MSFT_xWebsite.config.ps1 +++ b/Tests/Integration/MSFT_xWebsite.config.ps1 @@ -50,6 +50,16 @@ configuration MSFT_xWebsite_Present_Started CertificateThumbprint = $CertificateThumbprint CertificateStoreName = $Node.CertificateStoreName SslFlags = $Node.SslFlags + } + MSFT_xWebBindingInformation + { + Protocol = $Node.HTTPSProtocol + Port = $Node.HTTPSPort2 + IPAddress = '*' + Hostname = $Node.HTTPSHostname + CertificateSubject = $Node.HTTPSHostname + CertificateStoreName = $Node.CertificateStoreName + SslFlags = $Node.SslFlags }) DefaultPage = $Node.DefaultPage EnabledProtocols = $Node.EnabledProtocols @@ -113,7 +123,17 @@ configuration MSFT_xWebsite_Present_Stopped CertificateThumbprint = $CertificateThumbprint CertificateStoreName = $Node.CertificateStoreName SslFlags = $Node.SslFlags - }) + } + MSFT_xWebBindingInformation + { + Protocol = $Node.HTTPSProtocol + Port = $Node.HTTPSPort2 + IPAddress = '*' + Hostname = $Node.HTTPSHostname + CertificateSubject = $Node.HTTPSHostname + CertificateStoreName = $Node.CertificateStoreName + SslFlags = $Node.SslFlags + }) DefaultPage = $Node.DefaultPage EnabledProtocols = $Node.EnabledProtocols PhysicalPath = $Node.PhysicalPath diff --git a/Tests/Integration/MSFT_xWebsite.config.psd1 b/Tests/Integration/MSFT_xWebsite.config.psd1 index 716eed53b..b94e07c83 100644 --- a/Tests/Integration/MSFT_xWebsite.config.psd1 +++ b/Tests/Integration/MSFT_xWebsite.config.psd1 @@ -24,6 +24,7 @@ HTTP2Hostname = 'http2.website' HTTPSProtocol = 'https' HTTPSPort = '443' + HTTPSPort2 = '8444' HTTPSHostname = 'https.website' CertificateStoreName = 'MY' SslFlags = '1' From 26082407eff61ab4dfbd738b8858c4f4af747bf8 Mon Sep 17 00:00:00 2001 From: Chris Gardner Date: Wed, 3 May 2017 12:20:19 +0100 Subject: [PATCH 09/15] Update integration tests to account for new settings --- Tests/Integration/MSFT_xWebsite.Integration.Tests.ps1 | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Tests/Integration/MSFT_xWebsite.Integration.Tests.ps1 b/Tests/Integration/MSFT_xWebsite.Integration.Tests.ps1 index 5ff934609..85ad2beff 100644 --- a/Tests/Integration/MSFT_xWebsite.Integration.Tests.ps1 +++ b/Tests/Integration/MSFT_xWebsite.Integration.Tests.ps1 @@ -110,12 +110,16 @@ try $result.bindings.Collection.BindingInformation[0] | Should Match $dscConfig.AllNodes.HTTP1Hostname $result.bindings.Collection.BindingInformation[1] | Should Match $dscConfig.AllNodes.HTTP2Hostname $result.bindings.Collection.BindingInformation[2] | Should Match $dscConfig.AllNodes.HTTPSHostname + $result.bindings.Collection.BindingInformation[3] | Should Match $dscConfig.AllNodes.HTTPSHostname $result.bindings.Collection.BindingInformation[0] | Should Match $dscConfig.AllNodes.HTTPPort $result.bindings.Collection.BindingInformation[1] | Should Match $dscConfig.AllNodes.HTTPPort $result.bindings.Collection.BindingInformation[2] | Should Match $dscConfig.AllNodes.HTTPSPort $result.bindings.Collection.certificateHash[2] | Should Be $selfSignedCert.Thumbprint $result.bindings.Collection.certificateStoreName[2] | Should Be $dscConfig.AllNodes.CertificateStoreName - + $result.bindings.Collection.BindingInformation[3] | Should Match $dscConfig.AllNodes.HTTPSPort2 + $result.bindings.Collection.certificateHash[3] | Should Be $selfSignedCert.Thumbprint + $result.bindings.Collection.certificateStoreName[3] | Should Be $dscConfig.AllNodes.CertificateStoreName + #Test DefaultPage is correct $defultPages[0] | Should Match $dscConfig.AllNodes.DefaultPage @@ -178,11 +182,15 @@ try $result.bindings.Collection.BindingInformation[0] | Should Match $dscConfig.AllNodes.HTTP1Hostname $result.bindings.Collection.BindingInformation[1] | Should Match $dscConfig.AllNodes.HTTP2Hostname $result.bindings.Collection.BindingInformation[2] | Should Match $dscConfig.AllNodes.HTTPSHostname + $result.bindings.Collection.BindingInformation[3] | Should Match $dscConfig.AllNodes.HTTPSHostname $result.bindings.Collection.BindingInformation[0] | Should Match $dscConfig.AllNodes.HTTPPort $result.bindings.Collection.BindingInformation[1] | Should Match $dscConfig.AllNodes.HTTPPort $result.bindings.Collection.BindingInformation[2] | Should Match $dscConfig.AllNodes.HTTPSPort $result.bindings.Collection.certificateHash[2] | Should Be $selfSignedCert.Thumbprint $result.bindings.Collection.certificateStoreName[2] | Should Be $dscConfig.AllNodes.CertificateStoreName + $result.bindings.Collection.BindingInformation[3] | Should Match $dscConfig.AllNodes.HTTPSPort2 + $result.bindings.Collection.certificateHash[3] | Should Be $selfSignedCert.Thumbprint + $result.bindings.Collection.certificateStoreName[3] | Should Be $dscConfig.AllNodes.CertificateStoreName #Test DefaultPage is correct $defultPages[0] | Should Match $dscConfig.AllNodes.DefaultPage From 0146d3e68cce18ecacb68194927286742a261220 Mon Sep 17 00:00:00 2001 From: Remco Eissing Date: Fri, 27 Oct 2017 19:31:45 +0200 Subject: [PATCH 10/15] Get-TargetResource for xWebAppPoolDefaults now returns data, fixes /PowerShell/xWebAdministration#311 --- .../MSFT_xWebAppPoolDefaults.psm1 | 43 ++++++---- README.md | 2 + Tests/Unit/MSFT_xWebAppPoolDefaults.tests.ps1 | 80 +++++++++++++++++++ 3 files changed, 110 insertions(+), 15 deletions(-) create mode 100644 Tests/Unit/MSFT_xWebAppPoolDefaults.tests.ps1 diff --git a/DSCResources/MSFT_xWebAppPoolDefaults/MSFT_xWebAppPoolDefaults.psm1 b/DSCResources/MSFT_xWebAppPoolDefaults/MSFT_xWebAppPoolDefaults.psm1 index 3328e89d9..30026a673 100644 --- a/DSCResources/MSFT_xWebAppPoolDefaults/MSFT_xWebAppPoolDefaults.psm1 +++ b/DSCResources/MSFT_xWebAppPoolDefaults/MSFT_xWebAppPoolDefaults.psm1 @@ -24,18 +24,19 @@ function Get-TargetResource [OutputType([System.Collections.Hashtable])] param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [ValidateSet('Machine')] - [String] $ApplyTo + [System.String] + $ApplyTo ) - + Assert-Module Write-Verbose -Message $LocalizedData.VerboseGetTargetResource return @{ ManagedRuntimeVersion = (Get-Value -Path '' -Name 'managedRuntimeVersion') - IdentityType = ( Get-Value -Path 'processModel' -Name 'identityType') + IdentityType = (Get-Value -Path 'processModel' -Name 'identityType') } } @@ -186,28 +187,40 @@ function Set-Value function Get-Value { - [CmdletBinding()] param - ( - [String] $Path, - - [String] $Name + ( + [Parameter(Mandatory = $true)] + [AllowEmptyString()] + [System.String] + $Path, + + [Parameter(Mandatory = $true)] + [System.String] + $Name ) + if ($Path -ne '') { - if ($Path -ne '') - { - $Path = '/' + $Path - } + $Path = '/' + $Path + } - return Get-WebConfigurationProperty ` + $result = Get-WebConfigurationProperty ` -PSPath 'MACHINE/WEBROOT/APPHOST' ` -Filter "system.applicationHost/applicationPools/applicationPoolDefaults$Path" ` -Name $Name - + + if ($result -is [Microsoft.IIs.PowerShell.Framework.ConfigurationAttribute]) + { + return $result.Value + } else { + return $result } + return Get-WebConfigurationProperty ` + -PSPath 'MACHINE/WEBROOT/APPHOST' ` + -Filter "system.applicationHost/applicationPools/applicationPoolDefaults$Path" ` + -Name $Name } #endregion diff --git a/README.md b/README.md index f342e4385..430e7fae5 100644 --- a/README.md +++ b/README.md @@ -240,6 +240,8 @@ Please check out common DSC Resources [contributing guidelines](https://github.c ### Unreleased +* **xWebAppPoolDefaults** now returns values. Bugfix for #311. + ### 1.18.0.0 * Added sample for **xWebVirtualDirectory** for creating a new virtual directory. Bugfix for #195. diff --git a/Tests/Unit/MSFT_xWebAppPoolDefaults.tests.ps1 b/Tests/Unit/MSFT_xWebAppPoolDefaults.tests.ps1 new file mode 100644 index 000000000..4c4482613 --- /dev/null +++ b/Tests/Unit/MSFT_xWebAppPoolDefaults.tests.ps1 @@ -0,0 +1,80 @@ +#region HEADER + +$script:DSCModuleName = 'xWebAdministration' +$script:DSCResourceName = 'MSFT_xWebAppPoolDefaults' + +# Unit Test Template Version: 1.2.1 +$script:moduleRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) +if ( (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests'))) -or ` + (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1'))) ) +{ + & git @('clone','https://github.com/PowerShell/DscResource.Tests.git', ` + (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests')) +} + +Import-Module -Name (Join-Path -Path $script:moduleRoot -ChildPath (Join-Path ` + -Path 'DSCResource.Tests' -ChildPath 'TestHelper.psm1')) -Force + + +$TestEnvironment = Initialize-TestEnvironment ` + -DSCModuleName $script:DSCModuleName ` + -DSCResourceName $script:DSCResourceName ` + -TestType Unit + +#endregion HEADER + +function Invoke-TestSetup { +} + +function Invoke-TestCleanup { + Restore-TestEnvironment -TestEnvironment $TestEnvironment +} + +# Begin Testing +try +{ + Invoke-TestSetup + + InModuleScope $script:DSCResourceName { + + Describe "$($script:DSCResourceName)\Get-TargetResource" { + + Context 'Get application pool defaults' { + + $mockAppPoolDefaults = @{ + managedRuntimeVersion = 'v4.0' + processModel = @{ + identityType = 'SpecificUser' + } + } + + Mock Get-WebConfigurationProperty -MockWith { + $path = $Filter.Replace('system.applicationHost/applicationPools/applicationPoolDefaults', '') + + if ([System.String]::IsNullOrEmpty($path)) { + return $mockAppPoolDefaults[$Name] + } else { + $path = $path.Replace('/', '') + return $mockAppPoolDefaults[$path][$Name] + } + } + + $result = Get-TargetResource -ApplyTo 'Machine' + + It 'Should return managedRuntimeVersion' { + $result.managedRuntimeVersion | ` + Should Be $mockAppPoolDefaults.managedRuntimeVersion + } + + It 'Should return processModel\identityType' { + $result.identityType | ` + Should Be $mockAppPoolDefaults.processModel.identityType + } + } + } + } +} +finally +{ + Invoke-TestCleanup +} From 30e1cb4f17ddae145bd52dfa79d673dd483203a1 Mon Sep 17 00:00:00 2001 From: Remco Eissing Date: Fri, 27 Oct 2017 19:44:15 +0200 Subject: [PATCH 11/15] Style changes to xWebAppPoolDefaults --- .../MSFT_xWebAppPoolDefaults.psm1 | 41 +++++++++++-------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/DSCResources/MSFT_xWebAppPoolDefaults/MSFT_xWebAppPoolDefaults.psm1 b/DSCResources/MSFT_xWebAppPoolDefaults/MSFT_xWebAppPoolDefaults.psm1 index 30026a673..52f96cf3c 100644 --- a/DSCResources/MSFT_xWebAppPoolDefaults/MSFT_xWebAppPoolDefaults.psm1 +++ b/DSCResources/MSFT_xWebAppPoolDefaults/MSFT_xWebAppPoolDefaults.psm1 @@ -119,12 +119,19 @@ function Confirm-Value [CmdletBinding()] [OutputType([System.Boolean])] param - ( - [String] $Path, + ( + [Parameter(Mandatory = $true)] + [AllowEmptyString()] + [System.String] + $Path, - [String] $Name, + [Parameter(Mandatory = $true)] + [System.String] + $Name, - [String] $NewValue + [Parameter(Mandatory = $true)] + [System.String] + $NewValue ) if (-not($NewValue)) @@ -150,12 +157,19 @@ function Set-Value { [CmdletBinding()] param - ( - [String] $Path, - - [String] $Name, - - [String] $NewValue + ( + [Parameter(Mandatory = $true)] + [AllowEmptyString()] + [System.String] + $Path, + + [Parameter(Mandatory = $true)] + [System.String] + $Name, + + [Parameter(Mandatory = $true)] + [System.String] + $NewValue ) # if the variable doesn't exist, the user doesn't want to change this value @@ -180,9 +194,7 @@ function Set-Value $relPath = $Path + '/' + $Name Write-Verbose($LocalizedData.SettingValue -f $relPath,$NewValue); - } - } function Get-Value @@ -216,11 +228,6 @@ function Get-Value } else { return $result } - - return Get-WebConfigurationProperty ` - -PSPath 'MACHINE/WEBROOT/APPHOST' ` - -Filter "system.applicationHost/applicationPools/applicationPoolDefaults$Path" ` - -Name $Name } #endregion From 8568ab56d0c84748a56ec4c6e626f51d5031dcc9 Mon Sep 17 00:00:00 2001 From: Remco Eissing Date: Fri, 27 Oct 2017 22:02:41 +0200 Subject: [PATCH 12/15] Unit tests for xWebAppPoolDefaults. Fix PowerShell/xWebAdministration#183 --- .../MSFT_xWebAppPoolDefaults.psm1 | 26 ++-- README.md | 3 +- Tests/Unit/MSFT_xWebAppPoolDefaults.tests.ps1 | 116 +++++++++++++++++- 3 files changed, 133 insertions(+), 12 deletions(-) diff --git a/DSCResources/MSFT_xWebAppPoolDefaults/MSFT_xWebAppPoolDefaults.psm1 b/DSCResources/MSFT_xWebAppPoolDefaults/MSFT_xWebAppPoolDefaults.psm1 index 52f96cf3c..48f35c178 100644 --- a/DSCResources/MSFT_xWebAppPoolDefaults/MSFT_xWebAppPoolDefaults.psm1 +++ b/DSCResources/MSFT_xWebAppPoolDefaults/MSFT_xWebAppPoolDefaults.psm1 @@ -40,7 +40,6 @@ function Get-TargetResource } } - function Set-TargetResource { <# @@ -51,14 +50,16 @@ function Set-TargetResource [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSDSCUseVerboseMessageInDSCResource", "")] param - ( - [ValidateSet('Machine')] + ( [Parameter(Mandatory = $true)] + [ValidateSet('Machine')] [String] $ApplyTo, + [Parameter()] [ValidateSet('','v2.0','v4.0')] [String] $ManagedRuntimeVersion, + [Parameter()] [ValidateSet('ApplicationPoolIdentity','LocalService','LocalSystem','NetworkService')] [String] $IdentityType ) @@ -82,15 +83,20 @@ function Test-TargetResource [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSDSCUseVerboseMessageInDSCResource", "")] param ( - [ValidateSet('Machine')] [Parameter(Mandatory = $true)] - [String] $ApplyTo, - + [ValidateSet('Machine')] + [System.String] + $ApplyTo, + + [Parameter()] [ValidateSet('','v2.0','v4.0')] - [String] $ManagedRuntimeVersion, - + [System.String] + $ManagedRuntimeVersion, + + [Parameter()] [ValidateSet('ApplicationPoolIdentity','LocalService','LocalSystem','NetworkService')] - [String] $IdentityType + [System.String] + $IdentityType ) Assert-Module @@ -129,7 +135,7 @@ function Confirm-Value [System.String] $Name, - [Parameter(Mandatory = $true)] + [Parameter()] [System.String] $NewValue ) diff --git a/README.md b/README.md index 430e7fae5..4ba141d3e 100644 --- a/README.md +++ b/README.md @@ -240,7 +240,8 @@ Please check out common DSC Resources [contributing guidelines](https://github.c ### Unreleased -* **xWebAppPoolDefaults** now returns values. Bugfix for #311. +* **xWebAppPoolDefaults** now returns values. Fixes #311. +* Added unit tests for **xWebAppPoolDefaults**. Fixes #183. ### 1.18.0.0 diff --git a/Tests/Unit/MSFT_xWebAppPoolDefaults.tests.ps1 b/Tests/Unit/MSFT_xWebAppPoolDefaults.tests.ps1 index 4c4482613..c6161a95f 100644 --- a/Tests/Unit/MSFT_xWebAppPoolDefaults.tests.ps1 +++ b/Tests/Unit/MSFT_xWebAppPoolDefaults.tests.ps1 @@ -40,7 +40,6 @@ try Describe "$($script:DSCResourceName)\Get-TargetResource" { Context 'Get application pool defaults' { - $mockAppPoolDefaults = @{ managedRuntimeVersion = 'v4.0' processModel = @{ @@ -72,6 +71,121 @@ try } } } + + Describe "$($script:DSCResourceName)\Test-TargetResource" { + + $mockAppPoolDefaults = @{ + managedRuntimeVersion = 'v4.0' + processModel = @{ + identityType = 'NetworkService' + } + } + + Mock Get-WebConfigurationProperty -MockWith { + $path = $Filter.Replace('system.applicationHost/applicationPools/applicationPoolDefaults', '') + + if ([System.String]::IsNullOrEmpty($path)) { + return $mockAppPoolDefaults[$Name] + } else { + $path = $path.Replace('/', '') + return $mockAppPoolDefaults[$path][$Name] + } + } + + Context 'Application pool defaults correct' { + $result = Test-TargetResource -ApplyTo 'Machine' ` + -ManagedRuntimeVersion 'v4.0' ` + -IdentityType 'NetworkService' + + It 'Should return True' { + $result | Should Be $true + } + } + + Context 'Application pool different managedRuntimeVersion' { + $result = Test-TargetResource -ApplyTo 'Machine' ` + -ManagedRuntimeVersion 'v2.0' ` + -IdentityType 'NetworkService' + + It 'Should return False' { + $result | Should Be $false + } + } + + Context 'Application pool different processModel/@identityType' { + $result = Test-TargetResource -ApplyTo 'Machine' ` + -ManagedRuntimeVersion 'v4.0' ` + -IdentityType 'LocalSystem' + + It 'Should return False' { + $result | Should Be $false + } + } + + Context 'Application pool no value for managedRuntimeVersion' { + $result = Test-TargetResource -ApplyTo 'Machine' ` + -IdentityType 'NetworkService' + + It 'Should return True' { + $result | Should Be $true + } + } + } + + Describe "$($script:DSCResourceName)\Set-TargetResource" { + + $mockAppPoolDefaults = @{ + managedRuntimeVersion = 'v4.0' + processModel = @{ + identityType = 'NetworkService' + } + } + + Mock Get-WebConfigurationProperty -MockWith { + $path = $Filter.Replace('system.applicationHost/applicationPools/applicationPoolDefaults', '') + + if ([System.String]::IsNullOrEmpty($path)) { + return $mockAppPoolDefaults[$Name] + } else { + $path = $path.Replace('/', '') + return $mockAppPoolDefaults[$path][$Name] + } + } + + Mock Set-WebConfigurationProperty -MockWith { } + + Context 'Application pool defaults correct' { + Set-TargetResource -ApplyTo 'Machine' ` + -ManagedRuntimeVersion 'v4.0' ` + -IdentityType 'NetworkService' + + It 'Should not call Set-WebConfigurationProperty' { + Assert-MockCalled Set-WebConfigurationProperty -Exactly 0 + } + } + + Context 'Application pool different managedRuntimeVersion' { + Set-TargetResource -ApplyTo 'Machine' ` + -ManagedRuntimeVersion 'v2.0' ` + -IdentityType 'NetworkService' + + It 'Should call Set-WebConfigurationProperty once' { + Assert-MockCalled Set-WebConfigurationProperty -Exactly 1 ` + -ParameterFilter { $Name -eq 'managedRuntimeVersion' } + } + } + + Context 'Application pool different processModel/@identityType' { + Set-TargetResource -ApplyTo 'Machine' ` + -ManagedRuntimeVersion 'v4.0' ` + -IdentityType 'LocalSystem' + + It 'Should call Set-WebConfigurationProperty once' { + Assert-MockCalled Set-WebConfigurationProperty -Exactly 1 ` + -ParameterFilter { $Name -eq 'identityType' } + } + } + } } } finally From 26520e456452e7035e10a7d2f148d58299359ad2 Mon Sep 17 00:00:00 2001 From: Remco Eissing Date: Sat, 28 Oct 2017 16:10:29 +0200 Subject: [PATCH 13/15] Updated Pester version and fixes to the tests. --- .../MSFT_xWebAppPoolDefaults.psm1 | 2 +- .../MSFT_xWebApplication.psm1 | 10 +++++----- ...FT_xWebAppPoolDefaults.Integration.Tests.ps1 | 2 +- .../MSFT_xWebApplication.Integration.Tests.ps1 | 4 ++-- Tests/Unit/MSFT_xSSLSettings.Tests.ps1 | 17 +++++++++-------- Tests/Unit/MSFT_xWebsite.Tests.ps1 | 7 ++++--- appveyor.yml | 2 +- 7 files changed, 23 insertions(+), 21 deletions(-) diff --git a/DSCResources/MSFT_xWebAppPoolDefaults/MSFT_xWebAppPoolDefaults.psm1 b/DSCResources/MSFT_xWebAppPoolDefaults/MSFT_xWebAppPoolDefaults.psm1 index 48f35c178..773b472c9 100644 --- a/DSCResources/MSFT_xWebAppPoolDefaults/MSFT_xWebAppPoolDefaults.psm1 +++ b/DSCResources/MSFT_xWebAppPoolDefaults/MSFT_xWebAppPoolDefaults.psm1 @@ -173,7 +173,7 @@ function Set-Value [System.String] $Name, - [Parameter(Mandatory = $true)] + [Parameter()] [System.String] $NewValue ) diff --git a/DSCResources/MSFT_xWebApplication/MSFT_xWebApplication.psm1 b/DSCResources/MSFT_xWebApplication/MSFT_xWebApplication.psm1 index 0b18377f7..06452e598 100644 --- a/DSCResources/MSFT_xWebApplication/MSFT_xWebApplication.psm1 +++ b/DSCResources/MSFT_xWebApplication/MSFT_xWebApplication.psm1 @@ -266,9 +266,9 @@ function Set-TargetResource { Write-Verbose -Message ($LocalizedData.VerboseSetTargetEnabledProtocols -f $Name) # Make input bindings which are an array, into a string - $stringafiedEnabledProtocols = $EnabledProtocols -join ' ' + $stringafiedEnabledProtocols = $EnabledProtocols -join ',' Set-ItemProperty -Path "IIS:\Sites\$Website\$Name" ` - -Name EnabledProtocols ` + -Name 'enabledProtocols' ` -Value $stringafiedEnabledProtocols ` -ErrorAction Stop } @@ -625,9 +625,9 @@ function Get-SslFlags ForEach-Object { $_.sslFlags } if ($null -eq $SslFlags) - { - [String]::Empty - } + { + return [String]::Empty + } return $SslFlags } diff --git a/Tests/Integration/MSFT_xWebAppPoolDefaults.Integration.Tests.ps1 b/Tests/Integration/MSFT_xWebAppPoolDefaults.Integration.Tests.ps1 index 432c091a7..3b6e4d551 100644 --- a/Tests/Integration/MSFT_xWebAppPoolDefaults.Integration.Tests.ps1 +++ b/Tests/Integration/MSFT_xWebAppPoolDefaults.Integration.Tests.ps1 @@ -50,7 +50,7 @@ try } | Should not throw } - It 'should be able to call Get-DscConfiguration without throwing' { + It 'Should be able to call Get-DscConfiguration without throwing' { { Get-DscConfiguration -Verbose -ErrorAction Stop } | Should Not throw } #endregion diff --git a/Tests/Integration/MSFT_xWebApplication.Integration.Tests.ps1 b/Tests/Integration/MSFT_xWebApplication.Integration.Tests.ps1 index 2c96a201e..1f8d2a836 100644 --- a/Tests/Integration/MSFT_xWebApplication.Integration.Tests.ps1 +++ b/Tests/Integration/MSFT_xWebApplication.Integration.Tests.ps1 @@ -83,7 +83,7 @@ try It 'Should create a WebApplication with correct settings' -test { - Invoke-Expression -Command "$($script:DSCResourceName)_Present -ConfigurationData `$DSCConfg -OutputPath `$TestDrive" + Invoke-Expression -Command "$($script:DSCResourceName)_Present -ConfigurationData `$DSCConfg -OutputPath `$TestDrive" # Build results to test $Result = Get-WebApplication -Site $DSCConfig.AllNodes.Website -Name $DSCConfig.AllNodes.WebApplication @@ -113,7 +113,7 @@ try Get-SslFlags -Website $DSCConfig.AllNodes.Website -WebApplication $DSCConfig.AllNodes.WebApplication | Should Be $DSCConfig.AllNodes.WebApplicationSslFlags # Test EnabledProtocols - $Result.EnabledProtocols | Should Be $DSCConfig.AllNodes.EnabledProtocols + $Result.EnabledProtocols | Should Be ($DSCConfig.AllNodes.EnabledProtocols -join ',') } diff --git a/Tests/Unit/MSFT_xSSLSettings.Tests.ps1 b/Tests/Unit/MSFT_xSSLSettings.Tests.ps1 index e5dfda402..93671f582 100644 --- a/Tests/Unit/MSFT_xSSLSettings.Tests.ps1 +++ b/Tests/Unit/MSFT_xSSLSettings.Tests.ps1 @@ -27,6 +27,7 @@ try #region Pester Tests InModuleScope $DSCResourceName { + $script:DSCResourceName = 'MSFT_xSSLSettings' Describe "$script:DSCResourceName\Test-TargetResource" { Context 'Ensure is Present and SSLSettings is Present' { @@ -38,7 +39,7 @@ try $result = Test-TargetResource -Name 'Test' -Ensure 'Present' -Bindings 'Ssl' - Assert-VerifiableMocks + Assert-VerifiableMock It 'should return true' { $result | should be $true @@ -54,7 +55,7 @@ try $result = Test-TargetResource -Name 'Test' -Ensure 'Absent' -Bindings 'Ssl' - Assert-VerifiableMocks + Assert-VerifiableMock It 'should return true' { $result | should be $true @@ -70,7 +71,7 @@ try $result = Test-TargetResource -Name 'Test' -Ensure 'Present' -Bindings 'Ssl' - Assert-VerifiableMocks + Assert-VerifiableMock It 'should return true' { $result | should be $false @@ -90,7 +91,7 @@ try Ensure = 'Present' } - Assert-VerifiableMocks + Assert-VerifiableMock It 'should return the correct bindings' { $result.Bindings | should be $expected.Bindings @@ -112,7 +113,7 @@ try Ensure = 'Absent' } - Assert-VerifiableMocks + Assert-VerifiableMock It 'should return the correct bindings' { $result.Bindings | should be $expected.Bindings @@ -134,7 +135,7 @@ try # Check that the LocalizedData message from the Set-TargetResource is correct $resultMessage = $LocalizedData.SettingSSLConfig -f 'Name', '' - Assert-VerifiableMocks + Assert-VerifiableMock It 'should return the correct string' { $result | Should Be $resultMessage @@ -150,7 +151,7 @@ try # Check that the LocalizedData message from the Set-TargetResource is correct $resultMessage = $LocalizedData.SettingSSLConfig -f 'Name', 'Ssl' - Assert-VerifiableMocks + Assert-VerifiableMock It 'should return the correct string' { $result | Should Be $resultMessage @@ -166,7 +167,7 @@ try # Check that the LocalizedData message from the Set-TargetResource is correct $resultMessage = $LocalizedData.SettingSSLConfig -f 'Name', 'Ssl,SslNegotiateCert,SslRequireCert' - Assert-VerifiableMocks + Assert-VerifiableMock It 'should return the correct string' { $result | Should Be $resultMessage diff --git a/Tests/Unit/MSFT_xWebsite.Tests.ps1 b/Tests/Unit/MSFT_xWebsite.Tests.ps1 index 64ade6180..c9f9106a9 100644 --- a/Tests/Unit/MSFT_xWebsite.Tests.ps1 +++ b/Tests/Unit/MSFT_xWebsite.Tests.ps1 @@ -25,7 +25,8 @@ try { #region Pester Tests InModuleScope -ModuleName $script:DSCResourceName -ScriptBlock { - + $script:DSCResourceName = 'MSFT_xWebsite' + Describe "$script:DSCResourceName\Assert-Module" { Context 'WebAdminstration module is not installed' { Mock -ModuleName Helper -CommandName Get-Module -MockWith { return $null } @@ -3321,8 +3322,8 @@ try Update-WebsiteBinding -Name $MockWebsite.Name -BindingInfo $MockBindingInfo - It 'should call all the mocks' { - Assert-Verifiablemocks + It 'Should call all the mocks' { + Assert-VerifiableMock Assert-MockCalled -CommandName Add-WebConfiguration -Exactly $MockBindingInfo.Count Assert-MockCalled -CommandName Set-WebConfigurationProperty } diff --git a/appveyor.yml b/appveyor.yml index b1c94a908..191deddc7 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -7,7 +7,7 @@ install: - ps: | Import-Module -Name .\DscResource.Tests\TestHelper.psm1 -Force Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force - Install-Module -Name Pester -Repository PSGallery -MaximumVersion 3.4.3 -Force + Install-Module -Name Pester -Repository PSGallery -MaximumVersion 4.0.7 -Force Install-WindowsFeature -IncludeAllSubFeature -IncludeManagementTools -Name 'Web-Server' #---------------------------------# From b0e5e20f2db1b88bea7c17b67765274839b9cb22 Mon Sep 17 00:00:00 2001 From: Remco Eissing Date: Sat, 4 Nov 2017 09:02:10 +0100 Subject: [PATCH 14/15] Corrected parameter format of Set-TargetResource in MSFT_xWebAppPoolDefaults.psm1 --- .../MSFT_xWebAppPoolDefaults.psm1 | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/DSCResources/MSFT_xWebAppPoolDefaults/MSFT_xWebAppPoolDefaults.psm1 b/DSCResources/MSFT_xWebAppPoolDefaults/MSFT_xWebAppPoolDefaults.psm1 index 773b472c9..6def1b06a 100644 --- a/DSCResources/MSFT_xWebAppPoolDefaults/MSFT_xWebAppPoolDefaults.psm1 +++ b/DSCResources/MSFT_xWebAppPoolDefaults/MSFT_xWebAppPoolDefaults.psm1 @@ -53,15 +53,18 @@ function Set-TargetResource ( [Parameter(Mandatory = $true)] [ValidateSet('Machine')] - [String] $ApplyTo, + [System.String] + $ApplyTo, [Parameter()] [ValidateSet('','v2.0','v4.0')] - [String] $ManagedRuntimeVersion, + [System.String] + $ManagedRuntimeVersion, [Parameter()] [ValidateSet('ApplicationPoolIdentity','LocalService','LocalSystem','NetworkService')] - [String] $IdentityType + [System.String] + $IdentityType ) Assert-Module From d022234030fb71a5c1d293aac9a1ee1ccaeac867 Mon Sep 17 00:00:00 2001 From: Katie Keim Date: Wed, 15 Nov 2017 13:07:07 -0800 Subject: [PATCH 15/15] Releasing version 1.19.0.0 --- README.md | 2 ++ xWebAdministration.psd1 | 9 ++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 4ba141d3e..05d0480fe 100644 --- a/README.md +++ b/README.md @@ -240,6 +240,8 @@ Please check out common DSC Resources [contributing guidelines](https://github.c ### Unreleased +### 1.19.0.0 + * **xWebAppPoolDefaults** now returns values. Fixes #311. * Added unit tests for **xWebAppPoolDefaults**. Fixes #183. diff --git a/xWebAdministration.psd1 b/xWebAdministration.psd1 index c03ad7c8b..419031c92 100644 --- a/xWebAdministration.psd1 +++ b/xWebAdministration.psd1 @@ -1,6 +1,6 @@ @{ # Version number of this module. -ModuleVersion = '1.18.0.0' +ModuleVersion = '1.19.0.0' # ID used to uniquely identify this module GUID = 'b3239f27-d7d3-4ae6-a5d2-d9a1c97d6ae4' @@ -41,10 +41,8 @@ PrivateData = @{ # IconUri = '' # ReleaseNotes of this module - ReleaseNotes = '* Added sample for **xWebVirtualDirectory** for creating a new virtual directory. Bugfix for 195. -* Added integration tests for **xWebVirtualDirectory**. Fixes 188. -* xWebsite: - * Fixed bugs when setting log properties, fixes 299. + ReleaseNotes = '* **xWebAppPoolDefaults** now returns values. Fixes 311. +* Added unit tests for **xWebAppPoolDefaults**. Fixes 183. ' @@ -67,3 +65,4 @@ CmdletsToExport = '*' +