Chapter 9

Automate SQL Server administration

In the previous chapter, you learned about various tasks needed to maintain and monitor SQL Server. Managing these tasks can be time consuming and overwhelming, especially if you try to do them manually.

This chapter reviews common ways of automating Microsoft SQL Server instance administration, starting with an exploration of the tools that enable them.

Note

If you are unfamiliar with automating common administration tasks, this is an opportunity to grow and become a more seasoned DBA.

This chapter varies little for SQL Server instances on Windows and Linux except in the case of PowerShell. In some cases, you might have to run these commands from a Windows server. Where there are exceptions for Linux, we point them out.

Little in this chapter applies to Microsoft Azure SQL Database because many of the administration tasks are already automated, including most performance tuning and backup tasks. No initial configuration is needed. If you need more control, many of the features available in Azure SQL Database are being released through Azure SQL Managed Instance. As the Azure SQL platform as a service (PaaS) offering has matured, it has become a powerful cloud-based and complementary platform to SQL Server, neither fully replacing nor overlapping with the feature set or purpose of on-premises SQL Server instances.

Technically, the Azure elastic jobs feature is the automation framework for Azure SQL Database. However, it has been in preview for years. The Microsoft eventual road map for a generally available elastic jobs announcement or alternative solution is not public. Elastic jobs are nevertheless deployed in many Azure production environments. Read more about the current status of this preview feature at https://learn.microsoft.com/azure/azure-sql/database/job-automation-overview. If you are not comfortable with preview features handling automation tasks for your Azure SQL Database, consider:

Sample scripts in this chapter, and all scripts for this book, are all available for download at https://www.MicrosoftPressStore.com/SQLServer2022InsideOut/downloads.

Foundations of SQL Server automated administration

Since automation implies a mostly hands-off approach to repeatable tasks, DBAs need to understand two foundational automation tools:

  • Database Mail. Allows SQL Server to send emails to notify you of the outcome of SQL Server Agent jobs, server performance and error alerts, or custom notifications with Transact-SQL (T-SQL) calls to the dbo.sp_send_dbmail stored procedure (located in the msdb database).

    Note

    There are many ways (and products to buy) to be notified of an error or job failure in your SQL Server instance. Database mail is a built-in, easy, foundational notification feature to accomplish many notification tasks, as well as a platform for developing custom email-based reporting and notifications.

  • SQL Server Agent. The automation engine available in all editions of SQL Server except for Express. You can use SQL Server Agent to automate most maintenance tasks in SQL Server. It’s also available in Azure SQL Managed Instance, but not in Azure SQL Database. Let’s review these two key tools, both of which are fully supported on Windows and Linux.

Database Mail

Database Mail uses Simple Mail Transfer Protocol (SMTP) to send email. Email is handled asynchronously outside the SQL Server process, isolating both the process and any potential performance impact to the SQL Server instance. By design, this process is run outside SQL Server using a separate executable DatabaseMail.exe, which is started asynchronously using Service Broker.

Set up Database Mail

To begin sending automated emails, you must configure Database Mail. Then, configure SQL Server Agent to use the Database Mail profile you create.

In SQL SSMS, start the Database Mail Configuration Wizard by locating it in the Management folder of the server node you are configuring and selecting Configure Database Mail. You’ll need to set up a profile and then an associated account.

The wizard turns on the Database Mail feature in the Surface Area facet of the SQL Server instance. You need to do this only once. Database Mail is among a select few Surface Area facets that you should turn on for most SQL Server instances. To view surface area configuration settings in SSMS, open Object Explorer, connect to the SQL Server, right-click the server, and select Facets on the shortcut menu. (The Facets window sometimes takes a moment to load.) Then, in the dialog box that opens, change the value in the list box to Surface Area Configuration. Choose Database Mail and set the property value to True.

A Database Mail profile can be public or private. In the case of a private profile, only sysadmins and specific associated server principals are given access (users or roles in databases). A public profile allows any principal that is a member of the built-in database role DatabaseMailUsersRole in the msdb database.

Ideally, all Database Mail profiles are private. That way, by default only members of the sysadmin role are allowed access, and only those credentials that will be used to send emails will be given access. If you would like to grant additional database principals to access private database mail profiles, you can use the stored procedure msdb.dbo.sysmail_add_principalprofile_sp. This is crucial in a multitenant environment, or an environment that allows access to external developers or vendors, but even in internal environments, it could provide protection against malicious use to send emails.

When creating a Database Mail profile, you have the option to specify whether the profile will be the default—even if there is only one Database Mail profile created. If you choose a Database Mail profile to be the default, it will be used as the global profile for outgoing mail and will be used to send mail if a profile is not specified with the dbo.sp_send_dbmail stored procedure.

You can configure a Database Mail profile to use almost any SMTP configuration, including nonstandard ports and Secure Sockets Layer (SSL). You also can configure it with Windows Authentication (common for SMTP servers in the same domain), basic authentication (common for web authentication), or no authentication (common for anonymous relay in the local network, usually with an IP allow list).

You can configure Database Mail to use any SMTP server that it can reach, including web-based SMTP servers. You can even use Outlook web mail or another web-based email account if you’re configuring for testing purposes or have no other viable internal SMTP options. An internal SMTP server with Windows Authentication using a service account is preferred, though, because it gives you more control over your own environment.

Note

For Azure infrastructure-as-a-service (IaaS) environments without an internal SMTP presence, Twilio SendGrid is a common and supported SMTP solution. For more information, visit https://docs.sendgrid.com/for-developers/partners/microsoft-azure-2021.

After you configure your account’s SMTP settings (you’ll need to test them later), the Database Mail account will have several options that you can adjust:

  • Account Retry Attempts. Defaults to 1. You should probably leave this as is to avoid excessive retries that could lock out an account or trigger spam detection.

  • Account Retry Delay (seconds). Defaults to 60. Again, you should leave this as is, for the same reasons as for Account Retry Attempts.

  • Maximum File Size (Bytes). Defaults to roughly 1 MB. You should change this only if necessary.

  • Prohibited Attachment File Extensions. Specifies which file extensions cannot be sent. It is commonly set if third-party or multitenant development occurs on the SQL Server instance. This is a comma-delimited list that, by default, is “exe,dll,vbs,js.”

  • Database Mail Executable Minimum Lifetime (seconds). Defaults to 600 Seconds (10 minutes), which is a counter that starts after an email message is sent. If no other messages are sent in that time frame, the Database Mail executable stops. If stopped, the Database Mail process is started again any time a new email is sent. You’ll see messages indicating “Database Mail process is started” and “Database Mail process is shutting down” in the Database Mail log when this happens.

  • Logging Level. Defaults to Extended, which includes basic start/stop and error messages that should be kept in the Database Mail log. Change this to Verbose if you are troubleshooting Database Mail and need more information, or to Normal to suppress informational messages and see errors only.

After you’ve set up a Database Mail profile and account, you can send a test email via SSMS. Right-click Database Mail and select Send Test E-Mail on the shortcut menu that opens. Or you can send a plain-and-simple test email via T-SQL by using the following code:

exec msdb.dbo.sp_send_dbmail
@recipients ='[email protected]',
@subject ='test';

This code does not specify a @profile parameter, so the command will use the default profile for the current user, the default private profile if it exists, or the global (default public) profile. If you do not have a default Database Mail profile set up, you will get the following error:

Msg 14636, Level 16, State 1, Procedure msdb.dbo.sp_send_dbmail, Line 112 [Batch Start
Line 0]
No global profile is configured. Specify a profile name in the @profile_name parameter.

This is all that is necessary for developers and applications to send emails using Database Mail.

  • Image In case you have issues with setting up or sending emails, we cover troubleshooting later in this chapter in the “Troubleshoot Database Mail” section.

To allow SQL Server Agent to send emails based on job outcomes and alerts, you need to create an operator in SQL Server Agent, and then configure SQL Server Agent’s alert system to use a Database Mail profile. We look at SQL Server Agent and its initial configuration in depth later in this chapter.

Allow anonymous relay for anonymous authentication

If you’re using anonymous authentication internally with Microsoft Exchange, verify that the internal SMTP anonymous relay has a dedicated receive connector that allows for anonymous relay. By design, a receive connector just for anonymous relay should allow only a small list of internal hosts—your SQL Server instance(s) among them.

Maintain email history in the msdb database

The email messages attempted and sent by Database Mail are recorded and queued in a table in the msdb database named dbo.sysmail_mailitems. As you might suspect, data in the msdb tables for Database Mail will grow, potentially to an unmanageable size. This can cause queries to the msdb’s Database Mail tables to run for a long time. There is no automated process in place to maintain a retention policy for these tables, though there is a stored procedure to delete older messages, as well as a lengthy reference article in place to guide you through creating a set of archive tables and a SQL Server Agent job to maintain data over time. You can find both of these at https://learn.microsoft.com/sql/relational-databases/database-mail/create-a-sql-server-agent-job-to-archive-database-mail-messages-and-event-logs.

Troubleshoot Database Mail

We’ve already mentioned the Database Mail log. Now let’s go over the other diagnostics available for Database Mail.

Read email logs in the msdb database

If the SMTP Server or the Database Mail process becomes unavailable, the messages are queued in a table in the msdb database named dbo.sysmail_mailitems. This msdb database contains metadata tables for the Database Mail feature, including dbo.sysmail_allitems, which tracks all outbound email activity. Look for items for which the sent_status doesn’t equal sent for signs of messages that weren’t successfully sent; for example:

--Find recent unsent emails
SELECT m.send_request_date, m.recipients, m.copy_recipients, m.blind_copy_recipients
, m.[subject], m.send_request_user, m.sent_status
FROM msdb.dbo.sysmail_allitems AS m
WHERE
-- Only show recent day(s)
m.send_request_date > dateadd(day, -3, sysdatetime())
-- Possible values are sent (successful), unsent (in process),
-- retrying (failed but retrying), failed (no longer retrying)
AND m.sent_status <> 'sent'
ORDER BY m.send_request_date DESC;

There is also a view provided in msdb, dbo.sysmail_unsentitems, that filters on (sent_status = 'unsent' OR sent_status = 'retrying'). There are four possible values for sent_status in dbo.sysmail_allitems: sent, unsent, retrying, and failed.

Enable Service Broker on the msdb database

After restoring the msdb database or setting up Database Mail for the first time, the Service Broker feature might not be turned on for the msdb database. You can check the is_broker_enabled field in the system catalog view sys.databases; if it is 0, this is the case, and you must remedy it. Otherwise, if you try to send email and the Service Broker is disabled, you will receive the following self-explanatory error message:

Msg 14650, Level 16, State 1, Procedure msdb.dbo.sp_send_dbmail, Line 73 [Batch Start
Line 18] Service Broker message delivery is not enabled in this database. Use the ALTER
DATABASE statement to enable Service Broker message delivery.

