diff --git a/CHANGELOG.md b/CHANGELOG.md index 497913b5e..83131e47a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,38 @@ ## Unreleased +## 11.4.0.0 + +- Changes to SqlServerDsc + - Updated helper function Restart-SqlService to have to new optional parameters + `SkipClusterCheck` and `SkipWaitForOnline`. This was to support more aspects + of the resource SqlServerNetwork. + - Updated helper function `Import-SQLPSModule` + - To only import module if the + module does not exist in the session. + - To always import the latest version of 'SqlServer' or 'SQLPS' module, if + more than one version exist on the target node. It will still prefer to + use 'SqlServer' module. + - Updated all the examples and integration tests to not use + `PSDscAllowPlainTextPassword`, so examples using credentials or + passwords by default are secure. +- Changes to SqlAlwaysOnService + - Integration tests was updated to handle new IPv6 addresses on the AppVeyor + build worker ([issue #1155](https://github.com/PowerShell/SqlServerDsc/issues/1155)). +- Changes to SqlServerNetwork + - Refactor SqlServerNetwork to not load assembly from GAC ([issue #1151](https://github.com/PowerShell/SqlServerDsc/issues/1151)). + - The resource now supports restarting the SQL Server service when both + enabling and disabling the protocol. + - Added integration tests for this resource + ([issue #751](https://github.com/PowerShell/SqlServerDsc/issues/751)). +- Changes to SqlAG + - Removed excess `Import-SQLPSModule` call. +- Changes to SqlSetup + - Now after a successful install the "SQL PowerShell module" is reevaluated and + forced to be reimported into the session. This is to support that a never + version of SQL Server was installed side-by-side so that SQLPS module should + be used instead. + ## 11.3.0.0 - Changes to SqlServerDsc diff --git a/DSCResources/MSFT_SqlAG/MSFT_SqlAG.psm1 b/DSCResources/MSFT_SqlAG/MSFT_SqlAG.psm1 index 387c9e2b3..41ca2887d 100644 --- a/DSCResources/MSFT_SqlAG/MSFT_SqlAG.psm1 +++ b/DSCResources/MSFT_SqlAG/MSFT_SqlAG.psm1 @@ -232,8 +232,6 @@ function Set-TargetResource $ProcessOnlyOnActiveNode ) - Import-SQLPSModule - # Connect to the instance $serverObject = Connect-SQL -SQLServer $ServerName -SQLInstanceName $InstanceName diff --git a/DSCResources/MSFT_SqlServerNetwork/MSFT_SqlServerNetwork.psm1 b/DSCResources/MSFT_SqlServerNetwork/MSFT_SqlServerNetwork.psm1 index 0ce468776..98da5b0e6 100644 --- a/DSCResources/MSFT_SqlServerNetwork/MSFT_SqlServerNetwork.psm1 +++ b/DSCResources/MSFT_SqlServerNetwork/MSFT_SqlServerNetwork.psm1 @@ -33,31 +33,24 @@ function Get-TargetResource $ProtocolName ) - try - { - $applicationDomainObject = Register-SqlWmiManagement -SQLInstanceName $InstanceName - - $managedComputerObject = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Wmi.ManagedComputer + Import-SQLPSModule - Write-Verbose -Message ($script:localizedData.GetNetworkProtocol -f $ProtocolName, $InstanceName) - $tcp = $managedComputerObject.ServerInstances[$InstanceName].ServerProtocols[$ProtocolName] + $managedComputerObject = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Wmi.ManagedComputer - Write-Verbose -Message $script:localizedData.ReadingNetworkProperties - $returnValue = @{ - InstanceName = $InstanceName - ProtocolName = $ProtocolName - IsEnabled = $tcp.IsEnabled - TcpDynamicPort = ($tcp.IPAddresses['IPAll'].IPAddressProperties['TcpDynamicPorts'].Value -ge 0) - TcpPort = $tcp.IPAddresses['IPAll'].IPAddressProperties['TcpPort'].Value - } + Write-Verbose -Message ($script:localizedData.GetNetworkProtocol -f $ProtocolName, $InstanceName) + $tcp = $managedComputerObject.ServerInstances[$InstanceName].ServerProtocols[$ProtocolName] - $returnValue.Keys | ForEach-Object { - Write-Verbose -Message "$_ = $($returnValue[$_])" - } + Write-Verbose -Message $script:localizedData.ReadingNetworkProperties + $returnValue = @{ + InstanceName = $InstanceName + ProtocolName = $ProtocolName + IsEnabled = $tcp.IsEnabled + TcpDynamicPort = ($tcp.IPAddresses['IPAll'].IPAddressProperties['TcpDynamicPorts'].Value -ge 0) + TcpPort = $tcp.IPAddresses['IPAll'].IPAddressProperties['TcpPort'].Value } - finally - { - Unregister-SqlAssemblies -ApplicationDomain $applicationDomainObject + + $returnValue.Keys | ForEach-Object { + Write-Verbose -Message "$_ = $($returnValue[$_])" } return $returnValue @@ -145,90 +138,104 @@ function Set-TargetResource $getTargetResourceResult = Get-TargetResource -InstanceName $InstanceName -ProtocolName $ProtocolName - try - { - $applicationDomainObject = Register-SqlWmiManagement -SQLInstanceName $InstanceName - - $desiredState = @{ - InstanceName = $InstanceName - ProtocolName = $ProtocolName - IsEnabled = $IsEnabled - TcpDynamicPort = $TcpDynamicPort - TcpPort = $TcpPort - } + $desiredState = @{ + InstanceName = $InstanceName + ProtocolName = $ProtocolName + IsEnabled = $IsEnabled + TcpDynamicPort = $TcpDynamicPort + TcpPort = $TcpPort + } - $isRestartNeeded = $false + $isRestartNeeded = $false - $managedComputerObject = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Wmi.ManagedComputer + # Get-TargetResource makes the necessary calls so the type ManagedComputer is available. + $managedComputerObject = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Wmi.ManagedComputer - Write-Verbose -Message ($script:localizedData.GetNetworkProtocol -f $ProtocolName, $InstanceName) - $tcp = $managedComputerObject.ServerInstances[$InstanceName].ServerProtocols[$ProtocolName] + Write-Verbose -Message ($script:localizedData.GetNetworkProtocol -f $ProtocolName, $InstanceName) + $tcp = $managedComputerObject.ServerInstances[$InstanceName].ServerProtocols[$ProtocolName] - Write-Verbose -Message ($script:localizedData.CheckingProperty -f 'IsEnabled') - if ($desiredState.IsEnabled -ine $getTargetResourceResult.IsEnabled) - { - Write-Verbose -Message ($script:localizedData.UpdatingProperty -f 'IsEnabled', $getTargetResourceResult.IsEnabled, $desiredState.IsEnabled) - $tcp.IsEnabled = $desiredState.IsEnabled - $tcp.Alter() + Write-Verbose -Message ($script:localizedData.CheckingProperty -f 'IsEnabled') + if ($desiredState.IsEnabled -ine $getTargetResourceResult.IsEnabled) + { + Write-Verbose -Message ($script:localizedData.UpdatingProperty -f 'IsEnabled', $getTargetResourceResult.IsEnabled, $desiredState.IsEnabled) + $tcp.IsEnabled = $desiredState.IsEnabled + $tcp.Alter() + + $isRestartNeeded = $true + } - $isRestartNeeded = $true + Write-Verbose -Message ($script:localizedData.CheckingProperty -f 'TcpDynamicPort') + if ($desiredState.TcpDynamicPort -ne $getTargetResourceResult.TcpDynamicPort) + { + # Translates the current and desired state to a string for display + $dynamicPortDisplayValueTable = @{ + $true = 'enabled' + $false = 'disabled' } - Write-Verbose -Message ($script:localizedData.CheckingProperty -f 'TcpDynamicPort') - if ($desiredState.TcpDynamicPort -ne $getTargetResourceResult.TcpDynamicPort) - { - # Translates the current and desired state to a string for display - $dynamicPortDisplayValueTable = @{ - $true = 'enabled' - $false = 'disabled' - } + # Translates the desired state to a valid value + $desiredDynamicPortValue = @{ + $true = '0' + $false = '' + } - # Translates the desired state to a valid value - $desiredDynamicPortValue = @{ - $true = '0' - $false = '' - } + $fromTcpDynamicPortDisplayValue = $dynamicPortDisplayValueTable[$getTargetResourceResult.TcpDynamicPort] + $toTcpDynamicPortDisplayValue = $dynamicPortDisplayValueTable[$desiredState.TcpDynamicPort] - $fromTcpDynamicPortDisplayValue = $dynamicPortDisplayValueTable[$getTargetResourceResult.TcpDynamicPort] - $toTcpDynamicPortDisplayValue = $dynamicPortDisplayValueTable[$desiredState.TcpDynamicPort] + Write-Verbose -Message ($script:localizedData.UpdatingProperty -f 'TcpDynamicPorts', $fromTcpDynamicPortDisplayValue, $toTcpDynamicPortDisplayValue) + $tcp.IPAddresses['IPAll'].IPAddressProperties['TcpDynamicPorts'].Value = $desiredDynamicPortValue[$desiredState.TcpDynamicPort] + $tcp.Alter() - Write-Verbose -Message ($script:localizedData.UpdatingProperty -f 'TcpDynamicPorts', $fromTcpDynamicPortDisplayValue, $toTcpDynamicPortDisplayValue) - $tcp.IPAddresses['IPAll'].IPAddressProperties['TcpDynamicPorts'].Value = $desiredDynamicPortValue[$desiredState.TcpDynamicPort] - $tcp.Alter() + $isRestartNeeded = $true + } - $isRestartNeeded = $true + Write-Verbose -Message ($script:localizedData.CheckingProperty -f 'TcpPort') + if ($desiredState.TcpPort -ine $getTargetResourceResult.TcpPort) + { + $fromTcpPort = $getTargetResourceResult.TcpPort + if ($fromTcpPort -eq '') + { + $fromTcpPort = 'none' } - Write-Verbose -Message ($script:localizedData.CheckingProperty -f 'TcpPort') - if ($desiredState.TcpPort -ine $getTargetResourceResult.TcpPort) + $toTcpPort = $desiredState.TcpPort + if ($toTcpPort -eq '') { - $fromTcpPort = $getTargetResourceResult.TcpPort - if ($fromTcpPort -eq '') - { - $fromTcpPort = 'none' - } + $toTcpPort = 'none' + } - $toTcpPort = $desiredState.TcpPort - if ($toTcpPort -eq '') - { - $toTcpPort = 'none' - } + Write-Verbose -Message ($script:localizedData.UpdatingProperty -f 'TcpPort', $fromTcpPort, $toTcpPort) + $tcp.IPAddresses['IPAll'].IPAddressProperties['TcpPort'].Value = $desiredState.TcpPort + $tcp.Alter() - Write-Verbose -Message ($script:localizedData.UpdatingProperty -f 'TcpPort', $fromTcpPort, $toTcpPort) - $tcp.IPAddresses['IPAll'].IPAddressProperties['TcpPort'].Value = $desiredState.TcpPort - $tcp.Alter() + $isRestartNeeded = $true + } - $isRestartNeeded = $true + if ($RestartService -and $isRestartNeeded) + { + $restartSqlServiceParameters = @{ + SQLServer = $ServerName + SQLInstanceName = $InstanceName + Timeout = $RestartTimeout } - if ($RestartService -and $isRestartNeeded) + if ($getTargetResourceResult.IsEnabled -eq $false -and $IsEnabled -eq $true) { - Restart-SqlService -SQLServer $ServerName -SQLInstanceName $InstanceName -Timeout $RestartTimeout + <# + If the protocol was disabled and now being enabled, is not possible + to connect to the instance to evaluate if it is a clustered instance. + This is being tracked in issue #1174. + #> + $restartSqlServiceParameters['SkipClusterCheck'] = $true } - } - finally - { - Unregister-SqlAssemblies -ApplicationDomain $applicationDomainObject + + if ($PSBoundParameters.ContainsKey('IsEnabled') -and $IsEnabled -eq $false) + { + # If the protocol is disabled it is not possible to connect to the instance. + $restartSqlServiceParameters['SkipWaitForOnline'] = $true + } + + Restart-SqlService @restartSqlServiceParameters } } diff --git a/DSCResources/MSFT_SqlSetup/MSFT_SqlSetup.psm1 b/DSCResources/MSFT_SqlSetup/MSFT_SqlSetup.psm1 index e50fa2f72..9cdbec2a3 100644 --- a/DSCResources/MSFT_SqlSetup/MSFT_SqlSetup.psm1 +++ b/DSCResources/MSFT_SqlSetup/MSFT_SqlSetup.psm1 @@ -1420,8 +1420,9 @@ function Set-TargetResource elseif ($processExitCode -ne 0) { $setupExitMessageError = ('{0} {1}' -f $setupExitMessage, ($script:localizedData.SetupFailed)) - Write-Warning $setupExitMessageError + + $setupEndedInError = $true } else { @@ -1436,13 +1437,35 @@ function Set-TargetResource { Write-Verbose -Message $script:localizedData.Reboot + # Rebooting, so no point in refreshing the session. + $forceReloadPowerShellModule = $false + $global:DSCMachineStatus = 1 } else { Write-Verbose -Message $script:localizedData.SuppressReboot + $forceReloadPowerShellModule = $true } } + else + { + $forceReloadPowerShellModule = $true + } + + if ((-not $setupEndedInError) -and $forceReloadPowerShellModule) + { + <# + Force reload of SQLPS module in case a newer version of + SQL Server was installed that contains a newer version + of the SQLPS module, although if SqlServer module exist + on the target node, that will be used regardless. + This is to make sure we use the latest SQLPS module that + matches the latest assemblies in GAC, mitigating for example + issue #1151. + #> + Import-SQLPSModule -Force + } if (-not (Test-TargetResource @PSBoundParameters)) { diff --git a/Examples/Resources/SqlAG/1-CreateAvailabilityGroup.ps1 b/Examples/Resources/SqlAG/1-CreateAvailabilityGroup.ps1 index 5cdd6fe19..728e85fd8 100644 --- a/Examples/Resources/SqlAG/1-CreateAvailabilityGroup.ps1 +++ b/Examples/Resources/SqlAG/1-CreateAvailabilityGroup.ps1 @@ -6,19 +6,8 @@ $ConfigurationData = @{ AllNodes = @( @{ - NodeName = '*' - InstanceName = 'MSSQLSERVER' - - <# - NOTE! THIS IS NOT RECOMMENDED IN PRODUCTION. - This is added so that AppVeyor automatic tests can pass, otherwise - the tests will fail on passwords being in plain text and not being - encrypted. Because it is not possible to have a certificate in - AppVeyor to encrypt the passwords we need to add the parameter - 'PSDscAllowPlainTextPassword'. - NOTE! THIS IS NOT RECOMMENDED IN PRODUCTION. - #> - PSDscAllowPlainTextPassword = $true + NodeName = '*' + InstanceName = 'MSSQLSERVER' }, @{ diff --git a/Examples/Resources/SqlAG/2-RemoveAvailabilityGroup.ps1 b/Examples/Resources/SqlAG/2-RemoveAvailabilityGroup.ps1 index 6d0e326ce..8fd20b834 100644 --- a/Examples/Resources/SqlAG/2-RemoveAvailabilityGroup.ps1 +++ b/Examples/Resources/SqlAG/2-RemoveAvailabilityGroup.ps1 @@ -6,19 +6,8 @@ This example shows how to ensure that the Availability Group 'TestAG' does not e $ConfigurationData = @{ AllNodes = @( @{ - NodeName = '*' - InstanceName = 'MSSQLSERVER' - - <# - NOTE! THIS IS NOT RECOMMENDED IN PRODUCTION. - This is added so that AppVeyor automatic tests can pass, otherwise - the tests will fail on passwords being in plain text and not being - encrypted. Because it is not possible to have a certificate in - AppVeyor to encrypt the passwords we need to add the parameter - 'PSDscAllowPlainTextPassword'. - NOTE! THIS IS NOT RECOMMENDED IN PRODUCTION. - #> - PSDscAllowPlainTextPassword = $true + NodeName = '*' + InstanceName = 'MSSQLSERVER' }, @{ diff --git a/Examples/Resources/SqlAG/3-CreateAvailabilityGroupDetailed.ps1 b/Examples/Resources/SqlAG/3-CreateAvailabilityGroupDetailed.ps1 index 8672f42ae..a04d163ee 100644 --- a/Examples/Resources/SqlAG/3-CreateAvailabilityGroupDetailed.ps1 +++ b/Examples/Resources/SqlAG/3-CreateAvailabilityGroupDetailed.ps1 @@ -26,17 +26,6 @@ $ConfigurationData = @{ BasicAvailabilityGroup = $False DatabaseHealthTrigger = $True DtcSupportEnabled = $True - - <# - NOTE! THIS IS NOT RECOMMENDED IN PRODUCTION. - This is added so that AppVeyor automatic tests can pass, otherwise - the tests will fail on passwords being in plain text and not being - encrypted. Because it is not possible to have a certificate in - AppVeyor to encrypt the passwords we need to add the parameter - 'PSDscAllowPlainTextPassword'. - NOTE! THIS IS NOT RECOMMENDED IN PRODUCTION. - #> - PSDscAllowPlainTextPassword = $true }, @{ diff --git a/Examples/Resources/SqlAGDatabase/1-AddDatabaseToAvailabilityGroup.ps1 b/Examples/Resources/SqlAGDatabase/1-AddDatabaseToAvailabilityGroup.ps1 index 1e780fed6..014b729a0 100644 --- a/Examples/Resources/SqlAGDatabase/1-AddDatabaseToAvailabilityGroup.ps1 +++ b/Examples/Resources/SqlAGDatabase/1-AddDatabaseToAvailabilityGroup.ps1 @@ -11,19 +11,9 @@ $ConfigurationData = @{ AllNodes = @( @{ - NodeName = '*' - SQLInstanceName = 'MSSQLSERVER' - AvailabilityGroupName = 'TestAG' - <# - NOTE! THIS IS NOT RECOMMENDED IN PRODUCTION. - This is added so that AppVeyor automatic tests can pass, otherwise - the tests will fail on passwords being in plain text and not being - encrypted. Because it is not possible to have a certificate in - AppVeyor to encrypt the passwords we need to add the parameter - 'PSDscAllowPlainTextPassword'. - NOTE! THIS IS NOT RECOMMENDED IN PRODUCTION. - #> - PSDscAllowPlainTextPassword = $true + NodeName = '*' + SQLInstanceName = 'MSSQLSERVER' + AvailabilityGroupName = 'TestAG' }, @{ diff --git a/Examples/Resources/SqlAGDatabase/2-RemoveDatabaseFromAvailabilityGroup.ps1 b/Examples/Resources/SqlAGDatabase/2-RemoveDatabaseFromAvailabilityGroup.ps1 index 665b15272..5333cb3d6 100644 --- a/Examples/Resources/SqlAGDatabase/2-RemoveDatabaseFromAvailabilityGroup.ps1 +++ b/Examples/Resources/SqlAGDatabase/2-RemoveDatabaseFromAvailabilityGroup.ps1 @@ -6,20 +6,9 @@ $ConfigurationData = @{ AllNodes = @( @{ - NodeName = '*' - SQLInstanceName = 'MSSQLSERVER' - AvailabilityGroupName = 'TestAG' - - <# - NOTE! THIS IS NOT RECOMMENDED IN PRODUCTION. - This is added so that AppVeyor automatic tests can pass, otherwise - the tests will fail on passwords being in plain text and not being - encrypted. Because it is not possible to have a certificate in - AppVeyor to encrypt the passwords we need to add the parameter - 'PSDscAllowPlainTextPassword'. - NOTE! THIS IS NOT RECOMMENDED IN PRODUCTION. - #> - PSDscAllowPlainTextPassword = $true + NodeName = '*' + SQLInstanceName = 'MSSQLSERVER' + AvailabilityGroupName = 'TestAG' }, @{ diff --git a/Examples/Resources/SqlAGDatabase/3-MatchDefinedDatabaseInAvailabilityGroup.ps1 b/Examples/Resources/SqlAGDatabase/3-MatchDefinedDatabaseInAvailabilityGroup.ps1 index a2d8dcaf2..bf09492bb 100644 --- a/Examples/Resources/SqlAGDatabase/3-MatchDefinedDatabaseInAvailabilityGroup.ps1 +++ b/Examples/Resources/SqlAGDatabase/3-MatchDefinedDatabaseInAvailabilityGroup.ps1 @@ -6,20 +6,9 @@ $ConfigurationData = @{ AllNodes = @( @{ - NodeName = '*' - SQLInstanceName = 'MSSQLSERVER' - AvailabilityGroupName = 'TestAG' - - <# - NOTE! THIS IS NOT RECOMMENDED IN PRODUCTION. - This is added so that AppVeyor automatic tests can pass, otherwise - the tests will fail on passwords being in plain text and not being - encrypted. Because it is not possible to have a certificate in - AppVeyor to encrypt the passwords we need to add the parameter - 'PSDscAllowPlainTextPassword'. - NOTE! THIS IS NOT RECOMMENDED IN PRODUCTION. - #> - PSDscAllowPlainTextPassword = $true + NodeName = '*' + SQLInstanceName = 'MSSQLSERVER' + AvailabilityGroupName = 'TestAG' }, @{ diff --git a/Examples/Resources/SqlAGReplica/1-CreateAvailabilityGroupReplica.ps1 b/Examples/Resources/SqlAGReplica/1-CreateAvailabilityGroupReplica.ps1 index e36fca4d2..e4e82e2f1 100644 --- a/Examples/Resources/SqlAGReplica/1-CreateAvailabilityGroupReplica.ps1 +++ b/Examples/Resources/SqlAGReplica/1-CreateAvailabilityGroupReplica.ps1 @@ -11,21 +11,10 @@ $ConfigurationData = @{ AllNodes = @( @{ - NodeName = '*' - SQLInstanceName = 'MSSQLSERVER' - AvailabilityGroupName = 'TestAG' - ProcessOnlyOnActiveNode = $true - - <# - NOTE! THIS IS NOT RECOMMENDED IN PRODUCTION. - This is added so that AppVeyor automatic tests can pass, otherwise - the tests will fail on passwords being in plain text and not being - encrypted. Because it is not possible to have a certificate in - AppVeyor to encrypt the passwords we need to add the parameter - 'PSDscAllowPlainTextPassword'. - NOTE! THIS IS NOT RECOMMENDED IN PRODUCTION. - #> - PSDscAllowPlainTextPassword = $true + NodeName = '*' + SQLInstanceName = 'MSSQLSERVER' + AvailabilityGroupName = 'TestAG' + ProcessOnlyOnActiveNode = $true }, @{ diff --git a/Examples/Resources/SqlAGReplica/2-RemoveAvailabilityGroupReplica.ps1 b/Examples/Resources/SqlAGReplica/2-RemoveAvailabilityGroupReplica.ps1 index f9e485e12..f7a51ec84 100644 --- a/Examples/Resources/SqlAGReplica/2-RemoveAvailabilityGroupReplica.ps1 +++ b/Examples/Resources/SqlAGReplica/2-RemoveAvailabilityGroupReplica.ps1 @@ -6,20 +6,9 @@ $ConfigurationData = @{ AllNodes = @( @{ - NodeName = '*' - SQLInstanceName = 'MSSQLSERVER' - AvailabilityGroupName = 'TestAG' - - <# - NOTE! THIS IS NOT RECOMMENDED IN PRODUCTION. - This is added so that AppVeyor automatic tests can pass, otherwise - the tests will fail on passwords being in plain text and not being - encrypted. Because it is not possible to have a certificate in - AppVeyor to encrypt the passwords we need to add the parameter - 'PSDscAllowPlainTextPassword'. - NOTE! THIS IS NOT RECOMMENDED IN PRODUCTION. - #> - PSDscAllowPlainTextPassword = $true + NodeName = '*' + SQLInstanceName = 'MSSQLSERVER' + AvailabilityGroupName = 'TestAG' }, @{ diff --git a/Examples/Resources/SqlRS/4-CompleteWithTwoInstances.ps1 b/Examples/Resources/SqlRS/4-CompleteWithTwoInstances.ps1 index e881fff65..c439c693e 100644 --- a/Examples/Resources/SqlRS/4-CompleteWithTwoInstances.ps1 +++ b/Examples/Resources/SqlRS/4-CompleteWithTwoInstances.ps1 @@ -14,35 +14,24 @@ $ConfigurationData = @{ AllNodes = @( @{ - NodeName = 'localhost' + NodeName = 'localhost' # This is values used for the Reporting Services instance. - InstanceName = 'RS' - Features = 'RS' + InstanceName = 'RS' + Features = 'RS' # This is values used for the Database Engine instance. - DatabaseServerName = $env:COMPUTERNAME - DatabaseServerInstanceName = 'RSDB' - DatabaseServerFeatures = 'SQLENGINE' - DatabaseServerCollation = 'Finnish_Swedish_CI_AS' + DatabaseServerName = $env:COMPUTERNAME + DatabaseServerInstanceName = 'RSDB' + DatabaseServerFeatures = 'SQLENGINE' + DatabaseServerCollation = 'Finnish_Swedish_CI_AS' # This is values used for both instances. - MediaPath = 'Z:\Sql2016Media' - InstallSharedDir = 'C:\Program Files\Microsoft SQL Server' - InstallSharedWOWDir = 'C:\Program Files (x86)\Microsoft SQL Server' - UpdateEnabled = 'False' - BrowserSvcStartupType = 'Automatic' - - <# - NOTE! THIS IS NOT RECOMMENDED IN PRODUCTION. - This is added so that AppVeyor automatic tests can pass, otherwise - the tests will fail on passwords being in plain text and not being - encrypted. Because it is not possible to have a certificate in - AppVeyor to encrypt the passwords we need to add the parameter - 'PSDscAllowPlainTextPassword'. - NOTE! THIS IS NOT RECOMMENDED IN PRODUCTION. - #> - PSDscAllowPlainTextPassword = $true + MediaPath = 'Z:\Sql2016Media' + InstallSharedDir = 'C:\Program Files\Microsoft SQL Server' + InstallSharedWOWDir = 'C:\Program Files (x86)\Microsoft SQL Server' + UpdateEnabled = 'False' + BrowserSvcStartupType = 'Automatic' } ) } diff --git a/Examples/Resources/SqlScript/3-RunScriptCompleteExample.ps1 b/Examples/Resources/SqlScript/3-RunScriptCompleteExample.ps1 index 5a1d471a2..757533553 100644 --- a/Examples/Resources/SqlScript/3-RunScriptCompleteExample.ps1 +++ b/Examples/Resources/SqlScript/3-RunScriptCompleteExample.ps1 @@ -7,22 +7,22 @@ $ConfigurationData = @{ AllNodes = @( @{ - NodeName = 'localhost' + NodeName = 'localhost' - ServerName = $env:COMPUTERNAME - InstanceName = 'DSCTEST' + ServerName = $env:COMPUTERNAME + InstanceName = 'DSCTEST' - DatabaseName = 'ScriptDatabase1' + DatabaseName = 'ScriptDatabase1' - GetSqlScriptPath = Join-Path -Path $env:SystemDrive -ChildPath ([System.IO.Path]::GetRandomFileName()) - SetSqlScriptPath = Join-Path -Path $env:SystemDrive -ChildPath ([System.IO.Path]::GetRandomFileName()) - TestSqlScriptPath = Join-Path -Path $env:SystemDrive -ChildPath ([System.IO.Path]::GetRandomFileName()) + GetSqlScriptPath = Join-Path -Path $env:SystemDrive -ChildPath ([System.IO.Path]::GetRandomFileName()) + SetSqlScriptPath = Join-Path -Path $env:SystemDrive -ChildPath ([System.IO.Path]::GetRandomFileName()) + TestSqlScriptPath = Join-Path -Path $env:SystemDrive -ChildPath ([System.IO.Path]::GetRandomFileName()) - GetSqlScript = @' + GetSqlScript = @' SELECT Name FROM sys.databases WHERE Name = '$(DatabaseName)' FOR JSON AUTO '@ - TestSqlScript = @' + TestSqlScript = @' if (select count(name) from sys.databases where name = '$(DatabaseName)') = 0 BEGIN RAISERROR ('Did not find database [$(DatabaseName)]', 16, 1) @@ -33,20 +33,9 @@ BEGIN END '@ - SetSqlScript = @' + SetSqlScript = @' CREATE DATABASE [$(DatabaseName)] '@ - - <# - NOTE! THIS IS NOT RECOMMENDED IN PRODUCTION. - This is added so that AppVeyor automatic tests can pass, otherwise - the tests will fail on passwords being in plain text and not being - encrypted. Because it is not possible to have a certificate in - AppVeyor to encrypt the passwords we need to add the parameter - 'PSDscAllowPlainTextPassword'. - NOTE! THIS IS NOT RECOMMENDED IN PRODUCTION. - #> - PSDscAllowPlainTextPassword = $true } ) } diff --git a/Examples/Resources/SqlScriptQuery/3-RunScriptCompleteExample.ps1 b/Examples/Resources/SqlScriptQuery/3-RunScriptCompleteExample.ps1 index 7aa2dce5b..b1f0a9eaf 100644 --- a/Examples/Resources/SqlScriptQuery/3-RunScriptCompleteExample.ps1 +++ b/Examples/Resources/SqlScriptQuery/3-RunScriptCompleteExample.ps1 @@ -26,20 +26,9 @@ BEGIN END '@ - SetSqlQuery = @' + SetSqlQuery = @' CREATE DATABASE [$(DatabaseName)] '@ - - <# - NOTE! THIS IS NOT RECOMMENDED IN PRODUCTION. - This is added so that AppVeyor automatic tests can pass, otherwise - the tests will fail on passwords being in plain text and not being - encrypted. Because it is not possible to have a certificate in - AppVeyor to encrypt the passwords we need to add the parameter - 'PSDscAllowPlainTextPassword'. - NOTE! THIS IS NOT RECOMMENDED IN PRODUCTION. - #> - PSDscAllowPlainTextPassword = $true } ) } diff --git a/Examples/Resources/SqlServerDatabaseMail/1-EnableDatabaseMail.ps1 b/Examples/Resources/SqlServerDatabaseMail/1-EnableDatabaseMail.ps1 index 760697ee7..d992d5c45 100644 --- a/Examples/Resources/SqlServerDatabaseMail/1-EnableDatabaseMail.ps1 +++ b/Examples/Resources/SqlServerDatabaseMail/1-EnableDatabaseMail.ps1 @@ -7,28 +7,17 @@ $ConfigurationData = @{ AllNodes = @( @{ - NodeName = 'localhost' - ServerName = $env:COMPUTERNAME - InstanceName = 'DSCSQL2016' + NodeName = 'localhost' + ServerName = $env:COMPUTERNAME + InstanceName = 'DSCSQL2016' - MailServerName = 'mail.company.local' - AccountName = 'MyMail' - ProfileName = 'MyMailProfile' - EmailAddress = 'NoReply@company.local' - Description = 'Default mail account and profile.' - LoggingLevel = 'Normal' - TcpPort = 25 - - <# - NOTE! THIS IS NOT RECOMMENDED IN PRODUCTION. - This is added so that AppVeyor automatic tests can pass, otherwise - the tests will fail on passwords being in plain text and not being - encrypted. Because it is not possible to have a certificate in - AppVeyor to encrypt the passwords we need to add the parameter - 'PSDscAllowPlainTextPassword'. - NOTE! THIS IS NOT RECOMMENDED IN PRODUCTION. - #> - PSDscAllowPlainTextPassword = $true + MailServerName = 'mail.company.local' + AccountName = 'MyMail' + ProfileName = 'MyMailProfile' + EmailAddress = 'NoReply@company.local' + Description = 'Default mail account and profile.' + LoggingLevel = 'Normal' + TcpPort = 25 } ) } diff --git a/Examples/Resources/SqlServerDatabaseMail/2-DisableDatabaseMail.ps1 b/Examples/Resources/SqlServerDatabaseMail/2-DisableDatabaseMail.ps1 index da41beac4..c90da0f66 100644 --- a/Examples/Resources/SqlServerDatabaseMail/2-DisableDatabaseMail.ps1 +++ b/Examples/Resources/SqlServerDatabaseMail/2-DisableDatabaseMail.ps1 @@ -7,28 +7,17 @@ $ConfigurationData = @{ AllNodes = @( @{ - NodeName = 'localhost' - ServerName = $env:COMPUTERNAME - InstanceName = 'DSCSQL2016' + NodeName = 'localhost' + ServerName = $env:COMPUTERNAME + InstanceName = 'DSCSQL2016' - MailServerName = 'mail.company.local' - AccountName = 'MyMail' - ProfileName = 'MyMailProfile' - EmailAddress = 'NoReply@company.local' - Description = 'Default mail account and profile.' - LoggingLevel = 'Normal' - TcpPort = 25 - - <# - NOTE! THIS IS NOT RECOMMENDED IN PRODUCTION. - This is added so that AppVeyor automatic tests can pass, otherwise - the tests will fail on passwords being in plain text and not being - encrypted. Because it is not possible to have a certificate in - AppVeyor to encrypt the passwords we need to add the parameter - 'PSDscAllowPlainTextPassword'. - NOTE! THIS IS NOT RECOMMENDED IN PRODUCTION. - #> - PSDscAllowPlainTextPassword = $true + MailServerName = 'mail.company.local' + AccountName = 'MyMail' + ProfileName = 'MyMailProfile' + EmailAddress = 'NoReply@company.local' + Description = 'Default mail account and profile.' + LoggingLevel = 'Normal' + TcpPort = 25 } ) } diff --git a/Examples/Resources/SqlServerEndpointPermission/3-AddConnectPermissionToTwoReplicasEachWithDifferentServiceAccount.ps1 b/Examples/Resources/SqlServerEndpointPermission/3-AddConnectPermissionToTwoReplicasEachWithDifferentServiceAccount.ps1 index 801e7e8c5..2d14af6f1 100644 --- a/Examples/Resources/SqlServerEndpointPermission/3-AddConnectPermissionToTwoReplicasEachWithDifferentServiceAccount.ps1 +++ b/Examples/Resources/SqlServerEndpointPermission/3-AddConnectPermissionToTwoReplicasEachWithDifferentServiceAccount.ps1 @@ -7,19 +7,8 @@ $ConfigurationData = @{ AllNodes = @( @{ - NodeName = '*' - SqlInstanceName = 'MSSQLSERVER' - - <# - NOTE! THIS IS NOT RECOMMENDED IN PRODUCTION. - This is added so that AppVeyor automatic tests can pass, otherwise - the tests will fail on passwords being in plain text and not being - encrypted. Because it is not possible to have a certificate in - AppVeyor to encrypt the passwords we need to add the parameter - 'PSDscAllowPlainTextPassword'. - NOTE! THIS IS NOT RECOMMENDED IN PRODUCTION. - #> - PSDscAllowPlainTextPassword = $true + NodeName = '*' + SqlInstanceName = 'MSSQLSERVER' }, @{ diff --git a/Examples/Resources/SqlServerEndpointPermission/4-RemoveConnectPermissionForTwoReplicasEachWithDifferentServiceAccount.ps1 b/Examples/Resources/SqlServerEndpointPermission/4-RemoveConnectPermissionForTwoReplicasEachWithDifferentServiceAccount.ps1 index ef654671f..4ac8b2a42 100644 --- a/Examples/Resources/SqlServerEndpointPermission/4-RemoveConnectPermissionForTwoReplicasEachWithDifferentServiceAccount.ps1 +++ b/Examples/Resources/SqlServerEndpointPermission/4-RemoveConnectPermissionForTwoReplicasEachWithDifferentServiceAccount.ps1 @@ -7,19 +7,8 @@ $ConfigurationData = @{ AllNodes = @( @{ - NodeName = '*' - SqlInstanceName = 'MSSQLSERVER' - - <# - NOTE! THIS IS NOT RECOMMENDED IN PRODUCTION. - This is added so that AppVeyor automatic tests can pass, otherwise - the tests will fail on passwords being in plain text and not being - encrypted. Because it is not possible to have a certificate in - AppVeyor to encrypt the passwords we need to add the parameter - 'PSDscAllowPlainTextPassword'. - NOTE! THIS IS NOT RECOMMENDED IN PRODUCTION. - #> - PSDscAllowPlainTextPassword = $true + NodeName = '*' + SqlInstanceName = 'MSSQLSERVER' }, @{ diff --git a/SqlServerDsc.psd1 b/SqlServerDsc.psd1 index fef843b42..ab6800ed2 100644 --- a/SqlServerDsc.psd1 +++ b/SqlServerDsc.psd1 @@ -1,6 +1,6 @@ @{ # Version number of this module. - moduleVersion = '11.3.0.0' + moduleVersion = '11.4.0.0' # ID used to uniquely identify this module GUID = '693ee082-ed36-45a7-b490-88b07c86b42f' @@ -50,14 +50,34 @@ # ReleaseNotes of this module ReleaseNotes = '- Changes to SqlServerDsc - - Moved decoration for integration test to resolve a breaking change in - DscResource.Tests. - - Activated the GitHub App Stale on the GitHub repository. - - Added a CODE\_OF\_CONDUCT.md with the same content as in the README.md - [issue 939](https://github.com/PowerShell/SqlServerDsc/issues/939). - - New resources: - - Added SqlScriptQueryResource. [Chase Wilson (@chasewilson)](https://github.com/chasewilson) - - Fix for issue 779 [Paul Kelly (@prkelly)](https://github.com/prkelly) + - Updated helper function Restart-SqlService to have to new optional parameters + `SkipClusterCheck` and `SkipWaitForOnline`. This was to support more aspects + of the resource SqlServerNetwork. + - Updated helper function `Import-SQLPSModule` + - To only import module if the + module does not exist in the session. + - To always import the latest version of "SqlServer" or "SQLPS" module, if + more than one version exist on the target node. It will still prefer to + use "SqlServer" module. + - Updated all the examples and integration tests to not use + `PSDscAllowPlainTextPassword`, so examples using credentials or + passwords by default are secure. +- Changes to SqlAlwaysOnService + - Integration tests was updated to handle new IPv6 addresses on the AppVeyor + build worker ([issue 1155](https://github.com/PowerShell/SqlServerDsc/issues/1155)). +- Changes to SqlServerNetwork + - Refactor SqlServerNetwork to not load assembly from GAC ([issue 1151](https://github.com/PowerShell/SqlServerDsc/issues/1151)). + - The resource now supports restarting the SQL Server service when both + enabling and disabling the protocol. + - Added integration tests for this resource + ([issue 751](https://github.com/PowerShell/SqlServerDsc/issues/751)). +- Changes to SqlAG + - Removed excess `Import-SQLPSModule` call. +- Changes to SqlSetup + - Now after a successful install the "SQL PowerShell module" is reevaluated and + forced to be reimported into the session. This is to support that a never + version of SQL Server was installed side-by-side so that SQLPS module should + be used instead. ' @@ -80,3 +100,4 @@ + diff --git a/SqlServerDscHelper.psm1 b/SqlServerDscHelper.psm1 index e79e5ca10..8fc106edb 100644 --- a/SqlServerDscHelper.psm1 +++ b/SqlServerDscHelper.psm1 @@ -1,7 +1,7 @@ # Load Localization Data Import-Module -Name (Join-Path -Path (Join-Path -Path $PSScriptRoot ` - -ChildPath 'DscResources') ` - -ChildPath 'CommonResourceHelper.psm1') + -ChildPath 'DscResources') ` + -ChildPath 'CommonResourceHelper.psm1') $script:localizedData = Get-LocalizedData -ResourceName 'SqlServerDscHelper' -ScriptRoot $PSScriptRoot @@ -179,151 +179,6 @@ function Connect-SQLAnalysis return $analysisServicesObject } -<# - .SYNOPSIS - Creates a new application domain and loads the assemblies Microsoft.SqlServer.Smo - for the correct SQL Server major version. - - An isolated application domain is used to load version specific assemblies, this needed - if there is multiple versions of SQL server in the same configuration. So that a newer - version of SQL is not using an older version of the assembly, or vice verse. - - This should be unloaded using the helper function Unregister-SqlAssemblies or - using [System.AppDomain]::Unload($applicationDomainObject). - - .PARAMETER SQLInstanceName - String containing the SQL Server Database Engine instance name to get the major SQL version from. - - .PARAMETER ApplicationDomain - An optional System.AppDomain object to load the assembly into. - - .OUTPUTS - System.AppDomain. Returns the application domain object with SQL SMO loaded. -#> -function Register-SqlSmo -{ - [CmdletBinding()] - [OutputType([System.AppDomain])] - param - ( - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [System.String] - $SQLInstanceName, - - [Parameter()] - [ValidateNotNullOrEmpty()] - [System.AppDomain] - $ApplicationDomain - ) - - $sqlMajorVersion = Get-SqlInstanceMajorVersion -SQLInstanceName $SQLInstanceName - - Write-Verbose -Message ($script:localizedData.SqlMajorVersion -f $sqlMajorVersion) -Verbose - - if ( -not $ApplicationDomain ) - { - $applicationDomainName = $MyInvocation.MyCommand.ModuleName - Write-Verbose -Message ($script:localizedData.CreatingApplicationDomain -f $applicationDomainName) -Verbose - $applicationDomainObject = [System.AppDomain]::CreateDomain($applicationDomainName) - } - else - { - Write-Verbose -Message ($script:localizedData.ReusingApplicationDomain -f $ApplicationDomain.FriendlyName) -Verbose - $applicationDomainObject = $ApplicationDomain - } - - $sqlSmoAssemblyName = "Microsoft.SqlServer.Smo, Version=$sqlMajorVersion.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91" - Write-Verbose -Message ($script:localizedData.LoadingAssembly -f $sqlSmoAssemblyName) -Verbose - $applicationDomainObject.Load($sqlSmoAssemblyName) | Out-Null - - return $applicationDomainObject -} - -<# - .SYNOPSIS - Creates a new application domain and loads the assemblies Microsoft.SqlServer.Smo and - Microsoft.SqlServer.SqlWmiManagement for the correct SQL Server major version. - - An isolated application domain is used to load version specific assemblies, this needed - if there is multiple versions of SQL server in the same configuration. So that a newer - version of SQL is not using an older version of the assembly, or vice verse. - - This should be unloaded using the helper function Unregister-SqlAssemblies or - using [System.AppDomain]::Unload($applicationDomainObject) preferably in a finally block. - - .PARAMETER SQLInstanceName - String containing the SQL Server Database Engine instance name to get the major SQL version from. - - .PARAMETER ApplicationDomain - An optional System.AppDomain object to load the assembly into. - - .OUTPUTS - System.AppDomain. Returns the application domain object with SQL WMI Management loaded. -#> -function Register-SqlWmiManagement -{ - [CmdletBinding()] - [OutputType([System.AppDomain])] - param - ( - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [System.String] - $SQLInstanceName, - - [Parameter()] - [ValidateNotNull()] - [System.AppDomain] - $ApplicationDomain - ) - - $sqlMajorVersion = Get-SqlInstanceMajorVersion -SQLInstanceName $SQLInstanceName - Write-Verbose -Message ($script:localizedData.SqlMajorVersion -f $sqlMajorVersion) -Verbose - - <# - Must register Microsoft.SqlServer.Smo first because that is a - dependency of Microsoft.SqlServer.SqlWmiManagement. - #> - if (-not $ApplicationDomain) - { - $applicationDomainObject = Register-SqlSmo -SQLInstanceName $SQLInstanceName - } - # Returns zero (0) objects if the assembly is not found - elseif (-not ($ApplicationDomain.GetAssemblies().FullName -match 'Microsoft.SqlServer.Smo')) - { - $applicationDomainObject = Register-SqlSmo -SQLInstanceName $SQLInstanceName -ApplicationDomain $ApplicationDomain - } - - $sqlSqlWmiManagementAssemblyName = "Microsoft.SqlServer.SqlWmiManagement, Version=$sqlMajorVersion.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91" - Write-Verbose -Message ($script:localizedData.LoadingAssembly -f $sqlSqlWmiManagementAssemblyName) -Verbose - $applicationDomainObject.Load($sqlSqlWmiManagementAssemblyName) | Out-Null - - return $applicationDomainObject -} - -<# - .SYNOPSIS - Unloads all assemblies in an application domain. It unloads the application domain. - - .PARAMETER ApplicationDomain - System.AppDomain object containing the SQL assemblies to unload. -#> -function Unregister-SqlAssemblies -{ - [CmdletBinding()] - param - ( - [Parameter(Mandatory = $true)] - [ValidateNotNull()] - [System.AppDomain] - $ApplicationDomain - ) - - Write-Verbose -Message ($script:localizedData.UnloadingApplicationDomain -f $ApplicationDomain.FriendlyName) -Verbose - [System.AppDomain]::Unload($ApplicationDomain) -} - <# .SYNOPSIS Returns the major SQL version for the specific instance. @@ -416,11 +271,11 @@ function New-TerminatingError $errorMessage = $script:localizedData.$ErrorType - if(!$errorMessage) + if (!$errorMessage) { $errorMessage = ($script:localizedData.NoKeyFound -f $ErrorType) - if(!$errorMessage) + if (!$errorMessage) { $errorMessage = ("No Localization key found for key: {0}" -f $ErrorType) } @@ -428,7 +283,7 @@ function New-TerminatingError $errorMessage = ($errorMessage -f $FormatArgs) - if( $InnerException ) + if ( $InnerException ) { $errorMessage += " InnerException: $($InnerException.Message)" } @@ -436,7 +291,7 @@ function New-TerminatingError $callStack = Get-PSCallStack # Get Name of calling script - if($callStack[1] -and $callStack[1].ScriptName) + if ($callStack[1] -and $callStack[1].ScriptName) { $scriptPath = $callStack[1].ScriptName @@ -496,10 +351,10 @@ function New-WarningMessage if (!$warningMessage) { $errorParams = @{ - ErrorType = 'NoKeyFound' - FormatArgs = $WarningType + ErrorType = 'NoKeyFound' + FormatArgs = $WarningType ErrorCategory = 'InvalidArgument' - TargetObject = 'New-WarningMessage' + TargetObject = 'New-WarningMessage' } ## Raise an error indicating the localization data is not present @@ -576,8 +431,8 @@ function Test-SQLDscParameterState $returnValue = $true if (($DesiredValues.GetType().Name -ne 'HashTable') ` - -and ($DesiredValues.GetType().Name -ne 'CimInstance') ` - -and ($DesiredValues.GetType().Name -ne 'PSBoundParametersDictionary')) + -and ($DesiredValues.GetType().Name -ne 'CimInstance') ` + -and ($DesiredValues.GetType().Name -ne 'PSBoundParametersDictionary')) { $errorMessage = $script:localizedData.PropertyTypeInvalidForDesiredValues -f $($DesiredValues.GetType().Name) New-InvalidArgumentException -ArgumentName 'DesiredValues' -Message $errorMessage @@ -602,11 +457,11 @@ function Test-SQLDscParameterState if (($_ -ne 'Verbose')) { if (($CurrentValues.ContainsKey($_) -eq $false) ` - -or ($CurrentValues.$_ -ne $DesiredValues.$_) ` - -or (($DesiredValues.GetType().Name -ne 'CimInstance' -and $DesiredValues.ContainsKey($_) -eq $true) -and ($null -ne $DesiredValues.$_ -and $DesiredValues.$_.GetType().IsArray))) + -or ($CurrentValues.$_ -ne $DesiredValues.$_) ` + -or (($DesiredValues.GetType().Name -ne 'CimInstance' -and $DesiredValues.ContainsKey($_) -eq $true) -and ($null -ne $DesiredValues.$_ -and $DesiredValues.$_.GetType().IsArray))) { if ($DesiredValues.GetType().Name -eq 'HashTable' -or ` - $DesiredValues.GetType().Name -eq 'PSBoundParametersDictionary') + $DesiredValues.GetType().Name -eq 'PSBoundParametersDictionary') { $checkDesiredValue = $DesiredValues.ContainsKey($_) } @@ -630,7 +485,7 @@ function Test-SQLDscParameterState if ($desiredType.IsArray -eq $true) { if (($CurrentValues.ContainsKey($fieldName) -eq $false) ` - -or ($null -eq $CurrentValues.$fieldName)) + -or ($null -eq $CurrentValues.$fieldName)) { Write-Verbose -Message ($script:localizedData.PropertyValidationError -f $fieldName) -Verbose @@ -639,7 +494,7 @@ function Test-SQLDscParameterState else { $arrayCompare = Compare-Object -ReferenceObject $CurrentValues.$fieldName ` - -DifferenceObject $DesiredValues.$fieldName + -DifferenceObject $DesiredValues.$fieldName if ($null -ne $arrayCompare) { Write-Verbose -Message ($script:localizedData.PropertiesDoesNotMatch -f $fieldName) -Verbose @@ -659,10 +514,10 @@ function Test-SQLDscParameterState 'String' { if (-not [System.String]::IsNullOrEmpty($CurrentValues.$fieldName) -or ` - -not [System.String]::IsNullOrEmpty($DesiredValues.$fieldName)) + -not [System.String]::IsNullOrEmpty($DesiredValues.$fieldName)) { Write-Verbose -Message ($script:localizedData.ValueOfTypeDoesNotMatch ` - -f $desiredType.Name, $fieldName, $($CurrentValues.$fieldName), $($DesiredValues.$fieldName)) -Verbose + -f $desiredType.Name, $fieldName, $($CurrentValues.$fieldName), $($DesiredValues.$fieldName)) -Verbose $returnValue = $false } @@ -671,10 +526,10 @@ function Test-SQLDscParameterState 'Int32' { if (-not ($DesiredValues.$fieldName -eq 0) -or ` - -not ($null -eq $CurrentValues.$fieldName)) + -not ($null -eq $CurrentValues.$fieldName)) { Write-Verbose -Message ($script:localizedData.ValueOfTypeDoesNotMatch ` - -f $desiredType.Name, $fieldName, $($CurrentValues.$fieldName), $($DesiredValues.$fieldName)) -Verbose + -f $desiredType.Name, $fieldName, $($CurrentValues.$fieldName), $($DesiredValues.$fieldName)) -Verbose $returnValue = $false } @@ -683,10 +538,10 @@ function Test-SQLDscParameterState { $_ -eq 'Int16' -or $_ -eq 'UInt16'} { if (-not ($DesiredValues.$fieldName -eq 0) -or ` - -not ($null -eq $CurrentValues.$fieldName)) + -not ($null -eq $CurrentValues.$fieldName)) { Write-Verbose -Message ($script:localizedData.ValueOfTypeDoesNotMatch ` - -f $desiredType.Name, $fieldName, $($CurrentValues.$fieldName), $($DesiredValues.$fieldName)) -Verbose + -f $desiredType.Name, $fieldName, $($CurrentValues.$fieldName), $($DesiredValues.$fieldName)) -Verbose $returnValue = $false } @@ -695,7 +550,7 @@ function Test-SQLDscParameterState default { Write-Warning -Message ($script:localizedData.UnableToCompareProperty ` - -f $fieldName, $desiredType.Name) + -f $fieldName, $desiredType.Name) $returnValue = $false } @@ -712,17 +567,53 @@ function Test-SQLDscParameterState <# .SYNOPSIS Imports the module SQLPS in a standardized way. + + .PARAMETER Force + Forces the removal of the previous SQL module, to load the same or newer + version fresh. + This is meant to make sure the newest version is used, with the latest + assemblies. + #> function Import-SQLPSModule { [CmdletBinding()] param ( + [Parameter()] + [Switch] + $Force ) - $module = (Get-Module -FullyQualifiedName 'SqlServer' -ListAvailable).Name - if ($module) + if ($Force.IsPresent) { + Write-Verbose -Message $script:localizedData.ModuleForceRemoval -Verbose + Remove-Module -Name @('SqlServer','SQLPS','SQLASCmdlets') -Force -ErrorAction SilentlyContinue + } + + <# + Check if either of the modules are already loaded into the session. + Prefer to use the first one (in order found). + NOTE: There should actually only be either SqlServer or SQLPS loaded, + otherwise there can be problems with wrong assemblies being loaded. + #> + $loadedModuleName = (Get-Module -Name @('SqlServer', 'SQLPS') | Select-Object -First 1).Name + if ($loadedModuleName) + { + Write-Verbose -Message ($script:localizedData.PowerShellModuleAlreadyImported -f $loadedModuleName) -Verbose + return + } + + $availableModuleName = $null + + # Get the newest SqlServer module if more than one exist + $availableModule = Get-Module -FullyQualifiedName 'SqlServer' -ListAvailable | + Sort-Object -Property 'Version' -Descending | + Select-Object -First 1 -Property Name, Path, Version + + if ($availableModule) + { + $availableModuleName = $availableModule.Name Write-Verbose -Message ($script:localizedData.PreferredModuleFound) -Verbose } else @@ -737,29 +628,45 @@ function Import-SQLPSModule #> $env:PSModulePath = [System.Environment]::GetEnvironmentVariable('PSModulePath', 'Machine') - $module = (Get-Module -FullyQualifiedName 'SQLPS' -ListAvailable).Name + <# + Get the newest SQLPS module if more than one exist. + #> + $availableModule = Get-Module -FullyQualifiedName 'SQLPS' -ListAvailable | + Select-Object -Property Name, Path, @{ + Name = 'Version' + Expression = { + # Parse the build version number '120', '130' from the Path. + (Select-String -InputObject $_.Path -Pattern '\\([0-9]{3})\\' -List).Matches.Groups[1].Value + } + } | + Sort-Object -Property 'Version' -Descending | + Select-Object -First 1 + + if ($availableModule) + { + # This sets $availableModuleName to the Path of the module to be loaded. + $availableModuleName = $availableModule.Path + } } - if ($module) + if ($availableModuleName) { try { Write-Debug -Message ($script:localizedData.DebugMessagePushingLocation) Push-Location - Write-Verbose -Message ($script:localizedData.ImportingPowerShellModule -f $module) -Verbose - <# SQLPS has unapproved verbs, disable checking to ignore Warnings. Suppressing verbose so all cmdlet is not listed. #> - Import-Module -Name $module -DisableNameChecking -Verbose:$False -ErrorAction Stop + Import-Module -Name $availableModuleName -DisableNameChecking -Verbose:$False -Force:$Force -ErrorAction Stop - Write-Debug -Message ($script:localizedData.DebugMessageImportedPowerShellModule -f $module) + Write-Verbose -Message ($script:localizedData.ImportedPowerShellModule -f $availableModule.Name, $availableModule.Version, $availableModule.Path) -Verbose } catch { - $errorMessage = $script:localizedData.FailedToImportPowerShellSqlModule -f $module + $errorMessage = $script:localizedData.FailedToImportPowerShellSqlModule -f $availableModuleName New-InvalidOperationException -Message $errorMessage -ErrorRecord $_ } finally @@ -777,25 +684,42 @@ function Import-SQLPSModule <# .SYNOPSIS - Restarts a SQL Server instance and associated services + Restarts a SQL Server instance and associated services .PARAMETER SQLServer - Hostname of the SQL Server to be configured + Hostname of the SQL Server to be configured .PARAMETER SQLInstanceName - Name of the SQL instance to be configured. Default is 'MSSQLSERVER' + Name of the SQL instance to be configured. Default is 'MSSQLSERVER' .PARAMETER Timeout - Timeout value for restarting the SQL services. The default value is 120 seconds. + Timeout value for restarting the SQL services. The default value is 120 seconds. + + .PARAMETER SkipClusterCheck + If cluster check should be skipped. If this is present no connection + is made to the instance to check if the instance is on a cluster. + + This need to be used for some resource, for example for the SqlServerNetwork + resource when it's used to enable a disable protocol. + + .PARAMETER SkipWaitForOnline + If this is present no connection is made to the instance to check if the + instance is online. + + This need to be used for some resource, for example for the SqlServerNetwork + resource when it's used to disable protocol. .EXAMPLE - Restart-SqlService -SQLServer localhost + Restart-SqlService -SQLServer localhost .EXAMPLE - Restart-SqlService -SQLServer localhost -SQLInstanceName 'NamedInstance' + Restart-SqlService -SQLServer localhost -SQLInstanceName 'NamedInstance' .EXAMPLE - Restart-SqlService -SQLServer CLU01 -Timeout 300 + Restart-SqlService -SQLServer localhost -SQLInstanceName 'NamedInstance' -SkipClusterCheck -SkipWaitForOnline + + .EXAMPLE + Restart-SqlService -SQLServer CLU01 -Timeout 300 #> function Restart-SqlService { @@ -812,45 +736,76 @@ function Restart-SqlService [Parameter()] [System.UInt32] - $Timeout = 120 - ) + $Timeout = 120, - ## Connect to the instance - $serverObject = Connect-SQL -SQLServer $SQLServer -SQLInstanceName $SQLInstanceName + [Parameter()] + [Switch] + $SkipClusterCheck, + + [Parameter()] + [Switch] + $SkipWaitForOnline + ) - if ($serverObject.IsClustered) + if (-not $SkipClusterCheck.IsPresent) { - # Get the cluster resources - Write-Verbose -Message ($script:localizedData.GetSqlServerClusterResources) -Verbose - $sqlService = Get-CimInstance -Namespace root/MSCluster -ClassName MSCluster_Resource -Filter "Type = 'SQL Server'" | - Where-Object -FilterScript { $_.PrivateProperties.InstanceName -eq $serverObject.ServiceName } + ## Connect to the instance + $serverObject = Connect-SQL -SQLServer $SQLServer -SQLInstanceName $SQLInstanceName + + if ($serverObject.IsClustered) + { + # Get the cluster resources + Write-Verbose -Message ($script:localizedData.GetSqlServerClusterResources) -Verbose + $sqlService = Get-CimInstance -Namespace root/MSCluster -ClassName MSCluster_Resource -Filter "Type = 'SQL Server'" | + Where-Object -FilterScript { $_.PrivateProperties.InstanceName -eq $serverObject.ServiceName } - Write-Verbose -Message ($script:localizedData.GetSqlAgentClusterResource) -Verbose - $agentService = $sqlService | Get-CimAssociatedInstance -ResultClassName MSCluster_Resource | - Where-Object -FilterScript { ($_.Type -eq 'SQL Server Agent') -and ($_.State -eq 2) } + Write-Verbose -Message ($script:localizedData.GetSqlAgentClusterResource) -Verbose + $agentService = $sqlService | Get-CimAssociatedInstance -ResultClassName MSCluster_Resource | + Where-Object -FilterScript { ($_.Type -eq 'SQL Server Agent') -and ($_.State -eq 2) } - # Build a listing of resources being acted upon - $resourceNames = @($sqlService.Name, ($agentService | Select-Object -ExpandProperty Name)) -join "," + # Build a listing of resources being acted upon + $resourceNames = @($sqlService.Name, ($agentService | Select-Object -ExpandProperty Name)) -join "," - # Stop the SQL Server and dependent resources - Write-Verbose -Message ($script:localizedData.BringClusterResourcesOffline -f $resourceNames) -Verbose - $sqlService | Invoke-CimMethod -MethodName TakeOffline -Arguments @{ Timeout = $Timeout } + # Stop the SQL Server and dependent resources + Write-Verbose -Message ($script:localizedData.BringClusterResourcesOffline -f $resourceNames) -Verbose + $sqlService | Invoke-CimMethod -MethodName TakeOffline -Arguments @{ Timeout = $Timeout } - # Start the SQL server resource - Write-Verbose -Message ($script:localizedData.BringSqlServerClusterResourcesOnline) -Verbose - $sqlService | Invoke-CimMethod -MethodName BringOnline -Arguments @{ Timeout = $Timeout } + # Start the SQL server resource + Write-Verbose -Message ($script:localizedData.BringSqlServerClusterResourcesOnline) -Verbose + $sqlService | Invoke-CimMethod -MethodName BringOnline -Arguments @{ Timeout = $Timeout } - # Start the SQL Agent resource - if ($agentService) + # Start the SQL Agent resource + if ($agentService) + { + Write-Verbose -Message ($script:localizedData.BringSqlServerAgentClusterResourcesOnline) -Verbose + $agentService | Invoke-CimMethod -MethodName BringOnline -Arguments @{ Timeout = $Timeout } + } + } + else { - Write-Verbose -Message ($script:localizedData.BringSqlServerAgentClusterResourcesOnline) -Verbose - $agentService | Invoke-CimMethod -MethodName BringOnline -Arguments @{ Timeout = $Timeout } + # Not a cluster, restart the Windows service. + $restartWindowsService = $true } } else { + # Should not check if a cluster, assume that a Windows service should be restarted. + $restartWindowsService = $true + } + + if ($restartWindowsService) + { + if ($SQLInstanceName -eq 'MSSQLSERVER') + { + $serviceName = 'MSSQLSERVER' + } + else + { + $serviceName = 'MSSQL${0}' -f $SQLInstanceName + } + Write-Verbose -Message ($script:localizedData.GetServiceInformation -f 'SQL Server') -Verbose - $sqlService = Get-Service -DisplayName "SQL Server ($($serverObject.ServiceName))" + $sqlService = Get-Service -Name $serviceName <# Get all dependent services that are running. @@ -871,30 +826,33 @@ function Restart-SqlService Write-Verbose -Message ($script:localizedData.WaitingInstanceTimeout -f $SQLServer, $SQLInstanceName, $Timeout) -Verbose - $connectTimer = [System.Diagnostics.StopWatch]::StartNew() - - do + if (-not $SkipWaitForOnline.IsPresent) { - # This call, if it fails, will take between ~9-10 seconds to return. - $testConnectionServerObject = Connect-SQL -SQLServer $SQLServer -SQLInstanceName $SQLInstanceName -ErrorAction SilentlyContinue - if ($testConnectionServerObject -and $testConnectionServerObject.Status -ne 'Online') - { - # Waiting 2 seconds to not hammer the SQL Server instance. - Start-Sleep -Seconds 2 - } - else + $connectTimer = [System.Diagnostics.StopWatch]::StartNew() + + do { - break - } - } until ($connectTimer.Elapsed.Seconds -ge $Timeout) + # This call, if it fails, will take between ~9-10 seconds to return. + $testConnectionServerObject = Connect-SQL -SQLServer $SQLServer -SQLInstanceName $SQLInstanceName -ErrorAction SilentlyContinue + if ($testConnectionServerObject -and $testConnectionServerObject.Status -ne 'Online') + { + # Waiting 2 seconds to not hammer the SQL Server instance. + Start-Sleep -Seconds 2 + } + else + { + break + } + } until ($connectTimer.Elapsed.Seconds -ge $Timeout) - $connectTimer.Stop() + $connectTimer.Stop() - # Was the timeout period reach before able to connect to the SQL Server instance? - if (-not $testConnectionServerObject -or $testConnectionServerObject.Status -ne 'Online') - { - $errorMessage = $script:localizedData.FailedToConnectToInstanceTimeout -f $SQLServer, $SQLInstanceName, $Timeout - New-InvalidOperationException -Message $errorMessage + # Was the timeout period reach before able to connect to the SQL Server instance? + if (-not $testConnectionServerObject -or $testConnectionServerObject.Status -ne 'Online') + { + $errorMessage = $script:localizedData.FailedToConnectToInstanceTimeout -f $SQLServer, $SQLInstanceName, $Timeout + New-InvalidOperationException -Message $errorMessage + } } } @@ -1106,10 +1064,10 @@ function Test-LoginEffectivePermissions $permissionsPresent = $false $invokeQueryParameters = @{ - SQLServer = $SQLServer + SQLServer = $SQLServer SQLInstanceName = $SQLInstanceName - Database = 'master' - WithResults = $true + Database = 'master' + WithResults = $true } $queryToGetEffectivePermissionsForLogin = " @@ -1188,10 +1146,10 @@ function Test-AvailabilityReplicaSeedingModeAutomatic if ( $serverObject.Version -ge 13 ) { $invokeQueryParams = @{ - SQLServer = $SQLServer + SQLServer = $SQLServer SQLInstanceName = $SQLInstanceName - Database = 'master' - WithResults = $true + Database = 'master' + WithResults = $true } $queryToGetSeedingMode = " @@ -1264,10 +1222,10 @@ function Test-ImpersonatePermissions ) $testLoginEffectivePermissionsParams = @{ - SQLServer = $ServerObject.ComputerNamePhysicalNetBIOS + SQLServer = $ServerObject.ComputerNamePhysicalNetBIOS SQLInstanceName = $ServerObject.ServiceName - LoginName = $ServerObject.ConnectionContext.TrueLogin - Permissions = @('IMPERSONATE ANY LOGIN') + LoginName = $ServerObject.ConnectionContext.TrueLogin + Permissions = @('IMPERSONATE ANY LOGIN') } $impersonatePermissionsPresent = Test-LoginEffectivePermissions @testLoginEffectivePermissionsParams @@ -1300,7 +1258,7 @@ function Split-FullSQLInstanceName $FullSQLInstanceName ) - $sqlServer,$sqlInstanceName = $FullSQLInstanceName.Split('\') + $sqlServer, $sqlInstanceName = $FullSQLInstanceName.Split('\') if ( [System.String]::IsNullOrEmpty($sqlInstanceName) ) { @@ -1308,7 +1266,7 @@ function Split-FullSQLInstanceName } return @{ - SQLServer = $sqlServer + SQLServer = $sqlServer SQLInstanceName = $sqlInstanceName } } @@ -1359,12 +1317,12 @@ function Test-ClusterPermissions { $clusterServiceName { - Write-Verbose -Message ( $script:localizedData.ClusterLoginMissingRecommendedPermissions -f $loginName,( $availabilityGroupManagementPerms -join ', ' ) ) -Verbose + Write-Verbose -Message ( $script:localizedData.ClusterLoginMissingRecommendedPermissions -f $loginName, ( $availabilityGroupManagementPerms -join ', ' ) ) -Verbose } $ntAuthoritySystemName { - Write-Verbose -Message ( $script:localizedData.ClusterLoginMissingPermissions -f $loginName,( $availabilityGroupManagementPerms -join ', ' ) ) -Verbose + Write-Verbose -Message ( $script:localizedData.ClusterLoginMissingPermissions -f $loginName, ( $availabilityGroupManagementPerms -join ', ' ) ) -Verbose } } } @@ -1379,12 +1337,12 @@ function Test-ClusterPermissions { $clusterServiceName { - Write-Verbose -Message ($script:localizedData.ClusterLoginMissingRecommendedPermissions -f $loginName,"Trying with '$ntAuthoritySystemName'.") -Verbose + Write-Verbose -Message ($script:localizedData.ClusterLoginMissingRecommendedPermissions -f $loginName, "Trying with '$ntAuthoritySystemName'.") -Verbose } $ntAuthoritySystemName { - Write-Verbose -Message ( $script:localizedData.ClusterLoginMissing -f $loginName,'' ) -Verbose + Write-Verbose -Message ( $script:localizedData.ClusterLoginMissing -f $loginName, '' ) -Verbose } } } @@ -1393,7 +1351,7 @@ function Test-ClusterPermissions # If neither 'NT SERVICE\ClusSvc' or 'NT AUTHORITY\SYSTEM' have the required permissions, throw an error. if ( -not $clusterPermissionsPresent ) { - throw ($script:localizedData.ClusterPermissionsMissing -f $sqlServer,$sqlInstanceName ) + throw ($script:localizedData.ClusterPermissionsMissing -f $sqlServer, $sqlInstanceName ) } return $clusterPermissionsPresent diff --git a/Tests/Integration/MSFT_SqlAlwaysOnService.config.ps1 b/Tests/Integration/MSFT_SqlAlwaysOnService.config.ps1 index ab2bf33e3..9a5b513cd 100644 --- a/Tests/Integration/MSFT_SqlAlwaysOnService.config.ps1 +++ b/Tests/Integration/MSFT_SqlAlwaysOnService.config.ps1 @@ -2,11 +2,13 @@ Get all adapters with static IP addresses, all of which should be ignored when creating the cluster. #> + $ignoreAdapterIpAddress = Get-NetAdapter | Get-NetIPInterface | - Where-Object -FilterScript { - $_.Dhcp -eq 'Disabled' - } | Get-NetIPAddress + Where-Object -FilterScript { + $_.AddressFamily -eq 'IPv4' ` + -and $_.Dhcp -eq 'Disabled' +} | Get-NetIPAddress $ignoreIpNetwork = @() foreach ($adapterIpAddress in $ignoreAdapterIpAddress) @@ -22,19 +24,19 @@ foreach ($adapterIpAddress in $ignoreAdapterIpAddress) $ConfigurationData = @{ AllNodes = @( @{ - NodeName = 'localhost' - ComputerName = $env:COMPUTERNAME - InstanceName = 'DSCSQL2016' - RestartTimeout = 120 + NodeName = 'localhost' + ComputerName = $env:COMPUTERNAME + InstanceName = 'DSCSQL2016' + RestartTimeout = 120 - LoopbackAdapterName = 'ClusterNetwork' - LoopbackAdapterIpAddress = '192.168.40.10' - LoopbackAdapterGateway = '192.168.40.254' + LoopbackAdapterName = 'ClusterNetwork' + LoopbackAdapterIpAddress = '192.168.40.10' + LoopbackAdapterGateway = '192.168.40.254' - ClusterStaticIpAddress = '192.168.40.11' - IgnoreNetwork = $ignoreIpNetwork + ClusterStaticIpAddress = '192.168.40.11' + IgnoreNetwork = $ignoreIpNetwork - PSDscAllowPlainTextPassword = $true + CertificateFile = $env:DscPublicCertificatePath } ) } @@ -72,7 +74,7 @@ Configuration MSFT_SqlAlwaysOnService_CreateDependencies_Config #> xDefaultGatewayAddress LoopbackAdapterIPv4DefaultGateway { - Address = $Node.LoopbackAdapterGateway + Address = $Node.LoopbackAdapterGateway InterfaceAlias = $Node.LoopbackAdapterName AddressFamily = 'IPv4' } @@ -153,7 +155,7 @@ Configuration MSFT_SqlAlwaysOnService_CreateDependencies_Config } } - DependsOn = @( + DependsOn = @( '[WindowsFeature]AddFeatureFailoverClustering' '[WindowsFeature]AddFeatureFailoverClusteringPowerShellModule' ) diff --git a/Tests/Integration/MSFT_SqlDatabaseDefaultLocation.config.ps1 b/Tests/Integration/MSFT_SqlDatabaseDefaultLocation.config.ps1 index f2ef4b222..9c58f5254 100644 --- a/Tests/Integration/MSFT_SqlDatabaseDefaultLocation.config.ps1 +++ b/Tests/Integration/MSFT_SqlDatabaseDefaultLocation.config.ps1 @@ -1,16 +1,16 @@ $ConfigurationData = @{ AllNodes = @( @{ - NodeName = 'localhost' - ComputerName = $env:COMPUTERNAME - InstanceName = 'DSCSQL2016' - RestartTimeout = 120 + NodeName = 'localhost' + ComputerName = $env:COMPUTERNAME + InstanceName = 'DSCSQL2016' + RestartTimeout = 120 - PSDscAllowPlainTextPassword = $true + DataFilePath = 'C:\SQLData\' + LogFilePath = 'C:\SQLLog\' + BackupFilePath = 'C:\Backups\' - DataFilePath = 'C:\SQLData\' - LogFilePath = 'C:\SQLLog\' - BackupFilePath = 'C:\Backups\' + CertificateFile = $env:DscPublicCertificatePath } ) } diff --git a/Tests/Integration/MSFT_SqlRS.config.ps1 b/Tests/Integration/MSFT_SqlRS.config.ps1 index 736339b61..590d46774 100644 --- a/Tests/Integration/MSFT_SqlRS.config.ps1 +++ b/Tests/Integration/MSFT_SqlRS.config.ps1 @@ -5,23 +5,23 @@ $mockIsoMediaDriveLetter = [char](([int][char]$mockLastDrive) + 1) $ConfigurationData = @{ AllNodes = @( @{ - NodeName = 'localhost' + NodeName = 'localhost' - InstanceName = 'DSCRS2016' - Features = 'RS' - InstallSharedDir = 'C:\Program Files\Microsoft SQL Server' - InstallSharedWOWDir = 'C:\Program Files (x86)\Microsoft SQL Server' - UpdateEnabled = 'False' - SuppressReboot = $true # Make sure we don't reboot during testing. - ForceReboot = $false + InstanceName = 'DSCRS2016' + Features = 'RS' + InstallSharedDir = 'C:\Program Files\Microsoft SQL Server' + InstallSharedWOWDir = 'C:\Program Files (x86)\Microsoft SQL Server' + UpdateEnabled = 'False' + SuppressReboot = $true # Make sure we don't reboot during testing. + ForceReboot = $false - ImagePath = "$env:TEMP\SQL2016.iso" - DriveLetter = $mockIsoMediaDriveLetter + ImagePath = "$env:TEMP\SQL2016.iso" + DriveLetter = $mockIsoMediaDriveLetter - DatabaseServerName = $env:COMPUTERNAME - DatabaseInstanceName = 'DSCSQL2016' + DatabaseServerName = $env:COMPUTERNAME + DatabaseInstanceName = 'DSCSQL2016' - PSDscAllowPlainTextPassword = $true + CertificateFile = $env:DscPublicCertificatePath } ) } @@ -197,8 +197,8 @@ Configuration MSFT_SqlRS_StopReportingServicesInstance_Config { Service ('StopReportingServicesInstance{0}' -f $Node.InstanceName) { - Name = ('ReportServer${0}' -f $Node.InstanceName) - State = 'Stopped' + Name = ('ReportServer${0}' -f $Node.InstanceName) + State = 'Stopped' } } } diff --git a/Tests/Integration/MSFT_SqlScript.config.ps1 b/Tests/Integration/MSFT_SqlScript.config.ps1 index 859fd85cc..2ce6ccdd3 100644 --- a/Tests/Integration/MSFT_SqlScript.config.ps1 +++ b/Tests/Integration/MSFT_SqlScript.config.ps1 @@ -1,23 +1,23 @@ $ConfigurationData = @{ AllNodes = @( @{ - NodeName = 'localhost' + NodeName = 'localhost' - ServerName = $env:COMPUTERNAME - InstanceName = 'DSCSQL2016' + ServerName = $env:COMPUTERNAME + InstanceName = 'DSCSQL2016' - Database1Name = 'ScriptDatabase1' - Database2Name = 'ScriptDatabase2' + Database1Name = 'ScriptDatabase1' + Database2Name = 'ScriptDatabase2' - GetSqlScriptPath = Join-Path -Path $env:SystemDrive -ChildPath ([System.IO.Path]::GetRandomFileName()) - SetSqlScriptPath = Join-Path -Path $env:SystemDrive -ChildPath ([System.IO.Path]::GetRandomFileName()) - TestSqlScriptPath = Join-Path -Path $env:SystemDrive -ChildPath ([System.IO.Path]::GetRandomFileName()) + GetSqlScriptPath = Join-Path -Path $env:SystemDrive -ChildPath ([System.IO.Path]::GetRandomFileName()) + SetSqlScriptPath = Join-Path -Path $env:SystemDrive -ChildPath ([System.IO.Path]::GetRandomFileName()) + TestSqlScriptPath = Join-Path -Path $env:SystemDrive -ChildPath ([System.IO.Path]::GetRandomFileName()) - GetSqlScript = @' + GetSqlScript = @' SELECT Name FROM sys.databases WHERE Name = '$(DatabaseName)' FOR JSON AUTO '@ - TestSqlScript = @' + TestSqlScript = @' if (select count(name) from sys.databases where name = '$(DatabaseName)') = 0 BEGIN RAISERROR ('Did not find database [$(DatabaseName)]', 16, 1) @@ -28,11 +28,11 @@ BEGIN END '@ - SetSqlScript = @' + SetSqlScript = @' CREATE DATABASE [$(DatabaseName)] '@ - PSDscAllowPlainTextPassword = $true + CertificateFile = $env:DscPublicCertificatePath } ) } diff --git a/Tests/Integration/MSFT_SqlScriptQuery.config.ps1 b/Tests/Integration/MSFT_SqlScriptQuery.config.ps1 index 6ee7cef00..ab299f4ff 100644 --- a/Tests/Integration/MSFT_SqlScriptQuery.config.ps1 +++ b/Tests/Integration/MSFT_SqlScriptQuery.config.ps1 @@ -1,17 +1,17 @@ $ConfigurationData = @{ AllNodes = @( @{ - NodeName = 'localhost' - ServerName = $env:COMPUTERNAME - InstanceName = 'DSCSQL2016' - Database1Name = 'ScriptDatabase3' - Database2Name = 'ScriptDatabase4' + NodeName = 'localhost' + ServerName = $env:COMPUTERNAME + InstanceName = 'DSCSQL2016' + Database1Name = 'ScriptDatabase3' + Database2Name = 'ScriptDatabase4' - GetQuery = @' + GetQuery = @' SELECT Name FROM sys.databases WHERE Name = '$(DatabaseName)' FOR JSON AUTO '@ - TestQuery = @' + TestQuery = @' if (select count(name) from sys.databases where name = '$(DatabaseName)') = 0 BEGIN RAISERROR ('Did not find database [$(DatabaseName)]', 16, 1) @@ -22,11 +22,11 @@ BEGIN END '@ - SetQuery = @' + SetQuery = @' CREATE DATABASE [$(DatabaseName)] '@ - PSDscAllowPlainTextPassword = $true + CertificateFile = $env:DscPublicCertificatePath } ) } @@ -78,14 +78,14 @@ Configuration MSFT_SqlScriptQuery_RunSqlScriptQueryAsSqlUser_Config SqlScriptQuery 'Integration_Test' { ServerInstance = Join-Path -Path $Node.ServerName -ChildPath $Node.InstanceName - GetQuery = $Node.GetQuery - TestQuery = $Node.TestQuery - SetQuery = $Node.SetQuery - Variable = @( + GetQuery = $Node.GetQuery + TestQuery = $Node.TestQuery + SetQuery = $Node.SetQuery + Variable = @( ('DatabaseName={0}' -f $Node.Database2Name) ) - QueryTimeout = 30 - Credential = $UserCredential + QueryTimeout = 30 + Credential = $UserCredential } } } diff --git a/Tests/Integration/MSFT_SqlServerDatabaseMail.config.ps1 b/Tests/Integration/MSFT_SqlServerDatabaseMail.config.ps1 index f3bf0c483..f3f66dee6 100644 --- a/Tests/Integration/MSFT_SqlServerDatabaseMail.config.ps1 +++ b/Tests/Integration/MSFT_SqlServerDatabaseMail.config.ps1 @@ -1,19 +1,19 @@ $ConfigurationData = @{ AllNodes = @( @{ - NodeName = 'localhost' - ServerName = $env:COMPUTERNAME - InstanceName = 'DSCSQL2016' - - PSDscAllowPlainTextPassword = $true - - MailServerName = 'mail.company.local' - AccountName = 'MyMail' - ProfileName = 'MyMailProfile' - EmailAddress = 'NoReply@company.local' - Description = 'Default mail account and profile.' - LoggingLevel = 'Normal' - TcpPort = 25 + NodeName = 'localhost' + ServerName = $env:COMPUTERNAME + InstanceName = 'DSCSQL2016' + + MailServerName = 'mail.company.local' + AccountName = 'MyMail' + ProfileName = 'MyMailProfile' + EmailAddress = 'NoReply@company.local' + Description = 'Default mail account and profile.' + LoggingLevel = 'Normal' + TcpPort = 25 + + CertificateFile = $env:DscPublicCertificatePath } ) } diff --git a/Tests/Integration/MSFT_SqlServerLogin.config.ps1 b/Tests/Integration/MSFT_SqlServerLogin.config.ps1 index 7ac6a56f7..baec74a88 100644 --- a/Tests/Integration/MSFT_SqlServerLogin.config.ps1 +++ b/Tests/Integration/MSFT_SqlServerLogin.config.ps1 @@ -1,26 +1,26 @@ $ConfigurationData = @{ AllNodes = @( @{ - NodeName = 'localhost' - ServerName = $env:COMPUTERNAME - InstanceName = 'DSCSQL2016' + NodeName = 'localhost' + ServerName = $env:COMPUTERNAME + InstanceName = 'DSCSQL2016' - PSDscAllowPlainTextPassword = $true + DscUser1Name = ('{0}\{1}' -f $env:COMPUTERNAME, 'DscUser1') + DscUser1Type = 'WindowsUser' - DscUser1Name = ('{0}\{1}' -f $env:COMPUTERNAME, 'DscUser1') - DscUser1Type = 'WindowsUser' + DscUser2Name = ('{0}\{1}' -f $env:COMPUTERNAME, 'DscUser2') + DscUser2Type = 'WindowsUser' - DscUser2Name = ('{0}\{1}' -f $env:COMPUTERNAME, 'DscUser2') - DscUser2Type = 'WindowsUser' + DscUser3Name = ('{0}\{1}' -f $env:COMPUTERNAME, 'DscUser3') + DscUser3Type = 'WindowsUser' - DscUser3Name = ('{0}\{1}' -f $env:COMPUTERNAME, 'DscUser3') - DscUser3Type = 'WindowsUser' + DscUser4Name = 'DscUser4' + DscUser4Type = 'SqlLogin' - DscUser4Name = 'DscUser4' - DscUser4Type = 'SqlLogin' + DscSqlUsers1Name = ('{0}\{1}' -f $env:COMPUTERNAME, 'DscSqlUsers1') + DscSqlUsers1Type = 'WindowsGroup' - DscSqlUsers1Name = ('{0}\{1}' -f $env:COMPUTERNAME, 'DscSqlUsers1') - DscSqlUsers1Type = 'WindowsGroup' + CertificateFile = $env:DscPublicCertificatePath } ) } diff --git a/Tests/Integration/MSFT_SqlServerNetwork.Integration.Tests.ps1 b/Tests/Integration/MSFT_SqlServerNetwork.Integration.Tests.ps1 new file mode 100644 index 000000000..26bab827b --- /dev/null +++ b/Tests/Integration/MSFT_SqlServerNetwork.Integration.Tests.ps1 @@ -0,0 +1,151 @@ +# This is used to make sure the integration test run in the correct order. +[Microsoft.DscResourceKit.IntegrationTest(OrderNumber = 2)] +param() + +$script:DSCModuleName = 'SqlServerDsc' +$script:DSCResourceFriendlyName = 'SqlServerNetwork' +$script:DSCResourceName = "MSFT_$($script:DSCResourceFriendlyName)" + +if (-not $env:APPVEYOR -eq $true) +{ + Write-Warning -Message ('Integration test for {0} will be skipped unless $env:APPVEYOR equals $true' -f $script:DSCResourceName) + return +} + +#region HEADER +# Integration Test Template Version: 1.1.2 +[System.String] $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 Integration + +#endregion + +$mockSqlInstallAccountPassword = ConvertTo-SecureString -String 'P@ssw0rd1' -AsPlainText -Force +$mockSqlInstallAccountUserName = "$env:COMPUTERNAME\SqlInstall" +$mockSqlInstallCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $mockSqlInstallAccountUserName, $mockSqlInstallAccountPassword + +try +{ + $configFile = Join-Path -Path $PSScriptRoot -ChildPath "$($script:DSCResourceName).config.ps1" + . $configFile + + $mockProtocolName = $ConfigurationData.AllNodes.ProtocolName + $mockEnabled = $ConfigurationData.AllNodes.Enabled + $mockDisabled = $ConfigurationData.AllNodes.Disabled + $mockTcpDynamicPort = $ConfigurationData.AllNodes.TcpDynamicPort + + Describe "$($script:DSCResourceName)_Integration" { + BeforeAll { + $resourceId = "[$($script:DSCResourceFriendlyName)]Integration_Test" + } + + $configurationName = "$($script:DSCResourceName)_SetDisabled_Config" + + Context ('When using configuration {0}' -f $configurationName) { + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + SqlInstallCredential = $mockSqlInstallCredential + OutputPath = $TestDrive + # The variable $ConfigurationData was dot-sourced above. + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + + It 'Should be able to call Get-DscConfiguration without throwing' { + { + $script:currentConfiguration = Get-DscConfiguration -Verbose -ErrorAction Stop + } | Should -Not -Throw + } + + It 'Should have set the resource and all the parameters should match' { + $resourceCurrentState = $script:currentConfiguration | Where-Object -FilterScript { + $_.ConfigurationName -eq $configurationName + } | Where-Object -FilterScript { + $_.ResourceId -eq $resourceId + } + + $resourceCurrentState.IsEnabled | Should -Be $mockDisabled + $resourceCurrentState.ProtocolName | Should -Be $mockProtocolName + $resourceCurrentState.TcpDynamicPort | Should -Be $mockTcpDynamicPort + } + } + + $configurationName = "$($script:DSCResourceName)_SetEnabled_Config" + + Context ('When using configuration {0}' -f $configurationName) { + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + SqlInstallCredential = $mockSqlInstallCredential + OutputPath = $TestDrive + # The variable $ConfigurationData was dot-sourced above. + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + + It 'Should be able to call Get-DscConfiguration without throwing' { + { + $script:currentConfiguration = Get-DscConfiguration -Verbose -ErrorAction Stop + } | Should -Not -Throw + } + + It 'Should have set the resource and all the parameters should match' { + $resourceCurrentState = $script:currentConfiguration | Where-Object -FilterScript { + $_.ConfigurationName -eq $configurationName + } | Where-Object -FilterScript { + $_.ResourceId -eq $resourceId + } + + $resourceCurrentState.IsEnabled | Should -Be $mockEnabled + $resourceCurrentState.ProtocolName | Should -Be $mockProtocolName + $resourceCurrentState.TcpDynamicPort | Should -Be $mockTcpDynamicPort + } + } + } +} +finally +{ + #region FOOTER + + Restore-TestEnvironment -TestEnvironment $TestEnvironment + + #endregion +} diff --git a/Tests/Integration/MSFT_SqlServerNetwork.config.ps1 b/Tests/Integration/MSFT_SqlServerNetwork.config.ps1 new file mode 100644 index 000000000..2b0dcc9d6 --- /dev/null +++ b/Tests/Integration/MSFT_SqlServerNetwork.config.ps1 @@ -0,0 +1,70 @@ +$ConfigurationData = @{ + AllNodes = @( + @{ + NodeName = 'localhost' + ServerName = $env:COMPUTERNAME + InstanceName = 'DSCSQL2016' + + ProtocolName = 'Tcp' + Enabled = $true + Disabled = $false + TcpDynamicPort = $true + RestartService = $true + + CertificateFile = $env:DscPublicCertificatePath + } + ) +} + +Configuration MSFT_SqlServerNetwork_SetDisabled_Config +{ + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.Management.Automation.PSCredential] + $SqlInstallCredential + ) + + Import-DscResource -ModuleName 'SqlServerDsc' + + node localhost { + SqlServerNetwork 'Integration_Test' + { + ServerName = $Node.ServerName + InstanceName = $Node.InstanceName + ProtocolName = $Node.ProtocolName + IsEnabled = $Node.Disabled + TcpDynamicPort = $Node.TcpDynamicPort + + PsDscRunAsCredential = $SqlInstallCredential + } + } +} + +Configuration MSFT_SqlServerNetwork_SetEnabled_Config +{ + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.Management.Automation.PSCredential] + $SqlInstallCredential + ) + + Import-DscResource -ModuleName 'SqlServerDsc' + + node localhost { + SqlServerNetwork 'Integration_Test' + { + ServerName = $Node.ServerName + InstanceName = $Node.InstanceName + ProtocolName = $Node.ProtocolName + IsEnabled = $Node.Enabled + TcpDynamicPort = $Node.TcpDynamicPort + + PsDscRunAsCredential = $SqlInstallCredential + } + } +} + diff --git a/Tests/Integration/MSFT_SqlServerRole.config.ps1 b/Tests/Integration/MSFT_SqlServerRole.config.ps1 index 4c2ac6194..9fe712410 100644 --- a/Tests/Integration/MSFT_SqlServerRole.config.ps1 +++ b/Tests/Integration/MSFT_SqlServerRole.config.ps1 @@ -1,19 +1,19 @@ $ConfigurationData = @{ AllNodes = @( @{ - NodeName = 'localhost' - ServerName = $env:COMPUTERNAME - InstanceName = 'DSCSQL2016' + NodeName = 'localhost' + ServerName = $env:COMPUTERNAME + InstanceName = 'DSCSQL2016' - PSDscAllowPlainTextPassword = $true + Role1Name = 'DscServerRole1' + Role2Name = 'DscServerRole2' + Role3Name = 'DscServerRole3' - Role1Name = 'DscServerRole1' - Role2Name = 'DscServerRole2' - Role3Name = 'DscServerRole3' + User1Name = '{0}\{1}' -f $env:COMPUTERNAME, 'DscUser1' + User2Name = '{0}\{1}' -f $env:COMPUTERNAME, 'DscUser2' + User4Name = 'DscUser4' - User1Name = '{0}\{1}' -f $env:COMPUTERNAME, 'DscUser1' - User2Name = '{0}\{1}' -f $env:COMPUTERNAME, 'DscUser2' - User4Name = 'DscUser4' + CertificateFile = $env:DscPublicCertificatePath } ) } diff --git a/Tests/Integration/MSFT_SqlServiceAccount.config.ps1 b/Tests/Integration/MSFT_SqlServiceAccount.config.ps1 index 0f475cc02..8bb670ef3 100644 --- a/Tests/Integration/MSFT_SqlServiceAccount.config.ps1 +++ b/Tests/Integration/MSFT_SqlServiceAccount.config.ps1 @@ -1,15 +1,15 @@ $ConfigurationData = @{ AllNodes = @( @{ - NodeName = 'localhost' - ServerName = $env:COMPUTERNAME - DefaultInstanceName = 'MSSQLSERVER' - NamedInstanceName = 'DSCSQL2016' + NodeName = 'localhost' + ServerName = $env:COMPUTERNAME + DefaultInstanceName = 'MSSQLSERVER' + NamedInstanceName = 'DSCSQL2016' - ServiceTypeDatabaseEngine = 'DatabaseEngine' - ServiceTypeSqlServerAgent = 'SqlServerAgent' + ServiceTypeDatabaseEngine = 'DatabaseEngine' + ServiceTypeSqlServerAgent = 'SqlServerAgent' - PSDscAllowPlainTextPassword = $true + CertificateFile = $env:DscPublicCertificatePath } ) } @@ -30,14 +30,14 @@ Configuration MSFT_SqlServiceAccount_CreateDependencies_Config { Service ('StartSqlServerDefaultInstance{0}' -f $Node.DefaultInstanceName) { - Name = $Node.DefaultInstanceName - State = 'Running' + Name = $Node.DefaultInstanceName + State = 'Running' } Service ('StartSqlServerAgentForInstance{0}' -f $Node.DefaultInstanceName) { - Name = 'SQLSERVERAGENT' - State = 'Running' + Name = 'SQLSERVERAGENT' + State = 'Running' } } } @@ -198,14 +198,14 @@ Configuration MSFT_SqlServiceAccount_StopSqlServerDefaultInstance_Config { Service ('StartSqlServerAgentForInstance{0}' -f $Node.DefaultInstanceName) { - Name = 'SQLSERVERAGENT' - State = 'Stopped' + Name = 'SQLSERVERAGENT' + State = 'Stopped' } Service ('StartSqlServerDefaultInstance{0}' -f $Node.DefaultInstanceName) { - Name = $Node.DefaultInstanceName - State = 'Stopped' + Name = $Node.DefaultInstanceName + State = 'Stopped' } } } diff --git a/Tests/Integration/MSFT_SqlSetup.config.ps1 b/Tests/Integration/MSFT_SqlSetup.config.ps1 index 8c19e962e..fd42d9b29 100644 --- a/Tests/Integration/MSFT_SqlSetup.config.ps1 +++ b/Tests/Integration/MSFT_SqlSetup.config.ps1 @@ -45,12 +45,7 @@ $ConfigurationData = @{ ImagePath = "$env:TEMP\SQL2016.iso" DriveLetter = $mockIsoMediaDriveLetter - <# - We must compile the configuration using plain text since the - common integration test framework does not use certificates. - This should not be used in production. - #> - PSDscAllowPlainTextPassword = $true + CertificateFile = $env:DscPublicCertificatePath } ) } @@ -247,8 +242,8 @@ Configuration MSFT_SqlSetup_StopMultiAnalysisServicesInstance_Config Service ('StopMultiAnalysisServicesInstance{0}' -f $Node.DatabaseEngineNamedInstanceName) { - Name = ('MSOLAP${0}' -f $Node.DatabaseEngineNamedInstanceName) - State = 'Stopped' + Name = ('MSOLAP${0}' -f $Node.DatabaseEngineNamedInstanceName) + State = 'Stopped' } } } @@ -312,15 +307,15 @@ Configuration MSFT_SqlSetup_StopSqlServerDefaultInstance_Config { Service ('StopSqlServerAgentForInstance{0}' -f $Node.DatabaseEngineDefaultInstanceName) { - Name = 'SQLSERVERAGENT' - State = 'Stopped' + Name = 'SQLSERVERAGENT' + State = 'Stopped' } Service ('StopSqlServerInstance{0}' -f $Node.DatabaseEngineDefaultInstanceName) { - Name = $Node.DatabaseEngineDefaultInstanceName - State = 'Stopped' + Name = $Node.DatabaseEngineDefaultInstanceName + State = 'Stopped' } } } @@ -380,8 +375,8 @@ Configuration MSFT_SqlSetup_StopTabularAnalysisServices_Config { Service ('StopTabularAnalysisServicesInstance{0}' -f $Node.AnalysisServicesTabularInstanceName) { - Name = ('MSOLAP${0}' -f $Node.DatabaseEngineNamedInstanceName) - State = 'Stopped' + Name = ('MSOLAP${0}' -f $Node.DatabaseEngineNamedInstanceName) + State = 'Stopped' } } } diff --git a/Tests/Unit/MSFT_SqlAG.Tests.ps1 b/Tests/Unit/MSFT_SqlAG.Tests.ps1 index 11e3b17aa..75b305637 100644 --- a/Tests/Unit/MSFT_SqlAG.Tests.ps1 +++ b/Tests/Unit/MSFT_SqlAG.Tests.ps1 @@ -774,7 +774,6 @@ try BeforeAll { Mock -CommandName Connect-SQL -MockWith $mockConnectSql -Verifiable Mock -CommandName Get-PrimaryReplicaServerObject -MockWith $mockConnectSql -Verifiable - Mock -CommandName Import-SQLPSModule -Verifiable Mock -CommandName New-SqlAvailabilityGroup $mockNewSqlAvailabilityGroup -Verifiable Mock -CommandName New-SqlAvailabilityReplica -MockWith $mockNewSqlAvailabilityGroupReplica -Verifiable Mock -CommandName New-TerminatingError -MockWith { @@ -822,7 +821,6 @@ try Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Times 0 -Exactly - Assert-MockCalled -CommandName Import-SQLPSModule -Scope It -Times 1 -Exactly Assert-MockCalled -CommandName New-SqlAvailabilityGroup -Scope It -Times @{Absent=0;Present=1}.$Ensure -Exactly Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times @{Absent=0;Present=1}.$Ensure -Exactly Assert-MockCalled -CommandName New-TerminatingError -Scope It -Times 0 -Exactly @@ -877,7 +875,6 @@ try Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Times 0 -Exactly - Assert-MockCalled -CommandName Import-SQLPSModule -Scope It -Times 1 -Exactly Assert-MockCalled -CommandName New-SqlAvailabilityGroup -Scope It -Times $assertCreateAvailabilityGroup -Exactly Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 1 -Exactly Assert-MockCalled -CommandName New-TerminatingError -Scope It -Times 1 -Exactly @@ -934,7 +931,6 @@ try Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Import-SQLPSModule -Scope It -Times 1 -Exactly Assert-MockCalled -CommandName New-SqlAvailabilityGroup -Scope It -Times 0 -Exactly Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly Assert-MockCalled -CommandName New-TerminatingError -Scope It -Times 0 -Exactly @@ -975,7 +971,6 @@ try Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Times 0 -Exactly - Assert-MockCalled -CommandName Import-SQLPSModule -Scope It -Times 1 -Exactly Assert-MockCalled -CommandName New-SqlAvailabilityGroup -Scope It -Times 0 -Exactly Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly Assert-MockCalled -CommandName New-TerminatingError -Scope It -Times 0 -Exactly @@ -1030,7 +1025,6 @@ try Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Times 0 -Exactly - Assert-MockCalled -CommandName Import-SQLPSModule -Scope It -Times 1 -Exactly Assert-MockCalled -CommandName New-SqlAvailabilityGroup -Scope It -Times 0 -Exactly Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly Assert-MockCalled -CommandName New-TerminatingError -Scope It -Times 1 -Exactly @@ -1080,7 +1074,6 @@ try Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Times 0 -Exactly - Assert-MockCalled -CommandName Import-SQLPSModule -Scope It -Times 1 -Exactly Assert-MockCalled -CommandName New-SqlAvailabilityGroup -Scope It -Times 0 -Exactly Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly Assert-MockCalled -CommandName New-TerminatingError -Scope It -Times 1 -Exactly @@ -1130,7 +1123,6 @@ try Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Times 0 -Exactly - Assert-MockCalled -CommandName Import-SQLPSModule -Scope It -Times 1 -Exactly Assert-MockCalled -CommandName New-SqlAvailabilityGroup -Scope It -Times 0 -Exactly Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly Assert-MockCalled -CommandName New-TerminatingError -Scope It -Times 1 -Exactly @@ -1188,7 +1180,6 @@ try Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Import-SQLPSModule -Scope It -Times 1 -Exactly Assert-MockCalled -CommandName New-SqlAvailabilityGroup -Scope It -Times 0 -Exactly Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly Assert-MockCalled -CommandName New-TerminatingError -Scope It -Times 0 -Exactly diff --git a/Tests/Unit/MSFT_SqlServerNetwork.Tests.ps1 b/Tests/Unit/MSFT_SqlServerNetwork.Tests.ps1 index 21efdc997..65f459662 100644 --- a/Tests/Unit/MSFT_SqlServerNetwork.Tests.ps1 +++ b/Tests/Unit/MSFT_SqlServerNetwork.Tests.ps1 @@ -90,10 +90,6 @@ try $TypeName -eq 'Microsoft.SqlServer.Management.Smo.Wmi.ManagedComputer' } - $mockFunction_RegisterSqlWmiManagement = { - return [System.AppDomain]::CreateDomain('DummyTestApplicationDomain') - } - $mockDefaultParameters = @{ InstanceName = $mockInstanceName ProtocolName = $mockTcpProtocolName @@ -103,10 +99,7 @@ try BeforeEach { $testParameters = $mockDefaultParameters.Clone() - Mock -CommandName Register-SqlWmiManagement ` - -MockWith $mockFunction_RegisterSqlWmiManagement ` - -Verifiable - + Mock -CommandName Import-SQLPSModule Mock -CommandName New-Object ` -MockWith $mockFunction_NewObject_ManagedComputer ` -ParameterFilter $mockFunction_NewObject_ManagedComputer_ParameterFilter -Verifiable @@ -126,7 +119,6 @@ try $result.TcpDynamicPort | Should -Be $false $result.TcpPort | Should -Be $mockDynamicValue_TcpPort - Assert-MockCalled -CommandName Register-SqlWmiManagement -Exactly -Times 1 -Scope It Assert-MockCalled -CommandName New-Object -Exactly -Times 1 -Scope It ` -ParameterFilter $mockFunction_NewObject_ManagedComputer_ParameterFilter } @@ -145,10 +137,7 @@ try BeforeEach { $testParameters = $mockDefaultParameters.Clone() - Mock -CommandName Register-SqlWmiManagement ` - -MockWith $mockFunction_RegisterSqlWmiManagement ` - -Verifiable - + Mock -CommandName Import-SQLPSModule Mock -CommandName New-Object ` -MockWith $mockFunction_NewObject_ManagedComputer ` -ParameterFilter $mockFunction_NewObject_ManagedComputer_ParameterFilter -Verifiable @@ -200,6 +189,25 @@ try } } + Context 'When ProtocolName is not in desired state' { + BeforeEach { + $testParameters += @{ + IsEnabled = $false + TcpDynamicPort = $false + TcpPort = '4509' + } + + # Not supporting any other than 'TCP' yet. + $testParameters['ProtocolName'] = 'Unknown' + } + + # Skipped since no other protocol is supported yet (issue #14). + It 'Should return $false' -Skip:$true { + $result = Test-TargetResource @testParameters + $result | Should -Be $false + } + } + Context 'When current state is using static tcp port' { Context 'When TcpDynamicPort is not in desired state' { BeforeEach { @@ -314,10 +322,7 @@ try $testParameters = $mockDefaultParameters.Clone() Mock -CommandName Restart-SqlService -Verifiable - Mock -CommandName Register-SqlWmiManagement ` - -MockWith $mockFunction_RegisterSqlWmiManagement ` - -Verifiable - + Mock -CommandName Import-SQLPSModule Mock -CommandName New-Object ` -MockWith $mockFunction_NewObject_ManagedComputer ` -ParameterFilter $mockFunction_NewObject_ManagedComputer_ParameterFilter -Verifiable @@ -344,22 +349,55 @@ try } Context 'When IsEnabled is not in desired state' { - BeforeEach { - $testParameters += @{ - IsEnabled = $false - TcpDynamicPort = $false - TcpPort = '4509' - RestartService = $true + Context 'When IsEnabled should be $false' { + BeforeEach { + $testParameters += @{ + IsEnabled = $false + TcpDynamicPort = $false + TcpPort = '4509' + RestartService = $true + } + + $mockExpectedValue_IsEnabled = $false } - $mockExpectedValue_IsEnabled = $false + It 'Should call Set-TargetResource without throwing and should call Alter()' { + { Set-TargetResource @testParameters } | Should -Not -Throw + $script:WasMethodAlterCalled | Should -Be $true + + Assert-MockCalled -CommandName Restart-SqlService -Exactly -Times 1 -Scope It + } } - It 'Should call Set-TargetResource without throwing and should call Alter()' { - { Set-TargetResource @testParameters } | Should -Not -Throw - $script:WasMethodAlterCalled | Should -Be $true + Context 'When IsEnabled should be $true' { + BeforeEach { + $testParameters += @{ + IsEnabled = $true + TcpDynamicPort = $false + TcpPort = '4509' + RestartService = $true + } + + $mockExpectedValue_IsEnabled = $true + + Mock -CommandName Get-TargetResource -MockWith { + return @{ + ProtocolName = $mockTcpProtocolName + IsEnabled = $false + TcpDynamicPort = $testParameters.TcpDynamicPort + TcpPort = $testParameters.TcpPort + } + } + } + + It 'Should call Set-TargetResource without throwing and should call Alter()' { + { Set-TargetResource @testParameters } | Should -Not -Throw + $script:WasMethodAlterCalled | Should -Be $true - Assert-MockCalled -CommandName Restart-SqlService -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Restart-SqlService -ParameterFilter { + $SkipClusterCheck -eq $true + } -Exactly -Times 1 -Scope It + } } } diff --git a/Tests/Unit/MSFT_SqlSetup.Tests.ps1 b/Tests/Unit/MSFT_SqlSetup.Tests.ps1 index b3f152896..c13e57c57 100644 --- a/Tests/Unit/MSFT_SqlSetup.Tests.ps1 +++ b/Tests/Unit/MSFT_SqlSetup.Tests.ps1 @@ -1003,8 +1003,9 @@ try $testParameters = $mockDefaultParameters.Clone() } - $testProductVersion | ForEach-Object -Process { - $mockSqlMajorVersion = $_ + foreach ($version in $testProductVersion) + { + $mockSqlMajorVersion = $version $mockDefaultInstance_InstanceId = "$($mockSqlDatabaseEngineName)$($mockSqlMajorVersion).$($mockDefaultInstance_InstanceName)" @@ -2960,6 +2961,7 @@ try BeforeAll { # General mocks + Mock -CommandName Import-SQLPSModule Mock -CommandName Get-SqlMajorVersion -MockWith $mockGetSqlMajorVersion -Verifiable # Mocking SharedDirectory and SharedWowDirectory (when not previously installed) @@ -3041,7 +3043,7 @@ try Mock -CommandName Write-Warning - It 'Should warn that target nod need to restart' { + It 'Should warn that target node need to restart' { { Set-TargetResource @testParameters } | Should -Not -Throw Assert-MockCalled -CommandName Write-Warning -Exactly -Times 1 -Scope It @@ -3057,6 +3059,7 @@ try { Set-TargetResource @testParameters } | Should -Not -Throw Assert-MockCalled -CommandName Write-Warning -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Import-SQLPSModule -Exactly -Times 0 -Scope It } } } @@ -3151,6 +3154,7 @@ try Assert-MockCalled -CommandName Start-SqlSetupProcess -Exactly -Times 1 -Scope It Assert-MockCalled -CommandName Test-TargetResource -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Import-SQLPSModule -Exactly -Times 1 -Scope It } if( $mockSqlMajorVersion -in (13,14) ) @@ -3530,6 +3534,7 @@ try Assert-MockCalled -CommandName Start-SqlSetupProcess -Exactly -Times 1 -Scope It Assert-MockCalled -CommandName Test-TargetResource -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Import-SQLPSModule -Exactly -Times 1 -Scope It } if( $mockSqlMajorVersion -in (13,14) ) @@ -4701,7 +4706,7 @@ try } { Copy-ItemWithRobocopy @copyItemWithRobocopyParameter } | Should -Not -Throw - + } It 'Should finish successfully with exit code 2' { diff --git a/Tests/Unit/SqlServerDSCHelper.Tests.ps1 b/Tests/Unit/SqlServerDSCHelper.Tests.ps1 index 2b12a6ec9..b83a7286c 100644 --- a/Tests/Unit/SqlServerDSCHelper.Tests.ps1 +++ b/Tests/Unit/SqlServerDSCHelper.Tests.ps1 @@ -139,9 +139,30 @@ InModuleScope $script:moduleName { InstanceName = '' ServiceName = 'MSSQLSERVER' Status = $mockDynamicStatus + IsClustered = $false } } -Verifiable -ParameterFilter { $SQLInstanceName -eq 'MSSQLSERVER' } + Mock -CommandName Connect-SQL -MockWith { + return @{ + Name = 'NOCLUSTERCHECK' + InstanceName = 'NOCLUSTERCHECK' + ServiceName = 'NOCLUSTERCHECK' + Status = $mockDynamicStatus + IsClustered = $true + } + } -Verifiable -ParameterFilter { $SQLInstanceName -eq 'NOCLUSTERCHECK' } + + Mock -CommandName Connect-SQL -MockWith { + return @{ + Name = 'NOCONNECT' + InstanceName = 'NOCONNECT' + ServiceName = 'NOCONNECT' + Status = $mockDynamicStatus + IsClustered = $true + } + } -Verifiable -ParameterFilter { $SQLInstanceName -eq 'NOCONNECT' } + Mock -CommandName Connect-SQL -MockWith { return @{ Name = 'NOAGENT' @@ -176,7 +197,7 @@ InModuleScope $script:moduleName { } ) } - } -Verifiable -ParameterFilter { $DisplayName -eq 'SQL Server (MSSQLSERVER)' } + } -Verifiable -ParameterFilter { $Name -eq 'MSSQLSERVER' } ## SQL instance with no installed SQL Agent Service Mock -CommandName Get-Service -MockWith { @@ -185,7 +206,25 @@ InModuleScope $script:moduleName { DisplayName = 'Microsoft SQL Server (NOAGENT)' DependentServices = @() } - } -Verifiable -ParameterFilter { $DisplayName -eq 'SQL Server (NOAGENT)' } + } -Verifiable -ParameterFilter { $Name -eq 'MSSQL$NOAGENT' } + + ## SQL instance with no installed SQL Agent Service + Mock -CommandName Get-Service -MockWith { + return @{ + Name = 'MSSQL$NOCLUSTERCHECK' + DisplayName = 'Microsoft SQL Server (NOCLUSTERCHECK)' + DependentServices = @() + } + } -Verifiable -ParameterFilter { $Name -eq 'MSSQL$NOCLUSTERCHECK' } + + ## SQL instance with no installed SQL Agent Service + Mock -CommandName Get-Service -MockWith { + return @{ + Name = 'MSSQL$NOCONNECT' + DisplayName = 'Microsoft SQL Server (NOCONNECT)' + DependentServices = @() + } + } -Verifiable -ParameterFilter { $Name -eq 'MSSQL$NOCONNECT' } ## SQL instance with stopped SQL Agent Service Mock -CommandName Get-Service -MockWith { @@ -201,7 +240,7 @@ InModuleScope $script:moduleName { } ) } - } -Verifiable -ParameterFilter { $DisplayName -eq 'SQL Server (STOPPEDAGENT)' } + } -Verifiable -ParameterFilter { $Name -eq 'MSSQL$STOPPEDAGENT' } Mock -CommandName Restart-Service -Verifiable Mock -CommandName Start-Service -Verifiable @@ -220,6 +259,30 @@ InModuleScope $script:moduleName { Assert-MockCalled -CommandName Start-Service -Scope It -Exactly -Times 1 } + It 'Should restart SQL Service, and not do cluster cluster check' { + Mock -CommandName Get-CimInstance + + { Restart-SqlService -SQLServer $env:ComputerName -SQLInstanceName 'NOCLUSTERCHECK' -SkipClusterCheck } | Should -Not -Throw + + Assert-MockCalled -CommandName Connect-SQL -Scope It -Exactly -Times 1 + Assert-MockCalled -CommandName Get-Service -Scope It -Exactly -Times 1 + Assert-MockCalled -CommandName Restart-Service -Scope It -Exactly -Times 1 + Assert-MockCalled -CommandName Start-Service -Scope It -Exactly -Times 0 + Assert-MockCalled -CommandName Get-CimInstance -Scope It -Exactly -Times 0 + } + + It 'Should restart SQL Service, and not do cluster cluster check nor check online status' { + Mock -CommandName Get-CimInstance + + { Restart-SqlService -SQLServer $env:ComputerName -SQLInstanceName 'NOCONNECT' -SkipClusterCheck -SkipWaitForOnline } | Should -Not -Throw + + Assert-MockCalled -CommandName Get-Service -Scope It -Exactly -Times 1 + Assert-MockCalled -CommandName Restart-Service -Scope It -Exactly -Times 1 + Assert-MockCalled -CommandName Connect-SQL -Scope It -Exactly -Times 0 + Assert-MockCalled -CommandName Start-Service -Scope It -Exactly -Times 0 + Assert-MockCalled -CommandName Get-CimInstance -Scope It -Exactly -Times 0 + } + It 'Should restart SQL Service and not try to restart missing SQL Agent service' { { Restart-SqlService -SQLServer $env:ComputerName -SQLInstanceName 'NOAGENT' } | Should -Not -Throw @@ -662,10 +725,36 @@ InModuleScope $script:moduleName { } } - $mockGetModule = { - return New-Object -TypeName PSObject -Property @{ - Name = $mockModuleNameToImport - } + $mockGetModuleSqlServer = { + # Return an array to test so that the latest version is only imported. + return @( + New-Object -TypeName PSObject -Property @{ + Name = 'SqlServer' + Version = [Version] '1.0' + } + + New-Object -TypeName PSObject -Property @{ + Name = 'SqlServer' + Version = [Version] '2.0' + } + ) + } + + $sqlPsLatestModulePath = 'C:\Program Files (x86)\Microsoft SQL Server\130\Tools\PowerShell\Modules\SQLPS\Sqlps.ps1' + + $mockGetModuleSqlPs = { + # Return an array to test so that the latest version is only imported. + return @( + New-Object -TypeName PSObject -Property @{ + Name = 'SQLPS' + Path = 'C:\Program Files (x86)\Microsoft SQL Server\120\Tools\PowerShell\Modules\SQLPS\Sqlps.ps1' + } + + New-Object -TypeName PSObject -Property @{ + Name = 'SQLPS' + Path = $sqlPsLatestModulePath + } + ) } $mockGetModule_SqlServer_ParameterFilter = { @@ -676,7 +765,7 @@ InModuleScope $script:moduleName { $FullyQualifiedName.Name -eq 'SQLPS' -and $ListAvailable -eq $true } - Describe 'Testing Import-SQLPSModule' -Tag ImportSQLPSModule { + Describe 'Testing Import-SQLPSModule' -Tag 'ImportSQLPSModule' { BeforeEach { Mock -CommandName Push-Location -Verifiable Mock -CommandName Pop-Location -Verifiable @@ -684,12 +773,49 @@ InModuleScope $script:moduleName { Mock -CommandName New-InvalidOperationException -MockWith $mockThrowLocalizedMessage -Verifiable } - Context 'When module SqlServer exists' { - $mockModuleNameToImport = 'SqlServer' - $mockExpectedModuleNameToImport = 'SqlServer' + Context 'When module SqlServer is already loaded into the session' { + BeforeAll { + Mock -CommandName Get-Module -MockWith { + return @{ + Name = 'SqlServer' + } + } + } + + It 'Should use the already loaded module and not call Import-Module' { + { Import-SQLPSModule } | Should -Not -Throw + + Assert-MockCalled -CommandName Import-Module -Exactly -Times 0 -Scope It + } + } + + Context 'When module SQLPS is already loaded into the session' { + BeforeAll { + Mock -CommandName Get-Module -MockWith { + return @{ + Name = 'SQLPS' + } + } + } + + It 'Should use the already loaded module and not call Import-Module' { + { Import-SQLPSModule } | Should -Not -Throw + + Assert-MockCalled -CommandName Import-Module -Exactly -Times 0 -Scope It + } + } + + Context 'When module SqlServer exists, but not loaded into the session' { + BeforeAll { + Mock -CommandName Get-Module -ParameterFilter { + $PSBoundParameters.ContainsKey('Name') -eq $true + } + + $mockExpectedModuleNameToImport = 'SqlServer' + } It 'Should import the SqlServer module without throwing' { - Mock -CommandName Get-Module -MockWith $mockGetModule -ParameterFilter $mockGetModule_SqlServer_ParameterFilter -Verifiable + Mock -CommandName Get-Module -MockWith $mockGetModuleSqlServer -ParameterFilter $mockGetModule_SqlServer_ParameterFilter -Verifiable { Import-SQLPSModule } | Should -Not -Throw @@ -700,36 +826,43 @@ InModuleScope $script:moduleName { } } - Context 'When only module SQLPS exists' { - $mockModuleNameToImport = 'SQLPS' - $mockExpectedModuleNameToImport = 'SQLPS' + Context 'When only module SQLPS exists, but not loaded into the session, and using -Force' { + BeforeAll { + Mock -CommandName Remove-Module + Mock -CommandName Get-Module -ParameterFilter { + $PSBoundParameters.ContainsKey('Name') -eq $true + } + + $mockExpectedModuleNameToImport = $sqlPsLatestModulePath + } It 'Should import the SqlServer module without throwing' { - Mock -CommandName Get-Module -MockWith $mockGetModule -ParameterFilter $mockGetModule_SQLPS_ParameterFilter -Verifiable + Mock -CommandName Get-Module -MockWith $mockGetModuleSqlPs -ParameterFilter $mockGetModule_SQLPS_ParameterFilter -Verifiable Mock -CommandName Get-Module -MockWith { return $null } -ParameterFilter $mockGetModule_SqlServer_ParameterFilter -Verifiable - { Import-SQLPSModule } | Should -Not -Throw + { Import-SQLPSModule -Force } | Should -Not -Throw Assert-MockCalled -CommandName Get-Module -ParameterFilter $mockGetModule_SqlServer_ParameterFilter -Exactly -Times 1 -Scope It Assert-MockCalled -CommandName Get-Module -ParameterFilter $mockGetModule_SQLPS_ParameterFilter -Exactly -Times 1 -Scope It Assert-MockCalled -CommandName Push-Location -Exactly -Times 1 -Scope It Assert-MockCalled -CommandName Pop-Location -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Remove-Module -Exactly -Times 1 -Scope It Assert-MockCalled -CommandName Import-Module -Exactly -Times 1 -Scope It } } Context 'When neither SqlServer or SQLPS exists' { - $mockModuleNameToImport = 'UnknownModule' - $mockExpectedModuleNameToImport = 'SQLPS' + $mockExpectedModuleNameToImport = $sqlPsLatestModulePath It 'Should throw the correct error message' { Mock -CommandName Get-Module { Import-SQLPSModule } | Should -Throw $script:localizedData.PowerShellSqlModuleNotFound - Assert-MockCalled -CommandName Get-Module -Exactly -Times 2 -Scope It + Assert-MockCalled -CommandName Get-Module -ParameterFilter $mockGetModule_SqlServer_ParameterFilter -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Get-Module -ParameterFilter $mockGetModule_SQLPS_ParameterFilter -Exactly -Times 1 -Scope It Assert-MockCalled -CommandName Push-Location -Exactly -Times 0 -Scope It Assert-MockCalled -CommandName Pop-Location -Exactly -Times 0 -Scope It Assert-MockCalled -CommandName Import-Module -Exactly -Times 0 -Scope It @@ -737,12 +870,11 @@ InModuleScope $script:moduleName { } Context 'When Import-Module fails to load the module' { - $mockModuleNameToImport = 'SqlServer' $mockExpectedModuleNameToImport = 'SqlServer' It 'Should throw the correct error message' { $errorMessage = 'Mock Import-Module throwing a mocked error.' - Mock -CommandName Get-Module -MockWith $mockGetModule -ParameterFilter $mockGetModule_SqlServer_ParameterFilter -Verifiable + Mock -CommandName Get-Module -MockWith $mockGetModuleSqlServer -ParameterFilter $mockGetModule_SqlServer_ParameterFilter -Verifiable Mock -CommandName Import-Module -MockWith { throw $errorMessage } @@ -758,13 +890,12 @@ InModuleScope $script:moduleName { # This is to test the tests (so the mock throws correctly) Context 'When mock Import-Module is called with wrong module name' { - $mockModuleNameToImport = 'SqlServer' $mockExpectedModuleNameToImport = 'UnknownModule' It 'Should throw the correct error message' { - Mock -CommandName Get-Module -MockWith $mockGetModule -ParameterFilter $mockGetModule_SqlServer_ParameterFilter -Verifiable + Mock -CommandName Get-Module -MockWith $mockGetModuleSqlServer -ParameterFilter $mockGetModule_SqlServer_ParameterFilter -Verifiable - { Import-SQLPSModule } | Should -Throw ($script:localizedData.FailedToImportPowerShellSqlModule -f $mockModuleNameToImport) + { Import-SQLPSModule } | Should -Throw ($script:localizedData.FailedToImportPowerShellSqlModule -f 'SqlServer') Assert-MockCalled -CommandName Get-Module -Exactly -Times 1 -Scope It Assert-MockCalled -CommandName Push-Location -Exactly -Times 1 -Scope It @@ -852,109 +983,6 @@ InModuleScope $script:moduleName { Assert-VerifiableMock } - $mockApplicationDomainName = 'SqlServerDscHelperTests' - $mockApplicationDomainObject = [System.AppDomain]::CreateDomain($mockApplicationDomainName) - - <# - It is not possible to fully test this helper function since we can't mock having the correct assembly - in the GAC. So these test will try to load the wrong assembly and will catch the error. But that means - it will never test the rows after if fails to load the assembly. - #> - Describe 'Testing Register-SqlSmo' -Tag RegisterSqlSmo { - BeforeEach { - Mock -CommandName Get-SqlInstanceMajorVersion -MockWith { - return '0' # Mocking zero because that could never match a correct assembly - } -Verifiable - } - - Context 'When calling Register-SqlSmo to load the wrong assembly' { - It 'Should throw with the correct error' { - { - Register-SqlSmo -SQLInstanceName $mockInstanceName - } | Should -Throw 'Exception calling "Load" with "1" argument(s): "Could not load file or assembly ''Microsoft.SqlServer.Smo, Version=0.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91'' or one of its dependencies. The system cannot find the file specified."' - - Assert-MockCalled -CommandName Get-SqlInstanceMajorVersion - } - } - - Context 'When calling Register-SqlSmo with a application domain to load the wrong assembly' { - It 'Should throw with the correct error' { - { - Register-SqlSmo -SQLInstanceName $mockInstanceName -ApplicationDomain $mockApplicationDomainObject - } | Should -Throw 'Exception calling "Load" with "1" argument(s): "Could not load file or assembly ''Microsoft.SqlServer.Smo, Version=0.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91'' or one of its dependencies. The system cannot find the file specified."' - - Assert-MockCalled -CommandName Get-SqlInstanceMajorVersion - } - } - - Assert-VerifiableMock - } - - <# - It is not possible to fully test this helper function since we can't mock having the correct assembly - in the GAC. So these test will try to load the wrong assembly and will catch the error. But that means - it will never test the rows after if fails to load the assembly. - #> - Describe 'Testing Register-SqlWmiManagement' -Tag RegisterSqlWmiManagement { - BeforeEach { - Mock -CommandName Get-SqlInstanceMajorVersion -MockWith { - return '0' # Mocking zero because that could never match a correct assembly - } -Verifiable - - Mock -CommandName Register-SqlSmo -MockWith { - [System.AppDomain]::CreateDomain('SqlServerDscHelper') - } -ParameterFilter { - $SQLInstanceName -eq $mockInstanceName - } -Verifiable - } - - Context 'When calling Register-SqlWmiManagement to load the wrong assembly' { - It 'Should throw with the correct error' { - { - Register-SqlWmiManagement -SQLInstanceName $mockInstanceName - } | Should -Throw 'Exception calling "Load" with "1" argument(s): "Could not load file or assembly ''Microsoft.SqlServer.SqlWmiManagement, Version=0.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91'' or one of its dependencies. The system cannot find the file specified."' - - Assert-MockCalled -CommandName Get-SqlInstanceMajorVersion - Assert-MockCalled -CommandName Register-SqlSmo -Exactly -Times 1 -Scope It -ParameterFilter { - $SQLInstanceName -eq $mockInstanceName -and $ApplicationDomain -eq $null - } - } - } - - Context 'When calling Register-SqlWmiManagement with a application domain to load the wrong assembly' { - It 'Should throw with the correct error' { - { - Register-SqlWmiManagement -SQLInstanceName $mockInstanceName -ApplicationDomain $mockApplicationDomainObject - } | Should -Throw 'Exception calling "Load" with "1" argument(s): "Could not load file or assembly ''Microsoft.SqlServer.SqlWmiManagement, Version=0.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91'' or one of its dependencies. The system cannot find the file specified."' - - Assert-MockCalled -CommandName Get-SqlInstanceMajorVersion - Assert-MockCalled -CommandName Register-SqlSmo -Exactly -Times 0 -Scope It -ParameterFilter { - $SQLInstanceName -eq $mockInstanceName -and $ApplicationDomain -eq $null - } - - Assert-MockCalled -CommandName Register-SqlSmo -Exactly -Times 1 -Scope It -ParameterFilter { - $SQLInstanceName -eq $mockInstanceName -and $ApplicationDomain.FriendlyName -eq $mockApplicationDomainName - } - } - } - - Assert-VerifiableMock - } - - <# - NOTE! This test must be after the tests for Register-SqlSmo and Register-SqlWmiManagement. - This test unloads the application domain that is used during those tests. - #> - Describe 'Testing Unregister-SqlAssemblies' -Tag UnregisterSqlAssemblies { - Context 'When calling Unregister-SqlAssemblies to unload the assemblies' { - It 'Should not throw an error' { - { - Unregister-SqlAssemblies -ApplicationDomain $mockApplicationDomainObject - } | Should -Not -Throw - } - } - } - Describe 'Testing Get-PrimaryReplicaServerObject' { BeforeEach { $mockServerObject = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Server diff --git a/en-US/SqlServerDscHelper.strings.psd1 b/en-US/SqlServerDscHelper.strings.psd1 index ca2d4614e..71acf9033 100644 --- a/en-US/SqlServerDscHelper.strings.psd1 +++ b/en-US/SqlServerDscHelper.strings.psd1 @@ -6,10 +6,6 @@ ConvertFrom-StringData @' ConnectedToAnalysisServicesInstance = Connected to Analysis Services instance '{0}'. FailedToConnectToAnalysisServicesInstance = Failed to connected to Analysis Services instance '{0}'. SqlMajorVersion = SQL major version is {0}. - CreatingApplicationDomain = Creating application domain '{0}'. - ReusingApplicationDomain = Reusing application domain '{0}'. - LoadingAssembly = Loading assembly '{0}'. - UnloadingApplicationDomain = Unloading application domain '{0}'. SqlServerVersionIsInvalid = Could not get the SQL version for the instance '{0}'. PropertyTypeInvalidForDesiredValues = Property 'DesiredValues' must be either a [System.Collections.Hashtable], [CimInstance] or [PSBoundParametersDictionary]. The type detected was {0}. PropertyTypeInvalidForValuesToCheck = If 'DesiredValues' is a CimInstance, then property 'ValuesToCheck' must contain a value. @@ -20,9 +16,10 @@ ConvertFrom-StringData @' UnableToCompareProperty = Unable to compare property {0} as the type {1} is not handled by the Test-SQLDSCParameterState cmdlet. PreferredModuleFound = Preferred module SqlServer found. PreferredModuleNotFound = Information: PowerShell module SqlServer not found, trying to use older SQLPS module. - ImportingPowerShellModule = Importing PowerShell module {0}. + ImportedPowerShellModule = Importing PowerShell module '{0}' with version '{1}' from path '{2}'. + PowerShellModuleAlreadyImported = Found PowerShell module {0} already imported in the session. + ModuleForceRemoval = Forcibly removed the SQL PowerShell module from the session to import it fresh again. DebugMessagePushingLocation = SQLPS module changes CWD to SQLSERVER:\ when loading, pushing location to pop it when module is loaded. - DebugMessageImportedPowerShellModule = Module {0} imported. DebugMessagePoppingLocation = Popping location back to what it was before importing SQLPS module. PowerShellSqlModuleNotFound = Neither PowerShell module SqlServer or SQLPS was found. Unable to run SQL Server cmdlets. FailedToImportPowerShellSqlModule = Failed to import {0} module. diff --git a/sv-SE/SqlServerDscHelper.strings.psd1 b/sv-SE/SqlServerDscHelper.strings.psd1 index 987b25b50..905d49201 100644 --- a/sv-SE/SqlServerDscHelper.strings.psd1 +++ b/sv-SE/SqlServerDscHelper.strings.psd1 @@ -6,10 +6,6 @@ ConvertFrom-StringData @' ConnectedToAnalysisServicesInstance = Ansluten till Analysis Services instans '{0}'. FailedToConnectToAnalysisServicesInstance = Misslyckades att ansluta till Analysis Services instans '{0}'. SqlMajorVersion = SQL major version är {0}. - CreatingApplicationDomain = Skapar applikationsdomän '{0}'. - ReusingApplicationDomain = Återanvänder applikationsdomän '{0}'. - LoadingAssembly = Laddar samling '{0}'. - UnloadingApplicationDomain = Återställer applikationsdomän '{0}'. SqlServerVersionIsInvalid = Kunde inte hämta SQL version för instansen '{0}'. PropertyTypeInvalidForDesiredValues = Egenskapen 'DesiredValues' måste vara endera en [System.Collections.Hashtable], [CimInstance] eller [PSBoundParametersDictionary]. Den typ som hittades var {0}. PropertyTypeInvalidForValuesToCheck = Om 'DesiredValues' är av typ CimInstance, då måste egenskapen 'ValuesToCheck' sättas till ett värde. @@ -20,9 +16,10 @@ ConvertFrom-StringData @' UnableToCompareProperty = Inte möjligt att jämföra egenskapen {0} som typen {1}. {1} hanteras inte av Test-SQLDscParameterState cmdlet. PreferredModuleFound = Föredragen modul SqlServer funnen. PreferredModuleNotFound = Information: PowerShell modul SqlServer ej funnen, försöker att använda äldre SQLPS modul. - ImportingPowerShellModule = Importerar PowerShell modul {0}. + ImportedPowerShellModule = Importerade PowerShell modul '{0}' med version '{1}' från mapp '{2}'. + PowerShellModuleAlreadyImported = Fann att PowerShell modul {0} redan är importerad i sessionen. + ModuleForceRemoval = Tvingade bort den tidigare SQL PowerShell modulen från sessionen för att importera den fräsch igen. DebugMessagePushingLocation = SQLPS modul ändrar nuvarande katalog till SQLSERVER:\ när modulen laddas, sparar nuvarande katalog så den kan återställas efter modulen laddats. - DebugMessageImportedPowerShellModule = Modul {0} importerad. DebugMessagePoppingLocation = Återställer nuvarande katalog till vad den var innan modulen SQLPS importerades. PowerShellSqlModuleNotFound = Varken PowerShell modulen SqlServer eller SQLPS kunde hittas. Kommer inte kunna köra SQL Server cmdlets. FailedToImportPowerShellSqlModule = Misslyckades att importera {0} modulen. @@ -37,7 +34,7 @@ ConvertFrom-StringData @' ExecuteQueryWithResultsFailed = Exekvering av fråga med resultat misslyckades mot databas '{0}'. ExecuteNonQueryFailed = Exekvering av icke-fråga misslyckades på databas '{0}'. AlterAvailabilityGroupReplicaFailed = Misslyckades att ändra Availability Group kopia '{0}'. - GetEffectivePermissionForLogin = Hämtar effektiva behörigeter för inloggningen '{0}' på '{1}'. + GetEffectivePermissionForLogin = Hämtar effektiva behörigheter för inloggningen '{0}' på '{1}'. # - NOTE! # - Below strings are used by helper functions New-TerminatingError and New-WarningMessage.