diff --git a/CHANGELOG.md b/CHANGELOG.md index c68108640..477f4d001 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - SharePointDsc - Added logging to the event log when the code throws an exception +### Changed + +- SPFarm + - Switched from creating a Lock database to a Lock table in the TempDB. + This to allow the use of precreated databases. +- SPSite + - Added more explanation to documentation on which parameters are checked +- SPWeb + - Added more explanation to documentation on using this resource + +### Fixed + +- SPConfigWizard + - Fixes issue where a CU installation wasn't registered properly in the + config database. Added logic to run the Product Version timer job +- SPSearchTopology + - Fixes issue where applying a topology failed when the search service + instance was disabled instead of offline +- SPSecureStoreServiceApp + - Fixes issue where custom database name was no longer used since v4.3 +- SPShellAdmins + - Fixed issue with Get-DscConfiguration which threw an error when only one + item was returned by the Get method +- SPWordAutomationServiceApp + - Fixed issue where provisioning the service app requires a second run to + update all specified parameters + ## [4.3.0] - 2020-09-30 ### Added diff --git a/SharePointDsc/DSCResources/MSFT_SPConfigWizard/MSFT_SPConfigWizard.psm1 b/SharePointDsc/DSCResources/MSFT_SPConfigWizard/MSFT_SPConfigWizard.psm1 index fab968773..9dbc1b3a4 100644 --- a/SharePointDsc/DSCResources/MSFT_SPConfigWizard/MSFT_SPConfigWizard.psm1 +++ b/SharePointDsc/DSCResources/MSFT_SPConfigWizard/MSFT_SPConfigWizard.psm1 @@ -254,6 +254,26 @@ function Set-TargetResource -ScriptBlock { $psconfigExe = $args[0] + Write-Verbose -Message "Starting 'Product Version Job' timer job" + $pvTimerJob = Get-SPTimerJob -Identity 'job-admin-product-version' + $lastRunTime = $pvTimerJob.LastRunTime + + Start-SPTimerJob -Identity $pvTimerJob + + $jobRunning = $true + $maxCount = 30 + $count = 0 + Write-Verbose -Message "Waiting for 'Product Version Job' timer job to complete" + while ($jobRunning -and $count -le $maxCount) + { + Start-Sleep -Seconds 10 + + $pvTimerJob = Get-SPTimerJob -Identity 'job-admin-product-version' + $jobRunning = $lastRunTime -eq $pvTimerJob.LastRunTime + + $count++ + } + $stdOutTempFile = "$env:TEMP\$((New-Guid).Guid)" $psconfig = Start-Process -FilePath $psconfigExe ` -ArgumentList "-cmd upgrade -inplace b2b -wait -cmd applicationcontent -install -cmd installfeatures -cmd secureresources -cmd services -install" ` diff --git a/SharePointDsc/DSCResources/MSFT_SPFarm/MSFT_SPFarm.psm1 b/SharePointDsc/DSCResources/MSFT_SPFarm/MSFT_SPFarm.psm1 index 7546e5f89..384cf0150 100644 --- a/SharePointDsc/DSCResources/MSFT_SPFarm/MSFT_SPFarm.psm1 +++ b/SharePointDsc/DSCResources/MSFT_SPFarm/MSFT_SPFarm.psm1 @@ -631,7 +631,8 @@ function Set-TargetResource $currentUri = [System.Uri]$centralAdminSite.Url if ($desiredUri.AbsoluteUri -ne $currentUri.AbsoluteUri) { - Write-Verbose -Message "Re-provisioning CA because $($currentUri.AbsoluteUri) does not equal $($desiredUri.AbsoluteUri)" + Write-Verbose -Message ("Re-provisioning CA because $($currentUri.AbsoluteUri) " + ` + "does not equal $($desiredUri.AbsoluteUri)") $reprovisionCentralAdmin = $true } else @@ -657,14 +658,17 @@ function Set-TargetResource if ($desiredUri.Host -ne $iisBindings[0].HostHeader -or $desiredUri.Port -ne $iisBindings[0].Port) { - Write-Verbose -Message "Re-provisioning CA because $($desiredUri.Host) does not equal $($iisBindings[0].HostHeader) or $($desiredUri.Port) does not equal $($iisBindings[0].Port)" + Write-Verbose -Message ("Re-provisioning CA because $($desiredUri.Host) does not " + ` + "equal $($iisBindings[0].HostHeader) or $($desiredUri.Port) does not " + ` + "equal $($iisBindings[0].Port)") $reprovisionCentralAdmin = $true } } else { # iisBindings did not exist or did not contain a valid hostheader - Write-Verbose -Message "Re-provisioning CA because IIS Bindings does not exist or does not contain a valid host header" + Write-Verbose -Message ("Re-provisioning CA because IIS Bindings does not " + ` + "exist or does not contain a valid host header") $reprovisionCentralAdmin = $true } } @@ -707,7 +711,8 @@ function Set-TargetResource if ($CurrentValues.CentralAdministrationAuth -ne $CentralAdministrationAuth -and (-not $reprovisionCentralAdmin)) { - Write-Verbose -Message "Updating CentralAdmin authentication method from $($CurrentValues.CentralAdministrationAuth) to $CentralAdministrationAuth" + Write-Verbose -Message ("Updating CentralAdmin authentication method from " + ` + "$($CurrentValues.CentralAdministrationAuth) to $CentralAdministrationAuth") Invoke-SPDscCommand -Credential $InstallAccount ` -Arguments $PSBoundParameters ` -ScriptBlock { @@ -756,7 +761,8 @@ function Set-TargetResource if ($params.UseSQLAuthentication -eq $true) { - Write-Verbose -Message "Using SQL authentication to create service application as `$useSQLAuthentication is set to $($params.useSQLAuthentication)." + Write-Verbose -Message ("Using SQL authentication to create service application as " + ` + "`$useSQLAuthentication is set to $($params.useSQLAuthentication).") $databaseCredentialsParam = @{ DatabaseCredentials = $params.DatabaseCredentials } @@ -795,12 +801,24 @@ function Set-TargetResource if ($dbStatus.ValidPermissions -eq $false) { - $message = "The current user does not have sufficient permissions to SQL Server" - Add-SPDscEvent -Message $message ` - -EntryType 'Error' ` - -EventID 100 ` - -Source $eventSource - throw $message + if ($dbStatus.DatabaseEmpty -eq $true) + { + # If DatabaseEmpty is True most probably precreated databases are being used + Write-Verbose -Message ("IMPORTANT: Permissions check failed, but an empty " + ` + "configDB '$($params.FarmConfigDatabaseName)' was found. Assuming that " + ` + "precreated databases are being used.") + } + else + { + # If DatabaseEmpty is False, then either the specified ConfigDB doesn't exist or + # is already provisioned + $message = "The current user does not have sufficient permissions to SQL Server" + Add-SPDscEvent -Message $message ` + -EntryType 'Error' ` + -EventID 100 ` + -Source $eventSource + throw $message + } } $executeArgs = @{ @@ -814,12 +832,14 @@ function Set-TargetResource if ($params.useSQLAuthentication -eq $true) { - Write-Verbose -Message "Using SQL authentication to connect to / create farm as `$useSQLAuthentication is set to $($params.useSQLAuthentication)." + Write-Verbose -Message ("Using SQL authentication to connect to / create farm as " + ` + "`$useSQLAuthentication is set to $($params.useSQLAuthentication).") $executeArgs.Add("DatabaseCredentials", $params.DatabaseCredentials) } else { - Write-Verbose -Message "`$useSQLAuthentication is false or not specified; using default Windows authentication." + Write-Verbose -Message ("`$useSQLAuthentication is false or not specified; using " + ` + "default Windows authentication.") } $installedVersion = Get-SPDscInstalledProductVersion @@ -1087,7 +1107,8 @@ function Set-TargetResource } elseif ($desiredUri.AbsoluteUri -ne $currentUri.AbsoluteUri) { - Write-Verbose -Message "Re-provisioning CA because $($currentUri.AbsoluteUri) does not equal $($desiredUri.AbsoluteUri)" + Write-Verbose -Message ("Re-provisioning CA because $($currentUri.AbsoluteUri) " + ` + "does not equal $($desiredUri.AbsoluteUri)") $reprovisionCentralAdmin = $true } diff --git a/SharePointDsc/DSCResources/MSFT_SPFarm/Readme.md b/SharePointDsc/DSCResources/MSFT_SPFarm/Readme.md index f483a9722..9af563627 100644 --- a/SharePointDsc/DSCResources/MSFT_SPFarm/Readme.md +++ b/SharePointDsc/DSCResources/MSFT_SPFarm/Readme.md @@ -61,3 +61,6 @@ NOTE: When using SharePoint 2016 and later and enabling the Developer Dashboard, please make sure you also provision the Usage and Health service application to make sure the Developer Dashboard works properly. + +NOTE2: +Since v4.4 the resource supports the use of precreated databases. diff --git a/SharePointDsc/DSCResources/MSFT_SPSearchTopology/MSFT_SPSearchTopology.psm1 b/SharePointDsc/DSCResources/MSFT_SPSearchTopology/MSFT_SPSearchTopology.psm1 index 83c925bf1..20ffe1b7c 100644 --- a/SharePointDsc/DSCResources/MSFT_SPSearchTopology/MSFT_SPSearchTopology.psm1 +++ b/SharePointDsc/DSCResources/MSFT_SPSearchTopology/MSFT_SPSearchTopology.psm1 @@ -283,7 +283,7 @@ function Set-TargetResource $searchService = Get-SPEnterpriseSearchServiceInstance -Identity $searchServer } - if ($searchService.Status -eq "Offline") + if (($searchService.Status -eq "Offline") -or ($searchService.Status -eq "Disabled")) { Write-Verbose -Message "Start Search Service Instance" Start-SPEnterpriseSearchServiceInstance -Identity $searchServer diff --git a/SharePointDsc/DSCResources/MSFT_SPSecureStoreServiceApp/MSFT_SPSecureStoreServiceApp.psm1 b/SharePointDsc/DSCResources/MSFT_SPSecureStoreServiceApp/MSFT_SPSecureStoreServiceApp.psm1 index 55ac6f805..25f678a86 100644 --- a/SharePointDsc/DSCResources/MSFT_SPSecureStoreServiceApp/MSFT_SPSecureStoreServiceApp.psm1 +++ b/SharePointDsc/DSCResources/MSFT_SPSecureStoreServiceApp/MSFT_SPSecureStoreServiceApp.psm1 @@ -232,6 +232,13 @@ function Set-TargetResource $newParams.Add("DatabasePassword", $params.DatabaseCredentials.Password) } + $paramList = @('AuditlogMaxSize', 'DatabaseName', 'DatabaseServer', 'FailoverDatabaseServer', 'PartitionMode', 'Sharing') + + foreach ($item in ($params.GetEnumerator() | Where-Object -FilterScript { $_.Key -in $paramList })) + { + $newParams.Add($item.Key, $item.Value) + } + $pName = "$($params.Name) Proxy" if ($params.ContainsKey("ProxyName") -and $null -ne $params.ProxyName) diff --git a/SharePointDsc/DSCResources/MSFT_SPShellAdmins/MSFT_SPShellAdmins.psm1 b/SharePointDsc/DSCResources/MSFT_SPShellAdmins/MSFT_SPShellAdmins.psm1 index 9464c83e8..585a72b79 100644 --- a/SharePointDsc/DSCResources/MSFT_SPShellAdmins/MSFT_SPShellAdmins.psm1 +++ b/SharePointDsc/DSCResources/MSFT_SPShellAdmins/MSFT_SPShellAdmins.psm1 @@ -146,7 +146,7 @@ function Get-TargetResource return @{ IsSingleInstance = "Yes" - Members = $shellAdmins.UserName + Members = [System.Array]$shellAdmins.UserName MembersToInclude = $params.MembersToInclude MembersToExclude = $params.MembersToExclude Databases = $cdbPermissions diff --git a/SharePointDsc/DSCResources/MSFT_SPSite/readme.md b/SharePointDsc/DSCResources/MSFT_SPSite/readme.md index 7b20f6fea..656ba6d69 100644 --- a/SharePointDsc/DSCResources/MSFT_SPSite/readme.md +++ b/SharePointDsc/DSCResources/MSFT_SPSite/readme.md @@ -7,9 +7,13 @@ This resource will provision a site collection to the current farm, based on the settings that are passed through. These settings map to the New-SPSite cmdlet and accept the same values and types. -The current version of SharePointDsc is only able to check for the existence -of a site collection, the additional parameters are not checked for yet, but -will be in a later release +When the site collection exists, not all parameters are checked for being +in the desired state. The following parameters are checked: +QuotaTemplate, OwnerAlias, SecondaryOwnerAlias, AdministrationSiteType + +Since the title of the site collection can be changed by the site collection +owner and can result in a conflict between the owner and DSC. Therefore the +resource is only using the Name parameter during site creation. NOTE: When creating Host Header Site Collections, do not use the HostHeader diff --git a/SharePointDsc/DSCResources/MSFT_SPUserProfileServiceApp/readme.md b/SharePointDsc/DSCResources/MSFT_SPUserProfileServiceApp/readme.md index 2112766ab..e9d38ab2d 100644 --- a/SharePointDsc/DSCResources/MSFT_SPUserProfileServiceApp/readme.md +++ b/SharePointDsc/DSCResources/MSFT_SPUserProfileServiceApp/readme.md @@ -33,10 +33,6 @@ implications. More information about these risks can be found at: http://www.powershellmagazine.com/2014/03/06/accidental-sabotage-beware-of-credssp/ NOTE2: -You should always specify the MySiteHostLocation parameter. Currently this is not -a required parameter, but will be as of SharePointDsc v4.0. - -NOTE3: The UpdateProxyGroup parameter fixes the following issue: The User Profile service is looking up the proxy groups to find the correct MMS. Unfortunately it doesn't follow what you configured in Central Administration. diff --git a/SharePointDsc/DSCResources/MSFT_SPWeb/readme.md b/SharePointDsc/DSCResources/MSFT_SPWeb/readme.md index 1183566fd..473580d53 100644 --- a/SharePointDsc/DSCResources/MSFT_SPWeb/readme.md +++ b/SharePointDsc/DSCResources/MSFT_SPWeb/readme.md @@ -8,3 +8,8 @@ through. These settings map to the New-SPWeb cmdlet and accept the same values The default value for the Ensure parameter is Present. When not specifying this parameter, the web is created. + +NOTE: +Since subsites/webs can be created/deleted by site collection owners it is +possible that using this resource results in a conflict between the owner +and DSC. Therefore be careful using this resource. diff --git a/SharePointDsc/DSCResources/MSFT_SPWordAutomationServiceApp/MSFT_SPWordAutomationServiceApp.psm1 b/SharePointDsc/DSCResources/MSFT_SPWordAutomationServiceApp/MSFT_SPWordAutomationServiceApp.psm1 index 5cbd436b5..1aa2e35eb 100644 --- a/SharePointDsc/DSCResources/MSFT_SPWordAutomationServiceApp/MSFT_SPWordAutomationServiceApp.psm1 +++ b/SharePointDsc/DSCResources/MSFT_SPWordAutomationServiceApp/MSFT_SPWordAutomationServiceApp.psm1 @@ -371,20 +371,18 @@ function Set-TargetResource $appPool = Get-SPServiceApplicationPool -Identity $params.ApplicationPool if ($appPool) { - $cmdletparams = @{ } - $cmdletparams.Name = $params.Name - if ($params.Name) + $cmdletparams = @{ + Name = $params.Name + ApplicationPool = $params.ApplicationPool + } + if ($params.ContainsKey("DatabaseName")) { $cmdletparams.DatabaseName = $params.DatabaseName } - if ($params.Name) + if ($params.ContainsKey("DatabaseServer")) { $cmdletparams.DatabaseServer = $params.DatabaseServer } - if ($params.Name) - { - $cmdletparams.ApplicationPool = $params.ApplicationPool - } if ($params.useSQLAuthentication -eq $true) { @@ -408,6 +406,10 @@ function Set-TargetResource throw $message } } + + # Retrieving updated current state, so additionally + # specified parameters are also updated. + $result = Get-TargetResource @PSBoundParameters } if ($result.Ensure -eq "Present" -and $Ensure -eq "Present") diff --git a/SharePointDsc/Modules/SharePointDsc.Farm/SPFarm.psm1 b/SharePointDsc/Modules/SharePointDsc.Farm/SPFarm.psm1 index 5f7e521b9..9763b518f 100644 --- a/SharePointDsc/Modules/SharePointDsc.Farm/SPFarm.psm1 +++ b/SharePointDsc/Modules/SharePointDsc.Farm/SPFarm.psm1 @@ -64,9 +64,6 @@ function Get-SPDscConfigDBStatus $command.CommandText = "SELECT COUNT(*) FROM sys.databases WHERE name = '$Database'" $configDBexists = ($command.ExecuteScalar() -eq 1) - $command.CommandText = "SELECT COUNT(*) FROM sys.databases WHERE name = '$($Database)_Lock'" - $lockExists = ($command.ExecuteScalar() -eq 1) - $serverRolesToCheck = @("dbcreator", "securityadmin") $hasPermissions = $true foreach ($serverRole in $serverRolesToCheck) @@ -88,6 +85,10 @@ function Get-SPDscConfigDBStatus $configDBempty = ($command.ExecuteScalar() -eq 0) } + $connection.ChangeDatabase('TempDB') + $command.CommandText = "SELECT COUNT([name]) FROM sys.tables WHERE [name] = 'SPDscLock'" + $lockExists = ($command.ExecuteScalar() -eq 1) + return @{ DatabaseExists = $configDBexists DatabaseEmpty = $configDBempty @@ -232,7 +233,7 @@ function Add-SPDscConfigDBLock } else # Just use Windows integrated auth { - $connection.ConnectionString = "Server=$SQLServer;Integrated Security=SSPI;Database=Master" + $connection.ConnectionString = "Server=$SQLServer;Integrated Security=SSPI;Database=TempDB" } $command = New-Object -TypeName "System.Data.SqlClient.SqlCommand" @@ -241,8 +242,8 @@ function Add-SPDscConfigDBLock $connection.Open() $command.Connection = $connection - $command.CommandText = "CREATE DATABASE [$($Database)_Lock]" - $command.ExecuteNonQuery() + $command.CommandText = "CREATE TABLE SPDscLock (Locked BIT)" + $null = $command.ExecuteNonQuery() } finally { @@ -308,7 +309,7 @@ function Remove-SPDscConfigDBLock } else # Just use Windows integrated auth { - $connection.ConnectionString = "Server=$SQLServer;Integrated Security=SSPI;Database=Master" + $connection.ConnectionString = "Server=$SQLServer;Integrated Security=SSPI;Database=TempDB" } $command = New-Object -TypeName "System.Data.SqlClient.SqlCommand" @@ -317,8 +318,8 @@ function Remove-SPDscConfigDBLock $connection.Open() $command.Connection = $connection - $command.CommandText = "DROP DATABASE [$($Database)_Lock]" - $command.ExecuteNonQuery() + $command.CommandText = "DROP TABLE [SPDscLock]" + $null = $command.ExecuteNonQuery() } finally { @@ -329,4 +330,3 @@ function Remove-SPDscConfigDBLock } } } - diff --git a/tests/Unit/SharePointDsc/SharePointDsc.Farm.Tests.ps1 b/tests/Unit/SharePointDsc/SharePointDsc.Farm.Tests.ps1 new file mode 100644 index 000000000..e40334deb --- /dev/null +++ b/tests/Unit/SharePointDsc/SharePointDsc.Farm.Tests.ps1 @@ -0,0 +1,456 @@ +# Ignoring this because we need to generate a stub credential to run the tests here +[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingConvertToSecureStringWithPlainText", "")] +[CmdletBinding()] +param +( + [Parameter()] + [string] + $SharePointCmdletModule = (Join-Path -Path $PSScriptRoot ` + -ChildPath "..\Stubs\SharePoint\15.0.4805.1000\Microsoft.SharePoint.PowerShell.psm1" ` + -Resolve) +) + +#region HEADER +$script:projectPath = "$PSScriptRoot\..\..\.." | Convert-Path +$script:projectName = (Get-ChildItem -Path "$script:projectPath\*\*.psd1" | Where-Object -FilterScript { + ($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and + $(try + { Test-ModuleManifest -Path $_.FullName -ErrorAction Stop + } + catch + { $false + }) + }).BaseName + +$script:parentModule = Get-Module -Name $script:projectName -ListAvailable | Select-Object -First 1 +$script:subModulesFolder = Join-Path -Path $script:parentModule.ModuleBase -ChildPath 'Modules' +Remove-Module -Name $script:parentModule -Force -ErrorAction 'SilentlyContinue' + +$script:subModuleName = (Split-Path -Path $PSCommandPath -Leaf) -replace '\.Tests.ps1' +$script:subModuleFile = Join-Path -Path $script:subModulesFolder -ChildPath "$($script:subModuleName)\SPFarm.psm1" + +Import-Module $script:subModuleFile -Force -ErrorAction Stop +#endregion HEADER + +function Invoke-TestSetup +{ + try + { + Import-Module -Name DscResource.Test -Force + + Import-Module -Name (Join-Path -Path $PSScriptRoot ` + -ChildPath "..\UnitTestHelper.psm1" ` + -Resolve) + + $moduleVersionFolder = ($ModuleVersion -split "-")[0] + + $Global:SPDscHelper = New-SPDscUnitTestHelper -SharePointStubModule $SharePointCmdletModule ` + -SubModulePath "Modules\SharePointDsc.Farm\SPFarm.psm1" ` + -ExcludeInvokeHelper ` + -ModuleVersion $moduleVersionFolder + } + catch [System.IO.FileNotFoundException] + { + throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -Tasks build" first.' + } +} + +function Invoke-TestCleanup +{ +} + +Invoke-TestSetup + +try +{ + InModuleScope -ModuleName $Global:SPDscHelper.ModuleName -ScriptBlock { + Describe -Name $Global:SPDscHelper.DescribeHeader -Fixture { + BeforeAll { + Invoke-Command -ScriptBlock $Global:SPDscHelper.InitializeScript -NoNewScope + } + + Context -Name "Validate Get-SPDscConfigDBStatus" -Fixture { + BeforeAll { + Mock -CommandName New-Object -MockWith { + $returnval = @{ + ConnectionString = '' + State = 'Open' + } + + $returnval = $returnval | Add-Member -MemberType ScriptMethod ` + -Name Open ` + -Value { + } -PassThru -Force + + $returnval = $returnval | Add-Member -MemberType ScriptMethod ` + -Name ChangeDatabase ` + -Value { + } -PassThru -Force + + $returnval = $returnval | Add-Member -MemberType ScriptMethod ` + -Name Close ` + -Value { + } -PassThru -Force + + $returnval = $returnval | Add-Member -MemberType ScriptMethod ` + -Name Dispose ` + -Value { + } -PassThru -Force + + return $returnval + } -ParameterFilter { + $TypeName -eq "System.Data.SqlClient.SqlConnection" + } + } + + It "Should return ValidPermissions=False" { + Mock -CommandName New-Object -MockWith { + $returnval = @{ + Connection = '' + } + + $returnval = $returnval | Add-Member -MemberType ScriptMethod ` + -name ExecuteScalar ` + -Value { + $global:RunQuery++ + switch ($global:RunQuery) + { + 1 # ConfigDB exists + { return 1 + } + 2 # Check permissions + { return "0" + } + 3 # Check permissions + { return "1" + } + 4 # Database empty + { return 20 + } + 5 # Locked + { return 0 + } + } + } -PassThru -Force + + return $returnval + } -ParameterFilter { + $TypeName -eq "System.Data.SqlClient.SqlCommand" + } + + $global:RunQuery = 0 + $result = Get-SPDscConfigDBStatus -SQLServer 'sql01' -Database 'SP_Config' + $result.DatabaseExists | Should -Be $true + $result.DatabaseEmpty | Should -Be $false + $result.ValidPermissions | Should -Be $false + $result.Locked | Should -Be $false + } + + It "Should return DatabaseEmpty=False" { + Mock -CommandName New-Object -MockWith { + $returnval = @{ + Connection = '' + } + + $returnval = $returnval | Add-Member -MemberType ScriptMethod ` + -name ExecuteScalar ` + -Value { + $global:RunQuery++ + switch ($global:RunQuery) + { + 1 # ConfigDB exists + { return 1 + } + 2 # Check permissions + { return "1" + } + 3 # Check permissions + { return "1" + } + 4 # Database empty + { return 20 + } + 5 # Locked + { return 0 + } + } + } -PassThru -Force + + return $returnval + } -ParameterFilter { + $TypeName -eq "System.Data.SqlClient.SqlCommand" + } + + $global:RunQuery = 0 + $result = Get-SPDscConfigDBStatus -SQLServer 'sql01' -Database 'SP_Config' + $result.DatabaseExists | Should -Be $true + $result.DatabaseEmpty | Should -Be $false + $result.ValidPermissions | Should -Be $true + $result.Locked | Should -Be $false + } + + It "Should return DatabaseExists=True" { + Mock -CommandName New-Object -MockWith { + $returnval = @{ + Connection = '' + } + + $returnval = $returnval | Add-Member -MemberType ScriptMethod ` + -name ExecuteScalar ` + -Value { + $global:RunQuery++ + switch ($global:RunQuery) + { + 1 # ConfigDB exists + { return 1 + } + 2 # Check permissions + { return "1" + } + 3 # Check permissions + { return "1" + } + 4 # Database empty + { return 0 + } + 5 # Locked + { return 0 + } + } + } -PassThru -Force + + return $returnval + } -ParameterFilter { + $TypeName -eq "System.Data.SqlClient.SqlCommand" + } + + $global:RunQuery = 0 + $result = Get-SPDscConfigDBStatus -SQLServer 'sql01' -Database 'SP_Config' + $result.DatabaseExists | Should -Be $true + $result.DatabaseEmpty | Should -Be $true + $result.ValidPermissions | Should -Be $true + $result.Locked | Should -Be $false + } + + It "Should return DatabaseExists=False, ValidPermissions=True, DatabaseEmpty=False and Locked=False" { + Mock -CommandName New-Object -MockWith { + $returnval = @{ + Connection = '' + } + + $returnval = $returnval | Add-Member -MemberType ScriptMethod ` + -name ExecuteScalar ` + -Value { + $global:RunQuery++ + switch ($global:RunQuery) + { + 1 # ConfigDB exists + { return 0 + } + 2 # Check permissions + { return "1" + } + 3 # Check permissions + { return "1" + } + 4 # Locked + { return 0 + } + } + } -PassThru -Force + + return $returnval + } -ParameterFilter { + $TypeName -eq "System.Data.SqlClient.SqlCommand" + } + + $global:RunQuery = 0 + $result = Get-SPDscConfigDBStatus -SQLServer 'sql01' -Database 'SP_Config' + $result.DatabaseExists | Should -Be $false + $result.DatabaseEmpty | Should -Be $false + $result.ValidPermissions | Should -Be $true + $result.Locked | Should -Be $false + } + } + + Context -Name "Validate Get-SPDscSQLInstanceStatus" -Fixture { + BeforeAll { + Mock -CommandName New-Object -MockWith { + $returnval = @{ + ConnectionString = '' + State = 'Open' + } + + $returnval = $returnval | Add-Member -MemberType ScriptMethod ` + -Name Open ` + -Value { + } -PassThru -Force + + $returnval = $returnval | Add-Member -MemberType ScriptMethod ` + -Name Close ` + -Value { + } -PassThru -Force + + $returnval = $returnval | Add-Member -MemberType ScriptMethod ` + -Name Dispose ` + -Value { + } -PassThru -Force + + return $returnval + } -ParameterFilter { + $TypeName -eq "System.Data.SqlClient.SqlConnection" + } + } + + It "Should return MaxDopCorrect=True" { + Mock -CommandName New-Object -MockWith { + $returnval = @{ + Connection = '' + } + + $returnval = $returnval | Add-Member -MemberType ScriptMethod ` + -name ExecuteScalar ` + -Value { + return 1 + } -PassThru -Force + + return $returnval + } -ParameterFilter { + $TypeName -eq "System.Data.SqlClient.SqlCommand" + } + + $result = Get-SPDscSQLInstanceStatus -SQLServer 'sql01' + $result.MaxDOPCorrect | Should -Be $true + } + + It "Should return MaxDopCorrect=False" { + Mock -CommandName New-Object -MockWith { + $returnval = @{ + Connection = '' + } + + $returnval = $returnval | Add-Member -MemberType ScriptMethod ` + -name ExecuteScalar ` + -Value { + return 0 + } -PassThru -Force + + return $returnval + } -ParameterFilter { + $TypeName -eq "System.Data.SqlClient.SqlCommand" + } + + $result = Get-SPDscSQLInstanceStatus -SQLServer 'sql01' + $result.MaxDOPCorrect | Should -Be $false + } + } + + Context -Name "Validate Add-SPDscConfigDBLock" -Fixture { + BeforeAll { + Mock -CommandName New-Object -MockWith { + $returnval = @{ + ConnectionString = '' + State = 'Open' + } + + $returnval = $returnval | Add-Member -MemberType ScriptMethod ` + -Name Open ` + -Value { + } -PassThru -Force + + $returnval = $returnval | Add-Member -MemberType ScriptMethod ` + -Name Close ` + -Value { + } -PassThru -Force + + $returnval = $returnval | Add-Member -MemberType ScriptMethod ` + -Name Dispose ` + -Value { + } -PassThru -Force + + return $returnval + } -ParameterFilter { + $TypeName -eq "System.Data.SqlClient.SqlConnection" + } + } + + It "Should run query to create TempDB Lock table" { + Mock -CommandName New-Object -MockWith { + $returnval = @{ + Connection = '' + } + + $returnval = $returnval | Add-Member -MemberType ScriptMethod ` + -name ExecuteNonQuery ` + -Value { + $global:ExecutedQuery = $true + } -PassThru -Force + + return $returnval + } -ParameterFilter { + $TypeName -eq "System.Data.SqlClient.SqlCommand" + } + + $global:ExecutedQuery = $false + $result = Add-SPDscConfigDBLock -SQLServer 'sql01' -Database 'SP_Config' + $global:ExecutedQuery | Should -Be $true + } + } + + Context -Name "Validate Remove-SPDscConfigDBLock" -Fixture { + BeforeAll { + Mock -CommandName New-Object -MockWith { + $returnval = @{ + ConnectionString = '' + State = 'Open' + } + + $returnval = $returnval | Add-Member -MemberType ScriptMethod ` + -Name Open ` + -Value { + } -PassThru -Force + + $returnval = $returnval | Add-Member -MemberType ScriptMethod ` + -Name Close ` + -Value { + } -PassThru -Force + + $returnval = $returnval | Add-Member -MemberType ScriptMethod ` + -Name Dispose ` + -Value { + } -PassThru -Force + + return $returnval + } -ParameterFilter { + $TypeName -eq "System.Data.SqlClient.SqlConnection" + } + } + + It "Should run query to create TempDB Lock table" { + Mock -CommandName New-Object -MockWith { + $returnval = @{ + Connection = '' + } + + $returnval = $returnval | Add-Member -MemberType ScriptMethod ` + -name ExecuteNonQuery ` + -Value { + $global:ExecutedQuery = $true + } -PassThru -Force + + return $returnval + } -ParameterFilter { + $TypeName -eq "System.Data.SqlClient.SqlCommand" + } + + $global:ExecutedQuery = $false + $result = Remove-SPDscConfigDBLock -SQLServer 'sql01' -Database 'SP_Config' + $global:ExecutedQuery | Should -Be $true + } + } + } + } +} +finally +{ +} diff --git a/tests/Unit/SharePointDsc/SharePointDsc.SPConfigWizard.Tests.ps1 b/tests/Unit/SharePointDsc/SharePointDsc.SPConfigWizard.Tests.ps1 index 8b6949df2..f1df5b565 100644 --- a/tests/Unit/SharePointDsc/SharePointDsc.SPConfigWizard.Tests.ps1 +++ b/tests/Unit/SharePointDsc/SharePointDsc.SPConfigWizard.Tests.ps1 @@ -52,6 +52,14 @@ try Invoke-Command -ScriptBlock $Global:SPDscHelper.InitializeScript -NoNewScope # Mocks for all contexts + Mock -CommandName Start-Sleep -MockWith { } + Mock -CommandName Start-SPTimerJob -MockWith { } + Mock -CommandName Get-SPTimerJob -MockWith { + return @{ + LastRunTime = Get-Date + } + } + Mock -CommandName Remove-Item -MockWith { } Mock -CommandName Get-Content -MockWith { return "log info" } Mock -CommandName Get-SPDscServerPatchStatus -MockWith { return "UpgradeRequired" } diff --git a/tests/Unit/SharePointDsc/SharePointDsc.SPFarm.Tests.ps1 b/tests/Unit/SharePointDsc/SharePointDsc.SPFarm.Tests.ps1 index 94836ce9a..f9e6be1d6 100644 --- a/tests/Unit/SharePointDsc/SharePointDsc.SPFarm.Tests.ps1 +++ b/tests/Unit/SharePointDsc/SharePointDsc.SPFarm.Tests.ps1 @@ -316,6 +316,57 @@ try } } + Context -Name "A config database exists, but is empty. This server should be connected to it and needs to populate the empty database" -Fixture { + BeforeAll { + $testParams = @{ + IsSingleInstance = "Yes" + Ensure = "Present" + FarmConfigDatabaseName = "SP_Config" + DatabaseServer = "sql.contoso.com" + FarmAccount = $mockFarmAccount + Passphrase = $mockPassphrase + AdminContentDatabaseName = "SP_AdminContent" + RunCentralAdmin = $true + } + + Mock -CommandName "Get-SPDscRegistryKey" -MockWith { return $null } + Mock -CommandName "Get-SPFarm" -MockWith { return $null } + Mock -CommandName "Get-SPDscConfigDBStatus" -MockWith { + return @{ + Locked = $false + ValidPermissions = $false + DatabaseExists = $true + DatabaseEmpty = $true + } + } + Mock -CommandName "Get-SPDscSQLInstanceStatus" -MockWith { + return @{ + MaxDOPCorrect = $true + } + } + Mock -CommandName "Get-SPWebApplication" -MockWith { + return @{ + IsAdministrationWebApplication = $true + Url = "http://localhost:12345" + } + } + } + + It "Should return absent from the get method" { + (Get-TargetResource @testParams).Ensure | Should -Be "Absent" + } + + It "Should return false from the test method" { + Test-TargetResource @testParams | Should -Be $false + } + + It "Should create the config database in the set method" { + Set-TargetResource @testParams + Assert-MockCalled -CommandName "New-SPConfigurationDatabase" + Assert-MockCalled -CommandName "New-SPCentralAdministration" + } + } + Context -Name "A config database exists, and this server should be connected to it but isn't and this server won't run central admin" -Fixture { BeforeAll { $testParams = @{ @@ -430,6 +481,7 @@ try Locked = $false ValidPermissions = $true DatabaseExists = $true + DatabaseEmpty = $false } } Mock -CommandName "Get-SPDscSQLInstanceStatus" -MockWith {