diff --git a/CHANGELOG.md b/CHANGELOG.md index f07d423ec..e6475429d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,9 @@ - Properly checks for use of SQLSysAdminAccounts parameter in $PSBoundParameters. The test now also properly evaluates the setup argument for SQLSysAdminAccounts. - xSQLServerSetup should now function correctly for the InstallFailoverCluster action, and also supports cluster shared volumes. Note that the AddNode action is not currently working. - It now detects that feature Client Connectivity Tools (CONN) and Client Connectivity Backwards Compatibility Tools (BC) is installed. + - Now it can correctly determine the right cluster when only parameter InstallSQLDataDir is assigned a path (issue #401). + - Now the only mandatory path parameter is InstallSQLDataDir when installing Database Engine (issue #400). + - It now can handle mandatory parameters, and are not using wildcards to find the variables containing paths (issue #394). - Enables CodeCov.io code coverage reporting. - Added badge for CodeCov.io to README.md. - Examples diff --git a/DSCResources/MSFT_xSQLServerSetup/MSFT_xSQLServerSetup.psm1 b/DSCResources/MSFT_xSQLServerSetup/MSFT_xSQLServerSetup.psm1 index 287955234..295e104e6 100644 --- a/DSCResources/MSFT_xSQLServerSetup/MSFT_xSQLServerSetup.psm1 +++ b/DSCResources/MSFT_xSQLServerSetup/MSFT_xSQLServerSetup.psm1 @@ -509,7 +509,7 @@ function Get-TargetResource Specifies the startup mode for SQL Server Browser service .PARAMETER FailoverClusterGroupName - The name of the resource group to create for the clustered SQL Server instance + The name of the resource group to create for the clustered SQL Server instance. Default is 'SQL Server (InstanceName)'. .PARAMETER FailoverClusterIPAddress Array of IP Addresses to be assigned to the clustered SQL Server instance @@ -524,139 +524,181 @@ function Set-TargetResource [CmdletBinding()] param ( + [Parameter()] [ValidateSet('Install','InstallFailoverCluster','AddNode','PrepareFailoverCluster','CompleteFailoverCluster')] [System.String] $Action = 'Install', + [Parameter()] [System.String] $SourcePath, - [parameter(Mandatory = $true)] + [Parameter(Mandatory = $true)] [System.Management.Automation.PSCredential] $SetupCredential, + [Parameter()] [System.Management.Automation.PSCredential] $SourceCredential, + [Parameter()] [System.Boolean] $SuppressReboot, + [Parameter()] [System.Boolean] $ForceReboot, + [Parameter()] [System.String] $Features, - [parameter(Mandatory = $true)] + [Parameter(Mandatory = $true)] [System.String] $InstanceName, + [Parameter()] [System.String] $InstanceID, + [Parameter()] [System.String] $ProductKey, + [Parameter()] [System.String] $UpdateEnabled, + [Parameter()] [System.String] $UpdateSource, + [Parameter()] [System.String] $SQMReporting, + [Parameter()] [System.String] $ErrorReporting, + [Parameter()] [System.String] $InstallSharedDir, + [Parameter()] [System.String] $InstallSharedWOWDir, + [Parameter()] [System.String] $InstanceDir, + [Parameter()] [System.Management.Automation.PSCredential] $SQLSvcAccount, + [Parameter()] [System.Management.Automation.PSCredential] $AgtSvcAccount, + [Parameter()] [System.String] $SQLCollation, + [Parameter()] [System.String[]] $SQLSysAdminAccounts, + [Parameter()] [System.String] $SecurityMode, + [Parameter()] [System.Management.Automation.PSCredential] $SAPwd, + [Parameter()] [System.String] $InstallSQLDataDir, + [Parameter()] [System.String] $SQLUserDBDir, + [Parameter()] [System.String] $SQLUserDBLogDir, + [Parameter()] [System.String] $SQLTempDBDir, + [Parameter()] [System.String] $SQLTempDBLogDir, + [Parameter()] [System.String] $SQLBackupDir, + [Parameter()] [System.Management.Automation.PSCredential] $FTSvcAccount, + [Parameter()] [System.Management.Automation.PSCredential] $RSSvcAccount, + [Parameter()] [System.Management.Automation.PSCredential] $ASSvcAccount, + [Parameter()] [System.String] $ASCollation, + [Parameter()] [System.String[]] $ASSysAdminAccounts, + [Parameter()] [System.String] $ASDataDir, + [Parameter()] [System.String] $ASLogDir, + [Parameter()] [System.String] $ASBackupDir, + [Parameter()] [System.String] $ASTempDir, + [Parameter()] [System.String] $ASConfigDir, + [Parameter()] [System.Management.Automation.PSCredential] $ISSvcAccount, + [Parameter()] [System.String] [ValidateSet('Automatic', 'Disabled', 'Manual')] $BrowserSvcStartupType, + [Parameter()] [System.String] $FailoverClusterGroupName = "SQL Server ($InstanceName)", + [Parameter()] [System.String[]] $FailoverClusterIPAddress, + [Parameter()] [System.String] $FailoverClusterNetworkName ) @@ -672,6 +714,33 @@ function Set-TargetResource $InstanceName = $InstanceName.ToUpper() + $parametersToEvaluateTrailingSlash = @( + 'InstallSQLDataDir', + 'SQLUserDBDir', + 'SQLUserDBLogDir', + 'SQLTempDBDir', + 'SQLTempDBLogDir', + 'SQLBackupDir', + 'ASDataDir', + 'ASLogDir', + 'ASBackupDir', + 'ASTempDir', + 'ASConfigDir' + ) + + # Remove trailing slash ('\') from paths + foreach ($parameterName in $parametersToEvaluateTrailingSlash) + { + if ($PSBoundParameters.ContainsKey($parameterName)) + { + $parameterValue = Get-Variable -Name $parameterName -ValueOnly + if ($parameterValue) + { + Set-Variable -Name $parameterName -Value $parameterValue.TrimEnd('\') + } + } + } + $SourcePath = [Environment]::ExpandEnvironmentVariables($SourcePath) if ($SourceCredential) @@ -779,27 +848,6 @@ function Set-TargetResource } } - # Remove trailing "\" from paths - foreach ($var in @( - 'InstallSQLDataDir', - 'SQLUserDBDir', - 'SQLUserDBLogDir', - 'SQLTempDBDir', - 'SQLTempDBLogDir', - 'SQLBackupDir', - 'ASDataDir', - 'ASLogDir', - 'ASBackupDir', - 'ASTempDir', - 'ASConfigDir') - ) - { - if (Get-Variable -Name $var -ErrorAction SilentlyContinue) - { - Set-Variable -Name $var -Value (Get-Variable -Name $var).Value.TrimEnd('\') - } - } - $setupArguments = @{} if ($Action -in @('PrepareFailoverCluster','CompleteFailoverCluster','InstallFailoverCluster')) @@ -824,10 +872,39 @@ function Set-TargetResource # Perform disk mapping for specific cluster installation types if ($Action -in @('CompleteFailoverCluster','InstallFailoverCluster')) { - $failoverClusterDisks = @() + $requiredDrive = @() + + # This is also used to evaluate which cluster shard disks should be used. + $parametersToEvaluateShareDisk = @( + 'InstallSQLDataDir', + 'SQLUserDBDir', + 'SQLUserDBLogDir', + 'SQLTempDBDir', + 'SQLTempDBLogDir', + 'SQLBackupDir', + 'ASDataDir', + 'ASLogDir', + 'ASBackupDir', + 'ASTempDir', + 'ASConfigDir' + ) + + # Get a required listing of drives based on parameters assigned by user. + foreach ($parameterName in $parametersToEvaluateShareDisk) + { + if ($PSBoundParameters.ContainsKey($parameterName)) + { + $parameterValue = Get-Variable -Name $parameterName -ValueOnly + if ($parameterValue) + { + New-VerboseMessage -Message ("Found assigned parameter '{0}'. Adding path '{1}' to list of paths that required cluster drive." -f $parameterName, $parameterValue) + $requiredDrive += $parameterValue + } + } + } - # Get a required lising of drives based on user parameters - $requiredDrives = Get-Variable -Name 'SQL*Dir' -ValueOnly | Where-Object { -not [String]::IsNullOrEmpty($_) } | Sort-Object -Unique | Add-Member -MemberType NoteProperty -Name IsMapped -Value $false -PassThru + # Only keep unique paths and add a member to keep track if the path is mapped to a disk. + $requiredDrive = $requiredDrive | Sort-Object -Unique | Add-Member -MemberType NoteProperty -Name IsMapped -Value $false -PassThru # Get the disk resources that are available (not assigned to a cluster role) $availableStorage = Get-CimInstance -Namespace 'root/MSCluster' -ClassName 'MSCluster_ResourceGroup' -Filter "Name = 'Available Storage'" | @@ -846,27 +923,29 @@ function Set-TargetResource } } - foreach ($requiredDrive in $requiredDrives) + $failoverClusterDisks = @() + + foreach ($currentRequiredDrive in $requiredDrive) { foreach ($diskResource in ($availableStorage | Where-Object {$_.IsPossibleOwner -eq $true})) { $partitions = $diskResource | Get-CimAssociatedInstance -ResultClassName 'MSCluster_DiskPartition' | Select-Object -ExpandProperty Path foreach ($partition in $partitions) { - if ($requiredDrive -imatch $partition.Replace('\','\\')) + if ($currentRequiredDrive -imatch $partition.Replace('\','\\')) { - $requiredDrive.IsMapped = $true + $currentRequiredDrive.IsMapped = $true $failoverClusterDisks += $diskResource.Name break } - if ($requiredDrive.IsMapped) + if ($currentRequiredDrive.IsMapped) { break } } - if ($requiredDrive.IsMapped) + if ($currentRequiredDrive.IsMapped) { break } @@ -878,16 +957,16 @@ function Set-TargetResource foreach ($clusterSharedVolume in $clusterSharedVolumes) { - foreach ($requiredDrive in ($requiredDrives | Where-Object {$_.IsMapped -eq $false})) + foreach ($currentRequiredDrive in ($requiredDrive | Where-Object {$_.IsMapped -eq $false})) { - if ($requiredDrive -imatch $clusterSharedVolume.Name.Replace('\','\\')) + if ($currentRequiredDrive -imatch $clusterSharedVolume.Name.Replace('\','\\')) { $diskName = Get-CimInstance -ClassName 'MSCluster_ClusterSharedVolumeToResource' -Namespace 'root/MSCluster' | ` Where-Object {$_.GroupComponent.Name -eq $clusterSharedVolume.Name} | ` Select-Object -ExpandProperty PartComponent | ` Select-Object -ExpandProperty Name $failoverClusterDisks += $diskName - $requiredDrive.IsMapped = $true + $currentRequiredDrive.IsMapped = $true } } } @@ -896,7 +975,7 @@ function Set-TargetResource $failoverClusterDisks = $failoverClusterDisks | Sort-Object -Unique # Ensure we mapped all required drives - $unMappedRequiredDrives = $requiredDrives | Where-Object {$_.IsMapped -eq $false} | Measure-Object + $unMappedRequiredDrives = $requiredDrive | Where-Object {$_.IsMapped -eq $false} | Measure-Object if ($unMappedRequiredDrives.Count -gt 0) { throw New-TerminatingError -ErrorType FailoverClusterDiskMappingError -FormatArgs ($failoverClusterDisks -join '; ') -ErrorCategory InvalidResult @@ -1153,7 +1232,7 @@ function Set-TargetResource Path = $pathToSetupExecutable Arguments = $arguments } - + if ($Action -in @('InstallFailoverCluster','AddNode')) { $processArguments.Add('Credential',$SetupCredential) @@ -1316,7 +1395,7 @@ function Set-TargetResource Specifies the startup mode for SQL Server Browser service .PARAMETER FailoverClusterGroupName - The name of the resource group to create for the clustered SQL Server instance + The name of the resource group to create for the clustered SQL Server instance. Default is 'SQL Server (InstanceName)'. .PARAMETER FailoverClusterIPAddress Array of IP Addresses to be assigned to the clustered SQL Server instance @@ -1330,129 +1409,168 @@ function Test-TargetResource [OutputType([System.Boolean])] param ( + [Parameter()] [ValidateSet('Install','InstallFailoverCluster','AddNode','PrepareFailoverCluster','CompleteFailoverCluster')] [System.String] $Action = 'Install', + [Parameter()] [System.String] $SourcePath, - [parameter(Mandatory = $true)] + [Parameter(Mandatory = $true)] [System.Management.Automation.PSCredential] $SetupCredential, + [Parameter()] [System.Management.Automation.PSCredential] $SourceCredential, + [Parameter()] [System.Boolean] $SuppressReboot, + [Parameter()] [System.Boolean] $ForceReboot, + [Parameter()] [System.String] $Features, - [parameter(Mandatory = $true)] + [Parameter(Mandatory = $true)] [System.String] $InstanceName, + [Parameter()] [System.String] $InstanceID, + [Parameter()] [System.String] $ProductKey, + [Parameter()] [System.String] $UpdateEnabled, + [Parameter()] [System.String] $UpdateSource, + [Parameter()] [System.String] $SQMReporting, + [Parameter()] [System.String] $ErrorReporting, + [Parameter()] [System.String] $InstallSharedDir, + [Parameter()] [System.String] $InstallSharedWOWDir, + [Parameter()] [System.String] $InstanceDir, + [Parameter()] [System.Management.Automation.PSCredential] $SQLSvcAccount, + [Parameter()] [System.Management.Automation.PSCredential] $AgtSvcAccount, + [Parameter()] [System.String] $SQLCollation, + [Parameter()] [System.String[]] $SQLSysAdminAccounts, + [Parameter()] [System.String] $SecurityMode, + [Parameter()] [System.Management.Automation.PSCredential] $SAPwd, + [Parameter()] [System.String] $InstallSQLDataDir, + [Parameter()] [System.String] $SQLUserDBDir, + [Parameter()] [System.String] $SQLUserDBLogDir, + [Parameter()] [System.String] $SQLTempDBDir, + [Parameter()] [System.String] $SQLTempDBLogDir, + [Parameter()] [System.String] $SQLBackupDir, + [Parameter()] [System.Management.Automation.PSCredential] $FTSvcAccount, + [Parameter()] [System.Management.Automation.PSCredential] $RSSvcAccount, + [Parameter()] [System.Management.Automation.PSCredential] $ASSvcAccount, + [Parameter()] [System.String] $ASCollation, + [Parameter()] [System.String[]] $ASSysAdminAccounts, + [Parameter()] [System.String] $ASDataDir, + [Parameter()] [System.String] $ASLogDir, + [Parameter()] [System.String] $ASBackupDir, + [Parameter()] [System.String] $ASTempDir, + [Parameter()] [System.String] $ASConfigDir, + [Parameter()] [System.Management.Automation.PSCredential] $ISSvcAccount, + [Parameter()] [System.String] [ValidateSet('Automatic', 'Disabled', 'Manual')] $BrowserSvcStartupType, diff --git a/DSCResources/MSFT_xSQLServerSetup/MSFT_xSQLServerSetup.schema.mof b/DSCResources/MSFT_xSQLServerSetup/MSFT_xSQLServerSetup.schema.mof index fa3e30b52..f30fd5590 100644 --- a/DSCResources/MSFT_xSQLServerSetup/MSFT_xSQLServerSetup.schema.mof +++ b/DSCResources/MSFT_xSQLServerSetup/MSFT_xSQLServerSetup.schema.mof @@ -48,7 +48,7 @@ class MSFT_xSQLServerSetup : OMI_BaseResource [Write, EmbeddedInstance("MSFT_Credential"), Description("Service account for Integration Services service.")] String ISSvcAccount; [Read, Description("Output username for the Integration Services service.")] String ISSvcAccountUsername; [Write, Description("Specifies the startup mode for SQL Server Browser service."), ValueMap{"Automatic", "Disabled", "Manual"}, Values{"Automatic", "Disabled", "Manual"}] String BrowserSvcStartupType; - [Write, Description("The name of the resource group to create for the clustered SQL Server instance.")] String FailoverClusterGroupName; + [Write, Description("The name of the resource group to create for the clustered SQL Server instance. Default is 'SQL Server (InstanceName)'.")] String FailoverClusterGroupName; [Write, Description("Array of IP Addresses to be assigned to the clustered SQL Server instance.")] String FailoverClusterIPAddress[]; [Write, Description("Host name to be assigend to the clustered SQL Server instance.")] String FailoverClusterNetworkName; }; diff --git a/README.md b/README.md index 9ed44ea37..638ccb1ce 100644 --- a/README.md +++ b/README.md @@ -854,27 +854,18 @@ Installs SQL Server on the target node. #### Requirements * Target machine must be running Windows Server 2008 R2 or later. - -For configurations that utilize the 'InstallFailoverCluster' action, the following parameters are required (beyond those required for the standalone installation): - -* InstanceName (can be MSSQLSERVER if you want to install a default clustered instance) -* FailoverClusterNetworkName -* When installation SQL Server database engine: - * InstallSQLDataDir - * SQLUserDBDir - * SQLUserDBLogDir - * SQLTempDBDir - * SQLTempDBLogDir - * SQLBackupDir - * AgtSvcAccount - * SQLSvcAccount -* When installing SQL Analysis Services - * ASDataDir - * ASLogDir - * ASBackupDir - * ASTempDir - * ASConfigDir - * AsSvcAccount +* For configurations that utilize the 'InstallFailoverCluster' action, the following parameters are required (beyond those required for the standalone installation). See the article [Install SQL Server from the Command Prompt](https://msdn.microsoft.com/en-us/library/ms144259.aspx) under the section [Failover Cluster Parameters](https://msdn.microsoft.com/en-us/library/ms144259.aspx#Anchor_8) for more information. + * InstanceName (can be MSSQLSERVER if you want to install a default clustered instance). + * FailoverClusterNetworkName + * FailoverClusterIPAddress + * _When installing Database Engine._ + * InstallSQLDataDir + * AgtSvcAccount + * SQLSvcAccount + * SQLSysAdminAccounts + * _When installing Analysis Services._ + * ASSysAdminAccounts + * AsSvcAccount #### Parameters @@ -919,7 +910,7 @@ For configurations that utilize the 'InstallFailoverCluster' action, the followi * **[String] ASConfigDir** _(Write)_: Path for Analysis Services config. * **[PSCredential] ISSvcAccount** _(Write)_: Service account for Integration Services service. * **[String] BrowserSvcStartupType** _(Write)_: Specifies the startup mode for SQL Server Browser service. { Automatic | Disabled | 'Manual' } -* **[String] FailoverClusterGroupName** _(Write)_: The name of the resource group to create for the clustered SQL Server instance. Defaults to 'SQL Server (_InstanceName_)'. +* **[String] FailoverClusterGroupName** _(Write)_: The name of the resource group to create for the clustered SQL Server instance. Default is 'SQL Server (_InstanceName_)'. * **[String[]]FailoverClusterIPAddress** _(Write)_: Array of IP Addresses to be assigned to the clustered SQL Server instance. IP addresses must be in [dotted-decimal notation](https://en.wikipedia.org/wiki/Dot-decimal_notation), for example ````10.0.0.100````. If no IP address is specified, uses 'DEFAULT' for this setup parameter. * **[String] FailoverClusterNetworkName** _(Write)_: Host name to be assigned to the clustered SQL Server instance. @@ -943,6 +934,7 @@ No description. #### Requirements * Target machine must be running Windows Server 2008 R2 or later. +* Target machine must be running SQL Server Database Engine 2012 or later. #### Parameters diff --git a/Tests/Unit/MSFT_xSQLServerSetup.Tests.ps1 b/Tests/Unit/MSFT_xSQLServerSetup.Tests.ps1 index eb8de204c..e5332184c 100644 --- a/Tests/Unit/MSFT_xSQLServerSetup.Tests.ps1 +++ b/Tests/Unit/MSFT_xSQLServerSetup.Tests.ps1 @@ -109,15 +109,26 @@ try $mockClusterNodes = @($env:COMPUTERNAME,'SQL01','SQL02') - $mockClusterDiskMap = @{ - UserData = 'K:' - UserLogs = 'L:' - TempDbData = 'M:' - TempDbLogs = 'N:' - SQLBackup = 'O:' + $mockSqlDataDirectoryPath = 'E:\MSSQL\Data' + $mockSqlUserDatabasePath = 'K:\MSSQL\Data' + $mockSqlUserDatabaseLogPath = 'L:\MSSQL\Logs' + $mockSqlTempDatabasePath = 'M:\MSSQL\TempDb\Data' + $mockSqlTempDatabaseLogPath = 'N:\MSSQL\TempDb\Logs' + $mockSqlBackupPath = 'O:\MSSQL\Backup' + + $mockClusterDiskMap = { + @{ + SysData = Split-Path -Path $mockDynamicSqlDataDirectoryPath -Qualifier + UserData = Split-Path -Path $mockDynamicSqlUserDatabasePath -Qualifier + UserLogs = Split-Path -Path $mockDynamicSqlUserDatabaseLogPath -Qualifier + TempDbData = Split-Path -Path $mockDynamicSqlTempDatabasePath -Qualifier + TempDbLogs = Split-Path -Path $mockDynamicSqlTempDatabaseLogPath -Qualifier + Backup = Split-Path -Path $mockDynamicSqlBackupPath -Qualifier + } } $mockCSVClusterDiskMap = @{ + SysData = @{Path='C:\ClusterStorage\SysData';Name="Cluster Virtual Disk (SQL System Data Disk)"} UserData = @{Path='C:\ClusterStorage\SQLData';Name="Cluster Virtual Disk (SQL Data Disk)"} UserLogs = @{Path='C:\ClusterStorage\SQLLogs';Name="Cluster Virtual Disk (SQL Log Disk)"} TempDbData = @{Path='C:\ClusterStorage\TempDBData';Name="Cluster Virtual Disk (SQL TempDBData Disk)"} @@ -479,9 +490,9 @@ try Add-Member -MemberType NoteProperty -Name 'LoginMode' -Value $mockSqlLoginMode -PassThru | Add-Member -MemberType NoteProperty -Name 'Collation' -Value $mockSqlCollation -PassThru | Add-Member -MemberType NoteProperty -Name 'InstallDataDirectory' -Value $mockSqlInstallPath -PassThru | - Add-Member -MemberType NoteProperty -Name 'BackupDirectory' -Value $mockSqlBackupPath -PassThru | - Add-Member -MemberType NoteProperty -Name 'SQLTempDBDir' -Value $mockSqlTempDatabasePath -PassThru | - Add-Member -MemberType NoteProperty -Name 'SQLTempDBLogDir' -Value $mockSqlTempDatabaseLogPath -PassThru | + Add-Member -MemberType NoteProperty -Name 'BackupDirectory' -Value $mockDynamicSqlBackupPath -PassThru | + Add-Member -MemberType NoteProperty -Name 'SQLTempDBDir' -Value $mockDynamicSqlTempDatabasePath -PassThru | + Add-Member -MemberType NoteProperty -Name 'SQLTempDBLogDir' -Value $mockDynamicSqlTempDatabaseLogPath -PassThru | Add-Member -MemberType NoteProperty -Name 'DefaultFile' -Value $mockSqlDefaultDatabaseFilePath -PassThru | Add-Member -MemberType NoteProperty -Name 'DefaultLog' -Value $mockSqlDefaultDatabaseLogPath -PassThru | Add-Member ScriptProperty Logins { @@ -503,9 +514,9 @@ try Add-Member -MemberType NoteProperty -Name 'LoginMode' -Value $mockSqlLoginMode -PassThru | Add-Member -MemberType NoteProperty -Name 'Collation' -Value $mockSqlCollation -PassThru | Add-Member -MemberType NoteProperty -Name 'InstallDataDirectory' -Value $mockSqlInstallPath -PassThru | - Add-Member -MemberType NoteProperty -Name 'BackupDirectory' -Value $mockSqlBackupPath -PassThru | - Add-Member -MemberType NoteProperty -Name 'SQLTempDBDir' -Value $mockSqlTempDatabasePath -PassThru | - Add-Member -MemberType NoteProperty -Name 'SQLTempDBLogDir' -Value $mockSqlTempDatabaseLogPath -PassThru | + Add-Member -MemberType NoteProperty -Name 'BackupDirectory' -Value $mockDynamicSqlBackupPath -PassThru | + Add-Member -MemberType NoteProperty -Name 'SQLTempDBDir' -Value $mockDynamicSqlTempDatabasePath -PassThru | + Add-Member -MemberType NoteProperty -Name 'SQLTempDBLogDir' -Value $mockDynamicSqlTempDatabaseLogPath -PassThru | Add-Member -MemberType NoteProperty -Name 'DefaultFile' -Value $mockSqlDefaultDatabaseFilePath -PassThru | Add-Member -MemberType NoteProperty -Name 'DefaultLog' -Value $mockSqlDefaultDatabaseLogPath -PassThru | Add-Member -MemberType NoteProperty -Name 'IsClustered' -Value $true -PassThru | @@ -625,6 +636,10 @@ try $mockGetCIMInstance_MSCluster_ClusterSharedVolume = { return @( + ( + New-Object Microsoft.Management.Infrastructure.CimInstance 'MSCluster_ClusterSharedVolume', 'root/MSCluster' | + Add-Member -MemberType NoteProperty -Name 'Name' -Value $mockCSVClusterDiskMap['SysData'].Path -PassThru -Force + ), ( New-Object Microsoft.Management.Infrastructure.CimInstance 'MSCluster_ClusterSharedVolume', 'root/MSCluster' | Add-Member -MemberType NoteProperty -Name 'Name' -Value $mockCSVClusterDiskMap['UserData'].Path -PassThru -Force @@ -652,34 +667,36 @@ try return @( ( New-Object Microsoft.Management.Infrastructure.CimInstance 'MSCluster_ClusterSharedVolume', 'root/MSCluster' | - Add-Member -MemberType NoteProperty -Name GroupComponent -Value (New-Object PSObject -Property @{Name=$mockCSVClusterDiskMap['UserData'].Path}) -PassThru -Force | + Add-Member -MemberType NoteProperty -Name GroupComponent -Value (New-Object PSObject -Property @{Name=$mockCSVClusterDiskMap['SysData'].Path}) -PassThru -Force | + Add-Member -MemberType NoteProperty -Name PartComponent -Value (New-Object PSObject -Property @{Name=$mockCSVClusterDiskMap['SysData'].Name}) -PassThru -Force + ), + ( + New-Object Microsoft.Management.Infrastructure.CimInstance 'MSCluster_ClusterSharedVolume', 'root/MSCluster' | + Add-Member -MemberType NoteProperty -Name GroupComponent -Value (New-Object PSObject -Property @{Name=$mockCSVClusterDiskMap['UserData'].Path}) -PassThru -Force | Add-Member -MemberType NoteProperty -Name PartComponent -Value (New-Object PSObject -Property @{Name=$mockCSVClusterDiskMap['UserData'].Name}) -PassThru -Force ), ( New-Object Microsoft.Management.Infrastructure.CimInstance 'MSCluster_ClusterSharedVolume', 'root/MSCluster' | - Add-Member -MemberType NoteProperty -Name GroupComponent -Value (New-Object PSObject -Property @{Name=$mockCSVClusterDiskMap['UserLogs'].Path}) -PassThru -Force | + Add-Member -MemberType NoteProperty -Name GroupComponent -Value (New-Object PSObject -Property @{Name=$mockCSVClusterDiskMap['UserLogs'].Path}) -PassThru -Force | Add-Member -MemberType NoteProperty -Name PartComponent -Value (New-Object PSObject -Property @{Name=$mockCSVClusterDiskMap['UserLogs'].Name}) -PassThru -Force ), ( New-Object Microsoft.Management.Infrastructure.CimInstance 'MSCluster_ClusterSharedVolume', 'root/MSCluster' | - Add-Member -MemberType NoteProperty -Name GroupComponent -Value (New-Object PSObject -Property @{Name=$mockCSVClusterDiskMap['TempDBData'].Path}) -PassThru -Force | + Add-Member -MemberType NoteProperty -Name GroupComponent -Value (New-Object PSObject -Property @{Name=$mockCSVClusterDiskMap['TempDBData'].Path}) -PassThru -Force | Add-Member -MemberType NoteProperty -Name PartComponent -Value (New-Object PSObject -Property @{Name=$mockCSVClusterDiskMap['TempDBData'].Name}) -PassThru -Force ), ( New-Object Microsoft.Management.Infrastructure.CimInstance 'MSCluster_ClusterSharedVolume', 'root/MSCluster' | - Add-Member -MemberType NoteProperty -Name GroupComponent -Value (New-Object PSObject -Property @{Name=$mockCSVClusterDiskMap['TempDBLogs'].Path}) -PassThru -Force | + Add-Member -MemberType NoteProperty -Name GroupComponent -Value (New-Object PSObject -Property @{Name=$mockCSVClusterDiskMap['TempDBLogs'].Path}) -PassThru -Force | Add-Member -MemberType NoteProperty -Name PartComponent -Value (New-Object PSObject -Property @{Name=$mockCSVClusterDiskMap['TempDBLogs'].Name}) -PassThru -Force ), ( New-Object Microsoft.Management.Infrastructure.CimInstance 'MSCluster_ClusterSharedVolume', 'root/MSCluster' | - Add-Member -MemberType NoteProperty -Name GroupComponent -Value (New-Object PSObject -Property @{Name=$mockCSVClusterDiskMap['Backup'].Path}) -PassThru -Force | + Add-Member -MemberType NoteProperty -Name GroupComponent -Value (New-Object PSObject -Property @{Name=$mockCSVClusterDiskMap['Backup'].Path}) -PassThru -Force | Add-Member -MemberType NoteProperty -Name PartComponent -Value (New-Object PSObject -Property @{Name=$mockCSVClusterDiskMap['Backup'].Name}) -PassThru -Force ) ) } - - - $mockGetCimInstance_MSClusterNetwork = { return @( @@ -743,7 +760,8 @@ try $mockGetCimAssociatedInstance_MSCluster_ResourceGroupToResource = { return @( ( - $mockClusterDiskMap.Keys | ForEach-Object { + # $mockClusterDiskMap contains variables that are assigned dynamically (during runtime) before each test. + (& $mockClusterDiskMap).Keys | ForEach-Object { $diskName = $_ New-Object Microsoft.Management.Infrastructure.CimInstance 'MSCluster_Resource','root/MSCluster' | Add-Member -MemberType NoteProperty -Name 'Name' -Value $diskName -PassThru -Force | @@ -768,7 +786,9 @@ try $mockGetCimAssociatedInstance_MSCluster_DiskPartition = { $clusterDiskName = $InputObject.Name - $clusterDiskPath = $mockClusterDiskMap.$clusterDiskName + + # $mockClusterDiskMap contains variables that are assigned dynamically (during runtime) before each test. + $clusterDiskPath = (& $mockClusterDiskMap).$clusterDiskName return @( ( @@ -817,7 +837,7 @@ try } # Start by checking whether we have the same number of parameters - New-VerboseMessage 'Verifying argument count (expected vs actual)' + Write-Verbose 'Verifying argument count (expected vs actual)' -Verbose $mockStartWin32ProcessExpectedArgument.Keys.Count | Should BeExactly $argumentHashTable.Keys.Count foreach ($argumentKey in $mockStartWin32ProcessExpectedArgument.Keys) @@ -849,11 +869,6 @@ try # Feature support is tested elsewhere, so just include the minimum Features = 'SQLEngine' - # Ensure we use "clustered" disks for our paths - SQLUserDBDir = 'K:\MSSQL\Data\' - SQLUserDBLogDir = 'L:\MSSQL\Logs' - SQLTempDbDir = 'M:\MSSQL\TempDb\Data\' - SQLTempDbLogDir = 'N:\MSSQL\TempDb\Logs' } Describe "xSQLServerSetup\Get-TargetResource" -Tag 'Get' { @@ -919,9 +934,9 @@ try $mockDefaultInstance_InstanceId = "$($mockSqlDatabaseEngineName)$($mockSqlMajorVersion).$($mockDefaultInstance_InstanceName)" $mockSqlInstallPath = "C:\Program Files\Microsoft SQL Server\$($mockDefaultInstance_InstanceId)\MSSQL" - $mockSqlBackupPath = "C:\Program Files\Microsoft SQL Server\$($mockDefaultInstance_InstanceId)\MSSQL\Backup" - $mockSqlTempDatabasePath = '' - $mockSqlTempDatabaseLogPath = '' + $mockDynamicSqlBackupPath = "C:\Program Files\Microsoft SQL Server\$($mockDefaultInstance_InstanceId)\MSSQL\Backup" + $mockDynamicSqlTempDatabasePath = '' + $mockDynamicSqlTempDatabaseLogPath = '' $mockSqlDefaultDatabaseFilePath = "C:\Program Files\Microsoft SQL Server\$($mockDefaultInstance_InstanceId)\MSSQL\DATA\" $mockSqlDefaultDatabaseLogPath = "C:\Program Files\Microsoft SQL Server\$($mockDefaultInstance_InstanceId)\MSSQL\DATA\" @@ -1426,7 +1441,7 @@ try $result.InstallSQLDataDir | Should Be $mockSqlInstallPath $result.SQLUserDBDir | Should Be $mockSqlDefaultDatabaseFilePath $result.SQLUserDBLogDir | Should Be $mockSqlDefaultDatabaseLogPath - $result.SQLBackupDir | Should Be $mockSqlBackupPath + $result.SQLBackupDir | Should Be $mockDynamicSqlBackupPath $result.FTSvcAccountUsername | Should Be $mockSqlServiceAccount $result.RSSvcAccountUsername | Should Be $mockSqlServiceAccount $result.ASSvcAccountUsername | Should Be $mockSqlServiceAccount @@ -1622,7 +1637,7 @@ try $result.InstallSQLDataDir | Should Be $mockSqlInstallPath $result.SQLUserDBDir | Should Be $mockSqlDefaultDatabaseFilePath $result.SQLUserDBLogDir | Should Be $mockSqlDefaultDatabaseLogPath - $result.SQLBackupDir | Should Be $mockSqlBackupPath + $result.SQLBackupDir | Should Be $mockDynamicSqlBackupPath $result.FTSvcAccountUsername | Should Be $mockSqlServiceAccount $result.RSSvcAccountUsername | Should Be $mockSqlServiceAccount $result.ASSvcAccountUsername | Should Be $mockSqlServiceAccount @@ -1640,9 +1655,9 @@ try $mockNamedInstance_InstanceId = "$($mockSqlDatabaseEngineName)$($mockSqlMajorVersion).$($mockNamedInstance_InstanceName)" $mockSqlInstallPath = "C:\Program Files\Microsoft SQL Server\$($mockNamedInstance_InstanceId)\MSSQL" - $mockSqlBackupPath = "C:\Program Files\Microsoft SQL Server\$($mockNamedInstance_InstanceId)\MSSQL\Backup" - $mockSqlTempDatabasePath = '' - $mockSqlTempDatabaseLogPath = '' + $mockDynamicSqlBackupPath = "C:\Program Files\Microsoft SQL Server\$($mockNamedInstance_InstanceId)\MSSQL\Backup" + $mockDynamicSqlTempDatabasePath = '' + $mockDynamicSqlTempDatabaseLogPath = '' $mockSqlDefaultDatabaseFilePath = "C:\Program Files\Microsoft SQL Server\$($mockNamedInstance_InstanceId)\MSSQL\DATA\" $mockSqlDefaultDatabaseLogPath = "C:\Program Files\Microsoft SQL Server\$($mockNamedInstance_InstanceId)\MSSQL\DATA\" @@ -1931,7 +1946,7 @@ try $result.InstallSQLDataDir | Should Be $mockSqlInstallPath $result.SQLUserDBDir | Should Be $mockSqlDefaultDatabaseFilePath $result.SQLUserDBLogDir | Should Be $mockSqlDefaultDatabaseLogPath - $result.SQLBackupDir | Should Be $mockSqlBackupPath + $result.SQLBackupDir | Should Be $mockDynamicSqlBackupPath $result.FTSvcAccountUsername | Should Be $mockSqlServiceAccount $result.RSSvcAccountUsername | Should Be $mockSqlServiceAccount $result.ASSvcAccountUsername | Should Be $mockSqlServiceAccount @@ -2093,9 +2108,9 @@ try $mockDefaultInstance_InstanceId = "$($mockSqlDatabaseEngineName)$($mockSqlMajorVersion).$($mockDefaultInstance_InstanceName)" $mockSqlInstallPath = "C:\Program Files\Microsoft SQL Server\$($mockDefaultInstance_InstanceId)\MSSQL" - $mockSqlBackupPath = "C:\Program Files\Microsoft SQL Server\$($mockDefaultInstance_InstanceId)\MSSQL\Backup" - $mockSqlTempDatabasePath = '' - $mockSqlTempDatabaseLogPath = '' + $mockDynamicSqlBackupPath = "C:\Program Files\Microsoft SQL Server\$($mockDefaultInstance_InstanceId)\MSSQL\Backup" + $mockDynamicSqlTempDatabasePath = '' + $mockDynamicSqlTempDatabaseLogPath = '' $mockSqlDefaultDatabaseFilePath = "C:\Program Files\Microsoft SQL Server\$($mockDefaultInstance_InstanceId)\MSSQL\DATA\" $mockSqlDefaultDatabaseLogPath = "C:\Program Files\Microsoft SQL Server\$($mockDefaultInstance_InstanceId)\MSSQL\DATA\" @@ -2552,7 +2567,7 @@ try Mock -CommandName Connect-SQL -MockWith $mockConnectSQLCluster -Verifiable - Mock -CommandName Get-CimInstance -MockWith $mockGetCimInstance_MSClusterResource -Verifiable -ParameterFilter { + Mock -CommandName Get-CimInstance -MockWith $mockGetCimInstance_MSClusterResource -Verifiable -ParameterFilter { $Filter -eq "Type = 'SQL Server'" } @@ -2568,8 +2583,15 @@ try FailoverClusterNetworkName = $mockDefaultInstance_FailoverClusterNetworkName } - New-Variable -Name 'FailoverClusterDisks' -Value $mockClusterDiskMap['UserData'] - + $mockDynamicSqlDataDirectoryPath = $mockSqlDataDirectoryPath + $mockDynamicSqlUserDatabasePath = $mockSqlUserDatabasePath + $mockDynamicSqlUserDatabaseLogPath = $mockSqlUserDatabaseLogPath + $mockDynamicSqlTempDatabasePath = $mockSqlTempDatabasePath + $mockDynamicSqlTempDatabaseLogPath = $mockSqlTempDatabaseLogPath + $mockDynamicSqlBackupPath = $mockSqlBackupPath + + New-Variable -Name 'FailoverClusterDisks' -Value (& $mockClusterDiskMap)['UserData'] + $result = Test-TargetResource @testClusterParameters $result | Should Be $true @@ -2681,9 +2703,9 @@ try $mockDefaultInstance_InstanceId = "$($mockSqlDatabaseEngineName)$($mockSqlMajorVersion).$($mockDefaultInstance_InstanceName)" $mockSqlInstallPath = "C:\Program Files\Microsoft SQL Server\$($mockDefaultInstance_InstanceId)\MSSQL" - $mockSqlBackupPath = "C:\Program Files\Microsoft SQL Server\$($mockDefaultInstance_InstanceId)\MSSQL\Backup" - $mockSqlTempDatabasePath = '' - $mockSqlTempDatabaseLogPath = '' + $mockDynamicSqlBackupPath = "C:\Program Files\Microsoft SQL Server\$($mockDefaultInstance_InstanceId)\MSSQL\Backup" + $mockDynamicSqlTempDatabasePath = '' + $mockDynamicSqlTempDatabaseLogPath = '' $mockSqlDefaultDatabaseFilePath = "C:\Program Files\Microsoft SQL Server\$($mockDefaultInstance_InstanceId)\MSSQL\DATA\" $mockSqlDefaultDatabaseLogPath = "C:\Program Files\Microsoft SQL Server\$($mockDefaultInstance_InstanceId)\MSSQL\DATA\" @@ -3189,9 +3211,9 @@ try $mockNamedInstance_InstanceId = "$($mockSqlDatabaseEngineName)$($mockSqlMajorVersion).$($mockNamedInstance_InstanceName)" $mockSqlInstallPath = "C:\Program Files\Microsoft SQL Server\$($mockNamedInstance_InstanceId)\MSSQL" - $mockSqlBackupPath = "C:\Program Files\Microsoft SQL Server\$($mockNamedInstance_InstanceId)\MSSQL\Backup" - $mockSqlTempDatabasePath = '' - $mockSqlTempDatabaseLogPath = '' + $mockDynamicSqlBackupPath = "C:\Program Files\Microsoft SQL Server\$($mockNamedInstance_InstanceId)\MSSQL\Backup" + $mockDynamicSqlTempDatabasePath = '' + $mockDynamicSqlTempDatabaseLogPath = '' $mockSqlDefaultDatabaseFilePath = "C:\Program Files\Microsoft SQL Server\$($mockNamedInstance_InstanceId)\MSSQL\DATA\" $mockSqlDefaultDatabaseLogPath = "C:\Program Files\Microsoft SQL Server\$($mockNamedInstance_InstanceId)\MSSQL\DATA\" @@ -3374,7 +3396,7 @@ try $Filter -eq "Name = 'Available Storage'" } -Verifiable - Mock -CommandName Get-CimAssociatedInstance -MockWith $mockGetCimAssociatedInstance_MSCluster_ResourceGroupToResource -ParameterFilter { + Mock -CommandName Get-CimAssociatedInstance -MockWith $mockGetCimAssociatedInstance_MSCluster_ResourceGroupToResource -ParameterFilter { ($Association -eq 'MSCluster_ResourceGroupToResource') -and ($ResultClassName -eq 'MSCluster_Resource') } -Verfiable @@ -3386,7 +3408,7 @@ try $ResultClassName -eq 'MSCluster_DiskPartition' } -Verifiable - Mock -CommandName Get-CimInstance -MockWith $mockGetCimInstance_MSClusterNetwork -ParameterFilter { + Mock -CommandName Get-CimInstance -MockWith $mockGetCimInstance_MSClusterNetwork -ParameterFilter { ($Namespace -eq 'root/MSCluster') -and ($ClassName -eq 'MSCluster_Network') -and ($Filter -eq 'Role >= 2') } -Verifiable @@ -3429,12 +3451,18 @@ try { Set-TargetResource @testParameters } | Should Not Throw } } - + # For testing InstallFailoverCluster action Context "When SQL Server version is $mockSQLMajorVersion and the system is not in the desired state and the action is InstallFailoverCluster" { BeforeAll { - $testParameters = $mockDefaultClusterParameters.Clone() + $mockDynamicSqlDataDirectoryPath = $mockSqlDataDirectoryPath + $mockDynamicSqlUserDatabasePath = $mockSqlUserDatabasePath + $mockDynamicSqlUserDatabaseLogPath = $mockSqlUserDatabaseLogPath + $mockDynamicSqlTempDatabasePath = $mockSqlTempDatabasePath + $mockDynamicSqlTempDatabaseLogPath = $mockSqlTempDatabaseLogPath + $mockDynamicSqlBackupPath = $mockSqlBackupPath + $testParameters = $mockDefaultClusterParameters.Clone() $testParameters += @{ InstanceName = 'MSSQLSERVER' SourcePath = $mockSourcePath @@ -3442,16 +3470,21 @@ try FailoverClusterGroupName = 'SQL Server (MSSQLSERVER)' FailoverClusterNetworkName = $mockDefaultInstance_FailoverClusterNetworkName FailoverClusterIPAddress = $mockDefaultInstance_FailoverClusterIPAddress - } - } + # Ensure we use "clustered" disks for our paths + InstallSQLDataDir = $mockDynamicSqlDataDirectoryPath + SQLUserDBDir = $mockDynamicSqlUserDatabasePath + SQLUserDBLogDir = $mockDynamicSqlUserDatabaseLogPath + SQLTempDbDir = $mockDynamicSqlTempDatabasePath + SQLTempDbLogDir = $mockDynamicSqlTempDatabaseLogPath + SQLBackupDir = $mockDynamicSqlBackupPath + } - BeforeAll { Mock -CommandName Get-CimInstance -MockWith $mockGetCimInstance_MSClusterResourceGroup_AvailableStorage -ParameterFilter { $Filter -eq "Name = 'Available Storage'" } -Verifiable - Mock -CommandName Get-CimAssociatedInstance -MockWith $mockGetCimAssociatedInstance_MSCluster_ResourceGroupToResource -ParameterFilter { + Mock -CommandName Get-CimAssociatedInstance -MockWith $mockGetCimAssociatedInstance_MSCluster_ResourceGroupToResource -ParameterFilter { ($Association -eq 'MSCluster_ResourceGroupToResource') -and ($ResultClassName -eq 'MSCluster_Resource') } -Verfiable @@ -3471,7 +3504,7 @@ try $ClassName -eq 'MSCluster_ClusterSharedVolumeToResource' } -Verifiable - Mock -CommandName Get-CimInstance -MockWith $mockGetCimInstance_MSClusterNetwork -ParameterFilter { + Mock -CommandName Get-CimInstance -MockWith $mockGetCimInstance_MSClusterNetwork -ParameterFilter { ($Namespace -eq 'root/MSCluster') -and ($ClassName -eq 'MSCluster_Network') -and ($Filter -eq 'Role >= 2') } -Verifiable @@ -3488,27 +3521,70 @@ try } It 'Should pass proper parameters to setup' { - $mockStartWin32ProcessExpectedArgument = @{ - IAcceptSQLServerLicenseTerms = 'True' - SkipRules = 'Cluster_VerifyForErrors' - Quiet = 'True' - SQLSysAdminAccounts = 'COMPANY\sqladmin' + $mockStartWin32ProcessExpectedArgument = $mockStartWin32ProcessExpectedArgumentClusterDefault.Clone() + $mockStartWin32ProcessExpectedArgument += @{ Action = 'InstallFailoverCluster' - InstanceName = 'MSSQLSERVER' - Features = 'SQLEngine' - FailoverClusterDisks = 'TempDbData; TempDbLogs; UserData; UserLogs' + FailoverClusterDisks = 'Backup; SysData; TempDbData; TempDbLogs; UserData; UserLogs' FailoverClusterIPAddresses = $mockDefaultInstance_FailoverClusterIPAddressParameter_SingleSite - FailoverClusterGroup = 'SQL Server (MSSQLSERVER)' FailoverClusterNetworkName = $mockDefaultInstance_FailoverClusterNetworkName - SQLUserDBDir = 'K:\MSSQL\Data' - SQLUserDBLogDir = 'L:\MSSQL\Logs' - SQLTempDBDir = 'M:\MSSQL\TempDb\Data' - SQLTempDBLogDir = 'N:\MSSQL\TempDb\Logs' + SkipRules = 'Cluster_VerifyForErrors' + InstallSQLDataDir = $mockDynamicSqlDataDirectoryPath + SQLUserDBDir = $mockDynamicSqlUserDatabasePath + SQLUserDBLogDir = $mockDynamicSqlUserDatabaseLogPath + SQLTempDBDir = $mockDynamicSqlTempDatabasePath + SQLTempDBLogDir = $mockDynamicSqlTempDatabaseLogPath + SQLBackupDir = $mockDynamicSqlBackupPath } { Set-TargetResource @testParameters } | Should Not Throw } + It 'Should pass proper parameters to setup when only InstallSQLDataDir is assigned a path' { + $mockStartWin32ProcessExpectedArgument = $mockStartWin32ProcessExpectedArgumentClusterDefault.Clone() + $mockStartWin32ProcessExpectedArgument += @{ + Action = 'InstallFailoverCluster' + FailoverClusterDisks = 'SysData' + FailoverClusterIPAddresses = $mockDefaultInstance_FailoverClusterIPAddressParameter_SingleSite + FailoverClusterNetworkName = $mockDefaultInstance_FailoverClusterNetworkName + SkipRules = 'Cluster_VerifyForErrors' + InstallSQLDataDir = $mockDynamicSqlDataDirectoryPath + } + + $setTargetResourceParameters = $testParameters.Clone() + $setTargetResourceParameters.Remove('SQLUserDBDir') + $setTargetResourceParameters.Remove('SQLUserDBLogDir') + $setTargetResourceParameters.Remove('SQLTempDbDir') + $setTargetResourceParameters.Remove('SQLTempDbLogDir') + $setTargetResourceParameters.Remove('SQLBackupDir') + + { Set-TargetResource @setTargetResourceParameters } | Should Not Throw + } + + It 'Should pass proper parameters to setup when three variables are assigned the same drive, but different paths' { + $mockStartWin32ProcessExpectedArgument = $mockStartWin32ProcessExpectedArgumentClusterDefault.Clone() + $mockStartWin32ProcessExpectedArgument += @{ + Action = 'InstallFailoverCluster' + FailoverClusterDisks = 'SysData' + FailoverClusterIPAddresses = $mockDefaultInstance_FailoverClusterIPAddressParameter_SingleSite + FailoverClusterNetworkName = $mockDefaultInstance_FailoverClusterNetworkName + SkipRules = 'Cluster_VerifyForErrors' + InstallSQLDataDir = 'E:\SQLData' + SQLUserDBDir = 'E:\SQLData\UserDb' + SQLUserDBLogDir = 'E:\SQLData\UserDbLogs' + } + + $setTargetResourceParameters = $testParameters.Clone() + $setTargetResourceParameters.Remove('SQLTempDbDir') + $setTargetResourceParameters.Remove('SQLTempDbLogDir') + $setTargetResourceParameters.Remove('SQLBackupDir') + + $setTargetResourceParameters['InstallSQLDataDir'] = 'E:\SQLData\' # This ends with \ to test removal of paths ending with \ + $setTargetResourceParameters['SQLUserDBDir'] = 'E:\SQLData\UserDb' + $setTargetResourceParameters['SQLUserDBLogDir'] = 'E:\SQLData\UserDbLogs' + + { Set-TargetResource @setTargetResourceParameters } | Should Not Throw + } + It 'Should pass the SetupCredential object to the StartWin32Process function' { $mockStartWin32Process_SetupCredential = { $Credential | Should Not BeNullOrEmpty @@ -3519,28 +3595,30 @@ try { Set-TargetResource @testParameters } | Should Not Throw } + It 'Should throw an error when one or more paths are not resolved to clustered storage' { $badPathParameters = $testParameters.Clone() - + # Pass in a bad path $badPathParameters.SQLUserDBDir = 'C:\MSSQL\' - { Set-TargetResource @badPathParameters } | Should Throw 'Unable to map the specified paths to valid cluster storage. Drives mapped: TempDbData; TempDbLogs; UserLogs' + { Set-TargetResource @badPathParameters } | Should Throw 'Unable to map the specified paths to valid cluster storage. Drives mapped: Backup; SysData; TempDbData; TempDbLogs; UserLogs' } It 'Should properly map paths to clustered disk resources' { - $mockStartWin32ProcessExpectedArgument = $mockStartWin32ProcessExpectedArgumentClusterDefault.Clone() - $mockStartWin32ProcessExpectedArgument += @{ + $mockStartWin32ProcessExpectedArgument += @{ Action = 'InstallFailoverCluster' FailoverClusterIPAddresses = $mockDefaultInstance_FailoverClusterIPAddressParameter_SingleSite - SQLUserDBDir = 'K:\MSSQL\Data' - SQLUserDBLogDir = 'L:\MSSQL\Logs' - SQLTempDBDir = 'M:\MSSQL\TempDb\Data' - SQLTempDBLogDir = 'N:\MSSQL\TempDb\Logs' + InstallSQLDataDir = $mockDynamicSqlDataDirectoryPath + SQLUserDBDir = $mockDynamicSqlUserDatabasePath + SQLUserDBLogDir = $mockDynamicSqlUserDatabaseLogPath + SQLTempDBDir = $mockDynamicSqlTempDatabasePath + SQLTempDBLogDir = $mockDynamicSqlTempDatabaseLogPath + SQLBackupDir = $mockDynamicSqlBackupPath SkipRules = 'Cluster_VerifyForErrors' FailoverClusterNetworkName = $mockDefaultInstance_FailoverClusterNetworkName - FailoverClusterDisks = 'TempDbData; TempDbLogs; UserData; UserLogs' + FailoverClusterDisks = 'Backup; SysData; TempDbData; TempDbLogs; UserData; UserLogs' } { Set-TargetResource @testParameters } | Should Not Throw @@ -3555,11 +3633,13 @@ try Action = 'InstallFailoverCluster' FailoverClusterIPAddresses = 'DEFAULT' FailoverClusterNetworkName = $mockDefaultInstance_FailoverClusterNetworkName - SQLUserDBDir = 'K:\MSSQL\Data' - SQLUserDBLogDir = 'L:\MSSQL\Logs' - SQLTempDBDir = 'M:\MSSQL\TempDb\Data' - SQLTempDBLogDir = 'N:\MSSQL\TempDb\Logs' - FailoverClusterDisks = 'TempDbData; TempDbLogs; UserData; UserLogs' + InstallSQLDataDir = $mockDynamicSqlDataDirectoryPath + SQLUserDBDir = $mockDynamicSqlUserDatabasePath + SQLUserDBLogDir = $mockDynamicSqlUserDatabaseLogPath + SQLTempDBDir = $mockDynamicSqlTempDatabasePath + SQLTempDBLogDir = $mockDynamicSqlTempDatabaseLogPath + SQLBackupDir = $mockDynamicSqlBackupPath + FailoverClusterDisks = 'Backup; SysData; TempDbData; TempDbLogs; UserData; UserLogs' SkipRules = 'Cluster_VerifyForErrors' } @@ -3594,11 +3674,13 @@ try $mockStartWin32ProcessExpectedArgument += @{ FailoverClusterIPAddresses = $mockDefaultInstance_FailoverClusterIPAddressParameter_SingleSite FailoverClusterNetworkName = $mockDefaultInstance_FailoverClusterNetworkName - SQLUserDBDir = 'K:\MSSQL\Data' - SQLUserDBLogDir = 'L:\MSSQL\Logs' - SQLTempDBDir = 'M:\MSSQL\TempDb\Data' - SQLTempDBLogDir = 'N:\MSSQL\TempDb\Logs' - FailoverClusterDisks = 'TempDbData; TempDbLogs; UserData; UserLogs' + InstallSQLDataDir = $mockDynamicSqlDataDirectoryPath + SQLUserDBDir = $mockDynamicSqlUserDatabasePath + SQLUserDBLogDir = $mockDynamicSqlUserDatabaseLogPath + SQLTempDBDir = $mockDynamicSqlTempDatabasePath + SQLTempDBLogDir = $mockDynamicSqlTempDatabaseLogPath + SQLBackupDir = $mockDynamicSqlBackupPath + FailoverClusterDisks = 'Backup; SysData; TempDbData; TempDbLogs; UserData; UserLogs' SkipRules = 'Cluster_VerifyForErrors' Action = 'InstallFailoverCluster' } @@ -3617,11 +3699,13 @@ try $mockStartWin32ProcessExpectedArgument += @{ FailoverClusterIPAddresses = $mockDefaultInstance_FailoverClusterIPAddressParameter_MultiSite FailoverClusterNetworkName = $mockDefaultInstance_FailoverClusterNetworkName - SQLUserDBDir = 'K:\MSSQL\Data' - SQLUserDBLogDir = 'L:\MSSQL\Logs' - SQLTempDBDir = 'M:\MSSQL\TempDb\Data' - SQLTempDBLogDir = 'N:\MSSQL\TempDb\Logs' - FailoverClusterDisks = 'TempDbData; TempDbLogs; UserData; UserLogs' + InstallSQLDataDir = $mockDynamicSqlDataDirectoryPath + SQLUserDBDir = $mockDynamicSqlUserDatabasePath + SQLUserDBLogDir = $mockDynamicSqlUserDatabaseLogPath + SQLTempDBDir = $mockDynamicSqlTempDatabasePath + SQLTempDBLogDir = $mockDynamicSqlTempDatabaseLogPath + SQLBackupDir = $mockDynamicSqlBackupPath + FailoverClusterDisks = 'Backup; SysData; TempDbData; TempDbLogs; UserData; UserLogs' SkipRules = 'Cluster_VerifyForErrors' Action = 'InstallFailoverCluster' } @@ -3631,12 +3715,14 @@ try It 'Should pass proper parameters to setup when Cluster Shared volumes are specified' { $csvTestParameters = $testParameters.Clone() - + + $csvTestParameters['InstallSQLDataDir'] = $mockCSVClusterDiskMap['SysData'].Path $csvTestParameters['SQLUserDBDir'] = $mockCSVClusterDiskMap['UserData'].Path $csvTestParameters['SQLUserDBLogDir'] = $mockCSVClusterDiskMap['UserLogs'].Path $csvTestParameters['SQLTempDBDir'] = $mockCSVClusterDiskMap['TempDBData'].Path $csvTestParameters['SQLTempDBLogDir'] = $mockCSVClusterDiskMap['TempDBLogs'].Path - + $csvTestParameters['SQLBackupDir'] = $mockCSVClusterDiskMap['Backup'].Path + $mockStartWin32ProcessExpectedArgument = @{ IAcceptSQLServerLicenseTerms = 'True' SkipRules = 'Cluster_VerifyForErrors' @@ -3645,14 +3731,16 @@ try Action = 'InstallFailoverCluster' InstanceName = 'MSSQLSERVER' Features = 'SQLEngine' - FailoverClusterDisks = "$($mockCSVClusterDiskMap['UserData'].Name); $($mockCSVClusterDiskMap['UserLogs'].Name); $($mockCSVClusterDiskMap['TempDBData'].Name); $($mockCSVClusterDiskMap['TempDBLogs'].Name)" + FailoverClusterDisks = "$($mockCSVClusterDiskMap['Backup'].Name); $($mockCSVClusterDiskMap['UserData'].Name); $($mockCSVClusterDiskMap['UserLogs'].Name); $($mockCSVClusterDiskMap['SysData'].Name); $($mockCSVClusterDiskMap['TempDBData'].Name); $($mockCSVClusterDiskMap['TempDBLogs'].Name)" FailoverClusterIPAddresses = $mockDefaultInstance_FailoverClusterIPAddressParameter_SingleSite FailoverClusterGroup = 'SQL Server (MSSQLSERVER)' FailoverClusterNetworkName = $mockDefaultInstance_FailoverClusterNetworkName + InstallSQLDataDir = $mockCSVClusterDiskMap['SysData'].Path SQLUserDBDir = $mockCSVClusterDiskMap['UserData'].Path SQLUserDBLogDir = $mockCSVClusterDiskMap['UserLogs'].Path SQLTempDBDir = $mockCSVClusterDiskMap['TempDBData'].Path SQLTempDBLogDir = $mockCSVClusterDiskMap['TempDBLogs'].Path + SQLBackupDir = $mockCSVClusterDiskMap['Backup'].Path } { Set-TargetResource @csvTestParameters } | Should Not Throw @@ -3660,13 +3748,14 @@ try It 'Should pass proper parameters to setup when Cluster Shared volumes are specified and are the same for one or more parameter values' { $csvTestParameters = $testParameters.Clone() - + + $csvTestParameters['InstallSQLDataDir'] = $mockCSVClusterDiskMap['UserData'].Path + '\Data' $csvTestParameters['SQLUserDBDir'] = $mockCSVClusterDiskMap['UserData'].Path + '\Data' $csvTestParameters['SQLUserDBLogDir'] = $mockCSVClusterDiskMap['UserData'].Path + '\Logs' $csvTestParameters['SQLTempDBDir'] = $mockCSVClusterDiskMap['UserData'].Path + '\TEMPDB' $csvTestParameters['SQLTempDBLogDir'] = $mockCSVClusterDiskMap['UserData'].Path + '\TEMPDBLOG' $csvTestParameters['SQLBackupDir'] = $mockCSVClusterDiskMap['Backup'].Path + '\Backup' - + $mockStartWin32ProcessExpectedArgument = @{ IAcceptSQLServerLicenseTerms = 'True' SkipRules = 'Cluster_VerifyForErrors' @@ -3679,6 +3768,7 @@ try FailoverClusterIPAddresses = $mockDefaultInstance_FailoverClusterIPAddressParameter_SingleSite FailoverClusterGroup = 'SQL Server (MSSQLSERVER)' FailoverClusterNetworkName = $mockDefaultInstance_FailoverClusterNetworkName + InstallSQLDataDir = "$($mockCSVClusterDiskMap['UserData'].Path)\Data" SQLUserDBDir = "$($mockCSVClusterDiskMap['UserData'].Path)\Data" SQLUserDBLogDir = "$($mockCSVClusterDiskMap['UserData'].Path)\Logs" SQLTempDBDir = "$($mockCSVClusterDiskMap['UserData'].Path)\TEMPDB" @@ -3785,8 +3875,14 @@ try Context "When SQL Server version is $mockSqlMajorVersion and the system is not in the desired state and the action is CompleteFailoverCluster." { BeforeEach { - $testParameters = $mockDefaultClusterParameters.Clone() + $mockDynamicSqlDataDirectoryPath = $mockSqlDataDirectoryPath + $mockDynamicSqlUserDatabasePath = $mockSqlUserDatabasePath + $mockDynamicSqlUserDatabaseLogPath = $mockSqlUserDatabaseLogPath + $mockDynamicSqlTempDatabasePath = $mockSqlTempDatabasePath + $mockDynamicSqlTempDatabaseLogPath = $mockSqlTempDatabaseLogPath + $mockDynamicSqlBackupPath = $mockSqlBackupPath + $testParameters = $mockDefaultClusterParameters.Clone() $testParameters += @{ InstanceName = 'MSSQLSERVER' SourcePath = $mockSourcePath @@ -3794,6 +3890,14 @@ try FailoverClusterGroupName = 'SQL Server (MSSQLSERVER)' FailoverClusterNetworkName = $mockDefaultInstance_FailoverClusterNetworkName FailoverClusterIPAddress = $mockDefaultInstance_FailoverClusterIPAddress + + # Ensure we use "clustered" disks for our paths + InstallSQLDataDir = $mockDynamicSqlDataDirectoryPath + SQLUserDBDir = $mockDynamicSqlUserDatabasePath + SQLUserDBLogDir = $mockDynamicSqlUserDatabaseLogPath + SQLTempDbDir = $mockDynamicSqlTempDatabasePath + SQLTempDbLogDir = $mockDynamicSqlTempDatabaseLogPath + SQLBackupDir = $mockDynamicSqlBackupPath } } @@ -3835,7 +3939,7 @@ try # Pass in a bad path $badPathParameters.SQLUserDBDir = 'C:\MSSQL\' - { Set-TargetResource @badPathParameters } | Should Throw 'Unable to map the specified paths to valid cluster storage. Drives mapped: TempDbData; TempDbLogs; UserLogs' + { Set-TargetResource @badPathParameters } | Should Throw 'Unable to map the specified paths to valid cluster storage. Drives mapped: Backup; SysData; TempDbData; TempDbLogs; UserLogs' } It 'Should properly map paths to clustered disk resources' { @@ -3844,13 +3948,15 @@ try $mockStartWin32ProcessExpectedArgument += @{ Action = 'CompleteFailoverCluster' FailoverClusterIPAddresses = $mockDefaultInstance_FailoverClusterIPAddressParameter_SingleSite - SQLUserDBDir = 'K:\MSSQL\Data' - SQLUserDBLogDir = 'L:\MSSQL\Logs' - SQLTempDBDir = 'M:\MSSQL\TempDb\Data' - SQLTempDBLogDir = 'N:\MSSQL\TempDb\Logs' + InstallSQLDataDir = $mockDynamicSqlDataDirectoryPath + SQLUserDBDir = $mockDynamicSqlUserDatabasePath + SQLUserDBLogDir = $mockDynamicSqlUserDatabaseLogPath + SQLTempDBDir = $mockDynamicSqlTempDatabasePath + SQLTempDBLogDir = $mockDynamicSqlTempDatabaseLogPath + SQLBackupDir = $mockDynamicSqlBackupPath SkipRules = 'Cluster_VerifyForErrors' FailoverClusterNetworkName = $mockDefaultInstance_FailoverClusterNetworkName - FailoverClusterDisks = 'TempDbData; TempDbLogs; UserData; UserLogs' + FailoverClusterDisks = 'Backup; SysData; TempDbData; TempDbLogs; UserData; UserLogs' } { Set-TargetResource @testParameters } | Should Not Throw @@ -3865,11 +3971,13 @@ try Action = 'CompleteFailoverCluster' FailoverClusterIPAddresses = 'DEFAULT' FailoverClusterNetworkName = $mockDefaultInstance_FailoverClusterNetworkName - SQLUserDBDir = 'K:\MSSQL\Data' - SQLUserDBLogDir = 'L:\MSSQL\Logs' - SQLTempDBDir = 'M:\MSSQL\TempDb\Data' - SQLTempDBLogDir = 'N:\MSSQL\TempDb\Logs' - FailoverClusterDisks = 'TempDbData; TempDbLogs; UserData; UserLogs' + InstallSQLDataDir = $mockDynamicSqlDataDirectoryPath + SQLUserDBDir = $mockDynamicSqlUserDatabasePath + SQLUserDBLogDir = $mockDynamicSqlUserDatabaseLogPath + SQLTempDBDir = $mockDynamicSqlTempDatabasePath + SQLTempDBLogDir = $mockDynamicSqlTempDatabaseLogPath + SQLBackupDir = $mockDynamicSqlBackupPath + FailoverClusterDisks = 'Backup; SysData; TempDbData; TempDbLogs; UserData; UserLogs' SkipRules = 'Cluster_VerifyForErrors' } @@ -3904,11 +4012,13 @@ try $mockStartWin32ProcessExpectedArgument += @{ FailoverClusterIPAddresses = $mockDefaultInstance_FailoverClusterIPAddressParameter_SingleSite FailoverClusterNetworkName = $mockDefaultInstance_FailoverClusterNetworkName - SQLUserDBDir = 'K:\MSSQL\Data' - SQLUserDBLogDir = 'L:\MSSQL\Logs' - SQLTempDBDir = 'M:\MSSQL\TempDb\Data' - SQLTempDBLogDir = 'N:\MSSQL\TempDb\Logs' - FailoverClusterDisks = 'TempDbData; TempDbLogs; UserData; UserLogs' + InstallSQLDataDir = $mockDynamicSqlDataDirectoryPath + SQLUserDBDir = $mockDynamicSqlUserDatabasePath + SQLUserDBLogDir = $mockDynamicSqlUserDatabaseLogPath + SQLTempDBDir = $mockDynamicSqlTempDatabasePath + SQLTempDBLogDir = $mockDynamicSqlTempDatabaseLogPath + SQLBackupDir = $mockDynamicSqlBackupPath + FailoverClusterDisks = 'Backup; SysData; TempDbData; TempDbLogs; UserData; UserLogs' SkipRules = 'Cluster_VerifyForErrors' Action = 'CompleteFailoverCluster' } @@ -3927,11 +4037,13 @@ try $mockStartWin32ProcessExpectedArgument += @{ FailoverClusterIPAddresses = $mockDefaultInstance_FailoverClusterIPAddressParameter_MultiSite FailoverClusterNetworkName = $mockDefaultInstance_FailoverClusterNetworkName - SQLUserDBDir = 'K:\MSSQL\Data' - SQLUserDBLogDir = 'L:\MSSQL\Logs' - SQLTempDBDir = 'M:\MSSQL\TempDb\Data' - SQLTempDBLogDir = 'N:\MSSQL\TempDb\Logs' - FailoverClusterDisks = 'TempDbData; TempDbLogs; UserData; UserLogs' + InstallSQLDataDir = $mockDynamicSqlDataDirectoryPath + SQLUserDBDir = $mockDynamicSqlUserDatabasePath + SQLUserDBLogDir = $mockDynamicSqlUserDatabaseLogPath + SQLTempDBDir = $mockDynamicSqlTempDatabasePath + SQLTempDBLogDir = $mockDynamicSqlTempDatabaseLogPath + SQLBackupDir = $mockDynamicSqlBackupPath + FailoverClusterDisks = 'Backup; SysData; TempDbData; TempDbLogs; UserData; UserLogs' SkipRules = 'Cluster_VerifyForErrors' Action = 'CompleteFailoverCluster' } @@ -3949,14 +4061,16 @@ try Action = 'CompleteFailoverCluster' InstanceName = 'MSSQLSERVER' Features = 'SQLEngine' - FailoverClusterDisks = 'TempDbData; TempDbLogs; UserData; UserLogs' + FailoverClusterDisks = 'Backup; SysData; TempDbData; TempDbLogs; UserData; UserLogs' FailoverClusterIPAddresses = $mockDefaultInstance_FailoverClusterIPAddressParameter_SingleSite FailoverClusterGroup = 'SQL Server (MSSQLSERVER)' FailoverClusterNetworkName = $mockDefaultInstance_FailoverClusterNetworkName - SQLUserDBDir = 'K:\MSSQL\Data' - SQLUserDBLogDir = 'L:\MSSQL\Logs' - SQLTempDBDir = 'M:\MSSQL\TempDb\Data' - SQLTempDBLogDir = 'N:\MSSQL\TempDb\Logs' + InstallSQLDataDir = $mockDynamicSqlDataDirectoryPath + SQLUserDBDir = $mockDynamicSqlUserDatabasePath + SQLUserDBLogDir = $mockDynamicSqlUserDatabaseLogPath + SQLTempDBDir = $mockDynamicSqlTempDatabasePath + SQLTempDBLogDir = $mockDynamicSqlTempDatabaseLogPath + SQLBackupDir = $mockDynamicSqlBackupPath } { Set-TargetResource @testParameters } | Should Not Throw