From 08498e348929b2ab9725f4d926226b2bea6e05b8 Mon Sep 17 00:00:00 2001 From: "Hannappel, Christoph" Date: Thu, 18 Apr 2024 20:45:40 +0200 Subject: [PATCH 1/4] Fixes Compare-Object Exception if there are currently no CertificateNotificationContacts --- CHANGELOG.md | 6 + .../MSFT_SPCertificateSettings.psm1 | 264 +++++++++--------- ...rePointDsc.SPCertificateSettings.Tests.ps1 | 52 +++- 3 files changed, 195 insertions(+), 127 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bad923b84..5a14a91b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - SPFarm - Updated to run cmdlet `Update-SPFlightsConfigFile` on SharePoint Subscription. +### Fixed + +- SPCertificateSettings + - Fixed an error where the command failed to add + SPCertificateNotificationContacts when there are currently none set. + ## [5.4.0] - 2023-04-04 ### Fixed diff --git a/SharePointDsc/DSCResources/MSFT_SPCertificateSettings/MSFT_SPCertificateSettings.psm1 b/SharePointDsc/DSCResources/MSFT_SPCertificateSettings/MSFT_SPCertificateSettings.psm1 index 5748ab2ea..e7dbe34dc 100644 --- a/SharePointDsc/DSCResources/MSFT_SPCertificateSettings/MSFT_SPCertificateSettings.psm1 +++ b/SharePointDsc/DSCResources/MSFT_SPCertificateSettings/MSFT_SPCertificateSettings.psm1 @@ -285,141 +285,153 @@ function Set-TargetResource if ($contactsProvided) { Write-Verbose "Checking Certificate Notification Contacts" - $currentContacts = [array](Get-SPCertificateNotificationContact).Address + [array]$currentContacts = Get-SPCertificateNotificationContact - $diffs = Compare-Object -ReferenceObject $desiredContacts -DifferenceObject $currentContacts - foreach ($diff in $diffs) + # If there aren't any current Contacts we'll add them all. Also Compare-Object does not like $null Objects. + # This fixes Issue "SPCertificateSettings: Unable to set contacts when previously blank" https://github.com/dsccommunity/SharePointDsc/issues/1430 + if ($currentContacts.Count -eq 0) { - switch ($diff.SideIndicator) + foreach ($contact in $desiredContacts) { - "<=" - { - Write-Verbose "Adding $($diff.InputObject)" - $null = Add-SPCertificateNotificationContact -EmailAddress $diff.InputObject - } - "=>" + Write-Verbose "Adding $($diff.InputObject)" + $null = Add-SPCertificateNotificationContact -EmailAddress $contact + } + } + else + { + $diffs = Compare-Object -ReferenceObject $desiredContacts -DifferenceObject ($currentContacts | Select-Object -ExpandProperty Address) + foreach ($diff in $diffs) + { + switch ($diff.SideIndicator) { - Write-Verbose "Removing $($diff.InputObject)" - $null = Remove-SPCertificateNotificationContact -EmailAddress $diff.InputObject -Confirm:$false + "<=" + { + Write-Verbose "Adding $($diff.InputObject)" + $null = Add-SPCertificateNotificationContact -EmailAddress $diff.InputObject + } + "=>" + { + Write-Verbose "Removing $($diff.InputObject)" + $null = Remove-SPCertificateNotificationContact -EmailAddress $diff.InputObject -Confirm:$false + } } } } } } -} - -function Test-TargetResource -{ - [CmdletBinding()] - [OutputType([System.Boolean])] - param - ( - [Parameter(Mandatory = $true)] - [ValidateSet('Yes')] - [System.String] - $IsSingleInstance, - - [Parameter()] - [System.String] - $OrganizationalUnit, - - [Parameter()] - [System.String] - $Organization, - - [Parameter()] - [System.String] - $Locality, - - [Parameter()] - [System.String] - $State, - - [Parameter()] - [ValidateLength(2, 2)] - [System.String] - $Country, - - [Parameter()] - [ValidateSet('ECC', 'RSA')] - [System.String] - $KeyAlgorithm, - - [Parameter()] - [ValidateSet('0', '2048', '4096', '8192', '16384')] - [System.UInt16] - $KeySize, - [Parameter()] - [ValidateSet('nistP256', 'nistP384', 'nistP521')] - [System.String] - $EllipticCurve, - - [Parameter()] - [ValidateSet('SHA256', 'SHA384', 'SHA512')] - [System.String] - $HashAlgorithm, - - [Parameter()] - [ValidateSet('Pkcs1', 'Pss')] - [System.String] - $RsaSignaturePadding, - - [Parameter()] - [System.UInt32] - $CertificateExpirationAttentionThreshold, - - [Parameter()] - [System.UInt32] - $CertificateExpirationWarningThreshold, - - [Parameter()] - [System.UInt32] - $CertificateExpirationErrorThreshold, - - [Parameter()] - [System.String[]] - $CertificateNotificationContacts - ) - - Write-Verbose -Message "Testing certificate configuration settings" - - $CurrentValues = Get-TargetResource @PSBoundParameters - - Write-Verbose -Message "Current Values: $(Convert-SPDscHashtableToString -Hashtable $CurrentValues)" - Write-Verbose -Message "Target Values: $(Convert-SPDscHashtableToString -Hashtable $PSBoundParameters)" - - $result = Test-SPDscParameterState -CurrentValues $CurrentValues ` - -Source $($MyInvocation.MyCommand.Source) ` - -DesiredValues $PSBoundParameters - - Write-Verbose -Message "Test-TargetResource returned $result" - - return $result -} + function Test-TargetResource + { + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + [Parameter(Mandatory = $true)] + [ValidateSet('Yes')] + [System.String] + $IsSingleInstance, + + [Parameter()] + [System.String] + $OrganizationalUnit, + + [Parameter()] + [System.String] + $Organization, + + [Parameter()] + [System.String] + $Locality, + + [Parameter()] + [System.String] + $State, + + [Parameter()] + [ValidateLength(2, 2)] + [System.String] + $Country, + + [Parameter()] + [ValidateSet('ECC', 'RSA')] + [System.String] + $KeyAlgorithm, + + [Parameter()] + [ValidateSet('0', '2048', '4096', '8192', '16384')] + [System.UInt16] + $KeySize, + + [Parameter()] + [ValidateSet('nistP256', 'nistP384', 'nistP521')] + [System.String] + $EllipticCurve, + + [Parameter()] + [ValidateSet('SHA256', 'SHA384', 'SHA512')] + [System.String] + $HashAlgorithm, + + [Parameter()] + [ValidateSet('Pkcs1', 'Pss')] + [System.String] + $RsaSignaturePadding, + + [Parameter()] + [System.UInt32] + $CertificateExpirationAttentionThreshold, + + [Parameter()] + [System.UInt32] + $CertificateExpirationWarningThreshold, + + [Parameter()] + [System.UInt32] + $CertificateExpirationErrorThreshold, + + [Parameter()] + [System.String[]] + $CertificateNotificationContacts + ) + + Write-Verbose -Message "Testing certificate configuration settings" + + $CurrentValues = Get-TargetResource @PSBoundParameters + + Write-Verbose -Message "Current Values: $(Convert-SPDscHashtableToString -Hashtable $CurrentValues)" + Write-Verbose -Message "Target Values: $(Convert-SPDscHashtableToString -Hashtable $PSBoundParameters)" + + $result = Test-SPDscParameterState -CurrentValues $CurrentValues ` + -Source $($MyInvocation.MyCommand.Source) ` + -DesiredValues $PSBoundParameters + + Write-Verbose -Message "Test-TargetResource returned $result" + + return $result + } -function Export-TargetResource -{ - $VerbosePreference = "SilentlyContinue" - $ParentModuleBase = Get-Module "SharePointDsc" -ListAvailable | Select-Object -ExpandProperty Modulebase - $module = Join-Path -Path $ParentModuleBase -ChildPath "\DSCResources\MSFT_SPCertificateSettings\MSFT_SPCertificateSettings.psm1" -Resolve - - $Content = '' - $params = Get-DSCFakeParameters -ModulePath $module - $params.Country = "US" - - $PartialContent = " SPCertificateSettings CertificateSettings`r`n" - $PartialContent += " {`r`n" - $results = Get-TargetResource @params - $results = Repair-Credentials -results $results - $currentBlock = Get-DSCBlock -Params $results -ModulePath $module - $currentBlock = Convert-DSCStringParamToVariable -DSCBlock $currentBlock -ParameterName "PsDscRunAsCredential" - $PartialContent += $currentBlock - $PartialContent += " }`r`n" - - $Content += $PartialContent - - return $Content -} + function Export-TargetResource + { + $VerbosePreference = "SilentlyContinue" + $ParentModuleBase = Get-Module "SharePointDsc" -ListAvailable | Select-Object -ExpandProperty Modulebase + $module = Join-Path -Path $ParentModuleBase -ChildPath "\DSCResources\MSFT_SPCertificateSettings\MSFT_SPCertificateSettings.psm1" -Resolve + + $Content = '' + $params = Get-DSCFakeParameters -ModulePath $module + $params.Country = "US" + + $PartialContent = " SPCertificateSettings CertificateSettings`r`n" + $PartialContent += " {`r`n" + $results = Get-TargetResource @params + $results = Repair-Credentials -results $results + $currentBlock = Get-DSCBlock -Params $results -ModulePath $module + $currentBlock = Convert-DSCStringParamToVariable -DSCBlock $currentBlock -ParameterName "PsDscRunAsCredential" + $PartialContent += $currentBlock + $PartialContent += " }`r`n" + + $Content += $PartialContent + + return $Content + } -Export-ModuleMember -Function *-TargetResource + Export-ModuleMember -Function *-TargetResource diff --git a/tests/Unit/SharePointDsc/SharePointDsc.SPCertificateSettings.Tests.ps1 b/tests/Unit/SharePointDsc/SharePointDsc.SPCertificateSettings.Tests.ps1 index d633bc560..db0a87e90 100644 --- a/tests/Unit/SharePointDsc/SharePointDsc.SPCertificateSettings.Tests.ps1 +++ b/tests/Unit/SharePointDsc/SharePointDsc.SPCertificateSettings.Tests.ps1 @@ -33,7 +33,7 @@ function Invoke-TestSetup $script:testEnvironment = Initialize-TestEnvironment ` -DSCModuleName $script:DSCModuleName ` - -DSCResourceName $script:DSCResourceFullName ` + -DscResourceName $script:DSCResourceFullName ` -ResourceType 'Mof' ` -TestType 'Unit' } @@ -295,6 +295,56 @@ try } } + Context -Name "The server is in a farm and zero contacts have been applied" -Fixture { + BeforeAll { + $testParams = @{ + IsSingleInstance = 'Yes' + CertificateNotificationContacts = 'admin@contoso.com' + } + + Mock -CommandName Get-SPCertificateSettings -MockWith { + $returnVal = @{ + DefaultOrganizationalUnit = '' + DefaultOrganization = '' + DefaultLocality = '' + DefaultState = '' + DefaultCountry = '' + DefaultKeyAlgorithm = 'RSA' + DefaultRsaKeySize = 2048 + DefaultEllipticCurve = 'nistP256' + DefaultHashAlgorithm = 'SHA256' + DefaultRsaSignaturePadding = 'Pkcs1' + CertificateExpirationAttentionThresholdDays = 60 + CertificateExpirationWarningThresholdDays = 15 + CertificateExpirationErrorThresholdDays = 15 + CertificateNotificationContacts = [System.Net.Mail.MailAddressCollection]::new() + } + return $returnVal + } + Mock -CommandName Get-SPFarm -MockWith { return @{ } } + Mock -CommandName Get-SPCertificateNotificationContact -MockWith { + return [System.Net.Mail.MailAddressCollection]::new() + } + Mock -CommandName Add-SPCertificateNotificationContact -MockWith {} + Mock -CommandName Remove-SPCertificateNotificationContact -MockWith {} + } + + It "Should return values from the get method" { + $result = Get-TargetResource @testParams + $result.CertificateNotificationContacts.Count | Should -Be 0 + } + + It "Should return false from the test method" { + Test-TargetResource @testParams | Should -Be $false + } + + It "Should update the certificate settings" { + Set-TargetResource @testParams + Assert-MockCalled Add-SPCertificateNotificationContact + Assert-MockCalled Remove-SPCertificateNotificationContact + } + } + Context -Name "The server is in a farm and the correct settings have been applied" -Fixture { BeforeAll { $testParams = @{ From 222e42d3cd5f356306de82005d1cd54b5939b5c3 Mon Sep 17 00:00:00 2001 From: "Hannappel, Christoph" Date: Fri, 19 Apr 2024 09:40:26 +0200 Subject: [PATCH 2/4] Fixed Missing Closing } --- .../MSFT_SPCertificateSettings.psm1 | 227 +++++++++--------- 1 file changed, 114 insertions(+), 113 deletions(-) diff --git a/SharePointDsc/DSCResources/MSFT_SPCertificateSettings/MSFT_SPCertificateSettings.psm1 b/SharePointDsc/DSCResources/MSFT_SPCertificateSettings/MSFT_SPCertificateSettings.psm1 index e7dbe34dc..f24766620 100644 --- a/SharePointDsc/DSCResources/MSFT_SPCertificateSettings/MSFT_SPCertificateSettings.psm1 +++ b/SharePointDsc/DSCResources/MSFT_SPCertificateSettings/MSFT_SPCertificateSettings.psm1 @@ -319,119 +319,120 @@ function Set-TargetResource } } } +} - function Test-TargetResource - { - [CmdletBinding()] - [OutputType([System.Boolean])] - param - ( - [Parameter(Mandatory = $true)] - [ValidateSet('Yes')] - [System.String] - $IsSingleInstance, - - [Parameter()] - [System.String] - $OrganizationalUnit, - - [Parameter()] - [System.String] - $Organization, - - [Parameter()] - [System.String] - $Locality, - - [Parameter()] - [System.String] - $State, - - [Parameter()] - [ValidateLength(2, 2)] - [System.String] - $Country, - - [Parameter()] - [ValidateSet('ECC', 'RSA')] - [System.String] - $KeyAlgorithm, - - [Parameter()] - [ValidateSet('0', '2048', '4096', '8192', '16384')] - [System.UInt16] - $KeySize, - - [Parameter()] - [ValidateSet('nistP256', 'nistP384', 'nistP521')] - [System.String] - $EllipticCurve, - - [Parameter()] - [ValidateSet('SHA256', 'SHA384', 'SHA512')] - [System.String] - $HashAlgorithm, - - [Parameter()] - [ValidateSet('Pkcs1', 'Pss')] - [System.String] - $RsaSignaturePadding, - - [Parameter()] - [System.UInt32] - $CertificateExpirationAttentionThreshold, - - [Parameter()] - [System.UInt32] - $CertificateExpirationWarningThreshold, - - [Parameter()] - [System.UInt32] - $CertificateExpirationErrorThreshold, - - [Parameter()] - [System.String[]] - $CertificateNotificationContacts - ) - - Write-Verbose -Message "Testing certificate configuration settings" - - $CurrentValues = Get-TargetResource @PSBoundParameters - - Write-Verbose -Message "Current Values: $(Convert-SPDscHashtableToString -Hashtable $CurrentValues)" - Write-Verbose -Message "Target Values: $(Convert-SPDscHashtableToString -Hashtable $PSBoundParameters)" - - $result = Test-SPDscParameterState -CurrentValues $CurrentValues ` - -Source $($MyInvocation.MyCommand.Source) ` - -DesiredValues $PSBoundParameters - - Write-Verbose -Message "Test-TargetResource returned $result" - - return $result - } +function Test-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + [Parameter(Mandatory = $true)] + [ValidateSet('Yes')] + [System.String] + $IsSingleInstance, - function Export-TargetResource - { - $VerbosePreference = "SilentlyContinue" - $ParentModuleBase = Get-Module "SharePointDsc" -ListAvailable | Select-Object -ExpandProperty Modulebase - $module = Join-Path -Path $ParentModuleBase -ChildPath "\DSCResources\MSFT_SPCertificateSettings\MSFT_SPCertificateSettings.psm1" -Resolve - - $Content = '' - $params = Get-DSCFakeParameters -ModulePath $module - $params.Country = "US" - - $PartialContent = " SPCertificateSettings CertificateSettings`r`n" - $PartialContent += " {`r`n" - $results = Get-TargetResource @params - $results = Repair-Credentials -results $results - $currentBlock = Get-DSCBlock -Params $results -ModulePath $module - $currentBlock = Convert-DSCStringParamToVariable -DSCBlock $currentBlock -ParameterName "PsDscRunAsCredential" - $PartialContent += $currentBlock - $PartialContent += " }`r`n" - - $Content += $PartialContent - - return $Content - } + [Parameter()] + [System.String] + $OrganizationalUnit, + + [Parameter()] + [System.String] + $Organization, + + [Parameter()] + [System.String] + $Locality, + + [Parameter()] + [System.String] + $State, + + [Parameter()] + [ValidateLength(2, 2)] + [System.String] + $Country, + + [Parameter()] + [ValidateSet('ECC', 'RSA')] + [System.String] + $KeyAlgorithm, + + [Parameter()] + [ValidateSet('0', '2048', '4096', '8192', '16384')] + [System.UInt16] + $KeySize, + + [Parameter()] + [ValidateSet('nistP256', 'nistP384', 'nistP521')] + [System.String] + $EllipticCurve, + + [Parameter()] + [ValidateSet('SHA256', 'SHA384', 'SHA512')] + [System.String] + $HashAlgorithm, + + [Parameter()] + [ValidateSet('Pkcs1', 'Pss')] + [System.String] + $RsaSignaturePadding, + + [Parameter()] + [System.UInt32] + $CertificateExpirationAttentionThreshold, + + [Parameter()] + [System.UInt32] + $CertificateExpirationWarningThreshold, + + [Parameter()] + [System.UInt32] + $CertificateExpirationErrorThreshold, + + [Parameter()] + [System.String[]] + $CertificateNotificationContacts + ) + + Write-Verbose -Message "Testing certificate configuration settings" + + $CurrentValues = Get-TargetResource @PSBoundParameters + + Write-Verbose -Message "Current Values: $(Convert-SPDscHashtableToString -Hashtable $CurrentValues)" + Write-Verbose -Message "Target Values: $(Convert-SPDscHashtableToString -Hashtable $PSBoundParameters)" + + $result = Test-SPDscParameterState -CurrentValues $CurrentValues ` + -Source $($MyInvocation.MyCommand.Source) ` + -DesiredValues $PSBoundParameters + + Write-Verbose -Message "Test-TargetResource returned $result" + + return $result +} + +function Export-TargetResource +{ + $VerbosePreference = "SilentlyContinue" + $ParentModuleBase = Get-Module "SharePointDsc" -ListAvailable | Select-Object -ExpandProperty Modulebase + $module = Join-Path -Path $ParentModuleBase -ChildPath "\DSCResources\MSFT_SPCertificateSettings\MSFT_SPCertificateSettings.psm1" -Resolve + + $Content = '' + $params = Get-DSCFakeParameters -ModulePath $module + $params.Country = "US" + + $PartialContent = " SPCertificateSettings CertificateSettings`r`n" + $PartialContent += " {`r`n" + $results = Get-TargetResource @params + $results = Repair-Credentials -results $results + $currentBlock = Get-DSCBlock -Params $results -ModulePath $module + $currentBlock = Convert-DSCStringParamToVariable -DSCBlock $currentBlock -ParameterName "PsDscRunAsCredential" + $PartialContent += $currentBlock + $PartialContent += " }`r`n" + + $Content += $PartialContent + + return $Content +} - Export-ModuleMember -Function *-TargetResource +Export-ModuleMember -Function *-TargetResource From d187fc7514850aa196cc35e8515e3aca7ac4b0ac Mon Sep 17 00:00:00 2001 From: "Hannappel, Christoph" Date: Fri, 19 Apr 2024 13:46:51 +0200 Subject: [PATCH 3/4] Fix: Assert-MockCalled Remove-SPCertificateNotificationContact --- .../SharePointDsc/SharePointDsc.SPCertificateSettings.Tests.ps1 | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/Unit/SharePointDsc/SharePointDsc.SPCertificateSettings.Tests.ps1 b/tests/Unit/SharePointDsc/SharePointDsc.SPCertificateSettings.Tests.ps1 index db0a87e90..2cd10b176 100644 --- a/tests/Unit/SharePointDsc/SharePointDsc.SPCertificateSettings.Tests.ps1 +++ b/tests/Unit/SharePointDsc/SharePointDsc.SPCertificateSettings.Tests.ps1 @@ -341,7 +341,6 @@ try It "Should update the certificate settings" { Set-TargetResource @testParams Assert-MockCalled Add-SPCertificateNotificationContact - Assert-MockCalled Remove-SPCertificateNotificationContact } } From b310e7d7d0b01b71430b502a22e053adebf794eb Mon Sep 17 00:00:00 2001 From: "Hannappel, Christoph" Date: Fri, 19 Apr 2024 19:02:55 +0200 Subject: [PATCH 4/4] Fix: Unit Test for The server is in a farm and the incorrect contacts have been applied --- .../SharePointDsc.SPCertificateSettings.Tests.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Unit/SharePointDsc/SharePointDsc.SPCertificateSettings.Tests.ps1 b/tests/Unit/SharePointDsc/SharePointDsc.SPCertificateSettings.Tests.ps1 index 2cd10b176..e1fc07088 100644 --- a/tests/Unit/SharePointDsc/SharePointDsc.SPCertificateSettings.Tests.ps1 +++ b/tests/Unit/SharePointDsc/SharePointDsc.SPCertificateSettings.Tests.ps1 @@ -260,7 +260,7 @@ try CertificateExpirationWarningThresholdDays = 15 CertificateExpirationErrorThresholdDays = 15 CertificateNotificationContacts = @( - @{ + [PSCustomObject]@{ Address = 'wrong@contoso.com' } ) @@ -270,7 +270,7 @@ try Mock -CommandName Get-SPFarm -MockWith { return @{ } } Mock -CommandName Get-SPCertificateNotificationContact -MockWith { return @( - @{ + [PSCustomObject]@{ Address = 'wrong@contoso.com' } )