From 1026d201cf39bb1295b962292c05a70cc84b850c Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Thu, 24 Nov 2022 08:39:25 +0100 Subject: [PATCH] SqlServerDsc: Add public commands for setup actions (#1784) - SqlServerDsc - The following private functions were added to the module (see comment-based help for more information): - `Assert-ElevatedUser` - `Assert-RequiredCommandParameter` - `Test-IsNumericType` - `Assert-SetupActionProperties` - `Invoke-SetupAction` - The following public functions were added to the module (see comment-based help for more information): - `Install-SqlDscServer` - `Uninstall-SqlDscServer` - `Add-SqlDscNode` - `Remove-SqlDscNode` - `Repair-SqlDscServer` - `Complete-SqlDscImage` - `Complete-SqlDscFailoverCluster` - `Initialize-SqlDscRebuildDatabase` --- .vscode/settings.json | 2 +- CHANGELOG.md | 21 + .../SqlServerDsc.Common.psm1 | 1 + source/Private/Assert-ElevatedUser.ps1 | 45 + .../Assert-RequiredCommandParameter.ps1 | 93 + .../Private/Assert-SetupActionProperties.ps1 | 188 + source/Private/Invoke-SetupAction.ps1 | 1400 ++++++ source/Private/Test-IsNumericType.ps1 | 50 + .../Test-ServiceAccountRequirePassword.ps1 | 58 + source/Public/Add-SqlDscNode.ps1 | 165 + .../Public/Complete-SqlDscFailoverCluster.ps1 | 196 + source/Public/Complete-SqlDscImage.ps1 | 241 + .../Initialize-SqlDscRebuildDatabase.ps1 | 104 + source/Public/Install-SqlDscServer.ps1 | 832 ++++ source/Public/Remove-SqlDscNode.ps1 | 62 + source/Public/Repair-SqlDscServer.ps1 | 124 + source/Public/Uninstall-SqlDscServer.ps1 | 96 + source/en-US/SqlServerDsc.strings.psd1 | 38 +- .../Private/Assert-ElevatedUser.Tests.ps1 | 114 + .../Assert-RequiredCommandParameter.Tests.ps1 | 107 + .../Assert-SetupActionProperties.Tests.ps1 | 538 +++ .../Unit/Private/Invoke-SetupAction.Tests.ps1 | 4191 +++++++++++++++++ .../Unit/Private/Test-IsNumericType.Tests.ps1 | 89 + ...st-ServiceAccountRequirePassword.Tests.ps1 | 85 + tests/Unit/Public/Add-SqlDscNode.Tests.ps1 | 305 ++ .../Complete-SqlDscFailoverCluster.Tests.ps1 | 357 ++ .../Public/Complete-SqlDscImage.Tests.ps1 | 380 ++ ...Initialize-SqlDscRebuildDatabase.Tests.ps1 | 214 + .../Public/Install-SqlDscServer.Tests.ps1 | 2576 ++++++++++ tests/Unit/Public/Remove-SqlDscNode.Tests.ps1 | 170 + .../Unit/Public/Repair-SqlDscServer.Tests.ps1 | 225 + .../Public/Uninstall-SqlDscServer.Tests.ps1 | 137 + 32 files changed, 13196 insertions(+), 8 deletions(-) create mode 100644 source/Private/Assert-ElevatedUser.ps1 create mode 100644 source/Private/Assert-RequiredCommandParameter.ps1 create mode 100644 source/Private/Assert-SetupActionProperties.ps1 create mode 100644 source/Private/Invoke-SetupAction.ps1 create mode 100644 source/Private/Test-IsNumericType.ps1 create mode 100644 source/Private/Test-ServiceAccountRequirePassword.ps1 create mode 100644 source/Public/Add-SqlDscNode.ps1 create mode 100644 source/Public/Complete-SqlDscFailoverCluster.ps1 create mode 100644 source/Public/Complete-SqlDscImage.ps1 create mode 100644 source/Public/Initialize-SqlDscRebuildDatabase.ps1 create mode 100644 source/Public/Install-SqlDscServer.ps1 create mode 100644 source/Public/Remove-SqlDscNode.ps1 create mode 100644 source/Public/Repair-SqlDscServer.ps1 create mode 100644 source/Public/Uninstall-SqlDscServer.ps1 create mode 100644 tests/Unit/Private/Assert-ElevatedUser.Tests.ps1 create mode 100644 tests/Unit/Private/Assert-RequiredCommandParameter.Tests.ps1 create mode 100644 tests/Unit/Private/Assert-SetupActionProperties.Tests.ps1 create mode 100644 tests/Unit/Private/Invoke-SetupAction.Tests.ps1 create mode 100644 tests/Unit/Private/Test-IsNumericType.Tests.ps1 create mode 100644 tests/Unit/Private/Test-ServiceAccountRequirePassword.Tests.ps1 create mode 100644 tests/Unit/Public/Add-SqlDscNode.Tests.ps1 create mode 100644 tests/Unit/Public/Complete-SqlDscFailoverCluster.Tests.ps1 create mode 100644 tests/Unit/Public/Complete-SqlDscImage.Tests.ps1 create mode 100644 tests/Unit/Public/Initialize-SqlDscRebuildDatabase.Tests.ps1 create mode 100644 tests/Unit/Public/Install-SqlDscServer.Tests.ps1 create mode 100644 tests/Unit/Public/Remove-SqlDscNode.Tests.ps1 create mode 100644 tests/Unit/Public/Repair-SqlDscServer.Tests.ps1 create mode 100644 tests/Unit/Public/Uninstall-SqlDscServer.Tests.ps1 diff --git a/.vscode/settings.json b/.vscode/settings.json index 6b806e216..ef03f8461 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -13,7 +13,7 @@ "powershell.codeFormatting.alignPropertyValuePairs": true, "powershell.codeFormatting.useConstantStrings": true, "powershell.developer.bundledModulesPath": "${cwd}/output/RequiredModules", - "powershell.scriptAnalysis.settingsPath": ".vscode\\analyzersettings.psd1", + "powershell.scriptAnalysis.settingsPath": ".vscode/analyzersettings.psd1", "powershell.scriptAnalysis.enable": true, "files.trimTrailingWhitespace": true, "files.trimFinalNewlines": true, diff --git a/CHANGELOG.md b/CHANGELOG.md index ebacdfcaf..f32eeef1b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- SqlServerDsc + - The following private functions were added to the module (see comment-based + help for more information): + - `Assert-ElevatedUser` + - `Assert-RequiredCommandParameter` + - `Test-IsNumericType` + - `Assert-SetupActionProperties` + - `Invoke-SetupAction` + - The following public functions were added to the module (see comment-based + help for more information): + - `Install-SqlDscServer` + - `Uninstall-SqlDscServer` + - `Add-SqlDscNode` + - `Remove-SqlDscNode` + - `Repair-SqlDscServer` + - `Complete-SqlDscImage` + - `Complete-SqlDscFailoverCluster` + - `Initialize-SqlDscRebuildDatabase` + ### Changed - SqlServerDsc diff --git a/source/Modules/SqlServerDsc.Common/SqlServerDsc.Common.psm1 b/source/Modules/SqlServerDsc.Common/SqlServerDsc.Common.psm1 index d170b445d..e3e1868df 100644 --- a/source/Modules/SqlServerDsc.Common/SqlServerDsc.Common.psm1 +++ b/source/Modules/SqlServerDsc.Common/SqlServerDsc.Common.psm1 @@ -1397,6 +1397,7 @@ function Restart-ReportingServicesService #> function Invoke-Query { + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('UseSyntacticallyCorrectExamples', '', Justification = 'Because the rule does not yet support parsing the code when the output type is not available. The ScriptAnalyzer rule UseSyntacticallyCorrectExamples will always error in the editor due to https://github.com/indented-automation/Indented.ScriptAnalyzerRules/issues/8.')] [CmdletBinding(DefaultParameterSetName = 'SqlServer')] param ( diff --git a/source/Private/Assert-ElevatedUser.ps1 b/source/Private/Assert-ElevatedUser.ps1 new file mode 100644 index 000000000..396cc74b6 --- /dev/null +++ b/source/Private/Assert-ElevatedUser.ps1 @@ -0,0 +1,45 @@ +<# + .SYNOPSIS + Assert that the user has elevated the PowerShell session. + + .DESCRIPTION + Assert that the user has elevated the PowerShell session. + + .EXAMPLE + Assert-ElevatedUser + + Throws an exception if the user has not elevated the PowerShell session. + + .OUTPUTS + None. +#> +function Assert-ElevatedUser +{ + [CmdletBinding()] + param () + + $isElevated = $false + + if ($IsMacOS -or $IsLinux) + { + $isElevated = (id -u) -eq 0 + } + else + { + [Security.Principal.WindowsPrincipal] $user = [Security.Principal.WindowsIdentity]::GetCurrent() + + $isElevated = $user.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator) + } + + if (-not $isElevated) + { + $PSCmdlet.ThrowTerminatingError( + [System.Management.Automation.ErrorRecord]::new( + $script:localizedData.IsElevated_UserNotElevated, + 'AEU0001', + [System.Management.Automation.ErrorCategory]::InvalidOperation, + 'Command parameters' + ) + ) + } +} diff --git a/source/Private/Assert-RequiredCommandParameter.ps1 b/source/Private/Assert-RequiredCommandParameter.ps1 new file mode 100644 index 000000000..95c11d966 --- /dev/null +++ b/source/Private/Assert-RequiredCommandParameter.ps1 @@ -0,0 +1,93 @@ +<# + .SYNOPSIS + Assert that required parameters has been specified. + + .DESCRIPTION + Assert that required parameters has been specified, and throws an exception if not. + + .PARAMETER BoundParameter + A hashtable containing the parameters to evaluate. Normally this is set to + $PSBoundParameters. + + .PARAMETER RequiredParameter + One or more parameter names that is required to have been specified. + + .PARAMETER IfParameterPresent + One or more parameter names that if specified will trigger the evaluation. + If neither of the parameter names has been specified the evaluation of required + parameters are not made. + + .EXAMPLE + Assert-RequiredCommandParameter -BoundParameter $PSBoundParameters -RequiredParameter @('PBStartPortRange', 'PBEndPortRange') + + Throws an exception if either of the two parameters are not specified. + + .EXAMPLE + Assert-RequiredCommandParameter -BoundParameter $PSBoundParameters -RequiredParameter @('Property2', 'Property3') -IfParameterPresent @('Property1') + + Throws an exception if the parameter 'Property1' is specified and one of the required parameters are not. + + .OUTPUTS + None. + + .NOTES + This command should probably be a parmeter set of the command Assert-BoundParameter + in the module DscResource.Common, instead of being a separate command. +#> +function Assert-RequiredCommandParameter +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [System.Collections.Hashtable] + $BoundParameter, + + [Parameter(Mandatory = $true)] + [System.String[]] + $RequiredParameter, + + [Parameter()] + [System.String[]] + $IfParameterPresent + ) + + $evaluateRequiredParameter = $true + + if ($PSBoundParameters.ContainsKey('IfParameterPresent')) + { + $hasIfParameterPresent = $BoundParameter.Keys.Where( { $_ -in $IfParameterPresent } ) + + if (-not $hasIfParameterPresent) + { + $evaluateRequiredParameter = $false + } + } + + if ($evaluateRequiredParameter) + { + foreach ($parameter in $RequiredParameter) + { + if ($parameter -notin $BoundParameter.Keys) + { + $errorMessage = if ($PSBoundParameters.ContainsKey('IfParameterPresent')) + { + $script:localizedData.RequiredCommandParameter_SpecificParametersMustAllBeSetWhenParameterExist -f ($RequiredParameter -join ''', '''), ($IfParameterPresent -join ''', ''') + } + else + { + $script:localizedData.RequiredCommandParameter_SpecificParametersMustAllBeSet -f ($RequiredParameter -join ''', ''') + } + + $PSCmdlet.ThrowTerminatingError( + [System.Management.Automation.ErrorRecord]::new( + $errorMessage, + 'ARCP0001', # cspell: disable-line + [System.Management.Automation.ErrorCategory]::InvalidOperation, + 'Command parameters' + ) + ) + } + } + } +} diff --git a/source/Private/Assert-SetupActionProperties.ps1 b/source/Private/Assert-SetupActionProperties.ps1 new file mode 100644 index 000000000..1f6f7e170 --- /dev/null +++ b/source/Private/Assert-SetupActionProperties.ps1 @@ -0,0 +1,188 @@ +<# + .SYNOPSIS + Assert that the bound parameters are set as required + + .DESCRIPTION + Assert that required parameters has been specified, and throws an exception if not. + + .PARAMETER Property + A hashtable containing the parameters to evaluate. Normally this is set to + $PSBoundParameters. + + .PARAMETER SetupAction + A string value representing the setup action that is gonna be executed. + + .EXAMPLE + Assert-SetupActionProperties -Property $PSBoundParameters -SetupAction 'Install' + + Throws an exception if the bound parameters are not in the correct state. + + .OUTPUTS + None. + + .NOTES + This function is used by the command Invoke-SetupAction to verify that + the bound parameters are in the required state. +#> +function Assert-SetupActionProperties +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [System.Collections.Hashtable] + $Property, + + [Parameter(Mandatory = $true)] + [System.String] + $SetupAction + ) + + # If one of the properties PBStartPortRange and PBEndPortRange are specified, then both must be specified. + $assertParameters = @('PBStartPortRange', 'PBEndPortRange') + + $assertRequiredCommandParameterParameters = @{ + BoundParameter = $Property + RequiredParameter = $assertParameters + IfParameterPresent = $assertParameters + } + + Assert-RequiredCommandParameter @assertRequiredCommandParameterParameters + + # The parameter UseSqlRecommendedMemoryLimits is mutually exclusive to SqlMinMemory and SqlMaxMemory. + Assert-BoundParameter -BoundParameterList $Property -MutuallyExclusiveList1 @( + 'UseSqlRecommendedMemoryLimits' + ) -MutuallyExclusiveList2 @( + 'SqlMinMemory' + 'SqlMaxMemory' + ) + + # If Role is set to SPI_AS_NewFarm then the specific parameters are required. + if ($Property.ContainsKey('Role') -and $Property.Role -eq 'SPI_AS_NewFarm') + { + Assert-RequiredCommandParameter -BoundParameter $Property -RequiredParameter @( + 'FarmAccount' + 'FarmPassword' + 'Passphrase' + 'FarmAdminiPort' # cspell: disable-line + ) + } + + # If the parameter SecurityMode is set to 'SQL' then the parameter SAPwd is required. + if ($Property.ContainsKey('SecurityMode') -and $Property.SecurityMode -eq 'SQL') + { + Assert-RequiredCommandParameter -BoundParameter $Property -RequiredParameter @('SAPwd') + } + + # If the parameter FileStreamLevel is set and is greater or equal to 2 then the parameter FileStreamShareName is required. + if ($Property.ContainsKey('FileStreamLevel') -and $Property.FileStreamLevel -ge 2) + { + Assert-RequiredCommandParameter -BoundParameter $Property -RequiredParameter @('FileStreamShareName') + } + + # If a *SvcAccount is specified then the accompanying *SvcPassword must be set unless it is a (global) managed service account, virtual account, or a built-in account. + $accountProperty = @( + 'PBEngSvcAccount' + 'PBDMSSvcAccount' # cSpell: disable-line + 'AgtSvcAccount' + 'ASSvcAccount' + 'FarmAccount' + 'SqlSvcAccount' + 'ISSvcAccount' + 'RSSvcAccount' + ) + + foreach ($currentAccountProperty in $accountProperty) + { + if ($currentAccountProperty -in $Property.Keys) + { + # If not (global) managed service account, virtual account, or a built-in account. + if ((Test-ServiceAccountRequirePassword -Name $Property.$currentAccountProperty)) + { + $assertPropertyName = $currentAccountProperty -replace 'Account', 'Password' + + Assert-RequiredCommandParameter -BoundParameter $Property -RequiredParameter $assertPropertyName + } + } + } + + # If feature ARC is specified then the all the Azure* parameters must be set (except AzureArcProxy). + if ($Property.ContainsKey('Features') -and $Property.Features -contains 'ARC') + { + Assert-RequiredCommandParameter -BoundParameter $Property -RequiredParameter @( + 'AzureSubscriptionId' + 'AzureResourceGroup' + 'AzureRegion' + 'AzureTenantId' + 'AzureServicePrincipal' + 'AzureServicePrincipalSecret' + ) + } + + # If feature is SQLENGINE, then for specified setup actions the parameter AgtSvcAccount is mandatory. + if ($SetupAction -in ('Install', 'CompleteImage', 'InstallFailoverCluster', 'PrepareFailoverCluster', 'AddNode')) + { + if ($Property.ContainsKey('Features') -and $Property.Features -contains 'SQLENGINE') + { + Assert-RequiredCommandParameter -BoundParameter $Property -RequiredParameter @('AgtSvcAccount') + } + } + + if ($SetupAction -in ('InstallFailoverCluster', 'PrepareFailoverCluster', 'AddNode')) + { + # The parameter ASSvcAccount is mandatory if feature AS is installed and setup action is InstallFailoverCluster, PrepareFailoverCluster, or AddNode. + if ($Property.ContainsKey('Features') -and $Property.Features -contains 'AS') + { + Assert-RequiredCommandParameter -BoundParameter $Property -RequiredParameter @('ASSvcAccount') + } + + # The parameter SqlSvcAccount is mandatory if feature SQLENGINE is installed and setup action is InstallFailoverCluster, PrepareFailoverCluster, or AddNode. + if ($Property.ContainsKey('Features') -and $Property.Features -contains 'SQLENGINE') + { + Assert-RequiredCommandParameter -BoundParameter $Property -RequiredParameter @('SqlSvcAccount') + } + + # The parameter ISSvcAccount is mandatory if feature IS is installed and setup action is InstallFailoverCluster, PrepareFailoverCluster, or AddNode. + if ($Property.ContainsKey('Features') -and $Property.Features -contains 'IS') + { + Assert-RequiredCommandParameter -BoundParameter $Property -RequiredParameter @('ISSvcAccount') + } + + if ($Property.ContainsKey('Features') -and $Property.Features -contains 'RS') + { + Assert-RequiredCommandParameter -BoundParameter $Property -RequiredParameter @('RSSvcAccount') + } + } + + # The ASServerMode value PowerPivot is not allowed when parameter set is InstallFailoverCluster or CompleteFailoverCluster. + if ($SetupAction -in ('InstallFailoverCluster', 'CompleteFailoverCluster')) + { + if ($Property.ContainsKey('ASServerMode') -and $Property.ASServerMode -eq 'PowerPivot') + { + $PSCmdlet.ThrowTerminatingError( + [System.Management.Automation.ErrorRecord]::new( + ($script:localizedData.InstallSqlServerProperties_ASServerModeInvalidValue -f $SetupAction), + 'ASAP0001', # cSpell: disable-line + [System.Management.Automation.ErrorCategory]::InvalidOperation, + 'Command parameters' + ) + ) + } + } + + # The ASServerMode value PowerPivot is not allowed when parameter set is InstallFailoverCluster or CompleteFailoverCluster. + if ($SetupAction -in ('AddNode')) + { + if ($Property.ContainsKey('RsInstallMode') -and $Property.RsInstallMode -ne 'FilesOnlyMode') + { + $PSCmdlet.ThrowTerminatingError( + [System.Management.Automation.ErrorRecord]::new( + ($script:localizedData.InstallSqlServerProperties_RsInstallModeInvalidValue -f $SetupAction), + 'ASAP0002', # cSpell: disable-line + [System.Management.Automation.ErrorCategory]::InvalidOperation, + 'Command parameters' + ) + ) + } + } +} diff --git a/source/Private/Invoke-SetupAction.ps1 b/source/Private/Invoke-SetupAction.ps1 new file mode 100644 index 000000000..24b3131a1 --- /dev/null +++ b/source/Private/Invoke-SetupAction.ps1 @@ -0,0 +1,1400 @@ +<# + .SYNOPSIS + Executes an setup action using Microsoft SQL Server setup executable. + + .DESCRIPTION + Executes an setup action using Microsoft SQL Server setup executable. + + See the link in the commands help for information on each parameter. The + link points to SQL Server command line setup documentation. + + .PARAMETER Install + Specifies the setup action Install. + + .PARAMETER Uninstall + Specifies the setup action Uninstall. + + .PARAMETER PrepareImage + Specifies the setup action PrepareImage. + + .PARAMETER CompleteImage + Specifies the setup action CompleteImage. + + .PARAMETER Upgrade + Specifies the setup action Upgrade. + + .PARAMETER EditionUpgrade + Specifies the setup action EditionUpgrade. + + .PARAMETER Repair + Specifies the setup action Repair. + + .PARAMETER RebuildDatabase + Specifies the setup action RebuildDatabase. + + .PARAMETER InstallFailoverCluster + Specifies the setup action InstallFailoverCluster. + + .PARAMETER PrepareFailoverCluster + Specifies the setup action PrepareFailoverCluster. + + .PARAMETER CompleteFailoverCluster + Specifies the setup action CompleteFailoverCluster. + + .PARAMETER AddNode + Specifies the setup action AddNode. + + .PARAMETER RemoveNode + Specifies the setup action RemoveNode. + + .PARAMETER ConfigurationFile + Specifies an configuration file to use during SQL Server setup. This + parameter cannot be used together with any of the setup actions, but instead + it is expected that the configuration file specifies what setup action to + run. + + .PARAMETER AcceptLicensingTerms + Required parameter to be able to run unattended install. By specifying this + parameter you acknowledge the acceptance all license terms and notices for + the specified features, the terms and notices that the Microsoft SQL Server + setup executable normally ask for. + + .PARAMETER MediaPath + Specifies the path where to find the SQL Server installation media. On this + path the SQL Server setup executable must be found. + + .LINK + https://docs.microsoft.com/en-us/sql/database-engine/install-windows/install-sql-server-from-the-command-prompt + + .OUTPUTS + None. + + .EXAMPLE + Invoke-SetupAction -Install -AcceptLicensingTerms -InstanceName 'MyInstance' -Features 'SQLENGINE' -SqlSysAdminAccounts @('MyAdminAccount') -MediaPath 'E:\' + + Installs the database engine for the named instance MyInstance. + + .EXAMPLE + Invoke-SetupAction -Install -AcceptLicensingTerms -InstanceName 'MyInstance' -Features 'SQLENGINE','ARC' -SqlSysAdminAccounts @('MyAdminAccount') -MediaPath 'E:\' -AzureSubscriptionId 'MySubscriptionId' -AzureResourceGroup 'MyRG' -AzureRegion 'West-US' -AzureTenantId 'MyTenantId' -AzureServicePrincipal 'MyPrincipalName' -AzureServicePrincipalSecret ('MySecret' | ConvertTo-SecureString -AsPlainText -Force) + + Installs the database engine for the named instance MyInstance and onboard the server to Azure Arc. + + .EXAMPLE + Invoke-SetupAction -Install -AcceptLicensingTerms -MediaPath 'E:\' -AzureSubscriptionId 'MySubscriptionId' -AzureResourceGroup 'MyRG' -AzureRegion 'West-US' -AzureTenantId 'MyTenantId' -AzureServicePrincipal 'MyPrincipalName' -AzureServicePrincipalSecret ('MySecret' | ConvertTo-SecureString -AsPlainText -Force) + + Installs the Azure Arc Agent on the server. + + .EXAMPLE + Invoke-SetupAction -ConfigurationFile 'MySqlConfig.ini' -MediaPath 'E:\' + + Installs SQL Server using the configuration file 'MySqlConfig.ini'. + + .EXAMPLE + Invoke-SetupAction -Uninstall -InstanceName 'MyInstance' -Features 'SQLENGINE' -MediaPath 'E:\' + + Uninstalls the database engine from the named instance MyInstance. + + .EXAMPLE + Invoke-SetupAction -PrepareImage -AcceptLicensingTerms -InstanceName 'MyInstance' -Features 'SQLENGINE' -InstanceId 'MyInstance' -MediaPath 'E:\' + + Prepares the server for using the database engine for an instance named 'MyInstance'. + + .EXAMPLE + Invoke-SetupAction -CompleteImage -AcceptLicensingTerms -MediaPath 'E:\' + + Completes install on a server that was previously prepared (by using prepare image). + + .EXAMPLE + Invoke-SetupAction -Upgrade -AcceptLicensingTerms -InstanceName 'MyInstance' -MediaPath 'E:\' + + Upgrades the instance 'MyInstance' with the SQL Server version that is provided by the media path. + + .EXAMPLE + Invoke-SetupAction -EditionUpgrade -AcceptLicensingTerms -ProductKey 'NewEditionProductKey' -InstanceName 'MyInstance' -MediaPath 'E:\' + + Upgrades the instance 'MyInstance' with the SQL Server edition that is provided by the media path. + + .EXAMPLE + Invoke-SetupAction -Repair -InstanceName 'MyInstance' -Features 'SQLENGINE' -MediaPath 'E:\' + + Repairs the database engine of the instance 'MyInstance'. + + .EXAMPLE + Invoke-SetupAction -RebuildDatabase -InstanceName 'MyInstance' -SqlSysAdminAccounts @('MyAdminAccount') -MediaPath 'E:\' + + Rebuilds the database of the instance 'MyInstance'. + + .EXAMPLE + Invoke-SetupAction -InstallFailoverCluster -AcceptLicensingTerms -InstanceName 'MyInstance' -Features 'SQLENGINE' -InstallSqlDataDir 'D:\MSSQL\Data' -SqlSysAdminAccounts @('MyAdminAccount') -FailoverClusterNetworkName 'TestCluster01A' -FailoverClusterIPAddresses 'IPv4;192.168.0.46;ClusterNetwork1;255.255.255.0' -MediaPath 'E:\' + + Installs the database engine in a failover cluster with the instance name 'MyInstance'. + + .EXAMPLE + Invoke-SetupAction -PrepareFailoverCluster -AcceptLicensingTerms -InstanceName 'MyInstance' -Features 'SQLENGINE' -MediaPath 'E:\' + + Prepares to installs the database engine in a failover cluster with the instance name 'MyInstance'. + + .EXAMPLE + Invoke-SetupAction -CompleteFailoverCluster -InstanceName 'MyInstance' -InstallSqlDataDir 'D:\MSSQL\Data' -SqlSysAdminAccounts @('MyAdminAccount') -FailoverClusterNetworkName 'TestCluster01A' -FailoverClusterIPAddresses 'IPv4;192.168.0.46;ClusterNetwork1;255.255.255.0' -MediaPath 'E:\' + + Completes the install of the database engine in the failover cluster with the instance name 'MyInstance'. + + .EXAMPLE + Invoke-SetupAction -AddNode -AcceptLicensingTerms -InstanceName 'MyInstance' -FailoverClusterIPAddresses 'IPv4;192.168.0.46;ClusterNetwork1;255.255.255.0' -MediaPath 'E:\' + + Adds the node to the failover cluster for the instance 'MyInstance'. + + .EXAMPLE + Invoke-SetupAction -RemoveNode -InstanceName 'MyInstance' -MediaPath 'E:\' + + Removes the node from the failover cluster of the instance 'MyInstance'. + + .NOTES + All parameters has intentionally not been added to this comment-based help + since it would take a lot of effort to keep it up to date. Instead there is + a link in the comment-based help that points to the SQL Server command line + setup documentation which will stay relevant. + + For RebuildDatabase the parameter SAPwd must be set if the instance was + installed with SecurityMode = 'SQL'. +#> +function Invoke-SetupAction +{ + [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')] + [OutputType()] + param + ( + [Parameter(ParameterSetName = 'Install', Mandatory = $true)] + [Parameter(ParameterSetName = 'InstallRole', Mandatory = $true)] + [Parameter(ParameterSetName = 'InstallAzureArcAgent', Mandatory = $true)] + [System.Management.Automation.SwitchParameter] + $Install, + + [Parameter(ParameterSetName = 'Uninstall', Mandatory = $true)] + [System.Management.Automation.SwitchParameter] + $Uninstall, + + [Parameter(ParameterSetName = 'PrepareImage', Mandatory = $true)] + [System.Management.Automation.SwitchParameter] + $PrepareImage, + + [Parameter(ParameterSetName = 'CompleteImage', Mandatory = $true)] + [System.Management.Automation.SwitchParameter] + $CompleteImage, + + [Parameter(ParameterSetName = 'Upgrade', Mandatory = $true)] + [System.Management.Automation.SwitchParameter] + $Upgrade, + + [Parameter(ParameterSetName = 'EditionUpgrade', Mandatory = $true)] + [System.Management.Automation.SwitchParameter] + $EditionUpgrade, + + [Parameter(ParameterSetName = 'Repair', Mandatory = $true)] + [System.Management.Automation.SwitchParameter] + $Repair, + + [Parameter(ParameterSetName = 'RebuildDatabase', Mandatory = $true)] + [System.Management.Automation.SwitchParameter] + $RebuildDatabase, + + [Parameter(ParameterSetName = 'InstallFailoverCluster', Mandatory = $true)] + [System.Management.Automation.SwitchParameter] + $InstallFailoverCluster, + + [Parameter(ParameterSetName = 'PrepareFailoverCluster', Mandatory = $true)] + [System.Management.Automation.SwitchParameter] + $PrepareFailoverCluster, + + [Parameter(ParameterSetName = 'CompleteFailoverCluster', Mandatory = $true)] + [System.Management.Automation.SwitchParameter] + $CompleteFailoverCluster, + + [Parameter(ParameterSetName = 'AddNode', Mandatory = $true)] + [System.Management.Automation.SwitchParameter] + $AddNode, + + [Parameter(ParameterSetName = 'RemoveNode', Mandatory = $true)] + [System.Management.Automation.SwitchParameter] + $RemoveNode, + + [Parameter(ParameterSetName = 'UsingConfigurationFile', Mandatory = $true)] + [ValidateScript({ + if (-not (Test-Path -Path $_)) + { + throw $script:localizedData.Server_ConfigurationFileNotFound + } + + return $true + })] + [System.String] + $ConfigurationFile, + + [Parameter(ParameterSetName = 'Install', Mandatory = $true)] + [Parameter(ParameterSetName = 'InstallRole', Mandatory = $true)] + [Parameter(ParameterSetName = 'InstallAzureArcAgent', Mandatory = $true)] + [Parameter(ParameterSetName = 'PrepareImage', Mandatory = $true)] + [Parameter(ParameterSetName = 'Upgrade', Mandatory = $true)] + [Parameter(ParameterSetName = 'EditionUpgrade', Mandatory = $true)] + [Parameter(ParameterSetName = 'InstallFailoverCluster', Mandatory = $true)] + [Parameter(ParameterSetName = 'PrepareFailoverCluster', Mandatory = $true)] + [Parameter(ParameterSetName = 'AddNode', Mandatory = $true)] + [Parameter(ParameterSetName = 'CompleteImage', Mandatory = $true)] + [System.Management.Automation.SwitchParameter] + $AcceptLicensingTerms, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [System.Management.Automation.SwitchParameter] + $SuppressPrivacyStatementNotice, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'PrepareImage')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [Parameter(ParameterSetName = 'AddNode')] + [System.Management.Automation.SwitchParameter] + $IAcknowledgeEntCalLimits, + + [Parameter(Mandatory = $true)] + [ValidateScript({ + if (-not (Test-Path -Path (Join-Path -Path $_ -ChildPath 'setup.exe'))) + { + throw $script:localizedData.Server_MediaPathNotFound + } + + return $true + })] + [System.String] + $MediaPath, + + [Parameter(ParameterSetName = 'Install', Mandatory = $true)] + [Parameter(ParameterSetName = 'Uninstall', Mandatory = $true)] + [Parameter(ParameterSetName = 'PrepareImage', Mandatory = $true)] + [Parameter(ParameterSetName = 'Upgrade', Mandatory = $true)] + [Parameter(ParameterSetName = 'EditionUpgrade', Mandatory = $true)] + [Parameter(ParameterSetName = 'Repair', Mandatory = $true)] + [Parameter(ParameterSetName = 'RebuildDatabase', Mandatory = $true)] + [Parameter(ParameterSetName = 'InstallFailoverCluster', Mandatory = $true)] + [Parameter(ParameterSetName = 'PrepareFailoverCluster', Mandatory = $true)] + [Parameter(ParameterSetName = 'CompleteFailoverCluster', Mandatory = $true)] + [Parameter(ParameterSetName = 'AddNode', Mandatory = $true)] + [Parameter(ParameterSetName = 'RemoveNode', Mandatory = $true)] + [Parameter(ParameterSetName = 'CompleteImage')] + [Parameter(ParameterSetName = 'InstallRole')] + [System.String] + $InstanceName, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'PrepareImage')] + [Parameter(ParameterSetName = 'CompleteImage')] + [Parameter(ParameterSetName = 'Upgrade')] + [Parameter(ParameterSetName = 'Repair')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [Parameter(ParameterSetName = 'CompleteFailoverCluster')] + [Parameter(ParameterSetName = 'AddNode')] + [System.Management.Automation.SwitchParameter] + $Enu, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'PrepareImage')] + [Parameter(ParameterSetName = 'Upgrade')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [Parameter(ParameterSetName = 'AddNode')] + [System.Management.Automation.SwitchParameter] + $UpdateEnabled, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'PrepareImage')] + [Parameter(ParameterSetName = 'Upgrade')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [Parameter(ParameterSetName = 'AddNode')] + [System.String] + $UpdateSource, + + [Parameter(ParameterSetName = 'Install', Mandatory = $true)] + [Parameter(ParameterSetName = 'PrepareImage', Mandatory = $true)] + [Parameter(ParameterSetName = 'Repair', Mandatory = $true)] + [Parameter(ParameterSetName = 'Uninstall', Mandatory = $true)] + [Parameter(ParameterSetName = 'InstallFailoverCluster', Mandatory = $true)] + [Parameter(ParameterSetName = 'PrepareFailoverCluster', Mandatory = $true)] + [Parameter(ParameterSetName = 'InstallRole')] + [ValidateSet( + 'SQL', + 'SQLEngine', # Part of parent feature SQL + 'Replication', # Part of parent feature SQL + 'FullText', # Part of parent feature SQL + 'DQ', # Part of parent feature SQL + 'PolyBase', # Part of parent feature SQL + 'PolyBaseCore', # Part of parent feature SQL + 'PolyBaseJava', # Part of parent feature SQL + 'AdvancedAnalytics', # Part of parent feature SQL + 'SQL_INST_MR', # Part of parent feature SQL + 'SQL_INST_MPY', # Part of parent feature SQL + 'SQL_INST_JAVA', # Part of parent feature SQL + 'AS', + 'RS', + 'RS_SHP', + 'RS_SHPWFE', # cspell: disable-line + 'DQC', + 'IS', + 'IS_Master', # Part of parent feature IS + 'IS_Worker', # Part of parent feature IS + 'MDS', + 'SQL_SHARED_MPY', + 'SQL_SHARED_MR', + 'Tools', + 'BC', # Part of parent feature Tools + 'Conn', # Part of parent feature Tools + 'DREPLAY_CTLR', # Part of parent feature Tools (cspell: disable-line) + 'DREPLAY_CLT', # Part of parent feature Tools (cspell: disable-line) + 'SNAC_SDK', # Part of parent feature Tools (cspell: disable-line) + 'SDK', # Part of parent feature Tools + 'LocalDB', # Part of parent feature Tools + 'ARC' + )] + [System.String[]] + $Features, + + [Parameter(ParameterSetName = 'InstallRole', Mandatory = $true)] + [ValidateSet( + 'ALLFeatures_WithDefaults', + 'SPI_AS_NewFarm', + 'SPI_AS_ExistingFarm' + )] + [System.String] + $Role, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'PrepareImage')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [System.String] + $InstallSharedDir, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [System.String] + $InstallSharedWowDir, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'PrepareImage')] + [Parameter(ParameterSetName = 'Upgrade')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [System.String] + $InstanceDir, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'PrepareImage', Mandatory = $true)] + [Parameter(ParameterSetName = 'CompleteImage')] + [Parameter(ParameterSetName = 'Upgrade')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [System.String] + $InstanceId, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'PrepareImage')] + [Parameter(ParameterSetName = 'CompleteImage')] + [Parameter(ParameterSetName = 'Repair')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [Parameter(ParameterSetName = 'AddNode')] + [System.String] + $PBEngSvcAccount, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'PrepareImage')] + [Parameter(ParameterSetName = 'CompleteImage')] + [Parameter(ParameterSetName = 'Repair')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [Parameter(ParameterSetName = 'AddNode')] + [System.Security.SecureString] + $PBEngSvcPassword, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'PrepareImage')] + [Parameter(ParameterSetName = 'CompleteImage')] + [Parameter(ParameterSetName = 'Repair')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [Parameter(ParameterSetName = 'AddNode')] + [ValidateSet('Automatic', 'Disabled', 'Manual')] + [System.String] + $PBEngSvcStartupType, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [System.String] + $PBDMSSvcAccount, # cspell: disable-line + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [System.Security.SecureString] + $PBDMSSvcPassword, # cspell: disable-line + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [ValidateSet('Automatic', 'Disabled', 'Manual')] + [System.String] + $PBDMSSvcStartupType, # cspell: disable-line + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'PrepareImage')] + [Parameter(ParameterSetName = 'CompleteImage')] + [Parameter(ParameterSetName = 'Repair')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [Parameter(ParameterSetName = 'AddNode')] + [System.UInt16] + $PBStartPortRange, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'PrepareImage')] + [Parameter(ParameterSetName = 'CompleteImage')] + [Parameter(ParameterSetName = 'Repair')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [Parameter(ParameterSetName = 'AddNode')] + [System.UInt16] + $PBEndPortRange, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'PrepareImage')] + [Parameter(ParameterSetName = 'CompleteImage')] + [Parameter(ParameterSetName = 'Repair')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [Parameter(ParameterSetName = 'AddNode')] + [System.Management.Automation.SwitchParameter] + $PBScaleOut, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'CompleteImage')] + [Parameter(ParameterSetName = 'Upgrade')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [Parameter(ParameterSetName = 'CompleteFailoverCluster')] + [Parameter(ParameterSetName = 'AddNode')] + [Parameter(ParameterSetName = 'EditionUpgrade', Mandatory = $true)] + [System.String] + $ProductKey, # This is argument PID but $PID is reserved variable. + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'CompleteImage')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [Parameter(ParameterSetName = 'AddNode')] + [System.String] + $AgtSvcAccount, + + [Parameter(ParameterSetName = 'UsingConfigurationFile')] + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'CompleteImage')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [Parameter(ParameterSetName = 'AddNode')] + [System.Security.SecureString] + $AgtSvcPassword, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'CompleteImage')] + [ValidateSet('Automatic', 'Disabled', 'Manual')] + [System.String] + $AgtSvcStartupType, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'CompleteFailoverCluster')] + [System.String] + $ASBackupDir, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'CompleteFailoverCluster')] + [System.String] + $ASCollation, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'CompleteFailoverCluster')] + [System.String] + $ASConfigDir, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'CompleteFailoverCluster')] + [System.String] + $ASDataDir, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'CompleteFailoverCluster')] + [System.String] + $ASLogDir, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'CompleteFailoverCluster')] + [System.String] + $ASTempDir, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'CompleteFailoverCluster')] + [ValidateSet('Multidimensional', 'PowerPivot', 'Tabular')] + [System.String] + $ASServerMode, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [Parameter(ParameterSetName = 'AddNode')] + [System.String] + $ASSvcAccount, + + [Parameter(ParameterSetName = 'UsingConfigurationFile')] + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [Parameter(ParameterSetName = 'AddNode')] + [System.Security.SecureString] + $ASSvcPassword, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [ValidateSet('Automatic', 'Disabled', 'Manual')] + [System.String] + $ASSvcStartupType, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'CompleteFailoverCluster')] + [System.String[]] + $ASSysAdminAccounts, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'CompleteFailoverCluster')] + [System.Management.Automation.SwitchParameter] + $ASProviderMSOLAP, + + [Parameter(ParameterSetName = 'InstallRole')] + [System.String] + $FarmAccount, + + [Parameter(ParameterSetName = 'InstallRole')] + [System.Security.SecureString] + $FarmPassword, + + [Parameter(ParameterSetName = 'InstallRole')] + [System.Security.SecureString] + $Passphrase, + + [Parameter(ParameterSetName = 'InstallRole')] + [ValidateRange(0, 65536)] + [System.UInt16] + $FarmAdminiPort, # cspell: disable-line + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'CompleteImage')] + [Parameter(ParameterSetName = 'Upgrade')] + [ValidateSet('Automatic', 'Disabled', 'Manual')] + [System.String] + $BrowserSvcStartupType, + + [Parameter(ParameterSetName = 'Upgrade')] + [ValidateSet('Rebuild', 'Reset', 'Import')] + [System.String] + $FTUpgradeOption, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'CompleteImage')] + [System.Management.Automation.SwitchParameter] + $EnableRanU, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'CompleteImage')] + [Parameter(ParameterSetName = 'InstallFailoverCluster', Mandatory = $true)] + [Parameter(ParameterSetName = 'CompleteFailoverCluster', Mandatory = $true)] + [System.String] + $InstallSqlDataDir, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'CompleteImage')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'CompleteFailoverCluster')] + [System.String] + $SqlBackupDir, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'CompleteImage')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'CompleteFailoverCluster')] + [ValidateSet('SQL')] + [System.String] + $SecurityMode, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'CompleteImage')] + [Parameter(ParameterSetName = 'RebuildDatabase')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'CompleteFailoverCluster')] + [System.Security.SecureString] + $SAPwd, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'CompleteImage')] + [Parameter(ParameterSetName = 'RebuildDatabase')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'CompleteFailoverCluster')] + [System.String] + $SqlCollation, + + [Parameter(ParameterSetName = 'InstallRole')] + [System.Management.Automation.SwitchParameter] + $AddCurrentUserAsSqlAdmin, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'CompleteImage')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [Parameter(ParameterSetName = 'AddNode')] + [System.String] + $SqlSvcAccount, + + [Parameter(ParameterSetName = 'UsingConfigurationFile')] + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'CompleteImage')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [Parameter(ParameterSetName = 'AddNode')] + [System.Security.SecureString] + $SqlSvcPassword, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'CompleteImage')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [ValidateSet('Automatic', 'Disabled', 'Manual')] + [System.String] + $SqlSvcStartupType, + + [Parameter(ParameterSetName = 'Install', Mandatory = $true)] + [Parameter(ParameterSetName = 'CompleteImage')] + [Parameter(ParameterSetName = 'RebuildDatabase', Mandatory = $true)] + [Parameter(ParameterSetName = 'InstallFailoverCluster', Mandatory = $true)] + [Parameter(ParameterSetName = 'CompleteFailoverCluster', Mandatory = $true)] + [Parameter(ParameterSetName = 'InstallRole')] + [System.String[]] + $SqlSysAdminAccounts, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'CompleteImage')] + [Parameter(ParameterSetName = 'RebuildDatabase')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'CompleteFailoverCluster')] + [System.String] + $SqlTempDbDir, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'CompleteImage')] + [Parameter(ParameterSetName = 'RebuildDatabase')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'CompleteFailoverCluster')] + [System.String] + $SqlTempDbLogDir, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'CompleteImage')] + [Parameter(ParameterSetName = 'RebuildDatabase')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'CompleteFailoverCluster')] + [System.UInt16] + $SqlTempDbFileCount, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'CompleteImage')] + [Parameter(ParameterSetName = 'RebuildDatabase')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'CompleteFailoverCluster')] + [ValidateRange(4, 262144)] + [System.UInt16] + $SqlTempDbFileSize, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'CompleteImage')] + [Parameter(ParameterSetName = 'RebuildDatabase')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'CompleteFailoverCluster')] + [ValidateRange(0, 1024)] + [System.UInt16] + $SqlTempDbFileGrowth, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'CompleteImage')] + [Parameter(ParameterSetName = 'RebuildDatabase')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'CompleteFailoverCluster')] + [ValidateRange(4, 262144)] + [System.UInt16] + $SqlTempDbLogFileSize, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'CompleteImage')] + [Parameter(ParameterSetName = 'RebuildDatabase')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'CompleteFailoverCluster')] + [ValidateRange(0, 1024)] + [System.UInt16] + $SqlTempDbLogFileGrowth, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'CompleteImage')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'CompleteFailoverCluster')] + [System.String] + $SqlUserDbDir, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [System.Management.Automation.SwitchParameter] + $SqlSvcInstantFileInit, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'CompleteImage')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'CompleteFailoverCluster')] + [System.String] + $SqlUserDbLogDir, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [ValidateRange(0, 32767)] + [System.UInt16] + $SqlMaxDop, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [System.Management.Automation.SwitchParameter] + $UseSqlRecommendedMemoryLimits, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [ValidateRange(0, 2147483647)] + [System.UInt32] + $SqlMinMemory, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [ValidateRange(0, 2147483647)] + [System.UInt32] + $SqlMaxMemory, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'CompleteImage')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [ValidateRange(0, 3)] + [System.UInt16] + $FileStreamLevel, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'CompleteImage')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [System.String] + $FileStreamShareName, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'Upgrade')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [Parameter(ParameterSetName = 'AddNode')] + [System.String] + $ISSvcAccount, + + [Parameter(ParameterSetName = 'UsingConfigurationFile')] + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'Upgrade')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [Parameter(ParameterSetName = 'AddNode')] + [System.Security.SecureString] + $ISSvcPassword, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'Upgrade')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [ValidateSet('Automatic', 'Disabled', 'Manual')] + [System.String] + $ISSvcStartupType, + + [Parameter(ParameterSetName = 'Upgrade')] + [System.Management.Automation.SwitchParameter] + $AllowUpgradeForSSRSSharePointMode, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'CompleteImage')] + [System.Management.Automation.SwitchParameter] + $NpEnabled, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'CompleteImage')] + [System.Management.Automation.SwitchParameter] + $TcpEnabled, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'CompleteImage')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [Parameter(ParameterSetName = 'CompleteFailoverCluster')] + [Parameter(ParameterSetName = 'AddNode')] + [ValidateSet('SharePointFilesOnlyMode', 'DefaultNativeMode', 'FilesOnlyMode')] + [System.String] + $RsInstallMode, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'CompleteImage')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [Parameter(ParameterSetName = 'AddNode')] + [System.String] + $RSSvcAccount, + + [Parameter(ParameterSetName = 'UsingConfigurationFile')] + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'CompleteImage')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [Parameter(ParameterSetName = 'AddNode')] + [System.Security.SecureString] + $RSSvcPassword, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'CompleteImage')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [ValidateSet('Automatic', 'Disabled', 'Manual')] + [System.String] + $RSSvcStartupType, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [System.String] + $MPYCacheDirectory, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [System.String] + $MRCacheDirectory, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [System.Management.Automation.SwitchParameter] + $SqlInstJava, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [System.String] + $SqlJavaDir, + + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'CompleteFailoverCluster')] + [System.String] + $FailoverClusterGroup, + + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'CompleteFailoverCluster')] + [System.String[]] + $FailoverClusterDisks, + + [Parameter(ParameterSetName = 'InstallFailoverCluster', Mandatory = $true)] + [Parameter(ParameterSetName = 'CompleteFailoverCluster', Mandatory = $true)] + [System.String] + $FailoverClusterNetworkName, + + [Parameter(ParameterSetName = 'InstallFailoverCluster', Mandatory = $true)] + [Parameter(ParameterSetName = 'CompleteFailoverCluster', Mandatory = $true)] + [Parameter(ParameterSetName = 'AddNode', Mandatory = $true)] + [System.String[]] + $FailoverClusterIPAddresses, + + [Parameter(ParameterSetName = 'CompleteFailoverCluster')] + [Parameter(ParameterSetName = 'AddNode')] + [Parameter(ParameterSetName = 'RemoveNode')] + [System.Management.Automation.SwitchParameter] + $ConfirmIPDependencyChange, + + [Parameter(ParameterSetName = 'Upgrade')] + [ValidateRange(0, 2)] + [System.UInt16] + $FailoverClusterRollOwnership, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallAzureArcAgent', Mandatory = $true)] + [System.String] + $AzureSubscriptionId, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallAzureArcAgent', Mandatory = $true)] + [System.String] + $AzureResourceGroup, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallAzureArcAgent', Mandatory = $true)] + [System.String] + $AzureRegion, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallAzureArcAgent', Mandatory = $true)] + [System.String] + $AzureTenantId, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallAzureArcAgent', Mandatory = $true)] + [System.String] + $AzureServicePrincipal, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallAzureArcAgent', Mandatory = $true)] + [System.Security.SecureString] + $AzureServicePrincipalSecret, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallAzureArcAgent')] + [System.String] + $AzureArcProxy, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'EditionUpgrade')] + [System.String[]] + $SkipRules, + + [Parameter()] + [System.UInt32] + $Timeout = 7200, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + if ($Force.IsPresent) + { + $ConfirmPreference = 'None' + } + + Assert-ElevatedUser -ErrorAction 'Stop' + + switch ($PSCmdlet.ParameterSetName) + { + 'InstallRole' + { + $setupAction = 'Install' + + break + } + + 'InstallAzureArcAgent' + { + $setupAction = 'Install' + + <# + For this setup action the parameter Features is not part of the + parameter set, so this can be safely set. + #> + $PSBoundParameters.Features = @('ARC') + + break + } + + default + { + $setupAction = $PSCmdlet.ParameterSetName + + break + } + } + + Assert-SetupActionProperties -Property $PSBoundParameters -SetupAction $setupAction -ErrorAction 'Stop' + + $setupArgument = '/QUIET /ACTION={0}' -f $setupAction + + if ($DebugPreference -in @('Continue', 'Inquire')) + { + $setupArgument += ' /INDICATEPROGRESS' # cspell: disable-line + } + + if ($AcceptLicensingTerms.IsPresent) + { + $setupArgument += ' /IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line + + if ($PSBoundParameters.ContainsKey('Features')) + { + if ($PSBoundParameters.Features -contains 'SQL_SHARED_MR' ) + { + $setupArgument += ' /IACCEPTROPENLICENSETERMS' # cspell: disable-line + } + + if ($PSBoundParameters.Features -contains 'SQL_SHARED_MPY' ) + { + $setupArgument += ' /IACCEPTPYTHONLICENSETERMS' # cspell: disable-line + } + } + } + + $ignoreParameters = @( + $PSCmdlet.ParameterSetName + 'Install' # Must add this exclusively because of parameter set InstallAzureArcAgent + 'AcceptLicensingTerms' + 'MediaPath' + 'Timeout' + 'Force' + ) + + $ignoreParameters += [System.Management.Automation.PSCmdlet]::CommonParameters + $ignoreParameters += [System.Management.Automation.PSCmdlet]::OptionalCommonParameters + + $boundParameterName = $PSBoundParameters.Keys.Where({ $_ -notin $ignoreParameters }) + + $sensitiveValue = @() + + $pathParameter = @( + 'InstallSharedDir' + 'InstallSharedWowDir' + 'InstanceDir' + 'ASBackupDir' + 'ASConfigDir' + 'ASDataDir' + 'ASLogDir' + 'ASTempDir' + 'InstallSqlDataDir' + 'SqlBackupDir' + 'SqlTempDbDir' + 'SqlTempDbLogDir' + 'SqlUserDbDir' + 'SqlUserDbLogDir' + 'MPYCacheDirectory' + 'MRCacheDirectory' + 'SqlJavaDir' + ) + + <# + Remove trialing backslash from paths so they are not interpreted as + escape-characters for a double-quote. + See issue https://github.com/dsccommunity/SqlServerDsc/issues/1254. + #> + $boundParameterName.Where( { $_ -in $pathParameter } ).ForEach({ + # Must not change paths that reference a root directory (they are handle differently later) + if ($PSBoundParameters.$_ -notmatch '^[a-zA-Z]:\\$') + { + $PSBoundParameters.$_ = $PSBoundParameters.$_.TrimEnd('\') + } + }) + + # Loop through all bound parameters and build arguments for the setup executable. + foreach ($parameterName in $boundParameterName) + { + # Make sure parameter is upper-case. + $parameterName = $parameterName.ToUpper() + + $setupArgument += ' /{0}' -f $parameterName + + switch ($parameterName) + { + <# + Must be handled differently because it is an array and have a comma + separating the values, and the value shall be upper-case. + #> + { $_ -in @('FEATURES', 'ROLE') } + { + $setupArgument += '={0}' -f ($PSBoundParameters.$parameterName.ToUpper() -join ',') + + if ($PSBoundParameters.Features -contains 'ARC' -and $PSBoundParameters.Features -contains 'SQLENGINE') + { + $setupArgument += ' /ONBOARDSQLTOARC' # cspell: disable-line + } + + break + } + + # Must be handled differently because the value MUST be upper-case. + 'ASSERVERMODE' # cspell: disable-line + { + $setupArgument += '={0}' -f $PSBoundParameters.$parameterName.ToUpper() + + break + } + + # Must be handled differently because the parameter name could not be $PID. + 'PRODUCTKEY' # cspell: disable-line + { + # Remove the argument that was added above. + $setupArgument = $setupArgument -replace ' \/{0}' -f $parameterName + + $sensitiveValue += $PSBoundParameters.$parameterName + + $setupArgument += ' /PID="{0}"' -f $PSBoundParameters.$parameterName + + break + } + + # Must be handled differently because the argument name shall have an underscore in the argument. + 'SQLINSTJAVA' # cspell: disable-line + { + # Remove the argument that was added above. + $setupArgument = $setupArgument -replace ' \/{0}' -f $parameterName + + $setupArgument += ' /SQL_INST_JAVA' + + break + } + + # Must be handled differently because each value shall be separated by a semi-colon. + 'FAILOVERCLUSTERDISKS' # cspell: disable-line + { + $setupArgument += '="{0}"' -f ($PSBoundParameters.$parameterName -join ';') + + break + } + + # Must be handled differently because two parameters shall become one argument. + { $_ -in ('PBSTARTPORTRANGE', 'PBENDPORTRANGE') } # cspell: disable-line + { + # Remove the argument that was added above. + $setupArgument = $setupArgument -replace ' \/{0}' -f $parameterName + + # Only set argument if it is not present already. + if ($setupArgument -notmatch '\/PBPORTRANGE') # cspell: disable-line + { + # cspell: disable-next + $setupArgument += ' /PBPORTRANGE={0}-{1}' -f $PSBoundParameters.PBStartPortRange, $PSBoundParameters.PBEndPortRange + } + + break + } + + { $PSBoundParameters.$parameterName -is [System.Management.Automation.SwitchParameter] } + { + <# + If a switch parameter is not included below then those arguments + shall not have any value after argument name, e.g. '/ENU'. + #> + switch ($parameterName) + { + # Arguments that shall have the value set to the boolean numeric representation. + { $parameterName -in ('ASPROVIDERMSOLAP', 'NPENABLED', 'TCPENABLED', 'CONFIRMIPDEPENDENCYCHANGE') } # cspell: disable-line + { + $setupArgument += '={0}' -f [System.Byte] $PSBoundParameters.$parameterName.ToBool() + + break + } + + <# + Arguments that shall have the value set to the boolean string representation. + Excluding parameter names that shall be handled differently, those arguments + shall not have any value after argument name, e.g. '/ENU'. + #> + { $parameterName -in @('UPDATEENABLED', 'PBSCALEOUT', 'SQLSVCINSTANTFILEINIT', 'ALLOWUPGRADEFORSSRSSHAREPOINTMODE', 'ADDCURRENTUSERASSQLADMIN', 'IACKNOWLEDGEENTCALLIMITS') } # cspell: disable-line + { + $setupArgument += '={0}' -f $PSBoundParameters.$parameterName.ToString() + + break + } + } + + break + } + + <# + Must be handled differently because it is an numeric value and does not need to + be surrounded by double-quote. + #> + { $PSBoundParameters.$parameterName | Test-IsNumericType } + { + $setupArgument += '={0}' -f ($PSBoundParameters.$parameterName -join '" "') + + break + } + + <# + Must be handled differently because it is an array and have a space + separating the values, and each value is surrounded by double-quote. + #> + { $PSBoundParameters.$parameterName -is [System.Array] } + { + $setupArgument += '="{0}"' -f ($PSBoundParameters.$parameterName -join '" "') + + break + } + + { $PSBoundParameters.$parameterName -is [System.Security.SecureString] } + { + $passwordClearText = $PSBoundParameters.$parameterName | ConvertFrom-SecureString -AsPlainText + + $sensitiveValue += $passwordClearText + + $setupArgument += '="{0}"' -f $passwordClearText + + break + } + + default + { + <# + When there is backslash followed by a double-quote then the backslash + is treated as an escape character for the double-quote. For arguments + that holds a path and the value references a root directory, e.g. 'E:\', + then the value must not be surrounded by double-quotes. Other paths + should be surrounded by double-quotes as they can contain spaces. + See issue https://github.com/dsccommunity/SqlServerDsc/issues/1254. + #> + if ($PSBoundParameters.$parameterName -match '^[a-zA-Z]:\\$') + { + $setupArgument += '={0}' -f $PSBoundParameters.$parameterName + } + else + { + $setupArgument += '="{0}"' -f $PSBoundParameters.$parameterName + } + break + } + } + } + + $verboseSetupArgument = $setupArgument + + # Obfuscate sensitive values. + foreach ($currentSensitiveValue in $sensitiveValue) + { + $escapedRegExString = [System.Text.RegularExpressions.Regex]::Escape($currentSensitiveValue) + + $verboseSetupArgument = $verboseSetupArgument -replace $escapedRegExString, '********' + } + + # Clear sensitive values. + $sensitiveValue = $null + + Write-Verbose -Message ($script:localizedData.Server_SetupArguments -f $verboseSetupArgument) + + $verboseDescriptionMessage = $script:localizedData.Server_Install_ShouldProcessVerboseDescription -f $PSCmdlet.ParameterSetName + $verboseWarningMessage = $script:localizedData.Server_Install_ShouldProcessVerboseWarning -f $PSCmdlet.ParameterSetName + $captionMessage = $script:localizedData.Server_Install_ShouldProcessCaption + + if ($PSCmdlet.ShouldProcess($verboseDescriptionMessage, $verboseWarningMessage, $captionMessage)) + { + $startProcessParameters = @{ + FilePath = Join-Path -Path $MediaPath -ChildPath 'setup.exe' + ArgumentList = $setupArgument + Timeout = $Timeout + } + + # Clear setupArgument to remove any sensitive values. + $setupArgument = $null + + # Run setup executable. + $processExitCode = Start-SqlSetupProcess @startProcessParameters + + $setupExitMessage = ($script:localizedData.Server_SetupExitMessage -f $processExitCode) + + if ($processExitCode -eq 3010) + { + Write-Warning -Message ( + '{0} {1}' -f $setupExitMessage, $script:localizedData.Server_SetupSuccessfulRebootRequired + ) + } + elseif ($processExitCode -ne 0) + { + $PSCmdlet.ThrowTerminatingError( + [System.Management.Automation.ErrorRecord]::new( + ('{0} {1}' -f $setupExitMessage, $script:localizedData.Server_SetupFailed), + 'ISA0001', # cspell: disable-line + [System.Management.Automation.ErrorCategory]::InvalidOperation, + $InstanceName + ) + ) + } + else + { + Write-Verbose -Message ( + '{0} {1}' -f $setupExitMessage, ($script:localizedData.Server_SetupSuccessful) + ) + } + } +} diff --git a/source/Private/Test-IsNumericType.ps1 b/source/Private/Test-IsNumericType.ps1 new file mode 100644 index 000000000..04cdb45ab --- /dev/null +++ b/source/Private/Test-IsNumericType.ps1 @@ -0,0 +1,50 @@ +<# + .SYNOPSIS + Returns wether the specified object is of a numeric type. + + .DESCRIPTION + Returns wether the specified object is of a numeric type. + + .PARAMETER Object + The object to test if it is a numeric type. + + .EXAMPLE + Test-IsNumericType -Object ([System.UInt32] 1) + + Returns $true since the object passed is of a numeric type. + + .OUTPUTS + [System.Boolean] +#> +function Test-IsNumericType +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + [Parameter(ValueFromPipeline = $true)] + [System.Object] + $Object + ) + + $isNumeric = $false + + if ( + $Object -is [System.Byte] -or + $Object -is [System.Int16] -or + $Object -is [System.Int32] -or + $Object -is [System.Int64] -or + $Object -is [System.SByte] -or + $Object -is [System.UInt16] -or + $Object -is [System.UInt32] -or + $Object -is [System.UInt64] -or + $Object -is [System.Decimal] -or + $Object -is [System.Double] -or + $Object -is [System.Single] + ) + { + $isNumeric = $true + } + + return $isNumeric +} diff --git a/source/Private/Test-ServiceAccountRequirePassword.ps1 b/source/Private/Test-ServiceAccountRequirePassword.ps1 new file mode 100644 index 000000000..1f605474f --- /dev/null +++ b/source/Private/Test-ServiceAccountRequirePassword.ps1 @@ -0,0 +1,58 @@ +<# + .SYNOPSIS + Returns wether the specified service account require password to be provided. + + .SYNOPSIS + Returns wether the specified service account require password to be provided. + If the account is a (global) managed service account, virtual account, or a + built-in account then there is no need to provide a password. + + .PARAMETER Name + Credential name for the service account. + + .OUTPUTS + [System.Boolean] +#> +function Test-ServiceAccountRequirePassword +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $Name + ) + + # Assume local or domain service account. + $requirePassword = $true + + switch -Regex ($Name.ToUpper()) + { + # Built-in account. + '^(?:NT ?AUTHORITY\\)?(SYSTEM|LOCALSERVICE|LOCAL SERVICE|NETWORKSERVICE|NETWORK SERVICE)$' # CSpell: disable-line + { + $requirePassword = $false + + break + } + + # Virtual account. + '^(?:NT SERVICE\\)(.*)$' + { + $requirePassword = $false + + break + } + + # (Global) Managed Service Account. + '\$$' + { + $requirePassword = $false + + break + } + } + + return $requirePassword +} diff --git a/source/Public/Add-SqlDscNode.ps1 b/source/Public/Add-SqlDscNode.ps1 new file mode 100644 index 000000000..fac54138d --- /dev/null +++ b/source/Public/Add-SqlDscNode.ps1 @@ -0,0 +1,165 @@ +<# + .SYNOPSIS + Add a SQL Server node to an Failover Cluster instance (FCI). + + .DESCRIPTION + Add a SQL Server node to an Failover Cluster instance (FCI). + + See the link in the commands help for information on each parameter. The + link points to SQL Server command line setup documentation. + + .PARAMETER AcceptLicensingTerms + Required parameter to be able to run unattended install. By specifying this + parameter you acknowledge the acceptance all license terms and notices for + the specified features, the terms and notices that the Microsoft SQL Server + setup executable normally ask for. + + .PARAMETER MediaPath + Specifies the path where to find the SQL Server installation media. On this + path the SQL Server setup executable must be found. + + .LINK + https://docs.microsoft.com/en-us/sql/database-engine/install-windows/install-sql-server-from-the-command-prompt + + .OUTPUTS + None. + + .EXAMPLE + Add-SqlDscNode -AcceptLicensingTerms -InstanceName 'MyInstance' -FailoverClusterIPAddresses 'IPv4;192.168.0.46;ClusterNetwork1;255.255.255.0' -MediaPath 'E:\' + + Adds the current node's SQL Server instance 'MyInstance' to the Failover Cluster instance. + + .NOTES + All parameters has intentionally not been added to this comment-based help + since it would take a lot of effort to keep it up to date. Instead there is + a link in the comment-based help that points to the SQL Server command line + setup documentation which will stay relevant. +#> +function Add-SqlDscNode +{ + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '', Justification = 'Because ShouldProcess is used in Invoke-SetupAction')] + [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')] + [OutputType()] + param + ( + [Parameter(Mandatory = $true)] + [System.Management.Automation.SwitchParameter] + $AcceptLicensingTerms, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $IAcknowledgeEntCalLimits, + + [Parameter(Mandatory = $true)] + [System.String] + $MediaPath, + + [Parameter(Mandatory = $true)] + [System.String] + $InstanceName, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Enu, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $UpdateEnabled, + + [Parameter()] + [System.String] + $UpdateSource, + + [Parameter()] + [System.String] + $PBEngSvcAccount, + + [Parameter()] + [System.Security.SecureString] + $PBEngSvcPassword, + + [Parameter()] + [ValidateSet('Automatic', 'Disabled', 'Manual')] + [System.String] + $PBEngSvcStartupType, + + [Parameter()] + [System.UInt16] + $PBStartPortRange, + + [Parameter()] + [System.UInt16] + $PBEndPortRange, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $PBScaleOut, + + [Parameter()] + [System.String] + $ProductKey, # This is argument PID but $PID is reserved variable. + + [Parameter()] + [System.String] + $AgtSvcAccount, + + [Parameter()] + [System.Security.SecureString] + $AgtSvcPassword, + + [Parameter()] + [System.String] + $ASSvcAccount, + + [Parameter()] + [System.Security.SecureString] + $ASSvcPassword, + + [Parameter()] + [System.String] + $SqlSvcAccount, + + [Parameter()] + [System.Security.SecureString] + $SqlSvcPassword, + + [Parameter()] + [System.String] + $ISSvcAccount, + + [Parameter()] + [System.Security.SecureString] + $ISSvcPassword, + + [Parameter()] + [ValidateSet('SharePointFilesOnlyMode', 'DefaultNativeMode', 'FilesOnlyMode')] + [System.String] + $RsInstallMode, + + [Parameter()] + [System.String] + $RSSvcAccount, + + [Parameter()] + [System.Security.SecureString] + $RSSvcPassword, + + [Parameter(Mandatory = $true)] + [System.String[]] + $FailoverClusterIPAddresses, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $ConfirmIPDependencyChange, + + [Parameter()] + [System.UInt32] + $Timeout = 7200, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + Invoke-SetupAction -AddNode @PSBoundParameters -ErrorAction 'Stop' +} diff --git a/source/Public/Complete-SqlDscFailoverCluster.ps1 b/source/Public/Complete-SqlDscFailoverCluster.ps1 new file mode 100644 index 000000000..e4ca7e3f9 --- /dev/null +++ b/source/Public/Complete-SqlDscFailoverCluster.ps1 @@ -0,0 +1,196 @@ +<# + .SYNOPSIS + Completes the SQL Server instance installation in the Failover Cluster + instance. + + .DESCRIPTION + Completes the SQL Server instance installation in the Failover Cluster + instance that was prepared using `Install-SqlDscServer` with the parameter + `-PrepareFailoverCluster`. + + See the link in the commands help for information on each parameter. The + link points to SQL Server command line setup documentation. + + .PARAMETER MediaPath + Specifies the path where to find the SQL Server installation media. On this + path the SQL Server setup executable must be found. + + .LINK + https://docs.microsoft.com/en-us/sql/database-engine/install-windows/install-sql-server-from-the-command-prompt + + .OUTPUTS + None. + + .EXAMPLE + Complete-SqlDscFailoverCluster -InstanceName 'MyInstance' -InstallSqlDataDir 'D:\MSSQL\Data' -SqlSysAdminAccounts @('MyAdminAccount') -FailoverClusterNetworkName 'TestCluster01A' -FailoverClusterIPAddresses 'IPv4;192.168.0.46;ClusterNetwork1;255.255.255.0' -MediaPath 'E:\' + + Completes the installation of the SQL Server instance 'MyInstance' in the + Failover Cluster instance. + + .NOTES + All parameters has intentionally not been added to this comment-based help + since it would take a lot of effort to keep it up to date. Instead there is + a link in the comment-based help that points to the SQL Server command line + setup documentation which will stay relevant. +#> +function Complete-SqlDscFailoverCluster +{ + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '', Justification = 'Because ShouldProcess is used in Invoke-SetupAction')] + [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')] + [OutputType()] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $MediaPath, + + [Parameter(Mandatory = $true)] + [System.String] + $InstanceName, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Enu, + + [Parameter()] + [System.String] + $ProductKey, # This is argument PID but $PID is reserved variable. + + [Parameter()] + [System.String] + $ASBackupDir, + + [Parameter()] + [System.String] + $ASCollation, + + [Parameter()] + [System.String] + $ASConfigDir, + + [Parameter()] + [System.String] + $ASDataDir, + + [Parameter()] + [System.String] + $ASLogDir, + + [Parameter()] + [System.String] + $ASTempDir, + + [Parameter()] + [ValidateSet('Multidimensional', 'PowerPivot', 'Tabular')] + [System.String] + $ASServerMode, + + [Parameter()] + [System.String[]] + $ASSysAdminAccounts, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $ASProviderMSOLAP, + + [Parameter(Mandatory = $true)] + [System.String] + $InstallSqlDataDir, + + [Parameter()] + [System.String] + $SqlBackupDir, + + [Parameter()] + [ValidateSet('SQL')] + [System.String] + $SecurityMode, + + [Parameter()] + [System.Security.SecureString] + $SAPwd, + + [Parameter()] + [System.String] + $SqlCollation, + + [Parameter(Mandatory = $true)] + [System.String[]] + $SqlSysAdminAccounts, + + [Parameter()] + [System.String] + $SqlTempDbDir, + + [Parameter()] + [System.String] + $SqlTempDbLogDir, + + [Parameter()] + [System.UInt16] + $SqlTempDbFileCount, + + [Parameter()] + [ValidateRange(4, 262144)] + [System.UInt16] + $SqlTempDbFileSize, + + [Parameter()] + [ValidateRange(0, 1024)] + [System.UInt16] + $SqlTempDbFileGrowth, + + [Parameter()] + [ValidateRange(4, 262144)] + [System.UInt16] + $SqlTempDbLogFileSize, + + [Parameter()] + [ValidateRange(0, 1024)] + [System.UInt16] + $SqlTempDbLogFileGrowth, + + [Parameter()] + [System.String] + $SqlUserDbDir, + + [Parameter()] + [System.String] + $SqlUserDbLogDir, + + [Parameter()] + [ValidateSet('SharePointFilesOnlyMode', 'DefaultNativeMode', 'FilesOnlyMode')] + [System.String] + $RsInstallMode, + + [Parameter()] + [System.String] + $FailoverClusterGroup, + + [Parameter()] + [System.String[]] + $FailoverClusterDisks, + + [Parameter(Mandatory = $true)] + [System.String] + $FailoverClusterNetworkName, + + [Parameter(Mandatory = $true)] + [System.String[]] + $FailoverClusterIPAddresses, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $ConfirmIPDependencyChange, + + [Parameter()] + [System.UInt32] + $Timeout = 7200, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + Invoke-SetupAction -CompleteFailoverCluster @PSBoundParameters -ErrorAction 'Stop' +} diff --git a/source/Public/Complete-SqlDscImage.ps1 b/source/Public/Complete-SqlDscImage.ps1 new file mode 100644 index 000000000..e27b0f5e7 --- /dev/null +++ b/source/Public/Complete-SqlDscImage.ps1 @@ -0,0 +1,241 @@ +<# + .SYNOPSIS + Completes the image installation of an SQL Server instance. + + .DESCRIPTION + Completes the image installation of an SQL Server instance that was prepared + using `Install-SqlDscServer` with the parameter `-PrepareImage`. + + See the link in the commands help for information on each parameter. The + link points to SQL Server command line setup documentation. + + .PARAMETER AcceptLicensingTerms + Required parameter to be able to run unattended install. By specifying this + parameter you acknowledge the acceptance all license terms and notices for + the specified features, the terms and notices that the Microsoft SQL Server + setup executable normally ask for. + + .PARAMETER MediaPath + Specifies the path where to find the SQL Server installation media. On this + path the SQL Server setup executable must be found. + + .LINK + https://docs.microsoft.com/en-us/sql/database-engine/install-windows/install-sql-server-from-the-command-prompt + + .OUTPUTS + None. + + .EXAMPLE + Complete-SqlDscImage -AcceptLicensingTerms -MediaPath 'E:\' + + Completes the image installation of the SQL Server default instance that + was prepared using `Install-SqlDscServer` with the parameter `-PrepareImage`. + + .NOTES + All parameters has intentionally not been added to this comment-based help + since it would take a lot of effort to keep it up to date. Instead there is + a link in the comment-based help that points to the SQL Server command line + setup documentation which will stay relevant. +#> +function Complete-SqlDscImage +{ + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '', Justification = 'Because ShouldProcess is used in Invoke-SetupAction')] + [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')] + [OutputType()] + param + ( + [Parameter(Mandatory = $true)] + [System.Management.Automation.SwitchParameter] + $AcceptLicensingTerms, + + [Parameter(Mandatory = $true)] + [System.String] + $MediaPath, + + [Parameter()] + [System.String] + $InstanceName, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Enu, + + [Parameter()] + [System.String] + $InstanceId, + + [Parameter()] + [System.String] + $PBEngSvcAccount, + + [Parameter()] + [System.Security.SecureString] + $PBEngSvcPassword, + + [Parameter()] + [ValidateSet('Automatic', 'Disabled', 'Manual')] + [System.String] + $PBEngSvcStartupType, + + [Parameter()] + [System.UInt16] + $PBStartPortRange, + + [Parameter()] + [System.UInt16] + $PBEndPortRange, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $PBScaleOut, + + [Parameter()] + [System.String] + $ProductKey, # This is argument PID but $PID is reserved variable. + + [Parameter()] + [System.String] + $AgtSvcAccount, + + [Parameter()] + [System.Security.SecureString] + $AgtSvcPassword, + + [Parameter()] + [ValidateSet('Automatic', 'Disabled', 'Manual')] + [System.String] + $AgtSvcStartupType, + + [Parameter()] + [ValidateSet('Automatic', 'Disabled', 'Manual')] + [System.String] + $BrowserSvcStartupType, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $EnableRanU, + + [Parameter()] + [System.String] + $InstallSqlDataDir, + + [Parameter()] + [System.String] + $SqlBackupDir, + + [Parameter()] + [ValidateSet('SQL')] + [System.String] + $SecurityMode, + + [Parameter()] + [System.Security.SecureString] + $SAPwd, + + [Parameter()] + [System.String] + $SqlCollation, + + [Parameter()] + [System.String] + $SqlSvcAccount, + + [Parameter()] + [System.Security.SecureString] + $SqlSvcPassword, + + [Parameter()] + [ValidateSet('Automatic', 'Disabled', 'Manual')] + [System.String] + $SqlSvcStartupType, + + [Parameter()] + [System.String[]] + $SqlSysAdminAccounts, + + [Parameter()] + [System.String] + $SqlTempDbDir, + + [Parameter()] + [System.String] + $SqlTempDbLogDir, + + [Parameter()] + [System.UInt16] + $SqlTempDbFileCount, + + [Parameter()] + [ValidateRange(4, 262144)] + [System.UInt16] + $SqlTempDbFileSize, + + [Parameter()] + [ValidateRange(0, 1024)] + [System.UInt16] + $SqlTempDbFileGrowth, + + [Parameter()] + [ValidateRange(4, 262144)] + [System.UInt16] + $SqlTempDbLogFileSize, + + [Parameter()] + [ValidateRange(0, 1024)] + [System.UInt16] + $SqlTempDbLogFileGrowth, + + [Parameter()] + [System.String] + $SqlUserDbDir, + + [Parameter()] + [System.String] + $SqlUserDbLogDir, + + [Parameter()] + [ValidateRange(0, 3)] + [System.UInt16] + $FileStreamLevel, + + [Parameter()] + [System.String] + $FileStreamShareName, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $NpEnabled, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $TcpEnabled, + + [Parameter()] + [ValidateSet('SharePointFilesOnlyMode', 'DefaultNativeMode', 'FilesOnlyMode')] + [System.String] + $RsInstallMode, + + [Parameter()] + [System.String] + $RSSvcAccount, + + [Parameter()] + [System.Security.SecureString] + $RSSvcPassword, + + [Parameter()] + [ValidateSet('Automatic', 'Disabled', 'Manual')] + [System.String] + $RSSvcStartupType, + + [Parameter()] + [System.UInt32] + $Timeout = 7200, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + Invoke-SetupAction -CompleteImage @PSBoundParameters -ErrorAction 'Stop' +} diff --git a/source/Public/Initialize-SqlDscRebuildDatabase.ps1 b/source/Public/Initialize-SqlDscRebuildDatabase.ps1 new file mode 100644 index 000000000..8866d27bf --- /dev/null +++ b/source/Public/Initialize-SqlDscRebuildDatabase.ps1 @@ -0,0 +1,104 @@ +<# + .SYNOPSIS + Rebuilds the system databases for an SQL Server instance. + + .DESCRIPTION + Rebuilds the system databases for an SQL Server instance. + + See the link in the commands help for information on each parameter. The + link points to SQL Server command line setup documentation. + + .PARAMETER MediaPath + Specifies the path where to find the SQL Server installation media. On this + path the SQL Server setup executable must be found. + + .LINK + https://docs.microsoft.com/en-us/sql/database-engine/install-windows/install-sql-server-from-the-command-prompt + + .OUTPUTS + None. + + .EXAMPLE + Initialize-SqlDscRebuildDatabase -InstanceName 'MyInstance' -SqlSysAdminAccounts @('MyAdminAccount') -MediaPath 'E:\' + + Rebuilds the database of the instance 'MyInstance'. + + .NOTES + All parameters has intentionally not been added to this comment-based help + since it would take a lot of effort to keep it up to date. Instead there is + a link in the comment-based help that points to the SQL Server command line + setup documentation which will stay relevant. + + For RebuildDatabase the parameter SAPwd must be set if the instance was + installed with SecurityMode = 'SQL'. +#> +function Initialize-SqlDscRebuildDatabase +{ + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '', Justification = 'Because ShouldProcess is used in Invoke-SetupAction')] + [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')] + [OutputType()] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $MediaPath, + + [Parameter(Mandatory = $true)] + [System.String] + $InstanceName, + + [Parameter()] + [System.Security.SecureString] + $SAPwd, + + [Parameter()] + [System.String] + $SqlCollation, + + [Parameter(Mandatory = $true)] + [System.String[]] + $SqlSysAdminAccounts, + + [Parameter()] + [System.String] + $SqlTempDbDir, + + [Parameter()] + [System.String] + $SqlTempDbLogDir, + + [Parameter()] + [System.UInt16] + $SqlTempDbFileCount, + + [Parameter()] + [ValidateRange(4, 262144)] + [System.UInt16] + $SqlTempDbFileSize, + + [Parameter()] + [ValidateRange(0, 1024)] + [System.UInt16] + $SqlTempDbFileGrowth, + + [Parameter()] + [ValidateRange(4, 262144)] + [System.UInt16] + $SqlTempDbLogFileSize, + + [Parameter()] + [ValidateRange(0, 1024)] + [System.UInt16] + $SqlTempDbLogFileGrowth, + + [Parameter()] + [System.UInt32] + $Timeout = 7200, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + Invoke-SetupAction -RebuildDatabase @PSBoundParameters -ErrorAction 'Stop' +} diff --git a/source/Public/Install-SqlDscServer.ps1 b/source/Public/Install-SqlDscServer.ps1 new file mode 100644 index 000000000..7366f2c6b --- /dev/null +++ b/source/Public/Install-SqlDscServer.ps1 @@ -0,0 +1,832 @@ +<# + .SYNOPSIS + Executes an setup action using Microsoft SQL Server setup executable. + + .DESCRIPTION + Executes an setup action using Microsoft SQL Server setup executable. + + See the link in the commands help for information on each parameter. The + link points to SQL Server command line setup documentation. + + .PARAMETER Install + Specifies the setup action Install. + + .PARAMETER Uninstall + Specifies the setup action Uninstall. + + .PARAMETER PrepareImage + Specifies the setup action PrepareImage. + + .PARAMETER Upgrade + Specifies the setup action Upgrade. + + .PARAMETER EditionUpgrade + Specifies the setup action EditionUpgrade. + + .PARAMETER InstallFailoverCluster + Specifies the setup action InstallFailoverCluster. + + .PARAMETER PrepareFailoverCluster + Specifies the setup action PrepareFailoverCluster. + + .PARAMETER ConfigurationFile + Specifies an configuration file to use during SQL Server setup. This + parameter cannot be used together with any of the setup actions, but instead + it is expected that the configuration file specifies what setup action to + run. + + .PARAMETER AcceptLicensingTerms + Required parameter to be able to run unattended install. By specifying this + parameter you acknowledge the acceptance all license terms and notices for + the specified features, the terms and notices that the Microsoft SQL Server + setup executable normally ask for. + + .PARAMETER MediaPath + Specifies the path where to find the SQL Server installation media. On this + path the SQL Server setup executable must be found. + + .LINK + https://docs.microsoft.com/en-us/sql/database-engine/install-windows/install-sql-server-from-the-command-prompt + + .OUTPUTS + None. + + .EXAMPLE + Install-SqlDscServer -Install -AcceptLicensingTerms -InstanceName 'MyInstance' -Features 'SQLENGINE' -SqlSysAdminAccounts @('MyAdminAccount') -MediaPath 'E:\' + + Installs the database engine for the named instance MyInstance. + + .EXAMPLE + Install-SqlDscServer -Install -AcceptLicensingTerms -InstanceName 'MyInstance' -Features 'SQLENGINE','ARC' -SqlSysAdminAccounts @('MyAdminAccount') -MediaPath 'E:\' -AzureSubscriptionId 'MySubscriptionId' -AzureResourceGroup 'MyRG' -AzureRegion 'West-US' -AzureTenantId 'MyTenantId' -AzureServicePrincipal 'MyPrincipalName' -AzureServicePrincipalSecret ('MySecret' | ConvertTo-SecureString -AsPlainText -Force) + + Installs the database engine for the named instance MyInstance and onboard the server to Azure Arc. + + .EXAMPLE + Install-SqlDscServer -Install -AcceptLicensingTerms -MediaPath 'E:\' -AzureSubscriptionId 'MySubscriptionId' -AzureResourceGroup 'MyRG' -AzureRegion 'West-US' -AzureTenantId 'MyTenantId' -AzureServicePrincipal 'MyPrincipalName' -AzureServicePrincipalSecret ('MySecret' | ConvertTo-SecureString -AsPlainText -Force) + + Installs the Azure Arc Agent on the server. + + .EXAMPLE + Install-SqlDscServer -ConfigurationFile 'MySqlConfig.ini' -MediaPath 'E:\' + + Installs SQL Server using the configuration file 'MySqlConfig.ini'. + + .EXAMPLE + Install-SqlDscServer -PrepareImage -AcceptLicensingTerms -InstanceName 'MyInstance' -Features 'SQLENGINE' -InstanceId 'MyInstance' -MediaPath 'E:\' + + Prepares the server for using the database engine for an instance named 'MyInstance'. + + .EXAMPLE + Install-SqlDscServer -Upgrade -AcceptLicensingTerms -InstanceName 'MyInstance' -MediaPath 'E:\' + + Upgrades the instance 'MyInstance' with the SQL Server version that is provided by the media path. + + .EXAMPLE + Install-SqlDscServer -EditionUpgrade -AcceptLicensingTerms -ProductKey 'NewEditionProductKey' -InstanceName 'MyInstance' -MediaPath 'E:\' + + Upgrades the instance 'MyInstance' with the SQL Server edition that is provided by the media path. + + .EXAMPLE + Install-SqlDscServer -InstallFailoverCluster -AcceptLicensingTerms -InstanceName 'MyInstance' -Features 'SQLENGINE' -InstallSqlDataDir 'D:\MSSQL\Data' -SqlSysAdminAccounts @('MyAdminAccount') -FailoverClusterNetworkName 'TestCluster01A' -FailoverClusterIPAddresses 'IPv4;192.168.0.46;ClusterNetwork1;255.255.255.0' -MediaPath 'E:\' + + Installs the database engine in a failover cluster with the instance name 'MyInstance'. + + .EXAMPLE + Install-SqlDscServer -PrepareFailoverCluster -AcceptLicensingTerms -InstanceName 'MyInstance' -Features 'SQLENGINE' -MediaPath 'E:\' + + Prepares to installs the database engine in a failover cluster with the instance name 'MyInstance'. + + .NOTES + All parameters has intentionally not been added to this comment-based help + since it would take a lot of effort to keep it up to date. Instead there is + a link in the comment-based help that points to the SQL Server command line + setup documentation which will stay relevant. +#> +function Install-SqlDscServer +{ + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '', Justification = 'Because ShouldProcess is used in Invoke-SetupAction')] + [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')] + [OutputType()] + param + ( + [Parameter(ParameterSetName = 'Install', Mandatory = $true)] + [Parameter(ParameterSetName = 'InstallRole', Mandatory = $true)] + [Parameter(ParameterSetName = 'InstallAzureArcAgent', Mandatory = $true)] + [System.Management.Automation.SwitchParameter] + $Install, + + [Parameter(ParameterSetName = 'PrepareImage', Mandatory = $true)] + [System.Management.Automation.SwitchParameter] + $PrepareImage, + + [Parameter(ParameterSetName = 'Upgrade', Mandatory = $true)] + [System.Management.Automation.SwitchParameter] + $Upgrade, + + [Parameter(ParameterSetName = 'EditionUpgrade', Mandatory = $true)] + [System.Management.Automation.SwitchParameter] + $EditionUpgrade, + + [Parameter(ParameterSetName = 'InstallFailoverCluster', Mandatory = $true)] + [System.Management.Automation.SwitchParameter] + $InstallFailoverCluster, + + [Parameter(ParameterSetName = 'PrepareFailoverCluster', Mandatory = $true)] + [System.Management.Automation.SwitchParameter] + $PrepareFailoverCluster, + + [Parameter(ParameterSetName = 'UsingConfigurationFile', Mandatory = $true)] + [System.String] + $ConfigurationFile, + + [Parameter(ParameterSetName = 'Install', Mandatory = $true)] + [Parameter(ParameterSetName = 'InstallRole', Mandatory = $true)] + [Parameter(ParameterSetName = 'InstallAzureArcAgent', Mandatory = $true)] + [Parameter(ParameterSetName = 'PrepareImage', Mandatory = $true)] + [Parameter(ParameterSetName = 'Upgrade', Mandatory = $true)] + [Parameter(ParameterSetName = 'EditionUpgrade', Mandatory = $true)] + [Parameter(ParameterSetName = 'InstallFailoverCluster', Mandatory = $true)] + [Parameter(ParameterSetName = 'PrepareFailoverCluster', Mandatory = $true)] + [System.Management.Automation.SwitchParameter] + $AcceptLicensingTerms, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [System.Management.Automation.SwitchParameter] + $SuppressPrivacyStatementNotice, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'PrepareImage')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [System.Management.Automation.SwitchParameter] + $IAcknowledgeEntCalLimits, + + [Parameter(Mandatory = $true)] + [System.String] + $MediaPath, + + [Parameter(ParameterSetName = 'Install', Mandatory = $true)] + [Parameter(ParameterSetName = 'PrepareImage', Mandatory = $true)] + [Parameter(ParameterSetName = 'Upgrade', Mandatory = $true)] + [Parameter(ParameterSetName = 'EditionUpgrade', Mandatory = $true)] + [Parameter(ParameterSetName = 'InstallFailoverCluster', Mandatory = $true)] + [Parameter(ParameterSetName = 'PrepareFailoverCluster', Mandatory = $true)] + [Parameter(ParameterSetName = 'InstallRole')] + [System.String] + $InstanceName, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'PrepareImage')] + [Parameter(ParameterSetName = 'Upgrade')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [System.Management.Automation.SwitchParameter] + $Enu, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'PrepareImage')] + [Parameter(ParameterSetName = 'Upgrade')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [System.Management.Automation.SwitchParameter] + $UpdateEnabled, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'PrepareImage')] + [Parameter(ParameterSetName = 'Upgrade')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [System.String] + $UpdateSource, + + [Parameter(ParameterSetName = 'Install', Mandatory = $true)] + [Parameter(ParameterSetName = 'PrepareImage', Mandatory = $true)] + [Parameter(ParameterSetName = 'InstallFailoverCluster', Mandatory = $true)] + [Parameter(ParameterSetName = 'PrepareFailoverCluster', Mandatory = $true)] + [Parameter(ParameterSetName = 'InstallRole')] + [ValidateSet( + 'SQL', + 'SQLEngine', # Part of parent feature SQL + 'Replication', # Part of parent feature SQL + 'FullText', # Part of parent feature SQL + 'DQ', # Part of parent feature SQL + 'PolyBase', # Part of parent feature SQL + 'PolyBaseCore', # Part of parent feature SQL + 'PolyBaseJava', # Part of parent feature SQL + 'AdvancedAnalytics', # Part of parent feature SQL + 'SQL_INST_MR', # Part of parent feature SQL + 'SQL_INST_MPY', # Part of parent feature SQL + 'SQL_INST_JAVA', # Part of parent feature SQL + 'AS', + 'RS', + 'RS_SHP', + 'RS_SHPWFE', # cspell: disable-line + 'DQC', + 'IS', + 'IS_Master', # Part of parent feature IS + 'IS_Worker', # Part of parent feature IS + 'MDS', + 'SQL_SHARED_MPY', + 'SQL_SHARED_MR', + 'Tools', + 'BC', # Part of parent feature Tools + 'Conn', # Part of parent feature Tools + 'DREPLAY_CTLR', # Part of parent feature Tools (cspell: disable-line) + 'DREPLAY_CLT', # Part of parent feature Tools (cspell: disable-line) + 'SNAC_SDK', # Part of parent feature Tools (cspell: disable-line) + 'SDK', # Part of parent feature Tools + 'LocalDB', # Part of parent feature Tools + 'ARC' + )] + [System.String[]] + $Features, + + [Parameter(ParameterSetName = 'InstallRole', Mandatory = $true)] + [ValidateSet( + 'ALLFeatures_WithDefaults', + 'SPI_AS_NewFarm', + 'SPI_AS_ExistingFarm' + )] + [System.String] + $Role, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'PrepareImage')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [System.String] + $InstallSharedDir, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [System.String] + $InstallSharedWowDir, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'PrepareImage')] + [Parameter(ParameterSetName = 'Upgrade')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [System.String] + $InstanceDir, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'PrepareImage', Mandatory = $true)] + [Parameter(ParameterSetName = 'Upgrade')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [System.String] + $InstanceId, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'PrepareImage')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [System.String] + $PBEngSvcAccount, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'PrepareImage')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [System.Security.SecureString] + $PBEngSvcPassword, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'PrepareImage')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [ValidateSet('Automatic', 'Disabled', 'Manual')] + [System.String] + $PBEngSvcStartupType, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [System.String] + $PBDMSSvcAccount, # cspell: disable-line + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [System.Security.SecureString] + $PBDMSSvcPassword, # cspell: disable-line + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [ValidateSet('Automatic', 'Disabled', 'Manual')] + [System.String] + $PBDMSSvcStartupType, # cspell: disable-line + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'PrepareImage')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [System.UInt16] + $PBStartPortRange, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'PrepareImage')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [System.UInt16] + $PBEndPortRange, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'PrepareImage')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [System.Management.Automation.SwitchParameter] + $PBScaleOut, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'Upgrade')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [Parameter(ParameterSetName = 'EditionUpgrade', Mandatory = $true)] + [System.String] + $ProductKey, # This is argument PID but $PID is reserved variable. + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [System.String] + $AgtSvcAccount, + + [Parameter(ParameterSetName = 'UsingConfigurationFile')] + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [System.Security.SecureString] + $AgtSvcPassword, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [ValidateSet('Automatic', 'Disabled', 'Manual')] + [System.String] + $AgtSvcStartupType, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [System.String] + $ASBackupDir, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [System.String] + $ASCollation, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [System.String] + $ASConfigDir, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [System.String] + $ASDataDir, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [System.String] + $ASLogDir, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [System.String] + $ASTempDir, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [ValidateSet('Multidimensional', 'PowerPivot', 'Tabular')] + [System.String] + $ASServerMode, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [System.String] + $ASSvcAccount, + + [Parameter(ParameterSetName = 'UsingConfigurationFile')] + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [System.Security.SecureString] + $ASSvcPassword, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [ValidateSet('Automatic', 'Disabled', 'Manual')] + [System.String] + $ASSvcStartupType, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [System.String[]] + $ASSysAdminAccounts, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [System.Management.Automation.SwitchParameter] + $ASProviderMSOLAP, + + [Parameter(ParameterSetName = 'InstallRole')] + [System.String] + $FarmAccount, + + [Parameter(ParameterSetName = 'InstallRole')] + [System.Security.SecureString] + $FarmPassword, + + [Parameter(ParameterSetName = 'InstallRole')] + [System.Security.SecureString] + $Passphrase, + + [Parameter(ParameterSetName = 'InstallRole')] + [ValidateRange(0, 65536)] + [System.UInt16] + $FarmAdminiPort, # cspell: disable-line + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'Upgrade')] + [ValidateSet('Automatic', 'Disabled', 'Manual')] + [System.String] + $BrowserSvcStartupType, + + [Parameter(ParameterSetName = 'Upgrade')] + [ValidateSet('Rebuild', 'Reset', 'Import')] + [System.String] + $FTUpgradeOption, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [System.Management.Automation.SwitchParameter] + $EnableRanU, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster', Mandatory = $true)] + [System.String] + $InstallSqlDataDir, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [System.String] + $SqlBackupDir, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [ValidateSet('SQL')] + [System.String] + $SecurityMode, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [System.Security.SecureString] + $SAPwd, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [System.String] + $SqlCollation, + + [Parameter(ParameterSetName = 'InstallRole')] + [System.Management.Automation.SwitchParameter] + $AddCurrentUserAsSqlAdmin, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [System.String] + $SqlSvcAccount, + + [Parameter(ParameterSetName = 'UsingConfigurationFile')] + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [System.Security.SecureString] + $SqlSvcPassword, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [ValidateSet('Automatic', 'Disabled', 'Manual')] + [System.String] + $SqlSvcStartupType, + + [Parameter(ParameterSetName = 'Install', Mandatory = $true)] + [Parameter(ParameterSetName = 'InstallFailoverCluster', Mandatory = $true)] + [Parameter(ParameterSetName = 'InstallRole')] + [System.String[]] + $SqlSysAdminAccounts, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [System.String] + $SqlTempDbDir, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [System.String] + $SqlTempDbLogDir, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [System.UInt16] + $SqlTempDbFileCount, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [ValidateRange(4, 262144)] + [System.UInt16] + $SqlTempDbFileSize, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [ValidateRange(0, 1024)] + [System.UInt16] + $SqlTempDbFileGrowth, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [ValidateRange(4, 262144)] + [System.UInt16] + $SqlTempDbLogFileSize, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [ValidateRange(0, 1024)] + [System.UInt16] + $SqlTempDbLogFileGrowth, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [System.String] + $SqlUserDbDir, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [System.Management.Automation.SwitchParameter] + $SqlSvcInstantFileInit, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [System.String] + $SqlUserDbLogDir, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [ValidateRange(0, 32767)] + [System.UInt16] + $SqlMaxDop, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [System.Management.Automation.SwitchParameter] + $UseSqlRecommendedMemoryLimits, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [ValidateRange(0, 2147483647)] + [System.UInt32] + $SqlMinMemory, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [ValidateRange(0, 2147483647)] + [System.UInt32] + $SqlMaxMemory, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [ValidateRange(0, 3)] + [System.UInt16] + $FileStreamLevel, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [System.String] + $FileStreamShareName, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'Upgrade')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [System.String] + $ISSvcAccount, + + [Parameter(ParameterSetName = 'UsingConfigurationFile')] + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'Upgrade')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [System.Security.SecureString] + $ISSvcPassword, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'Upgrade')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [ValidateSet('Automatic', 'Disabled', 'Manual')] + [System.String] + $ISSvcStartupType, + + [Parameter(ParameterSetName = 'Upgrade')] + [System.Management.Automation.SwitchParameter] + $AllowUpgradeForSSRSSharePointMode, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [System.Management.Automation.SwitchParameter] + $NpEnabled, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [System.Management.Automation.SwitchParameter] + $TcpEnabled, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [ValidateSet('SharePointFilesOnlyMode', 'DefaultNativeMode', 'FilesOnlyMode')] + [System.String] + $RsInstallMode, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [System.String] + $RSSvcAccount, + + [Parameter(ParameterSetName = 'UsingConfigurationFile')] + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [System.Security.SecureString] + $RSSvcPassword, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'PrepareFailoverCluster')] + [ValidateSet('Automatic', 'Disabled', 'Manual')] + [System.String] + $RSSvcStartupType, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [System.String] + $MPYCacheDirectory, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [System.String] + $MRCacheDirectory, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [System.Management.Automation.SwitchParameter] + $SqlInstJava, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [System.String] + $SqlJavaDir, + + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [System.String] + $FailoverClusterGroup, + + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [System.String[]] + $FailoverClusterDisks, + + [Parameter(ParameterSetName = 'InstallFailoverCluster', Mandatory = $true)] + [System.String] + $FailoverClusterNetworkName, + + [Parameter(ParameterSetName = 'InstallFailoverCluster', Mandatory = $true)] + [System.String[]] + $FailoverClusterIPAddresses, + + [Parameter(ParameterSetName = 'Upgrade')] + [ValidateRange(0, 2)] + [System.UInt16] + $FailoverClusterRollOwnership, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallAzureArcAgent', Mandatory = $true)] + [System.String] + $AzureSubscriptionId, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallAzureArcAgent', Mandatory = $true)] + [System.String] + $AzureResourceGroup, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallAzureArcAgent', Mandatory = $true)] + [System.String] + $AzureRegion, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallAzureArcAgent', Mandatory = $true)] + [System.String] + $AzureTenantId, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallAzureArcAgent', Mandatory = $true)] + [System.String] + $AzureServicePrincipal, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallAzureArcAgent', Mandatory = $true)] + [System.Security.SecureString] + $AzureServicePrincipalSecret, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallAzureArcAgent')] + [System.String] + $AzureArcProxy, + + [Parameter(ParameterSetName = 'Install')] + [Parameter(ParameterSetName = 'InstallRole')] + [Parameter(ParameterSetName = 'InstallFailoverCluster')] + [Parameter(ParameterSetName = 'EditionUpgrade')] + [System.String[]] + $SkipRules, + + [Parameter()] + [System.UInt32] + $Timeout = 7200, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + Invoke-SetupAction @PSBoundParameters -ErrorAction 'Stop' +} diff --git a/source/Public/Remove-SqlDscNode.ps1 b/source/Public/Remove-SqlDscNode.ps1 new file mode 100644 index 000000000..32bd8ed20 --- /dev/null +++ b/source/Public/Remove-SqlDscNode.ps1 @@ -0,0 +1,62 @@ +<# + .SYNOPSIS + Removes a SQL Server node from an Failover Cluster instance (FCI). + + .DESCRIPTION + Removes a SQL Server node from an Failover Cluster instance (FCI). + + See the link in the commands help for information on each parameter. The + link points to SQL Server command line setup documentation. + + .PARAMETER MediaPath + Specifies the path where to find the SQL Server installation media. On this + path the SQL Server setup executable must be found. + + .LINK + https://docs.microsoft.com/en-us/sql/database-engine/install-windows/install-sql-server-from-the-command-prompt + + .OUTPUTS + None. + + .EXAMPLE + Remove-SqlDscNode -InstanceName 'MyInstance' -MediaPath 'E:\' + + Removes the current node's SQL Server instance 'MyInstance' from the + Failover Cluster instance. + + .NOTES + All parameters has intentionally not been added to this comment-based help + since it would take a lot of effort to keep it up to date. Instead there is + a link in the comment-based help that points to the SQL Server command line + setup documentation which will stay relevant. +#> +function Remove-SqlDscNode +{ + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '', Justification = 'Because ShouldProcess is used in Invoke-SetupAction')] + [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')] + [OutputType()] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $MediaPath, + + [Parameter(Mandatory = $true)] + [System.String] + $InstanceName, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $ConfirmIPDependencyChange, + + [Parameter()] + [System.UInt32] + $Timeout = 7200, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + Invoke-SetupAction -RemoveNode @PSBoundParameters -ErrorAction 'Stop' +} diff --git a/source/Public/Repair-SqlDscServer.ps1 b/source/Public/Repair-SqlDscServer.ps1 new file mode 100644 index 000000000..af3a73218 --- /dev/null +++ b/source/Public/Repair-SqlDscServer.ps1 @@ -0,0 +1,124 @@ +<# + .SYNOPSIS + Executes an setup action using Microsoft SQL Server setup executable. + + .DESCRIPTION + Executes an setup action using Microsoft SQL Server setup executable. + + See the link in the commands help for information on each parameter. The + link points to SQL Server command line setup documentation. + + .PARAMETER MediaPath + Specifies the path where to find the SQL Server installation media. On this + path the SQL Server setup executable must be found. + + .LINK + https://docs.microsoft.com/en-us/sql/database-engine/install-windows/install-sql-server-from-the-command-prompt + + .OUTPUTS + None. + + .EXAMPLE + Repair-SqlDscServer -InstanceName 'MyInstance' -Features 'SQLENGINE' -MediaPath 'E:\' + + Repairs the database engine of the instance 'MyInstance'. + + .NOTES + All parameters has intentionally not been added to this comment-based help + since it would take a lot of effort to keep it up to date. Instead there is + a link in the comment-based help that points to the SQL Server command line + setup documentation which will stay relevant. +#> +function Repair-SqlDscServer +{ + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '', Justification = 'Because ShouldProcess is used in Invoke-SetupAction')] + [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')] + [OutputType()] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $MediaPath, + + [Parameter(Mandatory = $true)] + [System.String] + $InstanceName, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Enu, + + [Parameter(Mandatory = $true)] + [ValidateSet( + 'SQL', + 'SQLEngine', # Part of parent feature SQL + 'Replication', # Part of parent feature SQL + 'FullText', # Part of parent feature SQL + 'DQ', # Part of parent feature SQL + 'PolyBase', # Part of parent feature SQL + 'PolyBaseCore', # Part of parent feature SQL + 'PolyBaseJava', # Part of parent feature SQL + 'AdvancedAnalytics', # Part of parent feature SQL + 'SQL_INST_MR', # Part of parent feature SQL + 'SQL_INST_MPY', # Part of parent feature SQL + 'SQL_INST_JAVA', # Part of parent feature SQL + 'AS', + 'RS', + 'RS_SHP', + 'RS_SHPWFE', # cspell: disable-line + 'DQC', + 'IS', + 'IS_Master', # Part of parent feature IS + 'IS_Worker', # Part of parent feature IS + 'MDS', + 'SQL_SHARED_MPY', + 'SQL_SHARED_MR', + 'Tools', + 'BC', # Part of parent feature Tools + 'Conn', # Part of parent feature Tools + 'DREPLAY_CTLR', # Part of parent feature Tools (cspell: disable-line) + 'DREPLAY_CLT', # Part of parent feature Tools (cspell: disable-line) + 'SNAC_SDK', # Part of parent feature Tools (cspell: disable-line) + 'SDK', # Part of parent feature Tools + 'LocalDB', # Part of parent feature Tools + 'ARC' + )] + [System.String[]] + $Features, + + [Parameter()] + [System.String] + $PBEngSvcAccount, + + [Parameter()] + [System.Security.SecureString] + $PBEngSvcPassword, + + [Parameter()] + [ValidateSet('Automatic', 'Disabled', 'Manual')] + [System.String] + $PBEngSvcStartupType, + + [Parameter()] + [System.UInt16] + $PBStartPortRange, + + [Parameter()] + [System.UInt16] + $PBEndPortRange, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $PBScaleOut, + + [Parameter()] + [System.UInt32] + $Timeout = 7200, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + Invoke-SetupAction -Repair @PSBoundParameters -ErrorAction 'Stop' +} diff --git a/source/Public/Uninstall-SqlDscServer.ps1 b/source/Public/Uninstall-SqlDscServer.ps1 new file mode 100644 index 000000000..1b9e36a49 --- /dev/null +++ b/source/Public/Uninstall-SqlDscServer.ps1 @@ -0,0 +1,96 @@ +<# + .SYNOPSIS + Uninstall features from a Microsoft SQL Server instance. + + .DESCRIPTION + Uninstall features from a Microsoft SQL Server instance. + + See the link in the commands help for information on each parameter for + the setup action Uninstall. The link points to SQL Server command line + setup documentation. + + .PARAMETER MediaPath + Specifies the path where to find the SQL Server installation media. On this + path the SQL Server setup executable must be found. + + .LINK + https://docs.microsoft.com/en-us/sql/database-engine/install-windows/install-sql-server-from-the-command-prompt + + .OUTPUTS + None. + + .EXAMPLE + Uninstall-SqlDscServer -InstanceName 'MyInstance' -Features 'SQLENGINE' -MediaPath 'E:\' + + Uninstalls the database engine from the named instance MyInstance. + + .NOTES + All parameters has intentionally not been added to this comment-based help + since it would take a lot of effort to keep it up to date. Instead there is + a link in the comment-based help that points to the SQL Server command line + setup documentation which will stay relevant. +#> +function Uninstall-SqlDscServer +{ + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '', Justification = 'Because ShouldProcess is used in Invoke-SetupAction')] + [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')] + [OutputType()] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $MediaPath, + + [Parameter(Mandatory = $true)] + [System.String] + $InstanceName, + + [Parameter()] + [ValidateSet( + 'SQL', + 'SQLEngine', # Part of parent feature SQL + 'Replication', # Part of parent feature SQL + 'FullText', # Part of parent feature SQL + 'DQ', # Part of parent feature SQL + 'PolyBase', # Part of parent feature SQL + 'PolyBaseCore', # Part of parent feature SQL + 'PolyBaseJava', # Part of parent feature SQL + 'AdvancedAnalytics', # Part of parent feature SQL + 'SQL_INST_MR', # Part of parent feature SQL + 'SQL_INST_MPY', # Part of parent feature SQL + 'SQL_INST_JAVA', # Part of parent feature SQL + 'AS', + 'RS', + 'RS_SHP', + 'RS_SHPWFE', # cspell: disable-line + 'DQC', + 'IS', + 'IS_Master', # Part of parent feature IS + 'IS_Worker', # Part of parent feature IS + 'MDS', + 'SQL_SHARED_MPY', + 'SQL_SHARED_MR', + 'Tools', + 'BC', # Part of parent feature Tools + 'Conn', # Part of parent feature Tools + 'DREPLAY_CTLR', # Part of parent feature Tools (cspell: disable-line) + 'DREPLAY_CLT', # Part of parent feature Tools (cspell: disable-line) + 'SNAC_SDK', # Part of parent feature Tools (cspell: disable-line) + 'SDK', # Part of parent feature Tools + 'LocalDB', # Part of parent feature Tools + 'ARC' + )] + [System.String[]] + $Features, + + [Parameter()] + [System.UInt32] + $Timeout = 7200, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + Invoke-SetupAction -Uninstall @PSBoundParameters -ErrorAction 'Stop' +} diff --git a/source/en-US/SqlServerDsc.strings.psd1 b/source/en-US/SqlServerDsc.strings.psd1 index bc6b852a3..95702bf56 100644 --- a/source/en-US/SqlServerDsc.strings.psd1 +++ b/source/en-US/SqlServerDsc.strings.psd1 @@ -16,7 +16,7 @@ ConvertFrom-StringData @' DatabasePermission_RevokePermission = Revoke the permissions '{0}' for the principal '{1}'. DatabasePermission_IgnoreWithGrantForStateDeny = The parameter WithGrant cannot be used together with the state Deny, the parameter WithGrant is ignored. DatabasePermission_ChangePermissionShouldProcessVerboseDescription = Changing the permission for the principal '{0}' in the database '{1}' on the instance '{2}'. - DatabasePermission_ChangePermissionShouldProcessVerboseWarning = Are you sure you want you change the permission for the principal '{0}'? + DatabasePermission_ChangePermissionShouldProcessVerboseWarning = Are you sure you want to change the permission for the principal '{0}'? # This string shall not end with full stop (.) since it is used as a title of ShouldProcess messages. DatabasePermission_ChangePermissionShouldProcessCaption = Change permission on principal @@ -29,7 +29,7 @@ ConvertFrom-StringData @' ## Set-SqlDscServerPermission ServerPermission_IgnoreWithGrantForStateDeny = The parameter WithGrant cannot be used together with the state Deny, the parameter WithGrant is ignored. ServerPermission_ChangePermissionShouldProcessVerboseDescription = Changing the permission for the principal '{0}' on the instance '{1}'. - ServerPermission_ChangePermissionShouldProcessVerboseWarning = Are you sure you want you change the permission for the principal '{0}'? + ServerPermission_ChangePermissionShouldProcessVerboseWarning = Are you sure you want to change the permission for the principal '{0}'? # This string shall not end with full stop (.) since it is used as a title of ShouldProcess messages. ServerPermission_ChangePermissionShouldProcessCaption = Change permission on principal ServerPermission_GrantPermission = Grant the permissions '{0}' for the principal '{1}'. @@ -44,14 +44,14 @@ ConvertFrom-StringData @' ## New-SqlDscAudit Audit_Add_ShouldProcessVerboseDescription = Adding the audit '{0}' on the instance '{1}'. - Audit_Add_ShouldProcessVerboseWarning = Are you sure you want you add the audit '{0}'? + Audit_Add_ShouldProcessVerboseWarning = Are you sure you want to add the audit '{0}'? # This string shall not end with full stop (.) since it is used as a title of ShouldProcess messages. Audit_Add_ShouldProcessCaption = Add audit on instance Audit_AlreadyPresent = There is already an audit with the name '{0}'. ## Set-SqlDscAudit Audit_Update_ShouldProcessVerboseDescription = Updating the audit '{0}' on the instance '{1}'. - Audit_Update_ShouldProcessVerboseWarning = Are you sure you want you update the audit '{0}'? + Audit_Update_ShouldProcessVerboseWarning = Are you sure you want to update the audit '{0}'? # This string shall not end with full stop (.) since it is used as a title of ShouldProcess messages. Audit_Update_ShouldProcessCaption = Update audit on instance Audit_MaximumFileSizeParameterValueInvalid = The maximum file size must be set to a value of 0 or a value between 2 and 2147483647. @@ -62,19 +62,43 @@ ConvertFrom-StringData @' ## Remove-SqlDscAudit Audit_Remove_ShouldProcessVerboseDescription = Removing the audit '{0}' on the instance '{1}'. - Audit_Remove_ShouldProcessVerboseWarning = Are you sure you want you remove the audit '{0}'? + Audit_Remove_ShouldProcessVerboseWarning = Are you sure you want to remove the audit '{0}'? # This string shall not end with full stop (.) since it is used as a title of ShouldProcess messages. Audit_Remove_ShouldProcessCaption = Remove audit on instance ## Enable-SqlDscAudit Audit_Enable_ShouldProcessVerboseDescription = Enabling the audit '{0}' on the instance '{1}'. - Audit_Enable_ShouldProcessVerboseWarning = Are you sure you want you enable the audit '{0}'? + Audit_Enable_ShouldProcessVerboseWarning = Are you sure you want to enable the audit '{0}'? # This string shall not end with full stop (.) since it is used as a title of ShouldProcess messages. Audit_Enable_ShouldProcessCaption = Enable audit on instance ## Disable-SqlDscAudit Audit_Disable_ShouldProcessVerboseDescription = Disabling the audit '{0}' on the instance '{1}'. - Audit_Disable_ShouldProcessVerboseWarning = Are you sure you want you disable the audit '{0}'? + Audit_Disable_ShouldProcessVerboseWarning = Are you sure you want to disable the audit '{0}'? # This string shall not end with full stop (.) since it is used as a title of ShouldProcess messages. Audit_Disable_ShouldProcessCaption = Disable audit on instance + + ## Install-SqlDscServer + Server_Install_ShouldProcessVerboseDescription = Invoking the Microsoft SQL Server setup action '{0}'. + Server_Install_ShouldProcessVerboseWarning = Are you sure you want to invoke the setup action '{0}'? + # This string shall not end with full stop (.) since it is used as a title of ShouldProcess messages. + Server_Install_ShouldProcessCaption = Invoke a Microsoft SQL Server setup action + Server_SetupExitMessage = Setup exited with code '{0}'. + Server_SetupSuccessful = Setup finished successfully. + Server_SetupSuccessfulRebootRequired = Setup finished successfully, but a reboot is required. + Server_SetupFailed = Please see the 'Summary.txt' log file in the 'Setup Bootstrap\\Log' folder. + Server_SetupArguments = Specified setup executable arguments: {0} + Server_MediaPathNotFound = The specified media path does not exist or does not contain 'setup.exe'. + Server_ConfigurationFileNotFound = The specified configuration file was not found. + + ## Assert-RequiredCommandParameter + RequiredCommandParameter_SpecificParametersMustAllBeSet = The parameters '{0}' must all be specified. + RequiredCommandParameter_SpecificParametersMustAllBeSetWhenParameterExist = The parameters '{0}' must all be specified if either parameter '{1}' is specified. + + ## Assert-IsElevated + IsElevated_UserNotElevated = This command must run in an elevated PowerShell session. + + ## Assert-SetupActionProperties + InstallSqlServerProperties_ASServerModeInvalidValue = The value for ASServerMode is not valid for the setup action {0}. + InstallSqlServerProperties_RsInstallModeInvalidValue = The only valid value for RsInstallMode is 'FilesOnlyMode' when using setup action {0}. '@ diff --git a/tests/Unit/Private/Assert-ElevatedUser.Tests.ps1 b/tests/Unit/Private/Assert-ElevatedUser.Tests.ps1 new file mode 100644 index 000000000..41ac2b0d5 --- /dev/null +++ b/tests/Unit/Private/Assert-ElevatedUser.Tests.ps1 @@ -0,0 +1,114 @@ +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] +param () + +BeforeDiscovery { + try + { + if (-not (Get-Module -Name 'DscResource.Test')) + { + # Assumes dependencies has been resolved, so if this module is not available, run 'noop' task. + if (-not (Get-Module -Name 'DscResource.Test' -ListAvailable)) + { + # Redirect all streams to $null, except the error stream (stream 2) + & "$PSScriptRoot/../../build.ps1" -Tasks 'noop' 2>&1 4>&1 5>&1 6>&1 > $null + } + + # If the dependencies has not been resolved, this will throw an error. + Import-Module -Name 'DscResource.Test' -Force -ErrorAction 'Stop' + } + } + catch [System.IO.FileNotFoundException] + { + throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -ResolveDependency -Tasks build" first.' + } +} + +BeforeAll { + $script:dscModuleName = 'SqlServerDsc' + + Import-Module -Name $script:dscModuleName + + $PSDefaultParameterValues['InModuleScope:ModuleName'] = $script:dscModuleName + $PSDefaultParameterValues['Mock:ModuleName'] = $script:dscModuleName + $PSDefaultParameterValues['Should:ModuleName'] = $script:dscModuleName +} + +AfterAll { + $PSDefaultParameterValues.Remove('InModuleScope:ModuleName') + $PSDefaultParameterValues.Remove('Mock:ModuleName') + $PSDefaultParameterValues.Remove('Should:ModuleName') + + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force +} + +Describe 'Assert-ElevatedUser' -Tag 'Private' { + BeforeDiscovery { + <# + Since it is not possible to elevated (or un-elevate) a user during testing + the test that cannot run need to be skipped. + #> + if ($IsMacOS -or $IsLinux) + { + $mockIsElevated = (id -u) -eq 0 + } + else + { + [Security.Principal.WindowsPrincipal] $mockCurrentUser = [Security.Principal.WindowsIdentity]::GetCurrent() + + $mockIsElevated = $mockCurrentUser.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator) + } + } + + It 'Should throw the correct error' -Skip:$mockIsElevated { + InModuleScope -ScriptBlock { + $mockErrorMessage = $script:localizedData.IsElevated_UserNotElevated + + { Assert-ElevatedUser } | Should -Throw -ExpectedMessage $mockErrorMessage + } + } + + It 'Should not throw an exception' -Skip:(-not $mockIsElevated) { + InModuleScope -ScriptBlock { + { Assert-ElevatedUser } | Should -Not -Throw + } + } + + Context 'When on Linux or macOS' { + BeforeAll { + $previousIsMacOS = InModuleScope -ScriptBlock { + $IsMacOS + } + + InModuleScope -ScriptBlock { + $script:IsMacOS = $true + + # Stub for command 'id'. + function id + { + throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand + } + + Mock -CommandName id -MockWith { + return 0 + } + } + } + + AfterAll { + $inModuleScopeParameters = @{ + PreviousIsMacOS = $previousIsMacOS + } + + InModuleScope -Parameters $inModuleScopeParameters -ScriptBlock { + $script:IsMacOS = $PreviousIsMacOS + } + } + + It 'Should not throw an exception' { + InModuleScope -ScriptBlock { + { Assert-ElevatedUser } | Should -Not -Throw + } + } + } +} diff --git a/tests/Unit/Private/Assert-RequiredCommandParameter.Tests.ps1 b/tests/Unit/Private/Assert-RequiredCommandParameter.Tests.ps1 new file mode 100644 index 000000000..2805bb04e --- /dev/null +++ b/tests/Unit/Private/Assert-RequiredCommandParameter.Tests.ps1 @@ -0,0 +1,107 @@ +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] +param () + +BeforeDiscovery { + try + { + if (-not (Get-Module -Name 'DscResource.Test')) + { + # Assumes dependencies has been resolved, so if this module is not available, run 'noop' task. + if (-not (Get-Module -Name 'DscResource.Test' -ListAvailable)) + { + # Redirect all streams to $null, except the error stream (stream 2) + & "$PSScriptRoot/../../build.ps1" -Tasks 'noop' 2>&1 4>&1 5>&1 6>&1 > $null + } + + # If the dependencies has not been resolved, this will throw an error. + Import-Module -Name 'DscResource.Test' -Force -ErrorAction 'Stop' + } + } + catch [System.IO.FileNotFoundException] + { + throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -ResolveDependency -Tasks build" first.' + } +} + +BeforeAll { + $script:dscModuleName = 'SqlServerDsc' + + Import-Module -Name $script:dscModuleName + + $PSDefaultParameterValues['InModuleScope:ModuleName'] = $script:dscModuleName + $PSDefaultParameterValues['Mock:ModuleName'] = $script:dscModuleName + $PSDefaultParameterValues['Should:ModuleName'] = $script:dscModuleName +} + +AfterAll { + $PSDefaultParameterValues.Remove('InModuleScope:ModuleName') + $PSDefaultParameterValues.Remove('Mock:ModuleName') + $PSDefaultParameterValues.Remove('Should:ModuleName') + + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force +} + +Describe 'Assert-RequiredCommandParameter' -Tag 'Private' { + Context 'When required parameter is missing' { + It 'Should throw the correct error' { + InModuleScope -ScriptBlock { + $mockErrorMessage = $script:localizedData.RequiredCommandParameter_SpecificParametersMustAllBeSet -f 'Parameter1' + + { Assert-RequiredCommandParameter -BoundParameter @{} -RequiredParameter 'Parameter1' } | + Should -Throw -ExpectedMessage $mockErrorMessage + } + } + } + + Context 'When the parameter in IfParameterPresent is not present' { + It 'Should not throw an error' { + InModuleScope -ScriptBlock { + { Assert-RequiredCommandParameter -BoundParameter @{} -RequiredParameter 'Parameter1' -IfParameterPresent 'Parameter2' } | + Should -Not -Throw + } + } + } + + Context 'When the parameter in IfParameterPresent is not present' { + It 'Should throw the correct error' { + InModuleScope -ScriptBlock { + $mockErrorMessage = $script:localizedData.RequiredCommandParameter_SpecificParametersMustAllBeSetWhenParameterExist -f 'Parameter1', 'Parameter2' + + { + Assert-RequiredCommandParameter -BoundParameter @{ + Parameter2 = 'Value2' + } -RequiredParameter 'Parameter1' -IfParameterPresent 'Parameter2' + } | Should -Throw -ExpectedMessage $mockErrorMessage + } + } + } + + Context 'When the parameters in IfParameterPresent is present and the required parameters are not present' { + It 'Should throw the correct error' { + InModuleScope -ScriptBlock { + $mockErrorMessage = $script:localizedData.RequiredCommandParameter_SpecificParametersMustAllBeSetWhenParameterExist -f "Parameter3', 'Parameter4", "Parameter1', 'Parameter2" + + { + Assert-RequiredCommandParameter -BoundParameter @{ + Parameter1 = 'Value1' + Parameter2 = 'Value2' + } -RequiredParameter @('Parameter3', 'Parameter4') -IfParameterPresent @('Parameter1', 'Parameter2') + } | Should -Throw -ExpectedMessage $mockErrorMessage + } + } + } + + Context 'When the parameters in IfParameterPresent is present and required parameters are present' { + It 'Should throw the correct error' { + InModuleScope -ScriptBlock { + { + Assert-RequiredCommandParameter -BoundParameter @{ + Parameter1 = 'Value1' + Parameter2 = 'Value2' + } -RequiredParameter @('Parameter1', 'Parameter2') -IfParameterPresent @('Parameter1', 'Parameter2') + } | Should -Not -Throw + } + } + } +} diff --git a/tests/Unit/Private/Assert-SetupActionProperties.Tests.ps1 b/tests/Unit/Private/Assert-SetupActionProperties.Tests.ps1 new file mode 100644 index 000000000..ffe2fcc51 --- /dev/null +++ b/tests/Unit/Private/Assert-SetupActionProperties.Tests.ps1 @@ -0,0 +1,538 @@ +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] +param () + +BeforeDiscovery { + try + { + if (-not (Get-Module -Name 'DscResource.Test')) + { + # Assumes dependencies has been resolved, so if this module is not available, run 'noop' task. + if (-not (Get-Module -Name 'DscResource.Test' -ListAvailable)) + { + # Redirect all streams to $null, except the error stream (stream 2) + & "$PSScriptRoot/../../build.ps1" -Tasks 'noop' 2>&1 4>&1 5>&1 6>&1 > $null + } + + # If the dependencies has not been resolved, this will throw an error. + Import-Module -Name 'DscResource.Test' -Force -ErrorAction 'Stop' + } + } + catch [System.IO.FileNotFoundException] + { + throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -ResolveDependency -Tasks build" first.' + } +} + +BeforeAll { + $script:dscModuleName = 'SqlServerDsc' + + Import-Module -Name $script:dscModuleName + + $PSDefaultParameterValues['InModuleScope:ModuleName'] = $script:dscModuleName + $PSDefaultParameterValues['Mock:ModuleName'] = $script:dscModuleName + $PSDefaultParameterValues['Should:ModuleName'] = $script:dscModuleName +} + +AfterAll { + $PSDefaultParameterValues.Remove('InModuleScope:ModuleName') + $PSDefaultParameterValues.Remove('Mock:ModuleName') + $PSDefaultParameterValues.Remove('Should:ModuleName') + + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force +} + +Describe 'Assert-SetupActionProperties' -Tag 'Private' { + Context 'When all properties are valid for setup action ''''' -ForEach @( + @{ + MockSetupAction = 'Install' + } + @{ + MockSetupAction = 'Uninstall' + } + @{ + MockSetupAction = 'InstallRole' + } + @{ + MockSetupAction = 'InstallAzureArcAgent' + } + @{ + MockSetupAction = 'UsingConfigurationFile' + } + @{ + MockSetupAction = 'PrepareImage' + } + @{ + MockSetupAction = 'CompleteImage' + } + @{ + MockSetupAction = 'Upgrade' + } + @{ + MockSetupAction = 'EditionUpgrade' + } + @{ + MockSetupAction = 'Repair' + } + @{ + MockSetupAction = 'RebuildDatabase' + } + @{ + MockSetupAction = 'InstallFailoverCluster' + } + @{ + MockSetupAction = 'PrepareFailoverCluster' + } + @{ + MockSetupAction = 'CompleteFailoverCluster' + } + @{ + MockSetupAction = 'AddNode' + } + @{ + MockSetupAction = 'RemoveNode' + } + ) { + It 'Should not throw an exception' { + InModuleScope -Parameters $_ -ScriptBlock { + { + Assert-SetupActionProperties -Property @{ + ValidProperty = 'Value' + } -SetupAction $MockSetupAction + } | Should -Not -Throw + } + } + } + + Context 'When passing only parameter ''''' -ForEach @( + @{ + MockParameterName = 'PBStartPortRange' + } + @{ + MockParameterName = 'PBEndPortRange' + } + ) { + It 'Should throw the correct error' { + InModuleScope -Parameters $_ -ScriptBlock { + { + Assert-SetupActionProperties -Property @{ + $MockParameterName = 'Value' + } -SetupAction 'NotUsed' + } | Should -Throw -ErrorId 'ARCP0001,Assert-RequiredCommandParameter' # cSpell: disable-line + } + } + } + + Context 'When specifying role ''SPI_AS_NewFarm'' and required parameter '''' is missing' -ForEach @( + @{ + MockParameters = @{ + FarmPassword = 'Value' + Passphrase = 'Value' + FarmAdminiPort = 'Value' # cspell: disable-line + } + MockMissingParameterName = 'FarmAccount' + } + @{ + MockParameters = @{ + FarmAccount = 'Value' + Passphrase = 'Value' + FarmAdminiPort = 'Value' # cspell: disable-line + } + MockMissingParameterName = 'FarmPassword' + } + @{ + MockParameters = @{ + FarmAccount = 'Value' + FarmPassword = 'Value' + FarmAdminiPort = 'Value' # cspell: disable-line + } + MockMissingParameterName = 'Passphrase' + } + @{ + MockParameters = @{ + FarmAccount = 'Value' + FarmPassword = 'Value' + Passphrase = 'Value' + } + MockMissingParameterName = 'FarmAdminiPort' # cspell: disable-line + } + ) { + It 'Should throw the correct error' { + InModuleScope -Parameters $_ -ScriptBlock { + { + $MockParameters.Role = 'SPI_AS_NewFarm' + + Assert-SetupActionProperties -Property $MockParameters -SetupAction 'NotUsed' + } | Should -Throw -ErrorId 'ARCP0001,Assert-RequiredCommandParameter' # cSpell: disable-line + } + } + } + + Context 'When specifying security mode ''SQL'' and required parameter ''SAPwd'' is missing' { + It 'Should throw the correct error' { + InModuleScope -ScriptBlock { + { + Assert-SetupActionProperties -Property @{ + SecurityMode = 'SQL' + } -SetupAction 'NotUsed' + } | Should -Throw -ErrorId 'ARCP0001,Assert-RequiredCommandParameter' # cSpell: disable-line + } + } + } + + Context 'When file stream level is set to <_> and ''FileStreamShareName'' is missing' -ForEach @(0, 1) { + It 'Should not throw an exception' { + InModuleScope -Parameters @{ + MockFileStreamLevel = $_ + } -ScriptBlock { + { + Assert-SetupActionProperties -Property @{ + FileStreamLevel = $MockFileStreamLevel + } -SetupAction 'NotUsed' + } | Should -Not -Throw + } + } + } + + Context 'When file stream level is set to <_> and ''FileStreamShareName'' is missing' -ForEach @(2, 3) { + It 'Should throw the correct error' { + InModuleScope -Parameters @{ + MockFileStreamLevel = $_ + } -ScriptBlock { + { + Assert-SetupActionProperties -Property @{ + FileStreamLevel = $MockFileStreamLevel + } -SetupAction 'NotUsed' + } | Should -Throw -ErrorId 'ARCP0001,Assert-RequiredCommandParameter' # cSpell: disable-line + } + } + } + + Context 'When specifying an account-parameter without the corresponding password-parameter' -ForEach @( + @{ + MockParameterName = 'PBEngSvcAccount' + } + @{ + MockParameterName = 'PBDMSSvcAccount' # cSpell: disable-line + } + @{ + MockParameterName = 'AgtSvcAccount' + } + @{ + MockParameterName = 'ASSvcAccount' + } + @{ + MockParameterName = 'FarmAccount' + } + @{ + MockParameterName = 'SqlSvcAccount' + } + @{ + MockParameterName = 'ISSvcAccount' + } + @{ + MockParameterName = 'RSSvcAccount' + } + ) { + It 'Should throw the correct error' { + InModuleScope -Parameters $_ -ScriptBlock { + { + Assert-SetupActionProperties -Property @{ + $MockParameterName = 'AccountName' + } -SetupAction 'NotUsed' + } | Should -Throw -ErrorId 'ARCP0001,Assert-RequiredCommandParameter' # cSpell: disable-line + } + } + } + + Context 'When specifying an account-parameter with the corresponding password-parameter' -ForEach @( + @{ + MockParameterName = 'PBEngSvcAccount' + } + @{ + MockParameterName = 'PBDMSSvcAccount' # cSpell: disable-line + } + @{ + MockParameterName = 'AgtSvcAccount' + } + @{ + MockParameterName = 'ASSvcAccount' + } + @{ + MockParameterName = 'FarmAccount' + } + @{ + MockParameterName = 'SqlSvcAccount' + } + @{ + MockParameterName = 'ISSvcAccount' + } + @{ + MockParameterName = 'RSSvcAccount' + } + ) { + It 'Should not throw an exception' { + InModuleScope -Parameters $_ -ScriptBlock { + { + Assert-SetupActionProperties -Property @{ + $MockParameterName = 'AccountName' + ($MockParameterName -replace 'Account', 'Password') = 'Password' + } -SetupAction 'NotUsed' + } | Should -Not -Throw + } + } + } + + Context 'When specifying an account-parameter that specifies a (global) managed service account, virtual account, or built-in account' -ForEach @( + @{ + MockParameterName = 'PBEngSvcAccount' + } + @{ + MockParameterName = 'PBDMSSvcAccount' # cSpell: disable-line + } + @{ + MockParameterName = 'AgtSvcAccount' + } + @{ + MockParameterName = 'ASSvcAccount' + } + @{ + MockParameterName = 'FarmAccount' + } + @{ + MockParameterName = 'SqlSvcAccount' + } + @{ + MockParameterName = 'ISSvcAccount' + } + @{ + MockParameterName = 'RSSvcAccount' + } + ) { + BeforeAll { + Mock -CommandName Test-ServiceAccountRequirePassword -MockWith { + return $false + } + } + + It 'Should not throw an exception' { + InModuleScope -Parameters $_ -ScriptBlock { + { + Assert-SetupActionProperties -Property @{ + $MockParameterName = 'myMSA$' + } -SetupAction 'NotUsed' + } | Should -Not -Throw + } + } + } + + Context 'When specifying feature ''ARC'' and required parameter '''' is missing' -ForEach @( + @{ + MockParameters = @{ + AzureResourceGroup = 'Value' + AzureRegion = 'Value' + AzureTenantId = 'Value' + AzureServicePrincipal = 'Value' + AzureServicePrincipalSecret = 'Value' + } + MockMissingParameterName = 'AzureSubscriptionId' + } + @{ + MockParameters = @{ + AzureSubscriptionId = 'Value' + AzureRegion = 'Value' + AzureTenantId = 'Value' + AzureServicePrincipal = 'Value' + AzureServicePrincipalSecret = 'Value' + } + MockMissingParameterName = 'AzureResourceGroup' + } + @{ + MockParameters = @{ + AzureSubscriptionId = 'Value' + AzureResourceGroup = 'Value' + AzureTenantId = 'Value' + AzureServicePrincipal = 'Value' + AzureServicePrincipalSecret = 'Value' + } + MockMissingParameterName = 'AzureRegion' + } + @{ + MockParameters = @{ + AzureSubscriptionId = 'Value' + AzureResourceGroup = 'Value' + AzureRegion = 'Value' + AzureServicePrincipal = 'Value' + AzureServicePrincipalSecret = 'Value' + } + MockMissingParameterName = 'AzureTenantId' # cspell: disable-line + } + @{ + MockParameters = @{ + AzureSubscriptionId = 'Value' + AzureResourceGroup = 'Value' + AzureRegion = 'Value' + AzureTenantId = 'Value' + AzureServicePrincipalSecret = 'Value' + } + MockMissingParameterName = 'AzureServicePrincipal' + } + @{ + MockParameters = @{ + AzureSubscriptionId = 'Value' + AzureResourceGroup = 'Value' + AzureRegion = 'Value' + AzureTenantId = 'Value' + AzureServicePrincipal = 'Value' + } + MockMissingParameterName = 'AzureServicePrincipalSecret' + } + ) { + It 'Should throw the correct error' { + InModuleScope -Parameters $_ -ScriptBlock { + { + $MockParameters.Features = @( + 'SQLENGINE' + 'ARC' + 'AS' + ) + + Assert-SetupActionProperties -Property $MockParameters -SetupAction 'NotUsed' + } | Should -Throw -ErrorId 'ARCP0001,Assert-RequiredCommandParameter' # cSpell: disable-line + } + } + } + + Context 'When setup action is '''' and feature is '''' but parameter '''' is missing' -ForEach @( + @{ + MockSetupAction = 'Install' + MockMissingParameterName = 'AgtSvcAccount' + MockFeature = 'SQLENGINE' + } + @{ + MockSetupAction = 'CompleteImage' + MockMissingParameterName = 'AgtSvcAccount' + MockFeature = 'SQLENGINE' + } + @{ + MockSetupAction = 'InstallFailoverCluster' + MockMissingParameterName = 'AgtSvcAccount' + MockFeature = 'SQLENGINE' + } + @{ + MockSetupAction = 'InstallFailoverCluster' + MockMissingParameterName = 'ASSvcAccount' + MockFeature = 'AS' + } + @{ + MockSetupAction = 'InstallFailoverCluster' + MockMissingParameterName = 'SqlSvcAccount' + MockFeature = 'SQLENGINE' + } + @{ + MockSetupAction = 'InstallFailoverCluster' + MockMissingParameterName = 'ISSvcAccount' + MockFeature = 'IS' + } + @{ + MockSetupAction = 'InstallFailoverCluster' + MockMissingParameterName = 'RSSvcAccount' + MockFeature = 'RS' + } + @{ + MockSetupAction = 'PrepareFailoverCluster' + MockMissingParameterName = 'AgtSvcAccount' + MockFeature = 'SQLENGINE' + } + @{ + MockSetupAction = 'PrepareFailoverCluster' + MockMissingParameterName = 'ASSvcAccount' + MockFeature = 'AS' + } + @{ + MockSetupAction = 'PrepareFailoverCluster' + MockMissingParameterName = 'SqlSvcAccount' + MockFeature = 'SQLENGINE' + } + @{ + MockSetupAction = 'PrepareFailoverCluster' + MockMissingParameterName = 'ISSvcAccount' + MockFeature = 'IS' + } + @{ + MockSetupAction = 'PrepareFailoverCluster' + MockMissingParameterName = 'RSSvcAccount' + MockFeature = 'RS' + } + @{ + MockSetupAction = 'AddNode' + MockMissingParameterName = 'AgtSvcAccount' + MockFeature = 'SQLENGINE' + } + @{ + MockSetupAction = 'AddNode' + MockMissingParameterName = 'ASSvcAccount' + MockFeature = 'AS' + } + @{ + MockSetupAction = 'AddNode' + MockMissingParameterName = 'SqlSvcAccount' + MockFeature = 'SQLENGINE' + } + @{ + MockSetupAction = 'AddNode' + MockMissingParameterName = 'ISSvcAccount' + MockFeature = 'IS' + } + @{ + MockSetupAction = 'AddNode' + MockMissingParameterName = 'RSSvcAccount' + MockFeature = 'RS' + } + ) { + It 'Should not throw an exception' { + InModuleScope -Parameters $_ -ScriptBlock { + { + Assert-SetupActionProperties -Property @{ + Features = $MockFeature + } -SetupAction $MockSetupAction + } | Should -Throw -ErrorId 'ARCP0001,Assert-RequiredCommandParameter' # cSpell: disable-line + } + } + } + + Context 'When setup action is '''' and also specifying analysis services server mode ''PowerPivot''' -ForEach @( + @{ + MockSetupAction = 'InstallFailoverCluster' + } + @{ + MockSetupAction = 'CompleteFailoverCluster' + } + ) { + It 'Should not throw an exception' { + InModuleScope -Parameters $_ -ScriptBlock { + { + Assert-SetupActionProperties -Property @{ + ASServerMode = 'PowerPivot' + } -SetupAction $MockSetupAction + } | Should -Throw -ErrorId 'ASAP0001,Assert-SetupActionProperties' # cSpell: disable-line + } + } + } + + Context 'When setup action is '''' and also specifying another reporting services install mode than ''FilesOnlyMode''' -ForEach @( + @{ + MockSetupAction = 'AddNode' + } + ) { + It 'Should not throw an exception' { + InModuleScope -Parameters $_ -ScriptBlock { + { + Assert-SetupActionProperties -Property @{ + RsInstallMode = 'DefaultNativeMode' + } -SetupAction $MockSetupAction + } | Should -Throw -ErrorId 'ASAP0002,Assert-SetupActionProperties' # cSpell: disable-line + } + } + } +} diff --git a/tests/Unit/Private/Invoke-SetupAction.Tests.ps1 b/tests/Unit/Private/Invoke-SetupAction.Tests.ps1 new file mode 100644 index 000000000..c84cf7697 --- /dev/null +++ b/tests/Unit/Private/Invoke-SetupAction.Tests.ps1 @@ -0,0 +1,4191 @@ +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '', Justification = 'because ConvertTo-SecureString is used to simplify the tests.')] +param () + +BeforeDiscovery { + try + { + if (-not (Get-Module -Name 'DscResource.Test')) + { + # Assumes dependencies has been resolved, so if this module is not available, run 'noop' task. + if (-not (Get-Module -Name 'DscResource.Test' -ListAvailable)) + { + # Redirect all streams to $null, except the error stream (stream 2) + & "$PSScriptRoot/../../build.ps1" -Tasks 'noop' 2>&1 4>&1 5>&1 6>&1 > $null + } + + # If the dependencies has not been resolved, this will throw an error. + Import-Module -Name 'DscResource.Test' -Force -ErrorAction 'Stop' + } + } + catch [System.IO.FileNotFoundException] + { + throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -ResolveDependency -Tasks build" first.' + } +} + +BeforeAll { + $script:dscModuleName = 'SqlServerDsc' + + Import-Module -Name $script:dscModuleName + + $PSDefaultParameterValues['InModuleScope:ModuleName'] = $script:dscModuleName + $PSDefaultParameterValues['Mock:ModuleName'] = $script:dscModuleName + $PSDefaultParameterValues['Should:ModuleName'] = $script:dscModuleName +} + +AfterAll { + $PSDefaultParameterValues.Remove('InModuleScope:ModuleName') + $PSDefaultParameterValues.Remove('Mock:ModuleName') + $PSDefaultParameterValues.Remove('Should:ModuleName') + + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force +} + +Describe 'Invoke-SetupAction' -Tag 'Public' { + It 'Should have the correct parameters in parameter set ' -ForEach @( + @{ + MockParameterSetName = 'Install' + # cSpell: disable-next + MockExpectedParameters = '-Install -AcceptLicensingTerms -MediaPath -InstanceName -Features -SqlSysAdminAccounts [-SuppressPrivacyStatementNotice] [-IAcknowledgeEntCalLimits] [-Enu] [-UpdateEnabled] [-UpdateSource ] [-InstallSharedDir ] [-InstallSharedWowDir ] [-InstanceDir ] [-InstanceId ] [-PBEngSvcAccount ] [-PBEngSvcPassword ] [-PBEngSvcStartupType ] [-PBDMSSvcAccount ] [-PBDMSSvcPassword ] [-PBDMSSvcStartupType ] [-PBStartPortRange ] [-PBEndPortRange ] [-PBScaleOut] [-ProductKey ] [-AgtSvcAccount ] [-AgtSvcPassword ] [-AgtSvcStartupType ] [-ASBackupDir ] [-ASCollation ] [-ASConfigDir ] [-ASDataDir ] [-ASLogDir ] [-ASTempDir ] [-ASServerMode ] [-ASSvcAccount ] [-ASSvcPassword ] [-ASSvcStartupType ] [-ASSysAdminAccounts ] [-ASProviderMSOLAP] [-BrowserSvcStartupType ] [-EnableRanU] [-InstallSqlDataDir ] [-SqlBackupDir ] [-SecurityMode ] [-SAPwd ] [-SqlCollation ] [-SqlSvcAccount ] [-SqlSvcPassword ] [-SqlSvcStartupType ] [-SqlTempDbDir ] [-SqlTempDbLogDir ] [-SqlTempDbFileCount ] [-SqlTempDbFileSize ] [-SqlTempDbFileGrowth ] [-SqlTempDbLogFileSize ] [-SqlTempDbLogFileGrowth ] [-SqlUserDbDir ] [-SqlSvcInstantFileInit] [-SqlUserDbLogDir ] [-SqlMaxDop ] [-UseSqlRecommendedMemoryLimits] [-SqlMinMemory ] [-SqlMaxMemory ] [-FileStreamLevel ] [-FileStreamShareName ] [-ISSvcAccount ] [-ISSvcPassword ] [-ISSvcStartupType ] [-NpEnabled] [-TcpEnabled] [-RsInstallMode ] [-RSSvcAccount ] [-RSSvcPassword ] [-RSSvcStartupType ] [-MPYCacheDirectory ] [-MRCacheDirectory ] [-SqlInstJava] [-SqlJavaDir ] [-AzureSubscriptionId ] [-AzureResourceGroup ] [-AzureRegion ] [-AzureTenantId ] [-AzureServicePrincipal ] [-AzureServicePrincipalSecret ] [-AzureArcProxy ] [-SkipRules ] [-Timeout ] [-Force] [-WhatIf] [-Confirm] []' + } + @{ + MockParameterSetName = 'InstallRole' + # cSpell: disable-next + MockExpectedParameters = '-Install -AcceptLicensingTerms -MediaPath -Role [-SuppressPrivacyStatementNotice] [-IAcknowledgeEntCalLimits] [-InstanceName ] [-Enu] [-UpdateEnabled] [-UpdateSource ] [-Features ] [-InstallSharedDir ] [-InstallSharedWowDir ] [-InstanceDir ] [-InstanceId ] [-PBEngSvcAccount ] [-PBEngSvcPassword ] [-PBEngSvcStartupType ] [-PBDMSSvcAccount ] [-PBDMSSvcPassword ] [-PBDMSSvcStartupType ] [-PBStartPortRange ] [-PBEndPortRange ] [-PBScaleOut] [-ProductKey ] [-AgtSvcAccount ] [-AgtSvcPassword ] [-AgtSvcStartupType ] [-ASBackupDir ] [-ASCollation ] [-ASConfigDir ] [-ASDataDir ] [-ASLogDir ] [-ASTempDir ] [-ASServerMode ] [-ASSvcAccount ] [-ASSvcPassword ] [-ASSvcStartupType ] [-ASSysAdminAccounts ] [-ASProviderMSOLAP] [-FarmAccount ] [-FarmPassword ] [-Passphrase ] [-FarmAdminiPort ] [-BrowserSvcStartupType ] [-EnableRanU] [-InstallSqlDataDir ] [-SqlBackupDir ] [-SecurityMode ] [-SAPwd ] [-SqlCollation ] [-AddCurrentUserAsSqlAdmin] [-SqlSvcAccount ] [-SqlSvcPassword ] [-SqlSvcStartupType ] [-SqlSysAdminAccounts ] [-SqlTempDbDir ] [-SqlTempDbLogDir ] [-SqlTempDbFileCount ] [-SqlTempDbFileSize ] [-SqlTempDbFileGrowth ] [-SqlTempDbLogFileSize ] [-SqlTempDbLogFileGrowth ] [-SqlUserDbDir ] [-SqlSvcInstantFileInit] [-SqlUserDbLogDir ] [-SqlMaxDop ] [-UseSqlRecommendedMemoryLimits] [-SqlMinMemory ] [-SqlMaxMemory ] [-FileStreamLevel ] [-FileStreamShareName ] [-ISSvcAccount ] [-ISSvcPassword ] [-ISSvcStartupType ] [-NpEnabled] [-TcpEnabled] [-RsInstallMode ] [-RSSvcAccount ] [-RSSvcPassword ] [-RSSvcStartupType ] [-MPYCacheDirectory ] [-MRCacheDirectory ] [-SqlInstJava] [-SqlJavaDir ] [-AzureSubscriptionId ] [-AzureResourceGroup ] [-AzureRegion ] [-AzureTenantId ] [-AzureServicePrincipal ] [-AzureServicePrincipalSecret ] [-AzureArcProxy ] [-SkipRules ] [-Timeout ] [-Force] [-WhatIf] [-Confirm] []' + } + @{ + MockParameterSetName = 'InstallAzureArcAgent' + # cSpell: disable-next + MockExpectedParameters = '-Install -AcceptLicensingTerms -MediaPath -AzureSubscriptionId -AzureResourceGroup -AzureRegion -AzureTenantId -AzureServicePrincipal -AzureServicePrincipalSecret [-AzureArcProxy ] [-Timeout ] [-Force] [-WhatIf] [-Confirm] []' + } + @{ + MockParameterSetName = 'UsingConfigurationFile' + # cSpell: disable-next + MockExpectedParameters = '-ConfigurationFile -MediaPath [-AgtSvcPassword ] [-ASSvcPassword ] [-SqlSvcPassword ] [-ISSvcPassword ] [-RSSvcPassword ] [-Timeout ] [-Force] [-WhatIf] [-Confirm] []' + } + @{ + MockParameterSetName = 'Uninstall' + # cSpell: disable-next + MockExpectedParameters = '-Uninstall -MediaPath -InstanceName -Features [-Timeout ] [-Force] [-WhatIf] [-Confirm] []' + } + @{ + MockParameterSetName = 'PrepareImage' + # cSpell: disable-next + MockExpectedParameters = '-PrepareImage -AcceptLicensingTerms -MediaPath -InstanceName -Features -InstanceId [-IAcknowledgeEntCalLimits] [-Enu] [-UpdateEnabled] [-UpdateSource ] [-InstallSharedDir ] [-InstanceDir ] [-PBEngSvcAccount ] [-PBEngSvcPassword ] [-PBEngSvcStartupType ] [-PBStartPortRange ] [-PBEndPortRange ] [-PBScaleOut] [-Timeout ] [-Force] [-WhatIf] [-Confirm] []' + } + @{ + MockParameterSetName = 'CompleteImage' + # cSpell: disable-next + MockExpectedParameters = '-CompleteImage -AcceptLicensingTerms -MediaPath [-InstanceName ] [-Enu] [-InstanceId ] [-PBEngSvcAccount ] [-PBEngSvcPassword ] [-PBEngSvcStartupType ] [-PBStartPortRange ] [-PBEndPortRange ] [-PBScaleOut] [-ProductKey ] [-AgtSvcAccount ] [-AgtSvcPassword ] [-AgtSvcStartupType ] [-BrowserSvcStartupType ] [-EnableRanU] [-InstallSqlDataDir ] [-SqlBackupDir ] [-SecurityMode ] [-SAPwd ] [-SqlCollation ] [-SqlSvcAccount ] [-SqlSvcPassword ] [-SqlSvcStartupType ] [-SqlSysAdminAccounts ] [-SqlTempDbDir ] [-SqlTempDbLogDir ] [-SqlTempDbFileCount ] [-SqlTempDbFileSize ] [-SqlTempDbFileGrowth ] [-SqlTempDbLogFileSize ] [-SqlTempDbLogFileGrowth ] [-SqlUserDbDir ] [-SqlUserDbLogDir ] [-FileStreamLevel ] [-FileStreamShareName ] [-NpEnabled] [-TcpEnabled] [-RsInstallMode ] [-RSSvcAccount ] [-RSSvcPassword ] [-RSSvcStartupType ] [-Timeout ] [-Force] [-WhatIf] [-Confirm] []' + } + @{ + MockParameterSetName = 'Upgrade' + # cSpell: disable-next + MockExpectedParameters = '-Upgrade -AcceptLicensingTerms -MediaPath -InstanceName [-Enu] [-UpdateEnabled] [-UpdateSource ] [-InstanceDir ] [-InstanceId ] [-ProductKey ] [-BrowserSvcStartupType ] [-FTUpgradeOption ] [-ISSvcAccount ] [-ISSvcPassword ] [-ISSvcStartupType ] [-AllowUpgradeForSSRSSharePointMode] [-FailoverClusterRollOwnership ] [-Timeout ] [-Force] [-WhatIf] [-Confirm] []' + } + @{ + MockParameterSetName = 'EditionUpgrade' + # cSpell: disable-next + MockExpectedParameters = '-EditionUpgrade -AcceptLicensingTerms -MediaPath -InstanceName -ProductKey [-SkipRules ] [-Timeout ] [-Force] [-WhatIf] [-Confirm] []' + } + @{ + MockParameterSetName = 'Repair' + # cSpell: disable-next + MockExpectedParameters = '-Repair -MediaPath -InstanceName -Features [-Enu] [-PBEngSvcAccount ] [-PBEngSvcPassword ] [-PBEngSvcStartupType ] [-PBStartPortRange ] [-PBEndPortRange ] [-PBScaleOut] [-Timeout ] [-Force] [-WhatIf] [-Confirm] []' + } + @{ + MockParameterSetName = 'RebuildDatabase' + # cSpell: disable-next + MockExpectedParameters = '-RebuildDatabase -MediaPath -InstanceName -SqlSysAdminAccounts [-SAPwd ] [-SqlCollation ] [-SqlTempDbDir ] [-SqlTempDbLogDir ] [-SqlTempDbFileCount ] [-SqlTempDbFileSize ] [-SqlTempDbFileGrowth ] [-SqlTempDbLogFileSize ] [-SqlTempDbLogFileGrowth ] [-Timeout ] [-Force] [-WhatIf] [-Confirm] []' + } + @{ + MockParameterSetName = 'InstallFailoverCluster' + # cSpell: disable-next + MockExpectedParameters = '-InstallFailoverCluster -AcceptLicensingTerms -MediaPath -InstanceName -Features -InstallSqlDataDir -SqlSysAdminAccounts -FailoverClusterNetworkName -FailoverClusterIPAddresses [-IAcknowledgeEntCalLimits] [-Enu] [-UpdateEnabled] [-UpdateSource ] [-InstallSharedDir ] [-InstallSharedWowDir ] [-InstanceDir ] [-InstanceId ] [-PBEngSvcAccount ] [-PBEngSvcPassword ] [-PBEngSvcStartupType ] [-PBStartPortRange ] [-PBEndPortRange ] [-PBScaleOut] [-ProductKey ] [-AgtSvcAccount ] [-AgtSvcPassword ] [-ASBackupDir ] [-ASCollation ] [-ASConfigDir ] [-ASDataDir ] [-ASLogDir ] [-ASTempDir ] [-ASServerMode ] [-ASSvcAccount ] [-ASSvcPassword ] [-ASSvcStartupType ] [-ASSysAdminAccounts ] [-ASProviderMSOLAP] [-SqlBackupDir ] [-SecurityMode ] [-SAPwd ] [-SqlCollation ] [-SqlSvcAccount ] [-SqlSvcPassword ] [-SqlSvcStartupType ] [-SqlTempDbDir ] [-SqlTempDbLogDir ] [-SqlTempDbFileCount ] [-SqlTempDbFileSize ] [-SqlTempDbFileGrowth ] [-SqlTempDbLogFileSize ] [-SqlTempDbLogFileGrowth ] [-SqlUserDbDir ] [-SqlUserDbLogDir ] [-FileStreamLevel ] [-FileStreamShareName ] [-ISSvcAccount ] [-ISSvcPassword ] [-ISSvcStartupType ] [-RsInstallMode ] [-RSSvcAccount ] [-RSSvcPassword ] [-RSSvcStartupType ] [-FailoverClusterGroup ] [-FailoverClusterDisks ] [-SkipRules ] [-Timeout ] [-Force] [-WhatIf] [-Confirm] []' + } + @{ + MockParameterSetName = 'PrepareFailoverCluster' + # cSpell: disable-next + MockExpectedParameters = '-PrepareFailoverCluster -AcceptLicensingTerms -MediaPath -InstanceName -Features [-IAcknowledgeEntCalLimits] [-Enu] [-UpdateEnabled] [-UpdateSource ] [-InstallSharedDir ] [-InstallSharedWowDir ] [-InstanceDir ] [-InstanceId ] [-PBEngSvcAccount ] [-PBEngSvcPassword ] [-PBEngSvcStartupType ] [-PBStartPortRange ] [-PBEndPortRange ] [-PBScaleOut] [-ProductKey ] [-AgtSvcAccount ] [-AgtSvcPassword ] [-ASSvcAccount ] [-ASSvcPassword ] [-SqlSvcAccount ] [-SqlSvcPassword ] [-FileStreamLevel ] [-FileStreamShareName ] [-ISSvcAccount ] [-ISSvcPassword ] [-ISSvcStartupType ] [-RsInstallMode ] [-RSSvcAccount ] [-RSSvcPassword ] [-RSSvcStartupType ] [-Timeout ] [-Force] [-WhatIf] [-Confirm] []' + } + @{ + MockParameterSetName = 'CompleteFailoverCluster' + # cSpell: disable-next + MockExpectedParameters = '-CompleteFailoverCluster -MediaPath -InstanceName -InstallSqlDataDir -SqlSysAdminAccounts -FailoverClusterNetworkName -FailoverClusterIPAddresses [-Enu] [-ProductKey ] [-ASBackupDir ] [-ASCollation ] [-ASConfigDir ] [-ASDataDir ] [-ASLogDir ] [-ASTempDir ] [-ASServerMode ] [-ASSysAdminAccounts ] [-ASProviderMSOLAP] [-SqlBackupDir ] [-SecurityMode ] [-SAPwd ] [-SqlCollation ] [-SqlTempDbDir ] [-SqlTempDbLogDir ] [-SqlTempDbFileCount ] [-SqlTempDbFileSize ] [-SqlTempDbFileGrowth ] [-SqlTempDbLogFileSize ] [-SqlTempDbLogFileGrowth ] [-SqlUserDbDir ] [-SqlUserDbLogDir ] [-RsInstallMode ] [-FailoverClusterGroup ] [-FailoverClusterDisks ] [-ConfirmIPDependencyChange] [-Timeout ] [-Force] [-WhatIf] [-Confirm] []' + } + @{ + MockParameterSetName = 'AddNode' + # cSpell: disable-next + MockExpectedParameters = '-AddNode -AcceptLicensingTerms -MediaPath -InstanceName -FailoverClusterIPAddresses [-IAcknowledgeEntCalLimits] [-Enu] [-UpdateEnabled] [-UpdateSource ] [-PBEngSvcAccount ] [-PBEngSvcPassword ] [-PBEngSvcStartupType ] [-PBStartPortRange ] [-PBEndPortRange ] [-PBScaleOut] [-ProductKey ] [-AgtSvcAccount ] [-AgtSvcPassword ] [-ASSvcAccount ] [-ASSvcPassword ] [-SqlSvcAccount ] [-SqlSvcPassword ] [-ISSvcAccount ] [-ISSvcPassword ] [-RsInstallMode ] [-RSSvcAccount ] [-RSSvcPassword ] [-ConfirmIPDependencyChange] [-Timeout ] [-Force] [-WhatIf] [-Confirm] []' + } + @{ + MockParameterSetName = 'RemoveNode' + # cSpell: disable-next + MockExpectedParameters = '-RemoveNode -MediaPath -InstanceName [-ConfirmIPDependencyChange] [-Timeout ] [-Force] [-WhatIf] [-Confirm] []' + } + ) { + InModuleScope -Parameters $_ -ScriptBlock { + $result = (Get-Command -Name 'Invoke-SetupAction').ParameterSets | + Where-Object -FilterScript { + $_.Name -eq $mockParameterSetName + } | + Select-Object -Property @( + @{ + Name = 'ParameterSetName' + Expression = { $_.Name } + }, + @{ + Name = 'ParameterListAsString' + Expression = { $_.ToString() } + } + ) + + $result.ParameterSetName | Should -Be $MockParameterSetName + $result.ParameterListAsString | Should -Be $MockExpectedParameters + } + } + + Context 'When setup action is ''Install''' { + BeforeAll { + Mock -CommandName Assert-SetupActionProperties + Mock -CommandName Assert-ElevatedUser + Mock -CommandName Test-Path -ParameterFilter { + $Path -match 'setup\.exe' + } -MockWith { + return $true + } + } + + Context 'When specifying only mandatory parameters' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + InModuleScope -ScriptBlock { + $script:mockDefaultParameters = @{ + Install = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + # Intentionally using both upper- and lower-case. + Features = 'SqlEngine', 'Arc' + SqlSysAdminAccounts = 'DOMAIN\User', 'COMPANY\SQL Administrators' + } + } + } + + Context 'When using parameter Confirm with value $false' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -Confirm:$false @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=Install' + $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/SQLSYSADMINACCOUNTS="DOMAIN\\User" "COMPANY\\SQL Administrators"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/FEATURES=SQLENGINE,ARC' + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/ONBOARDSQLTOARC' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When using parameter Force' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -Force @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=Install' + $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/SQLSYSADMINACCOUNTS="DOMAIN\\User" "COMPANY\\SQL Administrators"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/FEATURES=SQLENGINE,ARC' + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/ONBOARDSQLTOARC' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When using parameter WhatIf' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -WhatIf @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -Exactly -Times 0 -Scope It + } + } + } + } + + Context 'When specifying optional parameters PBStartPortRange and PBEndPortRange' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + InModuleScope -ScriptBlock { + $script:installSqlDscServerParameters = @{ + Install = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + # Intentionally using both upper- and lower-case in the value. + Features = 'SqlEngine' + SqlSysAdminAccounts = 'DOMAIN\User' + Force = $true + PBStartPortRange = 16450 + PBEndPortRange = 16460 + } + } + } + + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction @installSqlDscServerParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly 'PBPORTRANGE=16450-16460' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When specifying optional parameter ' -ForEach @( + @{ + MockParameterName = 'SuppressPrivacyStatementNotice' + MockParameterValue = $true + MockExpectedRegEx = '\/SUPPRESSPRIVACYSTATEMENTNOTICE\s*' # cspell: disable-line + } + @{ + MockParameterName = 'Enu' + MockParameterValue = $true + MockExpectedRegEx = '\/ENU\s*' + } + @{ + MockParameterName = 'UpdateEnabled' + MockParameterValue = $true + MockExpectedRegEx = '\/UPDATEENABLED=True' # cspell: disable-line + } + @{ + MockParameterName = 'UpdateSource' + MockParameterValue = '\SqlMedia\Updates' + MockExpectedRegEx = '\/UPDATESOURCE="\\SqlMedia\\Updates"' # cspell: disable-line + } + @{ + MockParameterName = 'InstallSharedDir' + # This value intentionally ends with a backslash to test so that it is removed. + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\' + MockExpectedRegEx = '\/INSTALLSHAREDDIR="C:\\Program Files\\Microsoft SQL Server"' # cspell: disable-line + } + @{ + MockParameterName = 'InstallSharedWOWDir' + MockParameterValue = 'C:\Program Files (x86)\Microsoft SQL Server' + MockExpectedRegEx = '\/INSTALLSHAREDWOWDIR="C:\\Program Files \(x86\)\\Microsoft SQL Server"' # cspell: disable-line + } + @{ + MockParameterName = 'InstanceDir' + <# + This value intentionally has 'D:\' to validate that the backslash + is not removed and that the argument is passed without double-quotes. + #> + MockParameterValue = 'D:\' + MockExpectedRegEx = '\/INSTANCEDIR=D:\\' # cspell: disable-line + } + @{ + MockParameterName = 'InstanceId' + MockParameterValue = 'Instance' + MockExpectedRegEx = '\/INSTANCEID="Instance"' # cspell: disable-line + } + @{ + MockParameterName = 'PBEngSvcAccount' + MockParameterValue = 'NT Authority\NETWORK SERVICE' + MockExpectedRegEx = '\/PBENGSVCACCOUNT="NT Authority\\NETWORK SERVICE"' # cspell: disable-line + } + @{ + MockParameterName = 'PBEngSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/PBENGSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'PBEngSvcStartupType' + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/PBENGSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + @{ + MockParameterName = 'PBDMSSvcAccount' # cspell: disable-line + MockParameterValue = 'NT Authority\NETWORK SERVICE' + MockExpectedRegEx = '\/PBDMSSVCACCOUNT="NT Authority\\NETWORK SERVICE"' # cspell: disable-line + } + @{ + MockParameterName = 'PBDMSSvcPassword' # cspell: disable-line + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/PBDMSSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'PBDMSSvcStartupType' # cspell: disable-line + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/PBDMSSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + @{ + MockParameterName = 'PBScaleOut' + MockParameterValue = $true + MockExpectedRegEx = '\/PBSCALEOUT=True' # cspell: disable-line + } + @{ + MockParameterName = 'ProductKey' + MockParameterValue = '22222-00000-00000-00000-00000' + MockExpectedRegEx = '\/PID="22222-00000-00000-00000-00000"' + } + @{ + MockParameterName = 'AgtSvcAccount' + MockParameterValue = 'NT Authority\NETWORK SERVICE' + MockExpectedRegEx = '\/AGTSVCACCOUNT="NT Authority\\NETWORK SERVICE"' # cspell: disable-line + } + @{ + MockParameterName = 'AgtSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/AGTSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'AgtSvcStartupType' + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/AGTSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + @{ + MockParameterName = 'ASBackupDir' + MockParameterValue = 'C:\MSOLAP13.INST2016\Backup' + MockExpectedRegEx = '\/ASBACKUPDIR="C:\\MSOLAP13\.INST2016\\Backup"' # cspell: disable-line + } + @{ + MockParameterName = 'ASConfigDir' + MockParameterValue = 'C:\MSOLAP13.INST2016\Config' + MockExpectedRegEx = '\/ASCONFIGDIR="C:\\MSOLAP13\.INST2016\\Config"' # cspell: disable-line + } + @{ + MockParameterName = 'ASDataDir' + MockParameterValue = 'C:\MSOLAP13.INST2016\Data' + MockExpectedRegEx = '\/ASDATADIR="C:\\MSOLAP13\.INST2016\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'ASLogDir' + MockParameterValue = 'C:\MSOLAP13.INST2016\Log' + MockExpectedRegEx = '\/ASLOGDIR="C:\\MSOLAP13\.INST2016\\Log"' # cspell: disable-line + } + @{ + MockParameterName = 'ASTempDir' + MockParameterValue = 'C:\MSOLAP13.INST2016\Temp' + MockExpectedRegEx = '\/ASTEMPDIR="C:\\MSOLAP13\.INST2016\\Temp"' # cspell: disable-line + } + @{ + MockParameterName = 'ASCollation' + MockParameterValue = 'latin1_general_100' + MockExpectedRegEx = '\/ASCOLLATION="latin1_general_100"' # cspell: disable-line + } + @{ + MockParameterName = 'ASServerMode' + MockParameterValue = 'Multidimensional' + MockExpectedRegEx = '\/ASSERVERMODE=MULTIDIMENSIONAL' # cspell: disable-line + } + @{ + MockParameterName = 'AsSvcAccount' + MockParameterValue = 'DOMAIN\ServiceAccount$' + MockExpectedRegEx = '\/ASSVCACCOUNT="DOMAIN\\ServiceAccount\$"' # cspell: disable-line + } + @{ + MockParameterName = 'AsSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/ASSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'AsSvcStartupType' + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/ASSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + @{ + MockParameterName = 'ASSysAdminAccounts' + MockParameterValue = 'COMPANY\SQL Administrators', 'LocalUser' + MockExpectedRegEx = '\/ASSYSADMINACCOUNTS="COMPANY\\SQL Administrators" "LocalUser"' # cspell: disable-line + } + @{ + MockParameterName = 'ASProviderMSOLAP' + MockParameterValue = $true + MockExpectedRegEx = '\/ASPROVIDERMSOLAP=1' # cspell: disable-line + } + @{ + MockParameterName = 'BrowserSvcStartupType' + MockParameterValue = 'Manual' + MockExpectedRegEx = '\/BROWSERSVCSTARTUPTYPE="Manual"' # cspell: disable-line + } + @{ + MockParameterName = 'EnableRanU' + MockParameterValue = $true + MockExpectedRegEx = '\/ENABLERANU' # cspell: disable-line + } + @{ + MockParameterName = 'InstallSqlDataDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + MockExpectedRegEx = '\/INSTALLSQLDATADIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlBackupDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Backup' + MockExpectedRegEx = '\/SQLBACKUPDIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Backup"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + MockExpectedRegEx = '\/SQLTEMPDBDIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbLogDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + MockExpectedRegEx = '\/SQLTEMPDBLOGDIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlUserDbDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + MockExpectedRegEx = '\/SQLUSERDBDIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlUserDbLogDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + MockExpectedRegEx = '\/SQLUSERDBLOGDIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'SecurityMode' + MockParameterValue = 'SQL' + MockExpectedRegEx = '\/SECURITYMODE="SQL"' # cspell: disable-line + } + @{ + MockParameterName = 'SAPwd' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/SAPWD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlCollation' + MockParameterValue = 'SQL_Latin1_General_CP1_CI_AS' + MockExpectedRegEx = '\/SQLCOLLATION="SQL_Latin1_General_CP1_CI_AS"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlSvcAccount' + MockParameterValue = 'DOMAIN\ServiceAccount$' + MockExpectedRegEx = '\/SQLSVCACCOUNT="DOMAIN\\ServiceAccount\$"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/SQLSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlSvcStartupType' + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/SQLSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbFileCount' + MockParameterValue = 8 + MockExpectedRegEx = '\/SQLTEMPDBFILECOUNT=8' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbFileSize' + MockParameterValue = 100 + MockExpectedRegEx = '\/SQLTEMPDBFILESIZE=100' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbFileGrowth' + MockParameterValue = 10 + MockExpectedRegEx = '\/SQLTEMPDBFILEGROWTH=10' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbLogFileSize' + MockParameterValue = 100 + MockExpectedRegEx = '\/SQLTEMPDBLOGFILESIZE=100' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbLogFileGrowth' + MockParameterValue = 10 + MockExpectedRegEx = '\/SQLTEMPDBLOGFILEGROWTH=10' # cspell: disable-line + } + @{ + MockParameterName = 'SqlSvcInstantFileInit' + MockParameterValue = $true + MockExpectedRegEx = '\/SQLSVCINSTANTFILEINIT=True' # cspell: disable-line + } + @{ + MockParameterName = 'SqlMaxDop' + MockParameterValue = 8 + MockExpectedRegEx = '\/SQLMAXDOP=8' # cspell: disable-line + } + @{ + MockParameterName = 'UseSqlRecommendedMemoryLimits' + MockParameterValue = $true + MockExpectedRegEx = '\/USESQLRECOMMENDEDMEMORYLIMITS' # cspell: disable-line + } + @{ + MockParameterName = 'SqlMinMemory' + MockParameterValue = 1000 + MockExpectedRegEx = '\/SQLMINMEMORY=1000' # cspell: disable-line + } + @{ + MockParameterName = 'SqlMaxMemory' + MockParameterValue = 2147483647 + MockExpectedRegEx = '\/SQLMAXMEMORY=2147483647' # cspell: disable-line + } + @{ + MockParameterName = 'FileStreamLevel' + MockParameterValue = 2 + MockExpectedRegEx = '\/FILESTREAMLEVEL=2' # cspell: disable-line + } + @{ + MockParameterName = 'FileStreamShareName' + MockParameterValue = 'ShareName' + MockExpectedRegEx = '\/FILESTREAMSHARENAME="ShareName"' # cspell: disable-line + } + @{ + MockParameterName = 'ISSvcAccount' + MockParameterValue = 'DOMAIN\ServiceAccount$' + MockExpectedRegEx = '\/ISSVCACCOUNT="DOMAIN\\ServiceAccount\$"' # cspell: disable-line + } + @{ + MockParameterName = 'ISSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/ISSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'ISSvcStartupType' + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/ISSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + @{ + MockParameterName = 'NpEnabled' + MockParameterValue = $true + MockExpectedRegEx = '\/NPENABLED=1' # cspell: disable-line + } + @{ + MockParameterName = 'TcpEnabled' + MockParameterValue = $true + MockExpectedRegEx = '\/TCPENABLED=1' # cspell: disable-line + } + @{ + MockParameterName = 'RsInstallMode' + MockParameterValue = 'FilesOnlyMode' + MockExpectedRegEx = '\/RSINSTALLMODE="FilesOnlyMode"' # cspell: disable-line + } + @{ + MockParameterName = 'RSSvcAccount' + MockParameterValue = 'DOMAIN\ServiceAccount$' + MockExpectedRegEx = '\/RSSVCACCOUNT="DOMAIN\\ServiceAccount\$"' # cspell: disable-line + } + @{ + MockParameterName = 'RSSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/RSSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'RSSvcStartupType' + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/RSSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + @{ + # Argument is reserved for future use, see SQL Server documentation. + MockParameterName = 'MPYCacheDirectory' + MockParameterValue = 'C:\Temp' + MockExpectedRegEx = '\/MPYCACHEDIRECTORY="C:\\Temp"' # cspell: disable-line + } + @{ + MockParameterName = 'MRCacheDirectory' + MockParameterValue = 'C:\Temp' + MockExpectedRegEx = '\/MRCACHEDIRECTORY="C:\\Temp"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlInstJava' + MockParameterValue = $true + MockExpectedRegEx = '\/SQL_INST_JAVA' # cspell: disable-line + } + @{ + MockParameterName = 'SqlJavaDir' + MockParameterValue = 'C:\Java' + MockExpectedRegEx = '\/SQLJAVADIR="C:\\Java"' # cspell: disable-line + } + @{ + MockParameterName = 'AzureSubscriptionId' + MockParameterValue = '5d19794a-89a4-4f0b-8d4e-58f213ea3546' + MockExpectedRegEx = '\/AZURESUBSCRIPTIONID="5d19794a-89a4-4f0b-8d4e-58f213ea3546"' # cspell: disable-line + } + @{ + MockParameterName = 'AzureResourceGroup' + MockParameterValue = 'MyResourceGroup' + MockExpectedRegEx = '\/AZURERESOURCEGROUP="MyResourceGroup"' # cspell: disable-line + } + @{ + MockParameterName = 'AzureRegion' + MockParameterValue = 'West-US' + MockExpectedRegEx = '\/AZUREREGION="West-US"' # cspell: disable-line + } + @{ + MockParameterName = 'AzureTenantId' + MockParameterValue = '5d19794a-89a4-4f0b-8d4e-58f213ea3546' + MockExpectedRegEx = '\/AZURETENANTID="5d19794a-89a4-4f0b-8d4e-58f213ea3546"' # cspell: disable-line + } + @{ + MockParameterName = 'AzureServicePrincipal' + MockParameterValue = 'MyServicePrincipal' + MockExpectedRegEx = '\/AZURESERVICEPRINCIPAL="MyServicePrincipal"' # cspell: disable-line + } + @{ + MockParameterName = 'AzureServicePrincipalSecret' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/AZURESERVICEPRINCIPALSECRET="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'AzureArcProxy' + MockParameterValue = 'proxy.company.local' + MockExpectedRegEx = '\/AZUREARCPROXY="proxy\.company\.local"' # cspell: disable-line + } + @{ + MockParameterName = 'SkipRules' + MockParameterValue = 'Cluster_VerifyForErrors' + MockExpectedRegEx = '\/SKIPRULES="Cluster_VerifyForErrors"' # cspell: disable-line + } + ) { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + InModuleScope -ScriptBlock { + $script:mockDefaultParameters = @{ + Install = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + # Intentionally using both upper- and lower-case in the value. + Features = 'SqlEngine' + SqlSysAdminAccounts = 'DOMAIN\User' + Force = $true + } + } + } + + BeforeEach { + InModuleScope -ScriptBlock { + $script:installSqlDscServerParameters = $mockDefaultParameters.Clone() + } + } + + It 'Should call the mock with the correct argument string' { + InModuleScope -Parameters $_ -ScriptBlock { + $installSqlDscServerParameters.$MockParameterName = $MockParameterValue + + Invoke-SetupAction @installSqlDscServerParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly $MockExpectedRegEx + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When specifying sensitive parameter ' -ForEach @( + @{ + MockParameterName = 'PBEngSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/PBENGSVCPASSWORD="\*{8}"' # cspell: disable-line + } + @{ + MockParameterName = 'PBDMSSvcPassword' # cspell: disable-line + MockParameterValue = ('jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force) # cspell: disable-line + MockExpectedRegEx = '\/PBDMSSVCPASSWORD="\*{8}"' # cspell: disable-line + } + @{ + MockParameterName = 'ProductKey' # cspell: disable-line + MockParameterValue = '22222-00000-00000-00000-00000' + MockExpectedRegEx = '\/PID="\*{8}"' # cspell: disable-line + } + @{ + MockParameterName = 'AgtSvcPassword' # cspell: disable-line + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/AGTSVCPASSWORD="\*{8}"' # cspell: disable-line + } + @{ + MockParameterName = 'SAPwd' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/SAPWD="\*{8}"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/SQLSVCPASSWORD="\*{8}"' # cspell: disable-line + } + @{ + MockParameterName = 'ISSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/ISSVCPASSWORD="\*{8}"' # cspell: disable-line + } + @{ + MockParameterName = 'RSSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/RSSVCPASSWORD="\*{8}"' # cspell: disable-line + } + @{ + MockParameterName = 'AzureServicePrincipalSecret' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/AZURESERVICEPRINCIPALSECRET="\*{8}"' # cspell: disable-line + } + ) { + BeforeAll { + Mock -CommandName Write-Verbose + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + InModuleScope -ScriptBlock { + $script:mockDefaultParameters = @{ + Install = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + # Intentionally using both upper- and lower-case in the value. + Features = 'SqlEngine' + SqlSysAdminAccounts = 'DOMAIN\User' + Force = $true + } + } + } + + BeforeEach { + InModuleScope -ScriptBlock { + $script:installSqlDscServerParameters = $mockDefaultParameters.Clone() + } + } + + It 'Should obfuscate the value in the verbose string' { + InModuleScope -Parameters $_ -ScriptBlock { + $installSqlDscServerParameters.$MockParameterName = $MockParameterValue + + # Redirect all verbose stream to $null to ge no output from ShouldProcess. + Invoke-SetupAction @installSqlDscServerParameters -Verbose 4> $null + + $mockVerboseMessage = $script:localizedData.Server_SetupArguments + + Should -Invoke -CommandName Write-Verbose -ParameterFilter { + # Only test the command that output the string that should be tested. + $correctMessage = $Message -match $mockVerboseMessage + + # Only test string if it is the correct verbose command + if ($correctMessage) + { + $Message | Should -MatchExactly $MockExpectedRegEx + } + + # Return wether the correct command was called or not. + $correctMessage + } -Exactly -Times 1 -Scope It + } + } + } + } + + Context 'When setup action is ''Upgrade''' { + BeforeAll { + Mock -CommandName Assert-SetupActionProperties + Mock -CommandName Assert-ElevatedUser + Mock -CommandName Test-Path -ParameterFilter { + $Path -match 'setup\.exe' + } -MockWith { + return $true + } + } + + Context 'When specifying only mandatory parameters' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + InModuleScope -ScriptBlock { + $script:mockDefaultParameters = @{ + Upgrade = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + } + } + } + + Context 'When using parameter Confirm with value $false' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -Confirm:$false @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=Upgrade' + $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When using parameter Force' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -Force @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=Upgrade' + $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When using parameter WhatIf' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -WhatIf @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -Exactly -Times 0 -Scope It + } + } + } + } + + Context 'When specifying optional parameter ' -ForEach @( + @{ + MockParameterName = 'Enu' + MockParameterValue = $true + MockExpectedRegEx = '\/ENU\s*' + } + @{ + MockParameterName = 'UpdateEnabled' + MockParameterValue = $true + MockExpectedRegEx = '\/UPDATEENABLED=True' # cspell: disable-line + } + @{ + MockParameterName = 'UpdateSource' + MockParameterValue = '\SqlMedia\Updates' + MockExpectedRegEx = '\/UPDATESOURCE="\\SqlMedia\\Updates"' # cspell: disable-line + } + @{ + MockParameterName = 'InstanceDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server' + MockExpectedRegEx = '\/INSTANCEDIR="C:\\Program Files\\Microsoft SQL Server"' # cspell: disable-line + } + @{ + MockParameterName = 'InstanceId' + MockParameterValue = 'Instance' + MockExpectedRegEx = '\/INSTANCEID="Instance"' # cspell: disable-line + } + @{ + MockParameterName = 'ProductKey' + MockParameterValue = '22222-00000-00000-00000-00000' + MockExpectedRegEx = '\/PID="22222-00000-00000-00000-00000"' + } + @{ + MockParameterName = 'BrowserSvcStartupType' + MockParameterValue = 'Manual' + MockExpectedRegEx = '\/BROWSERSVCSTARTUPTYPE="Manual"' # cspell: disable-line + } + @{ + MockParameterName = 'FTUpgradeOption' + MockParameterValue = 'Reset' + MockExpectedRegEx = '\/FTUPGRADEOPTION="Reset"' # cspell: disable-line + } + @{ + MockParameterName = 'ISSvcAccount' + MockParameterValue = 'DOMAIN\ServiceAccount$' + MockExpectedRegEx = '\/ISSVCACCOUNT="DOMAIN\\ServiceAccount\$"' # cspell: disable-line + } + @{ + MockParameterName = 'ISSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/ISSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'ISSvcStartupType' + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/ISSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + @{ + MockParameterName = 'AllowUpgradeForSSRSSharePointMode' + MockParameterValue = $true + MockExpectedRegEx = '\/ALLOWUPGRADEFORSSRSSHAREPOINTMODE=True' # cspell: disable-line + } + @{ + MockParameterName = 'FailoverClusterRollOwnership' + MockParameterValue = 2 + MockExpectedRegEx = '\/FAILOVERCLUSTERROLLOWNERSHIP=2' # cspell: disable-line + } + ) { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + InModuleScope -ScriptBlock { + $script:mockDefaultParameters = @{ + Upgrade = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + # Intentionally using both upper- and lower-case in the value. + Force = $true + } + } + } + + BeforeEach { + InModuleScope -ScriptBlock { + $script:installSqlDscServerParameters = $mockDefaultParameters.Clone() + } + } + + It 'Should call the mock with the correct argument string' { + InModuleScope -Parameters $_ -ScriptBlock { + $installSqlDscServerParameters.$MockParameterName = $MockParameterValue + + Invoke-SetupAction @installSqlDscServerParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly $MockExpectedRegEx + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + } + + Context 'When setup action is ''InstallFailoverCluster''' { + BeforeAll { + Mock -CommandName Assert-SetupActionProperties + Mock -CommandName Assert-ElevatedUser + Mock -CommandName Test-Path -ParameterFilter { + $Path -match 'setup\.exe' + } -MockWith { + return $true + } + } + + Context 'When specifying only mandatory parameters' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + InModuleScope -ScriptBlock { + $script:mockDefaultParameters = @{ + InstallFailoverCluster = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + # Intentionally using both upper- and lower-case. + Features = 'SqlEngine' + SqlSysAdminAccounts = 'DOMAIN\User', 'COMPANY\SQL Administrators' + InstallSqlDataDir = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + FailoverClusterNetworkName = 'TESTCLU01A' # cspell: disable-line + FailoverClusterIPAddresses = @( + 'IPv4;172.16.0.0;ClusterNetwork1;172.31.255.255', + 'IPv6;2001:db8:23:1002:20f:1fff:feff:b3a3;ClusterNetwork2' # cspell: disable-line + 'IPv6;DHCP;ClusterNetwork3' + 'IPv4;DHCP;ClusterNetwork4' + ) + } + } + } + + Context 'When using parameter Confirm with value $false' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -Confirm:$false @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=InstallFailoverCluster' + $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/SQLSYSADMINACCOUNTS="DOMAIN\\User" "COMPANY\\SQL Administrators"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/INSTALLSQLDATADIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/FEATURES=SQLENGINE' + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/FAILOVERCLUSTERNETWORKNAME="TESTCLU01A"' # cspell: disable-line + $ArgumentList | Should -MatchExactly ([System.Text.RegularExpressions.Regex]::Escape('/FAILOVERCLUSTERIPADDRESSES="IPv4;172.16.0.0;ClusterNetwork1;172.31.255.255" "IPv6;2001:db8:23:1002:20f:1fff:feff:b3a3;ClusterNetwork2" "IPv6;DHCP;ClusterNetwork3" "IPv4;DHCP;ClusterNetwork4"')) # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When using parameter Force' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -Force @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=InstallFailoverCluster' + $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/SQLSYSADMINACCOUNTS="DOMAIN\\User" "COMPANY\\SQL Administrators"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/INSTALLSQLDATADIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/FEATURES=SQLENGINE' + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/FAILOVERCLUSTERNETWORKNAME="TESTCLU01A"' # cspell: disable-line + $ArgumentList | Should -MatchExactly ([System.Text.RegularExpressions.Regex]::Escape('/FAILOVERCLUSTERIPADDRESSES="IPv4;172.16.0.0;ClusterNetwork1;172.31.255.255" "IPv6;2001:db8:23:1002:20f:1fff:feff:b3a3;ClusterNetwork2" "IPv6;DHCP;ClusterNetwork3" "IPv4;DHCP;ClusterNetwork4"')) # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When using parameter WhatIf' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -WhatIf @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -Exactly -Times 0 -Scope It + } + } + } + } + + Context 'When specifying optional parameters PBStartPortRange and PBEndPortRange' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + InModuleScope -ScriptBlock { + $script:installSqlDscServerParameters = @{ + InstallFailoverCluster = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + # Intentionally using both upper- and lower-case in the value. + Features = 'SqlEngine' + SqlSysAdminAccounts = 'DOMAIN\User', 'COMPANY\SQL Administrators' + InstallSqlDataDir = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + FailoverClusterNetworkName = 'TESTCLU01A' # cspell: disable-line + FailoverClusterIPAddresses = '192.168.0.46' + Force = $true + PBStartPortRange = 16450 + PBEndPortRange = 16460 + } + } + } + + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction @installSqlDscServerParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly 'PBPORTRANGE=16450-16460' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When specifying optional parameter ' -ForEach @( + @{ + MockParameterName = 'Enu' + MockParameterValue = $true + MockExpectedRegEx = '\/ENU\s*' + } + @{ + MockParameterName = 'UpdateEnabled' + MockParameterValue = $true + MockExpectedRegEx = '\/UPDATEENABLED=True' # cspell: disable-line + } + @{ + MockParameterName = 'UpdateSource' + MockParameterValue = '\SqlMedia\Updates' + MockExpectedRegEx = '\/UPDATESOURCE="\\SqlMedia\\Updates"' # cspell: disable-line + } + @{ + MockParameterName = 'InstallSharedDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server' + MockExpectedRegEx = '\/INSTALLSHAREDDIR="C:\\Program Files\\Microsoft SQL Server"' # cspell: disable-line + } + @{ + MockParameterName = 'InstallSharedWOWDir' + MockParameterValue = 'C:\Program Files (x86)\Microsoft SQL Server' + MockExpectedRegEx = '\/INSTALLSHAREDWOWDIR="C:\\Program Files \(x86\)\\Microsoft SQL Server"' # cspell: disable-line + } + @{ + MockParameterName = 'InstanceDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server' + MockExpectedRegEx = '\/INSTANCEDIR="C:\\Program Files\\Microsoft SQL Server"' # cspell: disable-line + } + @{ + MockParameterName = 'InstanceId' + MockParameterValue = 'Instance' + MockExpectedRegEx = '\/INSTANCEID="Instance"' # cspell: disable-line + } + @{ + MockParameterName = 'PBEngSvcAccount' + MockParameterValue = 'NT Authority\NETWORK SERVICE' + MockExpectedRegEx = '\/PBENGSVCACCOUNT="NT Authority\\NETWORK SERVICE"' # cspell: disable-line + } + @{ + MockParameterName = 'PBEngSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/PBENGSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'PBEngSvcStartupType' + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/PBENGSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + @{ + MockParameterName = 'PBScaleOut' + MockParameterValue = $true + MockExpectedRegEx = '\/PBSCALEOUT=True' # cspell: disable-line + } + @{ + MockParameterName = 'ProductKey' + MockParameterValue = '22222-00000-00000-00000-00000' + MockExpectedRegEx = '\/PID="22222-00000-00000-00000-00000"' + } + @{ + MockParameterName = 'AgtSvcAccount' + MockParameterValue = 'NT Authority\NETWORK SERVICE' + MockExpectedRegEx = '\/AGTSVCACCOUNT="NT Authority\\NETWORK SERVICE"' # cspell: disable-line + } + @{ + MockParameterName = 'AgtSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/AGTSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'ASBackupDir' + MockParameterValue = 'C:\MSOLAP13.INST2016\Backup' + MockExpectedRegEx = '\/ASBACKUPDIR="C:\\MSOLAP13\.INST2016\\Backup"' # cspell: disable-line + } + @{ + MockParameterName = 'ASConfigDir' + MockParameterValue = 'C:\MSOLAP13.INST2016\Config' + MockExpectedRegEx = '\/ASCONFIGDIR="C:\\MSOLAP13\.INST2016\\Config"' # cspell: disable-line + } + @{ + MockParameterName = 'ASDataDir' + MockParameterValue = 'C:\MSOLAP13.INST2016\Data' + MockExpectedRegEx = '\/ASDATADIR="C:\\MSOLAP13\.INST2016\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'ASLogDir' + MockParameterValue = 'C:\MSOLAP13.INST2016\Log' + MockExpectedRegEx = '\/ASLOGDIR="C:\\MSOLAP13\.INST2016\\Log"' # cspell: disable-line + } + @{ + MockParameterName = 'ASTempDir' + MockParameterValue = 'C:\MSOLAP13.INST2016\Temp' + MockExpectedRegEx = '\/ASTEMPDIR="C:\\MSOLAP13\.INST2016\\Temp"' # cspell: disable-line + } + @{ + MockParameterName = 'ASCollation' + MockParameterValue = 'latin1_general_100' + MockExpectedRegEx = '\/ASCOLLATION="latin1_general_100"' # cspell: disable-line + } + @{ + MockParameterName = 'ASServerMode' + MockParameterValue = 'Multidimensional' + MockExpectedRegEx = '\/ASSERVERMODE=MULTIDIMENSIONAL' # cspell: disable-line + } + @{ + MockParameterName = 'AsSvcAccount' + MockParameterValue = 'DOMAIN\ServiceAccount$' + MockExpectedRegEx = '\/ASSVCACCOUNT="DOMAIN\\ServiceAccount\$"' # cspell: disable-line + } + @{ + MockParameterName = 'AsSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/ASSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'AsSvcStartupType' + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/ASSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + @{ + MockParameterName = 'ASSysAdminAccounts' + MockParameterValue = 'COMPANY\SQL Administrators', 'LocalUser' + MockExpectedRegEx = '\/ASSYSADMINACCOUNTS="COMPANY\\SQL Administrators" "LocalUser"' # cspell: disable-line + } + @{ + MockParameterName = 'ASProviderMSOLAP' + MockParameterValue = $true + MockExpectedRegEx = '\/ASPROVIDERMSOLAP=1' # cspell: disable-line + } + @{ + MockParameterName = 'SqlBackupDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Backup' + MockExpectedRegEx = '\/SQLBACKUPDIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Backup"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + MockExpectedRegEx = '\/SQLTEMPDBDIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbLogDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + MockExpectedRegEx = '\/SQLTEMPDBLOGDIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlUserDbDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + MockExpectedRegEx = '\/SQLUSERDBDIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlUserDbLogDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + MockExpectedRegEx = '\/SQLUSERDBLOGDIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'SecurityMode' + MockParameterValue = 'SQL' + MockExpectedRegEx = '\/SECURITYMODE="SQL"' # cspell: disable-line + } + @{ + MockParameterName = 'SAPwd' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/SAPWD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlCollation' + MockParameterValue = 'SQL_Latin1_General_CP1_CI_AS' + MockExpectedRegEx = '\/SQLCOLLATION="SQL_Latin1_General_CP1_CI_AS"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlSvcAccount' + MockParameterValue = 'DOMAIN\ServiceAccount$' + MockExpectedRegEx = '\/SQLSVCACCOUNT="DOMAIN\\ServiceAccount\$"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/SQLSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlSvcStartupType' + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/SQLSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbFileCount' + MockParameterValue = 8 + MockExpectedRegEx = '\/SQLTEMPDBFILECOUNT=8' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbFileSize' + MockParameterValue = 100 + MockExpectedRegEx = '\/SQLTEMPDBFILESIZE=100' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbFileGrowth' + MockParameterValue = 10 + MockExpectedRegEx = '\/SQLTEMPDBFILEGROWTH=10' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbLogFileSize' + MockParameterValue = 100 + MockExpectedRegEx = '\/SQLTEMPDBLOGFILESIZE=100' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbLogFileGrowth' + MockParameterValue = 10 + MockExpectedRegEx = '\/SQLTEMPDBLOGFILEGROWTH=10' # cspell: disable-line + } + @{ + MockParameterName = 'FileStreamLevel' + MockParameterValue = 2 + MockExpectedRegEx = '\/FILESTREAMLEVEL=2' # cspell: disable-line + } + @{ + MockParameterName = 'FileStreamShareName' + MockParameterValue = 'ShareName' + MockExpectedRegEx = '\/FILESTREAMSHARENAME="ShareName"' # cspell: disable-line + } + @{ + MockParameterName = 'ISSvcAccount' + MockParameterValue = 'DOMAIN\ServiceAccount$' + MockExpectedRegEx = '\/ISSVCACCOUNT="DOMAIN\\ServiceAccount\$"' # cspell: disable-line + } + @{ + MockParameterName = 'ISSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/ISSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'ISSvcStartupType' + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/ISSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + @{ + MockParameterName = 'RsInstallMode' + MockParameterValue = 'FilesOnlyMode' + MockExpectedRegEx = '\/RSINSTALLMODE="FilesOnlyMode"' # cspell: disable-line + } + @{ + MockParameterName = 'RSSvcAccount' + MockParameterValue = 'DOMAIN\ServiceAccount$' + MockExpectedRegEx = '\/RSSVCACCOUNT="DOMAIN\\ServiceAccount\$"' # cspell: disable-line + } + @{ + MockParameterName = 'RSSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/RSSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'RSSvcStartupType' + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/RSSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + @{ + MockParameterName = 'SkipRules' + MockParameterValue = 'Cluster_VerifyForErrors' + MockExpectedRegEx = '\/SKIPRULES="Cluster_VerifyForErrors"' # cspell: disable-line + } + @{ + MockParameterName = 'FailoverClusterGroup' + MockParameterValue = 'TESTCLU01A' # cspell: disable-line + MockExpectedRegEx = '\/FAILOVERCLUSTERGROUP="TESTCLU01A"' # cspell: disable-line + } + @{ + MockParameterName = 'FailoverClusterGroup' + MockParameterValue = 'TESTCLU01A' # cspell: disable-line + MockExpectedRegEx = '\/FAILOVERCLUSTERGROUP="TESTCLU01A"' # cspell: disable-line + } + @{ + MockParameterName = 'FailoverClusterDisks' + # This is the failover cluster resource name. + MockParameterValue = @( + 'SysData' + ) + MockExpectedRegEx = '\/FAILOVERCLUSTERDISKS="SysData"' # cspell: disable-line + } + @{ + MockParameterName = 'FailoverClusterDisks' + # This is the failover cluster resource names. + MockParameterValue = @( + 'Backup' + 'SysData' + 'TempDbData' + 'TempDbLogs' + 'UserData' + 'UserLogs' + ) + MockExpectedRegEx = '\/FAILOVERCLUSTERDISKS="Backup;SysData;TempDbData;TempDbLogs;UserData;UserLogs"' # cspell: disable-line + } + ) { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + InModuleScope -ScriptBlock { + $script:mockDefaultParameters = @{ + InstallFailoverCluster = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + # Intentionally using both upper- and lower-case in the value. + Features = 'SqlEngine' + SqlSysAdminAccounts = 'DOMAIN\User', 'COMPANY\SQL Administrators' + InstallSqlDataDir = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + FailoverClusterNetworkName = 'TESTCLU01A' # cspell: disable-line + FailoverClusterIPAddresses = '192.168.0.46' + Force = $true + } + } + } + + BeforeEach { + InModuleScope -ScriptBlock { + $script:installSqlDscServerParameters = $mockDefaultParameters.Clone() + } + } + + It 'Should call the mock with the correct argument string' { + InModuleScope -Parameters $_ -ScriptBlock { + $installSqlDscServerParameters.$MockParameterName = $MockParameterValue + + Invoke-SetupAction @installSqlDscServerParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly $MockExpectedRegEx + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + } + + Context 'When setup action is ''PrepareFailoverCluster''' { + BeforeAll { + Mock -CommandName Assert-SetupActionProperties + Mock -CommandName Assert-ElevatedUser + Mock -CommandName Test-Path -ParameterFilter { + $Path -match 'setup\.exe' + } -MockWith { + return $true + } + } + + Context 'When specifying only mandatory parameters' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + InModuleScope -ScriptBlock { + $script:mockDefaultParameters = @{ + PrepareFailoverCluster = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + # Intentionally using both upper- and lower-case. + Features = 'SqlEngine' + } + } + } + + Context 'When using parameter Confirm with value $false' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -Confirm:$false @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=PrepareFailoverCluster' + $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When using parameter Force' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -Force @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=PrepareFailoverCluster' + $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When using parameter WhatIf' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -WhatIf @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -Exactly -Times 0 -Scope It + } + } + } + } + + Context 'When specifying optional parameters PBStartPortRange and PBEndPortRange' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + InModuleScope -ScriptBlock { + $script:installSqlDscServerParameters = @{ + PrepareFailoverCluster = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + # Intentionally using both upper- and lower-case in the value. + Features = 'SqlEngine' + Force = $true + PBStartPortRange = 16450 + PBEndPortRange = 16460 + } + } + } + + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction @installSqlDscServerParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly 'PBPORTRANGE=16450-16460' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When specifying optional parameter ' -ForEach @( + @{ + MockParameterName = 'Enu' + MockParameterValue = $true + MockExpectedRegEx = '\/ENU\s*' + } + @{ + MockParameterName = 'UpdateEnabled' + MockParameterValue = $true + MockExpectedRegEx = '\/UPDATEENABLED=True' # cspell: disable-line + } + @{ + MockParameterName = 'UpdateSource' + MockParameterValue = '\SqlMedia\Updates' + MockExpectedRegEx = '\/UPDATESOURCE="\\SqlMedia\\Updates"' # cspell: disable-line + } + @{ + MockParameterName = 'InstallSharedDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server' + MockExpectedRegEx = '\/INSTALLSHAREDDIR="C:\\Program Files\\Microsoft SQL Server"' # cspell: disable-line + } + @{ + MockParameterName = 'InstallSharedWOWDir' + MockParameterValue = 'C:\Program Files (x86)\Microsoft SQL Server' + MockExpectedRegEx = '\/INSTALLSHAREDWOWDIR="C:\\Program Files \(x86\)\\Microsoft SQL Server"' # cspell: disable-line + } + @{ + MockParameterName = 'InstanceDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server' + MockExpectedRegEx = '\/INSTANCEDIR="C:\\Program Files\\Microsoft SQL Server"' # cspell: disable-line + } + @{ + MockParameterName = 'InstanceId' + MockParameterValue = 'Instance' + MockExpectedRegEx = '\/INSTANCEID="Instance"' # cspell: disable-line + } + @{ + MockParameterName = 'PBEngSvcAccount' + MockParameterValue = 'NT Authority\NETWORK SERVICE' + MockExpectedRegEx = '\/PBENGSVCACCOUNT="NT Authority\\NETWORK SERVICE"' # cspell: disable-line + } + @{ + MockParameterName = 'PBEngSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/PBENGSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'PBEngSvcStartupType' + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/PBENGSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + @{ + MockParameterName = 'PBScaleOut' + MockParameterValue = $true + MockExpectedRegEx = '\/PBSCALEOUT=True' # cspell: disable-line + } + @{ + MockParameterName = 'ProductKey' + MockParameterValue = '22222-00000-00000-00000-00000' + MockExpectedRegEx = '\/PID="22222-00000-00000-00000-00000"' + } + @{ + MockParameterName = 'AgtSvcAccount' + MockParameterValue = 'NT Authority\NETWORK SERVICE' + MockExpectedRegEx = '\/AGTSVCACCOUNT="NT Authority\\NETWORK SERVICE"' # cspell: disable-line + } + @{ + MockParameterName = 'AgtSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/AGTSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'AsSvcAccount' + MockParameterValue = 'DOMAIN\ServiceAccount$' + MockExpectedRegEx = '\/ASSVCACCOUNT="DOMAIN\\ServiceAccount\$"' # cspell: disable-line + } + @{ + MockParameterName = 'AsSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/ASSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlSvcAccount' + MockParameterValue = 'DOMAIN\ServiceAccount$' + MockExpectedRegEx = '\/SQLSVCACCOUNT="DOMAIN\\ServiceAccount\$"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/SQLSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'FileStreamLevel' + MockParameterValue = 2 + MockExpectedRegEx = '\/FILESTREAMLEVEL=2' # cspell: disable-line + } + @{ + MockParameterName = 'FileStreamShareName' + MockParameterValue = 'ShareName' + MockExpectedRegEx = '\/FILESTREAMSHARENAME="ShareName"' # cspell: disable-line + } + @{ + MockParameterName = 'ISSvcAccount' + MockParameterValue = 'DOMAIN\ServiceAccount$' + MockExpectedRegEx = '\/ISSVCACCOUNT="DOMAIN\\ServiceAccount\$"' # cspell: disable-line + } + @{ + MockParameterName = 'ISSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/ISSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'ISSvcStartupType' + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/ISSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + @{ + MockParameterName = 'RsInstallMode' + MockParameterValue = 'FilesOnlyMode' + MockExpectedRegEx = '\/RSINSTALLMODE="FilesOnlyMode"' # cspell: disable-line + } + @{ + MockParameterName = 'RSSvcAccount' + MockParameterValue = 'DOMAIN\ServiceAccount$' + MockExpectedRegEx = '\/RSSVCACCOUNT="DOMAIN\\ServiceAccount\$"' # cspell: disable-line + } + @{ + MockParameterName = 'RSSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/RSSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'RSSvcStartupType' + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/RSSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + ) { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + InModuleScope -ScriptBlock { + $script:mockDefaultParameters = @{ + PrepareFailoverCluster = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + # Intentionally using both upper- and lower-case in the value. + Features = 'SqlEngine' + Force = $true + } + } + } + + BeforeEach { + InModuleScope -ScriptBlock { + $script:installSqlDscServerParameters = $mockDefaultParameters.Clone() + } + } + + It 'Should call the mock with the correct argument string' { + InModuleScope -Parameters $_ -ScriptBlock { + $installSqlDscServerParameters.$MockParameterName = $MockParameterValue + + Invoke-SetupAction @installSqlDscServerParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly $MockExpectedRegEx + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + } + + Context 'When setup action is ''CompleteFailoverCluster''' { + BeforeAll { + Mock -CommandName Assert-SetupActionProperties + Mock -CommandName Assert-ElevatedUser + Mock -CommandName Test-Path -ParameterFilter { + $Path -match 'setup\.exe' + } -MockWith { + return $true + } + } + + Context 'When specifying only mandatory parameters' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + InModuleScope -ScriptBlock { + $script:mockDefaultParameters = @{ + CompleteFailoverCluster = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + SqlSysAdminAccounts = 'DOMAIN\User', 'COMPANY\SQL Administrators' + InstallSqlDataDir = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + FailoverClusterNetworkName = 'TESTCLU01A' # cspell: disable-line + FailoverClusterIPAddresses = @( + 'IPv4;172.16.0.0;ClusterNetwork1;172.31.255.255', + 'IPv6;2001:db8:23:1002:20f:1fff:feff:b3a3;ClusterNetwork2' # cspell: disable-line + 'IPv6;DHCP;ClusterNetwork3' + 'IPv4;DHCP;ClusterNetwork4' + ) + } + } + } + + Context 'When using parameter Confirm with value $false' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -Confirm:$false @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=CompleteFailoverCluster' + $ArgumentList | Should -MatchExactly '\/SQLSYSADMINACCOUNTS="DOMAIN\\User" "COMPANY\\SQL Administrators"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/INSTALLSQLDATADIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/FAILOVERCLUSTERNETWORKNAME="TESTCLU01A"' # cspell: disable-line + $ArgumentList | Should -MatchExactly ([System.Text.RegularExpressions.Regex]::Escape('/FAILOVERCLUSTERIPADDRESSES="IPv4;172.16.0.0;ClusterNetwork1;172.31.255.255" "IPv6;2001:db8:23:1002:20f:1fff:feff:b3a3;ClusterNetwork2" "IPv6;DHCP;ClusterNetwork3" "IPv4;DHCP;ClusterNetwork4"')) # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When using parameter Force' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -Force @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=CompleteFailoverCluster' + $ArgumentList | Should -MatchExactly '\/SQLSYSADMINACCOUNTS="DOMAIN\\User" "COMPANY\\SQL Administrators"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/INSTALLSQLDATADIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/FAILOVERCLUSTERNETWORKNAME="TESTCLU01A"' # cspell: disable-line + $ArgumentList | Should -MatchExactly ([System.Text.RegularExpressions.Regex]::Escape('/FAILOVERCLUSTERIPADDRESSES="IPv4;172.16.0.0;ClusterNetwork1;172.31.255.255" "IPv6;2001:db8:23:1002:20f:1fff:feff:b3a3;ClusterNetwork2" "IPv6;DHCP;ClusterNetwork3" "IPv4;DHCP;ClusterNetwork4"')) # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When using parameter WhatIf' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -WhatIf @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -Exactly -Times 0 -Scope It + } + } + } + } + + Context 'When specifying optional parameter ' -ForEach @( + @{ + MockParameterName = 'Enu' + MockParameterValue = $true + MockExpectedRegEx = '\/ENU\s*' + } + @{ + MockParameterName = 'ProductKey' + MockParameterValue = '22222-00000-00000-00000-00000' + MockExpectedRegEx = '\/PID="22222-00000-00000-00000-00000"' + } + @{ + MockParameterName = 'ASBackupDir' + MockParameterValue = 'C:\MSOLAP13.INST2016\Backup' + MockExpectedRegEx = '\/ASBACKUPDIR="C:\\MSOLAP13\.INST2016\\Backup"' # cspell: disable-line + } + @{ + MockParameterName = 'ASConfigDir' + MockParameterValue = 'C:\MSOLAP13.INST2016\Config' + MockExpectedRegEx = '\/ASCONFIGDIR="C:\\MSOLAP13\.INST2016\\Config"' # cspell: disable-line + } + @{ + MockParameterName = 'ASDataDir' + MockParameterValue = 'C:\MSOLAP13.INST2016\Data' + MockExpectedRegEx = '\/ASDATADIR="C:\\MSOLAP13\.INST2016\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'ASLogDir' + MockParameterValue = 'C:\MSOLAP13.INST2016\Log' + MockExpectedRegEx = '\/ASLOGDIR="C:\\MSOLAP13\.INST2016\\Log"' # cspell: disable-line + } + @{ + MockParameterName = 'ASTempDir' + MockParameterValue = 'C:\MSOLAP13.INST2016\Temp' + MockExpectedRegEx = '\/ASTEMPDIR="C:\\MSOLAP13\.INST2016\\Temp"' # cspell: disable-line + } + @{ + MockParameterName = 'ASCollation' + MockParameterValue = 'latin1_general_100' + MockExpectedRegEx = '\/ASCOLLATION="latin1_general_100"' # cspell: disable-line + } + @{ + MockParameterName = 'ASServerMode' + MockParameterValue = 'Multidimensional' + MockExpectedRegEx = '\/ASSERVERMODE=MULTIDIMENSIONAL' # cspell: disable-line + } + @{ + MockParameterName = 'ASSysAdminAccounts' + MockParameterValue = 'COMPANY\SQL Administrators', 'LocalUser' + MockExpectedRegEx = '\/ASSYSADMINACCOUNTS="COMPANY\\SQL Administrators" "LocalUser"' # cspell: disable-line + } + @{ + MockParameterName = 'ASProviderMSOLAP' + MockParameterValue = $true + MockExpectedRegEx = '\/ASPROVIDERMSOLAP=1' # cspell: disable-line + } + @{ + MockParameterName = 'InstallSqlDataDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + MockExpectedRegEx = '\/INSTALLSQLDATADIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlBackupDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Backup' + MockExpectedRegEx = '\/SQLBACKUPDIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Backup"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + MockExpectedRegEx = '\/SQLTEMPDBDIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbLogDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + MockExpectedRegEx = '\/SQLTEMPDBLOGDIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlUserDbDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + MockExpectedRegEx = '\/SQLUSERDBDIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlUserDbLogDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + MockExpectedRegEx = '\/SQLUSERDBLOGDIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'SecurityMode' + MockParameterValue = 'SQL' + MockExpectedRegEx = '\/SECURITYMODE="SQL"' # cspell: disable-line + } + @{ + MockParameterName = 'SAPwd' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/SAPWD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlCollation' + MockParameterValue = 'SQL_Latin1_General_CP1_CI_AS' + MockExpectedRegEx = '\/SQLCOLLATION="SQL_Latin1_General_CP1_CI_AS"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbFileCount' + MockParameterValue = 8 + MockExpectedRegEx = '\/SQLTEMPDBFILECOUNT=8' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbFileSize' + MockParameterValue = 100 + MockExpectedRegEx = '\/SQLTEMPDBFILESIZE=100' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbFileGrowth' + MockParameterValue = 10 + MockExpectedRegEx = '\/SQLTEMPDBFILEGROWTH=10' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbLogFileSize' + MockParameterValue = 100 + MockExpectedRegEx = '\/SQLTEMPDBLOGFILESIZE=100' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbLogFileGrowth' + MockParameterValue = 10 + MockExpectedRegEx = '\/SQLTEMPDBLOGFILEGROWTH=10' # cspell: disable-line + } + @{ + MockParameterName = 'RsInstallMode' + MockParameterValue = 'FilesOnlyMode' + MockExpectedRegEx = '\/RSINSTALLMODE="FilesOnlyMode"' # cspell: disable-line + } + @{ + MockParameterName = 'FailoverClusterGroup' + MockParameterValue = 'TESTCLU01A' # cspell: disable-line + MockExpectedRegEx = '\/FAILOVERCLUSTERGROUP="TESTCLU01A"' # cspell: disable-line + } + @{ + MockParameterName = 'FailoverClusterGroup' + MockParameterValue = 'TESTCLU01A' # cspell: disable-line + MockExpectedRegEx = '\/FAILOVERCLUSTERGROUP="TESTCLU01A"' # cspell: disable-line + } + @{ + MockParameterName = 'FailoverClusterDisks' + # This is the failover cluster resource name. + MockParameterValue = @( + 'SysData' + ) + MockExpectedRegEx = '\/FAILOVERCLUSTERDISKS="SysData"' # cspell: disable-line + } + @{ + MockParameterName = 'FailoverClusterDisks' + # This is the failover cluster resource names. + MockParameterValue = @( + 'Backup' + 'SysData' + 'TempDbData' + 'TempDbLogs' + 'UserData' + 'UserLogs' + ) + MockExpectedRegEx = '\/FAILOVERCLUSTERDISKS="Backup;SysData;TempDbData;TempDbLogs;UserData;UserLogs"' # cspell: disable-line + } + @{ + MockParameterName = 'ConfirmIPDependencyChange' + MockParameterValue = $true + MockExpectedRegEx = '\/CONFIRMIPDEPENDENCYCHANGE=1' # cspell: disable-line + } + ) { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + InModuleScope -ScriptBlock { + $script:mockDefaultParameters = @{ + CompleteFailoverCluster = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + SqlSysAdminAccounts = 'DOMAIN\User', 'COMPANY\SQL Administrators' + InstallSqlDataDir = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + FailoverClusterNetworkName = 'TESTCLU01A' # cspell: disable-line + FailoverClusterIPAddresses = @( + 'IPv4;172.16.0.0;ClusterNetwork1;172.31.255.255', + 'IPv6;2001:db8:23:1002:20f:1fff:feff:b3a3;ClusterNetwork2' # cspell: disable-line + 'IPv6;DHCP;ClusterNetwork3' + 'IPv4;DHCP;ClusterNetwork4' + ) + Force = $true + } + } + } + + BeforeEach { + InModuleScope -ScriptBlock { + $script:installSqlDscServerParameters = $mockDefaultParameters.Clone() + } + } + + It 'Should call the mock with the correct argument string' { + InModuleScope -Parameters $_ -ScriptBlock { + $installSqlDscServerParameters.$MockParameterName = $MockParameterValue + + Invoke-SetupAction @installSqlDscServerParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly $MockExpectedRegEx + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + } + + Context 'When setup action is ''AddNode''' { + BeforeAll { + Mock -CommandName Assert-SetupActionProperties + Mock -CommandName Assert-ElevatedUser + Mock -CommandName Test-Path -ParameterFilter { + $Path -match 'setup\.exe' + } -MockWith { + return $true + } + } + + Context 'When specifying only mandatory parameters' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + InModuleScope -ScriptBlock { + $script:mockDefaultParameters = @{ + AddNode = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + FailoverClusterIPAddresses = @( + 'IPv4;172.16.0.0;ClusterNetwork1;172.31.255.255', + 'IPv6;2001:db8:23:1002:20f:1fff:feff:b3a3;ClusterNetwork2' # cspell: disable-line + 'IPv6;DHCP;ClusterNetwork3' + 'IPv4;DHCP;ClusterNetwork4' + ) + } + } + } + + Context 'When using parameter Confirm with value $false' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -Confirm:$false @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=AddNode' + $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When using parameter Force' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -Force @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=AddNode' + $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When using parameter WhatIf' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -WhatIf @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -Exactly -Times 0 -Scope It + } + } + } + } + + Context 'When specifying optional parameters PBStartPortRange and PBEndPortRange' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + InModuleScope -ScriptBlock { + $script:installSqlDscServerParameters = @{ + AddNode = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + FailoverClusterIPAddresses = @( + 'IPv4;172.16.0.0;ClusterNetwork1;172.31.255.255', + 'IPv6;2001:db8:23:1002:20f:1fff:feff:b3a3;ClusterNetwork2' # cspell: disable-line + 'IPv6;DHCP;ClusterNetwork3' + 'IPv4;DHCP;ClusterNetwork4' + ) + Force = $true + PBStartPortRange = 16450 + PBEndPortRange = 16460 + } + } + } + + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction @installSqlDscServerParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly 'PBPORTRANGE=16450-16460' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When specifying optional parameter ' -ForEach @( + @{ + MockParameterName = 'Enu' + MockParameterValue = $true + MockExpectedRegEx = '\/ENU\s*' + } + @{ + MockParameterName = 'UpdateEnabled' + MockParameterValue = $true + MockExpectedRegEx = '\/UPDATEENABLED=True' # cspell: disable-line + } + @{ + MockParameterName = 'UpdateSource' + MockParameterValue = '\SqlMedia\Updates' + MockExpectedRegEx = '\/UPDATESOURCE="\\SqlMedia\\Updates"' # cspell: disable-line + } + @{ + MockParameterName = 'PBEngSvcAccount' + MockParameterValue = 'NT Authority\NETWORK SERVICE' + MockExpectedRegEx = '\/PBENGSVCACCOUNT="NT Authority\\NETWORK SERVICE"' # cspell: disable-line + } + @{ + MockParameterName = 'PBEngSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/PBENGSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'PBEngSvcStartupType' + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/PBENGSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + @{ + MockParameterName = 'PBScaleOut' + MockParameterValue = $true + MockExpectedRegEx = '\/PBSCALEOUT=True' # cspell: disable-line + } + @{ + MockParameterName = 'ProductKey' + MockParameterValue = '22222-00000-00000-00000-00000' + MockExpectedRegEx = '\/PID="22222-00000-00000-00000-00000"' + } + @{ + MockParameterName = 'AgtSvcAccount' + MockParameterValue = 'NT Authority\NETWORK SERVICE' + MockExpectedRegEx = '\/AGTSVCACCOUNT="NT Authority\\NETWORK SERVICE"' # cspell: disable-line + } + @{ + MockParameterName = 'AgtSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/AGTSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'AsSvcAccount' + MockParameterValue = 'DOMAIN\ServiceAccount$' + MockExpectedRegEx = '\/ASSVCACCOUNT="DOMAIN\\ServiceAccount\$"' # cspell: disable-line + } + @{ + MockParameterName = 'AsSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/ASSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlSvcAccount' + MockParameterValue = 'DOMAIN\ServiceAccount$' + MockExpectedRegEx = '\/SQLSVCACCOUNT="DOMAIN\\ServiceAccount\$"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/SQLSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'ISSvcAccount' + MockParameterValue = 'DOMAIN\ServiceAccount$' + MockExpectedRegEx = '\/ISSVCACCOUNT="DOMAIN\\ServiceAccount\$"' # cspell: disable-line + } + @{ + MockParameterName = 'ISSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/ISSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'RsInstallMode' + MockParameterValue = 'FilesOnlyMode' + MockExpectedRegEx = '\/RSINSTALLMODE="FilesOnlyMode"' # cspell: disable-line + } + @{ + MockParameterName = 'ConfirmIPDependencyChange' + MockParameterValue = $true + MockExpectedRegEx = '\/CONFIRMIPDEPENDENCYCHANGE=1' # cspell: disable-line + } + ) { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + InModuleScope -ScriptBlock { + $script:mockDefaultParameters = @{ + AddNode = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + FailoverClusterIPAddresses = @( + 'IPv4;172.16.0.0;ClusterNetwork1;172.31.255.255', + 'IPv6;2001:db8:23:1002:20f:1fff:feff:b3a3;ClusterNetwork2' # cspell: disable-line + 'IPv6;DHCP;ClusterNetwork3' + 'IPv4;DHCP;ClusterNetwork4' + ) + Force = $true + } + } + } + + BeforeEach { + InModuleScope -ScriptBlock { + $script:installSqlDscServerParameters = $mockDefaultParameters.Clone() + } + } + + It 'Should call the mock with the correct argument string' { + InModuleScope -Parameters $_ -ScriptBlock { + $installSqlDscServerParameters.$MockParameterName = $MockParameterValue + + Invoke-SetupAction @installSqlDscServerParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly $MockExpectedRegEx + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + } + + Context 'When setup action is ''RemoveNode''' { + BeforeAll { + Mock -CommandName Assert-SetupActionProperties + Mock -CommandName Assert-ElevatedUser + Mock -CommandName Test-Path -ParameterFilter { + $Path -match 'setup\.exe' + } -MockWith { + return $true + } + } + + Context 'When specifying only mandatory parameters' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + InModuleScope -ScriptBlock { + $script:mockDefaultParameters = @{ + RemoveNode = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + } + } + } + + Context 'When using parameter Confirm with value $false' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -Confirm:$false @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=RemoveNode' + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When using parameter Force' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -Force @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=RemoveNode' + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When using parameter WhatIf' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -WhatIf @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -Exactly -Times 0 -Scope It + } + } + } + } + + Context 'When specifying optional parameter ' -ForEach @( + @{ + MockParameterName = 'ConfirmIPDependencyChange' + MockParameterValue = $true + MockExpectedRegEx = '\/CONFIRMIPDEPENDENCYCHANGE=1' # cspell: disable-line + } + ) { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + InModuleScope -ScriptBlock { + $script:mockDefaultParameters = @{ + RemoveNode = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + Force = $true + } + } + } + + BeforeEach { + InModuleScope -ScriptBlock { + $script:installSqlDscServerParameters = $mockDefaultParameters.Clone() + } + } + + It 'Should call the mock with the correct argument string' { + InModuleScope -Parameters $_ -ScriptBlock { + $installSqlDscServerParameters.$MockParameterName = $MockParameterValue + + Invoke-SetupAction @installSqlDscServerParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly $MockExpectedRegEx + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + } + + Context 'When using a ''ConfigurationFile''' { + BeforeAll { + Mock -CommandName Assert-SetupActionProperties + Mock -CommandName Assert-ElevatedUser + + Mock -CommandName Test-Path -ParameterFilter { + $Path -match 'MyConfig\.ini' + } -MockWith { + return $true + } + + Mock -CommandName Test-Path -ParameterFilter { + $Path -match 'setup\.exe' + } -MockWith { + return $true + } + } + + Context 'When specifying only mandatory parameters' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + InModuleScope -ScriptBlock { + $script:mockDefaultParameters = @{ + ConfigurationFile = 'C:\MyConfig.ini' + MediaPath = '\SqlMedia' + } + } + } + + Context 'When using parameter Confirm with value $false' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -Confirm:$false @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + # cspell: disable-next + $ArgumentList | Should -MatchExactly '\/CONFIGURATIONFILE="C:\\MyConfig\.ini"' + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When using parameter Force' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -Force @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + # cspell: disable-next + $ArgumentList | Should -MatchExactly '\/CONFIGURATIONFILE="C:\\MyConfig\.ini"' + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When using parameter WhatIf' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -WhatIf @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -Exactly -Times 0 -Scope It + } + } + } + } + + Context 'When specifying optional parameter ' -ForEach @( + @{ + MockParameterName = 'AgtSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/AGTSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'AsSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/ASSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/SQLSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'ISSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/ISSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'RSSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/RSSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + ) { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + InModuleScope -ScriptBlock { + $script:mockDefaultParameters = @{ + ConfigurationFile = 'C:\MyConfig.ini' + MediaPath = '\SqlMedia' + Force = $true + } + } + } + + BeforeEach { + InModuleScope -ScriptBlock { + $script:installSqlDscServerParameters = $mockDefaultParameters.Clone() + } + } + + It 'Should call the mock with the correct argument string' { + InModuleScope -Parameters $_ -ScriptBlock { + $installSqlDscServerParameters.$MockParameterName = $MockParameterValue + + Invoke-SetupAction @installSqlDscServerParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly $MockExpectedRegEx + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + } + + Context 'When setup action is ''RebuildDatabase''' { + BeforeAll { + Mock -CommandName Assert-SetupActionProperties + Mock -CommandName Assert-ElevatedUser + Mock -CommandName Test-Path -ParameterFilter { + $Path -match 'setup\.exe' + } -MockWith { + return $true + } + } + + Context 'When specifying only mandatory parameters' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + InModuleScope -ScriptBlock { + $script:mockDefaultParameters = @{ + RebuildDatabase = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + SqlSysAdminAccounts = 'DOMAIN\User', 'COMPANY\SQL Administrators' + } + } + } + + Context 'When using parameter Confirm with value $false' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -Confirm:$false @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=RebuildDatabase' + $ArgumentList | Should -MatchExactly '\/SQLSYSADMINACCOUNTS="DOMAIN\\User" "COMPANY\\SQL Administrators"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When using parameter Force' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -Force @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=RebuildDatabase' + $ArgumentList | Should -MatchExactly '\/SQLSYSADMINACCOUNTS="DOMAIN\\User" "COMPANY\\SQL Administrators"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When using parameter WhatIf' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -WhatIf @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -Exactly -Times 0 -Scope It + } + } + } + } + + Context 'When specifying optional parameter ' -ForEach @( + @{ + MockParameterName = 'SqlTempDbDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + MockExpectedRegEx = '\/SQLTEMPDBDIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbLogDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + MockExpectedRegEx = '\/SQLTEMPDBLOGDIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'SAPwd' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/SAPWD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlCollation' + MockParameterValue = 'SQL_Latin1_General_CP1_CI_AS' + MockExpectedRegEx = '\/SQLCOLLATION="SQL_Latin1_General_CP1_CI_AS"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbFileCount' + MockParameterValue = 8 + MockExpectedRegEx = '\/SQLTEMPDBFILECOUNT=8' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbFileSize' + MockParameterValue = 100 + MockExpectedRegEx = '\/SQLTEMPDBFILESIZE=100' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbFileGrowth' + MockParameterValue = 10 + MockExpectedRegEx = '\/SQLTEMPDBFILEGROWTH=10' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbLogFileSize' + MockParameterValue = 100 + MockExpectedRegEx = '\/SQLTEMPDBLOGFILESIZE=100' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbLogFileGrowth' + MockParameterValue = 10 + MockExpectedRegEx = '\/SQLTEMPDBLOGFILEGROWTH=10' # cspell: disable-line + } + ) { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + InModuleScope -ScriptBlock { + $script:mockDefaultParameters = @{ + RebuildDatabase = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + SqlSysAdminAccounts = 'DOMAIN\User' + Force = $true + } + } + } + + BeforeEach { + InModuleScope -ScriptBlock { + $script:installSqlDscServerParameters = $mockDefaultParameters.Clone() + } + } + + It 'Should call the mock with the correct argument string' { + InModuleScope -Parameters $_ -ScriptBlock { + $installSqlDscServerParameters.$MockParameterName = $MockParameterValue + + Invoke-SetupAction @installSqlDscServerParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly $MockExpectedRegEx + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + } + + Context 'When setup action is ''EditionUpgrade''' { + BeforeAll { + Mock -CommandName Assert-SetupActionProperties + Mock -CommandName Assert-ElevatedUser + Mock -CommandName Test-Path -ParameterFilter { + $Path -match 'setup\.exe' + } -MockWith { + return $true + } + } + + Context 'When specifying only mandatory parameters' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + InModuleScope -ScriptBlock { + $script:mockDefaultParameters = @{ + EditionUpgrade = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + ProductKey = '22222-00000-00000-00000-00000' + } + } + } + + Context 'When using parameter Confirm with value $false' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -Confirm:$false @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=EditionUpgrade' + $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/PID="22222-00000-00000-00000-00000"' + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When using parameter Force' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -Force @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=EditionUpgrade' + $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/PID="22222-00000-00000-00000-00000"' + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When using parameter WhatIf' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -WhatIf @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -Exactly -Times 0 -Scope It + } + } + } + } + + Context 'When specifying optional parameter ' -ForEach @( + @{ + MockParameterName = 'SkipRules' + MockParameterValue = 'Cluster_VerifyForErrors' + MockExpectedRegEx = '\/SKIPRULES="Cluster_VerifyForErrors"' # cspell: disable-line + } + ) { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + InModuleScope -ScriptBlock { + $script:mockDefaultParameters = @{ + EditionUpgrade = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + ProductKey = 22222-00000-00000-00000-00000 + Force = $true + } + } + } + + BeforeEach { + InModuleScope -ScriptBlock { + $script:installSqlDscServerParameters = $mockDefaultParameters.Clone() + } + } + + It 'Should call the mock with the correct argument string' { + InModuleScope -Parameters $_ -ScriptBlock { + $installSqlDscServerParameters.$MockParameterName = $MockParameterValue + + Invoke-SetupAction @installSqlDscServerParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly $MockExpectedRegEx + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + } + + Context 'When setup action is ''Repair''' { + BeforeAll { + Mock -CommandName Assert-SetupActionProperties + Mock -CommandName Assert-ElevatedUser + Mock -CommandName Test-Path -ParameterFilter { + $Path -match 'setup\.exe' + } -MockWith { + return $true + } + } + + Context 'When specifying only mandatory parameters' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + InModuleScope -ScriptBlock { + $script:mockDefaultParameters = @{ + Repair = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + # Intentionally using both upper- and lower-case in the value. + Features = 'SqlEngine' + } + } + } + + Context 'When using parameter Confirm with value $false' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -Confirm:$false @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=Repair' + $ArgumentList | Should -MatchExactly '\/FEATURES=SQLENGINE' + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When using parameter Force' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -Force @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=Repair' + $ArgumentList | Should -MatchExactly '\/FEATURES=SQLENGINE' + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When using parameter WhatIf' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -WhatIf @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -Exactly -Times 0 -Scope It + } + } + } + } + + Context 'When specifying optional parameters PBStartPortRange and PBEndPortRange' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + InModuleScope -ScriptBlock { + $script:installSqlDscServerParameters = @{ + Repair = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + # Intentionally using both upper- and lower-case in the value. + Features = 'SqlEngine' + Force = $true + PBStartPortRange = 16450 + PBEndPortRange = 16460 + } + } + } + + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction @installSqlDscServerParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly 'PBPORTRANGE=16450-16460' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When specifying optional parameter ' -ForEach @( + @{ + MockParameterName = 'Enu' + MockParameterValue = $true + MockExpectedRegEx = '\/ENU\s*' + } + @{ + MockParameterName = 'PBEngSvcAccount' + MockParameterValue = 'NT Authority\NETWORK SERVICE' + MockExpectedRegEx = '\/PBENGSVCACCOUNT="NT Authority\\NETWORK SERVICE"' # cspell: disable-line + } + @{ + MockParameterName = 'PBEngSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/PBENGSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'PBEngSvcStartupType' + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/PBENGSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + @{ + MockParameterName = 'PBScaleOut' + MockParameterValue = $true + MockExpectedRegEx = '\/PBSCALEOUT=True' # cspell: disable-line + } + ) { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + InModuleScope -ScriptBlock { + $script:mockDefaultParameters = @{ + Repair = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + # Intentionally using both upper- and lower-case in the value. + Features = 'SqlEngine' + Force = $true + } + } + } + + BeforeEach { + InModuleScope -ScriptBlock { + $script:installSqlDscServerParameters = $mockDefaultParameters.Clone() + } + } + + It 'Should call the mock with the correct argument string' { + InModuleScope -Parameters $_ -ScriptBlock { + $installSqlDscServerParameters.$MockParameterName = $MockParameterValue + + Invoke-SetupAction @installSqlDscServerParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly $MockExpectedRegEx + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + } + + Context 'When setup action is ''Upgrade''' { + BeforeAll { + Mock -CommandName Assert-SetupActionProperties + Mock -CommandName Assert-ElevatedUser + Mock -CommandName Test-Path -ParameterFilter { + $Path -match 'setup\.exe' + } -MockWith { + return $true + } + } + + Context 'When specifying only mandatory parameters' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + InModuleScope -ScriptBlock { + $script:mockDefaultParameters = @{ + Upgrade = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + } + } + } + + Context 'When using parameter Confirm with value $false' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -Confirm:$false @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=Upgrade' + $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When using parameter Force' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -Force @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=Upgrade' + $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When using parameter WhatIf' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -WhatIf @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -Exactly -Times 0 -Scope It + } + } + } + } + + Context 'When specifying optional parameter ' -ForEach @( + @{ + MockParameterName = 'Enu' + MockParameterValue = $true + MockExpectedRegEx = '\/ENU\s*' + } + @{ + MockParameterName = 'UpdateEnabled' + MockParameterValue = $true + MockExpectedRegEx = '\/UPDATEENABLED=True' # cspell: disable-line + } + @{ + MockParameterName = 'UpdateSource' + MockParameterValue = '\SqlMedia\Updates' + MockExpectedRegEx = '\/UPDATESOURCE="\\SqlMedia\\Updates"' # cspell: disable-line + } + @{ + MockParameterName = 'InstanceDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server' + MockExpectedRegEx = '\/INSTANCEDIR="C:\\Program Files\\Microsoft SQL Server"' # cspell: disable-line + } + @{ + MockParameterName = 'InstanceId' + MockParameterValue = 'Instance' + MockExpectedRegEx = '\/INSTANCEID="Instance"' # cspell: disable-line + } + @{ + MockParameterName = 'ProductKey' + MockParameterValue = '22222-00000-00000-00000-00000' + MockExpectedRegEx = '\/PID="22222-00000-00000-00000-00000"' + } + @{ + MockParameterName = 'BrowserSvcStartupType' + MockParameterValue = 'Manual' + MockExpectedRegEx = '\/BROWSERSVCSTARTUPTYPE="Manual"' # cspell: disable-line + } + @{ + MockParameterName = 'ISSvcAccount' + MockParameterValue = 'DOMAIN\ServiceAccount$' + MockExpectedRegEx = '\/ISSVCACCOUNT="DOMAIN\\ServiceAccount\$"' # cspell: disable-line + } + @{ + MockParameterName = 'ISSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/ISSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'ISSvcStartupType' + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/ISSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + ) { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + InModuleScope -ScriptBlock { + $script:mockDefaultParameters = @{ + Upgrade = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + Force = $true + } + } + } + + BeforeEach { + InModuleScope -ScriptBlock { + $script:installSqlDscServerParameters = $mockDefaultParameters.Clone() + } + } + + It 'Should call the mock with the correct argument string' { + InModuleScope -Parameters $_ -ScriptBlock { + $installSqlDscServerParameters.$MockParameterName = $MockParameterValue + + Invoke-SetupAction @installSqlDscServerParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly $MockExpectedRegEx + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + } + + Context 'When setup action is ''PrepareImage''' { + BeforeAll { + Mock -CommandName Assert-SetupActionProperties + Mock -CommandName Assert-ElevatedUser + Mock -CommandName Test-Path -ParameterFilter { + $Path -match 'setup\.exe' + } -MockWith { + return $true + } + } + + Context 'When specifying only mandatory parameters' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + InModuleScope -ScriptBlock { + $script:mockDefaultParameters = @{ + PrepareImage = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + # Intentionally using both upper- and lower-case. + Features = 'SqlEngine' + InstanceId = 'Instance' + } + } + } + + Context 'When using parameter Confirm with value $false' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -Confirm:$false @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=PrepareImage' + $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/FEATURES=SQLENGINE' + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/INSTANCEID="Instance"' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When using parameter Force' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -Force @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=PrepareImage' + $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/FEATURES=SQLENGINE' + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/INSTANCEID="Instance"' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When using parameter WhatIf' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -WhatIf @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -Exactly -Times 0 -Scope It + } + } + } + } + + Context 'When specifying optional parameter ' -ForEach @( + @{ + MockParameterName = 'Enu' + MockParameterValue = $true + MockExpectedRegEx = '\/ENU\s*' + } + @{ + MockParameterName = 'UpdateEnabled' + MockParameterValue = $true + MockExpectedRegEx = '\/UPDATEENABLED=True' # cspell: disable-line + } + @{ + MockParameterName = 'UpdateSource' + MockParameterValue = '\SqlMedia\Updates' + MockExpectedRegEx = '\/UPDATESOURCE="\\SqlMedia\\Updates"' # cspell: disable-line + } + @{ + MockParameterName = 'InstallSharedDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server' + MockExpectedRegEx = '\/INSTALLSHAREDDIR="C:\\Program Files\\Microsoft SQL Server"' # cspell: disable-line + } + @{ + MockParameterName = 'InstanceDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server' + MockExpectedRegEx = '\/INSTANCEDIR="C:\\Program Files\\Microsoft SQL Server"' # cspell: disable-line + } + @{ + MockParameterName = 'InstanceId' + MockParameterValue = 'Instance' + MockExpectedRegEx = '\/INSTANCEID="Instance"' # cspell: disable-line + } + @{ + MockParameterName = 'PBEngSvcAccount' + MockParameterValue = 'NT Authority\NETWORK SERVICE' + MockExpectedRegEx = '\/PBENGSVCACCOUNT="NT Authority\\NETWORK SERVICE"' # cspell: disable-line + } + @{ + MockParameterName = 'PBEngSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/PBENGSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'PBEngSvcStartupType' + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/PBENGSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + @{ + MockParameterName = 'PBScaleOut' + MockParameterValue = $true + MockExpectedRegEx = '\/PBSCALEOUT=True' # cspell: disable-line + } + ) { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + InModuleScope -ScriptBlock { + $script:mockDefaultParameters = @{ + PrepareImage = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + # Intentionally using both upper- and lower-case. + Features = 'SqlEngine' + InstanceId = 'Instance' + Force = $true + } + } + } + + BeforeEach { + InModuleScope -ScriptBlock { + $script:installSqlDscServerParameters = $mockDefaultParameters.Clone() + } + } + + It 'Should call the mock with the correct argument string' { + InModuleScope -Parameters $_ -ScriptBlock { + $installSqlDscServerParameters.$MockParameterName = $MockParameterValue + + Invoke-SetupAction @installSqlDscServerParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly $MockExpectedRegEx + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + } + + Context 'When setup action is ''CompleteImage''' { + BeforeAll { + Mock -CommandName Assert-SetupActionProperties + Mock -CommandName Assert-ElevatedUser + Mock -CommandName Test-Path -ParameterFilter { + $Path -match 'setup\.exe' + } -MockWith { + return $true + } + } + + Context 'When specifying only mandatory parameters' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + InModuleScope -ScriptBlock { + $script:mockDefaultParameters = @{ + CompleteImage = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + } + } + } + + Context 'When using parameter Confirm with value $false' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -Confirm:$false @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=CompleteImage' + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When using parameter Force' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -Force @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=CompleteImage' + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When using parameter WhatIf' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -WhatIf @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -Exactly -Times 0 -Scope It + } + } + } + } + + Context 'When specifying optional parameters PBStartPortRange and PBEndPortRange' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + InModuleScope -ScriptBlock { + $script:installSqlDscServerParameters = @{ + CompleteImage = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + Force = $true + PBStartPortRange = 16450 + PBEndPortRange = 16460 + } + } + } + + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction @installSqlDscServerParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly 'PBPORTRANGE=16450-16460' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When specifying optional parameter ' -ForEach @( + @{ + MockParameterName = 'InstanceName' + MockParameterValue = 'INSTANCE' + MockExpectedRegEx = '\/INSTANCENAME="INSTANCE"*' # cspell: disable-line + } + @{ + MockParameterName = 'Enu' + MockParameterValue = $true + MockExpectedRegEx = '\/ENU\s*' + } + @{ + MockParameterName = 'InstanceId' + MockParameterValue = 'Instance' + MockExpectedRegEx = '\/INSTANCEID="Instance"' # cspell: disable-line + } + @{ + MockParameterName = 'PBEngSvcAccount' + MockParameterValue = 'NT Authority\NETWORK SERVICE' + MockExpectedRegEx = '\/PBENGSVCACCOUNT="NT Authority\\NETWORK SERVICE"' # cspell: disable-line + } + @{ + MockParameterName = 'PBEngSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/PBENGSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'PBEngSvcStartupType' + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/PBENGSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + @{ + MockParameterName = 'PBScaleOut' + MockParameterValue = $true + MockExpectedRegEx = '\/PBSCALEOUT=True' # cspell: disable-line + } + @{ + MockParameterName = 'ProductKey' + MockParameterValue = '22222-00000-00000-00000-00000' + MockExpectedRegEx = '\/PID="22222-00000-00000-00000-00000"' + } + @{ + MockParameterName = 'AgtSvcAccount' + MockParameterValue = 'NT Authority\NETWORK SERVICE' + MockExpectedRegEx = '\/AGTSVCACCOUNT="NT Authority\\NETWORK SERVICE"' # cspell: disable-line + } + @{ + MockParameterName = 'AgtSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/AGTSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'AgtSvcStartupType' + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/AGTSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + @{ + MockParameterName = 'BrowserSvcStartupType' + MockParameterValue = 'Manual' + MockExpectedRegEx = '\/BROWSERSVCSTARTUPTYPE="Manual"' # cspell: disable-line + } + @{ + MockParameterName = 'EnableRanU' + MockParameterValue = $true + MockExpectedRegEx = '\/ENABLERANU' # cspell: disable-line + } + @{ + MockParameterName = 'InstallSqlDataDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + MockExpectedRegEx = '\/INSTALLSQLDATADIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlBackupDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Backup' + MockExpectedRegEx = '\/SQLBACKUPDIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Backup"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + MockExpectedRegEx = '\/SQLTEMPDBDIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbLogDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + MockExpectedRegEx = '\/SQLTEMPDBLOGDIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlUserDbDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + MockExpectedRegEx = '\/SQLUSERDBDIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlUserDbLogDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + MockExpectedRegEx = '\/SQLUSERDBLOGDIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'SecurityMode' + MockParameterValue = 'SQL' + MockExpectedRegEx = '\/SECURITYMODE="SQL"' # cspell: disable-line + } + @{ + MockParameterName = 'SAPwd' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/SAPWD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlCollation' + MockParameterValue = 'SQL_Latin1_General_CP1_CI_AS' + MockExpectedRegEx = '\/SQLCOLLATION="SQL_Latin1_General_CP1_CI_AS"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlSvcAccount' + MockParameterValue = 'DOMAIN\ServiceAccount$' + MockExpectedRegEx = '\/SQLSVCACCOUNT="DOMAIN\\ServiceAccount\$"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/SQLSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlSvcStartupType' + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/SQLSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbFileCount' + MockParameterValue = 8 + MockExpectedRegEx = '\/SQLTEMPDBFILECOUNT=8' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbFileSize' + MockParameterValue = 100 + MockExpectedRegEx = '\/SQLTEMPDBFILESIZE=100' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbFileGrowth' + MockParameterValue = 10 + MockExpectedRegEx = '\/SQLTEMPDBFILEGROWTH=10' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbLogFileSize' + MockParameterValue = 100 + MockExpectedRegEx = '\/SQLTEMPDBLOGFILESIZE=100' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbLogFileGrowth' + MockParameterValue = 10 + MockExpectedRegEx = '\/SQLTEMPDBLOGFILEGROWTH=10' # cspell: disable-line + } + @{ + MockParameterName = 'FileStreamLevel' + MockParameterValue = 2 + MockExpectedRegEx = '\/FILESTREAMLEVEL=2' # cspell: disable-line + } + @{ + MockParameterName = 'FileStreamShareName' + MockParameterValue = 'ShareName' + MockExpectedRegEx = '\/FILESTREAMSHARENAME="ShareName"' # cspell: disable-line + } + @{ + MockParameterName = 'NpEnabled' + MockParameterValue = $true + MockExpectedRegEx = '\/NPENABLED=1' # cspell: disable-line + } + @{ + MockParameterName = 'TcpEnabled' + MockParameterValue = $true + MockExpectedRegEx = '\/TCPENABLED=1' # cspell: disable-line + } + @{ + MockParameterName = 'RsInstallMode' + MockParameterValue = 'FilesOnlyMode' + MockExpectedRegEx = '\/RSINSTALLMODE="FilesOnlyMode"' # cspell: disable-line + } + @{ + MockParameterName = 'RSSvcAccount' + MockParameterValue = 'DOMAIN\ServiceAccount$' + MockExpectedRegEx = '\/RSSVCACCOUNT="DOMAIN\\ServiceAccount\$"' # cspell: disable-line + } + @{ + MockParameterName = 'RSSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/RSSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'RSSvcStartupType' + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/RSSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + ) { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + InModuleScope -ScriptBlock { + $script:mockDefaultParameters = @{ + CompleteImage = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + Force = $true + } + } + } + + BeforeEach { + InModuleScope -ScriptBlock { + $script:installSqlDscServerParameters = $mockDefaultParameters.Clone() + } + } + + It 'Should call the mock with the correct argument string' { + InModuleScope -Parameters $_ -ScriptBlock { + $installSqlDscServerParameters.$MockParameterName = $MockParameterValue + + Invoke-SetupAction @installSqlDscServerParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly $MockExpectedRegEx + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + } + + Context 'When setup action is ''Uninstall''' { + BeforeAll { + Mock -CommandName Assert-SetupActionProperties + Mock -CommandName Assert-ElevatedUser + Mock -CommandName Test-Path -ParameterFilter { + $Path -match 'setup\.exe' + } -MockWith { + return $true + } + } + + Context 'When specifying only mandatory parameters' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + InModuleScope -ScriptBlock { + $script:mockDefaultParameters = @{ + Uninstall = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + # Intentionally using both upper- and lower-case. + Features = 'SqlEngine' + } + } + } + + Context 'When using parameter Confirm with value $false' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -Confirm:$false @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=Uninstall' + $ArgumentList | Should -MatchExactly '\/FEATURES=SQLENGINE' + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When using parameter Force' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -Force @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=Uninstall' + $ArgumentList | Should -MatchExactly '\/FEATURES=SQLENGINE' + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When using parameter WhatIf' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -WhatIf @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -Exactly -Times 0 -Scope It + } + } + } + } + } + + Context 'When setup action is ''Install'' for installing Azure Arc Agent (parameter set InstallAzureArcAgent)' { + BeforeAll { + Mock -CommandName Assert-SetupActionProperties + Mock -CommandName Assert-ElevatedUser + Mock -CommandName Test-Path -ParameterFilter { + $Path -match 'setup\.exe' + } -MockWith { + return $true + } + } + + Context 'When specifying only mandatory parameters' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + InModuleScope -ScriptBlock { + $script:mockDefaultParameters = @{ + Install = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + AzureSubscriptionId = '5d19794a-89a4-4f0b-8d4e-58f213ea3546' + AzureResourceGroup = 'MyResourceGroup' + AzureRegion = 'West-US' + AzureTenantId = '7e52fb9e-6aad-426c-98c4-7d2f11f7e94b' + AzureServicePrincipal = 'MyServicePrincipal' + AzureServicePrincipalSecret = ('jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force) # cspell: disable-linePbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force) # cspell: disable-line + } + } + } + + Context 'When using parameter Confirm with value $false' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -Confirm:$false @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=Install' + $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/AZURESUBSCRIPTIONID="5d19794a-89a4-4f0b-8d4e-58f213ea3546"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/AZURERESOURCEGROUP="MyResourceGroup"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/AZUREREGION="West-US"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/AZURETENANTID="7e52fb9e-6aad-426c-98c4-7d2f11f7e94b"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/AZURESERVICEPRINCIPAL="MyServicePrincipal"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/AZURESERVICEPRINCIPALSECRET="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/FEATURES=ARC' + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When using parameter Force' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -Force @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=Install' + $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/AZURESUBSCRIPTIONID="5d19794a-89a4-4f0b-8d4e-58f213ea3546"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/AZURERESOURCEGROUP="MyResourceGroup"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/AZUREREGION="West-US"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/AZURETENANTID="7e52fb9e-6aad-426c-98c4-7d2f11f7e94b"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/AZURESERVICEPRINCIPAL="MyServicePrincipal"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/AZURESERVICEPRINCIPALSECRET="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/FEATURES=ARC' + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When using parameter WhatIf' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -WhatIf @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -Exactly -Times 0 -Scope It + } + } + } + } + + Context 'When specifying optional parameter ' -ForEach @( + @{ + MockParameterName = 'AzureArcProxy' + MockParameterValue = 'proxy.company.local' + MockExpectedRegEx = '\/AZUREARCPROXY="proxy\.company\.local"' # cspell: disable-line + } + ) { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + InModuleScope -ScriptBlock { + $script:mockDefaultParameters = @{ + Install = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + AzureSubscriptionId = '5d19794a-89a4-4f0b-8d4e-58f213ea3546' + AzureResourceGroup = 'MyResourceGroup' + AzureRegion = 'West-US' + AzureTenantId = '7e52fb9e-6aad-426c-98c4-7d2f11f7e94b' + AzureServicePrincipal = 'MyServicePrincipal' + AzureServicePrincipalSecret = ('jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force) # cspell: disable-line + Force = $true + } + } + } + + BeforeEach { + InModuleScope -ScriptBlock { + $script:installSqlDscServerParameters = $mockDefaultParameters.Clone() + } + } + + It 'Should call the mock with the correct argument string' { + InModuleScope -Parameters $_ -ScriptBlock { + $installSqlDscServerParameters.$MockParameterName = $MockParameterValue + + Invoke-SetupAction @installSqlDscServerParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly $MockExpectedRegEx + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + } + + Context 'When parameter set is ''InstallRole''' { + BeforeAll { + Mock -CommandName Assert-SetupActionProperties + Mock -CommandName Assert-ElevatedUser + Mock -CommandName Test-Path -ParameterFilter { + $Path -match 'setup\.exe' + } -MockWith { + return $true + } + } + + Context 'When role is ''SPI_AS_NewFarm''' { + Context 'When specifying only mandatory parameters' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } + + InModuleScope -ScriptBlock { + $script:mockDefaultParameters = @{ + Install = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + Role = 'SPI_AS_NewFarm' + } + } + } + + Context 'When using parameter Confirm with value $false' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -Confirm:$false @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=Install' + $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/ROLE=SPI_AS_NEWFARM' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When using parameter Force' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -Force @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=Install' + $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/ROLE=SPI_AS_NEWFARM' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When using parameter WhatIf' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -WhatIf @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -Exactly -Times 0 -Scope It + } + } + } + } + + Context 'When specifying optional parameter ' -ForEach @( + @{ + MockParameterName = 'FarmAccount' + MockParameterValue = 'DOMAIN\User' + MockExpectedRegEx = '\/FARMACCOUNT="DOMAIN\\User"' # cspell: disable-line + } + @{ + MockParameterName = 'FarmPassword' + MockParameterValue = ('jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force) # cspell: disable-line + MockExpectedRegEx = '\/FARMPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'Passphrase' + MockParameterValue = ('jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force) # cspell: disable-line + MockExpectedRegEx = '\/PASSPHRASE="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'FarmAdminiPort' # cspell: disable-line + MockParameterValue = '18000' + MockExpectedRegEx = '\/FARMADMINIPORT=18000' # cspell: disable-line + } + ) { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } + + InModuleScope -ScriptBlock { + $script:mockDefaultParameters = @{ + Install = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + Role = 'SPI_AS_NewFarm' + Force = $true + } + } + } + + BeforeEach { + InModuleScope -ScriptBlock { + $script:installSqlDscServerParameters = $mockDefaultParameters.Clone() + } + } + + It 'Should call the mock with the correct argument string' { + InModuleScope -Parameters $_ -ScriptBlock { + $installSqlDscServerParameters.$MockParameterName = $MockParameterValue + + Invoke-SetupAction @installSqlDscServerParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly $MockExpectedRegEx + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When specifying sensitive parameter ' -ForEach @( + @{ + MockParameterName = 'FarmPassword' + MockParameterValue = ('jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force) # cspell: disable-line + MockExpectedRegEx = '\/FARMPASSWORD="\*{8}"' # cspell: disable-line + } + @{ + MockParameterName = 'Passphrase' + MockParameterValue = ('jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force) # cspell: disable-line + MockExpectedRegEx = '\/PASSPHRASE="\*{8}"' # cspell: disable-line + } + ) { + BeforeAll { + Mock -CommandName Write-Verbose + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } + + InModuleScope -ScriptBlock { + $script:mockDefaultParameters = @{ + Install = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + Role = 'SPI_AS_NewFarm' + Force = $true + } + } + } + + BeforeEach { + InModuleScope -ScriptBlock { + $script:installSqlDscServerParameters = $mockDefaultParameters.Clone() + } + } + + It 'Should obfuscate the value in the verbose string' { + InModuleScope -Parameters $_ -ScriptBlock { + $installSqlDscServerParameters.$MockParameterName = $MockParameterValue + + # Redirect all verbose stream to $null to ge no output from ShouldProcess. + Invoke-SetupAction @installSqlDscServerParameters -Verbose 4> $null + + $mockVerboseMessage = $script:localizedData.Server_SetupArguments + + Should -Invoke -CommandName Write-Verbose -ParameterFilter { + # Only test the command that output the string that should be tested. + $correctMessage = $Message -match $mockVerboseMessage + + # Only test string if it is the correct verbose command + if ($correctMessage) + { + $Message | Should -MatchExactly $MockExpectedRegEx + } + + # Return wether the correct command was called or not. + $correctMessage + } -Exactly -Times 1 -Scope It + } + } + } + } + + Context 'When role is ''AllFeatures_WithDefaults''' { + Context 'When specifying only mandatory parameters' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } + + InModuleScope -ScriptBlock { + $script:mockDefaultParameters = @{ + Install = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + Role = 'AllFeatures_WithDefaults' + } + } + } + + Context 'When using parameter Confirm with value $false' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -Confirm:$false @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=Install' + $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/ROLE=ALLFEATURES_WITHDEFAULTS' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When using parameter Force' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -Force @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=Install' + $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/ROLE=ALLFEATURES_WITHDEFAULTS' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When using parameter WhatIf' { + It 'Should call the mock with the correct argument string' { + InModuleScope -ScriptBlock { + Invoke-SetupAction -WhatIf @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -Exactly -Times 0 -Scope It + } + } + } + } + + Context 'When specifying optional parameter ' -ForEach @( + @{ + MockParameterName = 'Features' # cspell: disable-line + MockParameterValue = 'SqlEngine', 'RS' + MockExpectedRegEx = '\/FEATURES=SQLENGINE,RS' # cspell: disable-line + } + @{ + MockParameterName = 'AddCurrentUserAsSqlAdmin' # cspell: disable-line + MockParameterValue = $true + MockExpectedRegEx = '\/ADDCURRENTUSERASSQLADMIN=True' # cspell: disable-line + } + ) { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } + + InModuleScope -ScriptBlock { + $script:mockDefaultParameters = @{ + Install = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + Role = 'AllFeatures_WithDefaults' + Force = $true + } + } + } + + BeforeEach { + InModuleScope -ScriptBlock { + $script:installSqlDscServerParameters = $mockDefaultParameters.Clone() + } + } + + It 'Should call the mock with the correct argument string' { + InModuleScope -Parameters $_ -ScriptBlock { + $installSqlDscServerParameters.$MockParameterName = $MockParameterValue + + Invoke-SetupAction @installSqlDscServerParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly $MockExpectedRegEx + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + } + } +} diff --git a/tests/Unit/Private/Test-IsNumericType.Tests.ps1 b/tests/Unit/Private/Test-IsNumericType.Tests.ps1 new file mode 100644 index 000000000..7e32db634 --- /dev/null +++ b/tests/Unit/Private/Test-IsNumericType.Tests.ps1 @@ -0,0 +1,89 @@ +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] +param () + +BeforeDiscovery { + try + { + if (-not (Get-Module -Name 'DscResource.Test')) + { + # Assumes dependencies has been resolved, so if this module is not available, run 'noop' task. + if (-not (Get-Module -Name 'DscResource.Test' -ListAvailable)) + { + # Redirect all streams to $null, except the error stream (stream 2) + & "$PSScriptRoot/../../build.ps1" -Tasks 'noop' 2>&1 4>&1 5>&1 6>&1 > $null + } + + # If the dependencies has not been resolved, this will throw an error. + Import-Module -Name 'DscResource.Test' -Force -ErrorAction 'Stop' + } + } + catch [System.IO.FileNotFoundException] + { + throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -ResolveDependency -Tasks build" first.' + } +} + +BeforeAll { + $script:dscModuleName = 'SqlServerDsc' + + Import-Module -Name $script:dscModuleName + + $PSDefaultParameterValues['InModuleScope:ModuleName'] = $script:dscModuleName + $PSDefaultParameterValues['Mock:ModuleName'] = $script:dscModuleName + $PSDefaultParameterValues['Should:ModuleName'] = $script:dscModuleName +} + +AfterAll { + $PSDefaultParameterValues.Remove('InModuleScope:ModuleName') + $PSDefaultParameterValues.Remove('Mock:ModuleName') + $PSDefaultParameterValues.Remove('Should:ModuleName') + + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force +} + +Describe 'Test-IsNumericType' -Tag 'Private' { + Context 'When passing value with named parameter' { + Context 'When type is numeric' { + It 'Should return the correct value' { + InModuleScope -ScriptBlock { + $result = Test-IsNumericType -Object ([System.UInt32] 3) + + $result | Should -BeTrue + } + } + } + + Context 'When type is not numeric' { + It 'Should return the correct value' { + InModuleScope -ScriptBlock { + $result = Test-IsNumericType -Object ([System.String] 'a') + + $result | Should -BeFalse + } + } + } + } + + Context 'When passing value in pipeline' { + Context 'When type is numeric' { + It 'Should return the correct value' { + InModuleScope -ScriptBlock { + $result = ([System.UInt32] 3) | Test-IsNumericType + + $result | Should -BeTrue + } + } + } + + Context 'When type is not numeric' { + It 'Should return the correct value' { + InModuleScope -ScriptBlock { + $result = ([System.String] 'a') | Test-IsNumericType + + $result | Should -BeFalse + } + } + } + } +} diff --git a/tests/Unit/Private/Test-ServiceAccountRequirePassword.Tests.ps1 b/tests/Unit/Private/Test-ServiceAccountRequirePassword.Tests.ps1 new file mode 100644 index 000000000..0d40bedaf --- /dev/null +++ b/tests/Unit/Private/Test-ServiceAccountRequirePassword.Tests.ps1 @@ -0,0 +1,85 @@ +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] +param () + +BeforeDiscovery { + try + { + if (-not (Get-Module -Name 'DscResource.Test')) + { + # Assumes dependencies has been resolved, so if this module is not available, run 'noop' task. + if (-not (Get-Module -Name 'DscResource.Test' -ListAvailable)) + { + # Redirect all streams to $null, except the error stream (stream 2) + & "$PSScriptRoot/../../build.ps1" -Tasks 'noop' 2>&1 4>&1 5>&1 6>&1 > $null + } + + # If the dependencies has not been resolved, this will throw an error. + Import-Module -Name 'DscResource.Test' -Force -ErrorAction 'Stop' + } + } + catch [System.IO.FileNotFoundException] + { + throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -ResolveDependency -Tasks build" first.' + } +} + +BeforeAll { + $script:dscModuleName = 'SqlServerDsc' + + Import-Module -Name $script:dscModuleName + + $PSDefaultParameterValues['InModuleScope:ModuleName'] = $script:dscModuleName + $PSDefaultParameterValues['Mock:ModuleName'] = $script:dscModuleName + $PSDefaultParameterValues['Should:ModuleName'] = $script:dscModuleName +} + +AfterAll { + $PSDefaultParameterValues.Remove('InModuleScope:ModuleName') + $PSDefaultParameterValues.Remove('Mock:ModuleName') + $PSDefaultParameterValues.Remove('Should:ModuleName') + + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force +} + +Describe 'Test-ServiceAccountRequirePassword' -Tag 'Private' { + Context 'When service account is a built-in account' { + It 'Should return $false' { + InModuleScope -ScriptBlock { + Test-ServiceAccountRequirePassword -Name 'NT Authority\NETWORK SERVICE' | Should -BeFalse + } + } + } + + Context 'When service account is a virtual account' { + It 'Should return $false' { + InModuleScope -ScriptBlock { + Test-ServiceAccountRequirePassword -Name 'NT SERVICE\MSSQL$PAYROLL' | Should -BeFalse + } + } + } + + Context 'When service account is a (global) managed service account' { + It 'Should return $false' { + InModuleScope -ScriptBlock { + Test-ServiceAccountRequirePassword -Name 'DOMAIN\MyMSA$' | Should -BeFalse + } + } + } + + Context 'When service account is a local user account' { + It 'Should return $true' { + InModuleScope -ScriptBlock { + Test-ServiceAccountRequirePassword -Name 'MySqlUser' | Should -BeTrue + } + } + } + + Context 'When service account is a domain user account' { + It 'Should return $true' { + InModuleScope -ScriptBlock { + Test-ServiceAccountRequirePassword -Name 'DOMAIN\MySqlUser' | Should -BeTrue + } + } + } +} diff --git a/tests/Unit/Public/Add-SqlDscNode.Tests.ps1 b/tests/Unit/Public/Add-SqlDscNode.Tests.ps1 new file mode 100644 index 000000000..ff1d59ee2 --- /dev/null +++ b/tests/Unit/Public/Add-SqlDscNode.Tests.ps1 @@ -0,0 +1,305 @@ +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '', Justification = 'because ConvertTo-SecureString is used to simplify the tests.')] +param () + +BeforeDiscovery { + try + { + if (-not (Get-Module -Name 'DscResource.Test')) + { + # Assumes dependencies has been resolved, so if this module is not available, run 'noop' task. + if (-not (Get-Module -Name 'DscResource.Test' -ListAvailable)) + { + # Redirect all streams to $null, except the error stream (stream 2) + & "$PSScriptRoot/../../build.ps1" -Tasks 'noop' 2>&1 4>&1 5>&1 6>&1 > $null + } + + # If the dependencies has not been resolved, this will throw an error. + Import-Module -Name 'DscResource.Test' -Force -ErrorAction 'Stop' + } + } + catch [System.IO.FileNotFoundException] + { + throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -ResolveDependency -Tasks build" first.' + } +} + +BeforeAll { + $script:dscModuleName = 'SqlServerDsc' + + Import-Module -Name $script:dscModuleName + + $PSDefaultParameterValues['InModuleScope:ModuleName'] = $script:dscModuleName + $PSDefaultParameterValues['Mock:ModuleName'] = $script:dscModuleName + $PSDefaultParameterValues['Should:ModuleName'] = $script:dscModuleName +} + +AfterAll { + $PSDefaultParameterValues.Remove('InModuleScope:ModuleName') + $PSDefaultParameterValues.Remove('Mock:ModuleName') + $PSDefaultParameterValues.Remove('Should:ModuleName') + + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force +} + +Describe 'Add-SqlDscNode' -Tag 'Public' { + It 'Should have the correct parameters in parameter set ' -ForEach @( + @{ + MockParameterSetName = '__AllParameterSets' + # cSpell: disable-next + MockExpectedParameters = '[-MediaPath] [-InstanceName] [[-UpdateSource] ] [[-PBEngSvcAccount] ] [[-PBEngSvcPassword] ] [[-PBEngSvcStartupType] ] [[-PBStartPortRange] ] [[-PBEndPortRange] ] [[-ProductKey] ] [[-AgtSvcAccount] ] [[-AgtSvcPassword] ] [[-ASSvcAccount] ] [[-ASSvcPassword] ] [[-SqlSvcAccount] ] [[-SqlSvcPassword] ] [[-ISSvcAccount] ] [[-ISSvcPassword] ] [[-RsInstallMode] ] [[-RSSvcAccount] ] [[-RSSvcPassword] ] [-FailoverClusterIPAddresses] [[-Timeout] ] -AcceptLicensingTerms [-IAcknowledgeEntCalLimits] [-Enu] [-UpdateEnabled] [-PBScaleOut] [-ConfirmIPDependencyChange] [-Force] [-WhatIf] [-Confirm] []' + } + ) { + $result = (Get-Command -Name 'Add-SqlDscNode').ParameterSets | + Where-Object -FilterScript { + $_.Name -eq $mockParameterSetName + } | + Select-Object -Property @( + @{ + Name = 'ParameterSetName' + Expression = { $_.Name } + }, + @{ + Name = 'ParameterListAsString' + Expression = { $_.ToString() } + } + ) + + $result.ParameterSetName | Should -Be $MockParameterSetName + $result.ParameterListAsString | Should -Be $MockExpectedParameters + } + + Context 'When setup action is ''AddNode''' { + BeforeAll { + Mock -CommandName Assert-SetupActionProperties + Mock -CommandName Assert-ElevatedUser + Mock -CommandName Test-Path -ParameterFilter { + $Path -match 'setup\.exe' + } -MockWith { + return $true + } + } + + Context 'When specifying only mandatory parameters' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + $mockDefaultParameters = @{ + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + FailoverClusterIPAddresses = @( + 'IPv4;172.16.0.0;ClusterNetwork1;172.31.255.255', + 'IPv6;2001:db8:23:1002:20f:1fff:feff:b3a3;ClusterNetwork2' # cspell: disable-line + 'IPv6;DHCP;ClusterNetwork3' + 'IPv4;DHCP;ClusterNetwork4' + ) + } + } + + Context 'When using parameter Confirm with value $false' { + It 'Should call the mock with the correct argument string' { + Add-SqlDscNode -Confirm:$false @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=AddNode' + $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + + Context 'When using parameter Force' { + It 'Should call the mock with the correct argument string' { + Add-SqlDscNode -Force @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=AddNode' + $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + + Context 'When using parameter WhatIf' { + It 'Should call the mock with the correct argument string' { + Add-SqlDscNode -WhatIf @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -Exactly -Times 0 -Scope It + } + } + } + + Context 'When specifying optional parameters PBStartPortRange and PBEndPortRange' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + $addSqlDscNodeParameters = @{ + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + FailoverClusterIPAddresses = @( + 'IPv4;172.16.0.0;ClusterNetwork1;172.31.255.255', + 'IPv6;2001:db8:23:1002:20f:1fff:feff:b3a3;ClusterNetwork2' # cspell: disable-line + 'IPv6;DHCP;ClusterNetwork3' + 'IPv4;DHCP;ClusterNetwork4' + ) + Force = $true + PBStartPortRange = 16450 + PBEndPortRange = 16460 + } + } + + It 'Should call the mock with the correct argument string' { + Add-SqlDscNode @addSqlDscNodeParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly 'PBPORTRANGE=16450-16460' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + + Context 'When specifying optional parameter ' -ForEach @( + @{ + MockParameterName = 'Enu' + MockParameterValue = $true + MockExpectedRegEx = '\/ENU\s*' + } + @{ + MockParameterName = 'UpdateEnabled' + MockParameterValue = $true + MockExpectedRegEx = '\/UPDATEENABLED=True' # cspell: disable-line + } + @{ + MockParameterName = 'UpdateSource' + MockParameterValue = '\SqlMedia\Updates' + MockExpectedRegEx = '\/UPDATESOURCE="\\SqlMedia\\Updates"' # cspell: disable-line + } + @{ + MockParameterName = 'PBEngSvcAccount' + MockParameterValue = 'NT Authority\NETWORK SERVICE' + MockExpectedRegEx = '\/PBENGSVCACCOUNT="NT Authority\\NETWORK SERVICE"' # cspell: disable-line + } + @{ + MockParameterName = 'PBEngSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/PBENGSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'PBEngSvcStartupType' + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/PBENGSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + @{ + MockParameterName = 'PBScaleOut' + MockParameterValue = $true + MockExpectedRegEx = '\/PBSCALEOUT=True' # cspell: disable-line + } + @{ + MockParameterName = 'ProductKey' + MockParameterValue = '22222-00000-00000-00000-00000' + MockExpectedRegEx = '\/PID="22222-00000-00000-00000-00000"' + } + @{ + MockParameterName = 'AgtSvcAccount' + MockParameterValue = 'NT Authority\NETWORK SERVICE' + MockExpectedRegEx = '\/AGTSVCACCOUNT="NT Authority\\NETWORK SERVICE"' # cspell: disable-line + } + @{ + MockParameterName = 'AgtSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/AGTSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'AsSvcAccount' + MockParameterValue = 'DOMAIN\ServiceAccount$' + MockExpectedRegEx = '\/ASSVCACCOUNT="DOMAIN\\ServiceAccount\$"' # cspell: disable-line + } + @{ + MockParameterName = 'AsSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/ASSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlSvcAccount' + MockParameterValue = 'DOMAIN\ServiceAccount$' + MockExpectedRegEx = '\/SQLSVCACCOUNT="DOMAIN\\ServiceAccount\$"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/SQLSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'ISSvcAccount' + MockParameterValue = 'DOMAIN\ServiceAccount$' + MockExpectedRegEx = '\/ISSVCACCOUNT="DOMAIN\\ServiceAccount\$"' # cspell: disable-line + } + @{ + MockParameterName = 'ISSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/ISSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'RsInstallMode' + MockParameterValue = 'FilesOnlyMode' + MockExpectedRegEx = '\/RSINSTALLMODE="FilesOnlyMode"' # cspell: disable-line + } + @{ + MockParameterName = 'ConfirmIPDependencyChange' + MockParameterValue = $true + MockExpectedRegEx = '\/CONFIRMIPDEPENDENCYCHANGE=1' # cspell: disable-line + } + ) { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + $mockDefaultParameters = @{ + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + FailoverClusterIPAddresses = @( + 'IPv4;172.16.0.0;ClusterNetwork1;172.31.255.255', + 'IPv6;2001:db8:23:1002:20f:1fff:feff:b3a3;ClusterNetwork2' # cspell: disable-line + 'IPv6;DHCP;ClusterNetwork3' + 'IPv4;DHCP;ClusterNetwork4' + ) + Force = $true + } + } + + BeforeEach { + $addSqlDscNodeParameters = $mockDefaultParameters.Clone() + } + + It 'Should call the mock with the correct argument string' { + $addSqlDscNodeParameters.$MockParameterName = $MockParameterValue + + Add-SqlDscNode @addSqlDscNodeParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly $MockExpectedRegEx + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } +} diff --git a/tests/Unit/Public/Complete-SqlDscFailoverCluster.Tests.ps1 b/tests/Unit/Public/Complete-SqlDscFailoverCluster.Tests.ps1 new file mode 100644 index 000000000..622160e77 --- /dev/null +++ b/tests/Unit/Public/Complete-SqlDscFailoverCluster.Tests.ps1 @@ -0,0 +1,357 @@ +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '', Justification = 'because ConvertTo-SecureString is used to simplify the tests.')] +param () + +BeforeDiscovery { + try + { + if (-not (Get-Module -Name 'DscResource.Test')) + { + # Assumes dependencies has been resolved, so if this module is not available, run 'noop' task. + if (-not (Get-Module -Name 'DscResource.Test' -ListAvailable)) + { + # Redirect all streams to $null, except the error stream (stream 2) + & "$PSScriptRoot/../../build.ps1" -Tasks 'noop' 2>&1 4>&1 5>&1 6>&1 > $null + } + + # If the dependencies has not been resolved, this will throw an error. + Import-Module -Name 'DscResource.Test' -Force -ErrorAction 'Stop' + } + } + catch [System.IO.FileNotFoundException] + { + throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -ResolveDependency -Tasks build" first.' + } +} + +BeforeAll { + $script:dscModuleName = 'SqlServerDsc' + + Import-Module -Name $script:dscModuleName + + $PSDefaultParameterValues['InModuleScope:ModuleName'] = $script:dscModuleName + $PSDefaultParameterValues['Mock:ModuleName'] = $script:dscModuleName + $PSDefaultParameterValues['Should:ModuleName'] = $script:dscModuleName +} + +AfterAll { + $PSDefaultParameterValues.Remove('InModuleScope:ModuleName') + $PSDefaultParameterValues.Remove('Mock:ModuleName') + $PSDefaultParameterValues.Remove('Should:ModuleName') + + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force +} + +Describe 'Complete-SqlDscFailoverCluster' -Tag 'Public' { + It 'Should have the correct parameters in parameter set ' -ForEach @( + @{ + MockParameterSetName = '__AllParameterSets' + # cSpell: disable-next + MockExpectedParameters = '[-MediaPath] [-InstanceName] [[-ProductKey] ] [[-ASBackupDir] ] [[-ASCollation] ] [[-ASConfigDir] ] [[-ASDataDir] ] [[-ASLogDir] ] [[-ASTempDir] ] [[-ASServerMode] ] [[-ASSysAdminAccounts] ] [-InstallSqlDataDir] [[-SqlBackupDir] ] [[-SecurityMode] ] [[-SAPwd] ] [[-SqlCollation] ] [-SqlSysAdminAccounts] [[-SqlTempDbDir] ] [[-SqlTempDbLogDir] ] [[-SqlTempDbFileCount] ] [[-SqlTempDbFileSize] ] [[-SqlTempDbFileGrowth] ] [[-SqlTempDbLogFileSize] ] [[-SqlTempDbLogFileGrowth] ] [[-SqlUserDbDir] ] [[-SqlUserDbLogDir] ] [[-RsInstallMode] ] [[-FailoverClusterGroup] ] [[-FailoverClusterDisks] ] [-FailoverClusterNetworkName] [-FailoverClusterIPAddresses] [[-Timeout] ] [-Enu] [-ASProviderMSOLAP] [-ConfirmIPDependencyChange] [-Force] [-WhatIf] [-Confirm] []' + } + ) { + $result = (Get-Command -Name 'Complete-SqlDscFailoverCluster').ParameterSets | + Where-Object -FilterScript { + $_.Name -eq $mockParameterSetName + } | + Select-Object -Property @( + @{ + Name = 'ParameterSetName' + Expression = { $_.Name } + }, + @{ + Name = 'ParameterListAsString' + Expression = { $_.ToString() } + } + ) + + $result.ParameterSetName | Should -Be $MockParameterSetName + $result.ParameterListAsString | Should -Be $MockExpectedParameters + } + + Context 'When setup action is ''CompleteFailoverCluster''' { + BeforeAll { + Mock -CommandName Assert-SetupActionProperties + Mock -CommandName Assert-ElevatedUser + Mock -CommandName Test-Path -ParameterFilter { + $Path -match 'setup\.exe' + } -MockWith { + return $true + } + } + + Context 'When specifying only mandatory parameters' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + $mockDefaultParameters = @{ + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + SqlSysAdminAccounts = 'DOMAIN\User', 'COMPANY\SQL Administrators' + InstallSqlDataDir = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + FailoverClusterNetworkName = 'TESTCLU01A' # cspell: disable-line + FailoverClusterIPAddresses = @( + 'IPv4;172.16.0.0;ClusterNetwork1;172.31.255.255', + 'IPv6;2001:db8:23:1002:20f:1fff:feff:b3a3;ClusterNetwork2' # cspell: disable-line + 'IPv6;DHCP;ClusterNetwork3' + 'IPv4;DHCP;ClusterNetwork4' + ) + } + } + + Context 'When using parameter Confirm with value $false' { + It 'Should call the mock with the correct argument string' { + Complete-SqlDscFailoverCluster -Confirm:$false @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=CompleteFailoverCluster' + $ArgumentList | Should -MatchExactly '\/SQLSYSADMINACCOUNTS="DOMAIN\\User" "COMPANY\\SQL Administrators"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/INSTALLSQLDATADIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/FAILOVERCLUSTERNETWORKNAME="TESTCLU01A"' # cspell: disable-line + $ArgumentList | Should -MatchExactly ([System.Text.RegularExpressions.Regex]::Escape('/FAILOVERCLUSTERIPADDRESSES="IPv4;172.16.0.0;ClusterNetwork1;172.31.255.255" "IPv6;2001:db8:23:1002:20f:1fff:feff:b3a3;ClusterNetwork2" "IPv6;DHCP;ClusterNetwork3" "IPv4;DHCP;ClusterNetwork4"')) # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + + Context 'When using parameter Force' { + It 'Should call the mock with the correct argument string' { + Complete-SqlDscFailoverCluster -Force @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=CompleteFailoverCluster' + $ArgumentList | Should -MatchExactly '\/SQLSYSADMINACCOUNTS="DOMAIN\\User" "COMPANY\\SQL Administrators"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/INSTALLSQLDATADIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/FAILOVERCLUSTERNETWORKNAME="TESTCLU01A"' # cspell: disable-line + $ArgumentList | Should -MatchExactly ([System.Text.RegularExpressions.Regex]::Escape('/FAILOVERCLUSTERIPADDRESSES="IPv4;172.16.0.0;ClusterNetwork1;172.31.255.255" "IPv6;2001:db8:23:1002:20f:1fff:feff:b3a3;ClusterNetwork2" "IPv6;DHCP;ClusterNetwork3" "IPv4;DHCP;ClusterNetwork4"')) # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + + Context 'When using parameter WhatIf' { + It 'Should call the mock with the correct argument string' { + Complete-SqlDscFailoverCluster -WhatIf @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -Exactly -Times 0 -Scope It + } + } + } + + Context 'When specifying optional parameter ' -ForEach @( + @{ + MockParameterName = 'Enu' + MockParameterValue = $true + MockExpectedRegEx = '\/ENU\s*' + } + @{ + MockParameterName = 'ProductKey' + MockParameterValue = '22222-00000-00000-00000-00000' + MockExpectedRegEx = '\/PID="22222-00000-00000-00000-00000"' + } + @{ + MockParameterName = 'ASBackupDir' + MockParameterValue = 'C:\MSOLAP13.INST2016\Backup' + MockExpectedRegEx = '\/ASBACKUPDIR="C:\\MSOLAP13\.INST2016\\Backup"' # cspell: disable-line + } + @{ + MockParameterName = 'ASConfigDir' + MockParameterValue = 'C:\MSOLAP13.INST2016\Config' + MockExpectedRegEx = '\/ASCONFIGDIR="C:\\MSOLAP13\.INST2016\\Config"' # cspell: disable-line + } + @{ + MockParameterName = 'ASDataDir' + MockParameterValue = 'C:\MSOLAP13.INST2016\Data' + MockExpectedRegEx = '\/ASDATADIR="C:\\MSOLAP13\.INST2016\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'ASLogDir' + MockParameterValue = 'C:\MSOLAP13.INST2016\Log' + MockExpectedRegEx = '\/ASLOGDIR="C:\\MSOLAP13\.INST2016\\Log"' # cspell: disable-line + } + @{ + MockParameterName = 'ASTempDir' + MockParameterValue = 'C:\MSOLAP13.INST2016\Temp' + MockExpectedRegEx = '\/ASTEMPDIR="C:\\MSOLAP13\.INST2016\\Temp"' # cspell: disable-line + } + @{ + MockParameterName = 'ASCollation' + MockParameterValue = 'latin1_general_100' + MockExpectedRegEx = '\/ASCOLLATION="latin1_general_100"' # cspell: disable-line + } + @{ + MockParameterName = 'ASServerMode' + MockParameterValue = 'Multidimensional' + MockExpectedRegEx = '\/ASSERVERMODE=MULTIDIMENSIONAL' # cspell: disable-line + } + @{ + MockParameterName = 'ASSysAdminAccounts' + MockParameterValue = 'COMPANY\SQL Administrators', 'LocalUser' + MockExpectedRegEx = '\/ASSYSADMINACCOUNTS="COMPANY\\SQL Administrators" "LocalUser"' # cspell: disable-line + } + @{ + MockParameterName = 'ASProviderMSOLAP' + MockParameterValue = $true + MockExpectedRegEx = '\/ASPROVIDERMSOLAP=1' # cspell: disable-line + } + @{ + MockParameterName = 'InstallSqlDataDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + MockExpectedRegEx = '\/INSTALLSQLDATADIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlBackupDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Backup' + MockExpectedRegEx = '\/SQLBACKUPDIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Backup"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + MockExpectedRegEx = '\/SQLTEMPDBDIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbLogDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + MockExpectedRegEx = '\/SQLTEMPDBLOGDIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlUserDbDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + MockExpectedRegEx = '\/SQLUSERDBDIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlUserDbLogDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + MockExpectedRegEx = '\/SQLUSERDBLOGDIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'SecurityMode' + MockParameterValue = 'SQL' + MockExpectedRegEx = '\/SECURITYMODE="SQL"' # cspell: disable-line + } + @{ + MockParameterName = 'SAPwd' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/SAPWD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlCollation' + MockParameterValue = 'SQL_Latin1_General_CP1_CI_AS' + MockExpectedRegEx = '\/SQLCOLLATION="SQL_Latin1_General_CP1_CI_AS"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbFileCount' + MockParameterValue = 8 + MockExpectedRegEx = '\/SQLTEMPDBFILECOUNT=8' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbFileSize' + MockParameterValue = 100 + MockExpectedRegEx = '\/SQLTEMPDBFILESIZE=100' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbFileGrowth' + MockParameterValue = 10 + MockExpectedRegEx = '\/SQLTEMPDBFILEGROWTH=10' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbLogFileSize' + MockParameterValue = 100 + MockExpectedRegEx = '\/SQLTEMPDBLOGFILESIZE=100' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbLogFileGrowth' + MockParameterValue = 10 + MockExpectedRegEx = '\/SQLTEMPDBLOGFILEGROWTH=10' # cspell: disable-line + } + @{ + MockParameterName = 'RsInstallMode' + MockParameterValue = 'FilesOnlyMode' + MockExpectedRegEx = '\/RSINSTALLMODE="FilesOnlyMode"' # cspell: disable-line + } + @{ + MockParameterName = 'FailoverClusterGroup' + MockParameterValue = 'TESTCLU01A' # cspell: disable-line + MockExpectedRegEx = '\/FAILOVERCLUSTERGROUP="TESTCLU01A"' # cspell: disable-line + } + @{ + MockParameterName = 'FailoverClusterGroup' + MockParameterValue = 'TESTCLU01A' # cspell: disable-line + MockExpectedRegEx = '\/FAILOVERCLUSTERGROUP="TESTCLU01A"' # cspell: disable-line + } + @{ + MockParameterName = 'FailoverClusterDisks' + # This is the failover cluster resource name. + MockParameterValue = @( + 'SysData' + ) + MockExpectedRegEx = '\/FAILOVERCLUSTERDISKS="SysData"' # cspell: disable-line + } + @{ + MockParameterName = 'FailoverClusterDisks' + # This is the failover cluster resource names. + MockParameterValue = @( + 'Backup' + 'SysData' + 'TempDbData' + 'TempDbLogs' + 'UserData' + 'UserLogs' + ) + MockExpectedRegEx = '\/FAILOVERCLUSTERDISKS="Backup;SysData;TempDbData;TempDbLogs;UserData;UserLogs"' # cspell: disable-line + } + @{ + MockParameterName = 'ConfirmIPDependencyChange' + MockParameterValue = $true + MockExpectedRegEx = '\/CONFIRMIPDEPENDENCYCHANGE=1' # cspell: disable-line + } + ) { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + $mockDefaultParameters = @{ + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + SqlSysAdminAccounts = 'DOMAIN\User', 'COMPANY\SQL Administrators' + InstallSqlDataDir = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + FailoverClusterNetworkName = 'TESTCLU01A' # cspell: disable-line + FailoverClusterIPAddresses = @( + 'IPv4;172.16.0.0;ClusterNetwork1;172.31.255.255', + 'IPv6;2001:db8:23:1002:20f:1fff:feff:b3a3;ClusterNetwork2' # cspell: disable-line + 'IPv6;DHCP;ClusterNetwork3' + 'IPv4;DHCP;ClusterNetwork4' + ) + Force = $true + } + } + + BeforeEach { + $completeSqlDscFailoverClusterParameters = $mockDefaultParameters.Clone() + } + + It 'Should call the mock with the correct argument string' { + $completeSqlDscFailoverClusterParameters.$MockParameterName = $MockParameterValue + + Complete-SqlDscFailoverCluster @completeSqlDscFailoverClusterParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly $MockExpectedRegEx + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } +} diff --git a/tests/Unit/Public/Complete-SqlDscImage.Tests.ps1 b/tests/Unit/Public/Complete-SqlDscImage.Tests.ps1 new file mode 100644 index 000000000..52e908630 --- /dev/null +++ b/tests/Unit/Public/Complete-SqlDscImage.Tests.ps1 @@ -0,0 +1,380 @@ +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '', Justification = 'because ConvertTo-SecureString is used to simplify the tests.')] +param () + +BeforeDiscovery { + try + { + if (-not (Get-Module -Name 'DscResource.Test')) + { + # Assumes dependencies has been resolved, so if this module is not available, run 'noop' task. + if (-not (Get-Module -Name 'DscResource.Test' -ListAvailable)) + { + # Redirect all streams to $null, except the error stream (stream 2) + & "$PSScriptRoot/../../build.ps1" -Tasks 'noop' 2>&1 4>&1 5>&1 6>&1 > $null + } + + # If the dependencies has not been resolved, this will throw an error. + Import-Module -Name 'DscResource.Test' -Force -ErrorAction 'Stop' + } + } + catch [System.IO.FileNotFoundException] + { + throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -ResolveDependency -Tasks build" first.' + } +} + +BeforeAll { + $script:dscModuleName = 'SqlServerDsc' + + Import-Module -Name $script:dscModuleName + + $PSDefaultParameterValues['InModuleScope:ModuleName'] = $script:dscModuleName + $PSDefaultParameterValues['Mock:ModuleName'] = $script:dscModuleName + $PSDefaultParameterValues['Should:ModuleName'] = $script:dscModuleName +} + +AfterAll { + $PSDefaultParameterValues.Remove('InModuleScope:ModuleName') + $PSDefaultParameterValues.Remove('Mock:ModuleName') + $PSDefaultParameterValues.Remove('Should:ModuleName') + + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force +} + +Describe 'Complete-SqlDscImage' -Tag 'Public' { + It 'Should have the correct parameters in parameter set ' -ForEach @( + @{ + MockParameterSetName = '__AllParameterSets' + # cSpell: disable-next + MockExpectedParameters = '[-MediaPath] [[-InstanceName] ] [[-InstanceId] ] [[-PBEngSvcAccount] ] [[-PBEngSvcPassword] ] [[-PBEngSvcStartupType] ] [[-PBStartPortRange] ] [[-PBEndPortRange] ] [[-ProductKey] ] [[-AgtSvcAccount] ] [[-AgtSvcPassword] ] [[-AgtSvcStartupType] ] [[-BrowserSvcStartupType] ] [[-InstallSqlDataDir] ] [[-SqlBackupDir] ] [[-SecurityMode] ] [[-SAPwd] ] [[-SqlCollation] ] [[-SqlSvcAccount] ] [[-SqlSvcPassword] ] [[-SqlSvcStartupType] ] [[-SqlSysAdminAccounts] ] [[-SqlTempDbDir] ] [[-SqlTempDbLogDir] ] [[-SqlTempDbFileCount] ] [[-SqlTempDbFileSize] ] [[-SqlTempDbFileGrowth] ] [[-SqlTempDbLogFileSize] ] [[-SqlTempDbLogFileGrowth] ] [[-SqlUserDbDir] ] [[-SqlUserDbLogDir] ] [[-FileStreamLevel] ] [[-FileStreamShareName] ] [[-RsInstallMode] ] [[-RSSvcAccount] ] [[-RSSvcPassword] ] [[-RSSvcStartupType] ] [[-Timeout] ] -AcceptLicensingTerms [-Enu] [-PBScaleOut] [-EnableRanU] [-NpEnabled] [-TcpEnabled] [-Force] [-WhatIf] [-Confirm] []' + } + ) { + $result = (Get-Command -Name 'Complete-SqlDscImage').ParameterSets | + Where-Object -FilterScript { + $_.Name -eq $mockParameterSetName + } | + Select-Object -Property @( + @{ + Name = 'ParameterSetName' + Expression = { $_.Name } + }, + @{ + Name = 'ParameterListAsString' + Expression = { $_.ToString() } + } + ) + + $result.ParameterSetName | Should -Be $MockParameterSetName + $result.ParameterListAsString | Should -Be $MockExpectedParameters + } + + Context 'When setup action is ''CompleteImage''' { + BeforeAll { + Mock -CommandName Assert-SetupActionProperties + Mock -CommandName Assert-ElevatedUser + Mock -CommandName Test-Path -ParameterFilter { + $Path -match 'setup\.exe' + } -MockWith { + return $true + } + } + + Context 'When specifying only mandatory parameters' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + $mockDefaultParameters = @{ + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + } + } + + Context 'When using parameter Confirm with value $false' { + It 'Should call the mock with the correct argument string' { + Complete-SqlDscImage -Confirm:$false @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=CompleteImage' + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + + Context 'When using parameter Force' { + It 'Should call the mock with the correct argument string' { + Complete-SqlDscImage -Force @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=CompleteImage' + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + + Context 'When using parameter WhatIf' { + It 'Should call the mock with the correct argument string' { + Complete-SqlDscImage -WhatIf @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -Exactly -Times 0 -Scope It + } + } + } + + Context 'When specifying optional parameters PBStartPortRange and PBEndPortRange' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + $completeSqlDscImageParameters = @{ + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + Force = $true + PBStartPortRange = 16450 + PBEndPortRange = 16460 + } + } + + It 'Should call the mock with the correct argument string' { + Complete-SqlDscImage @completeSqlDscImageParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly 'PBPORTRANGE=16450-16460' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + + Context 'When specifying optional parameter ' -ForEach @( + @{ + MockParameterName = 'InstanceName' + MockParameterValue = 'INSTANCE' + MockExpectedRegEx = '\/INSTANCENAME="INSTANCE"*' # cspell: disable-line + } + @{ + MockParameterName = 'Enu' + MockParameterValue = $true + MockExpectedRegEx = '\/ENU\s*' + } + @{ + MockParameterName = 'InstanceId' + MockParameterValue = 'Instance' + MockExpectedRegEx = '\/INSTANCEID="Instance"' # cspell: disable-line + } + @{ + MockParameterName = 'PBEngSvcAccount' + MockParameterValue = 'NT Authority\NETWORK SERVICE' + MockExpectedRegEx = '\/PBENGSVCACCOUNT="NT Authority\\NETWORK SERVICE"' # cspell: disable-line + } + @{ + MockParameterName = 'PBEngSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/PBENGSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'PBEngSvcStartupType' + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/PBENGSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + @{ + MockParameterName = 'PBScaleOut' + MockParameterValue = $true + MockExpectedRegEx = '\/PBSCALEOUT=True' # cspell: disable-line + } + @{ + MockParameterName = 'ProductKey' + MockParameterValue = '22222-00000-00000-00000-00000' + MockExpectedRegEx = '\/PID="22222-00000-00000-00000-00000"' + } + @{ + MockParameterName = 'AgtSvcAccount' + MockParameterValue = 'NT Authority\NETWORK SERVICE' + MockExpectedRegEx = '\/AGTSVCACCOUNT="NT Authority\\NETWORK SERVICE"' # cspell: disable-line + } + @{ + MockParameterName = 'AgtSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/AGTSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'AgtSvcStartupType' + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/AGTSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + @{ + MockParameterName = 'BrowserSvcStartupType' + MockParameterValue = 'Manual' + MockExpectedRegEx = '\/BROWSERSVCSTARTUPTYPE="Manual"' # cspell: disable-line + } + @{ + MockParameterName = 'EnableRanU' + MockParameterValue = $true + MockExpectedRegEx = '\/ENABLERANU' # cspell: disable-line + } + @{ + MockParameterName = 'InstallSqlDataDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + MockExpectedRegEx = '\/INSTALLSQLDATADIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlBackupDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Backup' + MockExpectedRegEx = '\/SQLBACKUPDIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Backup"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + MockExpectedRegEx = '\/SQLTEMPDBDIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbLogDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + MockExpectedRegEx = '\/SQLTEMPDBLOGDIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlUserDbDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + MockExpectedRegEx = '\/SQLUSERDBDIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlUserDbLogDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + MockExpectedRegEx = '\/SQLUSERDBLOGDIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'SecurityMode' + MockParameterValue = 'SQL' + MockExpectedRegEx = '\/SECURITYMODE="SQL"' # cspell: disable-line + } + @{ + MockParameterName = 'SAPwd' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/SAPWD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlCollation' + MockParameterValue = 'SQL_Latin1_General_CP1_CI_AS' + MockExpectedRegEx = '\/SQLCOLLATION="SQL_Latin1_General_CP1_CI_AS"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlSvcAccount' + MockParameterValue = 'DOMAIN\ServiceAccount$' + MockExpectedRegEx = '\/SQLSVCACCOUNT="DOMAIN\\ServiceAccount\$"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/SQLSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlSvcStartupType' + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/SQLSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbFileCount' + MockParameterValue = 8 + MockExpectedRegEx = '\/SQLTEMPDBFILECOUNT=8' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbFileSize' + MockParameterValue = 100 + MockExpectedRegEx = '\/SQLTEMPDBFILESIZE=100' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbFileGrowth' + MockParameterValue = 10 + MockExpectedRegEx = '\/SQLTEMPDBFILEGROWTH=10' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbLogFileSize' + MockParameterValue = 100 + MockExpectedRegEx = '\/SQLTEMPDBLOGFILESIZE=100' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbLogFileGrowth' + MockParameterValue = 10 + MockExpectedRegEx = '\/SQLTEMPDBLOGFILEGROWTH=10' # cspell: disable-line + } + @{ + MockParameterName = 'FileStreamLevel' + MockParameterValue = 2 + MockExpectedRegEx = '\/FILESTREAMLEVEL=2' # cspell: disable-line + } + @{ + MockParameterName = 'FileStreamShareName' + MockParameterValue = 'ShareName' + MockExpectedRegEx = '\/FILESTREAMSHARENAME="ShareName"' # cspell: disable-line + } + @{ + MockParameterName = 'NpEnabled' + MockParameterValue = $true + MockExpectedRegEx = '\/NPENABLED=1' # cspell: disable-line + } + @{ + MockParameterName = 'TcpEnabled' + MockParameterValue = $true + MockExpectedRegEx = '\/TCPENABLED=1' # cspell: disable-line + } + @{ + MockParameterName = 'RsInstallMode' + MockParameterValue = 'FilesOnlyMode' + MockExpectedRegEx = '\/RSINSTALLMODE="FilesOnlyMode"' # cspell: disable-line + } + @{ + MockParameterName = 'RSSvcAccount' + MockParameterValue = 'DOMAIN\ServiceAccount$' + MockExpectedRegEx = '\/RSSVCACCOUNT="DOMAIN\\ServiceAccount\$"' # cspell: disable-line + } + @{ + MockParameterName = 'RSSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/RSSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'RSSvcStartupType' + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/RSSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + ) { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + $mockDefaultParameters = @{ + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + Force = $true + } + } + + BeforeEach { + $completeSqlDscImageParameters = $mockDefaultParameters.Clone() + } + + It 'Should call the mock with the correct argument string' { + $completeSqlDscImageParameters.$MockParameterName = $MockParameterValue + + Complete-SqlDscImage @completeSqlDscImageParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly $MockExpectedRegEx + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } +} diff --git a/tests/Unit/Public/Initialize-SqlDscRebuildDatabase.Tests.ps1 b/tests/Unit/Public/Initialize-SqlDscRebuildDatabase.Tests.ps1 new file mode 100644 index 000000000..217fd6952 --- /dev/null +++ b/tests/Unit/Public/Initialize-SqlDscRebuildDatabase.Tests.ps1 @@ -0,0 +1,214 @@ +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '', Justification = 'because ConvertTo-SecureString is used to simplify the tests.')] +param () + +BeforeDiscovery { + try + { + if (-not (Get-Module -Name 'DscResource.Test')) + { + # Assumes dependencies has been resolved, so if this module is not available, run 'noop' task. + if (-not (Get-Module -Name 'DscResource.Test' -ListAvailable)) + { + # Redirect all streams to $null, except the error stream (stream 2) + & "$PSScriptRoot/../../build.ps1" -Tasks 'noop' 2>&1 4>&1 5>&1 6>&1 > $null + } + + # If the dependencies has not been resolved, this will throw an error. + Import-Module -Name 'DscResource.Test' -Force -ErrorAction 'Stop' + } + } + catch [System.IO.FileNotFoundException] + { + throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -ResolveDependency -Tasks build" first.' + } +} + +BeforeAll { + $script:dscModuleName = 'SqlServerDsc' + + Import-Module -Name $script:dscModuleName + + $PSDefaultParameterValues['InModuleScope:ModuleName'] = $script:dscModuleName + $PSDefaultParameterValues['Mock:ModuleName'] = $script:dscModuleName + $PSDefaultParameterValues['Should:ModuleName'] = $script:dscModuleName +} + +AfterAll { + $PSDefaultParameterValues.Remove('InModuleScope:ModuleName') + $PSDefaultParameterValues.Remove('Mock:ModuleName') + $PSDefaultParameterValues.Remove('Should:ModuleName') + + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force +} + +Describe 'Initialize-SqlDscRebuildDatabase' -Tag 'Public' { + It 'Should have the correct parameters in parameter set ' -ForEach @( + @{ + MockParameterSetName = '__AllParameterSets' + # cSpell: disable-next + MockExpectedParameters = '[-MediaPath] [-InstanceName] [[-SAPwd] ] [[-SqlCollation] ] [-SqlSysAdminAccounts] [[-SqlTempDbDir] ] [[-SqlTempDbLogDir] ] [[-SqlTempDbFileCount] ] [[-SqlTempDbFileSize] ] [[-SqlTempDbFileGrowth] ] [[-SqlTempDbLogFileSize] ] [[-SqlTempDbLogFileGrowth] ] [[-Timeout] ] [-Force] [-WhatIf] [-Confirm] []' + } + ) { + $result = (Get-Command -Name 'Initialize-SqlDscRebuildDatabase').ParameterSets | + Where-Object -FilterScript { + $_.Name -eq $mockParameterSetName + } | + Select-Object -Property @( + @{ + Name = 'ParameterSetName' + Expression = { $_.Name } + }, + @{ + Name = 'ParameterListAsString' + Expression = { $_.ToString() } + } + ) + + $result.ParameterSetName | Should -Be $MockParameterSetName + $result.ParameterListAsString | Should -Be $MockExpectedParameters + } + + Context 'When setup action is ''RebuildDatabase''' { + BeforeAll { + Mock -CommandName Assert-SetupActionProperties + Mock -CommandName Assert-ElevatedUser + Mock -CommandName Test-Path -ParameterFilter { + $Path -match 'setup\.exe' + } -MockWith { + return $true + } + } + + Context 'When specifying only mandatory parameters' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + $mockDefaultParameters = @{ + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + SqlSysAdminAccounts = 'DOMAIN\User', 'COMPANY\SQL Administrators' + } + } + + Context 'When using parameter Confirm with value $false' { + It 'Should call the mock with the correct argument string' { + Initialize-SqlDscRebuildDatabase -Confirm:$false @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=RebuildDatabase' + $ArgumentList | Should -MatchExactly '\/SQLSYSADMINACCOUNTS="DOMAIN\\User" "COMPANY\\SQL Administrators"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + + Context 'When using parameter Force' { + It 'Should call the mock with the correct argument string' { + Initialize-SqlDscRebuildDatabase -Force @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=RebuildDatabase' + $ArgumentList | Should -MatchExactly '\/SQLSYSADMINACCOUNTS="DOMAIN\\User" "COMPANY\\SQL Administrators"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + + Context 'When using parameter WhatIf' { + It 'Should call the mock with the correct argument string' { + Initialize-SqlDscRebuildDatabase -WhatIf @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -Exactly -Times 0 -Scope It + } + } + } + + Context 'When specifying optional parameter ' -ForEach @( + @{ + MockParameterName = 'SqlTempDbDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + MockExpectedRegEx = '\/SQLTEMPDBDIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbLogDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + MockExpectedRegEx = '\/SQLTEMPDBLOGDIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'SAPwd' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/SAPWD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlCollation' + MockParameterValue = 'SQL_Latin1_General_CP1_CI_AS' + MockExpectedRegEx = '\/SQLCOLLATION="SQL_Latin1_General_CP1_CI_AS"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbFileCount' + MockParameterValue = 8 + MockExpectedRegEx = '\/SQLTEMPDBFILECOUNT=8' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbFileSize' + MockParameterValue = 100 + MockExpectedRegEx = '\/SQLTEMPDBFILESIZE=100' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbFileGrowth' + MockParameterValue = 10 + MockExpectedRegEx = '\/SQLTEMPDBFILEGROWTH=10' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbLogFileSize' + MockParameterValue = 100 + MockExpectedRegEx = '\/SQLTEMPDBLOGFILESIZE=100' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbLogFileGrowth' + MockParameterValue = 10 + MockExpectedRegEx = '\/SQLTEMPDBLOGFILEGROWTH=10' # cspell: disable-line + } + ) { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + $mockDefaultParameters = @{ + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + SqlSysAdminAccounts = 'DOMAIN\User' + Force = $true + } + } + + BeforeEach { + $installSqlDscServerParameters = $mockDefaultParameters.Clone() + } + + It 'Should call the mock with the correct argument string' { + $installSqlDscServerParameters.$MockParameterName = $MockParameterValue + + Initialize-SqlDscRebuildDatabase @installSqlDscServerParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly $MockExpectedRegEx + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } +} diff --git a/tests/Unit/Public/Install-SqlDscServer.Tests.ps1 b/tests/Unit/Public/Install-SqlDscServer.Tests.ps1 new file mode 100644 index 000000000..aea0bf1fd --- /dev/null +++ b/tests/Unit/Public/Install-SqlDscServer.Tests.ps1 @@ -0,0 +1,2576 @@ +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '', Justification = 'because ConvertTo-SecureString is used to simplify the tests.')] +param () + +BeforeDiscovery { + try + { + if (-not (Get-Module -Name 'DscResource.Test')) + { + # Assumes dependencies has been resolved, so if this module is not available, run 'noop' task. + if (-not (Get-Module -Name 'DscResource.Test' -ListAvailable)) + { + # Redirect all streams to $null, except the error stream (stream 2) + & "$PSScriptRoot/../../build.ps1" -Tasks 'noop' 2>&1 4>&1 5>&1 6>&1 > $null + } + + # If the dependencies has not been resolved, this will throw an error. + Import-Module -Name 'DscResource.Test' -Force -ErrorAction 'Stop' + } + } + catch [System.IO.FileNotFoundException] + { + throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -ResolveDependency -Tasks build" first.' + } +} + +BeforeAll { + $script:dscModuleName = 'SqlServerDsc' + + Import-Module -Name $script:dscModuleName + + $PSDefaultParameterValues['InModuleScope:ModuleName'] = $script:dscModuleName + $PSDefaultParameterValues['Mock:ModuleName'] = $script:dscModuleName + $PSDefaultParameterValues['Should:ModuleName'] = $script:dscModuleName +} + +AfterAll { + $PSDefaultParameterValues.Remove('InModuleScope:ModuleName') + $PSDefaultParameterValues.Remove('Mock:ModuleName') + $PSDefaultParameterValues.Remove('Should:ModuleName') + + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force +} + +Describe 'Install-SqlDscServer' -Tag 'Public' { + It 'Should have the correct parameters in parameter set ' -ForEach @( + @{ + MockParameterSetName = 'Install' + # cSpell: disable-next + MockExpectedParameters = '-Install -AcceptLicensingTerms -MediaPath -InstanceName -Features -SqlSysAdminAccounts [-SuppressPrivacyStatementNotice] [-IAcknowledgeEntCalLimits] [-Enu] [-UpdateEnabled] [-UpdateSource ] [-InstallSharedDir ] [-InstallSharedWowDir ] [-InstanceDir ] [-InstanceId ] [-PBEngSvcAccount ] [-PBEngSvcPassword ] [-PBEngSvcStartupType ] [-PBDMSSvcAccount ] [-PBDMSSvcPassword ] [-PBDMSSvcStartupType ] [-PBStartPortRange ] [-PBEndPortRange ] [-PBScaleOut] [-ProductKey ] [-AgtSvcAccount ] [-AgtSvcPassword ] [-AgtSvcStartupType ] [-ASBackupDir ] [-ASCollation ] [-ASConfigDir ] [-ASDataDir ] [-ASLogDir ] [-ASTempDir ] [-ASServerMode ] [-ASSvcAccount ] [-ASSvcPassword ] [-ASSvcStartupType ] [-ASSysAdminAccounts ] [-ASProviderMSOLAP] [-BrowserSvcStartupType ] [-EnableRanU] [-InstallSqlDataDir ] [-SqlBackupDir ] [-SecurityMode ] [-SAPwd ] [-SqlCollation ] [-SqlSvcAccount ] [-SqlSvcPassword ] [-SqlSvcStartupType ] [-SqlTempDbDir ] [-SqlTempDbLogDir ] [-SqlTempDbFileCount ] [-SqlTempDbFileSize ] [-SqlTempDbFileGrowth ] [-SqlTempDbLogFileSize ] [-SqlTempDbLogFileGrowth ] [-SqlUserDbDir ] [-SqlSvcInstantFileInit] [-SqlUserDbLogDir ] [-SqlMaxDop ] [-UseSqlRecommendedMemoryLimits] [-SqlMinMemory ] [-SqlMaxMemory ] [-FileStreamLevel ] [-FileStreamShareName ] [-ISSvcAccount ] [-ISSvcPassword ] [-ISSvcStartupType ] [-NpEnabled] [-TcpEnabled] [-RsInstallMode ] [-RSSvcAccount ] [-RSSvcPassword ] [-RSSvcStartupType ] [-MPYCacheDirectory ] [-MRCacheDirectory ] [-SqlInstJava] [-SqlJavaDir ] [-AzureSubscriptionId ] [-AzureResourceGroup ] [-AzureRegion ] [-AzureTenantId ] [-AzureServicePrincipal ] [-AzureServicePrincipalSecret ] [-AzureArcProxy ] [-SkipRules ] [-Timeout ] [-Force] [-WhatIf] [-Confirm] []' + } + @{ + MockParameterSetName = 'InstallRole' + # cSpell: disable-next + MockExpectedParameters = '-Install -AcceptLicensingTerms -MediaPath -Role [-SuppressPrivacyStatementNotice] [-IAcknowledgeEntCalLimits] [-InstanceName ] [-Enu] [-UpdateEnabled] [-UpdateSource ] [-Features ] [-InstallSharedDir ] [-InstallSharedWowDir ] [-InstanceDir ] [-InstanceId ] [-PBEngSvcAccount ] [-PBEngSvcPassword ] [-PBEngSvcStartupType ] [-PBDMSSvcAccount ] [-PBDMSSvcPassword ] [-PBDMSSvcStartupType ] [-PBStartPortRange ] [-PBEndPortRange ] [-PBScaleOut] [-ProductKey ] [-AgtSvcAccount ] [-AgtSvcPassword ] [-AgtSvcStartupType ] [-ASBackupDir ] [-ASCollation ] [-ASConfigDir ] [-ASDataDir ] [-ASLogDir ] [-ASTempDir ] [-ASServerMode ] [-ASSvcAccount ] [-ASSvcPassword ] [-ASSvcStartupType ] [-ASSysAdminAccounts ] [-ASProviderMSOLAP] [-FarmAccount ] [-FarmPassword ] [-Passphrase ] [-FarmAdminiPort ] [-BrowserSvcStartupType ] [-EnableRanU] [-InstallSqlDataDir ] [-SqlBackupDir ] [-SecurityMode ] [-SAPwd ] [-SqlCollation ] [-AddCurrentUserAsSqlAdmin] [-SqlSvcAccount ] [-SqlSvcPassword ] [-SqlSvcStartupType ] [-SqlSysAdminAccounts ] [-SqlTempDbDir ] [-SqlTempDbLogDir ] [-SqlTempDbFileCount ] [-SqlTempDbFileSize ] [-SqlTempDbFileGrowth ] [-SqlTempDbLogFileSize ] [-SqlTempDbLogFileGrowth ] [-SqlUserDbDir ] [-SqlSvcInstantFileInit] [-SqlUserDbLogDir ] [-SqlMaxDop ] [-UseSqlRecommendedMemoryLimits] [-SqlMinMemory ] [-SqlMaxMemory ] [-FileStreamLevel ] [-FileStreamShareName ] [-ISSvcAccount ] [-ISSvcPassword ] [-ISSvcStartupType ] [-NpEnabled] [-TcpEnabled] [-RsInstallMode ] [-RSSvcAccount ] [-RSSvcPassword ] [-RSSvcStartupType ] [-MPYCacheDirectory ] [-MRCacheDirectory ] [-SqlInstJava] [-SqlJavaDir ] [-AzureSubscriptionId ] [-AzureResourceGroup ] [-AzureRegion ] [-AzureTenantId ] [-AzureServicePrincipal ] [-AzureServicePrincipalSecret ] [-AzureArcProxy ] [-SkipRules ] [-Timeout ] [-Force] [-WhatIf] [-Confirm] []' + } + @{ + MockParameterSetName = 'InstallAzureArcAgent' + # cSpell: disable-next + MockExpectedParameters = '-Install -AcceptLicensingTerms -MediaPath -AzureSubscriptionId -AzureResourceGroup -AzureRegion -AzureTenantId -AzureServicePrincipal -AzureServicePrincipalSecret [-AzureArcProxy ] [-Timeout ] [-Force] [-WhatIf] [-Confirm] []' + } + @{ + MockParameterSetName = 'UsingConfigurationFile' + # cSpell: disable-next + MockExpectedParameters = '-ConfigurationFile -MediaPath [-AgtSvcPassword ] [-ASSvcPassword ] [-SqlSvcPassword ] [-ISSvcPassword ] [-RSSvcPassword ] [-Timeout ] [-Force] [-WhatIf] [-Confirm] []' + } + @{ + MockParameterSetName = 'PrepareImage' + # cSpell: disable-next + MockExpectedParameters = '-PrepareImage -AcceptLicensingTerms -MediaPath -InstanceName -Features -InstanceId [-IAcknowledgeEntCalLimits] [-Enu] [-UpdateEnabled] [-UpdateSource ] [-InstallSharedDir ] [-InstanceDir ] [-PBEngSvcAccount ] [-PBEngSvcPassword ] [-PBEngSvcStartupType ] [-PBStartPortRange ] [-PBEndPortRange ] [-PBScaleOut] [-Timeout ] [-Force] [-WhatIf] [-Confirm] []' + } + @{ + MockParameterSetName = 'Upgrade' + # cSpell: disable-next + MockExpectedParameters = '-Upgrade -AcceptLicensingTerms -MediaPath -InstanceName [-Enu] [-UpdateEnabled] [-UpdateSource ] [-InstanceDir ] [-InstanceId ] [-ProductKey ] [-BrowserSvcStartupType ] [-FTUpgradeOption ] [-ISSvcAccount ] [-ISSvcPassword ] [-ISSvcStartupType ] [-AllowUpgradeForSSRSSharePointMode] [-FailoverClusterRollOwnership ] [-Timeout ] [-Force] [-WhatIf] [-Confirm] []' + } + @{ + MockParameterSetName = 'EditionUpgrade' + # cSpell: disable-next + MockExpectedParameters = '-EditionUpgrade -AcceptLicensingTerms -MediaPath -InstanceName -ProductKey [-SkipRules ] [-Timeout ] [-Force] [-WhatIf] [-Confirm] []' + } + @{ + MockParameterSetName = 'InstallFailoverCluster' + # cSpell: disable-next + MockExpectedParameters = '-InstallFailoverCluster -AcceptLicensingTerms -MediaPath -InstanceName -Features -InstallSqlDataDir -SqlSysAdminAccounts -FailoverClusterNetworkName -FailoverClusterIPAddresses [-IAcknowledgeEntCalLimits] [-Enu] [-UpdateEnabled] [-UpdateSource ] [-InstallSharedDir ] [-InstallSharedWowDir ] [-InstanceDir ] [-InstanceId ] [-PBEngSvcAccount ] [-PBEngSvcPassword ] [-PBEngSvcStartupType ] [-PBStartPortRange ] [-PBEndPortRange ] [-PBScaleOut] [-ProductKey ] [-AgtSvcAccount ] [-AgtSvcPassword ] [-ASBackupDir ] [-ASCollation ] [-ASConfigDir ] [-ASDataDir ] [-ASLogDir ] [-ASTempDir ] [-ASServerMode ] [-ASSvcAccount ] [-ASSvcPassword ] [-ASSvcStartupType ] [-ASSysAdminAccounts ] [-ASProviderMSOLAP] [-SqlBackupDir ] [-SecurityMode ] [-SAPwd ] [-SqlCollation ] [-SqlSvcAccount ] [-SqlSvcPassword ] [-SqlSvcStartupType ] [-SqlTempDbDir ] [-SqlTempDbLogDir ] [-SqlTempDbFileCount ] [-SqlTempDbFileSize ] [-SqlTempDbFileGrowth ] [-SqlTempDbLogFileSize ] [-SqlTempDbLogFileGrowth ] [-SqlUserDbDir ] [-SqlUserDbLogDir ] [-FileStreamLevel ] [-FileStreamShareName ] [-ISSvcAccount ] [-ISSvcPassword ] [-ISSvcStartupType ] [-RsInstallMode ] [-RSSvcAccount ] [-RSSvcPassword ] [-RSSvcStartupType ] [-FailoverClusterGroup ] [-FailoverClusterDisks ] [-SkipRules ] [-Timeout ] [-Force] [-WhatIf] [-Confirm] []' + } + @{ + MockParameterSetName = 'PrepareFailoverCluster' + # cSpell: disable-next + MockExpectedParameters = '-PrepareFailoverCluster -AcceptLicensingTerms -MediaPath -InstanceName -Features [-IAcknowledgeEntCalLimits] [-Enu] [-UpdateEnabled] [-UpdateSource ] [-InstallSharedDir ] [-InstallSharedWowDir ] [-InstanceDir ] [-InstanceId ] [-PBEngSvcAccount ] [-PBEngSvcPassword ] [-PBEngSvcStartupType ] [-PBStartPortRange ] [-PBEndPortRange ] [-PBScaleOut] [-ProductKey ] [-AgtSvcAccount ] [-AgtSvcPassword ] [-ASSvcAccount ] [-ASSvcPassword ] [-SqlSvcAccount ] [-SqlSvcPassword ] [-FileStreamLevel ] [-FileStreamShareName ] [-ISSvcAccount ] [-ISSvcPassword ] [-ISSvcStartupType ] [-RsInstallMode ] [-RSSvcAccount ] [-RSSvcPassword ] [-RSSvcStartupType ] [-Timeout ] [-Force] [-WhatIf] [-Confirm] []' + } + ) { + $result = (Get-Command -Name 'Install-SqlDscServer').ParameterSets | + Where-Object -FilterScript { + $_.Name -eq $mockParameterSetName + } | + Select-Object -Property @( + @{ + Name = 'ParameterSetName' + Expression = { $_.Name } + }, + @{ + Name = 'ParameterListAsString' + Expression = { $_.ToString() } + } + ) + + $result.ParameterSetName | Should -Be $MockParameterSetName + $result.ParameterListAsString | Should -Be $MockExpectedParameters + } + + Context 'When setup action is ''Install''' { + BeforeAll { + Mock -CommandName Assert-SetupActionProperties + Mock -CommandName Assert-ElevatedUser + Mock -CommandName Test-Path -ParameterFilter { + $Path -match 'setup\.exe' + } -MockWith { + return $true + } + } + + Context 'When specifying only mandatory parameters' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + $mockDefaultParameters = @{ + Install = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + # Intentionally using both upper- and lower-case. + Features = 'SqlEngine', 'Arc' + SqlSysAdminAccounts = 'DOMAIN\User', 'COMPANY\SQL Administrators' + } + } + + Context 'When using parameter Confirm with value $false' { + It 'Should call the mock with the correct argument string' { + Install-SqlDscServer -Confirm:$false @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=Install' + $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/SQLSYSADMINACCOUNTS="DOMAIN\\User" "COMPANY\\SQL Administrators"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/FEATURES=SQLENGINE,ARC' + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/ONBOARDSQLTOARC' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + + Context 'When using parameter Force' { + It 'Should call the mock with the correct argument string' { + Install-SqlDscServer -Force @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=Install' + $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/SQLSYSADMINACCOUNTS="DOMAIN\\User" "COMPANY\\SQL Administrators"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/FEATURES=SQLENGINE,ARC' + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/ONBOARDSQLTOARC' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + + Context 'When using parameter WhatIf' { + It 'Should call the mock with the correct argument string' { + Install-SqlDscServer -WhatIf @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -Exactly -Times 0 -Scope It + } + } + } + + Context 'When specifying optional parameters PBStartPortRange and PBEndPortRange' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + $installSqlDscServerParameters = @{ + Install = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + # Intentionally using both upper- and lower-case in the value. + Features = 'SqlEngine' + SqlSysAdminAccounts = 'DOMAIN\User' + Force = $true + PBStartPortRange = 16450 + PBEndPortRange = 16460 + } + } + + It 'Should call the mock with the correct argument string' { + Install-SqlDscServer @installSqlDscServerParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly 'PBPORTRANGE=16450-16460' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + + Context 'When specifying optional parameter ' -ForEach @( + @{ + MockParameterName = 'SuppressPrivacyStatementNotice' + MockParameterValue = $true + MockExpectedRegEx = '\/SUPPRESSPRIVACYSTATEMENTNOTICE\s*' # cspell: disable-line + } + @{ + MockParameterName = 'Enu' + MockParameterValue = $true + MockExpectedRegEx = '\/ENU\s*' + } + @{ + MockParameterName = 'UpdateEnabled' + MockParameterValue = $true + MockExpectedRegEx = '\/UPDATEENABLED=True' # cspell: disable-line + } + @{ + MockParameterName = 'UpdateSource' + MockParameterValue = '\SqlMedia\Updates' + MockExpectedRegEx = '\/UPDATESOURCE="\\SqlMedia\\Updates"' # cspell: disable-line + } + @{ + MockParameterName = 'InstallSharedDir' + # This value intentionally ends with a backslash to test so that it is removed. + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\' + MockExpectedRegEx = '\/INSTALLSHAREDDIR="C:\\Program Files\\Microsoft SQL Server"' # cspell: disable-line + } + @{ + MockParameterName = 'InstallSharedWOWDir' + MockParameterValue = 'C:\Program Files (x86)\Microsoft SQL Server' + MockExpectedRegEx = '\/INSTALLSHAREDWOWDIR="C:\\Program Files \(x86\)\\Microsoft SQL Server"' # cspell: disable-line + } + @{ + MockParameterName = 'InstanceDir' + <# + This value intentionally has 'D:\' to validate that the backslash + is not removed and that the argument is passed without double-quotes. + #> + MockParameterValue = 'D:\' + MockExpectedRegEx = '\/INSTANCEDIR=D:\\' # cspell: disable-line + } + @{ + MockParameterName = 'InstanceId' + MockParameterValue = 'Instance' + MockExpectedRegEx = '\/INSTANCEID="Instance"' # cspell: disable-line + } + @{ + MockParameterName = 'PBEngSvcAccount' + MockParameterValue = 'NT Authority\NETWORK SERVICE' + MockExpectedRegEx = '\/PBENGSVCACCOUNT="NT Authority\\NETWORK SERVICE"' # cspell: disable-line + } + @{ + MockParameterName = 'PBEngSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/PBENGSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'PBEngSvcStartupType' + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/PBENGSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + @{ + MockParameterName = 'PBDMSSvcAccount' # cspell: disable-line + MockParameterValue = 'NT Authority\NETWORK SERVICE' + MockExpectedRegEx = '\/PBDMSSVCACCOUNT="NT Authority\\NETWORK SERVICE"' # cspell: disable-line + } + @{ + MockParameterName = 'PBDMSSvcPassword' # cspell: disable-line + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/PBDMSSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'PBDMSSvcStartupType' # cspell: disable-line + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/PBDMSSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + @{ + MockParameterName = 'PBScaleOut' + MockParameterValue = $true + MockExpectedRegEx = '\/PBSCALEOUT=True' # cspell: disable-line + } + @{ + MockParameterName = 'ProductKey' + MockParameterValue = '22222-00000-00000-00000-00000' + MockExpectedRegEx = '\/PID="22222-00000-00000-00000-00000"' + } + @{ + MockParameterName = 'AgtSvcAccount' + MockParameterValue = 'NT Authority\NETWORK SERVICE' + MockExpectedRegEx = '\/AGTSVCACCOUNT="NT Authority\\NETWORK SERVICE"' # cspell: disable-line + } + @{ + MockParameterName = 'AgtSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/AGTSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'AgtSvcStartupType' + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/AGTSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + @{ + MockParameterName = 'ASBackupDir' + MockParameterValue = 'C:\MSOLAP13.INST2016\Backup' + MockExpectedRegEx = '\/ASBACKUPDIR="C:\\MSOLAP13\.INST2016\\Backup"' # cspell: disable-line + } + @{ + MockParameterName = 'ASConfigDir' + MockParameterValue = 'C:\MSOLAP13.INST2016\Config' + MockExpectedRegEx = '\/ASCONFIGDIR="C:\\MSOLAP13\.INST2016\\Config"' # cspell: disable-line + } + @{ + MockParameterName = 'ASDataDir' + MockParameterValue = 'C:\MSOLAP13.INST2016\Data' + MockExpectedRegEx = '\/ASDATADIR="C:\\MSOLAP13\.INST2016\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'ASLogDir' + MockParameterValue = 'C:\MSOLAP13.INST2016\Log' + MockExpectedRegEx = '\/ASLOGDIR="C:\\MSOLAP13\.INST2016\\Log"' # cspell: disable-line + } + @{ + MockParameterName = 'ASTempDir' + MockParameterValue = 'C:\MSOLAP13.INST2016\Temp' + MockExpectedRegEx = '\/ASTEMPDIR="C:\\MSOLAP13\.INST2016\\Temp"' # cspell: disable-line + } + @{ + MockParameterName = 'ASCollation' + MockParameterValue = 'latin1_general_100' + MockExpectedRegEx = '\/ASCOLLATION="latin1_general_100"' # cspell: disable-line + } + @{ + MockParameterName = 'ASServerMode' + MockParameterValue = 'Multidimensional' + MockExpectedRegEx = '\/ASSERVERMODE=MULTIDIMENSIONAL' # cspell: disable-line + } + @{ + MockParameterName = 'AsSvcAccount' + MockParameterValue = 'DOMAIN\ServiceAccount$' + MockExpectedRegEx = '\/ASSVCACCOUNT="DOMAIN\\ServiceAccount\$"' # cspell: disable-line + } + @{ + MockParameterName = 'AsSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/ASSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'AsSvcStartupType' + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/ASSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + @{ + MockParameterName = 'ASSysAdminAccounts' + MockParameterValue = 'COMPANY\SQL Administrators', 'LocalUser' + MockExpectedRegEx = '\/ASSYSADMINACCOUNTS="COMPANY\\SQL Administrators" "LocalUser"' # cspell: disable-line + } + @{ + MockParameterName = 'ASProviderMSOLAP' + MockParameterValue = $true + MockExpectedRegEx = '\/ASPROVIDERMSOLAP=1' # cspell: disable-line + } + @{ + MockParameterName = 'BrowserSvcStartupType' + MockParameterValue = 'Manual' + MockExpectedRegEx = '\/BROWSERSVCSTARTUPTYPE="Manual"' # cspell: disable-line + } + @{ + MockParameterName = 'EnableRanU' + MockParameterValue = $true + MockExpectedRegEx = '\/ENABLERANU' # cspell: disable-line + } + @{ + MockParameterName = 'InstallSqlDataDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + MockExpectedRegEx = '\/INSTALLSQLDATADIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlBackupDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Backup' + MockExpectedRegEx = '\/SQLBACKUPDIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Backup"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + MockExpectedRegEx = '\/SQLTEMPDBDIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbLogDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + MockExpectedRegEx = '\/SQLTEMPDBLOGDIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlUserDbDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + MockExpectedRegEx = '\/SQLUSERDBDIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlUserDbLogDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + MockExpectedRegEx = '\/SQLUSERDBLOGDIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'SecurityMode' + MockParameterValue = 'SQL' + MockExpectedRegEx = '\/SECURITYMODE="SQL"' # cspell: disable-line + } + @{ + MockParameterName = 'SAPwd' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/SAPWD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlCollation' + MockParameterValue = 'SQL_Latin1_General_CP1_CI_AS' + MockExpectedRegEx = '\/SQLCOLLATION="SQL_Latin1_General_CP1_CI_AS"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlSvcAccount' + MockParameterValue = 'DOMAIN\ServiceAccount$' + MockExpectedRegEx = '\/SQLSVCACCOUNT="DOMAIN\\ServiceAccount\$"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/SQLSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlSvcStartupType' + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/SQLSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbFileCount' + MockParameterValue = 8 + MockExpectedRegEx = '\/SQLTEMPDBFILECOUNT=8' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbFileSize' + MockParameterValue = 100 + MockExpectedRegEx = '\/SQLTEMPDBFILESIZE=100' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbFileGrowth' + MockParameterValue = 10 + MockExpectedRegEx = '\/SQLTEMPDBFILEGROWTH=10' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbLogFileSize' + MockParameterValue = 100 + MockExpectedRegEx = '\/SQLTEMPDBLOGFILESIZE=100' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbLogFileGrowth' + MockParameterValue = 10 + MockExpectedRegEx = '\/SQLTEMPDBLOGFILEGROWTH=10' # cspell: disable-line + } + @{ + MockParameterName = 'SqlSvcInstantFileInit' + MockParameterValue = $true + MockExpectedRegEx = '\/SQLSVCINSTANTFILEINIT=True' # cspell: disable-line + } + @{ + MockParameterName = 'SqlMaxDop' + MockParameterValue = 8 + MockExpectedRegEx = '\/SQLMAXDOP=8' # cspell: disable-line + } + @{ + MockParameterName = 'UseSqlRecommendedMemoryLimits' + MockParameterValue = $true + MockExpectedRegEx = '\/USESQLRECOMMENDEDMEMORYLIMITS' # cspell: disable-line + } + @{ + MockParameterName = 'SqlMinMemory' + MockParameterValue = 1000 + MockExpectedRegEx = '\/SQLMINMEMORY=1000' # cspell: disable-line + } + @{ + MockParameterName = 'SqlMaxMemory' + MockParameterValue = 2147483647 + MockExpectedRegEx = '\/SQLMAXMEMORY=2147483647' # cspell: disable-line + } + @{ + MockParameterName = 'FileStreamLevel' + MockParameterValue = 2 + MockExpectedRegEx = '\/FILESTREAMLEVEL=2' # cspell: disable-line + } + @{ + MockParameterName = 'FileStreamShareName' + MockParameterValue = 'ShareName' + MockExpectedRegEx = '\/FILESTREAMSHARENAME="ShareName"' # cspell: disable-line + } + @{ + MockParameterName = 'ISSvcAccount' + MockParameterValue = 'DOMAIN\ServiceAccount$' + MockExpectedRegEx = '\/ISSVCACCOUNT="DOMAIN\\ServiceAccount\$"' # cspell: disable-line + } + @{ + MockParameterName = 'ISSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/ISSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'ISSvcStartupType' + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/ISSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + @{ + MockParameterName = 'NpEnabled' + MockParameterValue = $true + MockExpectedRegEx = '\/NPENABLED=1' # cspell: disable-line + } + @{ + MockParameterName = 'TcpEnabled' + MockParameterValue = $true + MockExpectedRegEx = '\/TCPENABLED=1' # cspell: disable-line + } + @{ + MockParameterName = 'RsInstallMode' + MockParameterValue = 'FilesOnlyMode' + MockExpectedRegEx = '\/RSINSTALLMODE="FilesOnlyMode"' # cspell: disable-line + } + @{ + MockParameterName = 'RSSvcAccount' + MockParameterValue = 'DOMAIN\ServiceAccount$' + MockExpectedRegEx = '\/RSSVCACCOUNT="DOMAIN\\ServiceAccount\$"' # cspell: disable-line + } + @{ + MockParameterName = 'RSSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/RSSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'RSSvcStartupType' + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/RSSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + @{ + # Argument is reserved for future use, see SQL Server documentation. + MockParameterName = 'MPYCacheDirectory' + MockParameterValue = 'C:\Temp' + MockExpectedRegEx = '\/MPYCACHEDIRECTORY="C:\\Temp"' # cspell: disable-line + } + @{ + MockParameterName = 'MRCacheDirectory' + MockParameterValue = 'C:\Temp' + MockExpectedRegEx = '\/MRCACHEDIRECTORY="C:\\Temp"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlInstJava' + MockParameterValue = $true + MockExpectedRegEx = '\/SQL_INST_JAVA' # cspell: disable-line + } + @{ + MockParameterName = 'SqlJavaDir' + MockParameterValue = 'C:\Java' + MockExpectedRegEx = '\/SQLJAVADIR="C:\\Java"' # cspell: disable-line + } + @{ + MockParameterName = 'AzureSubscriptionId' + MockParameterValue = '5d19794a-89a4-4f0b-8d4e-58f213ea3546' + MockExpectedRegEx = '\/AZURESUBSCRIPTIONID="5d19794a-89a4-4f0b-8d4e-58f213ea3546"' # cspell: disable-line + } + @{ + MockParameterName = 'AzureResourceGroup' + MockParameterValue = 'MyResourceGroup' + MockExpectedRegEx = '\/AZURERESOURCEGROUP="MyResourceGroup"' # cspell: disable-line + } + @{ + MockParameterName = 'AzureRegion' + MockParameterValue = 'West-US' + MockExpectedRegEx = '\/AZUREREGION="West-US"' # cspell: disable-line + } + @{ + MockParameterName = 'AzureTenantId' + MockParameterValue = '5d19794a-89a4-4f0b-8d4e-58f213ea3546' + MockExpectedRegEx = '\/AZURETENANTID="5d19794a-89a4-4f0b-8d4e-58f213ea3546"' # cspell: disable-line + } + @{ + MockParameterName = 'AzureServicePrincipal' + MockParameterValue = 'MyServicePrincipal' + MockExpectedRegEx = '\/AZURESERVICEPRINCIPAL="MyServicePrincipal"' # cspell: disable-line + } + @{ + MockParameterName = 'AzureServicePrincipalSecret' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/AZURESERVICEPRINCIPALSECRET="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'AzureArcProxy' + MockParameterValue = 'proxy.company.local' + MockExpectedRegEx = '\/AZUREARCPROXY="proxy\.company\.local"' # cspell: disable-line + } + @{ + MockParameterName = 'SkipRules' + MockParameterValue = 'Cluster_VerifyForErrors' + MockExpectedRegEx = '\/SKIPRULES="Cluster_VerifyForErrors"' # cspell: disable-line + } + ) { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + $mockDefaultParameters = @{ + Install = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + # Intentionally using both upper- and lower-case in the value. + Features = 'SqlEngine' + SqlSysAdminAccounts = 'DOMAIN\User' + Force = $true + } + } + + BeforeEach { + $installSqlDscServerParameters = $mockDefaultParameters.Clone() + } + + It 'Should call the mock with the correct argument string' { + $installSqlDscServerParameters.$MockParameterName = $MockParameterValue + + Install-SqlDscServer @installSqlDscServerParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly $MockExpectedRegEx + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + + Context 'When specifying sensitive parameter ' -ForEach @( + @{ + MockParameterName = 'PBEngSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/PBENGSVCPASSWORD="\*{8}"' # cspell: disable-line + } + @{ + MockParameterName = 'PBDMSSvcPassword' # cspell: disable-line + MockParameterValue = ('jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force) # cspell: disable-line + MockExpectedRegEx = '\/PBDMSSVCPASSWORD="\*{8}"' # cspell: disable-line + } + @{ + MockParameterName = 'ProductKey' # cspell: disable-line + MockParameterValue = '22222-00000-00000-00000-00000' + MockExpectedRegEx = '\/PID="\*{8}"' # cspell: disable-line + } + @{ + MockParameterName = 'AgtSvcPassword' # cspell: disable-line + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/AGTSVCPASSWORD="\*{8}"' # cspell: disable-line + } + @{ + MockParameterName = 'SAPwd' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/SAPWD="\*{8}"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/SQLSVCPASSWORD="\*{8}"' # cspell: disable-line + } + @{ + MockParameterName = 'ISSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/ISSVCPASSWORD="\*{8}"' # cspell: disable-line + } + @{ + MockParameterName = 'RSSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/RSSVCPASSWORD="\*{8}"' # cspell: disable-line + } + @{ + MockParameterName = 'AzureServicePrincipalSecret' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/AZURESERVICEPRINCIPALSECRET="\*{8}"' # cspell: disable-line + } + ) { + BeforeAll { + Mock -CommandName Write-Verbose + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + $mockDefaultParameters = @{ + Install = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + # Intentionally using both upper- and lower-case in the value. + Features = 'SqlEngine' + SqlSysAdminAccounts = 'DOMAIN\User' + Force = $true + } + } + + BeforeEach { + $installSqlDscServerParameters = $mockDefaultParameters.Clone() + } + + It 'Should obfuscate the value in the verbose string' { + $installSqlDscServerParameters.$MockParameterName = $MockParameterValue + + # Redirect all verbose stream to $null to ge no output from ShouldProcess. + Install-SqlDscServer @installSqlDscServerParameters -Verbose 4> $null + + $mockVerboseMessage = InModuleScope -ScriptBlock { + $script:localizedData.Server_SetupArguments + } + + Should -Invoke -CommandName Write-Verbose -ParameterFilter { + # Only test the command that output the string that should be tested. + $correctMessage = $Message -match $mockVerboseMessage + + # Only test string if it is the correct verbose command + if ($correctMessage) + { + $Message | Should -MatchExactly $MockExpectedRegEx + } + + # Return wether the correct command was called or not. + $correctMessage + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When setup action is ''Upgrade''' { + BeforeAll { + Mock -CommandName Assert-SetupActionProperties + Mock -CommandName Assert-ElevatedUser + Mock -CommandName Test-Path -ParameterFilter { + $Path -match 'setup\.exe' + } -MockWith { + return $true + } + } + + Context 'When specifying only mandatory parameters' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + $mockDefaultParameters = @{ + Upgrade = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + } + } + + Context 'When using parameter Confirm with value $false' { + It 'Should call the mock with the correct argument string' { + Install-SqlDscServer -Confirm:$false @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=Upgrade' + $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + + Context 'When using parameter Force' { + It 'Should call the mock with the correct argument string' { + Install-SqlDscServer -Force @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=Upgrade' + $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + + Context 'When using parameter WhatIf' { + It 'Should call the mock with the correct argument string' { + Install-SqlDscServer -WhatIf @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -Exactly -Times 0 -Scope It + } + } + } + + Context 'When specifying optional parameter ' -ForEach @( + @{ + MockParameterName = 'Enu' + MockParameterValue = $true + MockExpectedRegEx = '\/ENU\s*' + } + @{ + MockParameterName = 'UpdateEnabled' + MockParameterValue = $true + MockExpectedRegEx = '\/UPDATEENABLED=True' # cspell: disable-line + } + @{ + MockParameterName = 'UpdateSource' + MockParameterValue = '\SqlMedia\Updates' + MockExpectedRegEx = '\/UPDATESOURCE="\\SqlMedia\\Updates"' # cspell: disable-line + } + @{ + MockParameterName = 'InstanceDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server' + MockExpectedRegEx = '\/INSTANCEDIR="C:\\Program Files\\Microsoft SQL Server"' # cspell: disable-line + } + @{ + MockParameterName = 'InstanceId' + MockParameterValue = 'Instance' + MockExpectedRegEx = '\/INSTANCEID="Instance"' # cspell: disable-line + } + @{ + MockParameterName = 'ProductKey' + MockParameterValue = '22222-00000-00000-00000-00000' + MockExpectedRegEx = '\/PID="22222-00000-00000-00000-00000"' + } + @{ + MockParameterName = 'BrowserSvcStartupType' + MockParameterValue = 'Manual' + MockExpectedRegEx = '\/BROWSERSVCSTARTUPTYPE="Manual"' # cspell: disable-line + } + @{ + MockParameterName = 'FTUpgradeOption' + MockParameterValue = 'Reset' + MockExpectedRegEx = '\/FTUPGRADEOPTION="Reset"' # cspell: disable-line + } + @{ + MockParameterName = 'ISSvcAccount' + MockParameterValue = 'DOMAIN\ServiceAccount$' + MockExpectedRegEx = '\/ISSVCACCOUNT="DOMAIN\\ServiceAccount\$"' # cspell: disable-line + } + @{ + MockParameterName = 'ISSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/ISSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'ISSvcStartupType' + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/ISSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + @{ + MockParameterName = 'AllowUpgradeForSSRSSharePointMode' + MockParameterValue = $true + MockExpectedRegEx = '\/ALLOWUPGRADEFORSSRSSHAREPOINTMODE=True' # cspell: disable-line + } + @{ + MockParameterName = 'FailoverClusterRollOwnership' + MockParameterValue = 2 + MockExpectedRegEx = '\/FAILOVERCLUSTERROLLOWNERSHIP=2' # cspell: disable-line + } + ) { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + $mockDefaultParameters = @{ + Upgrade = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + # Intentionally using both upper- and lower-case in the value. + Force = $true + } + } + + BeforeEach { + $installSqlDscServerParameters = $mockDefaultParameters.Clone() + } + + It 'Should call the mock with the correct argument string' { + $installSqlDscServerParameters.$MockParameterName = $MockParameterValue + + Install-SqlDscServer @installSqlDscServerParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly $MockExpectedRegEx + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When setup action is ''InstallFailoverCluster''' { + BeforeAll { + Mock -CommandName Assert-SetupActionProperties + Mock -CommandName Assert-ElevatedUser + Mock -CommandName Test-Path -ParameterFilter { + $Path -match 'setup\.exe' + } -MockWith { + return $true + } + } + + Context 'When specifying only mandatory parameters' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + $mockDefaultParameters = @{ + InstallFailoverCluster = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + # Intentionally using both upper- and lower-case. + Features = 'SqlEngine' + SqlSysAdminAccounts = 'DOMAIN\User', 'COMPANY\SQL Administrators' + InstallSqlDataDir = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + FailoverClusterNetworkName = 'TESTCLU01A' # cspell: disable-line + FailoverClusterIPAddresses = @( + 'IPv4;172.16.0.0;ClusterNetwork1;172.31.255.255', + 'IPv6;2001:db8:23:1002:20f:1fff:feff:b3a3;ClusterNetwork2' # cspell: disable-line + 'IPv6;DHCP;ClusterNetwork3' + 'IPv4;DHCP;ClusterNetwork4' + ) + } + } + + Context 'When using parameter Confirm with value $false' { + It 'Should call the mock with the correct argument string' { + Install-SqlDscServer -Confirm:$false @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=InstallFailoverCluster' + $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/SQLSYSADMINACCOUNTS="DOMAIN\\User" "COMPANY\\SQL Administrators"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/INSTALLSQLDATADIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/FEATURES=SQLENGINE' + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/FAILOVERCLUSTERNETWORKNAME="TESTCLU01A"' # cspell: disable-line + $ArgumentList | Should -MatchExactly ([System.Text.RegularExpressions.Regex]::Escape('/FAILOVERCLUSTERIPADDRESSES="IPv4;172.16.0.0;ClusterNetwork1;172.31.255.255" "IPv6;2001:db8:23:1002:20f:1fff:feff:b3a3;ClusterNetwork2" "IPv6;DHCP;ClusterNetwork3" "IPv4;DHCP;ClusterNetwork4"')) # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + + Context 'When using parameter Force' { + It 'Should call the mock with the correct argument string' { + Install-SqlDscServer -Force @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=InstallFailoverCluster' + $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/SQLSYSADMINACCOUNTS="DOMAIN\\User" "COMPANY\\SQL Administrators"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/INSTALLSQLDATADIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/FEATURES=SQLENGINE' + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/FAILOVERCLUSTERNETWORKNAME="TESTCLU01A"' # cspell: disable-line + $ArgumentList | Should -MatchExactly ([System.Text.RegularExpressions.Regex]::Escape('/FAILOVERCLUSTERIPADDRESSES="IPv4;172.16.0.0;ClusterNetwork1;172.31.255.255" "IPv6;2001:db8:23:1002:20f:1fff:feff:b3a3;ClusterNetwork2" "IPv6;DHCP;ClusterNetwork3" "IPv4;DHCP;ClusterNetwork4"')) # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + + Context 'When using parameter WhatIf' { + It 'Should call the mock with the correct argument string' { + Install-SqlDscServer -WhatIf @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -Exactly -Times 0 -Scope It + } + } + } + + Context 'When specifying optional parameters PBStartPortRange and PBEndPortRange' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + $installSqlDscServerParameters = @{ + InstallFailoverCluster = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + # Intentionally using both upper- and lower-case in the value. + Features = 'SqlEngine' + SqlSysAdminAccounts = 'DOMAIN\User', 'COMPANY\SQL Administrators' + InstallSqlDataDir = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + FailoverClusterNetworkName = 'TESTCLU01A' # cspell: disable-line + FailoverClusterIPAddresses = '192.168.0.46' + Force = $true + PBStartPortRange = 16450 + PBEndPortRange = 16460 + } + } + + It 'Should call the mock with the correct argument string' { + Install-SqlDscServer @installSqlDscServerParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly 'PBPORTRANGE=16450-16460' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + + Context 'When specifying optional parameter ' -ForEach @( + @{ + MockParameterName = 'Enu' + MockParameterValue = $true + MockExpectedRegEx = '\/ENU\s*' + } + @{ + MockParameterName = 'UpdateEnabled' + MockParameterValue = $true + MockExpectedRegEx = '\/UPDATEENABLED=True' # cspell: disable-line + } + @{ + MockParameterName = 'UpdateSource' + MockParameterValue = '\SqlMedia\Updates' + MockExpectedRegEx = '\/UPDATESOURCE="\\SqlMedia\\Updates"' # cspell: disable-line + } + @{ + MockParameterName = 'InstallSharedDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server' + MockExpectedRegEx = '\/INSTALLSHAREDDIR="C:\\Program Files\\Microsoft SQL Server"' # cspell: disable-line + } + @{ + MockParameterName = 'InstallSharedWOWDir' + MockParameterValue = 'C:\Program Files (x86)\Microsoft SQL Server' + MockExpectedRegEx = '\/INSTALLSHAREDWOWDIR="C:\\Program Files \(x86\)\\Microsoft SQL Server"' # cspell: disable-line + } + @{ + MockParameterName = 'InstanceDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server' + MockExpectedRegEx = '\/INSTANCEDIR="C:\\Program Files\\Microsoft SQL Server"' # cspell: disable-line + } + @{ + MockParameterName = 'InstanceId' + MockParameterValue = 'Instance' + MockExpectedRegEx = '\/INSTANCEID="Instance"' # cspell: disable-line + } + @{ + MockParameterName = 'PBEngSvcAccount' + MockParameterValue = 'NT Authority\NETWORK SERVICE' + MockExpectedRegEx = '\/PBENGSVCACCOUNT="NT Authority\\NETWORK SERVICE"' # cspell: disable-line + } + @{ + MockParameterName = 'PBEngSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/PBENGSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'PBEngSvcStartupType' + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/PBENGSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + @{ + MockParameterName = 'PBScaleOut' + MockParameterValue = $true + MockExpectedRegEx = '\/PBSCALEOUT=True' # cspell: disable-line + } + @{ + MockParameterName = 'ProductKey' + MockParameterValue = '22222-00000-00000-00000-00000' + MockExpectedRegEx = '\/PID="22222-00000-00000-00000-00000"' + } + @{ + MockParameterName = 'AgtSvcAccount' + MockParameterValue = 'NT Authority\NETWORK SERVICE' + MockExpectedRegEx = '\/AGTSVCACCOUNT="NT Authority\\NETWORK SERVICE"' # cspell: disable-line + } + @{ + MockParameterName = 'AgtSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/AGTSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'ASBackupDir' + MockParameterValue = 'C:\MSOLAP13.INST2016\Backup' + MockExpectedRegEx = '\/ASBACKUPDIR="C:\\MSOLAP13\.INST2016\\Backup"' # cspell: disable-line + } + @{ + MockParameterName = 'ASConfigDir' + MockParameterValue = 'C:\MSOLAP13.INST2016\Config' + MockExpectedRegEx = '\/ASCONFIGDIR="C:\\MSOLAP13\.INST2016\\Config"' # cspell: disable-line + } + @{ + MockParameterName = 'ASDataDir' + MockParameterValue = 'C:\MSOLAP13.INST2016\Data' + MockExpectedRegEx = '\/ASDATADIR="C:\\MSOLAP13\.INST2016\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'ASLogDir' + MockParameterValue = 'C:\MSOLAP13.INST2016\Log' + MockExpectedRegEx = '\/ASLOGDIR="C:\\MSOLAP13\.INST2016\\Log"' # cspell: disable-line + } + @{ + MockParameterName = 'ASTempDir' + MockParameterValue = 'C:\MSOLAP13.INST2016\Temp' + MockExpectedRegEx = '\/ASTEMPDIR="C:\\MSOLAP13\.INST2016\\Temp"' # cspell: disable-line + } + @{ + MockParameterName = 'ASCollation' + MockParameterValue = 'latin1_general_100' + MockExpectedRegEx = '\/ASCOLLATION="latin1_general_100"' # cspell: disable-line + } + @{ + MockParameterName = 'ASServerMode' + MockParameterValue = 'Multidimensional' + MockExpectedRegEx = '\/ASSERVERMODE=MULTIDIMENSIONAL' # cspell: disable-line + } + @{ + MockParameterName = 'AsSvcAccount' + MockParameterValue = 'DOMAIN\ServiceAccount$' + MockExpectedRegEx = '\/ASSVCACCOUNT="DOMAIN\\ServiceAccount\$"' # cspell: disable-line + } + @{ + MockParameterName = 'AsSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/ASSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'AsSvcStartupType' + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/ASSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + @{ + MockParameterName = 'ASSysAdminAccounts' + MockParameterValue = 'COMPANY\SQL Administrators', 'LocalUser' + MockExpectedRegEx = '\/ASSYSADMINACCOUNTS="COMPANY\\SQL Administrators" "LocalUser"' # cspell: disable-line + } + @{ + MockParameterName = 'ASProviderMSOLAP' + MockParameterValue = $true + MockExpectedRegEx = '\/ASPROVIDERMSOLAP=1' # cspell: disable-line + } + @{ + MockParameterName = 'SqlBackupDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Backup' + MockExpectedRegEx = '\/SQLBACKUPDIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Backup"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + MockExpectedRegEx = '\/SQLTEMPDBDIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbLogDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + MockExpectedRegEx = '\/SQLTEMPDBLOGDIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlUserDbDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + MockExpectedRegEx = '\/SQLUSERDBDIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlUserDbLogDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + MockExpectedRegEx = '\/SQLUSERDBLOGDIR="C:\\Program Files\\Microsoft SQL Server\\MSSQL13.INST2016\\MSSQL\\Data"' # cspell: disable-line + } + @{ + MockParameterName = 'SecurityMode' + MockParameterValue = 'SQL' + MockExpectedRegEx = '\/SECURITYMODE="SQL"' # cspell: disable-line + } + @{ + MockParameterName = 'SAPwd' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/SAPWD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlCollation' + MockParameterValue = 'SQL_Latin1_General_CP1_CI_AS' + MockExpectedRegEx = '\/SQLCOLLATION="SQL_Latin1_General_CP1_CI_AS"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlSvcAccount' + MockParameterValue = 'DOMAIN\ServiceAccount$' + MockExpectedRegEx = '\/SQLSVCACCOUNT="DOMAIN\\ServiceAccount\$"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/SQLSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlSvcStartupType' + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/SQLSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbFileCount' + MockParameterValue = 8 + MockExpectedRegEx = '\/SQLTEMPDBFILECOUNT=8' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbFileSize' + MockParameterValue = 100 + MockExpectedRegEx = '\/SQLTEMPDBFILESIZE=100' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbFileGrowth' + MockParameterValue = 10 + MockExpectedRegEx = '\/SQLTEMPDBFILEGROWTH=10' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbLogFileSize' + MockParameterValue = 100 + MockExpectedRegEx = '\/SQLTEMPDBLOGFILESIZE=100' # cspell: disable-line + } + @{ + MockParameterName = 'SqlTempDbLogFileGrowth' + MockParameterValue = 10 + MockExpectedRegEx = '\/SQLTEMPDBLOGFILEGROWTH=10' # cspell: disable-line + } + @{ + MockParameterName = 'FileStreamLevel' + MockParameterValue = 2 + MockExpectedRegEx = '\/FILESTREAMLEVEL=2' # cspell: disable-line + } + @{ + MockParameterName = 'FileStreamShareName' + MockParameterValue = 'ShareName' + MockExpectedRegEx = '\/FILESTREAMSHARENAME="ShareName"' # cspell: disable-line + } + @{ + MockParameterName = 'ISSvcAccount' + MockParameterValue = 'DOMAIN\ServiceAccount$' + MockExpectedRegEx = '\/ISSVCACCOUNT="DOMAIN\\ServiceAccount\$"' # cspell: disable-line + } + @{ + MockParameterName = 'ISSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/ISSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'ISSvcStartupType' + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/ISSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + @{ + MockParameterName = 'RsInstallMode' + MockParameterValue = 'FilesOnlyMode' + MockExpectedRegEx = '\/RSINSTALLMODE="FilesOnlyMode"' # cspell: disable-line + } + @{ + MockParameterName = 'RSSvcAccount' + MockParameterValue = 'DOMAIN\ServiceAccount$' + MockExpectedRegEx = '\/RSSVCACCOUNT="DOMAIN\\ServiceAccount\$"' # cspell: disable-line + } + @{ + MockParameterName = 'RSSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/RSSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'RSSvcStartupType' + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/RSSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + @{ + MockParameterName = 'SkipRules' + MockParameterValue = 'Cluster_VerifyForErrors' + MockExpectedRegEx = '\/SKIPRULES="Cluster_VerifyForErrors"' # cspell: disable-line + } + @{ + MockParameterName = 'FailoverClusterGroup' + MockParameterValue = 'TESTCLU01A' # cspell: disable-line + MockExpectedRegEx = '\/FAILOVERCLUSTERGROUP="TESTCLU01A"' # cspell: disable-line + } + @{ + MockParameterName = 'FailoverClusterGroup' + MockParameterValue = 'TESTCLU01A' # cspell: disable-line + MockExpectedRegEx = '\/FAILOVERCLUSTERGROUP="TESTCLU01A"' # cspell: disable-line + } + @{ + MockParameterName = 'FailoverClusterDisks' + # This is the failover cluster resource name. + MockParameterValue = @( + 'SysData' + ) + MockExpectedRegEx = '\/FAILOVERCLUSTERDISKS="SysData"' # cspell: disable-line + } + @{ + MockParameterName = 'FailoverClusterDisks' + # This is the failover cluster resource names. + MockParameterValue = @( + 'Backup' + 'SysData' + 'TempDbData' + 'TempDbLogs' + 'UserData' + 'UserLogs' + ) + MockExpectedRegEx = '\/FAILOVERCLUSTERDISKS="Backup;SysData;TempDbData;TempDbLogs;UserData;UserLogs"' # cspell: disable-line + } + ) { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + $mockDefaultParameters = @{ + InstallFailoverCluster = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + # Intentionally using both upper- and lower-case in the value. + Features = 'SqlEngine' + SqlSysAdminAccounts = 'DOMAIN\User', 'COMPANY\SQL Administrators' + InstallSqlDataDir = 'C:\Program Files\Microsoft SQL Server\MSSQL13.INST2016\MSSQL\Data' + FailoverClusterNetworkName = 'TESTCLU01A' # cspell: disable-line + FailoverClusterIPAddresses = '192.168.0.46' + Force = $true + } + } + + BeforeEach { + $installSqlDscServerParameters = $mockDefaultParameters.Clone() + } + + It 'Should call the mock with the correct argument string' { + $installSqlDscServerParameters.$MockParameterName = $MockParameterValue + + Install-SqlDscServer @installSqlDscServerParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly $MockExpectedRegEx + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When setup action is ''PrepareFailoverCluster''' { + BeforeAll { + Mock -CommandName Assert-SetupActionProperties + Mock -CommandName Assert-ElevatedUser + Mock -CommandName Test-Path -ParameterFilter { + $Path -match 'setup\.exe' + } -MockWith { + return $true + } + } + + Context 'When specifying only mandatory parameters' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + $mockDefaultParameters = @{ + PrepareFailoverCluster = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + # Intentionally using both upper- and lower-case. + Features = 'SqlEngine' + } + } + + Context 'When using parameter Confirm with value $false' { + It 'Should call the mock with the correct argument string' { + Install-SqlDscServer -Confirm:$false @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=PrepareFailoverCluster' + $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + + Context 'When using parameter Force' { + It 'Should call the mock with the correct argument string' { + Install-SqlDscServer -Force @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=PrepareFailoverCluster' + $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + + Context 'When using parameter WhatIf' { + It 'Should call the mock with the correct argument string' { + Install-SqlDscServer -WhatIf @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -Exactly -Times 0 -Scope It + } + } + } + + Context 'When specifying optional parameters PBStartPortRange and PBEndPortRange' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + $installSqlDscServerParameters = @{ + PrepareFailoverCluster = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + # Intentionally using both upper- and lower-case in the value. + Features = 'SqlEngine' + Force = $true + PBStartPortRange = 16450 + PBEndPortRange = 16460 + } + } + + It 'Should call the mock with the correct argument string' { + Install-SqlDscServer @installSqlDscServerParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly 'PBPORTRANGE=16450-16460' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + + Context 'When specifying optional parameter ' -ForEach @( + @{ + MockParameterName = 'Enu' + MockParameterValue = $true + MockExpectedRegEx = '\/ENU\s*' + } + @{ + MockParameterName = 'UpdateEnabled' + MockParameterValue = $true + MockExpectedRegEx = '\/UPDATEENABLED=True' # cspell: disable-line + } + @{ + MockParameterName = 'UpdateSource' + MockParameterValue = '\SqlMedia\Updates' + MockExpectedRegEx = '\/UPDATESOURCE="\\SqlMedia\\Updates"' # cspell: disable-line + } + @{ + MockParameterName = 'InstallSharedDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server' + MockExpectedRegEx = '\/INSTALLSHAREDDIR="C:\\Program Files\\Microsoft SQL Server"' # cspell: disable-line + } + @{ + MockParameterName = 'InstallSharedWOWDir' + MockParameterValue = 'C:\Program Files (x86)\Microsoft SQL Server' + MockExpectedRegEx = '\/INSTALLSHAREDWOWDIR="C:\\Program Files \(x86\)\\Microsoft SQL Server"' # cspell: disable-line + } + @{ + MockParameterName = 'InstanceDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server' + MockExpectedRegEx = '\/INSTANCEDIR="C:\\Program Files\\Microsoft SQL Server"' # cspell: disable-line + } + @{ + MockParameterName = 'InstanceId' + MockParameterValue = 'Instance' + MockExpectedRegEx = '\/INSTANCEID="Instance"' # cspell: disable-line + } + @{ + MockParameterName = 'PBEngSvcAccount' + MockParameterValue = 'NT Authority\NETWORK SERVICE' + MockExpectedRegEx = '\/PBENGSVCACCOUNT="NT Authority\\NETWORK SERVICE"' # cspell: disable-line + } + @{ + MockParameterName = 'PBEngSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/PBENGSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'PBEngSvcStartupType' + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/PBENGSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + @{ + MockParameterName = 'PBScaleOut' + MockParameterValue = $true + MockExpectedRegEx = '\/PBSCALEOUT=True' # cspell: disable-line + } + @{ + MockParameterName = 'ProductKey' + MockParameterValue = '22222-00000-00000-00000-00000' + MockExpectedRegEx = '\/PID="22222-00000-00000-00000-00000"' + } + @{ + MockParameterName = 'AgtSvcAccount' + MockParameterValue = 'NT Authority\NETWORK SERVICE' + MockExpectedRegEx = '\/AGTSVCACCOUNT="NT Authority\\NETWORK SERVICE"' # cspell: disable-line + } + @{ + MockParameterName = 'AgtSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/AGTSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'AsSvcAccount' + MockParameterValue = 'DOMAIN\ServiceAccount$' + MockExpectedRegEx = '\/ASSVCACCOUNT="DOMAIN\\ServiceAccount\$"' # cspell: disable-line + } + @{ + MockParameterName = 'AsSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/ASSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlSvcAccount' + MockParameterValue = 'DOMAIN\ServiceAccount$' + MockExpectedRegEx = '\/SQLSVCACCOUNT="DOMAIN\\ServiceAccount\$"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/SQLSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'FileStreamLevel' + MockParameterValue = 2 + MockExpectedRegEx = '\/FILESTREAMLEVEL=2' # cspell: disable-line + } + @{ + MockParameterName = 'FileStreamShareName' + MockParameterValue = 'ShareName' + MockExpectedRegEx = '\/FILESTREAMSHARENAME="ShareName"' # cspell: disable-line + } + @{ + MockParameterName = 'ISSvcAccount' + MockParameterValue = 'DOMAIN\ServiceAccount$' + MockExpectedRegEx = '\/ISSVCACCOUNT="DOMAIN\\ServiceAccount\$"' # cspell: disable-line + } + @{ + MockParameterName = 'ISSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/ISSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'ISSvcStartupType' + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/ISSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + @{ + MockParameterName = 'RsInstallMode' + MockParameterValue = 'FilesOnlyMode' + MockExpectedRegEx = '\/RSINSTALLMODE="FilesOnlyMode"' # cspell: disable-line + } + @{ + MockParameterName = 'RSSvcAccount' + MockParameterValue = 'DOMAIN\ServiceAccount$' + MockExpectedRegEx = '\/RSSVCACCOUNT="DOMAIN\\ServiceAccount\$"' # cspell: disable-line + } + @{ + MockParameterName = 'RSSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/RSSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'RSSvcStartupType' + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/RSSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + ) { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + $mockDefaultParameters = @{ + PrepareFailoverCluster = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + # Intentionally using both upper- and lower-case in the value. + Features = 'SqlEngine' + Force = $true + } + } + + BeforeEach { + $installSqlDscServerParameters = $mockDefaultParameters.Clone() + } + + It 'Should call the mock with the correct argument string' { + $installSqlDscServerParameters.$MockParameterName = $MockParameterValue + + Install-SqlDscServer @installSqlDscServerParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly $MockExpectedRegEx + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When using a ''ConfigurationFile''' { + BeforeAll { + Mock -CommandName Assert-SetupActionProperties + Mock -CommandName Assert-ElevatedUser + + Mock -CommandName Test-Path -ParameterFilter { + $Path -match 'MyConfig\.ini' + } -MockWith { + return $true + } + + Mock -CommandName Test-Path -ParameterFilter { + $Path -match 'setup\.exe' + } -MockWith { + return $true + } + } + + Context 'When specifying only mandatory parameters' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + $mockDefaultParameters = @{ + ConfigurationFile = 'C:\MyConfig.ini' + MediaPath = '\SqlMedia' + } + } + + Context 'When using parameter Confirm with value $false' { + It 'Should call the mock with the correct argument string' { + Install-SqlDscServer -Confirm:$false @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + # cspell: disable-next + $ArgumentList | Should -MatchExactly '\/CONFIGURATIONFILE="C:\\MyConfig\.ini"' + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + + Context 'When using parameter Force' { + It 'Should call the mock with the correct argument string' { + Install-SqlDscServer -Force @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + # cspell: disable-next + $ArgumentList | Should -MatchExactly '\/CONFIGURATIONFILE="C:\\MyConfig\.ini"' + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + + Context 'When using parameter WhatIf' { + It 'Should call the mock with the correct argument string' { + Install-SqlDscServer -WhatIf @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -Exactly -Times 0 -Scope It + } + } + } + + Context 'When specifying optional parameter ' -ForEach @( + @{ + MockParameterName = 'AgtSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/AGTSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'AsSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/ASSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'SqlSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/SQLSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'ISSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/ISSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'RSSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/RSSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + ) { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + $mockDefaultParameters = @{ + ConfigurationFile = 'C:\MyConfig.ini' + MediaPath = '\SqlMedia' + Force = $true + } + } + + BeforeEach { + $installSqlDscServerParameters = $mockDefaultParameters.Clone() + } + + It 'Should call the mock with the correct argument string' { + $installSqlDscServerParameters.$MockParameterName = $MockParameterValue + + Install-SqlDscServer @installSqlDscServerParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly $MockExpectedRegEx + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When setup action is ''EditionUpgrade''' { + BeforeAll { + Mock -CommandName Assert-SetupActionProperties + Mock -CommandName Assert-ElevatedUser + Mock -CommandName Test-Path -ParameterFilter { + $Path -match 'setup\.exe' + } -MockWith { + return $true + } + } + + Context 'When specifying only mandatory parameters' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + $mockDefaultParameters = @{ + EditionUpgrade = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + ProductKey = '22222-00000-00000-00000-00000' + } + } + + Context 'When using parameter Confirm with value $false' { + It 'Should call the mock with the correct argument string' { + Install-SqlDscServer -Confirm:$false @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=EditionUpgrade' + $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/PID="22222-00000-00000-00000-00000"' + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + + Context 'When using parameter Force' { + It 'Should call the mock with the correct argument string' { + Install-SqlDscServer -Force @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=EditionUpgrade' + $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/PID="22222-00000-00000-00000-00000"' + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + + Context 'When using parameter WhatIf' { + It 'Should call the mock with the correct argument string' { + Install-SqlDscServer -WhatIf @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -Exactly -Times 0 -Scope It + } + } + } + + Context 'When specifying optional parameter ' -ForEach @( + @{ + MockParameterName = 'SkipRules' + MockParameterValue = 'Cluster_VerifyForErrors' + MockExpectedRegEx = '\/SKIPRULES="Cluster_VerifyForErrors"' # cspell: disable-line + } + ) { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + $mockDefaultParameters = @{ + EditionUpgrade = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + ProductKey = 22222-00000-00000-00000-00000 + Force = $true + } + } + + BeforeEach { + $installSqlDscServerParameters = $mockDefaultParameters.Clone() + } + + It 'Should call the mock with the correct argument string' { + $installSqlDscServerParameters.$MockParameterName = $MockParameterValue + + Install-SqlDscServer @installSqlDscServerParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly $MockExpectedRegEx + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When setup action is ''Upgrade''' { + BeforeAll { + Mock -CommandName Assert-SetupActionProperties + Mock -CommandName Assert-ElevatedUser + Mock -CommandName Test-Path -ParameterFilter { + $Path -match 'setup\.exe' + } -MockWith { + return $true + } + } + + Context 'When specifying only mandatory parameters' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + $mockDefaultParameters = @{ + Upgrade = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + } + } + + Context 'When using parameter Confirm with value $false' { + It 'Should call the mock with the correct argument string' { + Install-SqlDscServer -Confirm:$false @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=Upgrade' + $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + + Context 'When using parameter Force' { + It 'Should call the mock with the correct argument string' { + Install-SqlDscServer -Force @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=Upgrade' + $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + + Context 'When using parameter WhatIf' { + It 'Should call the mock with the correct argument string' { + Install-SqlDscServer -WhatIf @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -Exactly -Times 0 -Scope It + } + } + } + + Context 'When specifying optional parameter ' -ForEach @( + @{ + MockParameterName = 'Enu' + MockParameterValue = $true + MockExpectedRegEx = '\/ENU\s*' + } + @{ + MockParameterName = 'UpdateEnabled' + MockParameterValue = $true + MockExpectedRegEx = '\/UPDATEENABLED=True' # cspell: disable-line + } + @{ + MockParameterName = 'UpdateSource' + MockParameterValue = '\SqlMedia\Updates' + MockExpectedRegEx = '\/UPDATESOURCE="\\SqlMedia\\Updates"' # cspell: disable-line + } + @{ + MockParameterName = 'InstanceDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server' + MockExpectedRegEx = '\/INSTANCEDIR="C:\\Program Files\\Microsoft SQL Server"' # cspell: disable-line + } + @{ + MockParameterName = 'InstanceId' + MockParameterValue = 'Instance' + MockExpectedRegEx = '\/INSTANCEID="Instance"' # cspell: disable-line + } + @{ + MockParameterName = 'ProductKey' + MockParameterValue = '22222-00000-00000-00000-00000' + MockExpectedRegEx = '\/PID="22222-00000-00000-00000-00000"' + } + @{ + MockParameterName = 'BrowserSvcStartupType' + MockParameterValue = 'Manual' + MockExpectedRegEx = '\/BROWSERSVCSTARTUPTYPE="Manual"' # cspell: disable-line + } + @{ + MockParameterName = 'ISSvcAccount' + MockParameterValue = 'DOMAIN\ServiceAccount$' + MockExpectedRegEx = '\/ISSVCACCOUNT="DOMAIN\\ServiceAccount\$"' # cspell: disable-line + } + @{ + MockParameterName = 'ISSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/ISSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'ISSvcStartupType' + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/ISSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + ) { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + $mockDefaultParameters = @{ + Upgrade = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + Force = $true + } + } + + BeforeEach { + $installSqlDscServerParameters = $mockDefaultParameters.Clone() + } + + It 'Should call the mock with the correct argument string' { + $installSqlDscServerParameters.$MockParameterName = $MockParameterValue + + Install-SqlDscServer @installSqlDscServerParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly $MockExpectedRegEx + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When setup action is ''PrepareImage''' { + BeforeAll { + Mock -CommandName Assert-SetupActionProperties + Mock -CommandName Assert-ElevatedUser + Mock -CommandName Test-Path -ParameterFilter { + $Path -match 'setup\.exe' + } -MockWith { + return $true + } + } + + Context 'When specifying only mandatory parameters' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + $mockDefaultParameters = @{ + PrepareImage = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + # Intentionally using both upper- and lower-case. + Features = 'SqlEngine' + InstanceId = 'Instance' + } + } + + Context 'When using parameter Confirm with value $false' { + It 'Should call the mock with the correct argument string' { + Install-SqlDscServer -Confirm:$false @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=PrepareImage' + $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/FEATURES=SQLENGINE' + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/INSTANCEID="Instance"' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + + Context 'When using parameter Force' { + It 'Should call the mock with the correct argument string' { + Install-SqlDscServer -Force @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=PrepareImage' + $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/FEATURES=SQLENGINE' + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/INSTANCEID="Instance"' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + + Context 'When using parameter WhatIf' { + It 'Should call the mock with the correct argument string' { + Install-SqlDscServer -WhatIf @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -Exactly -Times 0 -Scope It + } + } + } + + Context 'When specifying optional parameter ' -ForEach @( + @{ + MockParameterName = 'Enu' + MockParameterValue = $true + MockExpectedRegEx = '\/ENU\s*' + } + @{ + MockParameterName = 'UpdateEnabled' + MockParameterValue = $true + MockExpectedRegEx = '\/UPDATEENABLED=True' # cspell: disable-line + } + @{ + MockParameterName = 'UpdateSource' + MockParameterValue = '\SqlMedia\Updates' + MockExpectedRegEx = '\/UPDATESOURCE="\\SqlMedia\\Updates"' # cspell: disable-line + } + @{ + MockParameterName = 'InstallSharedDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server' + MockExpectedRegEx = '\/INSTALLSHAREDDIR="C:\\Program Files\\Microsoft SQL Server"' # cspell: disable-line + } + @{ + MockParameterName = 'InstanceDir' + MockParameterValue = 'C:\Program Files\Microsoft SQL Server' + MockExpectedRegEx = '\/INSTANCEDIR="C:\\Program Files\\Microsoft SQL Server"' # cspell: disable-line + } + @{ + MockParameterName = 'InstanceId' + MockParameterValue = 'Instance' + MockExpectedRegEx = '\/INSTANCEID="Instance"' # cspell: disable-line + } + @{ + MockParameterName = 'PBEngSvcAccount' + MockParameterValue = 'NT Authority\NETWORK SERVICE' + MockExpectedRegEx = '\/PBENGSVCACCOUNT="NT Authority\\NETWORK SERVICE"' # cspell: disable-line + } + @{ + MockParameterName = 'PBEngSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/PBENGSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'PBEngSvcStartupType' + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/PBENGSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + @{ + MockParameterName = 'PBScaleOut' + MockParameterValue = $true + MockExpectedRegEx = '\/PBSCALEOUT=True' # cspell: disable-line + } + ) { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + $mockDefaultParameters = @{ + PrepareImage = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + # Intentionally using both upper- and lower-case. + Features = 'SqlEngine' + InstanceId = 'Instance' + Force = $true + } + } + + BeforeEach { + $installSqlDscServerParameters = $mockDefaultParameters.Clone() + } + + It 'Should call the mock with the correct argument string' { + $installSqlDscServerParameters.$MockParameterName = $MockParameterValue + + Install-SqlDscServer @installSqlDscServerParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly $MockExpectedRegEx + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When setup action is ''Install'' for installing Azure Arc Agent (parameter set InstallAzureArcAgent)' { + BeforeAll { + Mock -CommandName Assert-SetupActionProperties + Mock -CommandName Assert-ElevatedUser + Mock -CommandName Test-Path -ParameterFilter { + $Path -match 'setup\.exe' + } -MockWith { + return $true + } + } + + Context 'When specifying only mandatory parameters' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + $mockDefaultParameters = @{ + Install = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + AzureSubscriptionId = '5d19794a-89a4-4f0b-8d4e-58f213ea3546' + AzureResourceGroup = 'MyResourceGroup' + AzureRegion = 'West-US' + AzureTenantId = '7e52fb9e-6aad-426c-98c4-7d2f11f7e94b' + AzureServicePrincipal = 'MyServicePrincipal' + AzureServicePrincipalSecret = ('jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force) # cspell: disable-line + } + } + + Context 'When using parameter Confirm with value $false' { + It 'Should call the mock with the correct argument string' { + Install-SqlDscServer -Confirm:$false @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=Install' + $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/AZURESUBSCRIPTIONID="5d19794a-89a4-4f0b-8d4e-58f213ea3546"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/AZURERESOURCEGROUP="MyResourceGroup"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/AZUREREGION="West-US"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/AZURETENANTID="7e52fb9e-6aad-426c-98c4-7d2f11f7e94b"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/AZURESERVICEPRINCIPAL="MyServicePrincipal"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/AZURESERVICEPRINCIPALSECRET="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/FEATURES=ARC' + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + + Context 'When using parameter Force' { + It 'Should call the mock with the correct argument string' { + Install-SqlDscServer -Force @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=Install' + $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/AZURESUBSCRIPTIONID="5d19794a-89a4-4f0b-8d4e-58f213ea3546"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/AZURERESOURCEGROUP="MyResourceGroup"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/AZUREREGION="West-US"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/AZURETENANTID="7e52fb9e-6aad-426c-98c4-7d2f11f7e94b"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/AZURESERVICEPRINCIPAL="MyServicePrincipal"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/AZURESERVICEPRINCIPALSECRET="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/FEATURES=ARC' + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + + Context 'When using parameter WhatIf' { + It 'Should call the mock with the correct argument string' { + Install-SqlDscServer -WhatIf @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -Exactly -Times 0 -Scope It + } + } + } + + Context 'When specifying optional parameter ' -ForEach @( + @{ + MockParameterName = 'AzureArcProxy' + MockParameterValue = 'proxy.company.local' + MockExpectedRegEx = '\/AZUREARCPROXY="proxy\.company\.local"' # cspell: disable-line + } + ) { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + $mockDefaultParameters = @{ + Install = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + AzureSubscriptionId = '5d19794a-89a4-4f0b-8d4e-58f213ea3546' + AzureResourceGroup = 'MyResourceGroup' + AzureRegion = 'West-US' + AzureTenantId = '7e52fb9e-6aad-426c-98c4-7d2f11f7e94b' + AzureServicePrincipal = 'MyServicePrincipal' + AzureServicePrincipalSecret = ('jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force) # cspell: disable-line + Force = $true + } + } + + BeforeEach { + $installSqlDscServerParameters = $mockDefaultParameters.Clone() + } + + It 'Should call the mock with the correct argument string' { + $installSqlDscServerParameters.$MockParameterName = $MockParameterValue + + Install-SqlDscServer @installSqlDscServerParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly $MockExpectedRegEx + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When parameter set is ''InstallRole''' { + BeforeAll { + Mock -CommandName Assert-SetupActionProperties + Mock -CommandName Assert-ElevatedUser + Mock -CommandName Test-Path -ParameterFilter { + $Path -match 'setup\.exe' + } -MockWith { + return $true + } + } + + Context 'When role is ''SPI_AS_NewFarm''' { + Context 'When specifying only mandatory parameters' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } + + $mockDefaultParameters = @{ + Install = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + Role = 'SPI_AS_NewFarm' + } + } + + Context 'When using parameter Confirm with value $false' { + It 'Should call the mock with the correct argument string' { + Install-SqlDscServer -Confirm:$false @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=Install' + $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/ROLE=SPI_AS_NEWFARM' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + + Context 'When using parameter Force' { + It 'Should call the mock with the correct argument string' { + Install-SqlDscServer -Force @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=Install' + $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/ROLE=SPI_AS_NEWFARM' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + + Context 'When using parameter WhatIf' { + It 'Should call the mock with the correct argument string' { + Install-SqlDscServer -WhatIf @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -Exactly -Times 0 -Scope It + } + } + } + + Context 'When specifying optional parameter ' -ForEach @( + @{ + MockParameterName = 'FarmAccount' + MockParameterValue = 'DOMAIN\User' + MockExpectedRegEx = '\/FARMACCOUNT="DOMAIN\\User"' # cspell: disable-line + } + @{ + MockParameterName = 'FarmPassword' + MockParameterValue = ('jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force) # cspell: disable-line + MockExpectedRegEx = '\/FARMPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'Passphrase' + MockParameterValue = ('jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force) # cspell: disable-line + MockExpectedRegEx = '\/PASSPHRASE="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'FarmAdminiPort' # cspell: disable-line + MockParameterValue = '18000' + MockExpectedRegEx = '\/FARMADMINIPORT=18000' # cspell: disable-line + } + ) { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } + + $mockDefaultParameters = @{ + Install = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + Role = 'SPI_AS_NewFarm' + Force = $true + } + } + + BeforeEach { + $installSqlDscServerParameters = $mockDefaultParameters.Clone() + } + + It 'Should call the mock with the correct argument string' { + $installSqlDscServerParameters.$MockParameterName = $MockParameterValue + + Install-SqlDscServer @installSqlDscServerParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly $MockExpectedRegEx + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + + Context 'When specifying sensitive parameter ' -ForEach @( + @{ + MockParameterName = 'FarmPassword' + MockParameterValue = ('jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force) # cspell: disable-line + MockExpectedRegEx = '\/FARMPASSWORD="\*{8}"' # cspell: disable-line + } + @{ + MockParameterName = 'Passphrase' + MockParameterValue = ('jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force) # cspell: disable-line + MockExpectedRegEx = '\/PASSPHRASE="\*{8}"' # cspell: disable-line + } + ) { + BeforeAll { + Mock -CommandName Write-Verbose + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } + + $mockDefaultParameters = @{ + Install = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + Role = 'SPI_AS_NewFarm' + Force = $true + } + } + + BeforeEach { + $installSqlDscServerParameters = $mockDefaultParameters.Clone() + } + + It 'Should obfuscate the value in the verbose string' { + $installSqlDscServerParameters.$MockParameterName = $MockParameterValue + + # Redirect all verbose stream to $null to ge no output from ShouldProcess. + Install-SqlDscServer @installSqlDscServerParameters -Verbose 4> $null + + $mockVerboseMessage = InModuleScope -ScriptBlock { + $script:localizedData.Server_SetupArguments + } + + Should -Invoke -CommandName Write-Verbose -ParameterFilter { + # Only test the command that output the string that should be tested. + $correctMessage = $Message -match $mockVerboseMessage + + # Only test string if it is the correct verbose command + if ($correctMessage) + { + $Message | Should -MatchExactly $MockExpectedRegEx + } + + # Return wether the correct command was called or not. + $correctMessage + } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When role is ''AllFeatures_WithDefaults''' { + Context 'When specifying only mandatory parameters' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } + + $mockDefaultParameters = @{ + Install = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + Role = 'AllFeatures_WithDefaults' + } + } + + Context 'When using parameter Confirm with value $false' { + It 'Should call the mock with the correct argument string' { + Install-SqlDscServer -Confirm:$false @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=Install' + $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/ROLE=ALLFEATURES_WITHDEFAULTS' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + + Context 'When using parameter Force' { + It 'Should call the mock with the correct argument string' { + Install-SqlDscServer -Force @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=Install' + $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line + $ArgumentList | Should -MatchExactly '\/ROLE=ALLFEATURES_WITHDEFAULTS' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + + Context 'When using parameter WhatIf' { + It 'Should call the mock with the correct argument string' { + Install-SqlDscServer -WhatIf @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -Exactly -Times 0 -Scope It + } + } + } + + Context 'When specifying optional parameter ' -ForEach @( + @{ + MockParameterName = 'Features' # cspell: disable-line + MockParameterValue = 'SqlEngine', 'RS' + MockExpectedRegEx = '\/FEATURES=SQLENGINE,RS' # cspell: disable-line + } + @{ + MockParameterName = 'AddCurrentUserAsSqlAdmin' # cspell: disable-line + MockParameterValue = $true + MockExpectedRegEx = '\/ADDCURRENTUSERASSQLADMIN=True' # cspell: disable-line + } + ) { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } + + $mockDefaultParameters = @{ + Install = $true + AcceptLicensingTerms = $true + MediaPath = '\SqlMedia' + Role = 'AllFeatures_WithDefaults' + Force = $true + } + } + + BeforeEach { + $installSqlDscServerParameters = $mockDefaultParameters.Clone() + } + + It 'Should call the mock with the correct argument string' { + $installSqlDscServerParameters.$MockParameterName = $MockParameterValue + + Install-SqlDscServer @installSqlDscServerParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly $MockExpectedRegEx + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } + } +} diff --git a/tests/Unit/Public/Remove-SqlDscNode.Tests.ps1 b/tests/Unit/Public/Remove-SqlDscNode.Tests.ps1 new file mode 100644 index 000000000..a17c00937 --- /dev/null +++ b/tests/Unit/Public/Remove-SqlDscNode.Tests.ps1 @@ -0,0 +1,170 @@ +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '', Justification = 'because ConvertTo-SecureString is used to simplify the tests.')] +param () + +BeforeDiscovery { + try + { + if (-not (Get-Module -Name 'DscResource.Test')) + { + # Assumes dependencies has been resolved, so if this module is not available, run 'noop' task. + if (-not (Get-Module -Name 'DscResource.Test' -ListAvailable)) + { + # Redirect all streams to $null, except the error stream (stream 2) + & "$PSScriptRoot/../../build.ps1" -Tasks 'noop' 2>&1 4>&1 5>&1 6>&1 > $null + } + + # If the dependencies has not been resolved, this will throw an error. + Import-Module -Name 'DscResource.Test' -Force -ErrorAction 'Stop' + } + } + catch [System.IO.FileNotFoundException] + { + throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -ResolveDependency -Tasks build" first.' + } +} + +BeforeAll { + $script:dscModuleName = 'SqlServerDsc' + + Import-Module -Name $script:dscModuleName + + $PSDefaultParameterValues['InModuleScope:ModuleName'] = $script:dscModuleName + $PSDefaultParameterValues['Mock:ModuleName'] = $script:dscModuleName + $PSDefaultParameterValues['Should:ModuleName'] = $script:dscModuleName +} + +AfterAll { + $PSDefaultParameterValues.Remove('InModuleScope:ModuleName') + $PSDefaultParameterValues.Remove('Mock:ModuleName') + $PSDefaultParameterValues.Remove('Should:ModuleName') + + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force +} + +Describe 'Remove-SqlDscNode' -Tag 'Public' { + It 'Should have the correct parameters in parameter set ' -ForEach @( + @{ + MockParameterSetName = '__AllParameterSets' + # cSpell: disable-next + MockExpectedParameters = '[-MediaPath] [-InstanceName] [[-Timeout] ] [-ConfirmIPDependencyChange] [-Force] [-WhatIf] [-Confirm] []' + } + ) { + $result = (Get-Command -Name 'Remove-SqlDscNode').ParameterSets | + Where-Object -FilterScript { + $_.Name -eq $mockParameterSetName + } | + Select-Object -Property @( + @{ + Name = 'ParameterSetName' + Expression = { $_.Name } + }, + @{ + Name = 'ParameterListAsString' + Expression = { $_.ToString() } + } + ) + + $result.ParameterSetName | Should -Be $MockParameterSetName + $result.ParameterListAsString | Should -Be $MockExpectedParameters + } + + Context 'When setup action is ''RemoveNode''' { + BeforeAll { + Mock -CommandName Assert-SetupActionProperties + Mock -CommandName Assert-ElevatedUser + Mock -CommandName Test-Path -ParameterFilter { + $Path -match 'setup\.exe' + } -MockWith { + return $true + } + } + + Context 'When specifying only mandatory parameters' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + $mockDefaultParameters = @{ + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + } + } + + Context 'When using parameter Confirm with value $false' { + It 'Should call the mock with the correct argument string' { + Remove-SqlDscNode -Confirm:$false @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=RemoveNode' + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + + Context 'When using parameter Force' { + It 'Should call the mock with the correct argument string' { + Remove-SqlDscNode -Force @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=RemoveNode' + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + + Context 'When using parameter WhatIf' { + It 'Should call the mock with the correct argument string' { + Remove-SqlDscNode -WhatIf @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -Exactly -Times 0 -Scope It + } + } + } + + Context 'When specifying optional parameter ' -ForEach @( + @{ + MockParameterName = 'ConfirmIPDependencyChange' + MockParameterValue = $true + MockExpectedRegEx = '\/CONFIRMIPDEPENDENCYCHANGE=1' # cspell: disable-line + } + ) { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + $mockDefaultParameters = @{ + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + Force = $true + } + } + + BeforeEach { + $removeSqlDscNodeParameters = $mockDefaultParameters.Clone() + } + + It 'Should call the mock with the correct argument string' { + $removeSqlDscNodeParameters.$MockParameterName = $MockParameterValue + + Remove-SqlDscNode @removeSqlDscNodeParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly $MockExpectedRegEx + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } +} diff --git a/tests/Unit/Public/Repair-SqlDscServer.Tests.ps1 b/tests/Unit/Public/Repair-SqlDscServer.Tests.ps1 new file mode 100644 index 000000000..90a727eca --- /dev/null +++ b/tests/Unit/Public/Repair-SqlDscServer.Tests.ps1 @@ -0,0 +1,225 @@ +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '', Justification = 'because ConvertTo-SecureString is used to simplify the tests.')] +param () + +BeforeDiscovery { + try + { + if (-not (Get-Module -Name 'DscResource.Test')) + { + # Assumes dependencies has been resolved, so if this module is not available, run 'noop' task. + if (-not (Get-Module -Name 'DscResource.Test' -ListAvailable)) + { + # Redirect all streams to $null, except the error stream (stream 2) + & "$PSScriptRoot/../../build.ps1" -Tasks 'noop' 2>&1 4>&1 5>&1 6>&1 > $null + } + + # If the dependencies has not been resolved, this will throw an error. + Import-Module -Name 'DscResource.Test' -Force -ErrorAction 'Stop' + } + } + catch [System.IO.FileNotFoundException] + { + throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -ResolveDependency -Tasks build" first.' + } +} + +BeforeAll { + $script:dscModuleName = 'SqlServerDsc' + + Import-Module -Name $script:dscModuleName + + $PSDefaultParameterValues['InModuleScope:ModuleName'] = $script:dscModuleName + $PSDefaultParameterValues['Mock:ModuleName'] = $script:dscModuleName + $PSDefaultParameterValues['Should:ModuleName'] = $script:dscModuleName +} + +AfterAll { + $PSDefaultParameterValues.Remove('InModuleScope:ModuleName') + $PSDefaultParameterValues.Remove('Mock:ModuleName') + $PSDefaultParameterValues.Remove('Should:ModuleName') + + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force +} + +Describe 'Repair-SqlDscServer' -Tag 'Public' { + It 'Should have the correct parameters in parameter set ' -ForEach @( + @{ + MockParameterSetName = '__AllParameterSets' + # cSpell: disable-next + MockExpectedParameters = '[-MediaPath] [-InstanceName] [-Features] [[-PBEngSvcAccount] ] [[-PBEngSvcPassword] ] [[-PBEngSvcStartupType] ] [[-PBStartPortRange] ] [[-PBEndPortRange] ] [[-Timeout] ] [-Enu] [-PBScaleOut] [-Force] [-WhatIf] [-Confirm] []' + } + ) { + $result = (Get-Command -Name 'Repair-SqlDscServer').ParameterSets | + Where-Object -FilterScript { + $_.Name -eq $mockParameterSetName + } | + Select-Object -Property @( + @{ + Name = 'ParameterSetName' + Expression = { $_.Name } + }, + @{ + Name = 'ParameterListAsString' + Expression = { $_.ToString() } + } + ) + + $result.ParameterSetName | Should -Be $MockParameterSetName + $result.ParameterListAsString | Should -Be $MockExpectedParameters + } + + Context 'When setup action is ''Repair''' { + BeforeAll { + Mock -CommandName Assert-SetupActionProperties + Mock -CommandName Assert-ElevatedUser + Mock -CommandName Test-Path -ParameterFilter { + $Path -match 'setup\.exe' + } -MockWith { + return $true + } + } + + Context 'When specifying only mandatory parameters' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + $mockDefaultParameters = @{ + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + # Intentionally using both upper- and lower-case in the value. + Features = 'SqlEngine' + } + } + + Context 'When using parameter Confirm with value $false' { + It 'Should call the mock with the correct argument string' { + Repair-SqlDscServer -Confirm:$false @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=Repair' + $ArgumentList | Should -MatchExactly '\/FEATURES=SQLENGINE' + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + + Context 'When using parameter Force' { + It 'Should call the mock with the correct argument string' { + Repair-SqlDscServer -Force @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=Repair' + $ArgumentList | Should -MatchExactly '\/FEATURES=SQLENGINE' + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + + Context 'When using parameter WhatIf' { + It 'Should call the mock with the correct argument string' { + Repair-SqlDscServer -WhatIf @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -Exactly -Times 0 -Scope It + } + } + } + + Context 'When specifying optional parameters PBStartPortRange and PBEndPortRange' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + $repairSqlDscServerParameters = @{ + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + # Intentionally using both upper- and lower-case in the value. + Features = 'SqlEngine' + Force = $true + PBStartPortRange = 16450 + PBEndPortRange = 16460 + } + } + + It 'Should call the mock with the correct argument string' { + Repair-SqlDscServer @repairSqlDscServerParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly 'PBPORTRANGE=16450-16460' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + + Context 'When specifying optional parameter ' -ForEach @( + @{ + MockParameterName = 'Enu' + MockParameterValue = $true + MockExpectedRegEx = '\/ENU\s*' + } + @{ + MockParameterName = 'PBEngSvcAccount' + MockParameterValue = 'NT Authority\NETWORK SERVICE' + MockExpectedRegEx = '\/PBENGSVCACCOUNT="NT Authority\\NETWORK SERVICE"' # cspell: disable-line + } + @{ + MockParameterName = 'PBEngSvcPassword' + MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line + MockExpectedRegEx = '\/PBENGSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line + } + @{ + MockParameterName = 'PBEngSvcStartupType' + MockParameterValue = 'Automatic' + MockExpectedRegEx = '\/PBENGSVCSTARTUPTYPE="Automatic"' # cspell: disable-line + } + @{ + MockParameterName = 'PBScaleOut' + MockParameterValue = $true + MockExpectedRegEx = '\/PBSCALEOUT=True' # cspell: disable-line + } + ) { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + $mockDefaultParameters = @{ + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + # Intentionally using both upper- and lower-case in the value. + Features = 'SqlEngine' + Force = $true + } + } + + BeforeEach { + $repairSqlDscServerParameters = $mockDefaultParameters.Clone() + } + + It 'Should call the mock with the correct argument string' { + $repairSqlDscServerParameters.$MockParameterName = $MockParameterValue + + Repair-SqlDscServer @repairSqlDscServerParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly $MockExpectedRegEx + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + } +} diff --git a/tests/Unit/Public/Uninstall-SqlDscServer.Tests.ps1 b/tests/Unit/Public/Uninstall-SqlDscServer.Tests.ps1 new file mode 100644 index 000000000..aa6b8ff7b --- /dev/null +++ b/tests/Unit/Public/Uninstall-SqlDscServer.Tests.ps1 @@ -0,0 +1,137 @@ +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '', Justification = 'because ConvertTo-SecureString is used to simplify the tests.')] +param () + +BeforeDiscovery { + try + { + if (-not (Get-Module -Name 'DscResource.Test')) + { + # Assumes dependencies has been resolved, so if this module is not available, run 'noop' task. + if (-not (Get-Module -Name 'DscResource.Test' -ListAvailable)) + { + # Redirect all streams to $null, except the error stream (stream 2) + & "$PSScriptRoot/../../build.ps1" -Tasks 'noop' 2>&1 4>&1 5>&1 6>&1 > $null + } + + # If the dependencies has not been resolved, this will throw an error. + Import-Module -Name 'DscResource.Test' -Force -ErrorAction 'Stop' + } + } + catch [System.IO.FileNotFoundException] + { + throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -ResolveDependency -Tasks build" first.' + } +} + +BeforeAll { + $script:dscModuleName = 'SqlServerDsc' + + Import-Module -Name $script:dscModuleName + + $PSDefaultParameterValues['InModuleScope:ModuleName'] = $script:dscModuleName + $PSDefaultParameterValues['Mock:ModuleName'] = $script:dscModuleName + $PSDefaultParameterValues['Should:ModuleName'] = $script:dscModuleName +} + +AfterAll { + $PSDefaultParameterValues.Remove('InModuleScope:ModuleName') + $PSDefaultParameterValues.Remove('Mock:ModuleName') + $PSDefaultParameterValues.Remove('Should:ModuleName') + + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force +} + +Describe 'Uninstall-SqlDscServer' -Tag 'Public' { + It 'Should have the correct parameters in parameter set ' -ForEach @( + @{ + MockParameterSetName = '__AllParameterSets' + # cSpell: disable-next + MockExpectedParameters = '[-MediaPath] [-InstanceName] [[-Features] ] [[-Timeout] ] [-Force] [-WhatIf] [-Confirm] []' + } + ) { + $result = (Get-Command -Name 'Uninstall-SqlDscServer').ParameterSets | + Where-Object -FilterScript { + $_.Name -eq $mockParameterSetName + } | + Select-Object -Property @( + @{ + Name = 'ParameterSetName' + Expression = { $_.Name } + }, + @{ + Name = 'ParameterListAsString' + Expression = { $_.ToString() } + } + ) + + $result.ParameterSetName | Should -Be $MockParameterSetName + $result.ParameterListAsString | Should -Be $MockExpectedParameters + } + + Context 'When setup action is ''Uninstall''' { + BeforeAll { + Mock -CommandName Assert-SetupActionProperties + Mock -CommandName Assert-ElevatedUser + Mock -CommandName Test-Path -ParameterFilter { + $Path -match 'setup\.exe' + } -MockWith { + return $true + } + } + + Context 'When specifying only mandatory parameters' { + BeforeAll { + Mock -CommandName Start-SqlSetupProcess -MockWith { + return 0 + } -RemoveParameterValidation 'FilePath' + + $mockDefaultParameters = @{ + MediaPath = '\SqlMedia' + InstanceName = 'INSTANCE' + # Intentionally using both upper- and lower-case. + Features = 'SqlEngine' + } + } + + Context 'When using parameter Confirm with value $false' { + It 'Should call the mock with the correct argument string' { + Uninstall-SqlDscServer -Confirm:$false @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=Uninstall' + $ArgumentList | Should -MatchExactly '\/FEATURES=SQLENGINE' + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + + Context 'When using parameter Force' { + It 'Should call the mock with the correct argument string' { + Uninstall-SqlDscServer -Force @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { + $ArgumentList | Should -MatchExactly '\/ACTION=Uninstall' + $ArgumentList | Should -MatchExactly '\/FEATURES=SQLENGINE' + $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line + + # Return $true if none of the above throw. + $true + } -Exactly -Times 1 -Scope It + } + } + + Context 'When using parameter WhatIf' { + It 'Should call the mock with the correct argument string' { + Uninstall-SqlDscServer -WhatIf @mockDefaultParameters + + Should -Invoke -CommandName Start-SqlSetupProcess -Exactly -Times 0 -Scope It + } + } + } + } +}