AutomatedLabSQL.psm1
#region Install-LabSqlServers function Install-LabSqlServers { # .ExternalHelp AutomatedLab.Help.xml [cmdletBinding()] param ( [int]$InstallationTimeout = $PSCmdlet.MyInvocation.MyCommand.Module.PrivateData.Timeout_Sql2012Installation, [switch]$CreateCheckPoints ) function Write-ArgumentVerbose { param ( $Argument ) Write-ScreenInfo -Type Verbose -Message "Argument '$Argument'" $Argument } Write-LogFunctionEntry $lab = Get-Lab -ErrorAction SilentlyContinue if (-not $lab) { Write-LogFunctionExitWithError -Message 'No lab definition imported, so there is nothing to do. Please use the Import-Lab cmdlet first' return } $machines = Get-LabMachine -Role SQLServer2008, SQLServer2008R2, SQLServer2012, SQLServer2014, SQLServer2016 #The dafault SQL installation in Azure does not give the standard buildin administrators group access. #This section adds the rights. As only the renamed Builtin Admin accout has permissions, Invoke-LabCommand cannot be used. $azureMachines = $machines | Where-Object HostType -eq Azure if ($azureMachines) { Write-ScreenInfo -Message 'Waiting for machines to start up' Start-LabVM -ComputerName $azureMachines -Wait -ProgressIndicator 2 Enable-LabVMRemoting -ComputerName $azureMachines Write-ScreenInfo -Message "Configuring Azure SQL Servers '$($azureMachines -join ', ')'" foreach ($machine in $azureMachines) { Write-ScreenInfo -Type Verbose -Message "Configuring Azure SQL Server '$machine'" Write-ScreenInfo -Message (Get-Date) $sqlCmd = { $query = @" USE [master] GO CREATE LOGIN [BUILTIN\Administrators] FROM WINDOWS WITH DEFAULT_DATABASE=[master], DEFAULT_LANGUAGE=[us_english] GO -- ALTER SERVER ROLE [sysadmin] ADD MEMBER [BUILTIN\Administrators] -- The folloing statement works in SQL 2008 to 2016 EXEC master..sp_addsrvrolemember @loginame = N'BUILTIN\Administrators', @rolename = N'sysadmin' GO "@ if ((Get-PSSnapin -Registered -Name SqlServerCmdletSnapin100 -ErrorAction SilentlyContinue) -and -not (Get-PSSnapin -Name SqlServerCmdletSnapin100 -ErrorAction SilentlyContinue)) { Add-PSSnapin -Name SqlServerCmdletSnapin100 } Invoke-Sqlcmd -Query $query } Invoke-LabCommand -ComputerName $machine -ActivityName SetupSqlPermissions -ScriptBlock $sqlCmd -UseLocalCredential } Write-ScreenInfo -Type Verbose -Message "Finished configuring Azure SQL Servers '$($azureMachines -join ', ')'" } $hypervMachines = @($machines | Where-Object HostType -eq HyperV) if ($hypervMachines) { if ((Get-LabMachine -Role SQLServer2008) -and -not ($lab.Sources.ISOs | Where-Object { $_.Name -eq 'SQLServer2008' })) { Write-LogFunctionExitWithError -Message "There is no ISO image available to install the role 'SQLServer2008'. Please add the required ISO to the lab and name it (warning: CaseSensitive) 'SQLServer2008'" return } if ((Get-LabMachine -Role SQLServer2008R2) -and -not ($lab.Sources.ISOs | Where-Object { $_.Name -eq 'SQLServer2008R2' })) { Write-LogFunctionExitWithError -Message "There is no ISO image available to install the role 'SQLServer2008R2'. Please add the required ISO to the lab and name it (warning: CaseSensitive) 'SQLServer2008R2'" return } if ((Get-LabMachine -Role SQLServer2012) -and -not ($lab.Sources.ISOs | Where-Object { $_.Name -eq 'SQLServer2012' })) { Write-LogFunctionExitWithError -Message "There is no ISO image available to install the role 'SQLServer2012'. Please add the required ISO to the lab and name it (warning: CaseSensitive) 'SQLServer2012'" return } if ((Get-LabMachine -Role SQLServer2014) -and -not ($lab.Sources.ISOs | Where-Object { $_.Name -eq 'SQLServer2014' })) { Write-LogFunctionExitWithError -Message "There is no ISO image available to install the role 'SQLServer2014'. Please add the required ISO to the lab and name it (warning: CaseSensitive) 'SQLServer2014'" return } $parallelInstalls = 4 Write-ScreenInfo -Type Verbose -Message "Parallel installs: $parallelInstalls" $machineIndex = 0 $installBatch = 0 $totalBatches = [math]::Ceiling($hypervMachines.count / $parallelInstalls) do { $jobs = @() $installBatch++ $machinesBatch = $($hypervMachines[$machineIndex..($machineIndex + $parallelInstalls - 1)]) Write-ScreenInfo -Message "Starting machines '$($machinesBatch -join ', ')'" Start-LabVM -ComputerName $machinesBatch $installFrameworkJobs = @() foreach ($m in $machinesBatch) { Write-ScreenInfo -Message "Waiting for machine '$m' to be ready" -NoNewLine -Type Info Wait-LabVM -ComputerName $m -ProgressIndicator 30 Write-ScreenInfo -Message "Starting installation of pre-requisite .Net 3.5 Framework on machine '$m'" -Type Info $installFrameworkJobs = Install-LabWindowsFeature -ComputerName $m -FeatureName Net-Framework-Core -NoDisplay -AsJob -PassThru } Write-ScreenInfo -Message "Waiting for pre-requisite .Net 3.5 Framework to finish installation on machines '$($machinesBatch -join ', ')'" -NoNewLine Wait-LWLabJob -Job $installFrameworkJobs -Timeout 10 -NoDisplay -ProgressIndicator 45 foreach ($machine in $machinesBatch) { $role = $machine.Roles | Where-Object Name -like SQLServer* #Dismounting ISO images to have just one drive later Dismount-LabIsoImage -ComputerName $machine -SupressOutput Mount-LabIsoImage -ComputerName $machine -IsoPath ($lab.Sources.ISOs | Where-Object Name -eq $role.Name).Path -SupressOutput $global:setupArguments = ' /Q /Action=Install /IndicateProgress' ?? { $role.Properties.ContainsKey('Features') } ` { $global:setupArguments += Write-ArgumentVerbose -Argument " /Features=$($role.Properties.Features.Replace(' ', ''))" } ` { $global:setupArguments += Write-ArgumentVerbose -Argument ' /Features=SQL,AS,RS,IS,Tools' } ?? { $role.Properties.ContainsKey('InstanceName') } ` { $global:setupArguments += Write-ArgumentVerbose -Argument " /InstanceName=$($role.Properties.InstanceName)" $script:instanceName = $role.Properties.InstanceName } ` { $global:setupArguments += Write-ArgumentVerbose -Argument ' /InstanceName=MSSQLSERVER' $script:instanceName = 'MSSQLSERVER' } $result = Invoke-LabCommand -ComputerName $machine -ScriptBlock { Get-Service -DisplayName "SQL Server ($instanceName)" -ErrorAction SilentlyContinue } -Variable (Get-Variable -Name instanceName) -PassThru -NoDisplay if ($result) { Write-ScreenInfo -Message "Machine '$machine' already has SQL Server installed with requested instance name '$instanceName'" -Type Warning continue } Invoke-Ternary -Decider {$role.Properties.ContainsKey('Collation')} { $global:setupArguments += Write-ArgumentVerbose -Argument (" /SQLCollation=" + "$($role.Properties.Collation)") } { $global:setupArguments += Write-ArgumentVerbose -Argument ' /SQLCollation=Latin1_General_CI_AS' } Invoke-Ternary -Decider {$role.Properties.ContainsKey('SQLSvcAccount')} { $global:setupArguments += Write-ArgumentVerbose -Argument (" /SQLSvcAccount=" + """$($role.Properties.SQLSvcAccount)""") } { $global:setupArguments += Write-ArgumentVerbose -Argument ' /SQLSvcAccount="NT Authority\Network Service"' } Invoke-Ternary -Decider {$role.Properties.ContainsKey('SQLSvcPassword')} { $global:setupArguments += Write-ArgumentVerbose -Argument (" /SQLSvcPassword=" + """$($role.Properties.SQLSvcPassword)""") } { } Invoke-Ternary -Decider {$role.Properties.ContainsKey('AgtSvcAccount')} { $global:setupArguments += Write-ArgumentVerbose -Argument (" /AgtSvcAccount=" + """$($role.Properties.AgtSvcAccount)""") } { $global:setupArguments += Write-ArgumentVerbose -Argument ' /AgtSvcAccount="NT Authority\System"' } Invoke-Ternary -Decider {$role.Properties.ContainsKey('AgtSvcPassword')} { $global:setupArguments += Write-ArgumentVerbose -Argument (" /AgtSvcPassword=" + """$($role.Properties.AgtSvcPassword)""") } { } Invoke-Ternary -Decider {$role.Properties.ContainsKey('RsSvcAccount')} { $global:setupArguments += Write-ArgumentVerbose -Argument (" /RsSvcAccount=" + """$($role.Properties.RsSvcAccount)""") } { $global:setupArguments += Write-ArgumentVerbose -Argument ' /RsSvcAccount="NT Authority\Network Service"' } Invoke-Ternary -Decider {$role.Properties.ContainsKey('AgtSvcStartupType')} { $global:setupArguments += Write-ArgumentVerbose -Argument (" /AgtSvcStartupType=" + "$($role.Properties.AgtSvcStartupType)") } { $global:setupArguments += Write-ArgumentVerbose -Argument ' /AgtSvcStartupType=Disabled' } Invoke-Ternary -Decider {$role.Properties.ContainsKey('BrowserSvcStartupType')} { $global:setupArguments += Write-ArgumentVerbose -Argument (" /BrowserSvcStartupType=" + "$($role.Properties.BrowserSvcStartupType)") } { $global:setupArguments += Write-ArgumentVerbose -Argument ' /BrowserSvcStartupType=Disabled' } Invoke-Ternary -Decider {$role.Properties.ContainsKey('RsSvcStartupType')} { $global:setupArguments += Write-ArgumentVerbose -Argument (" /RsSvcStartupType=" + "$($role.Properties.RsSvcStartupType)") } { $global:setupArguments += Write-ArgumentVerbose -Argument ' /RsSvcStartupType=Automatic' } Invoke-Ternary -Decider {$role.Properties.ContainsKey('AsSysAdminAccounts')} { $global:setupArguments += Write-ArgumentVerbose -Argument (" /AsSysAdminAccounts=" + "$($role.Properties.AsSysAdminAccounts)") } { $global:setupArguments += Write-ArgumentVerbose -Argument ' /AsSysAdminAccounts="BUILTIN\Administrators"' } Invoke-Ternary -Decider {$role.Properties.ContainsKey('AsSvcAccount')} { $global:setupArguments += Write-ArgumentVerbose -Argument (" /AsSvcAccount=" + "$($role.Properties.AsSvcAccount)") } { $global:setupArguments += Write-ArgumentVerbose -Argument ' /AsSvcAccount="NT Authority\System"' } Invoke-Ternary -Decider {$role.Properties.ContainsKey('IsSvcAccount')} { $global:setupArguments += Write-ArgumentVerbose -Argument (" /IsSvcAccount=" + "$($role.Properties.IsSvcAccount)") } { $global:setupArguments += Write-ArgumentVerbose -Argument ' /IsSvcAccount="NT Authority\System"' } Invoke-Ternary -Decider {$role.Properties.ContainsKey('SQLSysAdminAccounts')} { $global:setupArguments += Write-ArgumentVerbose -Argument (" /SQLSysAdminAccounts=" + "$($role.Properties.SQLSysAdminAccounts)") } { $global:setupArguments += Write-ArgumentVerbose -Argument ' /SQLSysAdminAccounts="BUILTIN\Administrators"' } Invoke-Ternary -Decider {$machine.roles.name -notcontains 'SQLServer2008'} { $global:setupArguments += Write-ArgumentVerbose -Argument (' /IAcceptSQLServerLicenseTerms') } { } $scriptBlock = { Write-Verbose 'Installing SQL Server...' $dvdDrive = '' $startTime = (Get-Date) while (-not $dvdDrive -and (($startTime).AddSeconds(120) -gt (Get-Date))) { Start-Sleep -Seconds 2 $dvdDrive = (Get-WmiObject -Class Win32_CDRomDrive).Drive } if ($dvdDrive) { #Configure App Compatibility for SQL Server 2008. Otherwise a warning pop-up will stop the installation New-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags' -Name '{f2d3ae3a-bfcc-45e2-bf63-178d1db34294}' -Value 4 -PropertyType 'DWORD' New-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags' -Name '{45da5a8b-67b5-4896-86b7-a2e838aee035}' -Value 4 -PropertyType 'DWORD' Set-Content -Path C:\InstallSQLServer.cmd -Value "$dvdDrive\Setup.exe$setupArguments" schtasks.exe /Create /SC ONLOGON /TN InstallSQLServer /TR "cmd /c c:\InstallSQLServer.cmd" schtasks.exe /Run /I /TN "InstallSQLServer" #Wait until installation starts while (schtasks.exe /Query /TN "InstallSQLServer" | Where-Object { $_ -like '*InstallSQLServer*' -and $_ -notlike '*Running*' }) { Start-Sleep -Seconds 1 } #Wait until installation finishes while (schtasks.exe /Query /TN "InstallSQLServer" | Where-Object { $_ -like '*InstallSQLServer*' -and $_ -like '*Running*' }) { Start-Sleep -Seconds 5 } schtasks.exe /Delete /TN "InstallSQLServer" /F if (-not (Test-Path -Path C:\DeployDebug)) { New-Item -ItemType Directory -Path C:\DeployDebug | Out-Null } Move-Item -Path c:\InstallSQLServer.cmd -Destination C:\DeployDebug Write-Verbose 'SQL Installation finished. Restarting machine.' Restart-Computer -Force } else { Write-Error -Message 'Setup.exe in ISO file could not be found (or ISO was not successfully mounted)' } } $param = @{} $param.Add('ComputerName', $machine) $param.Add('UseCredSSP', $true) $param.Add('ActivityName', 'Install SQL Server') $param.Add('AsJob', $True) $param.Add('PassThru', $True) $param.Add('NoDisplay', $True) $param.Add('Scriptblock', $scriptBlock) $param.Add('Variable', (Get-Variable -Name setupArguments)) $jobs += Invoke-LabCommand @param $machineIndex++ } if ($jobs) { Write-ScreenInfo -Type Verbose -Message "Waiting $InstallationTimeout minutes until the installation is finished" Write-ScreenInfo -Message "Waiting for installation of SQL server to complete on machines '$($machinesBatch -join ', ')'" -NoNewline #Start other machines while waiting for SQL server to install $startTime = Get-Date $additionalMachinesToInstall = Get-LabMachine -Role SQLServer2008, SQLServer2008R2, SQLServer2012, SQLServer2014 | Where-Object { (Get-LabVMStatus -ComputerName $_.Name) -eq 'Stopped' } if ($additionalMachinesToInstall) { #Start-LabVM -ComputerName $machinesToPrepare -DelayBetweenComputers 90 -ProgressIndicator 120 -NoNewline -Wait #Save-VM -Name $machinesToPrepare Write-Verbose -Message 'Preparing more machines while waiting for installation to finish' $machinesToPrepare = Get-LabMachine -Role SQLServer2008, SQLServer2008R2, SQLServer2012, SQLServer2014 | Where-Object { (Get-LabVMStatus -ComputerName $_) -eq 'Stopped' } | Select-Object -First 2 while ($startTime.AddMinutes(5) -gt (Get-Date) -and $machinesToPrepare) { Write-Verbose -Message "Starting machines '$($machinesToPrepare -join ', ')'" Start-LabVM -ComputerName $machinesToPrepare $installFrameworkJobs = @() foreach ($m in $machinesToPrepare) { Write-Verbose -Message "Waiting for machine '$m' to be ready" Wait-LabVM -ComputerName $m -ProgressIndicator 120 -NoNewLine Write-Verbose -Message "Starting installation of pre-requisite .Net 3.5 Framework on machine '$m'" $installFrameworkJobs = Install-LabWindowsFeature -ComputerName $m -FeatureName Net-Framework-Core -NoDisplay -AsJob -PassThru } Write-Verbose -Message "Waiting for machines '$($machinesToPrepare -join ', ')' to be finish installation of pre-requisite .Net 3.5 Framework" Wait-LWLabJob -Job $installFrameworkJobs -Timeout 10 -NoDisplay -ProgressIndicator 120 -NoNewLine $machinesToPrepare = Get-LabMachine -Role SQLServer2008, SQLServer2008R2, SQLServer2012, SQLServer2014 | Where-Object { (Get-LabVMStatus -ComputerName $_.Name) -eq 'Stopped' } | Select-Object -First 2 } Write-Verbose -Message "Resuming waiting for SQL Servers batch ($($machinesBatch -join ', ')) to complete installation and restart" } Wait-LabVMRestart -ComputerName $machinesBatch -TimeoutInMinutes $InstallationTimeout -ProgressIndicator 120 Wait-LabVM -ComputerName $machinesBatch -PostDelaySeconds 30 Dismount-LabIsoImage -ComputerName $machinesBatch -SupressOutput if ($installBatch -lt $totalBatches) { Write-ScreenInfo -Message "Saving machines '$($machinesBatch -join ', ')' as these are not needed right now" -Type Warning Save-VM -Name $machinesBatch } } } until ($machineIndex -ge $hypervMachines.Count) $machinesToPrepare = Get-LabMachine -Role SQLServer2008, SQLServer2008R2, SQLServer2012, SQLServer2014 $machinesToPrepare = $machinesToPrepare | Where-Object { (Get-LabVMStatus -ComputerName $_) -ne 'Started' } if ($machinesToPrepare) { Start-LabVM -ComputerName $machinesToPrepare -Wait } Write-ScreenInfo -Message "All SQL Servers '$($hypervMachines -join ', ')' have now been installed and restarted. Waiting for these to be ready." -NoNewline Wait-LabVM -ComputerName $hypervMachines -TimeoutInMinutes 30 -ProgressIndicator 10 if ($CreateCheckPoints) { Checkpoint-LabVM -ComputerName $machines -SnapshotName 'Post SQL Server Installation' } } Write-LogFunctionExit } #endregion Install-LabSqlServers |