├── Common ├── Encode-Html.ps1 ├── Test-Credential.ps1 ├── Build-SPADENodeAddFolder.ps1 ├── Get-Strong-Password.ps1 ├── Execute-Powershell.ps1 ├── Build-SPADEAddNodeStartScript.ps1 ├── Create-Folder.ps1 ├── Get-OsVersion.ps1 ├── Set-PsExecutionPolicy.ps1 ├── Verify-IsAdmin.ps1 ├── Get-PortNumber.ps1 ├── Execute-SqlScalarQuery.ps1 ├── Execute-SqlCommand.ps1 ├── Set-PrivateProfileString.ps1 ├── Ping-Server.ps1 ├── Validate-Strong-Password.ps1 ├── Execute-SqlScriptFiles.ps1 ├── Execute-Sql.ps1 ├── Invoke-WindowsApi.ps1 ├── Execute-ScriptFiles.ps1 ├── Create-ConfigFile.ps1 ├── Write-Log.ps1 └── Copy-InstallFiles.ps1 ├── PreScripts ├── 100-OS-Set-TcpOffload.ps1 ├── 100-OS-Set-WindowsFirewall.ps1 ├── 100-OS-Verify-PageFile.ps1 ├── 100-OS-Set-ProcessorPriority.ps1 ├── 100-OS-Verify-NetFramework.ps1 ├── 200-Service-Check-AncillaryServices.ps1 ├── 100-OS-Verify-AdminGroup.ps1 ├── 100-OS-Set-Dtc.ps1 └── 100-OS-Verify-Drives.ps1 ├── PostScripts ├── 800-Agent-CreateCycleErrorlogJob.sql ├── 800-Agent-CreatePurgeBackupHistoryJob.sql ├── 300-Server-ConfigureNumErrorLogs.sql ├── 300-Server-ResizeDefaultDatabases.sql ├── 800-Agent-Enable-AgentNotifications.ps1 ├── 300-Server-Set-MaxServerMemory.ps1 ├── 997-Management-RemoveBuiltinAdminsLogin.sql ├── 300-Server-RunSpConfigure.sql ├── 998-Management-Restart-SqlService.ps1 ├── 999-Management-Restart-SqlAgentService.ps1 ├── 700-Procedure-CreateCycleErrorlogProc.sql ├── 300-Server-Set-LocalSecurityPolicy.ps1 ├── _300-Server-ConfigureDatabaseMail.sql ├── 300-Server-ConfigureDatabaseMail.ps1 ├── 400-Database-Resize-TempDb.ps1 └── 800-Agent-CreateDatabaseMailCleanupJob.sql ├── Examples ├── PostScripts │ ├── 800-Agent-CreateHallengrenMaintenanceJobs.sql │ ├── 700-Procedure-CreateHallengrenCommandExecute.sql │ ├── 700-Procedure-CreateHallengrenDatabaseBackup.sql │ ├── 700-Procedure-CreateHallengrenIndexOptimize.sql │ ├── 700-Procedure-CreateHallengrenDatabaseIntegrityCheck.sql │ ├── 300-Server-Set-DbaTeam.ps1 │ ├── 345-Server-Set-SqlTcpPort.ps1 │ ├── 900-Management-Add-CmsRegistration.ps1 │ ├── 300-Server-Set-SsisDbInstance.ps1 │ └── 600-Function-CreateHallegrenDtabaseSelect.sql ├── _Template.ps1 ├── _Template.sql └── PreScripts │ └── 200-Service-Verify-MgmtServerAccess.ps1 ├── Templates ├── Configuration_2005.ini ├── Configuration_2014.ini ├── Configuration_2008.ini ├── Configuration_2012.ini ├── Configuration_2008R2.ini └── Configuration_2016.ini ├── Start-SqlSpade.ps1 ├── Tests └── Run-Install.PreOnly.Tests.ps1 ├── README.md ├── appveyor.yml ├── Start.bat ├── Run-Install.config └── Run-Install.ps1 /Common/Encode-Html.ps1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonCarter80/sqlspade/HEAD/Common/Encode-Html.ps1 -------------------------------------------------------------------------------- /Common/Test-Credential.ps1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonCarter80/sqlspade/HEAD/Common/Test-Credential.ps1 -------------------------------------------------------------------------------- /PreScripts/100-OS-Set-TcpOffload.ps1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonCarter80/sqlspade/HEAD/PreScripts/100-OS-Set-TcpOffload.ps1 -------------------------------------------------------------------------------- /PostScripts/800-Agent-CreateCycleErrorlogJob.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonCarter80/sqlspade/HEAD/PostScripts/800-Agent-CreateCycleErrorlogJob.sql -------------------------------------------------------------------------------- /PostScripts/800-Agent-CreatePurgeBackupHistoryJob.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonCarter80/sqlspade/HEAD/PostScripts/800-Agent-CreatePurgeBackupHistoryJob.sql -------------------------------------------------------------------------------- /Common/Build-SPADENodeAddFolder.ps1: -------------------------------------------------------------------------------- 1 | function Build-SPADENodeAddFolder 2 | { 3 | #Create the folder if it doesn't exist 4 | 5 | #Copy the Run-Install.ps1, Config 6 | } -------------------------------------------------------------------------------- /Examples/PostScripts/800-Agent-CreateHallengrenMaintenanceJobs.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonCarter80/sqlspade/HEAD/Examples/PostScripts/800-Agent-CreateHallengrenMaintenanceJobs.sql -------------------------------------------------------------------------------- /Examples/PostScripts/700-Procedure-CreateHallengrenCommandExecute.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonCarter80/sqlspade/HEAD/Examples/PostScripts/700-Procedure-CreateHallengrenCommandExecute.sql -------------------------------------------------------------------------------- /Examples/PostScripts/700-Procedure-CreateHallengrenDatabaseBackup.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonCarter80/sqlspade/HEAD/Examples/PostScripts/700-Procedure-CreateHallengrenDatabaseBackup.sql -------------------------------------------------------------------------------- /Examples/PostScripts/700-Procedure-CreateHallengrenIndexOptimize.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonCarter80/sqlspade/HEAD/Examples/PostScripts/700-Procedure-CreateHallengrenIndexOptimize.sql -------------------------------------------------------------------------------- /Examples/PostScripts/700-Procedure-CreateHallengrenDatabaseIntegrityCheck.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonCarter80/sqlspade/HEAD/Examples/PostScripts/700-Procedure-CreateHallengrenDatabaseIntegrityCheck.sql -------------------------------------------------------------------------------- /Common/Get-Strong-Password.ps1: -------------------------------------------------------------------------------- 1 | Function Get-Strong-Password() { 2 | Param( 3 | [int]$length=12 4 | ) 5 | 6 | $a = [Reflection.Assembly]::LoadWithPartialName("System.Web") 7 | $([System.Web.Security.Membership]::GeneratePassword($length,$length/4)) 8 | } 9 | 10 | 11 | -------------------------------------------------------------------------------- /Common/Execute-Powershell.ps1: -------------------------------------------------------------------------------- 1 | Function Execute-Powershell 2 | { 3 | param 4 | ( 5 | [Parameter(Position=0, Mandatory=$true)] [string] $psScript, 6 | [Parameter(Position=1, Mandatory=$true)] $configParams 7 | ) 8 | 9 | return Invoke-Expression -Command "$psScript `$configParams" 10 | } 11 | -------------------------------------------------------------------------------- /Common/Build-SPADEAddNodeStartScript.ps1: -------------------------------------------------------------------------------- 1 | function Build-SPADEAddNodeStartScript 2 | { 3 | 4 | 5 | #Add dot-sourcing command and declare variables 6 | 7 | #Convert Parameters hashtable to text 8 | 9 | #Convert Overrides hashtable to text 10 | 11 | #Add Run-Install command 12 | 13 | #Save script to folder 14 | 15 | } -------------------------------------------------------------------------------- /Common/Create-Folder.ps1: -------------------------------------------------------------------------------- 1 | Function Create-Folder 2 | { 3 | param 4 | ( 5 | [Parameter(Position=0, Mandatory=$true)] [string] $folderPath 6 | ) 7 | 8 | if(test-path $folderPath) 9 | { 10 | Write-Log -level "Info" -message "$folderPath folder already exists" 11 | } 12 | else 13 | { 14 | New-Item $folderPath -itemType Directory | Out-Null 15 | Write-Log -level "Info" -message "$folderPath folder has been created" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Common/Get-OsVersion.ps1: -------------------------------------------------------------------------------- 1 | Function Get-OsVersion 2 | { 3 | $comp = (Get-WmiObject Win32_OperatingSystem | Select Caption) 4 | 5 | switch -wildcard ($comp.Caption) 6 | { 7 | "Microsoft(R) Windows(R) Server 2003*" {$version = "2003"} 8 | "Microsoft Windows Server 2008*" {$version = "2008"} 9 | "Microsoftr Windows Serverr 2008*" {$version = "2008"} 10 | default {$version = "Unknown"} 11 | } 12 | return $version 13 | } 14 | -------------------------------------------------------------------------------- /Templates/Configuration_2005.ini: -------------------------------------------------------------------------------- 1 | [Options] 2 | PIDKEY="$Key" 3 | INSTALLSQLDIR="C:\Program Files\Microsoft SQL Server" 4 | INSTALLSQLSHAREDDIR="C:\Program Files\Microsoft SQL Server" 5 | INSTALLSQLDATADIR="$SystemDataDir" 6 | ADDLOCAL="$ProductString" 7 | INSTANCENAME="$InstanceName" 8 | SQLBROWSERACCOUNT="$SqlSvcAccount" 9 | SQLACCOUNT="$SqlSvcAccount" 10 | AGTACCOUNT="$AgtSvcAccount" 11 | SQLAUTOSTART=1 12 | AGTAUTOSTART=1 13 | ASAUTOSTART=1 14 | RSAUTOSTART=1 15 | SECURITYMODE="SQL" 16 | SQLCOLLATION="SQL_Latin1_General_CP1_CI_AS" 17 | DISABLENETWORKPROTOCOLS=0 18 | ERRORREPORTING=0 19 | SQMREPORTING=0 20 | ENABLERANU=0 21 | -------------------------------------------------------------------------------- /PostScripts/300-Server-ConfigureNumErrorLogs.sql: -------------------------------------------------------------------------------- 1 | /* 2005,2008,2008R2,2012,2014,2016 */ 2 | 3 | /***************************************************************************************************** 4 | * Script Information 5 | *---------------------------------------------------------------------------------------------------- 6 | * Description: Set the number of error logs to cycle 7 | *****************************************************************************************************/ 8 | 9 | USE [master]; 10 | 11 | EXEC xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'Software\Microsoft\MSSQLServer\MSSQLServer', N'NumErrorLogs', REG_DWORD, 30; -------------------------------------------------------------------------------- /Common/Set-PsExecutionPolicy.ps1: -------------------------------------------------------------------------------- 1 | Function Set-PsExecutionPolicy 2 | { 3 | #In both x64 & x86 versions of PowerShell issue a Set-ExecutionPolicy RemoteSigned 4 | $systemRoot = gc env:systemroot 5 | cd $systemRoot 6 | system32\WindowsPowerShell\v1.0\powershell.exe -NonInteractive -Command {Set-ExecutionPolicy RemoteSigned -force} #64bit 7 | 8 | if(test-path "syswow64\WindowsPowerShell\v1.0") 9 | { 10 | syswow64\WindowsPowerShell\v1.0\powershell.exe -NonInteractive -Command {Set-ExecutionPolicy RemoteSigned -force} #32bit 11 | } 12 | Write-Log -level "Info" -message "PowerShell Execution Policy set to RemoteSigned" 13 | } 14 | -------------------------------------------------------------------------------- /Start-SqlSpade.ps1: -------------------------------------------------------------------------------- 1 | #Clear the output window 2 | cls 3 | 4 | #Dot-Source the root function 5 | $Invocation = (Get-Variable MyInvocation).Value 6 | $path = Join-Path (Split-Path $Invocation.MyCommand.Path) Run-Install.ps1 7 | . $path 8 | 9 | 10 | [hashtable] $ht = New-Object hashtable 11 | [hashtable] $overrides = New-Object hashtable 12 | 13 | #Required Parameters 14 | $ht.Add("SqlVersion", 'SQL2008R2') #Valid values - Sql2005, Sql2008, Sql2008R2, Sql2012, Sql2014, Sql2016 15 | $ht.Add("SqlEdition", 'Standard') #Valid values - Standard, Enterprise, Developer (2008R2+) 16 | $ht.Add("SP", 'SP3') #Valid values - Varies by Version 17 | $ht.Add("DataCenter", 'RDP') #Valid values - Values setup in .Config DataCenter section 18 | 19 | ### TODO Show Overrides Here 20 | Run-Install -Parameters $ht -TemplateOverrides $overrides -Full -Confirm:$false -------------------------------------------------------------------------------- /Common/Verify-IsAdmin.ps1: -------------------------------------------------------------------------------- 1 | Function Verify-IsAdmin 2 | { 3 | $wid=[System.Security.Principal.WindowsIdentity]::GetCurrent() 4 | $prp=new-object System.Security.Principal.WindowsPrincipal($wid) 5 | $adm=[System.Security.Principal.WindowsBuiltInRole]::Administrator 6 | $IsAdmin=$prp.IsInRole($adm) 7 | if ($IsAdmin) 8 | { 9 | #"You are currently running with Administrator priviledges" 10 | #I needed the Out-Null because when -WhatIf or -Confirm are used it produces output to the console 11 | #that interferes with the return value of the function 12 | Write-Log -level "Info" -message ("Script running as admin by {0}" -f $prp.Identity.Name) | Out-Null 13 | return 1 14 | } 15 | else 16 | { 17 | #"Please launch this script again as an administrator" 18 | Write-Log -level "Error" -message "User did not run script as Admin" | Out-Null 19 | return 0 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Tests/Run-Install.PreOnly.Tests.ps1: -------------------------------------------------------------------------------- 1 | Import-Module -Force $PSScriptRoot\..\Run-Install.ps1 2 | 3 | Describe 'Execute PreOnly' { 4 | 5 | Context 'Pre Install Options Only' { 6 | 7 | #Set-StrictMode -Version latest 8 | 9 | It 'Should Not Break' { 10 | $ht.Add("DataCenter", 'Local') 11 | $ht.Add("SqlVersion", 'Sql2012') 12 | $ht.Add("SqlEdition", 'Developer') 13 | 14 | Run-Install -Parameters $ht -TemplateOverrides $overrides -PreOnly 15 | } 16 | 17 | It 'Should Break' { 18 | $ht.Add("SqlVersion", 'Sql2012') 19 | $ht.Add("SqlEdition", 'Developer') 20 | { Run-Install -Parameters $ht -TemplateOverrides $overrides -PreOnly } | Should Throw 21 | } 22 | 23 | BeforeEach { 24 | [hashtable] $ht = New-Object hashtable 25 | [hashtable] $overrides = New-Object hashtable 26 | $ht.Add("FilePath", 'C:\Code\sqlspade\') 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /Common/Get-PortNumber.ps1: -------------------------------------------------------------------------------- 1 | Function Get-PortNumber 2 | { 3 | param( 4 | [hashtable] $configParams 5 | ) 6 | 7 | #Load the script parameters from the config file 8 | [array] $nodes = ($Global:ScriptConfigs | ?{$_.Name -eq "Get-PortNumber"}).SelectNodes("Param") 9 | $portNumber = ($nodes | ? {$_.Name -eq "PortNumber"}).Value 10 | 11 | [array] $missing = $nodes | ? {$_.Value -eq ""} 12 | if ($missing.Count -gt 0) 13 | { 14 | Write-Log -level "Attention" -message "Port number not set - please check the Run-Install.config file for missing configuration items" 15 | } 16 | 17 | #Read the PortNumber or set to default value if missing 18 | $port = $configParams["PortNumber"] 19 | if ($port -eq $null -or $port -eq "") 20 | { 21 | $port = $portNumber 22 | } 23 | 24 | $instanceName = $configParams["InstanceName"] 25 | $sqlVersion = $configParams["SqlVersion"] 26 | 27 | #If for some reason we still don't have a valid port number then use the SQL default 28 | if ($port -isnot [int]) 29 | { 30 | $port = 1433 31 | } 32 | 33 | return $port 34 | } 35 | -------------------------------------------------------------------------------- /Common/Execute-SqlScalarQuery.ps1: -------------------------------------------------------------------------------- 1 | Function Execute-SqlScalarQuery 2 | { 3 | param 4 | ( 5 | [Parameter(Position=0, Mandatory=$true)] [string] $sqlScript, 6 | [Parameter(Position=1, Mandatory=$true)] [AllowEmptyString()] [string] $sqlInstance, 7 | [Parameter(Position=2, Mandatory=$false)] [string] $serverName = $Global:LogicalComputerName, 8 | [Parameter(Position=3, Mandatory=$false)] [string] $databaseName = "master" 9 | ) 10 | 11 | $conn = new-Object System.Data.SqlClient.SqlConnection("Server=$serverName\$sqlInstance;DataBase=$databaseName;Integrated Security=SSPI;") 12 | $sqlCommand = New-Object System.Data.SqlClient.SqlCommand 13 | $sqlCommand.Connection = $conn 14 | $sqlCommand.CommandType = [System.Data.CommandType]'Text' 15 | $sqlCommand.CommandTimeout = 300 16 | 17 | try 18 | { 19 | $conn.Open() | out-null 20 | 21 | $sqlCommand.CommandText = $sqlScript 22 | [int] $retVal = $sqlCommand.ExecuteScalar() 23 | 24 | $strResult = "Command(s) completed successfully." 25 | } 26 | catch [System.Exception] 27 | { 28 | $strResult = $_ 29 | } 30 | finally 31 | { 32 | if ($conn.State -ne [System.Data.ConnectionState]'Closed') 33 | { 34 | $conn.Close() 35 | } 36 | } 37 | 38 | return $retVal 39 | } 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | SPADE simplifies the process of standing up a new SQL Server instance by applying standard Operating System pre-configurations, Installing SQL Server and then applying post-configurations and creating standard objects. 3 | 4 | SPADE is a tool that is designed to speed up your standard deploymets of SQL Server. You may be saying "But I can already do an unsattended install"...but that's not all that's involved in most server builds. There are Operating System configurations like Microsoft Distrubuted Transaction Coordinator (MSDTC), Local Security Policy and others. I'm sure that you also have standard SQL objects that need to be deployed like Stored Procedures, Agent Jobs, Operators, etc. All of this can be done by SPADE automatically by running 1 simple PowerShell script. 5 | 6 | Every organization is different, so this tool has been built so that it can easily be customized without requiring you to be a master of PowerShell. A simple XML configuration file defines the options for your standard build. For those non-standard, or "one-off" builds, the script has been defined so that you can change things for a single build without having to change the configuration file. 7 | 8 | The current release supports standalone installs of SQL 2005, 2008, 2008R2, 2012, 2014, and 2016 (2017 will be supported soon). 9 | -------------------------------------------------------------------------------- /Common/Execute-SqlCommand.ps1: -------------------------------------------------------------------------------- 1 | Function Execute-SqlCommand 2 | { 3 | param 4 | ( 5 | [Parameter(Position=0, Mandatory=$true)] [string] $sqlScript, 6 | [Parameter(Position=1, Mandatory=$true)] [AllowEmptyString()] [string] $sqlInstance, 7 | [Parameter(Position=2, Mandatory=$false)] [string] $serverName = $Global:LogicalComputerName, 8 | [Parameter(Position=3, Mandatory=$false)] [string] $databaseName = "master" 9 | ) 10 | 11 | $conn = new-Object System.Data.SqlClient.SqlConnection("Server=$serverName\$sqlInstance;DataBase=$databaseName;Integrated Security=SSPI;") 12 | $sqlCommand = New-Object System.Data.SqlClient.SqlCommand 13 | $sqlCommand.Connection = $conn 14 | $sqlCommand.CommandType = [System.Data.CommandType]'Text' 15 | $sqlCommand.CommandTimeout = 300 16 | 17 | try 18 | { 19 | $conn.Open() | out-null 20 | 21 | $sqlCommand.CommandText = $sqlScript 22 | $sqlCommand.ExecuteNonQuery() | Out-Null 23 | 24 | $strResult = "Command(s) completed successfully." 25 | } 26 | catch [System.Exception] 27 | { 28 | $strResult = $_ 29 | } 30 | finally 31 | { 32 | if ($conn.State -ne [System.Data.ConnectionState]'Closed') 33 | { 34 | $conn.Close() 35 | } 36 | } 37 | 38 | #Write-Log -level "Info" -message "Execute-SqlCommand [DEBUG]: $strResult" 39 | return $strResult 40 | } 41 | -------------------------------------------------------------------------------- /Common/Set-PrivateProfileString.ps1: -------------------------------------------------------------------------------- 1 | ############################################################################## 2 | ## 3 | ## Set-PrivateProfileString.ps1 4 | ## by Lee Holmes (http://www.leeholmes.com/guide) 5 | ## 6 | ## Set an entry from an INI file. 7 | ## 8 | ## ie: 9 | ## 10 | ## PS >copy C:\winnt\system32\ntfrsrep.ini c:\temp\ 11 | ## PS >Set-PrivateProfileString.ps1 C:\temp\ntfrsrep.ini text ` 12 | ## >> DEV_CTR_24_009_HELP "New Value" 13 | ## >> 14 | ## PS >Get-PrivateProfileString.ps1 C:\temp\ntfrsrep.ini text DEV_CTR_24_009_HELP 15 | ## New Value 16 | ## PS >Set-PrivateProfileString.ps1 C:\temp\ntfrsrep.ini NEW_SECTION ` 17 | ## >> NewItem "Entirely New Value" 18 | ## >> 19 | ## PS >Get-PrivateProfileString.ps1 C:\temp\ntfrsrep.ini NEW_SECTION NewItem 20 | ## Entirely New Value 21 | ## 22 | ############################################################################## 23 | Function Set-PrivateProfileString 24 | { 25 | param( 26 | $file, 27 | $category, 28 | $key, 29 | $value) 30 | 31 | ## Prepare the parameter types and parameter values for the Invoke-WindowsApi script 32 | $parameterTypes = [string], [string], [string], [string] 33 | $parameters = [string] $category, [string] $key, [string] $value, [string] $file 34 | 35 | ## Invoke the API 36 | [void] (Invoke-WindowsApi "kernel32.dll" ([UInt32]) "WritePrivateProfileString" $parameterTypes $parameters) 37 | } 38 | -------------------------------------------------------------------------------- /Templates/Configuration_2014.ini: -------------------------------------------------------------------------------- 1 | [Options] 2 | PID="$Key" 3 | INSTANCEID="$InstanceId" 4 | ACTION="Install" 5 | FEATURES="$ProductString" 6 | HELP="False" 7 | INDICATEPROGRESS="True" 8 | IACCEPTSQLSERVERLICENSETERMS="True" 9 | QUIET="True" 10 | QUIETSIMPLE="False" 11 | X86="$X86" 12 | PCUSOURCE="$SPSource" 13 | CUSOURCE="$CUSource" 14 | ERRORREPORTING="False" 15 | INSTALLSHAREDDIR="C:\Program Files\Microsoft SQL Server" 16 | INSTALLSHAREDWOWDIR="C:\Program Files (x86)\Microsoft SQL Server" 17 | INSTANCEDIR="C:\Program Files\Microsoft SQL Server" 18 | SQMREPORTING="False" 19 | INSTANCENAME="$InstanceName" 20 | AGTSVCACCOUNT="$AgtSvcAccount" 21 | AGTSVCSTARTUPTYPE="Automatic" 22 | ISSVCSTARTUPTYPE="Manual" 23 | ISSVCACCOUNT="$IsSvcAccount" 24 | ASSVCSTARTUPTYPE="Automatic" 25 | ASCOLLATION="Latin1_General_CI_AS" 26 | ASDATADIR="Data" 27 | ASLOGDIR="Log" 28 | ASBACKUPDIR="Backup" 29 | ASTEMPDIR="Temp" 30 | ASCONFIGDIR="Config" 31 | ASPROVIDERMSOLAP="1" 32 | SQLSVCSTARTUPTYPE="Automatic" 33 | FILESTREAMLEVEL="0" 34 | ENABLERANU="False" 35 | SQLCOLLATION="SQL_Latin1_General_CP1_CI_AS" 36 | SQLSVCACCOUNT="$SqlSvcAccount" 37 | SQLSYSADMINACCOUNTS="$SqlSysAdminAccounts" 38 | SECURITYMODE="SQL" 39 | INSTALLSQLDATADIR="$SystemDataDir" 40 | SQLBACKUPDIR="" 41 | SQLUSERDBDIR="" 42 | SQLUSERDBLOGDIR="" 43 | ADDCURRENTUSERASSQLADMIN="False" 44 | TCPENABLED="1" 45 | NPENABLED="1" 46 | BROWSERSVCSTARTUPTYPE="Automatic" 47 | RSSVCSTARTUPTYPE="Automatic" 48 | RSINSTALLMODE="FilesOnlyMode" 49 | FTSVCACCOUNT="$FTSvcAccount" 50 | -------------------------------------------------------------------------------- /Templates/Configuration_2008.ini: -------------------------------------------------------------------------------- 1 | [SQLSERVER2008] 2 | PID="$Key" 3 | INSTANCEID="$InstanceId" 4 | ACTION="Install" 5 | FEATURES="$ProductString" 6 | HELP="False" 7 | INDICATEPROGRESS="True" 8 | QUIET="True" 9 | QUIETSIMPLE="False" 10 | X86="$X86" 11 | PCUSOURCE="$SPSource" 12 | CUSOURCE="$CUSource" 13 | ERRORREPORTING="False" 14 | INSTALLSHAREDDIR="C:\Program Files\Microsoft SQL Server" 15 | INSTALLSHAREDWOWDIR="C:\Program Files (x86)\Microsoft SQL Server" 16 | INSTANCEDIR="C:\Program Files\Microsoft SQL Server" 17 | SQMREPORTING="False" 18 | INSTANCENAME="$InstanceName" 19 | AGTSVCACCOUNT="$AgtSvcAccount" 20 | AGTSVCSTARTUPTYPE="Automatic" 21 | ISSVCSTARTUPTYPE="Manual" 22 | ISSVCACCOUNT="$IsSvcAccount" 23 | ASSVCSTARTUPTYPE="Automatic" 24 | ASCOLLATION="Latin1_General_CI_AS" 25 | ASDATADIR="Data" 26 | ASLOGDIR="Log" 27 | ASBACKUPDIR="Backup" 28 | ASTEMPDIR="Temp" 29 | ASCONFIGDIR="Config" 30 | ASPROVIDERMSOLAP="1" 31 | SQLSVCSTARTUPTYPE="Automatic" 32 | FILESTREAMLEVEL="0" 33 | ENABLERANU="False" 34 | SQLCOLLATION="SQL_Latin1_General_CP1_CI_AS" 35 | SQLSVCACCOUNT="$SqlSvcAccount" 36 | SQLSYSADMINACCOUNTS="$SqlSysAdminAccounts" 37 | SECURITYMODE="SQL" 38 | INSTALLSQLDATADIR="$SystemDataDir" 39 | SQLBACKUPDIR="" 40 | SQLUSERDBDIR="" 41 | SQLUSERDBLOGDIR="" 42 | ADDCURRENTUSERASSQLADMIN="False" 43 | TCPENABLED="1" 44 | NPENABLED="1" 45 | BROWSERSVCSTARTUPTYPE="Automatic" 46 | RSSVCSTARTUPTYPE="Automatic" 47 | RSINSTALLMODE="FilesOnlyMode" 48 | FTSVCACCOUNT="$FTSvcAccount" 49 | -------------------------------------------------------------------------------- /Templates/Configuration_2012.ini: -------------------------------------------------------------------------------- 1 | [Options] 2 | PID="$Key" 3 | INSTANCEID="$InstanceId" 4 | ACTION="Install" 5 | FEATURES="$ProductString" 6 | HELP="False" 7 | INDICATEPROGRESS="True" 8 | IACCEPTSQLSERVERLICENSETERMS="True" 9 | QUIET="True" 10 | QUIETSIMPLE="False" 11 | X86="$X86" 12 | PCUSOURCE="$SPSource" 13 | CUSOURCE="$CUSource" 14 | ERRORREPORTING="False" 15 | INSTALLSHAREDDIR="C:\Program Files\Microsoft SQL Server" 16 | INSTALLSHAREDWOWDIR="C:\Program Files (x86)\Microsoft SQL Server" 17 | INSTANCEDIR="C:\Program Files\Microsoft SQL Server" 18 | SQMREPORTING="False" 19 | INSTANCENAME="$InstanceName" 20 | AGTSVCACCOUNT="$AgtSvcAccount" 21 | AGTSVCSTARTUPTYPE="Automatic" 22 | ISSVCSTARTUPTYPE="Manual" 23 | ISSVCACCOUNT="$IsSvcAccount" 24 | ASSVCSTARTUPTYPE="Automatic" 25 | ASCOLLATION="Latin1_General_CI_AS" 26 | ASDATADIR="Data" 27 | ASLOGDIR="Log" 28 | ASBACKUPDIR="Backup" 29 | ASTEMPDIR="Temp" 30 | ASCONFIGDIR="Config" 31 | ASPROVIDERMSOLAP="1" 32 | SQLSVCSTARTUPTYPE="Automatic" 33 | FILESTREAMLEVEL="0" 34 | ENABLERANU="False" 35 | SQLCOLLATION="SQL_Latin1_General_CP1_CI_AS" 36 | SQLSVCACCOUNT="$SqlSvcAccount" 37 | SQLSYSADMINACCOUNTS="$SqlSysAdminAccounts" 38 | SECURITYMODE="SQL" 39 | INSTALLSQLDATADIR="$SystemDataDir" 40 | SQLBACKUPDIR="" 41 | SQLUSERDBDIR="" 42 | SQLUSERDBLOGDIR="" 43 | ADDCURRENTUSERASSQLADMIN="False" 44 | TCPENABLED="1" 45 | NPENABLED="1" 46 | BROWSERSVCSTARTUPTYPE="Automatic" 47 | RSSVCSTARTUPTYPE="Automatic" 48 | RSINSTALLMODE="FilesOnlyMode" 49 | FTSVCACCOUNT="$FTSvcAccount" 50 | -------------------------------------------------------------------------------- /Templates/Configuration_2008R2.ini: -------------------------------------------------------------------------------- 1 | [SQLSERVER2008] 2 | PID="$Key" 3 | INSTANCEID="$InstanceId" 4 | ACTION="Install" 5 | FEATURES="$ProductString" 6 | HELP="False" 7 | INDICATEPROGRESS="True" 8 | IACCEPTSQLSERVERLICENSETERMS="True" 9 | QUIET="True" 10 | QUIETSIMPLE="False" 11 | X86="$X86" 12 | PCUSOURCE="$SPSource" 13 | CUSOURCE="$CUSource" 14 | ERRORREPORTING="False" 15 | INSTALLSHAREDDIR="C:\Program Files\Microsoft SQL Server" 16 | INSTALLSHAREDWOWDIR="C:\Program Files (x86)\Microsoft SQL Server" 17 | INSTANCEDIR="C:\Program Files\Microsoft SQL Server" 18 | SQMREPORTING="False" 19 | INSTANCENAME="$InstanceName" 20 | AGTSVCACCOUNT="$AgtSvcAccount" 21 | AGTSVCSTARTUPTYPE="Automatic" 22 | ISSVCSTARTUPTYPE="Manual" 23 | ISSVCACCOUNT="$IsSvcAccount" 24 | ASSVCSTARTUPTYPE="Automatic" 25 | ASCOLLATION="Latin1_General_CI_AS" 26 | ASDATADIR="Data" 27 | ASLOGDIR="Log" 28 | ASBACKUPDIR="Backup" 29 | ASTEMPDIR="Temp" 30 | ASCONFIGDIR="Config" 31 | ASPROVIDERMSOLAP="1" 32 | SQLSVCSTARTUPTYPE="Automatic" 33 | FILESTREAMLEVEL="0" 34 | ENABLERANU="False" 35 | SQLCOLLATION="SQL_Latin1_General_CP1_CI_AS" 36 | SQLSVCACCOUNT="$SqlSvcAccount" 37 | SQLSYSADMINACCOUNTS="$SqlSysAdminAccounts" 38 | SECURITYMODE="SQL" 39 | INSTALLSQLDATADIR="$SystemDataDir" 40 | SQLBACKUPDIR="" 41 | SQLUSERDBDIR="" 42 | SQLUSERDBLOGDIR="" 43 | ADDCURRENTUSERASSQLADMIN="False" 44 | TCPENABLED="1" 45 | NPENABLED="1" 46 | BROWSERSVCSTARTUPTYPE="Automatic" 47 | RSSVCSTARTUPTYPE="Automatic" 48 | RSINSTALLMODE="FilesOnlyMode" 49 | FTSVCACCOUNT="$FTSvcAccount" 50 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | install: 2 | - cinst pester 3 | 4 | build: false 5 | 6 | test_script: 7 | - ps: $spadeBase = "C:\Code\sqlSpade\" 8 | - ps: $sqlBase = "C:\Code\MSSQL\" 9 | - ps: Copy-Item "." $spadeBase -Force -Recurse 10 | - ps: $versions = @( 11 | "SQL2005\X86","SQL2005\X64","SQL2005\SP","SQL2005\SP\SP1","SQL2005\SP\SP2","SQL2005\SP\SP3","SQL2005\SP\SP4","SQL2008\Enterprise", 12 | "SQL2008\Standard","SQL2008\Developer","SQL2008\SP","SQL2008\SP\SP1","SQL2008\SP\SP2","SQL2008\SP\SP3","SQL2008\SP\SP4","SQL2008R2\Enterprise", 13 | "SQL2008R2\Standard","SQL2008R2\Developer","SQL2008R2\SP","SQL2008R2\SP\SP1","SQL2008R2\SP\SP2","SQL2008R2\SP\SP3", 14 | "SQL2012\Enterprise","SQL2012\Standard","SQL2012\Developer","SQL2012\SP","SQL2012\SP\SP1","SQL2012\SP\SP2","SQL2012\SP\SP3", 15 | "SQL2014\Enterprise","SQL2014\Standard","SQL2014\Developer","SQL2014\SP","SQL2014\SP\SP1","SQL2014\SP\SP2", 16 | "SQL2016\Enterprise","SQL2016\Standard","SQL2016\Developer","SQL2016\SP" 17 | ) 18 | - ps: $versions | % { if (!(Test-Path -Path (Join-Path $sqlBase $_))) { $a = New-Item -Type Directory -Path (Join-Path $sqlBase $_)}} 19 | - ps: $res = Invoke-Pester -Path (Join-Path $spadeBase "Tests") -OutputFormat NUnitXml -OutputFile (Join-Path $spadeBase TestsResults.xml) -PassThru 20 | - ps: Get-ChildItem (Join-Path $spadeBase "Logs\*.*") | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name } 21 | - ps: (New-Object 'System.Net.WebClient').UploadFile("https://ci.appveyor.com/api/testresults/nunit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path (Join-Path $spadeBase TestsResults.xml))) 22 | - ps: if ($res.FailedCount -gt 0) { throw "$($res.FailedCount) tests failed."} 23 | 24 | -------------------------------------------------------------------------------- /Common/Ping-Server.ps1: -------------------------------------------------------------------------------- 1 | Function Ping-Server 2 | { 3 | param 4 | ( 5 | [string] $serverName=$(throw "Server Name is required") 6 | ) 7 | #This uses the ping command line utility and parses the results 8 | # [array] $pingResults = $(ping $serverName) 9 | # [string] $formattedResults = "Ping results for $serverName \n " 10 | # foreach($line in $pingResults) 11 | # { 12 | # $formattedResults += ($line + " \n ") 13 | # } 14 | # 15 | # return $formattedResults 16 | 17 | #This uses WMI and requires no parsing 18 | $statusCode = (get-wmiobject win32_pingstatus -Filter "address='$serverName'").StatusCode 19 | 20 | $result = "" 21 | switch ($statusCode) 22 | { 23 | 0 {$result = "Success"} 24 | 11001 {$result = "Buffer Too Small"} 25 | 11002 {$result = "Destination Net Unreachable"} 26 | 11003 {$result = "Destination Host Unreachable"} 27 | 11004 {$result = "Destination Protocol Unreachable"} 28 | 11005 {$result = "Destination Port Unreachable"} 29 | 11006 {$result = "No Resources"} 30 | 11007 {$result = "Bad Option"} 31 | 11008 {$result = "Hardware Error"} 32 | 11009 {$result = "Packet Too Big"} 33 | 11010 {$result = "Request Timed Out"} 34 | 11011 {$result = "Bad Request"} 35 | 11012 {$result = "Bad Route"} 36 | 11013 {$result = "TimeToLive Expired Transit"} 37 | 11014 {$result = "TimeToLive Expired Reassembly"} 38 | 11015 {$result = "Parameter Problem"} 39 | 11016 {$result = "Source Quench"} 40 | 11017 {$result = "Option Too Big"} 41 | 11018 {$result = "Bad Destination"} 42 | 11032 {$result = "Negotiating IPSEC"} 43 | 11050 {$result = "General Failure"} 44 | default {$result = "Unknown Failure"} 45 | } 46 | 47 | return $result 48 | } 49 | -------------------------------------------------------------------------------- /Templates/Configuration_2016.ini: -------------------------------------------------------------------------------- 1 | [Options] 2 | PID="$Key" 3 | INSTANCEID="$InstanceId" 4 | ACTION="Install" 5 | FEATURES="$ProductString" 6 | HELP="False" 7 | INDICATEPROGRESS="True" 8 | IACCEPTSQLSERVERLICENSETERMS="True" 9 | QUIET="True" 10 | QUIETSIMPLE="False" 11 | X86="$X86" 12 | PCUSOURCE="$SPSource" 13 | CUSOURCE="$CUSource" 14 | ERRORREPORTING="False" 15 | INSTALLSHAREDDIR="C:\Program Files\Microsoft SQL Server" 16 | INSTALLSHAREDWOWDIR="C:\Program Files (x86)\Microsoft SQL Server" 17 | INSTANCEDIR="C:\Program Files\Microsoft SQL Server" 18 | SQMREPORTING="False" 19 | INSTANCENAME="$InstanceName" 20 | AGTSVCACCOUNT="$AgtSvcAccount" 21 | AGTSVCSTARTUPTYPE="Automatic" 22 | ISSVCSTARTUPTYPE="Manual" 23 | ISSVCACCOUNT="$IsSvcAccount" 24 | ASSVCSTARTUPTYPE="Automatic" 25 | ASCOLLATION="Latin1_General_CI_AS" 26 | ASDATADIR="Data" 27 | ASLOGDIR="Log" 28 | ASSERVERMODE="TABULAR" 29 | ASBACKUPDIR="Backup" 30 | ASTEMPDIR="Temp" 31 | ASCONFIGDIR="Config" 32 | ASPROVIDERMSOLAP="1" 33 | SQLSVCSTARTUPTYPE="Automatic" 34 | FILESTREAMLEVEL="0" 35 | ENABLERANU="False" 36 | SQLCOLLATION="SQL_Latin1_General_CP1_CI_AS" 37 | SQLSVCACCOUNT="$SqlSvcAccount" 38 | SQLSYSADMINACCOUNTS="$SqlSysAdminAccounts" 39 | SECURITYMODE="SQL" 40 | SQLTEMPDBDIR="C:\TempDB\" 41 | SQLTEMPDBLOGDIR="C:\TempDB\" 42 | SQLTEMPDBFILESIZE="1024" 43 | SQLTEMPDBFILEGROWTH="1024" 44 | SQLTEMPDBLOGFILESIZE="1024" 45 | SQLTEMPDBLOGFILEGROWTH="1024" 46 | INSTALLSQLDATADIR="$SystemDataDir" 47 | SQLSVCINSTANTFILEINIT="True" 48 | SQLBACKUPDIR="" 49 | SQLUSERDBDIR="" 50 | SQLUSERDBLOGDIR="" 51 | ADDCURRENTUSERASSQLADMIN="False" 52 | TCPENABLED="1" 53 | NPENABLED="1" 54 | BROWSERSVCSTARTUPTYPE="Automatic" 55 | RSSVCSTARTUPTYPE="Automatic" 56 | RSINSTALLMODE="FilesOnlyMode" 57 | FTSVCACCOUNT="$FTSvcAccount" 58 | PBENGSVCSTARTUPTYPE="Automatic" 59 | PBPORTRANGE="16450-16460" 60 | PBSCALEOUT="False" 61 | 62 | 63 | -------------------------------------------------------------------------------- /Common/Validate-Strong-Password.ps1: -------------------------------------------------------------------------------- 1 | 2 | Function Validate-Strong-Password { 3 | 4 | param( 5 | [string]$Password = $(throw "Please specify password"), 6 | [int]$minLength=8, 7 | [int]$numUpper = 1, 8 | [int]$numLower = 1, 9 | [int]$numNumbers = 1, 10 | [int]$numSpecial = 1, 11 | [int]$minGroups = 3 12 | ) 13 | 14 | 15 | $upper = [regex]"[A-Z]" 16 | $lower = [regex]"[a-z]" 17 | $number = [regex]"[0-9]" 18 | $special = [regex]"[^a-zA-Z0-9]" 19 | $groups = 0 20 | # Check the length. 21 | 22 | if ($Password.length -lt $minLength) 23 | { 24 | Write-Log -Level Debug "Password does not meet Minimum Length Requirement of $minLength" 25 | return $false; 26 | } 27 | 28 | 29 | # Check for minimum number of occurrences. 30 | if ($upper.Matches($Password).Count -lt $numUpper ) 31 | { 32 | Write-Log -Level Debug "Password does not meet Upper Case Letter Requirement of $numUpper" 33 | return $false; 34 | } 35 | else 36 | { 37 | $group += 1 38 | } 39 | 40 | if ($lower.Matches($Password).Count -lt $numLower ) 41 | { 42 | Write-Log -Level Debug "Password does not meet Lower Case Letter Requirement of $numLower" 43 | return $false; 44 | } 45 | else 46 | { 47 | $group += 1 48 | } 49 | 50 | if ($number.Matches($Password).Count -lt $numNumbers ) 51 | { 52 | Write-Log -Level Debug "Password does not meet Numbers Requirement of $numNumbers" 53 | return $false; 54 | } 55 | else 56 | { 57 | $group += 1 58 | } 59 | 60 | if ($special.Matches($Password).Count -lt $numSpecial ) 61 | { 62 | Write-Log -Level Debug "Password does not meet Special Character Requirement of $numSpecial" 63 | return $false; 64 | } 65 | else 66 | { 67 | $group += 1 68 | } 69 | 70 | if ($group -lt $minGroups) 71 | { 72 | Write-Log -Level Debug "Password does not meet Matches Per Group of $minGroups" 73 | return $false; 74 | } 75 | 76 | 77 | return $true 78 | } 79 | -------------------------------------------------------------------------------- /Examples/_Template.ps1: -------------------------------------------------------------------------------- 1 | #/* 2005,2008,2008R2,2012,2014,2016 */ 2 | 3 | ############################################################################################################### 4 | # PowerShell Script Template 5 | ############################################################################################################### 6 | # For use with the Auto-Install process 7 | # 8 | # When called, the script will recieve a single hashtable object passed as a parameter. 9 | # This object contains the following items by default: 10 | # SqlVersion - version being installed 11 | # SqlEdition - edition being installed 12 | # ServiceAccount - service account being used 13 | # ServicePassword - password for service account 14 | # SysAdminPassword - SA password 15 | # FilePath - working folder for auto-install 16 | # DataCenter - data center the server is located in 17 | # DbaTeam - responsible DBA team 18 | # InstanceName - SQL instance name 19 | # ProductStringName - product features being installed 20 | # Environment - environment for server (dev, qa, bcp, prod) 21 | # 22 | # Additional items can be added by using the Add method on the $ht object in the Start-SqlSpade.ps1 script 23 | # 24 | # You can access any of these items using the following syntax: $configParams["SqlVersion"] 25 | # 26 | # This script should be placed in the appropriate scripts folder and will be automatically called during the 27 | # auto-install process. 28 | # 29 | # The script should be saved using the following naming convention: 30 | # 31 | # Pre Install Script (Save to PreScripts Folder) - 32 | # ------------------------------------------------- 33 | # Pre-100-OS-[ScriptName].ps1 34 | # Pre-200-Service-[ScriptName].ps1 35 | # 36 | # Post Install Script (Save to SQLScripts Folder) - 37 | # ------------------------------------------------- 38 | # 100-OS-[ScriptName].ps1 39 | # 200-Service-[ScriptName].ps1 40 | # 300-Server-[ScriptName].ps1 41 | # 400-Database-[ScriptName].ps1 42 | # 500-Table-[ScriptName].ps1 43 | # 600-View-[ScriptName].ps1 44 | # 700-Procedure-[ScriptName].ps1 45 | # 800-Agent-[ScriptName].ps1 46 | # 900-Management-ScriptName.ps1 47 | ############################################################################################################### 48 | 49 | $configParams = $args[0] -------------------------------------------------------------------------------- /PreScripts/100-OS-Set-WindowsFirewall.ps1: -------------------------------------------------------------------------------- 1 | #/* 2005,2008,2008R2,2012,2014,2016 */ 2 | 3 | ############################################################################################################### 4 | # PowerShell Script Template 5 | ############################################################################################################### 6 | # For use with the Auto-Install process 7 | # 8 | # When called, the script will recieve a single hashtable object passed as a 9 | # parameter. This object contains the following properties: 10 | # $params["SqlVersion"] - Version of SQL being installed (SQL2005, SQL2008, SQL2008R2) 11 | # $params["SqlEdition"] - Edition of SQL being installed (Standard, Enterprise) 12 | # $params["ProcessorArch"] - CPU Architecture (x86, X64) 13 | # $params["InstanceName"] - The name that the instance will be installed with (sqldev1) 14 | # $params["ServiceAccount"] - The name of the account to run the services under (domain\user) 15 | # $params["FilePath"] - path to save the configuration ini file 16 | # 17 | # To add additional parameters - add them to the hashtable passed to the Run-Install function 18 | # 19 | # This script should be placed in the appropriate scripts folder and will be automatically called during the 20 | # auto-install process. 21 | # 22 | # The script should be saved using the following naming convention: 23 | # 24 | # Pre Install Script (Save to PreScripts Folder) - 25 | # ------------------------------------------------- 26 | # Pre-100-OS-[ScriptName].ps1 27 | # Pre-200-Service-[ScriptName].ps1 28 | # 29 | # Post Install Script (Save to SQLScripts Folder) - 30 | # ------------------------------------------------- 31 | # 100-OS-[ScriptName].ps1 32 | # 200-Service-[ScriptName].ps1 33 | # 300-Server-[ScriptName].ps1 34 | # 400-Database-[ScriptName].ps1 35 | # 500-Table-[ScriptName].ps1 36 | # 600-View-[ScriptName].ps1 37 | # 700-Procedure-[ScriptName].ps1 38 | # 800-Agent-[ScriptName].ps1 39 | ############################################################################################################### 40 | 41 | $configParams = $args[0] 42 | 43 | $cmdResult = netsh advfirewall "set" "domainprofile" "firewallpolicy" "allowinbound,allowoutbound" 44 | 45 | Write-Log -level "Info" -message "Windows Firewall Domain Profile complete - $cmdResult" 46 | -------------------------------------------------------------------------------- /PreScripts/100-OS-Verify-PageFile.ps1: -------------------------------------------------------------------------------- 1 | #/* 2005,2008,2008R2,2012,2014,2016 */ 2 | 3 | ############################################################################################################### 4 | # PowerShell Script Template 5 | ############################################################################################################### 6 | # For use with the Auto-Install process 7 | # 8 | # When called, the script will recieve a single hashtable object passed as a 9 | # parameter. This object contains the following properties: 10 | # $params["SqlVersion"] - Version of SQL being installed (SQL2005, SQL2008, SQL2008R2) 11 | # $params["SqlEdition"] - Edition of SQL being installed (Standard, Enterprise) 12 | # $params["ProcessorArch"] - CPU Architecture (x86, X64) 13 | # $params["InstanceName"] - The name that the instance will be installed with (sqldev1) 14 | # $params["ServiceAccount"] - The name of the account to run the services under (domain\user) 15 | # $params["FilePath"] - path to save the configuration ini file 16 | # 17 | # To add additional parameters - add them to the hashtable passed to the Run-Install function 18 | # 19 | # This script should be placed in the appropriate scripts folder and will be automatically called during the 20 | # auto-install process. 21 | # 22 | # The script should be saved using the following naming convention: 23 | # 24 | # Pre Install Script (Save to PreScripts Folder) - 25 | # ------------------------------------------------- 26 | # Pre-100-OS-[ScriptName].ps1 27 | # Pre-200-Service-[ScriptName].ps1 28 | # 29 | # Post Install Script (Save to SQLScripts Folder) - 30 | # ------------------------------------------------- 31 | # 100-OS-[ScriptName].ps1 32 | # 200-Service-[ScriptName].ps1 33 | # 300-Server-[ScriptName].ps1 34 | # 400-Database-[ScriptName].ps1 35 | # 500-Table-[ScriptName].ps1 36 | # 600-View-[ScriptName].ps1 37 | # 700-Procedure-[ScriptName].ps1 38 | # 800-Agent-[ScriptName].ps1 39 | ############################################################################################################### 40 | 41 | $configParams = $args[0] 42 | 43 | #Verify that page file size on the C: drive is 2GB 44 | $winOS = Get-WMIObject Win32_OperatingSystem 45 | $swapFile = $winOS.SizeStoredInPagingFiles 46 | $pageFile = Get-WmiObject Win32_PageFileUsage 47 | [int]$swapFile = $swapFile/1024 48 | Write-Log -level "Info" -message ("The page file is {0} MB located at {1}" -f $swapFile,$pageFile.Name) 49 | -------------------------------------------------------------------------------- /PreScripts/100-OS-Set-ProcessorPriority.ps1: -------------------------------------------------------------------------------- 1 | #/* 2005,2008,2008R2,2012,2014,2016 */ 2 | 3 | ############################################################################################################### 4 | # PowerShell Script Template 5 | ############################################################################################################### 6 | # For use with the Auto-Install process 7 | # 8 | # When called, the script will recieve a single hashtable object passed as a 9 | # parameter. This object contains the following properties: 10 | # $params["SqlVersion"] - Version of SQL being installed (SQL2005, SQL2008, SQL2008R2) 11 | # $params["SqlEdition"] - Edition of SQL being installed (Standard, Enterprise) 12 | # $params["ProcessorArch"] - CPU Architecture (x86, X64) 13 | # $params["InstanceName"] - The name that the instance will be installed with (sqldev1) 14 | # $params["ServiceAccount"] - The name of the account to run the services under (domain\user) 15 | # $params["FilePath"] - path to save the configuration ini file 16 | # 17 | # To add additional parameters - add them to the hashtable passed to the Run-Install function 18 | # 19 | # This script should be placed in the appropriate scripts folder and will be automatically called during the 20 | # auto-install process. 21 | # 22 | # The script should be saved using the following naming convention: 23 | # 24 | # Pre Install Script (Save to PreScripts Folder) - 25 | # ------------------------------------------------- 26 | # Pre-100-OS-[ScriptName].ps1 27 | # Pre-200-Service-[ScriptName].ps1 28 | # 29 | # Post Install Script (Save to SQLScripts Folder) - 30 | # ------------------------------------------------- 31 | # 100-OS-[ScriptName].ps1 32 | # 200-Service-[ScriptName].ps1 33 | # 300-Server-[ScriptName].ps1 34 | # 400-Database-[ScriptName].ps1 35 | # 500-Table-[ScriptName].ps1 36 | # 600-View-[ScriptName].ps1 37 | # 700-Procedure-[ScriptName].ps1 38 | # 800-Agent-[ScriptName].ps1 39 | ############################################################################################################### 40 | 41 | $configParams = $args[0] 42 | 43 | #Verify MS Best Practices for SQL have been implemented 44 | #Only piece that applies to Windows Server 2008 is the Processor Scheduling 45 | Set-ItemProperty -path "HKLM:\SYSTEM\CurrentControlSet\Control\PriorityControl" -name "Win32PrioritySeparation" -value 0x26 #Programs 46 | Write-Log -level "Info" -message "Processor priority set to Programs" 47 | -------------------------------------------------------------------------------- /PostScripts/300-Server-ResizeDefaultDatabases.sql: -------------------------------------------------------------------------------- 1 | /* 2005,2008,2008R2,2012,2014,2016 */ 2 | /***************************************************************************************************** 3 | * Script Information 4 | *---------------------------------------------------------------------------------------------------- 5 | * Author: Jason Carter 6 | * Date: 11/07/2016 7 | * Description: Resize Default Databases 8 | * History: 08/15/2016 - Adapted to Auto-Install script 9 | * 10 | *****************************************************************************************************/ 11 | 12 | USE [master]; 13 | 14 | 15 | ---- Adjust Default Database Options 16 | IF NOT EXISTS(select * from sys.master_files where name='master' and size>=8192) 17 | BEGIN 18 | ALTER DATABASE [master] MODIFY FILE (NAME = master, SIZE = 64MB, FILEGROWTH = 64MB) 19 | PRINT 'Master Database Sizes Set (Initial/Growth): 64MB / 64MB' 20 | END 21 | 22 | IF NOT EXISTS(select * from sys.master_files where name='mastlog' and size>=2048) 23 | BEGIN 24 | ALTER DATABASE [master] MODIFY FILE (NAME = mastlog, SIZE = 16MB, FILEGROWTH = 16MB) 25 | PRINT 'Master Database Log Sizes Set (Initial/Growth): 16MB / 16MB' 26 | END 27 | 28 | IF NOT EXISTS(Select * from sys.databases where name = N'model' and recovery_model=3) 29 | BEGIN 30 | ALTER DATABASE [Model] SET RECOVERY SIMPLE 31 | PRINT 'Model Database Set to SIMPLE RECOVERY MODE' 32 | END 33 | 34 | IF NOT EXISTS(select * from sys.master_files where name='modeldev' and size>=16384) 35 | BEGIN 36 | ALTER DATABASE [Model] MODIFY FILE (NAME = 'modeldev', SIZE = 128MB, FILEGROWTH = 64MB) 37 | PRINT 'Model Database Sizes Set (Initial/Growth): 128MB / 64MB' 38 | END 39 | 40 | IF NOT EXISTS(select * from sys.master_files where name='modellog' and size>=4096) 41 | BEGIN 42 | ALTER DATABASE [Model] MODIFY FILE (NAME = 'modellog', SIZE = 32MB, FILEGROWTH = 32MB) 43 | PRINT '[Model] Database Log Sizes Set (Initial/Growth): 32MB / 32MB' 44 | END 45 | 46 | IF NOT EXISTS(select * from sys.master_files where name='MSDBData' and size>=32768) 47 | BEGIN 48 | ALTER DATABASE [MSDB] MODIFY FILE (NAME = 'MSDBData', SIZE = 256MB, FILEGROWTH = 64MB) 49 | PRINT '[MSDB] Database Sizes Set (Initial/Growth): 256MB / 64MB' 50 | END 51 | 52 | IF NOT EXISTS(select * from sys.master_files where name='MSDBLog' and size>=4096) 53 | BEGIN 54 | ALTER DATABASE [MSDB] MODIFY FILE (NAME = 'MSDBLog', SIZE = 32MB, FILEGROWTH = 32MB) 55 | PRINT '[MSDB] Database Log Sizes Set (Initial/Growth): 32MB / 32MB' 56 | END -------------------------------------------------------------------------------- /Examples/PostScripts/300-Server-Set-DbaTeam.ps1: -------------------------------------------------------------------------------- 1 | #/* 2005,2008,2008R2,2012,2014,2016 */ 2 | 3 | ############################################################################################################### 4 | # PowerShell Script Template 5 | ############################################################################################################### 6 | # For use with the Auto-Install process 7 | # 8 | # When called, the script will recieve a single hashtable object passed as a 9 | # parameter. This object contains the following properties: 10 | # $params["SqlVersion"] - Version of SQL being installed (SQL2005, SQL2008, SQL2008R2) 11 | # $params["SqlEdition"] - Edition of SQL being installed (Standard, Enterprise) 12 | # $params["ProcessorArch"] - CPU Architecture (x86, X64) 13 | # $params["InstanceName"] - The name that the instance will be installed with (sqldev1) 14 | # $params["ServiceAccount"] - The name of the account to run the services under (domain\user) 15 | # $params["FilePath"] - path to save the configuration ini file 16 | # 17 | # To add additional parameters - add them to the hashtable passed to the Run-Install function 18 | # 19 | # This script should be placed in the appropriate scripts folder and will be automatically called during the 20 | # auto-install process. 21 | # 22 | # The script should be saved using the following naming convention: 23 | # 24 | # Pre Install Script (Save to PreScripts Folder) - 25 | # ------------------------------------------------- 26 | # Pre-100-OS-[ScriptName].ps1 27 | # Pre-200-Service-[ScriptName].ps1 28 | # 29 | # Post Install Script (Save to SQLScripts Folder) - 30 | # ------------------------------------------------- 31 | # 100-OS-[ScriptName].ps1 32 | # 200-Service-[ScriptName].ps1 33 | # 300-Server-[ScriptName].ps1 34 | # 400-Database-[ScriptName].ps1 35 | # 500-Table-[ScriptName].ps1 36 | # 600-View-[ScriptName].ps1 37 | # 700-Procedure-[ScriptName].ps1 38 | # 800-Agent-[ScriptName].ps1 39 | # 900-Management-[ScriptName].ps1 40 | ############################################################################################################### 41 | 42 | $configParams = $args[0] 43 | $dbaTeam = $configParams["DbaTeam"] 44 | $instance = $configParams["InstanceName"] 45 | 46 | $command = " 47 | use master; 48 | EXEC sp_addextendedproperty @name = N'DBA', @value = '$dbaTeam'; 49 | use model; 50 | EXEC sp_addextendedproperty @name = N'DBA', @value = '$dbaTeam';" 51 | 52 | Execute-SqlCommand -sqlScript $command -sqlInstance $instance 53 | 54 | Write-Log -level "Info" -message "The DBA extended property has been added to Master and Model with the value of $dbaTeam" 55 | -------------------------------------------------------------------------------- /PreScripts/100-OS-Verify-NetFramework.ps1: -------------------------------------------------------------------------------- 1 | #/* 2005,2008,2008R2,2012,2014,2016 */ 2 | 3 | ############################################################################################################### 4 | # PowerShell Script Template 5 | ############################################################################################################### 6 | # For use with the Auto-Install process 7 | # 8 | # When called, the script will recieve a single hashtable object passed as a 9 | # parameter. This object contains the following properties: 10 | # $params["SqlVersion"] - Version of SQL being installed (SQL2005, SQL2008, SQL2008R2) 11 | # $params["SqlEdition"] - Edition of SQL being installed (Standard, Enterprise) 12 | # $params["ProcessorArch"] - CPU Architecture (x86, X64) 13 | # $params["InstanceName"] - The name that the instance will be installed with (sqldev1) 14 | # $params["ServiceAccount"] - The name of the account to run the services under (domain\user) 15 | # $params["FilePath"] - path to save the configuration ini file 16 | # 17 | # To add additional parameters - add them to the hashtable passed to the Run-Install function 18 | # 19 | # This script should be placed in the appropriate scripts folder and will be automatically called during the 20 | # auto-install process. 21 | # 22 | # The script should be saved using the following naming convention: 23 | # 24 | # Pre Install Script (Save to PreScripts Folder) - 25 | # ------------------------------------------------- 26 | # Pre-100-OS-[ScriptName].ps1 27 | # Pre-200-Service-[ScriptName].ps1 28 | # 29 | # Post Install Script (Save to SQLScripts Folder) - 30 | # ------------------------------------------------- 31 | # 100-OS-[ScriptName].ps1 32 | # 200-Service-[ScriptName].ps1 33 | # 300-Server-[ScriptName].ps1 34 | # 400-Database-[ScriptName].ps1 35 | # 500-Table-[ScriptName].ps1 36 | # 600-View-[ScriptName].ps1 37 | # 700-Procedure-[ScriptName].ps1 38 | # 800-Agent-[ScriptName].ps1 39 | ############################################################################################################### 40 | 41 | $configParams = $args[0] 42 | 43 | #Verify .NetFramework 3.5 SP1 is installed 44 | $framework = Get-ItemProperty -path "HKLM:\Software\Microsoft\NET Framework Setup\ndp\v3.5" -name "SP" 45 | if($framework.SP -ge 1) 46 | { 47 | #Correct version installed 48 | Write-Log -level "Info" -message ("The .net framework 3.5 is at SP 1 or greater - SP{0}" -f $framework.SP) 49 | } 50 | else 51 | { 52 | #Not the correct version 53 | Write-Log -level "Error" -message ("The .net framework 3.5 needs to be SP 1 or greater - SP{0}" -f $framework.SP) 54 | } 55 | -------------------------------------------------------------------------------- /Common/Execute-SqlScriptFiles.ps1: -------------------------------------------------------------------------------- 1 | Function Execute-SqlScriptFiles 2 | { 3 | param 4 | ( 5 | [Parameter(Position=0, Mandatory=$true)] [string] $sqlVersion, 6 | [Parameter(Position=1, Mandatory=$true)] [string] $sqlInstance 7 | ) 8 | 9 | #$tempPath = [environment]::GetEnvironmentVariable("temp","machine") 10 | #$tempPath 11 | #$Script:LogFile = join-path -path $tempPath -childPath "SqlInstallerLog_ScriptTest.html" 12 | 13 | #Load a list of all files from the scripts folder (\\ray\dba_public$\cur_installs\SqlScripts) 14 | [array] $files = get-childitem -path '\\walrus1xb\f$\cur_installs\sqlscripts' -filter '*.sql' 15 | 16 | #Sort the list on file name (file name should start with script level - 100 OS, 200 Service, 300 Server, 400 Database, 500 Table, 600 View, 700 Procedure, 800 Agent) 17 | $files = $files | Sort-Object 18 | 19 | Write-Log -level "Section" -message "Applying Standard SQL Scripts" 20 | 21 | #Loop through the list 22 | foreach ($file in $files) 23 | { 24 | #Read the first line of each file (/* 2005,2008,2008R2 */) 25 | [string] $strSupported = Get-Content -Path $file.FullName -TotalCount 1 26 | 27 | #Clean off the /* */ from the first line and split to a string array 28 | #Original code - .Net method using the replace method of the string object 29 | #[array] $arySupported = $strSupported.Replace('/*', '').Replace('*/', '').Trim().Split(',') 30 | 31 | #Code showing the PowerShell + RegEx method of performing the same task 32 | $arySupported = $strSupported -replace '/\*|\*/' -replace '^\s+|\s+$' -split ',' 33 | 34 | #If the current version is contained in the array then execute the script using the Execute-Sql function 35 | if ($arySupported -contains $($sqlVersion -replace "sql")) 36 | { 37 | #Write the script name and results (query results or skipped) to the log 38 | Write-Log -level "Info" -message "Executing SQL Script - $file" 39 | 40 | #Check to see if we are connecting to a default instance 41 | if ([string]::IsNullOrEmpty($sqlInstance)) 42 | { 43 | $strResult = Execute-SQL -sqlScript $file.FullName 44 | } 45 | else 46 | { 47 | $strResult = Execute-SQL -sqlScript $file.FullName -sqlInstance $sqlInstance 48 | } 49 | 50 | if ($strResult -eq "Command(s) completed successfully.") 51 | { 52 | Write-Log -level "Info" -message "$file - $strResult" 53 | } 54 | else 55 | { 56 | Write-Log -level "Warning" -message "$file - Failed: $strResult" 57 | if ($sqlConn.State -ne [System.Data.ConnectionState]'Closed') 58 | { 59 | $sqlConn.Close() 60 | Write-Log -level "Info" -message "Closing Connection" 61 | } 62 | } 63 | } 64 | else 65 | { 66 | Write-Log -level "Feature" -message "Skipping SQL Script - $file" 67 | } 68 | #Next 69 | } 70 | Write-Log -level "Info" -message "Standard SQL Scripts Complete" 71 | } 72 | -------------------------------------------------------------------------------- /PostScripts/800-Agent-Enable-AgentNotifications.ps1: -------------------------------------------------------------------------------- 1 | #/* 2005,2008,2008R2,2012,2014,2016 */ 2 | 3 | ############################################################################################################### 4 | # PowerShell Script Template 5 | ############################################################################################################### 6 | # For use with the Auto-Install process 7 | # 8 | # When called, the script will recieve a single hashtable object passed as a 9 | # parameter. This object contains the following properties: 10 | # $params["SqlVersion"] - Version of SQL being installed (SQL2005, SQL2008, SQL2008R2) 11 | # $params["SqlEdition"] - Edition of SQL being installed (Standard, Enterprise) 12 | # $params["ProcessorArch"] - CPU Architecture (x86, X64) 13 | # $params["InstanceName"] - The name that the instance will be installed with (sqldev1) 14 | # $params["ServiceAccount"] - The name of the account to run the services under (domain\user) 15 | # $params["FilePath"] - path to save the configuration ini file 16 | # 17 | # To add additional parameters - add them to the hashtable passed to the Run-Install function 18 | # 19 | # This script should be placed in the appropriate scripts folder and will be automatically called during the 20 | # auto-install process. 21 | # 22 | # The script should be saved using the following naming convention: 23 | # 24 | # Pre Install Script (Save to PreScripts Folder) - 25 | # ------------------------------------------------- 26 | # Pre-100-OS-[ScriptName].ps1 27 | # Pre-200-Service-[ScriptName].ps1 28 | # 29 | # Post Install Script (Save to SQLScripts Folder) - 30 | # ------------------------------------------------- 31 | # 100-OS-[ScriptName].ps1 32 | # 200-Service-[ScriptName].ps1 33 | # 300-Server-[ScriptName].ps1 34 | # 400-Database-[ScriptName].ps1 35 | # 500-Table-[ScriptName].ps1 36 | # 600-View-[ScriptName].ps1 37 | # 700-Procedure-[ScriptName].ps1 38 | # 800-Agent-[ScriptName].ps1 39 | # 900-Management-[ScriptName].ps1 40 | ############################################################################################################### 41 | 42 | $configParams = $args[0] 43 | $instance = $configParams["InstanceName"] 44 | 45 | $computerName = $Global:ComputerName 46 | $profileName = "SQLMAIL$computerName" 47 | 48 | $command = " 49 | EXEC sp_configure 'Agent XPs', 1; 50 | RECONFIGURE; 51 | EXEC msdb.dbo.sp_set_sqlagent_properties @email_save_in_sent_folder=1; 52 | EXEC master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'UseDatabaseMail', N'REG_DWORD', 1; 53 | EXEC master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'DatabaseMailProfile', N'REG_SZ', N'$profileName';" 54 | 55 | Execute-SqlCommand -sqlScript $command -sqlInstance $instance 56 | 57 | Write-Log -level "Info" -message "Enabled Agent Notifications for $profileName profile" 58 | -------------------------------------------------------------------------------- /Examples/_Template.sql: -------------------------------------------------------------------------------- 1 | /* 2005,2008,2008R2,2012,2014,2016 */ 2 | 3 | /***************************************************************************************************** 4 | * Auto-Install Script Template 5 | *---------------------------------------------------------------------------------------------------- 6 | * 7 | * Instructions: 8 | * The top line of this document must contain a commented comma seperated list of the versions of SQL 9 | * that this script applies to. Example: "/* 2000,2005,2008,2008R2 */" 10 | * 11 | * This script template is only suitable for statements that are to be executed as part of the 12 | * auto-install process and must run only against the server instance being installed. 13 | * 14 | * The script must terminate each statement using the ";" operator and must not contain the keyword 15 | * "GO". 16 | * 17 | * This template does not support scripts that need to be called with parameters. If your script 18 | * requires parameters please use the PowerShell Script template. 19 | * 20 | * Scripts must be named using the following pattern: 21 | * level-level name-script name 22 | * 23 | * level: The numeric level of the script. This controls the order in which scripts are applied to 24 | * ensure that dependancies are not broken. See Level list for the possible values. 25 | * 26 | * level name: The friendly name of the level. This is meant to makes the scripts more easily 27 | * identifiable. See Level list for the possible values. 28 | * 29 | * script name: The friendly name of the script. This should be short, but detailed enought to tell 30 | * what the script will accomplish. 31 | * 32 | * Example: "300-Server-AddExtendedProperty.sql" - Server level script that adds the DBA Extended 33 | * property to the master and model databases 34 | * 35 | * Level List: 36 | * --------------- 37 | * 300 - Server - Scripts that create/alter/drop server level objects and settings 38 | * 400 - Database - Scripts that create/alter/drop databases and settings 39 | * 500 - Table - Scripts that create/alter/drop tables, schemas, users, roles 40 | * 600 - View - Scripts that create/alter/drop views, indexes, or other objects with table dependancies 41 | * 700 - Procedure - Scripts that create/alter/drop objects with table/view dependancies 42 | * 800 - Agent - Scripts that create/alter/drop agent jobs, job steps, job schedules, notifications, etc 43 | * 900 - Management - Scripts that are run last that pertain to management of the instance 44 | *****************************************************************************************************/ 45 | 46 | /***************************************************************************************************** 47 | * Script Information 48 | *---------------------------------------------------------------------------------------------------- 49 | * Author: 50 | * Date: 51 | * Description: 52 | * History: 53 | *****************************************************************************************************/ 54 | -------------------------------------------------------------------------------- /PreScripts/200-Service-Check-AncillaryServices.ps1: -------------------------------------------------------------------------------- 1 | #/* 2005,2008,2008R2,2012,2014,2016 */ 2 | 3 | ############################################################################################################### 4 | # PowerShell Script Template 5 | ############################################################################################################### 6 | # For use with the Auto-Install process 7 | # 8 | # When called, the script will recieve a single hashtable object passed as a 9 | # parameter. This object contains the following properties: 10 | # $params["SqlVersion"] - Version of SQL being installed (SQL2005, SQL2008, SQL2008R2) 11 | # $params["SqlEdition"] - Edition of SQL being installed (Standard, Enterprise) 12 | # $params["ProcessorArch"] - CPU Architecture (x86, X64) 13 | # $params["InstanceName"] - The name that the instance will be installed with (sqldev1) 14 | # $params["ServiceAccount"] - The name of the account to run the services under (domain\user) 15 | # $params["FilePath"] - path to save the configuration ini file 16 | # 17 | # To add additional parameters - add them to the hashtable passed to the Run-Install function 18 | # 19 | # This script should be placed in the appropriate scripts folder and will be automatically called during the 20 | # auto-install process. 21 | # 22 | # The script should be saved using the following naming convention: 23 | # 24 | # Pre Install Script (Save to PreScripts Folder) - 25 | # ------------------------------------------------- 26 | # Pre-100-OS-[ScriptName].ps1 27 | # Pre-200-Service-[ScriptName].ps1 28 | # 29 | # Post Install Script (Save to SQLScripts Folder) - 30 | # ------------------------------------------------- 31 | # 100-OS-[ScriptName].ps1 32 | # 200-Service-[ScriptName].ps1 33 | # 300-Server-[ScriptName].ps1 34 | # 400-Database-[ScriptName].ps1 35 | # 500-Table-[ScriptName].ps1 36 | # 600-View-[ScriptName].ps1 37 | # 700-Procedure-[ScriptName].ps1 38 | # 800-Agent-[ScriptName].ps1 39 | ############################################################################################################### 40 | 41 | $configParams = $args[0] 42 | 43 | #Load the script parameters from the config file 44 | [array] $nodes = ($Global:ScriptConfigs | ?{$_.Name -eq "Check-AncillaryServices"}).SelectNodes("Param") 45 | $paramServices = ($nodes | ? {$_.Name -eq "Services"}).Value 46 | 47 | [array] $missing = $nodes | ? {$_.Value -eq ""} 48 | if ($missing.Count -gt 0) 49 | { 50 | return "Script not executed: please check the Run-Install.config file for missing configuration items" 51 | } 52 | 53 | #Split the comma separated list of services into an array 54 | [array] $checks = $paramServices.Split(',') 55 | 56 | #Loop through the array to perform the check 57 | foreach($check in $checks) 58 | { 59 | [array] $services = Get-Service | where{$_.Name -eq $check }| select Name 60 | if ($service.Length > 0) 61 | { 62 | Write-Log -level "Info" -message "$check was found on this server" 63 | } 64 | else 65 | { 66 | Write-Log -level "Info" -message "$check was not found on this server" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Common/Execute-Sql.ps1: -------------------------------------------------------------------------------- 1 | Function Execute-Sql 2 | { 3 | param 4 | ( 5 | [Parameter(Position=0, Mandatory=$true)] [string] $sqlScript, 6 | [Parameter(Position=1, Mandatory=$true)] [AllowEmptyString()] [string] $sqlInstance, 7 | [Parameter(Position=2, Mandatory=$false)] [string] $serverName = $Global:LogicalComputerName, 8 | [Parameter(Position=3, Mandatory=$false)] [string] $databaseName = "master" 9 | ) 10 | $scriptName = (Split-Path $sqlScript -Leaf).Split(".")[0] 11 | Write-Log -Level Debug -Message "Looking for params for script: $scriptName" 12 | 13 | [array] $nodes = $Global:ScriptConfigs | ?{$_.Name -eq $scriptName} 14 | if ($nodes) 15 | { 16 | Write-Log -Level Debug -Message "Loading Parameters from Global Config" 17 | $nodes = $nodes.SelectNodes("Param") 18 | foreach ($node in $nodes) 19 | { 20 | Write-Log -Level Debug -Message "$($node.Name) = $($node.Value)" 21 | } 22 | } 23 | 24 | 25 | 26 | $sqlConn = new-Object System.Data.SqlClient.SqlConnection("Server=$serverName\$sqlInstance;DataBase=$databaseName;Integrated Security=SSPI;") 27 | 28 | #### Lets output script output to our log. 29 | $handler = [System.Data.SqlClient.SqlInfoMessageEventHandler] {param($sender, $event) ($event.Message -split '[\r\n]') |? {Write-Log -level "Info" -message "--------$_" } }; 30 | $sqlConn.add_InfoMessage($handler); 31 | $sqlConn.FireInfoMessageEventOnUserErrors = $false; 32 | 33 | $sqlCmd = New-Object System.Data.SqlClient.SqlCommand 34 | $sqlCmd.Connection = $sqlConn 35 | $sqlCmd.CommandType = [System.Data.CommandType]'Text' 36 | $sqlCmd.CommandTimeout = 300 37 | $strCommands = [System.IO.File]::ReadAllText($sqlScript) 38 | 39 | #Look for New Line without Carriage Return and vice versa 40 | $strCommands = $strCommands -replace "`r(?!`n)","`r`n" -replace "`(? nul 17 | if %ERRORLEVEL% == 0 goto RunNonCore 18 | 19 | ver | find "2003" > nul 20 | if %ERRORLEVEL% == 0 goto RunNonCore 21 | 22 | REM ********************************************************************************* 23 | REM * Check to see if we are on a Server Core installation 24 | REM ********************************************************************************* 25 | FOR /F "tokens=*" %%A IN ('WMIC OS Get OperatingSystemSKU /Value ^| FIND "="') DO (SET %%A) 26 | 27 | REM ECHO OperatingSystemSKU=%OperatingSystemSKU% 28 | 29 | IF %OperatingSystemSKU% gtr 11 IF %OperatingSystemSKU% lss 15 GOTO RunCore 30 | GOTO RunNonCore 31 | 32 | 33 | REM ********************************************************************************* 34 | REM * Core - so the PowerShell ISE is not available 35 | REM ********************************************************************************* 36 | :RunCore 37 | echo Opening the Start-SqlSpade script using Notepad 38 | powershell.exe -NoProfile "start-process notepad.exe -wait -argumentlist %curr_path% -verb RunAs" 39 | GOTO RunCoreMenu 40 | 41 | REM ********************************************************************************* 42 | REM * Core Menu - are you ready to execute the script 43 | REM ********************************************************************************* 44 | :RunCoreMenu 45 | echo. 46 | set /p web=Would you like to (R)un the script or (E)xit SPADE? 47 | if "%web%"=="R" goto LaunchScript 48 | if "%web%"=="r" goto LaunchScript 49 | if "%web%"=="L" goto LaunchScriptLogged 50 | if "%web%"=="l" goto LaunchScriptLogged 51 | if "%web%"=="E" goto End 52 | if "%web%"=="e" goto End 53 | echo. 54 | echo Invalid Selection 55 | GOTO RunCoreMenu 56 | 57 | REM ********************************************************************************* 58 | REM * Launch the start-sqlspade.ps1 script directly 59 | REM ********************************************************************************* 60 | :LaunchScript 61 | echo Running Script... 62 | REM powershell.exe "start-process PowerShell.exe -wait -argumentlist %curr_path% -verb RunAs" 63 | powershell.exe %curr_path% 64 | GOTO End 65 | 66 | REM ********************************************************************************* 67 | REM * Launch the start-sqlspade.ps1 script directly with logging 68 | REM ********************************************************************************* 69 | :LaunchScriptLogged 70 | echo Running Script... 71 | REM powershell.exe "start-process PowerShell.exe -wait -argumentlist %curr_path% -verb RunAs" 72 | powershell.exe %curr_path% > SetupLog.txt 73 | GOTO End 74 | 75 | REM ********************************************************************************* 76 | REM * Not Core - so we have the PowerShell ISE available 77 | REM ********************************************************************************* 78 | :RunNonCore 79 | echo Opening the Start-SqlSpade script using PowerShell ISE 80 | 81 | powershell.exe -NoProfile "start-process PowerShell_ISE.exe -argumentlist %curr_path% -verb RunAs" 82 | 83 | REM IF %ERRORLEVEL% NEQ 0 GOTO ErrorNonCore 84 | GOTO End 85 | 86 | REM ********************************************************************************* 87 | REM * An error ocurred when trying to launch the ISE 88 | REM ********************************************************************************* 89 | :ErrorNonCore 90 | echo Unable to open Start-SqlSpade.ps1 - please verify that the file is present 91 | echo and that the PowerShell ISE is installed then run again 92 | pause 93 | 94 | REM ********************************************************************************* 95 | REM * Exit the batch 96 | REM ********************************************************************************* 97 | :End 98 | PAUSE -------------------------------------------------------------------------------- /Common/Invoke-WindowsApi.ps1: -------------------------------------------------------------------------------- 1 | ############################################################################## 2 | ## 3 | ## Invoke-WindowsApi 4 | ## 5 | ## From Windows PowerShell Cookbook (O'Reilly) 6 | ## by Lee Holmes (http://www.leeholmes.com/guide) 7 | ## 8 | ############################################################################## 9 | Function Invoke-WindowsApi 10 | { 11 | <# 12 | 13 | .SYNOPSIS 14 | 15 | Invoke a native Windows API call that takes and returns simple data types. 16 | 17 | 18 | .EXAMPLE 19 | 20 | ## Prepare the parameter types and parameters for the CreateHardLink function 21 | PS >$filename = "c:\temp\hardlinked.txt" 22 | PS >$existingFilename = "c:\temp\link_target.txt" 23 | PS >Set-Content $existingFilename "Hard Link target" 24 | PS >$parameterTypes = [string], [string], [IntPtr] 25 | PS >$parameters = [string] $filename, [string] $existingFilename, 26 | [IntPtr]::Zero 27 | 28 | ## Call the CreateHardLink method in the Kernel32 DLL 29 | PS >$result = Invoke-WindowsApi "kernel32" ([bool]) "CreateHardLink" ` 30 | $parameterTypes $parameters 31 | PS >Get-Content C:\temp\hardlinked.txt 32 | Hard Link target 33 | 34 | #> 35 | 36 | param( 37 | ## The name of the DLL that contains the Windows API, such as "kernel32" 38 | [string] $DllName, 39 | 40 | ## The return type expected from Windows API 41 | [Type] $ReturnType, 42 | 43 | ## The name of the Windows API 44 | [string] $MethodName, 45 | 46 | ## The types of parameters expected by the Windows API 47 | [Type[]] $ParameterTypes, 48 | 49 | ## Parameter values to pass to the Windows API 50 | [Object[]] $Parameters 51 | ) 52 | 53 | Set-StrictMode -Version Latest 54 | 55 | ## Begin to build the dynamic assembly 56 | $domain = [AppDomain]::CurrentDomain 57 | $name = New-Object Reflection.AssemblyName 'PInvokeAssembly' 58 | $assembly = $domain.DefineDynamicAssembly($name, 'Run') 59 | $module = $assembly.DefineDynamicModule('PInvokeModule') 60 | $type = $module.DefineType('PInvokeType', "Public,BeforeFieldInit") 61 | 62 | ## Go through all of the parameters passed to us. As we do this, 63 | ## we clone the user's inputs into another array that we will use for 64 | ## the P/Invoke call. 65 | $inputParameters = @() 66 | $refParameters = @() 67 | 68 | for($counter = 1; $counter -le $parameterTypes.Length; $counter++) 69 | { 70 | ## If an item is a PSReference, then the user 71 | ## wants an [out] parameter. 72 | if($parameterTypes[$counter - 1] -eq [Ref]) 73 | { 74 | ## Remember which parameters are used for [Out] parameters 75 | $refParameters += $counter 76 | 77 | ## On the cloned array, we replace the PSReference type with the 78 | ## .Net reference type that represents the value of the PSReference, 79 | ## and the value with the value held by the PSReference. 80 | $parameterTypes[$counter - 1] = 81 | $parameters[$counter - 1].Value.GetType().MakeByRefType() 82 | $inputParameters += $parameters[$counter - 1].Value 83 | } 84 | else 85 | { 86 | ## Otherwise, just add their actual parameter to the 87 | ## input array. 88 | $inputParameters += $parameters[$counter - 1] 89 | } 90 | } 91 | 92 | ## Define the actual P/Invoke method, adding the [Out] 93 | ## attribute for any parameters that were originally [Ref] 94 | ## parameters. 95 | $method = $type.DefineMethod( 96 | $methodName, 'Public,HideBySig,Static,PinvokeImpl', 97 | $returnType, $parameterTypes) 98 | foreach($refParameter in $refParameters) 99 | { 100 | [void] $method.DefineParameter($refParameter, "Out", $null) 101 | } 102 | 103 | ## Apply the P/Invoke constructor 104 | $ctor = [Runtime.InteropServices.DllImportAttribute].GetConstructor([string]) 105 | $attr = New-Object Reflection.Emit.CustomAttributeBuilder $ctor, $dllName 106 | $method.SetCustomAttribute($attr) 107 | 108 | ## Create the temporary type, and invoke the method. 109 | $realType = $type.CreateType() 110 | 111 | $realType.InvokeMember( 112 | $methodName, 'Public,Static,InvokeMethod', $null, $null,$inputParameters) 113 | 114 | ## Finally, go through all of the reference parameters, and update the 115 | ## values of the PSReference objects that the user passed in. 116 | foreach($refParameter in $refParameters) 117 | { 118 | $parameters[$refParameter - 1].Value = $inputParameters[$refParameter - 1] 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /PostScripts/_300-Server-ConfigureDatabaseMail.sql: -------------------------------------------------------------------------------- 1 | /* 2005,2008,2008R2,2012,2014,2016 */ 2 | 3 | /***************************************************************************************************** 4 | * Auto-Install Script Template 5 | *---------------------------------------------------------------------------------------------------- 6 | * 7 | * Instructions: 8 | * The top line of this document must contain a commented comma seperated list of the versions of SQL 9 | * that this script applies to. Example: "/* 2000,2005,2008,2008R2 */" 10 | * 11 | * This script template is only suitable for statements that are to be executed as part of the 12 | * auto-install process and must run only against the server instance being installed. 13 | * 14 | * The script must terminate each statement using the ";" operator and the keyword 15 | * "GO" must be enclosed in square brackets []. 16 | * 17 | * This template does not support scripts that need to be called with parameters. If your script 18 | * requires parameters please use the PowerShell Script template. 19 | * 20 | * Scripts must be named using the following pattern: 21 | * level-level name-script name 22 | * 23 | * level: The numeric level of the script. This controls the order in which scripts are applied to 24 | * ensure that dependancies are not broken. See Level list for the possible values. 25 | * 26 | * level name: The friendly name of the level. This is meant to makes the scripts more easily 27 | * identifiable. See Level list for the possible values. 28 | * 29 | * script name: The friendly name of the script. This should be short, but detailed enought to tell 30 | * what the script will accomplish. 31 | * 32 | * Example: "300-Server-AddExtendedProperty.sql" - Server level script that adds the DBA Extended 33 | * property to the master and model databases 34 | * 35 | * Level List: 36 | * --------------- 37 | * 300 - Server - Scripts that create/alter/drop server level objects and settings 38 | * 400 - Database - Scripts that create/alter/drop databases and settings 39 | * 500 - Table - Scripts that create/alter/drop tables, schemas, users, roles 40 | * 600 - View - Scripts that create/alter/drop views, indexes, or other objects with table dependancies 41 | * 700 - Procedure - Scripts that create/alter/drop objects with table/view dependancies 42 | * 800 - Agent - Scripts that create/alter/drop agent jobs, job steps, job schedules, notifications, etc 43 | * 900 - Management - Scripts that are used for management operations 44 | *****************************************************************************************************/ 45 | 46 | /***************************************************************************************************** 47 | * Script Information 48 | *---------------------------------------------------------------------------------------------------- 49 | * Author: Michael Wells 50 | * Date: 11/29/2010 51 | * Description: Configure Database Mail and create a default profile 52 | * History: 11/29/2010 - Adapted to Auto-Install script 53 | * 54 | *****************************************************************************************************/ 55 | 56 | USE [master]; 57 | 58 | -- Enable Database Mail for this instance 59 | EXECUTE sp_configure 'show advanced', 1; 60 | RECONFIGURE; 61 | EXECUTE sp_configure 'Database Mail XPs',1; 62 | RECONFIGURE; 63 | EXECUTE sp_configure 'Agent XPs', 1; 64 | RECONFIGURE; 65 | 66 | declare @servername varchar(40), @email varchar(50) 67 | 68 | select @servername = 'SQLMAIL_' + convert(varchar, serverproperty('machinename')) 69 | 70 | select @email = @servername + '@alfki.com' 71 | 72 | -- Create a Database Mail account 73 | EXECUTE msdb.dbo.sysmail_add_account_sp 74 | @account_name = 'Primary Account', 75 | @description = 'Account used by all mail profiles.', 76 | @email_address = @email, 77 | @replyto_address = 'IT-DBA@alfki.com', 78 | @display_name = @servername, 79 | @mailserver_name = 'mailrelay.alfki.com'; 80 | 81 | -- Create a Database Mail profile 82 | EXECUTE msdb.dbo.sysmail_add_profile_sp 83 | @profile_name = @servername, 84 | @description = 'Default public profile for all users'; 85 | 86 | -- Add the account to the profile 87 | EXECUTE msdb.dbo.sysmail_add_profileaccount_sp 88 | @profile_name = @servername, 89 | @account_name = 'Primary Account', 90 | @sequence_number = 1; 91 | 92 | -- Grant access to the profile to all msdb database users 93 | EXECUTE msdb.dbo.sysmail_add_principalprofile_sp 94 | @profile_name = @servername, 95 | @principal_name = 'public', 96 | @is_default = 1; 97 | 98 | --EXECUTE msdb.dbo.sysmail_configure_sp 99 | -- 'LoggingLevel', '1' ; -------------------------------------------------------------------------------- /Examples/PostScripts/300-Server-Set-SsisDbInstance.ps1: -------------------------------------------------------------------------------- 1 | #/* 2005,2008,2008R2,2012,2014,2016 */ 2 | 3 | ############################################################################################################### 4 | # PowerShell Script Template 5 | ############################################################################################################### 6 | # For use with the Auto-Install process 7 | # 8 | # When called, the script will recieve a single hashtable object passed as a 9 | # parameter. This object contains the following properties: 10 | # $params["SqlVersion"] - Version of SQL being installed (SQL2005, SQL2008, SQL2008R2) 11 | # $params["SqlEdition"] - Edition of SQL being installed (Standard, Enterprise) 12 | # $params["ProcessorArch"] - CPU Architecture (x86, X64) 13 | # $params["InstanceName"] - The name that the instance will be installed with (sqldev1) 14 | # $params["ServiceAccount"] - The name of the account to run the services under (domain\user) 15 | # $params["FilePath"] - path to save the configuration ini file 16 | # 17 | # To add additional parameters - add them to the hashtable passed to the Run-Install function 18 | # 19 | # This script should be placed in the appropriate scripts folder and will be automatically called during the 20 | # auto-install process. 21 | # 22 | # The script should be saved using the following naming convention: 23 | # 24 | # Pre Install Script (Save to PreScripts Folder) - 25 | # ------------------------------------------------- 26 | # Pre-100-OS-[ScriptName].ps1 27 | # Pre-200-Service-[ScriptName].ps1 28 | # 29 | # Post Install Script (Save to SQLScripts Folder) - 30 | # ------------------------------------------------- 31 | # 100-OS-[ScriptName].ps1 32 | # 200-Service-[ScriptName].ps1 33 | # 300-Server-[ScriptName].ps1 34 | # 400-Database-[ScriptName].ps1 35 | # 500-Table-[ScriptName].ps1 36 | # 600-View-[ScriptName].ps1 37 | # 700-Procedure-[ScriptName].ps1 38 | # 800-Agent-[ScriptName].ps1 39 | # 900-Management-[ScriptName].ps1 40 | ############################################################################################################### 41 | 42 | ############################################################################################################################### 43 | # 44 | # 45 | # true 46 | # 47 | # 48 | # MSDB 49 | # . 50 | # 51 | # 52 | # File System 53 | # ..\Packages 54 | # 55 | # 56 | # 57 | ############################################################################################################################### 58 | 59 | $configParams = $args[0] 60 | $instanceName = $configParams["InstanceName"] 61 | $sqlVersion = $configParams["SqlVersion"] 62 | $productString = $configParams["ProductString"] 63 | 64 | ##Debug 65 | #$instanceName = "SQL2008R2" 66 | #$sqlVersion = "SQL2008R2" 67 | 68 | if ($productString -contains "SQL_DTS" -or $productString -contains "IS") 69 | { 70 | switch ($sqlVersion) 71 | { 72 | "SQL2012" {$filePath = "C:\Program Files\Microsoft SQL Server\110\DTS\Binn\MsDtsSrvr.ini.xml"} 73 | "SQL2008R2" {$filePath = "C:\Program Files\Microsoft SQL Server\100\DTS\Binn\MsDtsSrvr.ini.xml"} 74 | "SQL2008" {$filePath = "C:\Program Files\Microsoft SQL Server\100\DTS\Binn\MsDtsSrvr.ini.xml"} 75 | "SQL2005" {$filePath = "C:\Program Files\Microsoft SQL Server\90\DTS\Binn\MsDtsSrvr.ini.xml"} 76 | } 77 | 78 | if (Test-Path $filePath) 79 | { 80 | $xml = [xml](gc $filePath) 81 | $root = $xml.SelectSingleNode("./DtsServiceConfiguration/TopLevelFolders/Folder[1]/ServerName") 82 | 83 | #If there is no instance name then use the default instance 84 | if ($instanceName -ne "") 85 | { 86 | $root.InnerText = ".\$instanceName" 87 | } 88 | else 89 | { 90 | $root.InnerText = "." 91 | } 92 | 93 | $xml.Save($filePath) 94 | 95 | Write-Log -level "Info" -message "The SSIS configuration has been updated to use the $instanceName instance" 96 | } 97 | else 98 | { 99 | throw "Unable to locate the SSIS configuration file at $filePath" 100 | } 101 | } 102 | else 103 | { 104 | Write-Log -level "Attention" -message "The SSIS configuration file has not been updated becuase Integration Services was not selected to be installed" 105 | } -------------------------------------------------------------------------------- /PostScripts/300-Server-ConfigureDatabaseMail.ps1: -------------------------------------------------------------------------------- 1 | #/* 2005,2008,2008R2,2012,2014,2016 */ 2 | 3 | ############################################################################################################### 4 | # PowerShell Script Template 5 | ############################################################################################################### 6 | # For use with the Auto-Install process 7 | # 8 | # When called, the script will recieve a single hashtable object passed as a 9 | # parameter. This object contains the following properties: 10 | # $params["SqlVersion"] - Version of SQL being installed (SQL2005, SQL2008, SQL2008R2) 11 | # $params["SqlEdition"] - Edition of SQL being installed (Standard, Enterprise) 12 | # $params["ProcessorArch"] - CPU Architecture (x86, X64) 13 | # $params["InstanceName"] - The name that the instance will be installed with (sqldev1) 14 | # $params["ServiceAccount"] - The name of the account to run the services under (domain\user) 15 | # $params["FilePath"] - path to save the configuration ini file 16 | # 17 | # To add additional parameters - add them to the hashtable passed to the Run-Install function 18 | # 19 | # This script should be placed in the appropriate scripts folder and will be automatically called during the 20 | # auto-install process. 21 | # 22 | # The script should be saved using the following naming convention: 23 | # 24 | # Pre Install Script (Save to PreScripts Folder) - 25 | # ------------------------------------------------- 26 | # Pre-100-OS-[ScriptName].ps1 27 | # Pre-200-Service-[ScriptName].ps1 28 | # 29 | # Post Install Script (Save to SQLScripts Folder) - 30 | # ------------------------------------------------- 31 | # 100-OS-[ScriptName].ps1 32 | # 200-Service-[ScriptName].ps1 33 | # 300-Server-[ScriptName].ps1 34 | # 400-Database-[ScriptName].ps1 35 | # 500-Table-[ScriptName].ps1 36 | # 600-View-[ScriptName].ps1 37 | # 700-Procedure-[ScriptName].ps1 38 | # 800-Agent-[ScriptName].ps1 39 | # 900-Management-[ScriptName].ps1 40 | ############################################################################################################### 41 | 42 | $configParams = $args[0] 43 | #$instanceName = "SQL2008R2" 44 | $computerName = gc env:computername 45 | $instanceName = $configParams["InstanceName"] 46 | 47 | #Load the script parameters from the config file 48 | [array] $nodes = ($Global:ScriptConfigs | ?{$_.Name -eq "Configure-DatabaseMail"}).SelectNodes("Param") 49 | $email = ($nodes | ? {$_.Name -eq "Email"}).Value 50 | $relay = ($nodes | ? {$_.Name -eq "Relay"}).Value 51 | $replyTo = ($nodes | ? {$_.Name -eq "ReplyTo"}).Value 52 | $displayName = ($nodes | ? {$_.Name -eq "DisplayName"}).Value 53 | $profileName = ($nodes | ? {$_.Name -eq "ProfileName"}).Value 54 | 55 | #Handle expanding the computername alias if used 56 | $email = $email.Replace("[computername]", $computerName) 57 | $displayName = $displayName.Replace("[computername]", $computerName) 58 | $profileName = $profileName.Replace("[computername]", $computerName) 59 | 60 | [array] $missing = $nodes | ? {$_.Value -eq ""} 61 | if ($missing.Count -gt 0) 62 | { 63 | Write-Log -level "Attention" -message "Port number not set - please check the Run-Install.config file for missing configuration items" 64 | } 65 | 66 | $command = @" 67 | USE [master]; 68 | 69 | /* Enable Database Mail for this instance */ 70 | EXECUTE sp_configure 'show advanced', 1; 71 | RECONFIGURE; 72 | EXECUTE sp_configure 'Agent XPs', 1; 73 | RECONFIGURE; 74 | EXECUTE sp_configure 'Database Mail XPs', 1; 75 | RECONFIGURE; 76 | 77 | /* Create a Database Mail account */ 78 | EXECUTE msdb.dbo.sysmail_add_account_sp 79 | @account_name = 'Primary Account', 80 | @description = 'Account used by all mail profiles.', 81 | @email_address = '$email', 82 | @replyto_address = '$replyTo', 83 | @display_name = '$displayName', 84 | @mailserver_name = '$relay'; 85 | 86 | /* Create a Database Mail profile */ 87 | EXECUTE msdb.dbo.sysmail_add_profile_sp 88 | @profile_name = '$profileName', 89 | @description = 'Default public profile for all users'; 90 | 91 | /* Add the account to the profile */ 92 | EXECUTE msdb.dbo.sysmail_add_profileaccount_sp 93 | @profile_name = '$profileName', 94 | @account_name = 'Primary Account', 95 | @sequence_number = 1; 96 | 97 | /* Grant access to the profile to all msdb database users */ 98 | EXECUTE msdb.dbo.sysmail_add_principalprofile_sp 99 | @profile_name = '$profileName', 100 | @principal_name = 'public', 101 | @is_default = 1; 102 | "@ 103 | 104 | #write-log -level "Info" -message "Debug Command: $command" 105 | 106 | $result = Execute-SqlCommand -sqlScript $command -sqlInstance $instanceName 107 | 108 | if ($result -ne "Command(s) completed successfully.") 109 | { 110 | throw $result 111 | } 112 | -------------------------------------------------------------------------------- /PostScripts/400-Database-Resize-TempDb.ps1: -------------------------------------------------------------------------------- 1 | #/* 2005,2008,2008R2,2012,2014 */ 2 | 3 | ############################################################################################################### 4 | # PowerShell Script Template 5 | ############################################################################################################### 6 | # For use with the Auto-Install process 7 | # 8 | # When called, the script will recieve a single hashtable object passed as a 9 | # parameter. This object contains the following properties: 10 | # $params["SqlVersion"] - Version of SQL being installed (SQL2005, SQL2008, SQL2008R2) 11 | # $params["SqlEdition"] - Edition of SQL being installed (Standard, Enterprise) 12 | # $params["ProcessorArch"] - CPU Architecture (x86, X64) 13 | # $params["InstanceName"] - The name that the instance will be installed with (sqldev1) 14 | # $params["ServiceAccount"] - The name of the account to run the services under (domain\user) 15 | # $params["FilePath"] - path to save the configuration ini file 16 | # 17 | # To add additional parameters - add them to the hashtable passed to the Run-Install function 18 | # 19 | # This script should be placed in the appropriate scripts folder and will be automatically called during the 20 | # auto-install process. 21 | # 22 | # The script should be saved using the following naming convention: 23 | # 24 | # Pre Install Script (Save to PreScripts Folder) - 25 | # ------------------------------------------------- 26 | # Pre-100-OS-[ScriptName].ps1 27 | # Pre-200-Service-[ScriptName].ps1 28 | # 29 | # Post Install Script (Save to SQLScripts Folder) - 30 | # ------------------------------------------------- 31 | # 100-OS-[ScriptName].ps1 32 | # 200-Service-[ScriptName].ps1 33 | # 300-Server-[ScriptName].ps1 34 | # 400-Database-[ScriptName].ps1 35 | # 500-Table-[ScriptName].ps1 36 | # 600-View-[ScriptName].ps1 37 | # 700-Procedure-[ScriptName].ps1 38 | # 800-Agent-[ScriptName].ps1 39 | # 900-Management-[ScriptName].ps1 40 | ############################################################################################################### 41 | 42 | $configParams = $args[0] 43 | $instanceName = $configParams["InstanceName"] 44 | 45 | #Load the script parameters from the config file 46 | [array] $nodes = ($Global:ScriptConfigs | ?{$_.Name -eq "Resize-TempDB"}).SelectNodes("Param") 47 | $maxFileCount = ($nodes | ? {$_.Name -eq "MaxFileCount"}).Value 48 | $maxFileInitialSizeMB = ($nodes | ? {$_.Name -eq "MaxFileInitialSizeMB"}).Value 49 | $maxFileGrowthSizeMB = ($nodes | ? {$_.Name -eq "MaxFileGrowthSizeMB"}).Value 50 | $fileGrowthMB = ($nodes | ? {$_.Name -eq "FileGrowthMB"}).Value 51 | $coreMultiplier = ($nodes | ? {$_.Name -eq "CoreMultiplier"}).Value 52 | 53 | [array] $missing = $nodes | ? {$_.Value -eq ""} 54 | if ($missing.Count -gt 0) 55 | { 56 | return "Script not executed: please check the Run-Install.config file for missing configuration items" 57 | } 58 | 59 | $wmi = Get-WmiObject Win32_OperatingSystem 60 | [array] $procs = Get-WmiObject Win32_Processor 61 | 62 | #get the number of physical procs and cores 63 | $totalProcs = $procs.Count 64 | $totalCores = 0 65 | 66 | foreach ($proc in $procs) 67 | { 68 | $totalCores = $totalCores + $proc.NumberOfCores 69 | } 70 | 71 | #get the amount of total memory (MB) 72 | $totalMemory = ($wmi.TotalVisibleMemorySize / 1024) 73 | 74 | #calculate the number of files needed (= number of procs) 75 | $fileCount = $totalCores * $coreMultiplier 76 | 77 | if ($fileCount -gt $maxFileCount) 78 | { 79 | $fileCount = $maxFileCount 80 | } 81 | 82 | #calculate file size (total memory / number of files) 83 | $fileSize = $totalMemory / $fileCount 84 | 85 | if ($fileSize -gt $maxFileInitialSizeMB) 86 | { 87 | $fileSize = $maxFileInitialSizeMB 88 | } 89 | 90 | #build the sql command 91 | $command = " 92 | declare @data_path varchar(300); 93 | 94 | select 95 | @data_path = replace([filename], '.mdf','') 96 | from 97 | sysaltfiles s 98 | where 99 | name = 'tempdev'; 100 | 101 | ALTER DATABASE [tempdb] MODIFY FILE ( NAME = N'tempdev', SIZE = {0}MB , MAXSIZE = {1}MB, FILEGROWTH = {2}MB ); 102 | " -f $fileSize, $maxFileGrowthSizeMB, $fileGrowthMB 103 | 104 | for ($i = 2; $i -le $fileCount; $i++) 105 | { 106 | $command = $command + " 107 | declare @stmnt{3} nvarchar(500) 108 | select @stmnt{3} = N'ALTER DATABASE [tempdb] ADD FILE ( NAME = N''tempdev{3}'', FILENAME = ''' + @data_path + '{3}.mdf'' , SIZE = {0}MB , MAXSIZE = {1}MB, FILEGROWTH = {2}MB )'; 109 | exec sp_executesql @stmnt{3}; 110 | " -f $fileSize, $maxFileGrowthSizeMB, $fileGrowthMB, $i 111 | } 112 | 113 | #execute the sql command 114 | Write-Log -level "Info" -message "Resizing TempDB to contain $fileCount files that are each $fileSize MB in size" 115 | 116 | Execute-SqlCommand -sqlScript $command -sqlInstance $instanceName 117 | 118 | Write-Log -level "Info" -message "Resizing TempDB Complete" 119 | -------------------------------------------------------------------------------- /Common/Execute-ScriptFiles.ps1: -------------------------------------------------------------------------------- 1 | Function Execute-ScriptFiles 2 | { 3 | param 4 | ( 5 | [Parameter(Position=0, Mandatory=$true)] $configParams, 6 | [Parameter(Position=1, Mandatory=$true)] [ValidateSet("pre", "post")] $sequence 7 | ) 8 | 9 | [string] $sqlVersion = $configParams.SqlVersion 10 | [string] $sqlInstance = $configParams.InstanceName 11 | 12 | #Load a list of all files from the appropriate scripts folder (\\ray\dba_public$\cur_installs\SqlScripts) - Added exclusion for files that start with "_" 13 | if ($sequence -eq "pre") 14 | { 15 | #write-log -level "Info" -message ("PreScripts: " + $Global:PreScripts) 16 | #[array] $files = (get-childitem -path $Global:PreScripts | ?{-not ($_.Name -like "_*")}) 17 | [array] $files = get-childitem -path $Global:PreScripts 18 | $count = $files.Count 19 | $message = "Applying $count Standard Pre-Install Scripts" 20 | } 21 | else 22 | { 23 | #write-log -level "Info" -message ("PostScripts: " + $Global:PostScripts) 24 | #[array] $files = (get-childitem -path $Global:PostScripts | ?{-not ($_.Name -like "_*")}) 25 | [array] $files = get-childitem -path $Global:PostScripts 26 | $count = $files.Count 27 | $message = "Applying $count Standard Post-Install Scripts" 28 | } 29 | 30 | #Sort the list on file name (file name should start with script level - 100 OS, 200 Service, 300 Server, 400 Database, 500 Table, 600 View, 700 Procedure, 800 Agent) 31 | $files = $files | Sort-Object 32 | 33 | #write-log -level "Info" -message $files 34 | 35 | Write-Log -level "Section" -message $message 36 | 37 | #Loop through the list 38 | foreach ($file in $files) 39 | { 40 | #Read the first line of each file (/* 2005,2008,2008R2 */) 41 | [string] $strSupported = Get-Content -Path $file.FullName -TotalCount 1 42 | 43 | #Clean off the /* */ from the first line and split to a string array 44 | #Original code - .Net method using the replace method of the string object 45 | #[array] $arySupported = $strSupported.Replace('/*', '').Replace('*/', '').Trim().Split(',') 46 | #Code showing the PowerShell + RegEx method of performing the same task 47 | $arySupported = $strSupported -replace '#' -replace '/\*|\*/' -replace '^\s+|\s+$' -split ',' 48 | 49 | if (-not ($file.Name -like "_*")) 50 | { 51 | #If the current version is contained in the array then execute the script using the appropriate execute function 52 | if ($arySupported -contains $($sqlVersion -replace "SQL")) 53 | { 54 | if ($file.Extension -eq ".sql") 55 | { 56 | #Write the script name and results (query results or skipped) to the log 57 | Write-Log -level "Info" -message "##########################################################################################" 58 | Write-Log -level "Info" -message "# Executing SQL Script - $file" 59 | Write-Log -level "Info" -message "##########################################################################################" 60 | $strResult = Execute-SQL -sqlScript $file.FullName -sqlInstance $sqlInstance 61 | 62 | if ($strResult -eq "Command(s) completed successfully.") 63 | { 64 | Write-Log -level "Info" -message "$file - $strResult" 65 | } 66 | elseif ($strResult -like '*A transport-level error has occurred when sending the request to the server*') 67 | { 68 | $strResult = Execute-SQL -sqlScript $file.FullName -sqlInstance $sqlInstance 69 | 70 | if ($strResult -eq "Command(s) completed successfully.") 71 | { 72 | Write-Log -level "Info" -message "$file - $strResult" 73 | } 74 | else 75 | { 76 | Write-Log -level "Warning" -message "$file - Failed: $strResult" 77 | } 78 | } 79 | else 80 | { 81 | Write-Log -level "Warning" -message "$file - Failed: $strResult" 82 | } 83 | } 84 | elseif ($file.Extension -eq ".ps1") 85 | { 86 | #Write the script name and results (query results or skipped) to the log 87 | Write-Log -level "Info" -message "##########################################################################################" 88 | Write-Log -level "Info" -message "# Executing PowerShell Script - $file" 89 | Write-Log -level "Info" -message "##########################################################################################" 90 | 91 | try 92 | { 93 | $strResult = Execute-Powershell -psScript $file.FullName -configParams $configParams 94 | 95 | if ($strResult -eq "" -or $strResult -eq $null) 96 | { 97 | Write-Log -level "Info" -message "$file - Command(s) completed successfully." 98 | } 99 | else 100 | { 101 | Write-Log -level "Info" -message "$file - $strResult" 102 | } 103 | } 104 | catch 105 | { 106 | $strResult = $_ 107 | Write-Log -level "Warning" -message "$file - Failed: $strResult" 108 | } 109 | } 110 | else 111 | { 112 | Write-Log -level "Attention" -message "Skipping Script - $file - Unsupported script type" 113 | } 114 | } 115 | else 116 | { 117 | Write-Log -level "Attention" -message "Skipping Script - $file - Does not apply to this SQL version" 118 | } 119 | } 120 | else 121 | { 122 | Write-Log -level "Attention" -message "Skipping Script - $file - Script has been marked as excluded (filename starts with '_')" 123 | } 124 | #Next 125 | } 126 | Write-Log -level "Info" -message "Standard Scripts Complete" 127 | } 128 | -------------------------------------------------------------------------------- /PostScripts/800-Agent-CreateDatabaseMailCleanupJob.sql: -------------------------------------------------------------------------------- 1 | /* 2005,2008,2008R2,2012,2014,2016 */ 2 | 3 | /***************************************************************************************************** 4 | * Auto-Install Script Template 5 | *---------------------------------------------------------------------------------------------------- 6 | * 7 | * Instructions: 8 | * The top line of this document must contain a commented comma seperated list of the versions of SQL 9 | * that this script applies to. Example: "/* 2000, 2005, 2008, 2008R2 */" 10 | * 11 | * This script template is only suitable for statements that are to be executed as part of the 12 | * auto-install process and must run only against the server instance being installed. 13 | * 14 | * The script must terminate each statement using the ";" operator and must not contain the keyword 15 | * "GO". 16 | * 17 | * This template does not support scripts that need to be called with parameters. If your script 18 | * requires parameters please use the PowerShell Script template. 19 | * 20 | * Scripts must be named using the following pattern: 21 | * level-level name-script name 22 | * 23 | * level: The numeric level of the script. This controls the order in which scripts are applied to 24 | * ensure that dependancies are not broken. See Level list for the possible values. 25 | * 26 | * level name: The friendly name of the level. This is meant to makes the scripts more easily 27 | * identifiable. See Level list for the possible values. 28 | * 29 | * script name: The friendly name of the script. This should be short, but detailed enought to tell 30 | * what the script will accomplish. 31 | * 32 | * Example: "300-Server-AddExtendedProperty.sql" - Server level script that adds the DBA Extended 33 | * property to the master and model databases 34 | * 35 | * Level List: 36 | * --------------- 37 | * 300 - Server - Scripts that create/alter/drop server level objects and settings 38 | * 400 - Database - Scripts that create/alter/drop databases and settings 39 | * 500 - Table - Scripts that create/alter/drop tables, schemas, users, roles 40 | * 600 - View - Scripts that create/alter/drop views, indexes, or other objects with table dependancies 41 | * 700 - Procedure - Scripts that create/alter/drop objects with table/view dependancies 42 | * 800 - Agent - Scripts that create/alter/drop agent jobs, job steps, job schedules, notifications, etc 43 | * 900 - Management - Scripts that are used for management operations 44 | *****************************************************************************************************/ 45 | 46 | /***************************************************************************************************** 47 | * Script Information 48 | *---------------------------------------------------------------------------------------------------- 49 | * Author: Michael Wells 50 | * Date: 12/1/2010 51 | * Description: Create the agent job that purges Database Mail history 52 | * History: 12/1/2010 Adapted for Auto-Install 53 | *****************************************************************************************************/ 54 | 55 | USE [msdb]; 56 | 57 | BEGIN TRANSACTION 58 | DECLARE @ReturnCode INT 59 | SELECT @ReturnCode = 0 60 | 61 | IF NOT EXISTS (SELECT name FROM msdb.dbo.syscategories WHERE name=N'Database Maintenance' AND category_class=1) 62 | BEGIN 63 | EXEC @ReturnCode = msdb.dbo.sp_add_category @class=N'JOB', @type=N'LOCAL', @name=N'Database Maintenance' 64 | IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback 65 | 66 | END 67 | 68 | DECLARE @jobId BINARY(16) 69 | EXEC @ReturnCode = msdb.dbo.sp_add_job @job_name=N'DatabaseMail Cleanup', 70 | @enabled=1, 71 | @notify_level_eventlog=0, 72 | @notify_level_email=0, 73 | @notify_level_netsend=0, 74 | @notify_level_page=0, 75 | @delete_level=0, 76 | @description=N'No description available.', 77 | @category_name=N'Database Maintenance', 78 | @owner_login_name=N'sa', @job_id = @jobId OUTPUT 79 | IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback 80 | /****** Object: Step [Archive mail and log over 1 month old] Script Date: 10/29/2007 16:18:48 ******/ 81 | EXEC @ReturnCode = msdb.dbo.sp_add_jobstep @job_id=@jobId, @step_name=N'Archive mail and log over 1 month old', 82 | @step_id=1, 83 | @cmdexec_success_code=0, 84 | @on_success_action=1, 85 | @on_success_step_id=0, 86 | @on_fail_action=2, 87 | @on_fail_step_id=0, 88 | @retry_attempts=0, 89 | @retry_interval=0, 90 | @os_run_priority=0, @subsystem=N'TSQL', 91 | @command=N'DECLARE @CopyDate nvarchar(20) ; 92 | SELECT @CopyDate = CONVERT(VARCHAR, DATEADD(MONTH, -1, GETDATE()), 101) 93 | 94 | EXECUTE msdb.dbo.sysmail_delete_mailitems_sp @sent_before = @CopyDate ; 95 | EXECUTE msdb.dbo.sysmail_delete_log_sp @logged_before = @CopyDate ;', 96 | @database_name=N'msdb', 97 | @flags=0 98 | IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback 99 | EXEC @ReturnCode = msdb.dbo.sp_update_job @job_id = @jobId, @start_step_id = 1 100 | IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback 101 | EXEC @ReturnCode = msdb.dbo.sp_add_jobschedule @job_id=@jobId, @name=N'Every month', 102 | @enabled=1, 103 | @freq_type=32, 104 | @freq_interval=7, 105 | @freq_subday_type=1, 106 | @freq_subday_interval=0, 107 | @freq_relative_interval=1, 108 | @freq_recurrence_factor=1, 109 | @active_start_date=20060328, 110 | @active_end_date=99991231, 111 | @active_start_time=0, 112 | @active_end_time=235959 113 | IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback 114 | EXEC @ReturnCode = msdb.dbo.sp_add_jobserver @job_id = @jobId, @server_name = N'(local)' 115 | IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback 116 | COMMIT TRANSACTION 117 | GOTO EndSave 118 | QuitWithRollback: 119 | IF (@@TRANCOUNT > 0) ROLLBACK TRANSACTION 120 | EndSave: 121 | 122 | -------------------------------------------------------------------------------- /PreScripts/100-OS-Verify-Drives.ps1: -------------------------------------------------------------------------------- 1 | #/* 2005,2008,2008R2,2012,2014,2016 */ 2 | 3 | ############################################################################################################### 4 | # PowerShell Script Template 5 | ############################################################################################################### 6 | # For use with the Auto-Install process 7 | # 8 | # When called, the script will recieve a single hashtable object passed as a 9 | # parameter. This object contains the following properties: 10 | # $params["SqlVersion"] - Version of SQL being installed (SQL2005, SQL2008, SQL2008R2) 11 | # $params["SqlEdition"] - Edition of SQL being installed (Standard, Enterprise) 12 | # $params["ProcessorArch"] - CPU Architecture (x86, X64) 13 | # $params["InstanceName"] - The name that the instance will be installed with (sqldev1) 14 | # $params["ServiceAccount"] - The name of the account to run the services under (domain\user) 15 | # $params["FilePath"] - path to save the configuration ini file 16 | # 17 | # To add additional parameters - add them to the hashtable passed to the Run-Install function 18 | # 19 | # This script should be placed in the appropriate scripts folder and will be automatically called during the 20 | # auto-install process. 21 | # 22 | # The script should be saved using the following naming convention: 23 | # 24 | # Pre Install Script (Save to PreScripts Folder) - 25 | # ------------------------------------------------- 26 | # Pre-100-OS-[ScriptName].ps1 27 | # Pre-200-Service-[ScriptName].ps1 28 | # 29 | # Post Install Script (Save to SQLScripts Folder) - 30 | # ------------------------------------------------- 31 | # 100-OS-[ScriptName].ps1 32 | # 200-Service-[ScriptName].ps1 33 | # 300-Server-[ScriptName].ps1 34 | # 400-Database-[ScriptName].ps1 35 | # 500-Table-[ScriptName].ps1 36 | # 600-View-[ScriptName].ps1 37 | # 700-Procedure-[ScriptName].ps1 38 | # 800-Agent-[ScriptName].ps1 39 | ############################################################################################################### 40 | 41 | $configParams = $args[0] 42 | 43 | $filePath = $configParams["FilePath"] 44 | $scriptFolder = $configParams["WindowsScriptsFolder"] 45 | $dbaDrive = $filePath.Substring(0,2) 46 | $scriptsDrive = $scriptFolder.Substring(0,2) 47 | 48 | #Load the script parameters from the config file 49 | [array] $nodes = ($Global:ScriptConfigs | ?{$_.Name -eq "Verify-Drives"}).SelectNodes("Param") 50 | $paramFailOnFileSystem = ($nodes | ? {$_.Name -eq "FailOnFileSystem"}).Value 51 | $paramFailOnDiskAlign = ($nodes | ? {$_.Name -eq "FailOnDiskAlign"}).Value 52 | $paramFailOnCompressed = ($nodes | ? {$_.Name -eq "FailOnCompressed"}).Value 53 | 54 | [array] $missing = $nodes | ? {$_.Value -eq ""} 55 | if ($missing.Count -gt 0) 56 | { 57 | return "Script not executed: please check the Run-Install.config file for missing configuration items" 58 | } 59 | 60 | # [array] $drives = gwmi win32_logicaldisk | where {$_.DriveType -eq 3 -or $_.Drive.Type -eq 4} 61 | # foreach($drive in $drives) 62 | # { 63 | # $path = $drive.DeviceID + "\test_file.txt" 64 | # New-Item $path -itemType File | out-null 65 | # Write-Log -level "Info" -message "Test file written - $path" 66 | # Remove-Item $path 67 | # Write-Log -level "Info" -message "Test file deleted - $path" 68 | 69 | # if($drive.DeviceID -eq "C:") 70 | # { 71 | # Create-Folder 'C:\Windows\Script' 72 | # } 73 | 74 | # if($drive.DeviceID -eq $dbaDrive) 75 | # { 76 | # Create-Folder "$dbaDrive\Tools" 77 | # Create-Folder "$dbaDrive\Tools\Install" 78 | # Create-Folder "$dbaDrive\sqlrec" 79 | # } 80 | # } 81 | 82 | [array] $drives = gwmi win32_Volume | where {$_.DriveType -eq 3 -or $_.Drive.Type -eq 4} 83 | foreach($drive in $drives) 84 | { 85 | #Check File System 86 | if ($drive.FileSystem -ne "NTFS") 87 | { 88 | if ($paramFailOnFileSystem -eq 1) 89 | { 90 | write-log -level "Error" -message "The $($drive.DriveLetter) drive is formatted as $($drive.FileSystem)" 91 | } 92 | else 93 | { 94 | write-log -level "Warning" -message "The $($drive.DriveLetter) drive is formatted as $($drive.FileSystem)" 95 | } 96 | } 97 | else 98 | { 99 | write-log -level "Info" -message "The $($drive.DriveLetter) drive is formatted as $($drive.FileSystem)" 100 | } 101 | 102 | #Check disk alignment 103 | if ($drive.BlockSize -ne "4096") 104 | { 105 | if ($paramFailOnDiskAlign -eq 1) 106 | { 107 | write-log -level "Error" -message "The $($drive.DriveLetter) drive does not appear to be disk aligned - Block Size:$($drive.BlockSize)" 108 | } 109 | else 110 | { 111 | write-log -level "Warning" -message "The $($drive.DriveLetter) drive does not appear to be disk aligned - Block Size:$($drive.BlockSize)" 112 | } 113 | } 114 | else 115 | { 116 | write-log -level "Info" -message "The $($drive.DriveLetter) drive appears to be disk aligned - Block Size:$($drive.BlockSize)" 117 | } 118 | 119 | #Check disk compression 120 | if ($drive.Compressed -eq $true) 121 | { 122 | if ($paramFailOnCompressed -eq 1) 123 | { 124 | write-log -level "Error" -message "The $($drive.DriveLetter) drive is compressed - SQL Server cannot use a compressed drive" 125 | } 126 | else 127 | { 128 | write-log -level "Warning" -message "The $($drive.DriveLetter) drive is compressed - SQL Server cannot use a compressed drive" 129 | } 130 | } 131 | else 132 | { 133 | write-log -level "Info" -message "The $($drive.DriveLetter) drive is not compressed" 134 | } 135 | 136 | $path = $drive.DriveLetter + "\test_file.txt" 137 | New-Item $path -itemType File | out-null 138 | Write-Log -level "Info" -message "Test file written - $path" 139 | Remove-Item $path 140 | Write-Log -level "Info" -message "Test file deleted - $path" 141 | 142 | if($drive.DriveLetter -eq $scriptsDrive) 143 | { 144 | Create-Folder $scriptFolder 145 | } 146 | 147 | if($drive.DriveLetter -eq $dbaDrive) 148 | { 149 | Create-Folder "$filePath" 150 | Create-Folder "$filePath\Install" 151 | Create-Folder "$dbaDrive\sqlrec" 152 | } 153 | } -------------------------------------------------------------------------------- /Common/Create-ConfigFile.ps1: -------------------------------------------------------------------------------- 1 | Function Create-ConfigFile 2 | { 3 | param 4 | ( 5 | [Parameter(Position=0, Mandatory=$true)][hashtable] $params, 6 | [Parameter(Position=1, Mandatory=$false)][hashtable] $overrides 7 | ) 8 | 9 | #Get the template path from params 10 | $templateName = $params["TemplateName"] 11 | $category = $params["TemplateCategory"] 12 | 13 | #Define the required variables 14 | #Handle the fact that Evaluation editions don't use keys 15 | if ($params["SqlEdition"] -ne "Evaluation") 16 | { 17 | #PID="$Key" 18 | $Key = $params["ProductKey"] 19 | } 20 | 21 | #INSTANCENAME="$InstanceName" 22 | $InstanceName = $overrides["InstanceName"] 23 | 24 | #INSTANCEID="$InstanceId" 25 | $InstanceId = $overrides["InstanceName"] 26 | 27 | 28 | #FEATURES="$ProductString" 29 | $ProductString = $params["ProductString"] 30 | 31 | #X86="$X86" 32 | switch ($params["ProcessorArch"]) 33 | { 34 | "X86" {$X86 = "True"} 35 | "X64" {$X86 = "False"} 36 | default {$X86 = "False"} 37 | } 38 | 39 | #SQLSVCACCOUNT="$SqlSvcAccount" 40 | $sqlSvcAccount = $overrides["sqlSvcAccount"] 41 | 42 | #AGTSVCACCOUNT="$AgtSvcAccount" 43 | $agtSvcAccount = $overrides["agtSvcAccount"] 44 | 45 | #ISSVCACCOUNT="$IsSvcAccount" 46 | $isSvcAccount = $overrides["isSvcAccount"] 47 | 48 | #RSSVCACCOUNT="$rsSvcAccount" 49 | $rsSvcAccount = $overrides["rsSvcAccount"] 50 | 51 | #AsSVCACCOUNT="$asSvcAccount" 52 | $asSvcAccount = $overrides["asSvcAccount"] 53 | 54 | #FTSVCACCOUNT="$FTSvcAccount" 55 | $ftSvcAccount = $overrides["ftSvcAccount"] 56 | 57 | #Load the Config File Template into an array 58 | $templateFile = Join-Path -Path $Global:Templates -ChildPath $templateName 59 | [array] $template = gc $templateFile 60 | 61 | #copy the file to the destination path 62 | $configFile = Join-Path -Path $Global:RootPath -ChildPath "Configuration.ini" 63 | Copy-Item -Path $templateFile -Destination $configFile -Force 64 | Set-Content -Path $configFile -Value "" -Force 65 | 66 | #Load key/value pairs from the array into the $ini hashtable 67 | $ini = New-Object hashtable 68 | 69 | foreach ($line in $template) 70 | { 71 | if ($line -like "*=*") 72 | { 73 | $pair = $line -split "=" 74 | 75 | 76 | if (!$ini.ContainsKey($pair[0])) 77 | { 78 | # Invoke-Expression allows us to evaluate any variables stored in the template 79 | $val = Invoke-Expression $pair[1] 80 | Write-Log -Level Debug "Adding from Template: $($pair[0]) = $val" 81 | $ini.Add($pair[0], $val) 82 | } 83 | } 84 | } 85 | 86 | #Set the Slipstream update locations for installs other than SQL 2005 87 | if ($params["SqlVersion"] -ne "SQL2005") 88 | { 89 | $folders = Get-ChildItem -Path $Global:BinariesPath 90 | 91 | foreach ($folder in $folders) 92 | { 93 | if ($folder.Attributes -eq "Directory") 94 | { 95 | if ($folder.Name -like "SP*") 96 | { 97 | if ($ini.ContainsKey("PCUSOURCE")) 98 | { 99 | $ini["PCUSOURCE"] = $folder.FullName 100 | Write-Log -Level Debug "Found SP Folder, overriding: PCUSOURCE = $($folder.FullName)" 101 | } 102 | else 103 | { 104 | $ini.Add("PCUSOURCE", $folder.FullName) 105 | Write-Log -Level Debug "Found SP Folder, adding: PCUSOURCE = $($folder.FullName)" 106 | } 107 | } 108 | elseif ($folder.Name -like "CU*") 109 | { 110 | if ($ini.ContainsKey("CUSOURCE")) 111 | { 112 | $ini["CUSOURCE"] = $folder.FullName 113 | Write-Log -Level Debug "Found CU Folder, overriding: CUSOURCE = $($folder.FullName)" 114 | } 115 | else 116 | { 117 | $ini.Add("CUSOURCE", $folder.FullName) 118 | Write-Log -Level Debug "Found CU Folder, adding: CUSOURCE = $($folder.FullName)" 119 | } 120 | } 121 | } 122 | } 123 | 124 | #Remove the SP/CU configuration if there is nothing set 125 | if ($ini["PCUSOURCE"] -eq "") 126 | { 127 | $ini.Remove("PCUSOURCE") 128 | Write-Log -Level Debug "Removing PCUSOURCE: Empty" 129 | } 130 | if ($ini["CUSOURCE"] -eq "") 131 | { 132 | $ini.Remove("CUSOURCE") 133 | Write-Log -Level Debug "Removing CUSOURCE: Empty" 134 | } 135 | 136 | #Remove the PID if there is nothing set 137 | if ($params["SqlEdition"] -eq "Evaluation") 138 | { 139 | $ini.Remove("PID") 140 | Write-Log -Level Debug "Removing PID - Evaluation Version" 141 | } 142 | } 143 | #Add any entries from the overrides hastable 144 | foreach ($override in $overrides.GetEnumerator()) 145 | { 146 | if ($ini.ContainsKey($override.Key)) 147 | { 148 | Write-Log -Level Debug "Overriding: $($override.Key) = $($override.Value)" 149 | $ini[$override.Key] = $override.Value 150 | } 151 | else 152 | { 153 | Write-Log -Level Debug "Adding: $($override.Key) = $($override.Value)" 154 | $ini.Add($override.Key, $override.Value) 155 | } 156 | } 157 | 158 | $thisUser = "$($env:USERDOMAIN)\$($env:USERNAME)" 159 | if ($ini.ContainsKey("SQLSYSADMINACCOUNTS") -and $ini["SQLSYSADMINACCOUNTS"] -eq "") 160 | { 161 | Write-Log -Level Info "Defaulting: SQLSYSADMINACCOUNTS = $thisUser" 162 | $ini["SQLSYSADMINACCOUNTS"] = $thisUser 163 | } 164 | 165 | if (!($ini.ContainsKey("SQLSYSADMINACCOUNTS"))) 166 | { 167 | Write-Log -Level Info "Adding: SQLSYSADMINACCOUNTS = $thisUser" 168 | $ini.Add("SQLSYSADMINACCOUNTS", $thisUser) 169 | } 170 | 171 | #Loop thorugh the $ini hashtable writing each key/value pair to the ini file 172 | foreach($item in $ini.GetEnumerator() | sort -Property name) 173 | { 174 | #Special handling for Directory settings that may contain spaces and aren't already quoted 175 | if (($item.Key -like "*DIR" -OR $item.Key -like "*ACCOUNT") -and $item.Value -match '^[^/"]*$') 176 | { 177 | $value = "`"" + $item.Value + "`"" 178 | } 179 | else 180 | { 181 | $value = $item.Value 182 | } 183 | 184 | Set-PrivateProfileString -file $configFile -category $category -key $item.Key.ToString().ToUpper() -value $value 185 | } 186 | 187 | #Return the path of the ini file 188 | return $configFile 189 | } 190 | -------------------------------------------------------------------------------- /Examples/PostScripts/600-Function-CreateHallegrenDtabaseSelect.sql: -------------------------------------------------------------------------------- 1 | /* 2005,2008,2008R2,2012,2014,2016 */ 2 | CREATE FUNCTION [dbo].[DatabaseSelect] (@DatabaseList nvarchar(max)) 3 | 4 | RETURNS @Database TABLE (DatabaseName nvarchar(max) NOT NULL) 5 | 6 | AS 7 | 8 | BEGIN 9 | 10 | ---------------------------------------------------------------------------------------------------- 11 | --// Declare variables //-- 12 | ---------------------------------------------------------------------------------------------------- 13 | 14 | DECLARE @DatabaseItem nvarchar(max) 15 | DECLARE @Position int 16 | 17 | DECLARE @CurrentID int 18 | DECLARE @CurrentDatabaseName nvarchar(max) 19 | DECLARE @CurrentDatabaseStatus bit 20 | 21 | DECLARE @Database01 TABLE (DatabaseName nvarchar(max)) 22 | 23 | DECLARE @Database02 TABLE (ID int IDENTITY PRIMARY KEY, 24 | DatabaseName nvarchar(max), 25 | DatabaseStatus bit, 26 | Completed bit) 27 | 28 | DECLARE @Database03 TABLE (DatabaseName nvarchar(max), 29 | DatabaseStatus bit) 30 | 31 | DECLARE @Sysdatabases TABLE (DatabaseName nvarchar(max)) 32 | 33 | ---------------------------------------------------------------------------------------------------- 34 | --// Split input string into elements //-- 35 | ---------------------------------------------------------------------------------------------------- 36 | 37 | SET @DatabaseList = REPLACE(REPLACE(REPLACE(REPLACE(@DatabaseList,'[',''),']',''),'''',''),'"','') 38 | 39 | WHILE CHARINDEX(', ',@DatabaseList) > 0 SET @DatabaseList = REPLACE(@DatabaseList,', ',',') 40 | WHILE CHARINDEX(' ,',@DatabaseList) > 0 SET @DatabaseList = REPLACE(@DatabaseList,' ,',',') 41 | WHILE CHARINDEX(',,',@DatabaseList) > 0 SET @DatabaseList = REPLACE(@DatabaseList,',,',',') 42 | 43 | IF RIGHT(@DatabaseList,1) = ',' SET @DatabaseList = LEFT(@DatabaseList,LEN(@DatabaseList) - 1) 44 | IF LEFT(@DatabaseList,1) = ',' SET @DatabaseList = RIGHT(@DatabaseList,LEN(@DatabaseList) - 1) 45 | 46 | SET @DatabaseList = LTRIM(RTRIM(@DatabaseList)) 47 | 48 | WHILE LEN(@DatabaseList) > 0 49 | BEGIN 50 | SET @Position = CHARINDEX(',', @DatabaseList) 51 | IF @Position = 0 52 | BEGIN 53 | SET @DatabaseItem = @DatabaseList 54 | SET @DatabaseList = '' 55 | END 56 | ELSE 57 | BEGIN 58 | SET @DatabaseItem = LEFT(@DatabaseList, @Position - 1) 59 | SET @DatabaseList = RIGHT(@DatabaseList, LEN(@DatabaseList) - @Position) 60 | END 61 | IF @DatabaseItem <> '-' INSERT INTO @Database01 (DatabaseName) VALUES(@DatabaseItem) 62 | END 63 | 64 | ---------------------------------------------------------------------------------------------------- 65 | --// Handle database exclusions //-- 66 | ---------------------------------------------------------------------------------------------------- 67 | 68 | INSERT INTO @Database02 (DatabaseName, DatabaseStatus, Completed) 69 | SELECT DISTINCT DatabaseName = CASE WHEN DatabaseName LIKE '-%' THEN RIGHT(DatabaseName,LEN(DatabaseName) - 1) ELSE DatabaseName END, 70 | DatabaseStatus = CASE WHEN DatabaseName LIKE '-%' THEN 0 ELSE 1 END, 71 | 0 AS Completed 72 | FROM @Database01 73 | 74 | ---------------------------------------------------------------------------------------------------- 75 | --// Resolve elements //-- 76 | ---------------------------------------------------------------------------------------------------- 77 | 78 | WHILE EXISTS (SELECT * FROM @Database02 WHERE Completed = 0) 79 | BEGIN 80 | 81 | SELECT TOP 1 @CurrentID = ID, 82 | @CurrentDatabaseName = DatabaseName, 83 | @CurrentDatabaseStatus = DatabaseStatus 84 | FROM @Database02 85 | WHERE Completed = 0 86 | ORDER BY ID ASC 87 | 88 | IF @CurrentDatabaseName = 'SYSTEM_DATABASES' 89 | BEGIN 90 | INSERT INTO @Database03 (DatabaseName, DatabaseStatus) 91 | SELECT [name], @CurrentDatabaseStatus 92 | FROM sys.databases 93 | WHERE database_id <= 4 94 | END 95 | ELSE IF @CurrentDatabaseName = 'USER_DATABASES' 96 | BEGIN 97 | INSERT INTO @Database03 (DatabaseName, DatabaseStatus) 98 | SELECT [name], @CurrentDatabaseStatus 99 | FROM sys.databases 100 | WHERE database_id > 4 101 | END 102 | ELSE IF @CurrentDatabaseName = 'ALL_DATABASES' 103 | BEGIN 104 | INSERT INTO @Database03 (DatabaseName, DatabaseStatus) 105 | SELECT [name], @CurrentDatabaseStatus 106 | FROM sys.databases 107 | END 108 | ELSE IF CHARINDEX('%',@CurrentDatabaseName) > 0 109 | BEGIN 110 | INSERT INTO @Database03 (DatabaseName, DatabaseStatus) 111 | SELECT [name], @CurrentDatabaseStatus 112 | FROM sys.databases 113 | WHERE [name] LIKE REPLACE(@CurrentDatabaseName,'_','[_]') 114 | END 115 | ELSE 116 | BEGIN 117 | INSERT INTO @Database03 (DatabaseName, DatabaseStatus) 118 | SELECT [name], @CurrentDatabaseStatus 119 | FROM sys.databases 120 | WHERE [name] = @CurrentDatabaseName 121 | END 122 | 123 | UPDATE @Database02 124 | SET Completed = 1 125 | WHERE ID = @CurrentID 126 | 127 | SET @CurrentID = NULL 128 | SET @CurrentDatabaseName = NULL 129 | SET @CurrentDatabaseStatus = NULL 130 | 131 | END 132 | 133 | ---------------------------------------------------------------------------------------------------- 134 | --// Handle tempdb and database snapshots //-- 135 | ---------------------------------------------------------------------------------------------------- 136 | 137 | INSERT INTO @Sysdatabases (DatabaseName) 138 | SELECT [name] 139 | FROM sys.databases 140 | WHERE [name] <> 'tempdb' 141 | AND source_database_id IS NULL 142 | 143 | ---------------------------------------------------------------------------------------------------- 144 | --// Return results //-- 145 | ---------------------------------------------------------------------------------------------------- 146 | 147 | INSERT INTO @Database (DatabaseName) 148 | SELECT DatabaseName 149 | FROM @Sysdatabases 150 | INTERSECT 151 | SELECT DatabaseName 152 | FROM @Database03 153 | WHERE DatabaseStatus = 1 154 | EXCEPT 155 | SELECT DatabaseName 156 | FROM @Database03 157 | WHERE DatabaseStatus = 0 158 | 159 | RETURN 160 | 161 | ---------------------------------------------------------------------------------------------------- 162 | 163 | END 164 | -------------------------------------------------------------------------------- /Common/Write-Log.ps1: -------------------------------------------------------------------------------- 1 | function Write-Log 2 | { 3 | [CmdletBinding()] 4 | Param 5 | ( 6 | [Parameter(Mandatory=$true, 7 | ValueFromPipelineByPropertyName=$true)] 8 | [ValidateNotNullOrEmpty()] 9 | [Alias("LogContent")] 10 | [string]$Message, 11 | 12 | [Parameter(Mandatory=$false)] 13 | [Alias('LogPath')] 14 | [string]$Path='c:\Logs\Path.log', 15 | 16 | [Parameter(Mandatory=$false)] 17 | [ValidateSet("Error","Warn","Warning","Attention","Info","Header","Section","Debug")] 18 | [string]$Level="Info", 19 | 20 | [Parameter(Mandatory=$false)] 21 | [string]$Continuous=$true, 22 | 23 | [Parameter(Mandatory=$false)] 24 | [string]$LogType="FLAT" 25 | ) 26 | ## Use Parameters based on order, Passed-In, Global, then Default 27 | if (!$PSBoundParameters.ContainsKey("LogType") -and $Global:LogType) 28 | { 29 | $LogType = $Global:LogType 30 | } 31 | 32 | ## Use Parameters based on order, Passed-In, Global, then Default 33 | if (!$PSBoundParameters.ContainsKey("LogPath") -and $Global:LogPath) 34 | { 35 | $LogPath = $Global:LogPath 36 | } 37 | 38 | ## Use Parameters based on order, Passed-In, Global, then Default 39 | if (!$PSBoundParameters.ContainsKey("Continuous") -and $Global:LogContinuous) 40 | { 41 | $Continuous = $Global:LogContinuous 42 | } 43 | 44 | if (($Level -eq "Debug" -and !$Global:Debug)) { return } 45 | Write-To-Console -PassThru | Write-To-Flat-Log -PassThru | Write-To-Html -PassThru | Out-Null 46 | 47 | } 48 | 49 | function CreateLogFile { 50 | [CmdletBinding()] 51 | Param 52 | ( 53 | [Parameter(Mandatory=$true)] 54 | [string]$LogType 55 | ) 56 | process { 57 | 58 | $FormattedDate = Get-Variable -Name StartTime -Scope Global -ValueOnly -ErrorAction SilentlyContinue 59 | if (!$Continuous -and !$FormattedDate) 60 | { 61 | $FormattedDate = Get-Date -Format "yyyyMMddHHmmss" 62 | Set-Variable -Name StartTime -Scope Global -Value $FormattedDate 63 | } 64 | else 65 | { 66 | $FormattedDate = "" 67 | } 68 | 69 | switch($LogType) 70 | { 71 | "HTML" { $extension = "html" } 72 | default { $extension = "log" } 73 | } 74 | 75 | 76 | $logFile = (Join-Path $LogPath "SpadeInstaller_$($LogType)_$($FormattedDate).$($extension)") 77 | if (!(Test-Path $logFIle)) 78 | { 79 | $NewLogFile = New-Item $logFile -Force -ItemType File -WhatIf:$false 80 | } 81 | $logFile 82 | } 83 | } 84 | 85 | function Write-To-Console { 86 | param( 87 | [switch] $PassThru 88 | ) 89 | 90 | process { 91 | # Pass the the input to the next processor 92 | if($PassThru) {$_} 93 | 94 | 95 | # Leave if we're not writing this type of log 96 | if ((@('ALL','CONSOLE') | Where-Object { $LogType -Split ',' -contains $_ }).Length -eq 0) { return } 97 | # Create our log file 98 | $FormattedDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss" 99 | # Write message to error, warning, or verbose pipeline and specify $LevelText 100 | switch ($Level.ToString().ToUpper()) { 101 | 'Error' { 102 | Write-Host -ForegroundColor Black -BackgroundColor Red "$FormattedDate $($_): $Message" 103 | 104 | } 105 | { @('Warn','Warning') -contains $_ } { 106 | Write-Host -ForegroundColor Black -BackgroundColor Yellow "$FormattedDate $($_): $Message" 107 | } 108 | 'Attention' { 109 | Write-Host -ForegroundColor White -BackgroundColor Cyan "$FormattedDate $($_): $Message" 110 | } 111 | 'Info' { 112 | Write-Host "$FormattedDate $($_): $Message" 113 | } 114 | 'Debug' { 115 | Write-Host -ForegroundColor White -BackgroundColor Green "$FormattedDate $($_): $Message" 116 | } 117 | { @('Section','Header') -contains $_ } { 118 | Write-Host "$FormattedDate $($_): ------------ $Message -----------------" 119 | 120 | } 121 | } 122 | } 123 | 124 | } 125 | 126 | function Write-To-Flat-Log { 127 | param( 128 | [switch] $PassThru 129 | ) 130 | process 131 | { 132 | # Pass the the input to the next processor 133 | if($PassThru) {$_} 134 | 135 | # Leave if we're not writing this type of log 136 | if ((@('ALL','Flat') | Where-Object { $LogType -Split ',' -contains $_ }).Length -eq 0) { return } 137 | 138 | # Create our log file 139 | $logFile = CreateLogFile "FLAT" 140 | # Write message to error, warning, or verbose pipeline and specify $LevelText 141 | switch ($Level.ToString().ToUpper()) { 142 | 'Error' { 143 | $LevelText = 'ERROR:' 144 | } 145 | { @('Warn','Warning') -contains $_ } { 146 | $LevelText = 'WARN:' 147 | } 148 | 'Attention' { 149 | $LevelText = 'ATTENTION:' 150 | } 151 | 'Info' { 152 | $LevelText = 'INFO:' 153 | } 154 | 'Debug' { 155 | $LevelText = 'Debug:' 156 | } 157 | { @('Section','Header') -contains $_ } { 158 | $LevelText = $_ 159 | } 160 | } 161 | 162 | $FormattedDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss" 163 | # Write log entry to $Path 164 | "$FormattedDate $LevelText $Message" | Out-File -FilePath $logFile -Append -WhatIf:$false 165 | } 166 | } 167 | 168 | function Write-To-Html { 169 | param( 170 | [switch] $PassThru 171 | ) 172 | process 173 | { 174 | # Pass the the input to the next processor 175 | if($PassThru) {$_} 176 | 177 | # Leave if we're not writing this type of log 178 | if ((@('ALL','Html') | Where-Object { $LogType -Split ',' -contains $_ }).Length -eq 0) { return } 179 | 180 | # Create our log file 181 | $logFile = CreateLogFile "HTML" 182 | 183 | if (@('HTML') -contains $LogType -and !$Global:HtmlInitialized) 184 | { 185 | $Global:HtmlInitializated = $true 186 | '' | Out-File -FilePath $logFile -Append 187 | Write-Log -level "Header" -message "SQL Installer Run on $strComputer" 188 | Write-Log -level "Section" -message "Sample Messages" 189 | Write-Log -level "Warning" -message "Sample Warning" 190 | Write-Log -level "Error" -message "Sample Error" 191 | Write-Log -level "Attention" -message "Sample Notification" 192 | Write-Log -level "Info" -message "Sample Information" 193 | Write-Log -level "Info" -message "These styles can be modified by editing the Write-Log.ps1 file in the common scripts folder" 194 | Write-Log -level "Section" -message "Start Parameters" 195 | 196 | #Open the log file for the user 197 | #start-process iexplore.exe -argumentlist $Global:LogFile 198 | $noie = @() 199 | try 200 | { 201 | Invoke-Item -ErrorAction SilentlyContinue -ErrorVariable noie -Path $logFile -WhatIf:$false 202 | 203 | } 204 | catch 205 | { 206 | Write-Log -Level "Attention" -Message "Cound not start IE" 207 | } 208 | } 209 | 210 | $debugColor = "green" 211 | $attentionColor = "yellow" 212 | $warningColor = "orange" 213 | $errorColor = "red" 214 | 215 | Add-Type -AssemblyName System.Web 216 | $messageHtml = [System.Web.HttpUtility]::HtmlEncode($Message) 217 | 218 | $FormattedDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss" 219 | switch ($level.ToString().ToUpper()) 220 | { 221 | "Debug" {$string = "$FormattedDate $($_): $messageHtml
"} 222 | "Info" {$string = "$FormattedDate $($_): $messageHtml
"} 223 | "Attention"{$string = "$FormattedDate $($_): $messageHtml*
"} 224 | "Warning" {$string = "$FormattedDate $($_): $messageHtml
"} 225 | "Error" {$string = "$FormattedDate $($_): $messageHtml
"; if ($message -ne "Sample Error"){$Global:CriticalError = $true}} 226 | "Header" {$string = "

$messageHtml

"} 227 | "Section" {$string = "

$messageHtml

"} 228 | } 229 | $string | Out-File -FilePath $logFile -Append -WhatIf:$false 230 | 231 | } 232 | } -------------------------------------------------------------------------------- /Common/Copy-InstallFiles.ps1: -------------------------------------------------------------------------------- 1 | Function Copy-InstallFiles 2 | { 3 | param 4 | ( 5 | # [Parameter(Position=0, Mandatory=$true)] [string] $computerName, 6 | # [Parameter(Position=1, Mandatory=$true)] [string] $sqlVersion, 7 | # [Parameter(Position=2, Mandatory=$true)] [string] $sqlEdition, 8 | # [Parameter(Position=3, Mandatory=$false)] [bool] $isX86 = $false 9 | [Parameter(Position=0, Mandatory=$true)] [hashtable] $params, 10 | [Parameter(Position=1, Mandatory=$false)] [switch] $DontCopyLocal 11 | ) 12 | 13 | $sqlVersion = $params["SqlVersion"] 14 | $sqlEdition = $params["SqlEdition"] 15 | $scriptFolder = $params["WindowsScriptsFolder"] 16 | $modulesFolder = $params["PowerShellModulesFolder"] 17 | [bool] $isX86 = $false 18 | $installCU = Join-Path -Path $Global:Install -ChildPath "CU\" 19 | $installSP = Join-Path -Path $Global:Install -ChildPath "SP\" 20 | $installUpdates = Join-Path -Path $Global:Install -ChildPath "Updates\" 21 | $robocopyLog = Join-Path -Path $Global:LogPath -ChildPath "RoboCopyLogInstall.txt" 22 | $robocopyLogCU = Join-Path -Path $Global:LogPath -ChildPath "RoboCopyLogCU.txt" 23 | $robocopyLogSP = Join-Path -Path $Global:LogPath -ChildPath "RoboCopyLogSP.txt" 24 | $robocopyLogUpdates = Join-Path -Path $Global:LogPath -ChildPath "RoboCopyLogUpdates.txt" 25 | $copyDoneFlag = Join-Path -Path $Global:Install -ChildPath 'CopyComplete.txt' 26 | 27 | if (Test-Path $copyDoneFlag) 28 | { 29 | Write-Log -level "Info" -message "Installation files have already been copied" 30 | return 31 | } 32 | 33 | if ($modulesFolder -ne $null) 34 | { 35 | Write-Log -level "Info" -message "Copying PowerShell scripts to $scriptFolder" 36 | if($Global:Simulation) 37 | { 38 | Write-Log -Level Info -message "Copy-Item -Path ($Global:Scripts + '*.*') -Destination $scriptFolder -Recurse -Force" 39 | } 40 | else 41 | { 42 | if (Test-Path $Global:Scripts) 43 | { 44 | #Ensure that the script folder is formatted properly 45 | if (!$scriptFolder.EndsWith("\")) 46 | { 47 | $scriptFolder += "\" 48 | } 49 | 50 | #Check for the destination folder 51 | if (!(Test-Path $scriptFolder)) 52 | { 53 | New-Item -Path $scriptFolder -ItemType Directory 54 | } 55 | 56 | Copy-Item -Path ($Global:Scripts + '*.*') -Destination $scriptFolder -Recurse -Force 57 | } 58 | } 59 | Write-Log -level "Info" -message "Copy of PowerShell scripts to $scriptFolder complete" 60 | } 61 | else 62 | { 63 | Write-Log -level "Attention" -message "AppSetting WindowsScriptsFolder not defined: no scripts deployed" 64 | } 65 | 66 | if ($modulesFolder -ne $null) 67 | { 68 | Write-Log -level "Info" -message "Copying PowerShell modules to $modulesFolder" 69 | if($Global:Simulation) 70 | { 71 | Write-Log -Level Info -message "Copy-Item -Path ($Global:Modules + '*.*') -Destination $modulesFolder -Recurse -Force" 72 | } 73 | else 74 | { 75 | if (Test-Path $Global:Modules) 76 | { 77 | #Ensure that the script folder is formatted properly 78 | if (!$modulesFolder.EndsWith("\")) 79 | { 80 | $modulesFolder += "\" 81 | } 82 | 83 | #Check for the destination folder 84 | if (!(Test-Path $modulesFolder)) 85 | { 86 | New-Item -Path $modulesFolder -ItemType Directory 87 | } 88 | 89 | Copy-Item -Path $Global:Modules -Destination ($modulesFolder + "..") -Recurse -Force 90 | } 91 | } 92 | Write-Log -level "Info" -message "Copy of PowerShell modules to $modulesFolder complete" 93 | 94 | #Add the new module folder to the PSModulePath environment variable 95 | $CurrentPSModulePath = [Environment]::GetEnvironmentVariable("PSModulePath", "Machine") 96 | if ($CurrentPSModulePath.Split(";") -notcontains $modulesFolder) 97 | { 98 | #Add module path to current session 99 | $env:PSModulePath = $env:PSModulePath + ";$modulesFolder" 100 | 101 | #Persist module path for future sessions 102 | [Environment]::SetEnvironmentVariable("PSModulePath", $CurrentPSModulePath + ";$modulesFolder", "Machine") 103 | Write-Log -level "Info" -message "The following location has been added to PSModulePath: $modulesFolder" 104 | } 105 | else 106 | { 107 | Write-Log -level "Info" -message "The current PSModulePath already contains: $modulesFolder" 108 | } 109 | } 110 | else 111 | { 112 | Write-Log -level "Attention" -message "AppSetting PowerShellModulesFolder not defined: no PowerShell modules deployed" 113 | } 114 | 115 | if ($DontCopyLocal) 116 | { 117 | Write-Log -level "Info" -message 'DontCopyLocal selected - the installation binaries will not be copied to the server' 118 | } 119 | else 120 | { 121 | # $copySource = $Global:SourcePath 122 | # $copySource += $sqlVersion 123 | # $copySource += '\' 124 | $copySource = $Global:BinariesPath 125 | $copyCU = $Global:SourcePath + $sqlVersion + '\CU' 126 | $copySP = $Global:SourcePath + $sqlVersion + '\SP' 127 | $copyUpdates = $Global:SourcePath + $sqlVersion + '\Updates' 128 | 129 | # switch -wildcard ($sqlEdition) 130 | # { 131 | # "*32" {$copySource += ($sqlEdition -replace "32", "\x86"); $isX86 = $true} 132 | # "*64" {$copySource += ($sqlEdition -replace "64", "\x64")} 133 | # default {$copySource += $sqlEdition} 134 | # } 135 | 136 | Write-Log -level "Info" -message ('Beginning copy of install files from ' + $copySource) 137 | 138 | #Robo copy took the operation from 16 minutes to 4 minutes for SQL 2008 SP 1 Slipstream 139 | #Copy-Item -Path $copySource -Destination 'S:\Tools\Install' -ErrorAction Stop -Recurse -WarningAction Stop 140 | #Check for RoboCopy (part of the resource kit since NT4 and standard on Server 2008 an up) 141 | if (Test-Path "C:\Windows\System32\Robocopy.exe") 142 | { 143 | $robocopy = $true 144 | } 145 | else 146 | { 147 | $robocopy = $false 148 | Write-Log -level "Warning" -message "RoboCopy is not present on this server, so the copy operations will take much longer." 149 | } 150 | 151 | if ($robocopy) 152 | { 153 | if($Global:Simulation) 154 | { 155 | Write-Log -Level Info -message "robocopy $copySource $Global:Install /R:2 /W:5 /E /MT:8 /NP /LOG+:`"$robocopyLog`"" 156 | } 157 | else 158 | { 159 | robocopy $copySource $Global:Install /R:2 /W:5 /E /MT:8 /NP /LOG+:"$robocopyLog" 160 | } 161 | } 162 | else 163 | { 164 | if($Global:Simulation) 165 | { 166 | Write-Log -Level Info -message "Copy-Item -Path $copySource -Destination $Global:Install -Recurse" 167 | } 168 | else 169 | { 170 | Copy-Item -Path $copySource -Destination $Global:Install -Recurse 171 | } 172 | } 173 | 174 | #2005 doesn't support slipstream, so we need to copy the SP and CU installs seperately 175 | if ($sqlVersion -eq 'SQL2005') 176 | { 177 | if($isX86) 178 | { 179 | #$copyCU += '\x86\*.*' 180 | #$copySP += '\x86\*.*' 181 | $copyCU += '\x86\' 182 | $copySP += '\x86\' 183 | } 184 | else 185 | { 186 | #$copyCU += '\x64\*.*' 187 | #$copySP += '\x64\*.*' 188 | $copyCU += '\x64\' 189 | $copySP += '\x64\' 190 | } 191 | 192 | if($robocopy) 193 | { 194 | if($Global:Simulation) 195 | { 196 | Write-Log -Level Info -message "robocopy $copyCU $installCU /R:2 /W:5 /E /MT:8 /NP /LOG+:`"$robocopyLogCU`"" 197 | Write-Log -Level Info -message "robocopy $copySP $installSP /R:2 /W:5 /E /MT:8 /NP /LOG+:`"$robocopyLogSP`"" 198 | } 199 | else 200 | { 201 | robocopy $copyCU $installCU /R:2 /W:5 /E /MT:8 /NP /LOG+:"$robocopyLogCU" 202 | robocopy $copySP $installSP /R:2 /W:5 /E /MT:8 /NP /LOG+:"$robocopyLogSP" 203 | } 204 | } 205 | else 206 | { 207 | if($Global:Simulation) 208 | { 209 | Write-Log -Level Info -message "Copy-Item -Path $($copyCU + "*.*") -Destination $Global:Install -Recurse" 210 | Write-Log -Level Info -message "Copy-Item -Path $($copySP + "*.*") -Destination $Global:Install -Recurse" 211 | } 212 | else 213 | { 214 | Copy-Item -Path ($copyCU + "*.*") -Destination $Global:Install -Recurse 215 | Copy-Item -Path ($copySP + "*.*") -Destination $Global:Install -Recurse 216 | } 217 | } 218 | } 219 | elseif ($sqlVersion -gt 'SQL2008R2') # -and (Test-Path $installUpdates)) 220 | { 221 | #SQL 2012 replaced the SlipStreams functionality with ProductUpdates 222 | #so you no longer need to build the slipstream media 223 | if($robocopy) 224 | { 225 | if($Global:Simulation) 226 | { 227 | Write-Log -Level Info -message "robocopy $copyUpdates $installUpdates /R:2 /W:5 /E /MT:8 /NP /LOG+:`"$robocopyLogUpdates`"" 228 | } 229 | else 230 | { 231 | robocopy $copyUpdates $installUpdates /R:2 /W:5 /E /MT:8 /NP /LOG+:"$robocopyLogUpdates" 232 | } 233 | } 234 | else 235 | { 236 | if($Global:Simulation) 237 | { 238 | Write-Log -Level Info -message "Copy-Item -Path $copyUpdates -Destination $Global:Install -Recurse" 239 | } 240 | else 241 | { 242 | Copy-Item -Path $copyUpdates -Destination $Global:Install -Recurse 243 | } 244 | } 245 | } 246 | 247 | if(!$Global:Simulation) 248 | { 249 | Get-Date | Set-Content -Path $copyDoneFlag 250 | } 251 | 252 | Write-Log -level "Info" -message "Copy of install files complete" 253 | } 254 | } 255 | -------------------------------------------------------------------------------- /Run-Install.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 21 | 26 | 29 | 32 | 39 | 44 | 47 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | -------------------------------------------------------------------------------- /Run-Install.ps1: -------------------------------------------------------------------------------- 1 | function Run-Install 2 | { 3 | <# 4 | .SYNOPSIS 5 | Executes the SQL Server Auto-Install process. 6 | .DESCRIPTION 7 | Copies the required files from the configured source location, runs any 8 | pre-install tasks, installs the specified version of SQL Server, and then 9 | runs any post-install tasks. Supports common parameters -verbose, -whatif, and -confirm. 10 | .PARAM Parameters 11 | A hashtable containing the installation parameters. 12 | .PARAM TemplateOverrides 13 | A hashtable containing any SQL configuration file elements that need to be overriden. 14 | These entries will superceed both the configuration template and the config xml file. 15 | #> 16 | [CmdletBinding(SupportsShouldProcess=$true)] 17 | param 18 | ( 19 | [Parameter(Position=0,Mandatory=$true)][hashtable] $Parameters, 20 | [Parameter(Position=1,Mandatory=$false)][hashtable] $TemplateOverrides, 21 | [Parameter(Position=2,Mandatory=$false)][ValidateSet("Install", "InstallFailoverCluster", "AddNode")][string] $InstallAction = "Install", 22 | [Parameter(Position=3,Mandatory=$false)][switch] $Full, 23 | [Parameter(Position=4,Mandatory=$false)][switch] $PreOnly, 24 | [Parameter(Position=5,Mandatory=$false)][switch] $PostOnly, 25 | [Parameter(Position=6,Mandatory=$false)][switch] $InstallOnly, 26 | [Parameter(Position=7,Mandatory=$false)][switch] $DontCopyLocal, 27 | [Parameter(Position=8,Mandatory=$false)][switch] $ForceBinariesOverwrite 28 | 29 | ) 30 | 31 | #Capture the start time 32 | $start = Get-Date 33 | 34 | #Ensure that the execution policy is set correctly 35 | #Set-ExecutionPolicy RemoteSigned -force 36 | 37 | #Set the Critical Failure flag 38 | $Global:CriticalError = $false 39 | 40 | #Parse the parameters hashtable 41 | $sqlVersion = $Parameters["SqlVersion"] 42 | $sqlEdition = $Parameters["SqlEdition"] 43 | 44 | $procssorArch = $Parameters["ProcessorArch"] 45 | $dataCenter = $Parameters["DataCenter"] 46 | $sysAdminPassword = $Parameters["SysAdminPassword"] 47 | $environment = $Parameters["Environment"] 48 | 49 | $sqlSvcAccount = $TemplateOverrides["SqlSvcAccount"] 50 | $sqlSvcPassword = $TemplateOverrides["SqlSvcPassword"] 51 | $agtSvcAccount = $TemplateOverrides["agtSvcAccount"] 52 | $agtSvcPassword = $TemplateOverrides["agtSvcPassword"] 53 | $FTSvcAccount = $TemplateOverrides["ftSvcAccount"] 54 | $ftSvcPassword = $TemplateOverrides["ftSvcPassword"] 55 | $isSvcAccount = $TemplateOverrides["isSvcAccount"] 56 | $isSvcPassword = $TemplateOverrides["isSvcPassword"] 57 | $rsSvcAccount = $TemplateOverrides["rsSvcAccount"] 58 | $rsSvcPassword = $TemplateOverrides["rsSvcPassword"] 59 | $instanceName = $TemplateOverrides["InstanceName"] 60 | 61 | $Global:Debug = ($Parameters["Debug"] -eq "True" -or $PSBoundParameters['Debug']) 62 | $Global:Simulation = ($Parameters["Simulation"] -eq "True" -or $PSBoundParameters['WhatIf'] ) 63 | 64 | Write-Output "SPADE Installer Starting...." 65 | 66 | #Set the Global variables for Physical and Logical computer name 67 | #Default the logical computer name to the physical name when not provided 68 | $strComputer = gc env:computername 69 | $Global:PhysicalComputerName = $env:COMPUTERNAME 70 | $Global:LogicalComputerName = $Global:PhysicalComputerName 71 | if ($Parameters.ContainsKey("ComputerName")) 72 | { 73 | $Global:LogicalComputerName = $Parameters["ComputerName"] 74 | } 75 | 76 | ## Assume Local if not present 77 | if ($Parameters["FilePath"]) 78 | { 79 | $filePath = $Parameters["FilePath"] 80 | } 81 | else 82 | { 83 | $filePath = Join-Path (Split-Path -Path $MyInvocation.ScriptName) "\" 84 | } 85 | Set-Location $filePath 86 | 87 | #Load the XML configuration file 88 | $configFilePath = join-path -path $filePath -childPath "Run-Install.config" 89 | if ($Global:Debug) {Write-Output "Reading Config file: $configFilePath" } 90 | [xml] $settings = gc $configFilePath 91 | 92 | #Store the ScriptConfigs.Script nodes in a global variable 93 | [array] $Global:ScriptConfigs = $settings.InstallerConfig.ScriptConfigs.Script 94 | 95 | #Add parameters from the AppSettings node of the XML config file 96 | $add = $settings.InstallerConfig.AppSettings.Setting 97 | if ($add) 98 | { 99 | foreach ($key in $add) 100 | { 101 | if (!$Parameters.ContainsKey($key.Name)) 102 | { 103 | 104 | $var = Get-Variable -Name "$($key.Name)" -Scope Global -ValueOnly -ErrorAction silentlycontinue 105 | if (!$var) 106 | { 107 | if ($Global:Debug) {Write-Output "Adding Global Variable $($key.Name) = $($key.Value)"} 108 | New-Variable -Name "$($key.Name)" -Value $key.Value -Scope Global 109 | } else 110 | { 111 | if ($Global:Debug) {Write-Output "Setting Global Variable $($key.Name) = $($key.Value)"} 112 | Set-Variable -Name "$($key.Name)" -Scope Global -Value $key.Value 113 | } 114 | #The user didn't specify a value, so we will use the default from the config file 115 | $Parameters.Add($key.Name,$key.Value) 116 | } 117 | } 118 | } 119 | 120 | ## Validate DataCenter - minimum we need to get started 121 | $dcs = $settings.InstallerConfig.DataCenters.DataCenter | ?{$_.Name -eq $dataCenter} 122 | if (!($dcs)) 123 | { 124 | throw "You have selected an invalid Data Center: $dataCenter" 125 | } 126 | 127 | #Set the script level variables containing path info based on the Data Center location selected 128 | $Global:SourcePath = $dcs.FilePath 129 | 130 | ## Allow for an alternate Binary Location outside of Spade Sources, can be the same, or alternate 131 | $Global:RootPath = $filePath 132 | $Global:CommonScripts = (Join-Path $Global:RootPath "Common\") 133 | $Global:PreScripts = (Join-Path $Global:RootPath "PreScripts\") 134 | $Global:PostScripts = (Join-Path $Global:RootPath "PostScripts\") 135 | $Global:Modules = (Join-Path $Global:RootPath "Modules\") 136 | $Global:Packages = (Join-Path $Global:RootPath "Packages\") 137 | $Global:Templates = (Join-Path $Global:RootPath "Templates\") 138 | $Global:Install = (Join-Path $Global:RootPath "Install\") 139 | $Global:Scripts = (Join-Path $Global:SourcePath "PowerShellScripts\") 140 | 141 | $RootPathFolders = GCI -Path $Global:RootPath | Where-Object {$_.PSIsContainer -and $_.Name -ne "Install" -and $_.Name -notlike 'SQL*' } 142 | $SourcePathFolders = GCI -Path $Global:SourcePath | Where-Object {$_.PSIsContainer } 143 | 144 | ## Loop Through and Copy all folders found in Source 145 | foreach ($folder in $SourcePathFolders) 146 | { 147 | $target = $folder.FullName.Replace($Global:SourcePath, $Global:RootPath) 148 | if (Test-Path -Path $target) 149 | { 150 | if ($Global:Debug) { Write-Output "Removing $target" } 151 | Remove-Item -path $target -recurse -force 152 | 153 | } 154 | 155 | Copy-Item -path $folder.FullName -destination $target -recurse -Force 156 | 157 | #Dot-source everything in the common scripts folder 158 | if ($folder.Name -eq "Common") 159 | { 160 | 161 | Get-ChildItem "$target\*.ps1" | ForEach-Object {. $_.FullName; if ($Global:Debug) {Write-Output "Loading $($_.FullName)"} }#| Out-Null 162 | } 163 | 164 | } 165 | Write-Log -Level Info "Folders copied from source, modules loaded" 166 | 167 | #Make sure that the script is being run with admin rights 168 | [bool]$Admin = Verify-IsAdmin 169 | if(!$Admin) 170 | { 171 | Write-Log -Level Error "This script requires administrative rights." 172 | return 173 | } 174 | 175 | 176 | #Special Handling for SQL 2005 (seperate installers for 32 and 64 bit) 177 | if ($sqlVersion -eq "Sql2005") 178 | { 179 | switch ($procssorArch) 180 | { 181 | "X86" {$sqlEdition += "32"} 182 | "X64" {$sqlEdition += "64"} 183 | default {$sqlEdition += "64"} 184 | } 185 | $Parameters["SqlEdition"] = $sqlEdition 186 | } 187 | 188 | #Validate SqlVerion 189 | $versions = $settings.InstallerConfig.SqlVersions.Version | ?{$_.Name -eq $sqlVersion} 190 | if (!($versions)) 191 | { 192 | Write-Log -Level Error "You have selected an invalid/unsupported version of SQL Server: $sqlVersion" 193 | return 194 | } 195 | 196 | #Add TemplateName to Hashtable 197 | $template = $versions.ConfigurationTemplate | Select -ExpandProperty Name 198 | if (!($template)) 199 | { 200 | Write-Log -Level Error "Unable to locate configuration template for the specified sql version; $sqlVersion" 201 | return 202 | } 203 | else 204 | { 205 | if ($Parameters.ContainsKey("TemplateName")) 206 | { 207 | $Parameters["TemplateName"] = $template 208 | } 209 | else 210 | { 211 | $Parameters.Add("TemplateName", $template) 212 | } 213 | } 214 | 215 | #Add Template INI Category to Hashtable 216 | $category = $versions.ConfigurationTemplate | Select -ExpandProperty Category 217 | if (!($category)) 218 | { 219 | Write-Log -Level Error "Unable to locate configuration template category for the specified sql version: $sqlVersion" 220 | return 221 | } 222 | else 223 | { 224 | if (!$Parameters.ContainsKey("TemplateCategory")) 225 | { 226 | $Parameters.Add("TemplateCategory", $category) 227 | } 228 | } 229 | 230 | #Read the ProductStringName or set to default if missing 231 | $prod = (&{If($Parameters["ProductStringName"]) {$Parameters["ProductStringName"]} Else {"Default"}}) 232 | 233 | #Validate ProductString 234 | $productString = $versions.ProductStrings.ProductString | ?{$_.Name -eq $prod} | Select -ExpandProperty Value 235 | if (!($productString)) 236 | { 237 | Write-Log -Level Error "Unable to locate $prod product string for $sqlVersion in the config file or the product string is invalid" 238 | return 239 | } 240 | else 241 | { 242 | if ($Parameters.ContainsKey("ProductString")) 243 | { 244 | $Parameters["ProductString"] = $productString 245 | } 246 | else 247 | { 248 | $Parameters.Add("ProductString", $productString) 249 | } 250 | } 251 | 252 | #Validate SqlEdition 253 | $editions = $versions.Editions.Edition | ?{$_.Name -eq $sqlEdition}; 254 | if (!($editions)) 255 | { 256 | Write-Log -Level Error "You have selected an invalid/unsupported edition of SQL Server: $sqlEdition" 257 | return 258 | } 259 | 260 | #Validate Product Key 261 | $key = $editions.Key 262 | if (!($key)) 263 | { 264 | #Handle the fact that eval editions don't use keys 265 | if ($sqlEdition -ne "Evaluation") 266 | { 267 | Write-Log -Level Error "There is no matching product key in the configuration file for $sqlVersion - $sqlEdition edtion" 268 | return 269 | } 270 | } 271 | else 272 | { 273 | if (!$Parameters.ContainsKey("ProductKey")) 274 | { 275 | $Parameters.Add("ProductKey", $key) 276 | } 277 | } 278 | 279 | #Validate path to Install Binaries 280 | $binaryPath = $editions.FolderName 281 | if (!($binaryPath)) 282 | { 283 | Write-Log -Level Error "The FolderName property is missing in the configuration file for $sqlVersion - $sqlEdition edtion" 284 | return 285 | } 286 | 287 | ## Allow for an alternate Binary Location outside of Spade Sources, can be the same, or alternate 288 | if (!($dcs.BinaryPath)) 289 | { 290 | $Global:BinariesPath = (Join-Path $Global:SourcePath ($binaryPath + "\")) 291 | } 292 | else 293 | { 294 | $Global:BinariesPath = (Join-Path $dcs.BinaryPath ($binaryPath + "\")) 295 | } 296 | 297 | #Validate SQLServiceAccount 298 | if (!$sqlSvcAccount) 299 | { 300 | if (@('SQL2005', 'SQL2008','SQL2008R2') -contains $sqlVersion ) 301 | { 302 | $sqlSvcAccount = "NT AUTHORITY\NETWORK SERVICE" 303 | 304 | } 305 | else 306 | { 307 | if (!$instanceName ) 308 | { 309 | $sqlSvcAccount = "NT Service\MSSQLSERVER" 310 | } 311 | else 312 | { 313 | $sqlSvcAccount = "NT Service\MSSQL$" + $instanceName 314 | } 315 | } 316 | Write-Log -Level Info "Defaulting SQLSERVICEACCOUNT = $sqlSvcAccount" 317 | } 318 | else 319 | { 320 | if (!$sqlSvcPassword) 321 | { 322 | Write-Log -Level Error "sqlServiceAccount specified but sqlSvcPassword is blank" 323 | } 324 | else 325 | { 326 | if (!Test-Credential -UserName $sqlSvcAccount -Password $sqlSvcPassword) 327 | { 328 | Write-Log -Level Error "sqlSvcPassword does not appear to be valid" 329 | } 330 | } 331 | } 332 | 333 | 334 | #Validate AgtServiceAccount 335 | if (!$agtSvcAccount) 336 | { 337 | if (@('SQL2005', 'SQL2008','SQL2008R2') -contains $sqlVersion ) 338 | { 339 | $agtSvcAccount = "NT AUTHORITY\NETWORK SERVICE" 340 | } 341 | else 342 | { 343 | if (!$instanceName ) 344 | { 345 | $agtSvcAccount = "NT Service\SQLSERVERAGENT" 346 | } 347 | else 348 | { 349 | $agtSvcAccount = "NT SERVICE\SQLAGENT$" + $instanceName 350 | } 351 | } 352 | Write-Log -Level Info "Defaulting AgtServiceAccount = $agtSvcAccount" 353 | } 354 | else 355 | { 356 | if (!$agtSvcAccount) 357 | { 358 | Write-Log -Level Error "agtServiceAccount specified but agtSvcPassword is blank" 359 | } 360 | else 361 | { 362 | if (!Test-Credential -UserName $agtSvcAccount -Password $agtSvcPassword) 363 | { 364 | Write-Log -Level Error "agtSvcPassword does not appear to be valid" 365 | } 366 | } 367 | } 368 | 369 | #Validate ftServiceAccount 370 | if (!$FTSvcAccount) 371 | { 372 | if (@('SQL2005', 'SQL2008') -contains $sqlVersion ) 373 | { 374 | $FTSvcAccount = "NT AUTHORITY\NETWORK SERVICE" 375 | } 376 | else 377 | { 378 | if (!$instanceName ) 379 | { 380 | $FTSvcAccount = "NT Service\MSSQLFDLauncher" 381 | } 382 | else 383 | { 384 | $FTSvcAccount = "NT Service\MSSQLFDLauncher$" + $instanceName 385 | } 386 | } 387 | Write-Log -Level Info "Defaulting ftServiceAccount = $FTSvcAccount" 388 | } 389 | else 390 | { 391 | if (!$ftSvcPassword) 392 | { 393 | Write-Log -Level Error "ftServiceAccount specified but ftSvcPassword is blank" 394 | } 395 | else 396 | { 397 | if (!Test-Credential -UserName $FTSvcAccount -Password $ftSvcPassword) 398 | { 399 | Write-Log -Level Error "ftSvcPassword does not appear to be valid" 400 | } 401 | } 402 | } 403 | 404 | #Validate isServiceAccount 405 | if (!$isSvcAccount) 406 | { 407 | switch($sqlVersion) 408 | { 409 | 'SQL2016' { $isSvcAccount = 'NT SERVICE\MsDtsServer130';break } 410 | 'SQL2014' { $isSvcAccount = 'NT SERVICE\MsDtsServer120';break } 411 | 'SQL2012' { $isSvcAccount = 'NT SERVICE\MsDtsServer110';break } 412 | #'SQL2008R2' { $isSvcAccount = 'NT SERVICE\MsDtsServer100';break } 413 | default { $isSvcAccount = "NT AUTHORITY\NETWORK SERVICE"; break;} 414 | } 415 | Write-Log -Level Info "Defaulting isSvcAccount = $isSvcAccount" 416 | 417 | } 418 | else 419 | { 420 | if (!$isSvcPassword) 421 | { 422 | Write-Log -Level Error "isServiceAccount specified but isSvcPassword is blank" 423 | } 424 | else 425 | { 426 | if (!Test-Credential -UserName $isSvcAccount -Password $isSvcPassword) 427 | { 428 | Write-Log -Level Error "ISServiceAccount does not appear to be valid" 429 | } 430 | } 431 | } 432 | 433 | #Validate RsServiceAccount 434 | if (!$rsSvcAccount) 435 | { 436 | if (@('SQL2005', 'SQL2008','SQL2008R2') -contains $sqlVersion ) 437 | { 438 | $rsSvcAccount = "NT AUTHORITY\NETWORK SERVICE" 439 | } 440 | else 441 | { 442 | if (!$instanceName ) 443 | { 444 | $rsSvcAccount = "NT Service\ReportServer" 445 | } 446 | else 447 | { 448 | $rsSvcAccount = "NT SERVICE\ReportServer$" + $instanceName 449 | } 450 | } 451 | Write-Log -Level Info "Defaulting RsServiceAccount = $rsSvcAccount" 452 | } 453 | else 454 | { 455 | if (!$rsSvcAccount) 456 | { 457 | Write-Log -Level Error "rsServiceAccount specified but agtSvcPassword is blank" 458 | } 459 | else 460 | { 461 | if (!Test-Credential -UserName $rsSvcAccount -Password $rsSvcPassword) 462 | { 463 | Write-Log -Level Error "rsSvcPassword does not appear to be valid" 464 | } 465 | } 466 | } 467 | 468 | #Validate asServiceAccount 469 | if (!$asSvcAccount) 470 | { 471 | if (@('SQL2005', 'SQL2008') -contains $sqlVersion ) 472 | { 473 | $asSvcAccount = "NT AUTHORITY\NETWORK SERVICE" 474 | } 475 | else 476 | { 477 | if (!$instanceName ) 478 | { 479 | $asSvcAccount = "NT Service\SQLServerMSASUser$" + $strComputer + '$MSSQLSERVER' 480 | } 481 | else 482 | { 483 | $asSvcAccount = "NT SERVICE\SQLServerMSASUser$" + $strComputer + '$' + $instanceName 484 | } 485 | } 486 | Write-Log -Level Info "Defaulting asServiceAccount = $asSvcAccount" 487 | } 488 | else 489 | { 490 | if (!$asSvcAccount) 491 | { 492 | Write-Log -Level Error "asSvcAccount specified but asSvcPassword is blank" 493 | } 494 | else 495 | { 496 | if (!Test-Credential -UserName $asSvcAccount -Password $asSvcPassword) 497 | { 498 | Write-Log -Level Error "asSvcPassword does not appear to be valid" 499 | } 500 | } 501 | } 502 | 503 | $TemplateOverrides["sqlSvcAccount"] = $sqlSvcAccount 504 | $TemplateOverrides["agtSvcAccount"] = $agtSvcAccount 505 | $TemplateOverrides["ftSvcAccount"] = $ftSvcAccount 506 | $TemplateOverrides["isSvcAccount"] = $isSvcAccount 507 | $TemplateOverrides["rsSvcAccount"] = $rsSvcAccount 508 | $TemplateOverrides["asSvcAccount"] = $asSvcAccount 509 | 510 | #Validate SysAdminPassword 511 | if (!$sysAdminPassword) 512 | { 513 | $sysAdminPassword = Get-Strong-Password 514 | while (!(Validate-Strong-Password $sysAdminPassword)) 515 | { 516 | $sysAdminPassword = Get-Strong-Password 517 | } 518 | $FormattedDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss" 519 | $pwSavePath = (Join-Path $Global:LogPath "details.txt") 520 | $a = New-Item -Type File -Force -Path $pwSavePath 521 | "$($FormattedDate): $sysAdminPassword" | Out-File $pwSavePath -Append 522 | Write-Log -Level Attention "SysAdmin password missing, strong password created and saved $pwSavePath" 523 | } 524 | 525 | ## Validate Strong Password 526 | $strong = Validate-Strong-Password $sysAdminPassword 527 | if (!($strong)) 528 | { 529 | Write-Log -Level Error "SysAdmin Password does not meet minimum standards" 530 | return 531 | } 532 | 533 | 534 | 535 | #Validate FilePath 536 | if (!($filePath)) 537 | { 538 | Write-Log -Level Error "You must specify a file path" 539 | return 540 | } 541 | 542 | #Validate Environment 543 | $envs = $settings.InstallerConfig.Environments.Environment | ?{$_.Name -eq $environment} 544 | if (!($envs)) 545 | { 546 | $envs = "PROD" 547 | Write-Log -Level Attention "Environment Not Provided: Defaulting to PROD" 548 | } 549 | 550 | #Use default instance if none is provided 551 | if (!($instanceName)) 552 | { 553 | $instanceName = "MSSQLSERVER" 554 | } 555 | $TemplateOverrides['InstanceName'] = $instanceName 556 | 557 | #Cluster Specific Validations 558 | if($InstallAction -eq "InstallFailoverCluster" -or $InstallAction -eq "AddNode") 559 | { 560 | #Validate ClusterInstall Supported 561 | $clusterSupported = $versions.ClusterInstallSupported | Select -ExpandProperty Value 562 | if ($clusterSupported -eq $null -or $clusterSupported -eq $false) 563 | { 564 | Write-Log -Level Error "Clustering is not currently supported for: $sqlVersion" 565 | return 566 | } 567 | 568 | #Build Passive Node List 569 | if ($Parameters.ContainsKey("PassiveNodes")) 570 | { 571 | [array] $PassiveNodeList = $Parameters["PassiveNodes"].Split(",") 572 | } 573 | 574 | #Validate Cluster Disks 575 | $clusterDisks = $TemplateOverrides["FAILOVERCLUSTERDISKS"] 576 | if (!($clusterDisks)) 577 | { 578 | Write-Log -Level Error "You must specify the cluster disks when doing a clustered install" 579 | return 580 | } 581 | 582 | 583 | #Validate Cluster Name 584 | $clusterName = $TemplateOverrides["FAILOVERCLUSTERNETWORKNAME"] 585 | if (!($clusterName)) 586 | { 587 | Write-Log -Level Error "You must specify a cluster name when doing a clustered install" 588 | return 589 | } 590 | 591 | #Validate Cluster Address 592 | $clusterAddress = $TemplateOverrides["FAILOVERCLUSTERIPADDRESSES"] 593 | if (!($clusterAddress)) 594 | { 595 | Write-Log -Level Error "You must specify the cluster IP address when doing a clustered install" 596 | return 597 | } 598 | } 599 | 600 | if ($TemplateOverrides.Count -gt 0) 601 | { 602 | Write-Log -Level Debug "-------------- Template Override Contents - Command Line ----------------------" 603 | foreach ($key in $TemplateOverrides.Keys) 604 | { 605 | Write-Log -Level Debug "$Key = $($TemplateOverrides[$Key])" 606 | } 607 | } 608 | 609 | #Load Template Overrides from the XML Config file 610 | if (!($TemplateOverrides)) 611 | { 612 | $TemplateOverrides = New-Object hashtable 613 | } 614 | 615 | $to = $versions.TemplateOverrides.Setting 616 | 617 | foreach ($setting in $to) 618 | { 619 | if (!$TemplateOverrides.ContainsKey($setting.Name)) 620 | { 621 | $TemplateOverrides.Add($setting.Name,$setting.Value) 622 | } 623 | } 624 | 625 | Write-Log -Level Debug "-------------- Template Override Contents - From Settings ----------------------" 626 | foreach ($key in $TemplateOverrides.Keys) 627 | { 628 | $message = "{0,-25} = {1,-40}" -f $Key, $TemplateOverrides[$Key] 629 | Write-Log -Level Debug $message 630 | } 631 | 632 | 633 | 634 | Write-Log -Level Info "-------------- Parameters Contents ----------------------" 635 | foreach ($param in $Parameters.GetEnumerator()) 636 | { 637 | #For security reasons we will not display any passwords in the log 638 | if ($param.Key -match "password") 639 | { 640 | $message = "{0,-25} = {1,-40}" -f $param.Key, "********" 641 | } 642 | else 643 | { 644 | $message = "{0,-25} = {1,-40}" -f $param.Key, $param.Value 645 | } 646 | Write-Log -level Info -message $message 647 | } 648 | 649 | 650 | if ($ForceBinariesOverwrite) 651 | { 652 | #Remove the CopyComplete flag to force the SQL install files to be re-copied 653 | if (Test-Path (Join-Path $Global:Install "CopyComplete.txt")) 654 | { 655 | Remove-Item -path (Join-Path $Global:Install "CopyComplete.txt") 656 | } 657 | } 658 | 659 | if ($DontCopyLocal) 660 | { 661 | Copy-InstallFiles -params $Parameters -DontCopyLocal 662 | } 663 | else 664 | { 665 | Copy-InstallFiles -params $Parameters 666 | } 667 | 668 | #Call the code that generates the configuration ini file 669 | $configFile = Create-ConfigFile -params $Parameters -overrides $TemplateOverrides 670 | 671 | Write-Log -level INFO -message "Configuration file has been created and is located at $configFile" 672 | 673 | #execute pre-install checklist 674 | if (($PreOnly -eq $true -or $Full -eq $true) -and $Global:CriticalError -eq $false) 675 | { 676 | Write-Log -level SECTION -message "Starting Pre-Install Checklist" 677 | if ($pscmdlet.ShouldProcess("Execute Pre-Install Scripts", "Pre-Install")) #if (!$Global:Simulation) 678 | { 679 | Execute-ScriptFiles -configParams $Parameters -sequence "pre" 680 | } 681 | Write-Log -level INFO -message "Completed Pre-Install Checklist" 682 | } 683 | else 684 | { 685 | Write-Log -level SECTION -message "Skipping Pre-Install Checklist" 686 | } 687 | 688 | #This flag prevents the installation binaries from being copied to the server 689 | if ($DontCopyLocal) 690 | { 691 | [string] $command = $Global:BinariesPath 692 | } 693 | else 694 | { 695 | [string] $command = $Global:Install 696 | } 697 | 698 | [string] $arguments = '' 699 | 700 | if($sqlVersion -eq "Sql2005") 701 | { 702 | #Format the command to be executed by Start-Process (SQL 2005 has no output to console mode so we display a passive GUI to monitor progress) 703 | $command += 'Servers\setup.exe' 704 | $arguments = '/SETTINGS {0} /QB SAPWD="{1}" SQLPASSWORD="{2}" AGTPASSWORD="{2}" SQLBROWSERPASSWORD="{2}"' -f $configFile, $sysadminPassword, $sqlSvcAccount 705 | } 706 | else 707 | { 708 | #Format the command to be executed by Invoke-Expression (SQL 2008+ has the option to pass the log to the console to monitor progress) 709 | $command += 'setup.exe ' 710 | $arguments = '/CONFIGURATIONFILE=`"$configFile`" /SAPWD=`"$sysadminPassword`"' 711 | if ($sqlSvcAccount -and $sqlSvcPassword) 712 | { 713 | $arguments += ' /SQLSVCPASSWORD=`"$sqlSvcPassword`"' 714 | } 715 | if ($agtSvcAccount -and $agtSvcPassword) 716 | { 717 | $arguments += ' /AGTSVCPASSWORD=`"$agtSvcPassword`"' 718 | } 719 | if ($FTSvcAccount -and $ftSvcPassword) 720 | { 721 | $arguments += ' /FTSVCPASSWORD=`"$ftSvcPassword`"' 722 | } 723 | if ($isSvcAccount -and $isSvcPassword) 724 | { 725 | $arguments += ' /ISSVCPASSWORD=`"$isSvcPassword`"' 726 | } 727 | if ($rsSvcAccount -and $rsSvcPassword) 728 | { 729 | $arguments += ' /RSSVCPASSWORD=`"$rsSvcPassword`"' 730 | } 731 | } 732 | 733 | Write-Log -level DEBUG "Setup Command: $command" 734 | Write-Log -Level DEBUG "Arguments: $arguments" 735 | 736 | [int] $exitCode = 0 737 | 738 | #execte command 739 | if (($InstallOnly -eq $true -or $Full -eq $true) -and $Global:CriticalError -eq $false) 740 | { 741 | Write-Log -level SECTION -message "Starting SQL Server Install" 742 | if($sqlVersion -eq "Sql2005") 743 | { 744 | #Call the GUI (Basic) install process and wait for it to complete 745 | if ($pscmdlet.ShouldProcess("Start SQL 2005 Installer Package", "Install SQL")) #if (!$Global:Simulation) 746 | { 747 | Write-Log -Level Debug "Starting $command" 748 | Start-Process -FilePath $command -ArgumentList $arguments -Wait 749 | $exitCode = $lastexitcode 750 | Write-Log -Level Debug "Installer Exit Code: $exitCode" 751 | } 752 | } 753 | else 754 | { 755 | #Call the queit install process - because it writes to the console we don't have to force the code to wait 756 | if ($pscmdlet.ShouldProcess("Start SQL Installer Package", "Install SQL")) #if (!$Global:Simulation) 757 | { 758 | 759 | Write-Log -Level Debug "Starting $command" 760 | Write-Log -Level Debug "Arguments $arguments" 761 | Write-Log -Level Debug "ConfigFile: $configFile" 762 | $command += $arguments 763 | Invoke-Expression $command 764 | $exitCode = $lastexitcode 765 | Write-Log -Level Debug "Installer Exit Code: $exitCode" 766 | } 767 | } 768 | 769 | if ($exitCode -eq 0) 770 | { 771 | Write-Log -level INFO -message "Completed SQL Server Install" 772 | $Global:CriticalError = $false 773 | } 774 | else 775 | { 776 | Write-Log -level "Error" -message "SQL Server Install failed - please check the console output" 777 | $Global:CriticalError = $true 778 | } 779 | } 780 | else 781 | { 782 | Write-Log -level SECTION -message "Skipping SQL Server Install" 783 | } 784 | 785 | #execute post-install checklist 786 | if (($PostOnly -eq $true -or $Full -eq $true) -and $Global:CriticalError -eq $false) 787 | { 788 | Write-Log -level SECTION -message "Starting Post-Install Checklist" 789 | if ($pscmdlet.ShouldProcess("Execute Post-Install Scripts", "Post-Install")) #if (!$Global:Simulation) 790 | { 791 | $IsSysAdmin = Execute-SqlScalarQuery -sqlScript "select is_srvrolemember('sysadmin')" -sqlInstance $Parameters.InstanceName 792 | 793 | if($IsSysAdmin -eq 1) 794 | { 795 | Execute-ScriptFiles -configParams $Parameters -sequence "post" 796 | } 797 | else 798 | { 799 | Write-Log -level ERROR -message "The current user does not have sufficient permissions to run the Post-Install Checklist - please check permissions and run the process again with the '-PostOnly' option" 800 | } 801 | } 802 | Write-Log -level INFO -message "Completed Post-Install Checklist" 803 | } 804 | else 805 | { 806 | Write-Log -level SECTION -message "Skipping Post-Install Checklist" 807 | } 808 | 809 | #Capture end time 810 | $end = Get-Date 811 | $timeResult = ($end - $start) 812 | 813 | Write-Log -Level SECTION -Message "Script Time Results" 814 | Write-Log -Level INFO -Message "Script Duration: $timeResult" 815 | 816 | } --------------------------------------------------------------------------------