To turn on Service Broker for the msdb database, you must stop the SQL Server Agent service and close any connections active to the msdb database before running the following code:

ALTER DATABASE msdb SET ENABLE_BROKER;
Identify SMTP server authentication

Authentication with the SMTP server is likely the problem if you observe errors in the Database Mail log after attempting to send email, such as:

Cannot send mails to mail server. (Mailbox unavailable. The server response was: 5.7.1
Unable to relay...

or:

Cannot send mails to mail server. (Mailbox unavailable. The server response was: 5.7.1
Service unavailable...

SQL Server Agent

SQL Server Agent is the native automation platform for internal task automation, maintenance, log and file retention, and even backups. SQL Server Agent is like Windows Task Scheduler (and cron on Linux), but it has several advantages for automating SQL Server tasks, including integration with SQL Server security, authentication, logging, and native T-SQL programming.

On Windows, SQL Server Agent can accomplish many of the same tasks as Windows Task Scheduler, including running operating system (CmdExec) and PowerShell commands. (CmdExec and PowerShell tasks are not available on Linux.) Metadata, configuration, and history data for the SQL Server Agent are kept in the msdb database.

Configure SQL Server Agent jobs

A job contains a series of steps. Each job step is of a type that allows for different actions to take place, such as the aforementioned T-SQL, CmdExec, or PowerShell tasks.

A job can be automatically started based on a number of conditions, including the following:

  • A predefined schedule or schedules

  • In response to an alert

  • As a result of running the dbo.sp_start_job stored procedure in the msdb database

  • When SQL Server Agent starts

  • When the host computer is idle

You can script jobs in their entirety through SSMS, providing script-level recoverability, migration to other servers, and source control possibility for SQL Server Agent jobs. Jobs are backed up and restored via the msdb database or scripted for backup and migration.

In SSMS, in Object Explorer, expand SQL Server Agent, then expand the Jobs folder. Next, right-click any job, select Properties, and navigate to the Steps page. Here you will see that job steps do not necessarily need to run linearly. You can set a job to default to start at any job step. Additionally, when starting a job, you can manually change the start step for the job.

Each job step reports back whether it succeeded or failed, and you can configure it to move to another step or fail based on the job step outcome. These step completion actions are defined on the Advanced page of the Job Step Properties dialog box. However, for future ease of management, we recommend that you create job steps that are designed and run in a consistent pattern. Using a standard process will help others more easily understand and support these SQL Server Agent jobs. This embraces another key aspect of database automation: creating processes that can be easily managed.

You can assign jobs to categories. In fact, many system-generated jobs (such as replication) are assigned to categories. You can access settings to create your own categories in SSMS by right-clicking the Jobs folder under SQL Server Agent and selecting Manage Categories on the shortcut menu.

In T-SQL, you can run this script to add a new category:

EXEC msdb.dbo.sp_add_category @class=N'JOB', @type=N'LOCAL', @name=N'Health Check';

This should aid your efforts to report on, maintain, redeploy, and migrate jobs in the future.

Understand job step security

A critical step that many developers and administrators skip is the use of credentials and proxies in SQL Server Agent job steps. SQL Server Agent jobs, by default, run steps in the security context of the SQL Agent service account. This may be acceptable for some local usage such as indexing, but using a proxy to run a job step instead of the SQL Server Agent service account or another named user is the most secure approach. Proxies make it possible for administrators to set job steps to run under a specific credential rather than giving the SQL Server Agent service account access to everything that each job needs.

Proxies are used for all job step types except one. It is not possible to run a T-SQL script job step using a proxy. A T-SQL step will run in the security context of the owner of the job if the owner is not a sysadmin. If the owner of the job is a member of the sysadmin server role, the job will run as the SQL Server Agent service account.

For all other job step types, there is a proxy. You can select the job step to run as a proxy on the Job Step Properties page. SQL Server Agent checks for access to the subsystem each time the job step is run to verify that the security has not changed.

A subsystem can be one of the following items:

  • Operating system (CmdExec)

  • Replication agent (Snapshot, Log Reader, Distribution, Merge, or Queue Reader)

  • Analysis Services query or Analysis Services command

  • SSIS package execution

  • PowerShell script

  • Microsoft ActiveX script (discontinued as of SQL Server 2016; replaced with PowerShell script)

You can associate each proxy with one or more subsystems, but to reduce your attack surface, you should create many proxies for different job step security requirements and subsystems.

Without a proxy specified, jobs must be owned by a member of the sysadmin role to run job steps other than the T-SQL step type. This is because the T-SQL step type allows you to specify an account under Run as so that the code will run under a different user’s permissions. These job steps will then run as the SQL Server Agent service account. This isn’t ideal, for two reasons:

The owner of the job must also have permission to use any subsystem that the job’s steps use. This is important because job steps often need to access other servers, and proxies give you the ability to assign pinpoint rights to those other resources. You will not be able to create or modify a job step for a subsystem if the job owner is not listed as a principal who has access to the proxy. Sysadmins automatically have access to all proxies.

Proxies map to credentials on the SQL Server; you’ll find a subfolder for credentials in the Security folder on the Server level in Object Explorer in SSMS. Each proxy is linked to a credential in SQL Server. The credential stores the account’s username and password, which means that if it changes, the proxy and SQL Server Agent job steps that depend on it will not be able to authenticate and will fail. Therefore, you should use service accounts, not individuals’ named accounts, in credentials that will be used by proxies. Credential account passwords shouldn’t be widely known, and the accounts should not regularly be used interactively by administrators to avoid accidentally becoming locked out.

Note

You should keep a script for recovering locked service accounts in a safe place.

You can create a credential for a local Windows account or a domain account. You also can create credentials for accounts on Extensible Key Management (EKM) modules, including the Azure Key Vault service. The Windows account of the credential must have Log on as a batch job permission on the server. As a local administrator, you can grant this permission in the Local Security Policy dialog box.

  • Image You can read more about Azure Key Vault and EKM modules in Chapter 13.

Secure permissions to interact with jobs

To set up a SQL Server Agent job in SSMS, your login must be a member of the sysadmin server role or one of the SQL Server Agent database roles in the msdb database. The SQLAgentOperatorRole, SQLAgentReaderRole, and SQLAgentUserRole have permission to create jobs, start jobs, view jobs, view a job’s history, and edit job properties, though mostly only for jobs they own.

The SQLAgentUserRole is the least privileged of the three roles, but the other two roles are members of the SQLAgentUserRole. Typically, membership to these roles is limited to service accounts and third-party developers. Grant permission on proxies to custom database roles, individuals, or service accounts. Do not grant permissions directly to the SQLAgentUserRole database role, including the ability to use proxies.

Schedule and monitor jobs

A job can be run based on one or more schedules assigned to it. You give schedules a name upon creation and can assign them to multiple jobs. This can be especially useful for uncommon or esoteric job schedules, or to centralize management of jobs that should run simultaneously. To view and select schedules from other jobs, select the Pick button on the Schedules tab of the Job Properties dialog box. You will see only the job schedules to which you have access.

There are four schedule types:

  • Start automatically when SQL Server Agent starts

  • Start whenever the CPUs become idle

  • Recurring

  • One time (for running a schedule manually—for instance, during testing or a one-off index rebuild)

Jobs run asynchronously when they are started manually or by SQL Server Agent. A dialog box with a spinning progress icon appears, but you can close it, and the job will continue to run until completion. You can monitor the progress of jobs in SSMS by viewing the Job Activity Monitor, and you can observe the job’s current request in sys.dm_exec_requests.

Note

Using the SQL Server Agent extension, you can also view and manage SQL Agent jobs in Azure Data Studio. For more information, visit https://learn.microsoft.com/sql/azure-data-studio/sql-server-agent-extension.

You can use T-SQL to query the status of jobs with the undocumented stored procedure master.dbo.xp_sqlagent_enum_jobs, which you can join to msdb.dbo.sysjobs, as shown here:

--Jobs still running
DECLARE @xp_sqlagent_enum_jobs TABLE (
id int not null IDENTITY(1,1) PRIMARY KEY,
Job_ID uniqueidentifier not null,
Last_Run_Date int not null,
Last_Run_Time int not null,
Next_Run_Date int not null,
Next_Run_Time int not null,
Next_Run_Schedule_ID int not null,
Requested_To_Run int not null,
Request_Source int not null,
Request_Source_ID varchar(100) null,
Running int not null,
Current_Step int not null,
Current_Retry_Attempt int not null,
[State] int not null);

INSERT INTO @xp_sqlagent_enum_jobs
EXEC master.dbo.xp_sqlagent_enum_jobs 1, '';

SELECT j.name
, state_desc = CASE ej.state
WHEN 0 THEN 'not idle or suspended'
WHEN 1 THEN 'Executing'
WHEN 2 THEN 'Waiting for thread'
WHEN 3 THEN 'Between retries'
WHEN 4 THEN 'Idle'
WHEN 5 THEN 'Suspended'
WHEN 7 THEN 'Performing completion actions'
END
, *
 FROM msdb.dbo.sysjobs j
 LEFT OUTER JOIN @xp_sqlagent_enum_jobs ej
 ON j.job_id = ej.Job_ID
ORDER BY j.name;
Configure and view job history

Every time a job is run, a record is maintained in the msdb database in the dbo.sysjobhistory table. To review the job’s history, right-click it in SSMS and select Job History on the shortcut menu. History is stored for each job step. You can expand a given job to view the output for each step, including any errors.

With jobs that run frequently (for example, transaction log backup jobs), a large amount of job history will be created and stored in msdb. It is initially defaulted to two very low and likely unrealistic row caps: 1,000 rows of history for all jobs, and 100 rows of history at most for one job. If a job runs once per hour, it loses visibility into history after just four days—likely an unrealistic window for troubleshooting and diagnostic information.

In SSMS, in Object Explorer, right-click SQL Server Agent, select Properties, and select the History page. As shown in Figure 9-1, this page is not intuitive. The first option, Limit size of job history log, is a rolling job history retention setting. You might find it a good start to simply add a 0 to each value, increasing the maximum log history size in rows from the default of 1,000 to 10,000 or more, and also increase the maximum job history per job in rows from the default of 100 to 1,000 or more. These settings would store just over 41 days of history for a job that runs hourly, if this were the only job on the server. You might find these numbers also insufficient on a SQL Server instance with many frequently running jobs, so you should increase these settings until you have a comfortable job run history retention.

"Current job history log size (in rows)" is the header. The "Limit size of job history log" check box is selected. "Maximum job history log size (in rows)" is set to 1000. "Maximum job history rows per job" is set to 100. The "Remove agent history" check box is not selected. "Older than 4 weeks" is grayed out.

Figure 9-1 The two options to retain SQL Agent job history.

The job history log can be useful, but it has two limitations of which you should be aware:

  • The message text in the Job History viewer is truncated after 1,024 characters. To view the full results of the output, you must query the dbo.sysjobhistory table. The message column in that table is considerably larger at 8,000 characters.

  • The history of SQL Server Integration Services (SSIS) package execution in the SQL Server Agent job history is extremely limited—reduced to details around the fact that the package started, completed, and/or errored, without additional details. More verbose detail appears in the thorough history available in the SSISDB. To access and view this history, open the Integration Services Catalogs menu in SSMS and select the project or packages that failed.

You can also configure additional logging for each job step to capture the full step output and, more commonly, the error text. The following options are available on the Advanced page of the Job Step Properties dialog box:

  • Output file. You can send step output to an Output file and Append output to existing file. Be wary of keeping the Append output to existing file option turned on long term—the output file can grow to a significant size in a short amount of time. If your history file becomes too large, this may cause the SQL Server Agent to crash.

  • Log to table. This writes to the dbo.sysjobstepslogs table in the msdb database. This table has an nvarchar(max) data type for the Log field, allowing for more output data to be captured per step if needed. Be careful of this option, as well—the table can grow to a significant size in a short amount of time. You should schedule the stored procedure sp_delete_jobsteplog to remove old records from the table over time.

  • Include step output in history. This adds a row to the job history log to include the output of the job step. This should contain valuable information, and, unlike with the other two options, job history is automatically maintained over time by SQL Server Agent.

Administer SQL Server Agent operators

An operator is an alias in SQL Server Agent that allows you to set up a name and email address(es) to receive messages. Usually, an operator should not be pointed to an individual (although you can create a semicolon-delimited list of email address); instead, it should be pointed to a distribution group (even if that group initially contains only one person). In most situations, you will create an operator to notify SQL Server first responders in your environment. You should maintain your environment’s list of DBA personnel in distribution lists, and not inside the operator lists of each SQL Server instance.

Note

The pager email name is deprecated along with the on-duty schedule, so don’t use these. Alerting using Net Send is also deprecated and has been removed from the user interface.

To set up an operator, in SSMS, in Object Explorer, expand the SQL Server Agent folder, right-click Operator, and select New Operator on the shortcut menu.

Note

If you have a big team and an on-call rotation, you could set up a scheduled process in a SQL Server Agent job to update an “on call rotation” distribution list email address to resource(s) currently “on call.” Use the sp_update_operator stored procedure to update the email address for an operator on a schedule.

Configure alerts

Alerts are created to set conditions and, when met, prompt email notifications or the kickoff of SQL Server Agent jobs in response. Alerts are versatile and can look for SQL Server events in the Error Log for performance conditions that you would view in the Performance Monitor application or for Windows Management Instrumentation (WMI) queries.

As recommended in the “Set up SQL Agent” section of Chapter 4, you should set up alerts for high-severity SQL Server errors. However, do not overcommit your personal inbox with alerts, and do not set an inbox rule to Mark As Read and file away emails from SQL Server. By careful selection of emails, you can assure yourself and your team that emails from SQL Server will be actionable concerns that rarely arrive.

With a large number of SQL Server instances under your purview, email alerts for even severe issues can become too numerous and frequent. This is especially true if you receive individual alerts from each server! Look into gathering and queuing actionable errors in a system that provides for aggregation, dashboarding, and team assignment. There are third-party log-collection and log-management software applications that perform the task of log aggregation and centralized alerting.

You might also configure the Delay between responses setting for each alert to prevent an unchecked flooding of emails arriving from a repeating error. Consider a delay of up to 5 minutes between responses, as your environment and Service-Level Agreement (SLA) deem appropriate.

You can specify only a single error message or severity per alert. Consider scripting the mass creation of a standard batch of alerts, to be created consistently on all your SQL Server instances. We include a script in the accompanying downloads for this book as an example that includes the alerts we will examine in just a moment.

First, we review the three types of alerts you can set up:

  • SQL Server event alert

  • Performance condition alert

  • WMI event alert

Note

On Linux, you can only set up a SQL Server event alert, but as we’ll show, it is possible to query performance counters using a dynamic management view.

SQL Server event

You should set up alerts on actual error messages that are important enough for you to receive emails. SQL Server generates a lot of informational-only events, such as successful backup messages, for which you would not want to receive messages.

You can set up alerts based on the actual error number (samples follow shortly) or any error of a certain severity (1 to 25). You can optionally filter the alert to a single database or for a specific message text.

It is common practice to set up alerts for severity 16 through 19 and 21 through 25 because these tend to be actionable errors. Severities 21 and above are severe and unrecoverable errors.

The most common severity 20 errors are nuisance authentication-related and transient (the user tried, experienced an error, tried again, and succeeded). An alert for severity 20 might send out many unactionable alerts to the SQL Server DBA team. You will still see severity 20 issues in the SQL Server Error Log and should make note of them as they appear, especially if they do so in large numbers, given that this can be a sign of greater authentication or domain issues or malicious intrusion attempts. The goal of alerts is to send out actionable errors or performance conditions worth investigating.

Note

Every SQL Server error message includes a severity, but that doesn’t mean you want to be alerted to them. For example, basic syntax, CHECK constraint, and FOREIGN KEY constraint errors that you might make while writing queries in SSMS or Azure Data Studio will surface as severity 15 or 16 errors, which aren’t worth alerting.

You might also want to configure alerts to send out error messages for the following SQL Server error numbers that are not already covered in severities 16 through 19 and 21 through 25. These errors are rare, but immediately actionable:

  • 825 (severity 10). A dreaded “read-retry” error, which occurs when a file read succeeds after failing n number of times. This is often a harbinger of a database-integrity failure and should prompt immediate action.

  • 854, 855, 856 (severity 10). Uncorrectable hardware memory corruption detected via the operating system’s memory diagnostics. This may indicate a potentially immediate threat to system stability due to memory.

  • 3624 (severity 20). An internal SQL Server error called an assertion failure. This is typically a software bug, though it could indicate internal data corruption. Errors at this severity are often addressed via a SQL Server cumulative update or patch.

Performance conditions

On Windows, you can set up performance condition alerts for any performance counter in the SQLServer category—the same set of alerts you would see in Windows Performance Monitor with the prefix SQLServer or MSSQL$instancename. For example, if you want to receive an email when the SQL Server’s page life expectancy (PLE) drops below a certain value, you select it in the same way you would in Performance Monitor: Choose the Buffer Manager object, the Page Life Expectancy counter, the Falls Below comparison operator, and a comparison value. In the case of PLE, this is measured in seconds.

Note

SQL Server samples the data periodically, so there might be a few seconds’ delay between when you receive the alert and when the threshold was reached.

WMI event alert conditions

The third option for SQL Server Agent alerts allows for custom WMI queries to be run (for SQL Server on Windows only). WMI queries can gather alerts on a variety of Data Definition Language (DDL) events in SQL Server, such as CREATE, ALTER, and DROP. While WMI queries follow the basic syntax of T-SQL queries, the FROM of the WMI query will be a WMI object, not an object in a SQL Server database.

This type of alert is not as straightforward as the other types. In general, you might find better results, more flexibility, and less complexity by using Extended Events, SQL Server Agent jobs, SQL Server Audit, and/or third-party monitoring tools than by using WMI alert queries.

  • Image You can read more about Extended Events in Chapter 8.

Setting up an email recipient for a WMI event alert does not send over any useful or actionable information in the email aside from the alert’s name. This does little more than let you know that a WMI event occurred (observed asynchronously, so there might be some delay).

To view the information regarding the event—for example, the T-SQL command associated with the event—you must enable token replacement. To do so, open the SQL Server Agent Properties dialog box. Then, at the bottom of the Alert System page, select the Token Replacement check box. This allows for the tokenization (replacement at runtime) of WMI commands in a T-SQL job step.

We have prepared a sample WMI event alert to capture the CREATE DATABASE DDL event in the accompanying downloads for this book.

SQL Server Agent job considerations when using availability groups

If you are running SQL Server Agent jobs in an availability group environment, you will still need to configure maintenance plans on each SQL Server instance. You must cover databases that are writeable when the instance is the primary replica, databases not included in availability groups, as well as the system databases master and msdb. You should ensure that your maintenance plans, regardless of platform, are consistently updated on all replicas and also are aware of their local replica role so that maintenance plans do not need to be turned on, turned off, or reconfigured when a failover occurs.

The SQL Server Agent must be enabled, and your SQL Server Agent jobs must exist on all replicas of the availability group and be replica-aware. (The script should know if it is running on the primary replica for a database.) You will need multiple versions of any custom maintenance task in order to separate scripts for databases in each availability group, and one more for databases not in an availability group (including any system databases that you intend to maintain with custom scripts).

To avoid having SQL Server Agent jobs generate an error when their local replica is not the primary replica for a database, you can add a T-SQL step to the start of the job to detect and raise a failure. The goal of the first step is to prevent subsequent job steps from running and failing against secondary replica databases, which will not be writeable. Name the first step “Am I Primary?” or something similar, and then add the following script:

--add as step 1 on every AG-aware job
IF NOT EXISTS (
SELECT @@SERVERNAME, *
   FROM sys.dm_hadr_availability_replica_states rs
   INNER JOIN sys.availability_databases_cluster dc
   on rs.group_id = dc.group_id
   WHERE is_local = 1
   and role_desc = 'PRIMARY'
--Any databases in the same Availability Group
   and dc.database_name in (N'databasename1', N'databasename2'))
BEGIN
   print 'local SQL instance is not primary, skipping';
   throw 50000, 'Do not continue', 1;
END;

This code causes step 1 to fail when it is not run on a primary replica for the specified database(s). If you run the preceding code referencing a database that is not in an availability group, the script will also cause step 1 to fail. In the Advanced settings of the “Am I Primary?” job step, the On Success Action setting should be Go to The Next Step, as usual, but the On Failure Action setting should be Quit the Job Reporting Success, which would not register as a job failure. Instead of a green check mark or a red X next to the job, SQL Server Job History displays a yellow triangle. This prevents subsequent job steps from running and failing against secondary replica databases, which will not be writeable.

Note

The previous script is not appropriate for maintenance plan jobs. Any change to the maintenance plan will re-create the job and overwrite the new “Am I Primary?” task you added. Instead, take advantage of the availability group–aware backup priority settings in the Back Up Database task. We look at this in more detail in the next section.

Maintain SQL Server

This section reviews what you should be doing as a day-to-day database administrator of a SQL Server instance, how to accomplish these tasks, and the built-in tools that SQL Server provides to help you. SQL Server editions above Express edition (because Express has no SQL Server Agent) ship fully featured and ready for you to configure to perform basic maintenance.

Note

This section provides ways to accomplish all major maintenance objectives using tools built into SQL Server. However, that doesn’t mean you shouldn’t use third-party tools.

For the most part, the tasks in this section are built into Azure SQL Database and Azure SQL Managed Instance. In some cases, maintenance tasks are completely automated (especially in the case of disaster recovery) or partially automated (in the case of index maintenance). This section focuses on SQL Server instances because the fast evolution of Azure SQL reduces the hands-on maintenance required by DBAs on the PaaS platform.

  • Image For more information on Azure SQL Database and Azure SQL Managed Instance, see Chapter 17 and Chapter 18.

Basic care and feeding of SQL Server

You can carry out the regular proactive maintenance of a SQL Server instance by using one or more of the following strategies:

  • SQL Server maintenance plans, including the option to use a Maintenance Plan Wizard

  • Custom scripting using DMVs and T-SQL or PowerShell commands

  • Third-party tools

Each has advantages and disadvantages, and each requires different compromises between ease of setup, customizability, cost, and maintainability.

You can run these strategies via SQL Server Agent jobs. You can configure each one to provide customized activity logging, retention, and the ability to view history in different ways.

Regardless of the strategy or strategies you adopt, whether with built-in or third-party tools, you should accomplish the following as a bare minimum on a regular schedule, tailored to meet your SLA and recovery objectives agreed with your organization:

  • Back up system and user databases.

    • Full backups for all databases.

    • Transaction log backups for databases not in the simple recovery model.

    • To save space and reduce time to recover using transaction log restores, differential backups between less frequent full backups. Differential backups can also be used for databases in the simple recovery model.

  • Implement a retention policy for database backups, if backups are stored locally, by deleting backups after a business-approved amount of time. In the case of tape backups, have a rotation policy instead.

  • Implement a retention policy for various SQL Server event logs and history.

    • SSMS maintenance plan log text files can be deleted after a certain amount of time using the Maintenance Cleanup task.

    • Prune the history of backup and restore operations in msdb.

    • Prune the Database Mail log.

    • SQL Server Error Log files are already maintained by SQL Server to a configurable number of log files. (The default is six, which should be increased.)

    • SQL Server Agent job history is also maintained automatically by settings in the SQL Server Agent properties.

  • Maintain index and heap health in SQL Server.

    • There are different strategies to reduce fragmentation in clustered and nonclustered indexes.

    • Columnstore indexes also require maintenance via REORGANIZE steps, especially if there is update or delete activity in the table, and fragmentation is measured differently.

    • Monitor heap structures (tables without a clustered index) for excessive forwarding pointers.

  • Update statistics.

    • This should accompany INDEX REORGANIZE steps, but not INDEX REBUILD steps. Remember that the INDEX REBUILD command also updates index statistics.

  • Check database integrity via DBCC CHECKDB.

  • Maintain copies of backed up data in secure off-premises facilities.

    • Storage area network (SAN) replication or another file-level backup system can accomplish this, as can integration with Azure Storage for easy cloud-based backup.

    • Remember that your data-loss tolerance isn’t defined by how often you take backups, but by how often those backups get securely off-premises and tested!

What will vary is how often these tasks need to run on each server and database, and what type of backups you need. This section of the chapter walks you through the process of creating tools to manage the previous tasks by writing T-SQL scripts scheduled using SQL Server Agent, using the Maintenance Plan Designer, and using the Maintenance Plan Wizard.

Even though we don’t make recommendations regarding third-party tools, we do want to note that many third-party tools do not provide an end-to-end solution for maintaining secure offsite backups (the final item in the previous list), which typically involves coordination with the storage administrators and/or cloud hosting such as Azure Storage. SQL Server Managed Backup to Azure is a full-featured SQL Server backup solution (though not free, it is relatively inexpensive). It is the Microsoft-recommended backup solution for SQL Server instances running on Azure virtual machines (VMs).

If you want to maintain direct control of backing up off-premises, you can use BACKUP ... TO URL statements to write backups directly to Azure Storage, often to complement local backup storage. (Scripting this yourself is free, of course, but Azure Storage is not.) To meet your Recovery Time Objective (RTO) goals, or in the event of an external network failure, you should also maintain local backups within your network for a time. Remember to take regulatory data-retention guidelines into account.

Use SQL Server maintenance plans

SQL Server maintenance plans are a free, low-cost, low-complexity, visually built option to implement SQL Server maintenance and disaster recovery. The drag-and-drop tasks built into the maintenance plan design surface have some distinct shortcomings that we’ll review. You will see differences when creating maintenance plans in SSMS from version to version of SQL Server.

Note

Maintenance plans are not supported for SQL Server on Linux, but that doesn’t prevent you from targeting a Linux instance from SQL Server on Windows.

The Maintenance Plan Wizard is a step-by-step tour through most of the steps necessary for SQL Server. This wizard guides you through an easy process of creating a maintenance plan with most of the basics, which you can then review with the maintenance plan design surface in SSMS. To begin with a fresh slate, open the Management folder in Object Explorer, right-click on Maintenance Plans, and select New Maintenance Plan. This prepopulates objects for you in the designer, with which we recommend you become familiar.

The Maintenance Plan designer has three main elements: the subplans list, the design surface, and the maintenance plan tasks toolbox. When you open a maintenance plan, the first two elements will be obvious, but the toolbox might not be docked. To display the toolbox and pin it to the side of SSMS, open the View menu and select Toolbox or press Ctrl+Alt+X.

If you have any experience with SSIS, the interface of a maintenance plan will feel very familiar. Behind the scenes, maintenance plans create and store SSIS packages internally.

The tasks in a maintenance plan and the choices in the Maintenance Plan Wizard translate directly to the options you’re already familiar with in SQL Server Agent jobs or the options for backups and index maintenance T-SQL commands. For example, the Run As option on the first screen of the wizard and in the Subplan properties of the designer provides a list of proxies, just as a SQL Server Agent job step does. Instead of using the SQL Server Agent service account, ideally you should choose an SSIS proxy (under the Management node) that has access to the SSIS Package Execution subsystem.

Note

You may see an explanation screen as the first screen of many wizards in SSMS, and you can choose not to show these explanations in the future.

Cover databases with the maintenance plan

When you select the maintenance tasks that you want the wizard to configure, you’ll be able to select the databases you want to run the tasks against. The options are:

  • All databases

  • System databases

  • All user databases

  • Specify a list of databases

You also have the option to ignore databases for which the status is not online, which we also recommend.

To isolate the configuration, maintenance, and logging from one another, it is common to create two maintenance plans: one for system databases (master, model, and msdb) and at least one for all user databases, depending on business requirements. The system plan just handles system database backups, and the user plan handles everything else. This ensures that if there are any issues with ongoing changes to the maintenance plan for user databases, crucial system database backups are unaffected.

Maintenance plan tasks

On the first page of the Maintenance Plan Wizard, you have the option to run each task with separate schedules or with a single schedule for the entire plan. We recommend you choose the Separate Schedules For Each Task option, or if you’re building the maintenance plan in the designer, break activities into multiple subplans, each with its own schedules. This is because some tasks such as index maintenance or database-integrity checks can take a long time to run, and you do not want your backups in serial with those, and then delayed and inconsistently occurring. To work the maintenance plan into your after-hours windows, you will want more scheduling flexibility than a single start time for all tasks to run serially.

The Select Maintenance Tasks page of the Maintenance Plan Wizard features a list of all built-in maintenance tasks. In the graphical designer, you have one additional tool to run custom T-SQL scripts named the Execute T-SQL Statement Task. You can use this to run your custom maintenance scripting or other administrative scripts. We review this later in this section.

Note

The Maintenance Plan Wizard can create only one copy of each available task. To create two different tasks of the types we’ll be looking at in a moment—for example, one for system databases and one for user databases—you will need to use the Maintenance Plan designer in SSMS.

The following sections present the available tasks that you can select from, along with descriptions of what they do.

Check Database Integrity task

The Check Database Integrity task runs DBCC CHECKDB to check for database corruption—a necessary task that you should run periodically. You should run DBCC CHECKDB at least as often as your backup retention plan. For example, if you keep local backups around for one month, you should perform a successful DBCC CHECKDB no fewer than one time per month—and more often, if possible. On large databases, this task could take hours.

  • Image For more about data corruption and checking database integrity, see Chapter 8.

The options available in the maintenance plan task match the common parameters you would use in the DBCC CHECKDB command. The Physical Only check box uses the PHYSICAL_ONLY parameter of DBCC CHECKDB, which limits DBCC CHECKDB to checking physical structures, torn pages, checksum failures, and common hardware failures. It is less comprehensive as a result. However, using PHYSICAL_ONLY can take significantly less time to complete while still detecting the signs of common storage hardware failure.

Note

A common practice when using the PHYSICAL_ONLY option of DBCC CHECKDB or the Check Database Integrity maintenance plan task is to maintain a system in which production databases are restored on a matching nonproduction system, and running a time-consuming full integrity check (without the PHYSICAL_ONLY parameter) to catch any corruption issues. However, there is no substitute for running a full DBCC CHECKDB on your production system.

Shrink Database task

There is no sound reason to ever perform a Shrink Database task on a schedule. A Shrink Database task removes free space from a file and returns it to the OS, causing the file to experience an autogrowth event the next time data is written to it. Do not ever include the Shrink Database task in the maintenance plan.

Reorganize Index task

A Reorganize Index task runs an ALTER INDEX ... REORGANIZE statement, which reduces index fragmentation but does not update statistics. By default, this task does not reorganize all indexes. If you need to reorganize all indexes on a scheduled basis, you will need to remove the default values on this task. On large databases, this could take hours, but will have less overhead, less query disruption, and finish faster than a Rebuild Index task (discussed next).

Because Reorganize Index is an online operation and reads only one 8-KB data page at a time, it will not take long-term table locks and might block other user queries for only a very short amount of time. Online index operations will consume server resources and generate large amounts of logged transactions.

If you use this task in your maintenance plan, remember to add an Update Statistics task to run immediately after it.

  • Image The “Rebuild Index task” section covers a method to maintain indexes that are above a certain fragmentation percentage.

Rebuild Index task

More thorough than a Reorganize Index step at removing index fragmentation, this task runs an ALTER INDEX ... REBUILD statement and does update statistics. The options available in the Rebuild Index dialog box correspond to the options for the ALTER INDEX ... REBUILD syntax.

Note

At the time of this writing, maintenance plans currently do not support RESUMABLE index rebuilds, which might be necessary for you on very large tables. See Chapter 8 for more information on ALTER INDEX ... REORGANIZE and REBUILD.

With Enterprise edition, you can perform a Rebuild Index step as an online operation, which is not likely to block other user queries like an offline rebuild does. Not all indexes and data types can have an online rebuild performed, so the Maintenance Plan dialog box for the Rebuild Index task will ask you what you want to happen.

Rebuilding indexes without the ONLINE option will block other user queries attempting to use that index, and will consume additional server resources. On large tables, this could take hours to finish, and even more without the ONLINE option due to the overhead of managing blocking.

Maintain indexes above a certain fragmentation percentage

You can intelligently limit index maintenance to certain thresholds, starting with the options to select between Fast (LIMITED), Sampled, and Detailed. This corresponds to the parameters provided to the structural statistics dynamic management function (DMF), sys.dm_db_index_physical_stats.

Note

This task does not operate on columnstore indexes. See Chapter 15, “Understand and design indexes,” for more information on maintaining columnstore indexes.

You can configure the Reorganize Index and Rebuild Index tasks to maintain only indexes filtered by percentage of fragmentation and page count, both from sys.dm_db_index_physical_stats, and/or actual index usage (based on the sys.dm_db_index_usage_stats DMF). The fragmentation threshold is 15 percent by default in the Reorganize Index task, and 30 percent in the Rebuild Index task, but these values are only a guideline.

Other options added to the Reorganize Index and Rebuild Index tasks match the options for the ALTER INDEX … REORGANIZE, and REBUILD T-SQL commands, covered in detail in Chapter 8.

Update Statistics task

The Update Statistics task runs an UPDATE STATISTICS statement, rebuilding index statistics, which we discuss in Chapter 15. Do not follow an Index Rebuild task with an Update Statistics task for the same objects, because this is redundant work. Updating statistics is an online operation, so it will not block other user queries, but it will consume server resources unnecessarily. This task should finish faster than either a REORGANIZE or REBUILD step. On larger databases, or databases with flash storage, this task can replace more frequent Rebuild Index or Reorganize Index tasks.

History Cleanup task

This task deletes older rows in msdb tables that contain database backup and restore history, prunes the SQL Server Agent log file, and removes older maintenance plan log records. These are accomplished by running three stored procedures in the msdb database: dbo.sp_delete_backuphistory, dbo.sp_purge_jobhistory, and dbo.sp_maintplan_delete_log, respectively. You should run this task to prevent excessively old data from being retained, according to your environmental data-retention requirements. This will save space and prevent large table sizes from degrading the performance of maintenance tasks. This step should finish quickly and not disrupt user queries. This step does not delete backup files or maintenance plan log files; that is the job of the Maintenance Cleanup task, discussed next.

Maintenance Cleanup task

The Maintenance Cleanup task deletes files from folders and is commonly used to delete old database backup files, using the system stored procedure master.dbo.xp_delete_file. You also can use it to clean up the .txt files that maintenance plans write their history to in the SQL Server instance’s Log folder. You can configure the task to look for and delete any extension by folder directory, and then specify that subdirectories be included. The date filter uses the Date Modified file attribute (not the Date Created attribute). While this may not be an issue in many cases, it can prevent files from being cleaned up if you are appending data to these files. Combined with the option to create a subdirectory for each database, this means you can create and remove backup files in the folder structure for each database.

Note

In the case of maintenance plans, by default, logs are kept in a table, msdb.dbo.sysmaintplan_log, as well as in text files in the SQL Server instance default Log folder. Deleting one does not delete the other. You should maintain a retention policy on both sources of the maintenance plan run history.

The Maintenance Cleanup task deletes files only from folders, and thus isn’t an option to enforce a retention policy for backups to URL in Azure Storage.

Execute SQL Server Agent Job task

Using this task, you can orchestrate the asynchronous start of another SQL Server Agent job during the maintenance plan, perhaps to start another middle-of-the-night process as soon as possible after maintenance is complete.

Back Up Database (Full, Differential, Transaction Log) task

With this task, you can take a backup of any kind of the specified databases. The options in the Maintenance Plan dialog box for the backup are similar to the SSMS Database Backup dialog box, plus some minor extra options, including an option to ignore replica priority in an availability group database.

Note

The standard extensions for backup files are .bak (full), .dif (differential), and .trn (log), but these are just conventions. You can provide any file extension (or none at all) for your backup types, as long as you are consistent across your entire SQL Server environment with backup file extensions.

The Back Up Database task affords you multiple strategies for backups, including backing up to disk or to Azure Storage via URL as well as to the deprecated tape backup support.

Backing up to URL writes files directly to Azure Storage natively, without the need to install any software or network connections. This was a fairly limited feature before SQL Server 2016, but now can be accomplished via a shared access signature (SAS) credential for secure access to Azure Blob Storage.

You can configure disk backups to append multiple database backups multiple times to the same file or files, or create a backup file and a subdirectory for each database per backup. This is generally recommended both for ease of accessing files and for using tools to remove out-of-date files as needed. For backups to disk, we recommend that each database has a subdirectory in the folder location to separate the backup files of databases with potentially different retention plans or recovery strategies. The maintenance plan backup will automatically create subdirectories for new databases, and when performing backups, append a timestamp and a unique string to backup names in the following format:

databasename_backup_yyyy_mm_dd_hhmmss_uniquenumber.bak|dif|trn

We recommend that you select the options for Verify Backup Integrity, CHECKSUM, and Compress Backup for all database types, for all databases.

Note

If you are backing up your databases to a compressed location, you can still enable database backup compression. While there may be an additional resource cost of compressing the backups, this can save time if you are copying all your backups to a different or central location.

This is supported even for backups to URL. Keep in mind that the Verify Backup step performs a RESTORE VERIFYONLY statement to examine the backup file and verify that it was valid, complete, and should be restorable. Should be is key, because the only way to truly test whether the backup was valid is to test a restore. The RESTORE VERIFYONLY does not actually restore the database backup, but could give you an early heads-up on a potential drive or backup issue, and is always recommended when time permits. The Verify Backup step could significantly increase the duration of the backup, scaling with the size of the database backup, but is time well worth spending in your regular maintenance window.

Execute T-SQL Statement task (not available in the wizard)

This task can run T-SQL statements against any SQL Server connection, with a configurable time-out. A simple text box accepts T-SQL statements. Because of its simplicity, we recommend that instead of pasting lengthy commands, you reference a stored procedure. This would be easier to maintain and potentially keep in source control by developing the stored procedure in other tools. You may find it useful to keep this and other stored procedures in a database specifically dedicated to database administration tasks.

Maintenance plan report options

By default, maintenance plans create a report in two places to record the history for each time a subplan runs. Logs are kept in a table, msdb.dbo.sysmaintplan_log, as well as in .txt files in the SQL Server instance default Log folder. You can also choose the Email Report option in the Maintenance Plan Wizard, which adds a Notify Operator task.

Build maintenance plans using the Maintenance Plan designer in SSMS

Due to the nature of wizards, there are some inherent issues with configuring a robust maintenance solution that covers all the needs of your databases with the Maintenance Plan Wizard. Fortunately, the SSMS Maintenance Plan designer gives you the ability to set up your task run order and precedence constraints as well as to maintain multiple subplan schedules within a maintenance plan. Figure 9-2 displays a sample maintenance plan.

A sample maintenance plan for user databases has been created, with multiple subplans, each running on a different schedule. Subplan_1 runs every week on Sunday and starts with the Reorganize Index Task. Subplan_2 runs the Update Statistics Task every day. Looking at Subplan_1, the first task is a Reorganize Index Task. Both the Maintenance Cleanup Task and History Output Task run after the Reorganize Index Task.

Figure 9-2 A sample maintenance plan for user databases has been created, with multiple subplans, each running on a different schedule.

In Figure 9-2, a sample maintenance plan for user databases has been created, with multiple subplans, each running on a different schedule. Subplan_1 runs every week on Sunday and starts with the Reorganize Index Task. Subplan_2 runs the Update Statistics Task every day. Looking at Subplan_1, the first task is a Reorganize Index Task. Both the Maintenance Cleanup Task and History Output Task run after the Reorganize Index Task.

When you save the maintenance plan, each of the subplans in the maintenance plan will become a SQL Server Agent job with the naming convention maintenance plan name.subplan name.

Note

When you save a maintenance plan, the job(s) it creates might be owned by your personal login to the SQL Server. Be aware that if your account becomes disabled or locked out, this will prevent the SQL Server Agent job from running. To avoid this, it would be better to have all maintenance plans owned by an account that is not tied to a specific user.

At the top of the Maintenance Plan designer window is where the subplans are listed. Initially, there will be just one plan, called Subplan_1. You should break down the tasks that will be accomplished in the subplan by the schedules they will follow, and name them accordingly. You can add subplans and manage their schedules in the Maintenance Plan designer window. Note that you should not make changes to the SQL Server Agent jobs after they’ve been created; otherwise, the next time you edit the maintenance plan, your changes will most likely be overwritten.

The large gray area beneath the subplan list is the design surface for maintenance plans—a graphical, drag-and-drop interface. To add tasks to a subplan, you can drag a task from the toolbox to the design surface. To serialize the running of multiple tasks, select one, and then select the green arrow beneath the box, dragging it to the task that should follow. You can create a long string of sequential activities or a wide set of parallel-running activities, similar to designing SSIS packages.

Note

Each Maintenance Plan task has a View T-SQL button that shows you the exact T-SQL that will be run to perform the maintenance tasks. You can use this to learn the commands for maintenance tasks so you can make your own customized plans, which we talk about in the next section.

When not to use SQL Server maintenance plans

Personal preference, of course, is a fine enough reason not to use built-in maintenance plans. You can write your own, as long as your library of scripts or third-party tools accomplishes the necessary maintenance tasks with consistency, configurability, and good logging for review.

For SQL Server instances with manageable maintenance windows, maintenance plans will meet your needs if the schedules are set up appropriately. You can create a variety of maintenance plans to cover databases with various levels of importance or availability based on business requirements (with the caveat of not being able to detect new databases). For very large databases or databases with 24×7 availability requirements, more granularity for maintenance operations will likely be necessary.

Not every business has the luxury of having all night and/or all weekend to perform maintenance outside of business hours. When you become familiar with the T-SQL commands and their various options, you can be creative to overcome tight scheduling, crowded maintenance windows, very large databases, or other maintenance plan complications.

After reviewing the capabilities of the Back Up Database and Rebuild Index tasks, you should consider maintenance plans more full-featured and capable of handling the bulk of maintenance, even on larger databases with tight schedules. Ultimately, the success of maintenance plans or custom scripts depends on your understanding of the various options available for the seven core maintenance tasks listed earlier in this chapter and elsewhere in this book.

Back up availability groups using a secondary replica

This section may look out of place here, but rest assured it makes sense. We have been looking at single-instance SQL Server maintenance tasks up to now, but automation applies to availability groups as well. Even maintenance plans can be used successfully with availability groups.

One of the many useful features in availability groups is the ability to utilize read-only secondary replicas for remote backups. Performing backups on a secondary replica, including a geographically separated replica, introduces complexity but has a big advantage. Backups do not take locks and will never block a user query, but they will incur significant CPU, memory, and I/O overhead. Backups can slow database response, so on servers with large databases and/or busy 24×7 utilization, it might be helpful, and in some cases necessary, to find alternative strategies to backups. Taking database backups on secondary replicas is one of the alternatives to move the resource expense of backups off the primary replica.

Understand backup priority values

In SSMS, in Object Explorer, expand the Always On High Availability node for the instance of the primary replica, and then the Availability Groups node. Right-click the availability group you want to view, select Properties, and look at the Backup Preferences page. It’s important to understand the priority values and how they interact with various backup tasks.

The default option is Prefer Secondary, which specifies that backups occur on the secondary replica first or, if it is not available, on the primary replica. You then can provide priority values (0 to 100, where 100 is highest) to decide which of the multiple secondary replicas should be the preferred backup location. The values apply to both full and transaction log backups.

Other self-explanatory options include Primary, Secondary Only, and Any Replica, which uses the priority values to decide which replica is preferred for the backups. When failing over to another replica, you will need to review and script the changes to the backup priority. Your planned failover scripts should include the reassignment of backup priority values.

This Backup Preferences page affects only backup systems or scripts that are aware of the backup preferences. For example, in SSMS, in Object Explorer, right-click a database, select Tasks on the shortcut menu, and then Backup. The dialog box that opens takes a backup of a database but does not include any availability groups–aware settings. On the other hand, the Back Up Database task in SQL Server maintenance plans is aware of availability group backup priority value settings.

You are limited to taking full copy-only database backups and transaction log backups on readable secondary replica databases. When including databases in an availability group, the maintenance plan Back Up Database task will warn you if you are attempting to configure a full database backup without a copy-only check or differential backup, or if you select the For availability databases, ignore Replica Priority for Backup and Backup on Primary Settings check box. If this is misconfigured, it is possible to create a maintenance plan that will run but not create backups of databases in an availability group.

Use replica backup priority in your backup schedules

If you attempt to configure a full database backup without copy-only or a differential backup on a secondary node, you will see the warning in the Back Up Database task, “This backup type is not supported on a secondary replica and this task will fail if the task runs on a secondary replica.” If you select the Ignore Replica Priority check box, the warning will read, “Note: Ignoring the backup priority and availability group settings may result in simultaneous parallel backups if the maintenance plan is cloned on all replicas.”

Maintenance plans should run on a schedule on all availability group replicas. The priority values for backups can cause a backup not to be taken on a primary or nonpreferred secondary replica, but the maintenance plan backup task will start and complete as usual. The maintenance plans will use the backup priority values and do not need to be configured when different replicas in the availability group become primary.

You can still take a manual backup of any type of the databases and bypass the availability group backup preferences. In fact, your backup strategy might include intentionally taking full backups in more than one node of a geographically dispersed availability group.

If you are not using SQL Server maintenance plans or scripting to take backups, be aware that not all third-party backup solutions are aware of availability group backup preferences or even availability groups in general. Maintenance plans in SSMS are aware of these backup preferences, and your custom scripting can be, too, via the scalar function sys.fn_hadr_backup_is_preferred_replica. This function returns a 0 or 1, based on whether the current SQL Server instance is operating as the preferred backup.

Strategies for administering multiple SQL Servers

There are options for creating orchestration in SQL Server to allow for a multiplication and standardization of SQL Server DBA effort across multiple servers. The potential to set up SQL Server Agent jobs that are run simultaneously on multiple servers is powerful, especially for custom-developed scripts to gather and report information back to a central SQL Server.

Master/Target servers for SQL Agent jobs

Master/Target (MSX/TSX) servers are built into SQL Server Agent to help DBAs who want to manage identical jobs across multiple SQL Server instances. This feature has been in the product since SQL Server 7.0, but many DBAs are unaware of the convenience that it can deliver. There is no doubt that the feature is useful and works seamlessly with technologies it could not have foreseen, including availability groups (more on that later).

You can designate one SQL Server as a Master server (MSX), set up multiserver jobs on that server, and configure each instance to have its SQL Server Agent jobs remotely managed into a Target server (TSX). The MSX cannot be a TSX of itself, so using a separate production server to orchestrate multiserver administration of SQL Server Agent jobs is necessary. The MSX should be a production environment server that does not host performance-sensitive production workloads.

Other considerations for the MSX include the following:

  • Of the servers you have available, choose the most recent version of SQL Server for the MSX. You can communicate with a TSX within two versions of the MSX. For instance, if you have a SQL Server 2022 MSX, the TSX can be running SQL Server 2017, SQL Server 2019, or SQL Server 2022.

  • Each TSX can have only one MSX.

  • Before changing the name of a TSX, first defect it from the MSX and then reenlist it. However, we recommend modifying a TSX name using DNS instead.

  • Do not use a built-in account for the SQL Server Agent service account on all servers; instead, use a domain service account, as recommended earlier in this chapter in the “Understand job step security” section.

  • While Azure SQL Managed Instance has many of the features of SQL Agent, multiserver administration is not supported.

Create an MSX/TSX with SSMS

In SSMS, in Object Explorer, right-click SQL Server Agent, select Multi Server Administration, and then select Make This A Master to launch the Master Server Wizard. The wizard first sets up a special operator just for running multiserver jobs; this operator is created for you and named MSXOperator. You can specify only one operator to run multiserver jobs, so think carefully about who should be notified about these jobs. Specify the email address. As always with operators, it’s best not to use an individual’s email addresses, but to use an email distribution group, instead.

Next, the wizard presents locally registered and Central Management–registered servers so you can select them as targets for the MSX. Select the target servers from the list or, at the bottom, select Add Connection and add servers not registered in your list.

When you are finished with the wizard, the labels in Object Explorer will be different for both master and target SQL Server Agents. Similarly, on the MSX, under SQL Server Agent in Object Explorer, you will see two new subfolders under Jobs: Local Jobs and Multi-Server Jobs.

By default, SSL encryption is used between the servers, but you can change this through a registry setting. You should not need to change it, however, because encrypted communication between the servers is recommended, even on your internal networks. You will need to install an SSL certificate on the server before using it through the wizard.

Although we do not recommend it, you can disable the encryption of an MSX/TSX by changing the registry set to not use encryption. The registry key HKEY_LOCAL_MACHINESOFTWAREMicrosoftMicrosoft SQL ServerinstanceSQLServerAgentMSxEncryptChannelOptions is by default 2, which means all communication is encrypted. Changing this to 0 on all servers removes encryption.

Manage multiserver (MSX/TSX) administration

To manage SQL Server Agent jobs in a multiserver environment, set up the job the same way you would any other job on the MSX. Then, navigate to the targets page on the job, which outside of a multiserver environment has no use. From there, select the Target Multiple Servers option button; then select the servers on which the job should run. In the background, code is run to send the jobs to the TSX.

Any jobs with steps that use proxies will need the proxy accounts to have access to the TSX, and a proxy by that same name on the TSX. Otherwise, the job will not find a proxy on the TSX. By default, matching proxy names from MSX to TSX isn’t allowed, because of the potential for malicious action. This must be enabled via the registry on the TSX.

On a TSX, jobs appear in the SQL Server Agent, in the Jobs folder, but you can’t edit them.

Sometimes, synchronizing job definitions to the TSX will not be queued up and will not post to a server. In this case, issue the following command, but be aware that this will delete and re-create any jobs specified for the target server, therefore cancelling those jobs on the TSX:

EXEC msdb.dbo.sp_resync_targetserver '<Target Server Name>';

Most other issues with multiserver jobs are solved by defecting the TSX and then adding it again.

Manage multiserver administration in availability groups

MSX/TSX works with availability groups and can be quite useful for ensuring that jobs stay synchronized across servers. MSX/TSX data is stored in system tables in the msdb database, which cannot be part of an availability group.

While SQL Server 2022 allows you to set up the MSX feature in a contained availability group, it is not recommended due to the additional complexity in managing SQL Server Agent jobs. Thus, you should not have the MSX on a SQL Server instance in the availability group, because this would limit your ability to failover and use SQL Server Agent Multiserver Administration. You would lose your ability to orchestrate jobs across the target servers if one of the nodes in your availability group was unreachable, compromising your failover state. Instead, consider other high-availability solutions for the MSX.

Using MSX/TSX for availability group SQL Server Agents doesn’t change the need to set up the “Am I Primary?” logic in step 1 of any job that should run only on a SQL Server instance that currently hosts the primary replica.

SQL Server Agent event forwarding

Event forwarding occurs when one central Windows server receives the SQL Server events of many. The server that is the destination of many other servers’ forwarded events might handle a heavy workload, especially network traffic. The destination server should be a production-environment server that does not host performance-sensitive production workloads. You can refer to this server as the alerts management server.

Event forwarding allows the Windows Event Viewer to be a single-pane view of events on many instances of SQL Server. Further, it allows for alerts on the alerts management server to prompt a response to the originating server, via SQL Server Agent alerts. Forwarded errors arrive in the Windows Application Event Log, not the SQL Server Error Log. Because of this, the SQL Server Agent service account needs to be a local Windows administrator.

Typically, this setup is the same server as your MSX/TSX server; in fact, the features work together. If your MSX and alerts management server are on separate SQL Server instances, you will not have the ability to run a job to respond to specific events.

Set up event forwarding

You configure event forwarding in the SQL Server Agent Properties dialog box. In SSMS, open the SQL Server Agent Properties dialog box and choose the Advanced page. In the SQL Server Event Forwarding section, select the Forward Events to a Different Server check box. In the text box, type the name of the alerts management server. Be aware that the alerts management server that receives forwarded events must be the SQL Server default instance of a server. (In other words, it cannot be a named instance.)

You can also choose whether to send all events or only unhandled alerts that have not been handled by local alerts on each SQL Server instance. You can then specify a minimum error severity to be forwarded. Choose a severity level that matches your needs, keeping in mind the caveat of nuisance severity errors.

Policy-based management

Policy-based management (PBM) is a powerful tool for enforcing rules for configuration settings, options for databases and servers, security principals, table design, even database object naming conventions. As of this writing, this feature does not yet apply to Azure SQL Database.

PBM is structured around policies. Policies contain a single condition, which is a Boolean expression. The condition expression is evaluated against properties and settings of destination objects, such as the server itself, a database, a table, or an index.

For example, you might set up a condition around the AdHocRemoteQueries server-level setting.

SQL Server has a large list of facet properties built into it, such as AdHocRemoveQueriesEnabled in the Surface Area Configuration facet. (As discussed in Chapter 4, this facet contains a number of security-sensitive features, many—but not all—of which we recommend you disable unless needed.) To check that this Surface Area Configuration option is always turned off, create an expression that checks whether AdHocRemoveQueriesEnabled can be evaluated to Enabled = False.

Policies can contain only one condition, but you can configure many different expressions, any one of which could initiate the policy. You can, for example, create a PBM policy called “Configuration Settings,” a PBM condition called “Settings That Should be Disabled,” and a list of expressions, each of which evaluates a different Surface Area Configuration option.

To access a list of PBM facets, right-click the server name in Object Explorer and select Facets. This opens the SSMS View Facets dialog box; select Export Current State as Policy to ease the implementation of many configuration options into policy. You can apply the resulting settings to the local server as a policy right away, or export them as .xml, which then can be imported as a new policy.

PBM is built into SSMS, accessible via the Policy Management subfolder in the Management folder. To export a policy, in SSMS, in Object Explorer, in the Policy Management folder, right-click a policy, choose Export Policy, and follow the prompts. To import a policy, right-click on the Policies folder, choose Import Policy, and follow the prompts.

Evaluate policies and gather compliance data

After you create a policy, you have multiple options for when to evaluate it: on demand (manually); on a schedule; and continuously, which either logs or blocks policy violations.

SQL Server maintains a history of all policy evaluations in a policy history log, which is available within SSMS. To access it, expand the Management folder for the instance, right-click Policy Management, and select View History on the shortcut menu.

In the System Policies subfolder, you’ll find 14 prebuilt policies for checking the health of availability groups, and two more for the SQL Server Managed Backup feature, first introduced in SQL Server 2014. The SQL Server Managed Backup policies begin with SmartAdmin.

For example, after creating a policy, you can configure it with On Demand Evaluation Mode (see the code examples at the end of this section), and then test it by enabling the AdHocRemoteQueries setting in the Surface Area Configuration facet. You can also enable it using T-SQL with the following command:

EXEC sp_configure 'Ad Hoc Distributed Queries', 1;
GO
RECONFIGURE;
GO

A message will immediately appear in the SQL Server Error Log stating, “Policy ‘Keep AdHocRemoteQueries Disabled’ has been violated by target ‘SQLSERVER:SQLservernameinstancename’,” accompanied by a severity 16 error 34053 message.

The on-demand Evaluate mode gives the administrator an immediate report of all policy compliance. From the Evaluate Policies window—which you access by expanding Policy Management, right-clicking Policies, and choosing Evaluate—you can view any number of policies. Should any expressions fail, the policy will display a red X beside it. In this example, evaluating the policy with the AdHocRemoteQueries facet enabled displays an error message and provides you with the ability to apply the change to bring the servers in line with policy.

In the Target Details pane of the Evaluate Policies window, you can select Details to start an analysis of all expression evaluations in the condition.

You can have the policies run automatically by either defining a schedule or having the policy evaluated in response to an action. To select either of these options, go to Policy Management, expand the list, and choose Policies. To set up an evaluation method for a specific policy, right-click the policy and select Properties. Then, in the Evaluation Mode drop-down near the bottom of the window, select On Schedule. You will get the option to set up the schedule in the same window. The scheduled Evaluate mode generates only SQL Server Error Log activity.

The other option in the drop-down is On Demand. This option makes it possible for you to create a policy that prevents developers or other administrations from making DDL changes that violate condition expressions. Not all facet expressions allow for the On Change: Prevent Evaluation mode.

This is accomplished by rolling back the transaction that contains the violating action. As with all rollbacks, this transaction could contain other statements and could cause a database change deployment to fail in a manner that could complicate your change process. In many cases, it may be preferable to have rules automated so that the developers can quickly see if their changes don’t meet coding standards. Not only can this save time in the development and QA process, but it removes the burden of enforcing these changes from the DBA. Administrators and database developers should be aware of the potential impact to database deployments. You should limit the use of the On Change: Prevent Evaluation mode to security-related or stability-related properties in both production and QA environments, such as the following:

  • Server security. @CrossDBOwnershipChainingEnabled

    Enforce the evaluation of this to False unless this is part of application security design.

  • Server security. @PublicServerRoleIsGrantedPermissions

    Enforce the evaluation of this setting to False in any circumstance.

  • Login. @PasswordPolicyEnforced

    Enforce the evaluation of this setting to True for all logins.

  • Certificate. @ExpirationDate

    Enforce that this date is not within a certain time frame in the future (six months), in every database (sample code to follow).

  • Database. @AutoShrink

    Enforce the evaluation of this setting to False in all databases.

  • Database. @AutoClose

    Enforce the evaluation of this setting to False in all databases.

Samples

You can script policies to T-SQL for application to multiple servers, though be wary of applying policies for production systems to development systems, and vice versa. You can use a T-SQL script to create the two aforementioned samples. This script is available in the accompanying downloads for this book.

The first part of the script creates a sample policy to keep the Surface Area Configuration option AdHocRemoteQueries turned off (it will be evaluated on demand). The result sets returned by these queries contain only the integer IDs of new policy objects that have been created.

The second part of the script verifies whether any non-system certificates have an expiration in the next six months and, if so, fail the policy. The policy will also ignore any certificates with ## in the name. These are built into SQL Server, are for internal use only, are generated when SQL Server is installed, and cannot be modified. Also keep in mind that certificates used for transparent data encryption (TDE) will continue to work just fine after expiration, so there’s no cause for concern if they show up in this list.

The result sets returned by these queries contain only the integer IDs of new policy objects that have been created.

Use PowerShell to automate SQL Server administration

SQL Server has supported close integration with PowerShell for over a decade. PowerShell is a robust shell scripting language that you can use to perform many administrative tasks. It was first released in 2006, has been integrated with SQL Server since 2008, and was made open source and cross-platform in 2016.

The goal of this section is not to list every possible interaction of PowerShell with SQL Server, Azure, or availability groups, but to provide instructional, realistic samples that will help you to learn the PowerShell language and add it to your DBA toolbox. All the scripts in this section are available in the accompanying downloads for this book.

  • Image We review several useful PowerShell scripts for Azure SQL Database interaction in Chapter 17.

IT professionals in all walks of life are learning PowerShell to ease their administrative tasks with various technologies, not just SQL Server on Windows and Linux, but Active Directory, Machine Learning, Azure, Office 365, SharePoint, Exchange, and even Office products like Microsoft Excel. There is very little in the Microsoft stack for which PowerShell cannot help. Developers have created third-party downloadable modules, available for download in the PowerShell Gallery (https://powershellgallery.com), to further enhance PowerShell’s ability to interact even with non-Microsoft platforms such as Amazon Web Services, Slack, Internet of Things (IoT) devices, Linux, and more.

There are even scenarios for which PowerShell fills feature gaps in configuration panels and UI, necessitating some basic literacy for PowerShell on the part of the modern system administrator, DBA, or developer. PowerShell is especially useful when building code to automate the failover of Windows Server failover clusters or for interacting with DNS. You also can use PowerShell remoting to manage multiple Windows servers and SQL Server instances from a single command prompt.

PowerShell is a full-featured shell where you can still manage the file system and launch applications you’re familiar with as a Windows or Linux user—for example, ping, ipconfig, ifconfig, telnet, net start, regedit, notepad, sqlcmd, and even shutdown.

Note

You should get into the habit of using the PowerShell console window directly, as well as in Visual Studio Code or Azure Data Studio with PowerShell extensions.

You can also start a PowerShell console from within SSMS; simply right-click most folders and select Launch PowerShell on the shortcut menu.

  • Image Follow the instructions in “Install the PowerShell SQLServer module” later in this section before using this feature.

You might find the Visual Studio Code or Azure Data Studio environments more conducive to authoring multiline PowerShell scripts, especially if you have any prior familiarity with Visual Studio or Visual Studio Code.

PowerShell basics

Cmdlets for PowerShell follow a pattern of verb-noun. This helps provide ease and consistency when trying to find cmdlets to run your desired task.

For database administration tasks, we will become familiar with cmdlets and using SQL Server Management Objects (SMO). PowerShell for SQL Server is installed separately from SQL Server Setup or the SSMS installation and can be installed from the PowerShell Gallery. While this functionality is available in SSMS 17.0 going forward, you should use the most current release of SSMS available. At the time of writing this book, SSMS 19.0 is the most current version. We will demonstrate how to install and check the current version of the SQLServer module.

For each cmdlet, there is a built-in way to receive a description of the cmdlet and see all the parameters along with descriptions and examples (if provided by the author). Let’s try it on Invoke-Sqlcmd, a cmdlet that runs a T-SQL query using statements supported by the sqlcmd command.

First, run the cmdlet Update-Help. This command updates the extensive and helpful local help files for PowerShell and installed modules.

Note

The Visual Studio Code shortcuts for running scripts are different from SSMS, and you need to be aware of the following:

  • In SSMS, pressing F5 runs the entire script if no text is highlighted, or just the highlighted text if any is selected. Pressing Ctrl+E does the same by default.

  • In Visual Studio Code, pressing F5 saves and then runs the entire script file, regardless of whether any code is highlighted. Pressing F8 runs only highlighted code.

Install the PowerShell SQLServer module

You must be running at least Windows PowerShell 5.0 on Windows to download modules from the PowerShell Gallery. On Linux you need PowerShell 7. (Note that PowerShell 7 also runs on Windows and macOS.) To determine the version of PowerShell on your system, run the following code in the PowerShell window:

$PSVersionTable

The PSVersion value contains the current installed version of PowerShell.

There are many PowerShell modules available that can be useful for managing SQL Server. However, before installing these modules, you should confirm that the modules come from a trusted source. You should also test PowerShell, including modules, in a lower environment so you can confirm that PowerShell will not negatively affect your environment. In addition, you may want to discuss the use of PowerShell and any specific modules with your internal security team.

To install the latest version of the SQLServer module, use the following code on an Internet-connected device, running the PowerShell console or Visual Studio Code in administrator mode:

Install-Module -Name SQLServer -Force -AllowClobber

In the preceding script, we used a few handy parameters. Let’s review them:

  • -Name. Specifies the unique name of the module you want.

  • -Force. Prevents you from having to answer Yes to confirm you want to download.

  • -AllowClobber. Allows this module to overwrite cmdlet aliases already in place. Without AllowClobber, the installation will fail if it finds that the new module contains commands with the same name as existing commands.

To find the current installed versions of the SQLServer PowerShell module, as well as other SQL modules (including SQLPS), use the following:

Get-Module -ListAvailable -Name "sql*" | Select-Object Name, Version, RootModule

To access the help information for any cmdlet, use the Get-Help cmdlet. Here are some examples:

#Basic Reference
Get-Help Invoke-SqlCmd
#See actual examples of code use
Get-Help Invoke-SqlCmd - Examples
#All cmdlets that match a wildcard search
Get-Help -Name "*Backup*database*"

Note that the # character begins a single-line comment in PowerShell code. Alternatively, you can use <# and #> to enclose and declare a multiline comment block.

Offline installation

To install the module on a server or workstation that is not Internet-connected or cannot reach the PowerShell Gallery, go to a workstation that can reach the PowerShell Gallery and has at least Windows PowerShell 5.0 or PowerShell 7. Then, use the following command to download the module, making sure to choose a folder that exists on your machine.

Save-Module -Name SQLServer -LiteralPath "C:	emp"

Next, copy the entire C:tempSqlServer folder to the machine that cannot reach the PowerShell Gallery to a path that is in the list of PSModule paths. The potential paths for modules list is stored in a PSModulePath environment variable, which you can view in Windows System Properties, or more easily with this PowerShell script:

$env:PSModulePath.replace(";","`n"")

The default folder for the module downloaded from the gallery would likely be C:Program FilesWindowsPowerShellModules on Windows, or /usr/local/share/powershell/Modules on Linux. Verify that this path is available or choose another PSModule folder, and then copy the downloaded SQLServer folder there. The following script adds the SQLServer module, and then shows a list of all available modules on your workstation:

Import-Module SQLServer
Get-Module

Note

When writing code for readability, we recommend you use the actual cmdlet names. With PowerShell, there are many shorthand and shortcuts possible, but you should try to write easy-to-read code that is approachable and maintainable for the next administrator.

Use PowerShell with SQL Server

PowerShell can interact with SQL Server instances all the way back to SQL Server 2000 (with some limitations in earlier versions). This book is not a good medium to demonstrate the full capability that PowerShell can bring to your regular DBA tasks, nor should it try to detail all the possibilities. Nonetheless, here are some selected, representative, but simple examples.

Backup-SqlDatabase

Let’s learn about some more basics and syntax of PowerShell via the Backup-SqlDatabase cmdlet. With this PowerShell cmdlet, you have access to the same parameters as the T-SQL command BACKUP DATABASE.

Again, use PowerShell’s built-in help files to see full syntax and examples, many of which will be familiar to you if you have a good understanding of the BACKUP DATABASE options.

Get-Help Backup-SqlDatabase -Examples

Here is an example of how to back up all databases on a local SQL Server instance, providing the backup path, and including a subfolder with the database’s name. If you are using a Windows computer, the default execution policy is RemoteSigned. If you would like to verify your execution policy before running the following script, you can run Get-ExecutionPolicy. If your policy is not set to RemoteSigned, you can run the following: Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope LocalMachine. The script also adds the current date and time to the name of the backup file:

#Backup all databases (except for tempdb)
$instanceName = "localhost" #set instance to back up
$path = "F:Backup"
Get-SqlDatabase -ServerInstance $instanceName | `
   Where-Object { $_.Name -ne 'tempdb' } | `
    ForEach-Object {
    Backup-SqlDatabase -DatabaseObject $_ `
    -BackupAction "Database" `
    -CompressionOption On `
    -BackupFile "$($path)$($_.Name)$($_.Name)_$(`
        Get-Date -Format "yyyyMMdd")_$(`
        Get-Date -Format "HHmmss_FFFF").bak" `
    -Script #The -Script generates T-SQL, but does not execute
}

Here are some notes about this script:

  • Adding the -Script parameter to this and many other cmdlets outputs only the T-SQL code, split by GO batch separators; it does not actually perform the operation.

  • The back tick, or grave accent (`) symbol (below the tilde on most standard keyboards), is a line extension operator. Adding the ` character to the end of a line gives you the ability to display long commands, such as in the previous example, over multiple lines.

  • The pipe character (|) is an important concept in PowerShell to grasp. It passes the output of one cmdlet to the next. In the previous script, the list of databases is passed as an array from Get-SQLDatabase to Where-Object, which filters the array and passes it to ForEach-Object, which loops through each value in the array.

Remove-Item

Let’s learn some more about common PowerShell syntax parameters. You can use the Remove-Item cmdlet to write your own retention policy to delete old files, including backup files, stored locally. Remember to coordinate the removal of old local backups with your off-premises strategy that keeps backups safely in a different location.

In this script, we use the Get-ChildItem cmdlet to Recurse through a subfolder, ignore folders, and select only files that are more than $RetentionDays old and have a file extension in a list we provide:

$path = "F:Backup"
$RetentionDays = 1
$BackupFileExtensions = ".bak", ".trn", ".dif"
Get-ChildItem -Path $path -Recurse | `
    Where-Object { !$_.PSIsContainer `
         -and $_.CreationTime -lt (get-date).AddDays(-$RetentionDays) `
        -and ($_.Extension -In $BackupFileExtensions) `
         } | Remove-Item -WhatIf

Here are some notes about this script:

  • The $RetentionDays parameter is a positive value, but a negative is added to it as part of the AddDays() method to subtract the number of retention days from the current date (specified with get-date).

  • The Get-ChildItem cmdlet gathers a list of objects from the provided path, including files and folders. The -Recurse parameter of Get-ChildItem causes the cmdlet to include subfolders.

  • The $_ syntax is used to accept the data from the object before the previous pipe character (|). In this example, the objects discovered by Get-ChildItem are passed to the Where-Object, which filters the objects and passes that data to Remove-Item.

  • Adding the -WhatIf parameter to this and many other cmdlets does not actually perform the operation, but provides a verbose summary of the action, instead. For example, rather than deleting old backup files, this PowerShell script returns something similar to the following sample:

    What if: Performing the operation "Remove File" on target "F:Backupackup_
    test_202202010200.bak".
Invoke-Sqlcmd

The Invoke-Sqlcmd cmdlet can run T-SQL commands, including on remote SQL Server instances and Azure SQL databases. Invoke-Sqlcmd can run batch file scripts that used to be run by sqlcmd. Use Invoke-Sqlcmd when no cmdlet exists to return the same data for which you’re already looking. In this script, we connect to a database in Azure SQL Database and run a query to see current sessions:

$instanceName = "azure-databasename.database.windows.net"
Invoke-Sqlcmd -Database master -ServerInstance $instanceName `
-Query "select * from sys.dm_exec_sessions" | `
Format-Table | Out-File -FilePath "C:TempSessions.txt" -Append

Note

If you see the error message “Could not load file or assembly ‘Microsoft.SqlServer.BatchParser’,” find step-by-step instructions to resolve it at https://social.technet.microsoft.com/wiki/contents/articles/35832.sql-server-troubleshooting-could-not-load-file-or-assembly-microsoft-sqlserver-batchparser.aspx.

Here are some notes about this script:

  • The Invoke-SqlCmd cmdlet uses Windows Authentication by default. Notice that we passed no authentication information at all. You can also provide the UserName and Password parameters to the Invoke-SqlCmd to connect via SQL Authentication to SQL Server instances, though this is not recommended unless you are on a secure connection. There is also the option to connect to Azure SQL Database or Azure SQL Managed Instance using an access token or a service principal.

  • The | Format-Table cmdlet has a big impact on the readability of script output. Without Format-Table, the script returns a long list of column names and row values. The Format-Table output does not include all columns by default, but returns a wide line of column headers and row values, similar to how SSMS returns results in Text mode.

  • The | Out-File cmdlet dumps the output to a text file instead of to the PowerShell console, creating the script if needed. The -Append parameter adds the text to the bottom of an existing file.

  • The Out-File can be handy for archival purposes, but for viewing live rowsets—especially SQL Server command results—try using the Out-GridView cmdlet instead (Windows-only). It provides a full-featured grid dialog box with re-sortable and filterable columns, and so on. Out-GridView is used instead in the following sample:

    $instanceName = "localhost"
    Invoke-Sqlcmd -Database master -ServerInstance $instanceName `
    -Query "select * from sys.dm_exec_sessions" | `
    Out-GridView

Use PowerShell with availability groups

You can script the creation and administration of availability groups and automate them with PowerShell instead of using SSMS commands or wizards. If you work in an environment in which creating, managing, or failing over availability groups is a repeated process, you should invest time in automating and standardizing these activities with PowerShell.

Following are some code samples to help you along the way, starting with the very beginning. Suppose a new group of servers has been created for availability groups, and you need to add the Failover Clustering feature to each server. This could be time consuming and click-heavy in a remote desktop session to each server. Instead, consider the following script in which we quickly deploy the failover clustering feature and tools on four servers (you can parameterize the computer names as you require):

Invoke-Command -Script {Install-WindowsFeature -Name "Failover-Clustering" } `
   -ComputerName SQLDEV11, SQLDEV12, SQLDEV14, SQLDEV15
Invoke-Command -Script {Install-WindowsFeature -Name "RSAT-Clustering-Mgmt" } `
   -ComputerName SQLDEV11, SQLDEV12, SQLDEV14, SQLDEV15
Invoke-Command -Script {Install-WindowsFeature -Name "RSAT-Clustering-PowerShell" } `
    -ComputerName SQLDEV11, SQLDEV12, SQLDEV14, SQLDEV15
  • Image For more about enabling and configuring availability groups, see Chapter 11.

Let’s fast-forward to an in-place availability group, with two replicas set to asynchronous synchronization. A planned failover is coming up, and you need to automate the script as much as possible. Start with the sample script in the accompanying downloads, which accomplishes these goals:

  • Sets asynchronous replicas to synchronous and waits so we can perform a planned failover with no data loss

  • Performs availability group failover

  • Sets replicas back to asynchronous

Here are a few notes about the script:

  • We need to do some character trickery to pass in a named instance in the SMO path for the availability group, providing %5C for the backslash () in the replica name, SQLSERVER-0SQL2022. The need here is rare, albeit frustrating.

  • We see another control structure, Do ... Until. In this case, we’re waiting until the RollupSynchronizationState of the availability group has changed from Synchronizing to Synchronized, indicating that the synchronization has changed from asynchronous to synchronous.

  • After the replica is set to synchronous, the failover can occur without data loss, without being forced. In an emergency, in which the primary server SQLSERVER-0 is offline, we could skip the steps where we change the synchronization and proceed straight to the most important cmdlet in the script: Switch-SqlAvailabilityGroup. Except in a forced failover, for which data loss is possible, we must specify the -AllowDataLoss and -Force parameters.

  • You must run this entire script from the primary node, as it is currently written. A hint of how you could customize the script to be run from anywhere lies in the Invoke-Command cmdlet, where we connect to the original secondary replica (now the primary replica) and set the synchronization from asynchronous back to synchronous.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.116.59.8