diff --git a/CHANGELOG.md b/CHANGELOG.md index aa6287c83..092f03e7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,21 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- SPFarmPropertyBag - - Added support for boolean and int32 data types -- SPInstall - - Added additional ExitCode for incorrect license key -- SPShellAdmin - - Added additional logging to improve troubleshooting + - SPWebAppPeoplePickerSettings + - Added the CustomFilter parameter to the resource + - Added the ShortDomainName parameter to the resource ### Fixed -- SPSearchServiceApp - - Fixed issue where the database permissions were not corrected for new - search service applications. - SPWebApplication - - Fixed an issue where the Set method tried to use the Parameter SecureSocketsLayer with - Set-SPWebApplication on SharePoint Server older than Subscription Edition. + - Fixed an issue where the Set method tried to use the Parameter SecureSocketsLayer with Set-SPWebApplication on SharePoint Server older than Subscription Edition. +- SPWebAppPeoplePickerSettings + - Fixed an issue where the Set method would not update a non mandatory parameter on an existing SPWebAppPPSearchDomain ## [5.1.0] - 2022-02-24 diff --git a/SharePointDsc/DSCResources/MSFT_SPWebAppPeoplePickerSettings/MSFT_SPWebAppPeoplePickerSettings.psm1 b/SharePointDsc/DSCResources/MSFT_SPWebAppPeoplePickerSettings/MSFT_SPWebAppPeoplePickerSettings.psm1 index 96e776b69..25f2adc6e 100644 --- a/SharePointDsc/DSCResources/MSFT_SPWebAppPeoplePickerSettings/MSFT_SPWebAppPeoplePickerSettings.psm1 +++ b/SharePointDsc/DSCResources/MSFT_SPWebAppPeoplePickerSettings/MSFT_SPWebAppPeoplePickerSettings.psm1 @@ -61,6 +61,8 @@ function Get-TargetResource $searchADDomain.FQDN = $searchDomain.DomainName $searchADDomain.IsForest = $searchDomain.IsForest $searchADDomain.AccessAccount = $searchDomain.LoginName + $searchADDomain.CustomFilter = $searchDomain.CustomFilter + $searchADDomain.ShortDomainName = $searchDomain.ShortDomainName $searchADDomains += $searchADDomain } @@ -211,9 +213,42 @@ function Set-TargetResource $adsearchobj.SetPassword($accessAccountPassword) } } + if ($null -ne ($searchADDomain.CimInstanceProperties | Where-Object -FilterScript { $_.Name -eq 'CustomFilter' })) + { + $adsearchobj.CustomFilter = $searchADDomain.CustomFilter + } + if ($null -ne ($searchADDomain.CimInstanceProperties | Where-Object -FilterScript { $_.Name -eq 'ShortDomainName' })) + { + $adsearchobj.ShortDomainName = $searchADDomain.ShortDomainName + } $wa.PeoplePickerSettings.SearchActiveDirectoryDomains.Add($adsearchobj) } + else + { + if ($null -ne ($searchADDomain.CimInstanceProperties | Where-Object -FilterScript { $_.Name -eq 'AccessAccount' })) + { + $configuredDomain.LoginName = $searchADDomain.AccessAccount.UserName + + if ([string]::IsNullOrEmpty($searchADDomain.AccessAccount.Password)) + { + $configuredDomain.SetPassword($null) + } + else + { + $accessAccountPassword = ConvertTo-SecureString $searchADDomain.AccessAccount.Password -AsPlainText -Force + $configuredDomain.SetPassword($accessAccountPassword) + } + } + if ($null -ne ($searchADDomain.CimInstanceProperties | Where-Object -FilterScript { $_.Name -eq 'CustomFilter' })) + { + $configuredDomain.CustomFilter = $searchADDomain.CustomFilter + } + if ($null -ne ($searchADDomain.CimInstanceProperties | Where-Object -FilterScript { $_.Name -eq 'ShortDomainName' })) + { + $configuredDomain.ShortDomainName = $searchADDomain.ShortDomainName + } + } } # Reverse Check: Configured domains do not exist in config @@ -319,15 +354,45 @@ function Test-TargetResource Write-Verbose -Message "Test-TargetResource returned false" return $false } + else + { + if ($searchADDomain.ContainsKey('AccessAccount') -and $searchADDomain.AccessAccount.UserName -ne $specifiedDomain.LoginName) + { + $message = "Current LoginName Property of SearchActiveDirectoryDomain $searchADDomain does not match the desired state." + Write-Verbose -Message $message + Add-SPDscEvent -Message $message -EntryType 'Error' -EventID 1 -Source $MyInvocation.MyCommand.Source + + Write-Verbose -Message "Test-TargetResource returned false" + return $false + } + if ($searchADDomain.ContainsKey('CustomFilter') -and $searchADDomain.CustomFilter -ne $specifiedDomain.CustomFilter) + { + $message = "Current CustomFilter Property of SearchActiveDirectoryDomain $searchADDomain does not match the desired state." + Write-Verbose -Message $message + Add-SPDscEvent -Message $message -EntryType 'Error' -EventID 1 -Source $MyInvocation.MyCommand.Source + + Write-Verbose -Message "Test-TargetResource returned false" + return $false + } + if ($searchADDomain.ContainsKey('ShortDomainName') -and $searchADDomain.ShortDomainName -ne $specifiedDomain.ShortDomainName) + { + $message = "Current ShortDomainName Property of SearchActiveDirectoryDomain $searchADDomain does not match the desired state." + Write-Verbose -Message $message + Add-SPDscEvent -Message $message -EntryType 'Error' -EventID 1 -Source $MyInvocation.MyCommand.Source + + Write-Verbose -Message "Test-TargetResource returned false" + return $false + } + } } $result = Test-SPDscParameterState -CurrentValues $CurrentValues ` -Source $($MyInvocation.MyCommand.Source) ` -DesiredValues $PSBoundParameters ` - -ValuesToCheck @("ActiveDirectoryCustomFilter", ` - "ActiveDirectoryCustomQuery", ` - "ActiveDirectorySearchTimeout", ` - "OnlySearchWithinSiteCollection", + -ValuesToCheck @("ActiveDirectoryCustomFilter", + "ActiveDirectoryCustomQuery", + "ActiveDirectorySearchTimeout", + "OnlySearchWithinSiteCollection", "PeopleEditorOnlyResolveWithinSiteCollection") Write-Verbose -Message "Test-TargetResource returned $result" diff --git a/SharePointDsc/DSCResources/MSFT_SPWebAppPeoplePickerSettings/MSFT_SPWebAppPeoplePickerSettings.schema.mof b/SharePointDsc/DSCResources/MSFT_SPWebAppPeoplePickerSettings/MSFT_SPWebAppPeoplePickerSettings.schema.mof index fd2bdf8c7..f452421d1 100644 --- a/SharePointDsc/DSCResources/MSFT_SPWebAppPeoplePickerSettings/MSFT_SPWebAppPeoplePickerSettings.schema.mof +++ b/SharePointDsc/DSCResources/MSFT_SPWebAppPeoplePickerSettings/MSFT_SPWebAppPeoplePickerSettings.schema.mof @@ -4,6 +4,8 @@ Class MSFT_SPWebAppPPSearchDomain [Required, Description("FQDN of the domain or forest")] String FQDN; [Required, Description("Is the FQDN a forest?")] Boolean IsForest; [Write, Description("Specifies the credentials to use to connect to the specified domain or forest"), EmbeddedInstance("MSFT_Credential")] String AccessAccount; + [Write, Description("Sets a customized query filter to send to Active Directory")] String CustomFilter; + [Write, Description("NetBIOS name of the domain or forest")] String ShortDomainName; }; [ClassVersion("1.0.0.0"), FriendlyName("SPWebAppPeoplePickerSettings")] class MSFT_SPWebAppPeoplePickerSettings : OMI_BaseResource diff --git a/SharePointDsc/DSCResources/MSFT_SPWebAppPeoplePickerSettings/readme.md b/SharePointDsc/DSCResources/MSFT_SPWebAppPeoplePickerSettings/readme.md index 32987f58c..1c7289feb 100644 --- a/SharePointDsc/DSCResources/MSFT_SPWebAppPeoplePickerSettings/readme.md +++ b/SharePointDsc/DSCResources/MSFT_SPWebAppPeoplePickerSettings/readme.md @@ -15,3 +15,8 @@ queried before you can configure the SearchActiveDirectoryDomains. The encryption key must be set on every front-end web server in the farm on which SharePoint is installed: https://technet.microsoft.com/en-us/library/gg602075(v=office.15).aspx#section3 + +Due to a SharePoint API limitation a password missmatch cannot be detected. +To update the password after the initial add to the SearchActiveDirectoryDomains +the `SPPeoplePickerSearchActiveDirectoryDomain` has to be removed from the SearchActiveDirectoryDomains or +the the password needs to be updated with the `SetPassword(SecureString)` Method directly. diff --git a/SharePointDsc/Examples/Resources/SPWebAppPeoplePickerSettings/1-Example.ps1 b/SharePointDsc/Examples/Resources/SPWebAppPeoplePickerSettings/1-Example.ps1 index fd1122727..c1cc25da6 100644 --- a/SharePointDsc/Examples/Resources/SPWebAppPeoplePickerSettings/1-Example.ps1 +++ b/SharePointDsc/Examples/Resources/SPWebAppPeoplePickerSettings/1-Example.ps1 @@ -64,10 +64,13 @@ Configuration Example ActiveDirectorySearchTimeout = 30 OnlySearchWithinSiteCollection = $false SearchActiveDirectoryDomains = @( - MSFT_SPWebAppPPSearchDomain { - FQDN = "contoso.com" - IsForest = $false - AccessAccount = $AccessAccount + MSFT_SPWebAppPPSearchDomain + { + FQDN = "contoso.com" + IsForest = $false + AccessAccount = $AccessAccount + CustomFilter = '(company=Contoso)' + ShortDomainName = 'CONTOSO' } ) PsDscRunAsCredential = $SetupAccount diff --git a/tests/Unit/SharePointDsc/SharePointDsc.SPWebAppPeoplePickerSettings.Tests.ps1 b/tests/Unit/SharePointDsc/SharePointDsc.SPWebAppPeoplePickerSettings.Tests.ps1 index c45ea920d..f7d119146 100644 --- a/tests/Unit/SharePointDsc/SharePointDsc.SPWebAppPeoplePickerSettings.Tests.ps1 +++ b/tests/Unit/SharePointDsc/SharePointDsc.SPWebAppPeoplePickerSettings.Tests.ps1 @@ -32,7 +32,7 @@ function Invoke-TestSetup $script:testEnvironment = Initialize-TestEnvironment ` -DSCModuleName $script:DSCModuleName ` - -DSCResourceName $script:DSCResourceFullName ` + -DscResourceName $script:DSCResourceFullName ` -ResourceType 'Mof' ` -TestType 'Unit' } @@ -49,7 +49,7 @@ try InModuleScope -ModuleName $script:DSCResourceFullName -ScriptBlock { Describe -Name $Global:SPDscHelper.DescribeHeader -Fixture { BeforeAll { - Invoke-Command -Scriptblock $Global:SPDscHelper.InitializeScript -NoNewScope + Invoke-Command -ScriptBlock $Global:SPDscHelper.InitializeScript -NoNewScope # Initialize tests $mockPassword = ConvertTo-SecureString -String "password" -AsPlainText -Force @@ -130,7 +130,7 @@ try } } - Context -Name "Search domain settings do not match actual values" -Fixture { + Context -Name "Search domain settings do not match actual values (Domain does not exist)" -Fixture { BeforeAll { $testParams = @{ WebAppUrl = "http://sharepoint.contoso.com" @@ -198,6 +198,119 @@ try } } + Context -Name "Search domain settings do not match actual values (Domain exists)" -Fixture { + BeforeAll { + $testParams = @{ + WebAppUrl = "http://sharepoint.contoso.com" + SearchActiveDirectoryDomains = @( + (New-CimInstance -ClassName MSFT_SPWebAppPPSearchDomain -Property @{ + FQDN = "contoso.intra" + IsForest = $false + AccessAccount = (New-CimInstance -ClassName MSFT_Credential ` + -Property @{ + Username = [string]$mockAccount.UserName; + Password = [string]$mockAccount.Password; + } ` + -Namespace root/microsoft/windows/desiredstateconfiguration ` + -ClientOnly) + CustomFilter = "(company=Contoso)" + ShortDomainName = "CONTOSO" + } -ClientOnly) + (New-CimInstance -ClassName MSFT_SPWebAppPPSearchDomain -Property @{ + FQDN = "fabrikam.intra" + IsForest = $false + AccessAccount = (New-CimInstance -ClassName MSFT_Credential ` + -Property @{ + Username = [string]$mockAccount.UserName; + Password = [string]$null; + } ` + -Namespace root/microsoft/windows/desiredstateconfiguration ` + -ClientOnly) + CustomFilter = "(company=FABRIKAM)" + ShortDomainName = "FABRIKAM" + } -ClientOnly) + ) + } + + Mock -CommandName Get-SPWebApplication -MockWith { + $searchADdom = New-Object -TypeName "System.Collections.Generic.List[System.Object]" + # Create a SPPeoplePickerSearchActiveDirectoryDomain + $searchDom1 = New-Object -TypeName "Object" + Add-Member -InputObject $searchDom1 -MemberType 'NoteProperty' -Name DomainName -Value "contoso.intra" + Add-Member -InputObject $searchDom1 -MemberType 'NoteProperty' -Name IsForest -Value $false + Add-Member -InputObject $searchDom1 -MemberType 'NoteProperty' -Name LoginName -Value "wrongUsername" + Add-Member -InputObject $searchDom1 -MemberType 'NoteProperty' -Name CustomFilter -Value "(company=Fabrikam)" + Add-Member -InputObject $searchDom1 -MemberType 'NoteProperty' -Name ShortDomainName -Value "FABRIKAM" + $addMemberSetPassword = @{ + InputObject = $searchDom1 + MemberType = 'ScriptMethod' + Name = 'SetPassword' + Value = { + param( + [securestring] + $Password + ) + } + } + Add-Member @addMemberSetPassword + $searchADdom.Add($searchDom1) + + # Create a SPPeoplePickerSearchActiveDirectoryDomain + $searchDom2 = New-Object -TypeName "Object" + Add-Member -InputObject $searchDom2 -MemberType 'NoteProperty' -Name DomainName -Value "fabrikam.intra" + Add-Member -InputObject $searchDom2 -MemberType 'NoteProperty' -Name IsForest -Value $false + Add-Member -InputObject $searchDom2 -MemberType 'NoteProperty' -Name LoginName -Value "wrongUsername" + Add-Member -InputObject $searchDom2 -MemberType 'NoteProperty' -Name CustomFilter -Value "(company=Fabrikam)" + Add-Member -InputObject $searchDom2 -MemberType 'NoteProperty' -Name ShortDomainName -Value "FABRIKAM" + $addMemberSetPassword = @{ + InputObject = $searchDom2 + MemberType = 'ScriptMethod' + Name = 'SetPassword' + Value = { + param( + [securestring] + $Password + ) + } + } + Add-Member @addMemberSetPassword + $searchADdom.Add($searchDom2) + + $returnval = @{ + PeoplePickerSettings = @{ + ActiveDirectoryCustomFilter = "()" + ActiveDirectoryCustomQuery = "()" + ActiveDirectorySearchTimeout = @{ + TotalSeconds = 10 + } + OnlySearchWithinSiteCollection = $true + PeopleEditorOnlyResolveWithinSiteCollection = $true + SearchActiveDirectoryDomains = $searchADdom + } + } + $returnval = $returnval | Add-Member -MemberType ScriptMethod -Name Update -Value { + $Global:SPDscWebApplicationUpdateCalled = $true + } -PassThru + + return $returnval + } + } + + It "Should return SearchTimeOut=10 from the get method" { + (Get-TargetResource @testParams).ActiveDirectorySearchTimeout | Should -Be 10 + } + + It "Should return false from the test method" { + Test-TargetResource @testParams | Should -Be $false + } + + It "Should update the people picker settings" { + $Global:SPDscWebApplicationUpdateCalled = $false + Set-TargetResource @testParams + $Global:SPDscWebApplicationUpdateCalled | Should -Be $true + } + } + Context -Name "Settings do not match actual values" -Fixture { BeforeAll { $testParams = @{ @@ -264,15 +377,17 @@ try WebAppUrl = "http://sharepoint.contoso.com" SearchActiveDirectoryDomains = @( (New-CimInstance -ClassName MSFT_SPWebAppPPSearchDomain -Property @{ - FQDN = "contoso.intra" - IsForest = $false - AccessAccount = (New-CimInstance -ClassName MSFT_Credential ` + FQDN = "contoso.intra" + IsForest = $false + AccessAccount = (New-CimInstance -ClassName MSFT_Credential ` -Property @{ Username = [string]$mockAccount.UserName; Password = [string]$mockAccount.Password; } ` -Namespace root/microsoft/windows/desiredstateconfiguration ` -ClientOnly) + CustomFilter = "(company=Contoso)" + ShortDomainName = "CONTOSO" } -ClientOnly) ) } @@ -288,7 +403,13 @@ try -Value ( $false ) -PassThru | ` Add-Member -MemberType NoteProperty ` -Name LoginName ` - -Value ( $mockAccount.UserName ) -PassThru + -Value ( $mockAccount.UserName ) -PassThru | ` + Add-Member -MemberType NoteProperty ` + -Name CustomFilter ` + -Value ( "(company=Contoso)" ) -PassThru | ` + Add-Member -MemberType NoteProperty ` + -Name ShortDomainName ` + -Value ( "CONTOSO" ) -PassThru $searchADdom.Add($searchDom1) $returnval = @{