├── .github
├── CONTRIBUTING.md
├── ISSUE_TEMPLATE.md
├── PULL_REQUEST_TEMPLATE.md
└── workflows
│ └── jekyll-gh-pages.yml
├── Allfiles
├── Demos
│ └── 01
│ │ └── azuredeploy.json
└── Labs
│ ├── 10
│ ├── CreateRandomWorkloadGenerator.sql
│ └── ExecuteRandomWorkload.sql
│ ├── 13
│ └── usp_AdaptiveIndexDefrag.sql
│ ├── 01
│ └── Starter
│ │ └── azuredeploy.json
│ ├── 04
│ └── AdventureWorksLT.bacpac
│ ├── Setup
│ ├── SqlDatabase.bicep
│ └── deploy-sql-database.ps1
│ └── Shared
│ └── AdventureWorks2017.bak
├── Instructions
├── Labs
│ ├── 00-setup-environment.md
│ ├── 01-provision-sql-vm.md
│ ├── 02-provision-sql-database.md
│ ├── 03-authorize-access-azure-sql-database.md
│ ├── 04-configure-firewall-rule.md
│ ├── 05-enable-sql-defender-and-data-classification.md
│ ├── 06-isolate-performance-problems.md
│ ├── 07-detect-correct-fragmentation-issues.md
│ ├── 08-identify-resolve-blocking-issues.md
│ ├── 09-identify-issues-database-design.md
│ ├── 10-isolate-problem-areas-poor-performing-queries.md
│ ├── 11-deploy-azure-database-using-template.md
│ ├── 12-create-cpu-status-alert.md
│ ├── 13-deploy-automation-runbook-rebuild-indexes.md
│ ├── 14-configure-geo-replication-for-azure-sql-database.md
│ └── 15-backup-url.md
├── Templates
│ ├── 03-provision-an-azure-sql-database.json
│ ├── AdventureWorks.bacpac
│ ├── AdventureWorks2017.bak
│ ├── AdventureWorksLT.bacpac
│ ├── CreateRandomWorkloadGenerator.sql
│ └── ExecuteRandomWorkload.sql
└── images
│ ├── dp-300-module-11-lab-01.png
│ ├── dp-300-module-11-lab-02.png
│ └── dp-300-module-11-lab-03.png
├── LICENSE
├── _build.yml
├── _config.yml
├── index.md
└── readme.md
/.github/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to Microsoft Learning Repositories
2 |
3 | MCT contributions are a key part of keeping the lab and demo content current as the Azure platform changes. We want to make it as easy as possible for you to contribute changes to the lab files. Here are a few guidelines to keep in mind as you contribute changes.
4 |
5 | ## GitHub Use & Purpose
6 |
7 | Microsoft Learning is using GitHub to publish the lab steps and lab scripts for courses that cover cloud services like Azure. Using GitHub allows the course’s authors and MCTs to keep the lab content current with Azure platform changes. Using GitHub allows the MCTs to provide feedback and suggestions for lab changes, and then the course authors can update lab steps and scripts quickly and relatively easily.
8 |
9 | > When you prepare to teach these courses, you should ensure that you are using the latest lab steps and scripts by downloading the appropriate files from GitHub. GitHub should not be used to discuss technical content in the course, or how to prep. It should only be used to address changes in the labs.
10 |
11 | It is strongly recommended that MCTs and Partners access these materials and in turn, provide them separately to students. Pointing students directly to GitHub to access Lab steps as part of an ongoing class will require them to access yet another UI as part of the course, contributing to a confusing experience for the student. An explanation to the student regarding why they are receiving separate Lab instructions can highlight the nature of an always-changing cloud-based interface and platform. Microsoft Learning support for accessing files on GitHub and support for navigation of the GitHub site is limited to MCTs teaching this course only.
12 |
13 | > As an alternative to pointing students directly to the GitHub repository, you can point students to the GitHub Pages website to view the lab instructions. The URL for the GitHub Pages website can be found at the top of the repository.
14 |
15 | To address general comments about the course and demos, or how to prepare for a course delivery, please use the existing MCT forums.
16 |
17 | ## Additional Resources
18 |
19 | A user guide has been provided for MCTs who are new to GitHub. It provides steps for connecting to GitHub, downloading and printing course materials, updating the scripts that students use in labs, and explaining how you can help ensure that this course’s content remains current.
20 |
21 |
22 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | # Module: 00
2 | ## Lab/Demo: 00
3 | ### Task: 00
4 | #### Step: 00
5 |
6 | Description of issue
7 |
8 | Repro steps:
9 |
10 | 1.
11 | 1.
12 | 1.
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | # Module: 00
2 | ## Lab/Demo: 00
3 |
4 | Fixes # .
5 |
6 | Changes proposed in this pull request:
7 |
8 | -
9 | -
10 | -
--------------------------------------------------------------------------------
/.github/workflows/jekyll-gh-pages.yml:
--------------------------------------------------------------------------------
1 | # Sample workflow for building and deploying a Jekyll site to GitHub Pages
2 | name: Deploy Jekyll with GitHub Pages dependencies preinstalled
3 |
4 | on:
5 | # Runs on pushes targeting the default branch
6 | push:
7 | branches: ["master"]
8 |
9 | # Allows you to run this workflow manually from the Actions tab
10 | workflow_dispatch:
11 |
12 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
13 | permissions:
14 | contents: read
15 | pages: write
16 | id-token: write
17 |
18 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
19 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
20 | concurrency:
21 | group: "pages"
22 | cancel-in-progress: false
23 |
24 | jobs:
25 | # Build job
26 | build:
27 | runs-on: ubuntu-latest
28 | steps:
29 | - name: Checkout
30 | uses: actions/checkout@v4
31 | - name: Setup Pages
32 | uses: actions/configure-pages@v5
33 | - name: Build with Jekyll
34 | uses: actions/jekyll-build-pages@v1
35 | with:
36 | source: ./
37 | destination: ./_site
38 | - name: Upload artifact
39 | uses: actions/upload-pages-artifact@v3
40 |
41 | # Deployment job
42 | deploy:
43 | environment:
44 | name: github-pages
45 | url: ${{ steps.deployment.outputs.page_url }}
46 | runs-on: ubuntu-latest
47 | needs: build
48 | steps:
49 | - name: Deploy to GitHub Pages
50 | id: deployment
51 | uses: actions/deploy-pages@v4
52 |
--------------------------------------------------------------------------------
/Allfiles/Demos/01/azuredeploy.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | },
6 | "variables": {
7 | },
8 | "resources": [
9 | ],
10 | "outputs": {
11 | }
12 | }
--------------------------------------------------------------------------------
/Allfiles/Labs/01/Starter/azuredeploy.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | },
6 | "variables": {
7 | },
8 | "resources": [
9 | ],
10 | "outputs": {
11 | }
12 | }
--------------------------------------------------------------------------------
/Allfiles/Labs/04/AdventureWorksLT.bacpac:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MicrosoftLearning/dp-300-database-administrator/3c74b78e799d1ed721474f0e8aab85a240548ba7/Allfiles/Labs/04/AdventureWorksLT.bacpac
--------------------------------------------------------------------------------
/Allfiles/Labs/10/CreateRandomWorkloadGenerator.sql:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MicrosoftLearning/dp-300-database-administrator/3c74b78e799d1ed721474f0e8aab85a240548ba7/Allfiles/Labs/10/CreateRandomWorkloadGenerator.sql
--------------------------------------------------------------------------------
/Allfiles/Labs/10/ExecuteRandomWorkload.sql:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MicrosoftLearning/dp-300-database-administrator/3c74b78e799d1ed721474f0e8aab85a240548ba7/Allfiles/Labs/10/ExecuteRandomWorkload.sql
--------------------------------------------------------------------------------
/Allfiles/Labs/Setup/SqlDatabase.bicep:
--------------------------------------------------------------------------------
1 | @secure()
2 | param sqlAdminPw string
3 | param location string = resourceGroup().location
4 | param sqlAdminUsername string = 'dp300admin'
5 | param uniqueSuffix string
6 | param adminIpAddress string
7 |
8 | var sqlServerName = 'dp300-lab-${uniqueSuffix}'
9 |
10 | resource vnet 'Microsoft.Network/virtualNetworks@2022-05-01' = {
11 | name: 'dp300-lab-${uniqueSuffix}-vnet'
12 | location: location
13 | properties: {
14 | addressSpace: {
15 | addressPrefixes: ['10.0.0.0/16']
16 | }
17 | subnets: [
18 | {
19 | name: 'default'
20 | properties: {
21 | addressPrefix: '10.0.0.0/24'
22 | serviceEndpoints: [
23 | {
24 | service: 'Microsoft.Sql'
25 | }
26 | ]
27 | }
28 | }
29 | ]
30 | }
31 | }
32 |
33 | resource sqlServer 'Microsoft.Sql/servers@2022-05-01-preview' = {
34 | name: sqlServerName
35 | location: location
36 | properties: {
37 | administratorLogin: sqlAdminUsername
38 | administratorLoginPassword: sqlAdminPw
39 | version: '12.0'
40 | }
41 | }
42 |
43 | resource sqlDB 'Microsoft.Sql/servers/databases@2022-05-01-preview' = {
44 | name: 'AdventureWorksLT'
45 | parent: sqlServer
46 | location: location
47 | properties: {
48 | createMode: 'Default'
49 | sampleName: 'AdventureWorksLT'
50 | requestedBackupStorageRedundancy: 'Local' // Remove if needed
51 | }
52 | }
53 |
54 | resource privateEndpoint 'Microsoft.Network/privateEndpoints@2022-07-01' = {
55 | name: 'dp300-${uniqueSuffix}-private-endpoint'
56 | location: location
57 | properties: {
58 | subnet: {
59 | id: vnet.properties.subnets[0].id
60 | }
61 | privateLinkServiceConnections: [
62 | {
63 | name: 'sqlPrivateLink'
64 | properties: {
65 | privateLinkServiceId: sqlServer.id
66 | groupIds: ['sqlServer']
67 | }
68 | }
69 | ]
70 | }
71 | }
72 |
73 | resource firewallRules 'Microsoft.Sql/servers/firewallRules@2022-05-01-preview' = {
74 | name: 'AllowAllAzureServices'
75 | parent: sqlServer
76 | properties: {
77 | startIpAddress: '0.0.0.0'
78 | endIpAddress: '0.0.0.0'
79 | }
80 | }
81 |
82 | resource firewallRuleCurrentIP 'Microsoft.Sql/servers/firewallRules@2022-05-01-preview' = {
83 | name: 'AllowCurrentIP'
84 | parent: sqlServer
85 | properties: {
86 | startIpAddress: adminIpAddress
87 | endIpAddress: adminIpAddress
88 | }
89 | }
90 |
91 | output sqlServerName string = sqlServer.name
92 | output sqlAdminUsername string = sqlAdminUsername
93 | output sqlDatabaseName string = sqlDB.name
94 | output vnetName string = vnet.name
95 | output privateEndpointName string = privateEndpoint.name
96 |
--------------------------------------------------------------------------------
/Allfiles/Labs/Setup/deploy-sql-database.ps1:
--------------------------------------------------------------------------------
1 | param (
2 | [string]$rgName = "contoso-rg", # Can be a full name or a prefix
3 | [string]$location = "westus2",
4 | [string]$sqlAdminPw = $null # Optional, if not provided, a secure one will be generated
5 | )
6 |
7 | $bicepFile = "SqlDatabase.bicep"
8 |
9 | # Function to generate a secure random password (12 characters, no spaces)
10 | function Get-SecurePassword {
11 | $lowercase = Get-Random -InputObject "abcdefghijklmnopqrstuvwxyz".ToCharArray()
12 | $uppercase = Get-Random -InputObject "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray()
13 | $numbers = Get-Random -InputObject "0123456789".ToCharArray()
14 | $specialChars = Get-Random -InputObject "$!@#%^&*".ToCharArray()
15 | $randomChars = Get-Random -InputObject "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789$!@#%^&*()-_=+{}[];:,.<>?".ToCharArray() -Count 8
16 |
17 | # Initialize an empty string
18 | $secureSuffix = ""
19 |
20 | # Loop through each selection and append only character position [0]
21 | foreach ($char in ($lowercase, $uppercase, $numbers, $specialChars) + $randomChars) {
22 | if ($char -ne " ") {
23 | $secureSuffix += $char
24 | }
25 | }
26 |
27 | # Shuffle the characters to remove any patterns
28 | $secureSuffix = -join ($secureSuffix.ToCharArray() | Sort-Object {Get-Random})
29 |
30 | return $secureSuffix
31 |
32 | }
33 |
34 | # Function to validate the provided password (only if provided)
35 | function Test-Password {
36 | param (
37 | [string]$testPw
38 | )
39 |
40 | #Write-Host "Evaluating password == $testPw" -ForegroundColor Yellow
41 |
42 | if ($testPw.Length -lt 12) {
43 | Write-Host "Error: Password must be at least 12 characters long" -ForegroundColor Red
44 | exit 1
45 | }
46 | if ($testPw -cnotmatch "[a-z]") {
47 | Write-Host "Error: Password must contain at least one lowercase letter." -ForegroundColor Red
48 | exit 1
49 | }
50 | if ($testPw -cnotmatch "[A-Z]") {
51 | Write-Host "Error: Password must contain at least one uppercase letter." -ForegroundColor Red
52 | exit 1
53 | }
54 | if ($testPw -notmatch "[0-9]") {
55 | Write-Host "Error: Password must contain at least one number." -ForegroundColor Red
56 | exit 1
57 | }
58 | if ($testPw -notmatch "[\$!@#%^&*()\-_=+{}\[\];:,.<>?]") {
59 | Write-Host "Error: Password must contain at least one special character ($!@#%^&*()-_=+{}[];:,.<>?)." -ForegroundColor Red
60 | exit 1
61 | }
62 |
63 | }
64 |
65 | # Function to get the public IP
66 | function Get-PublicIP {
67 | try {
68 | $ip = Invoke-RestMethod -Uri "https://api.ipify.org"
69 | return $ip
70 | } catch {
71 | Write-Host "Failed to retrieve public IP."
72 | exit 1
73 | }
74 | }
75 |
76 | # Function to get existing resource groups
77 | function Get-ExistingResourceGroups {
78 | try {
79 | Write-Host "Retrieving existing resource groups..."
80 | $rgList = az group list --query "[].{Name:name, Location:location}" --output table
81 | if ($rgList) {
82 | Write-Host "Here are the available resource groups in your subscription:"
83 | Write-Host $rgList
84 | exit 1
85 | } else {
86 | Write-Host "No resource groups found in this subscription."
87 | exit 1
88 | }
89 | } catch {
90 | Write-Host "Error retrieving resource groups."
91 | exit 1
92 | }
93 | }
94 |
95 | # Secure Unique Suffix Generator (12 Characters, No Spaces)
96 | function Get-SecureUniqueSuffix {
97 | $randomNumber = Get-Random -Minimum 10000000 -Maximum 99999999
98 | $uniqueSuffix = "$randomNumber"
99 |
100 | return $uniqueSuffix
101 | }
102 |
103 |
104 | # If a password was provided, validate it; otherwise, generate a new one
105 | if ($sqlAdminPw) {
106 | Test-Password -testPw $sqlAdminPw
107 | } else {
108 | $sqlAdminPw = Get-SecurePassword
109 | Write-Host "Generated Secure Password: $sqlAdminPw"
110 | }
111 |
112 | # Get current public IP
113 | $publicIp = Get-PublicIP
114 | Write-Host "Your current public IP is: $publicIp"
115 |
116 | # Check if a Resource Group with the prefix exists
117 | $matchingRg = az group list --query "[?starts_with(name, '$rgName')].name" --output tsv
118 |
119 | if ($matchingRg) {
120 | Write-Host "A resource group prefixed with '$rgName' already exists. Using resource group '$matchingRg'."
121 | $rgName = $matchingRg
122 | } else {
123 | # Step C: Try to create the resource group
124 | Write-Host "No existing resource group prefixed with '$rgName'. Attempting to create '$rgName'..."
125 | try {
126 | az group create --name $rgName --location $location --output none
127 | Write-Host "Resource Group '$rgName' created successfully."
128 | } catch {
129 | # Step D: If creation fails, list existing resource groups and exit
130 | Write-Host "Error: Unable to create resource group '$rgName'. Checking for existing resource groups..."
131 | Get-ExistingResourceGroups
132 | }
133 | }
134 |
135 | # Generate a unique string for resource names
136 | $uniqueSuffix = Get-SecureUniqueSuffix
137 | Write-Host "Generated unique suffix: $uniqueSuffix"
138 |
139 | # Deploy the Bicep file and capture output values
140 | $deploymentOutput = az deployment group create `
141 | --resource-group $rgName `
142 | --template-file "./$bicepFile" `
143 | --parameters sqlAdminPw="$sqlAdminPw" `
144 | adminIpAddress="$publicIp" `
145 | uniqueSuffix="$uniqueSuffix" `
146 | --query "properties.outputs" --output json | ConvertFrom-Json
147 |
148 | Write-Host "Deployment completed!"
149 |
150 | # Return SQL Server details
151 | Write-Host "`n==================== Deployment Results ===================="
152 | Write-Host "Resource Group Name: $rgName"
153 | Write-Host "SQL Server Name: $($deploymentOutput.sqlServerName.value).database.windows.net"
154 | Write-Host "SQL Database Name: $($deploymentOutput.sqlDatabaseName.value)"
155 | Write-Host "SQL Admin Username: $($deploymentOutput.sqlAdminUsername.value)"
156 | Write-Host "Password: $sqlAdminPw"
157 | Write-Host "============================================================"
158 |
--------------------------------------------------------------------------------
/Allfiles/Labs/Shared/AdventureWorks2017.bak:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MicrosoftLearning/dp-300-database-administrator/3c74b78e799d1ed721474f0e8aab85a240548ba7/Allfiles/Labs/Shared/AdventureWorks2017.bak
--------------------------------------------------------------------------------
/Instructions/Labs/00-setup-environment.md:
--------------------------------------------------------------------------------
1 | ---
2 | lab:
3 | title: 'Setup your own environment'
4 | module: 'Setup your own environment'
5 | ---
6 |
7 | # Setup local lab environment
8 |
9 | Ideally, you should complete these labs in a hosted lab environment. If you want to complete them on your own computer, you can do so by installing the following software. You may experience unexpected dialogs and behavior when using your own environment. Due to the wide range of possible local configurations, the course team cannot support issues you may encounter in your own environment.
10 |
11 | ## Windows Installation
12 |
13 | > 📝 The instructions below are for a Windows 10 computer. You can also use Linux or MacOS. You may need to adapt the lab instructions for your chosen OS.
14 |
15 | ### Windows 11 (OS)
16 |
17 | 1. Install Windows 11.
18 |
19 | 1. Apply all available updates.
20 |
21 | ### Edge
22 |
23 | 1. Install the latest version of Microsoft Edge from [https://microsoft.com/edge].
24 |
25 | ### Git
26 |
27 | 1. Download and install from [https://git-scm.com/downloads].
28 |
29 | - Use the default options in the installer.
30 |
31 | ### Azure CLI
32 |
33 | 1. Download and install from [https://aka.ms/installazurecliwindows].
34 |
35 | - Use the default options in the installer.
36 |
37 | ### Visual Studio Code
38 |
39 | 1. Download and install from [https://code.visualstudio.com/download].
40 |
41 | - Use the default options in the installer.
42 |
43 | ### SQL Server Management Studio
44 |
45 | 1. Download and install from [https://aka.ms/ssmsfullsetup].
46 |
47 | - Use the default options in the installer.
48 |
49 | ### SQL Server 2022 Developer Edition
50 |
51 | 1. Download and install from [https://www.microsoft.com/en-us/sql-server/sql-server-downloads].
52 |
53 | - Use the default options in the installer. Only the SQL Server Database Engine is required for these labs.
54 | - You can use the default instance.
55 | - You can use the default authentication mode.
56 |
--------------------------------------------------------------------------------
/Instructions/Labs/01-provision-sql-vm.md:
--------------------------------------------------------------------------------
1 | ---
2 | lab:
3 | title: 'Lab 1 - Provision SQL Server on an Azure Virtual Machine'
4 | module: 'Plan and Implement Data Platform Resources'
5 | ---
6 |
7 | # Provision a SQL Server on an Azure Virtual Machine
8 |
9 | **Estimated Time: 30 minutes**
10 |
11 | Students will explore the Azure Portal and use it to create an Azure VM with SQL Server 2022 installed. Then they will connect to the virtual machine through Remote Desktop Protocol.
12 |
13 | You are a database administrator for AdventureWorks. You need to create a test environment for use in a proof of concept. The proof of concept will use SQL Server on an Azure Virtual Machine and a backup of the AdventureWorksDW database. You need to set up the Virtual Machine, restore the database, and query it to ensure it is available.
14 |
15 | ## Deploy a SQL Server on an Azure Virtual Machine
16 |
17 | 1. From the lab virtual machine, start a browser session and navigate to [https://portal.azure.com](https://portal.azure.com/), and sign in using the Microsoft account associated with your Azure subscription.
18 |
19 | 1. Locate the search bar at the top of the page. Search for **Azure SQL**. Select the search result for **Azure SQL** that appears in the results under **Services**.
20 |
21 | 1. On the **Azure SQL** blade, select **Create**.
22 |
23 | 1. On the **Select SQL deployment option** blade, click on the drop-down box under **SQL virtual machines**. Select the option labeled **Free SQL Server License: SQL Server 2022 Developer on Windows Server 2022**. Then select **Create**.
24 |
25 | 1. On the **Create a virtual machine** page, enter the following information, *leave all other options as the default values*:
26 |
27 | - **Subscription:** <Your subscription>
28 | - **Resource group:** <Your resource group>
29 | - **Virtual machine name:** AzureSQLServerVM
30 | - **Region:** <Choose your local region, same as the selected region for your resource group.>
31 | - **Availability Options:** No infrastructure redundancy required
32 | - **Image:** Free SQL Server License: SQL Server 2022 Developer on Windows Server 2022 - Gen2
33 | - **Run with Azure spot discount:** No (unchecked)
34 | - **Size:** Standard *D2s_v5* (2 vCPUs, 8 GiB memory). *You may need to select the "See all sizes" link to see this option.*
35 | - **Administrator account username:** <Choose a name for your administrator account.>
36 | - **Administrator account password:** <Choose a strong password.>
37 | - **Select inbound ports:** RDP (3389)
38 | - **Would you like to use an existing Windows Server license?:** No (unchecked)
39 |
40 | > 📝 Make note of the username and password for later use.
41 |
42 | 1. Navigate to the **Disks** tab and review the configuration.
43 |
44 | 1. Navigate to the **Networking** tab and review the configuration.
45 |
46 | 1. Navigate to the **Management** tab and review the configuration.
47 |
48 | Verify that **Enable auto_shutdown** is unchecked.
49 |
50 | 1. Navigate to the **Advanced** tab and review the configuration.
51 |
52 | 1. Navigate to the **SQL Server settings** tab and review the configuration.
53 |
54 | > 📝 Note that you can also configure the storage for your SQL Server VM on this screen. By default, the SQL Server Azure VM templates create one premium disk with read caching for data, one premium disk without caching for transaction log, and uses the local SSD (D:\ on Windows) for tempdb.
55 |
56 | 1. Select the **Review + create** button. Then select **Create**.
57 |
58 | 1. On the deployment blade, wait until the deployment is complete. The VM will take approximate 5-10 minutes to deploy. After the deployment is complete, select **Go to resource**.
59 |
60 | > 📝 Note that your deployment may take several minutes to complete.
61 |
62 | 1. On the **Overview** page for the virtual machine, explore the menu options for this resource to review what is available.
63 |
64 | ---
65 |
66 | ## Connect to SQL Server on an Azure Virtual Machine
67 |
68 | 1. On the **Overview** page for the virtual machine, select the **Connect** pulldown and select **Connect**.
69 |
70 | 1. On the Connect pane, select the **Download RDP File** button.
71 |
72 | > 📝 If see the error **Port prerequisite not met**. Make sure to select the link to add an inbound network security group rule with the destination port mentioned in the *Port number* field.
73 |
74 | 1. Open the RDP file that was just downloaded. When a dialog appears asking if you want to connect, select **Connect**.
75 |
76 | 1. Enter the username and password selected during the virtual machine provisioning process. Then select **OK**.
77 |
78 | 1. When the **Remote Desktop Connection** dialog appears asking if you want to connect, select **Yes**.
79 |
80 | 1. Select the search bar besides the Windows Start button and type SSMS. Select **Microsoft SQL Server Management Studio** from the list.
81 |
82 | 1. When SSMS opens, notice that the **Connect to Server** dialog will be pre-populated with the default instance name. Check the option **Trust server certificate** and then select **Connect**.
83 |
84 | 1. Close SSMS by selecting the **X** in the upper right corner.
85 |
86 | 1. You can now disconnect from the virtual machine to close the RDP session.
87 |
88 | The Azure portal gives you powerful tools to manage a SQL Server hosted in a virtual machine. These tools include control over automated patching, automated backups, and giving you an easy way to setup high availability.
89 |
90 | ---
91 |
92 | ## Cleanup Resources
93 |
94 | If you are not using the virtual machine for any other purpose, you can clean up the resources you created in this lab.
95 |
96 | ### Delete the Resource Group
97 |
98 | If you created a new resource group for this lab, you can delete the resource group to remove all resources created in this lab.
99 |
100 | 1. In the Azure portal, select **Resource groups** from the left navigation pane or search for **Resource groups** in the search bar and select it from the results.
101 |
102 | 1. Go into the resource group that you created for this lab. The resource group will contain the virtual machine and other resources created in this lab.
103 |
104 | 1. Select **Delete resource group** from the top menu.
105 |
106 | 1. In the **Delete resource group** dialog, type the name of the resource group to confirm and select **Delete**.
107 |
108 | 1. Wait for the resource group to be deleted.
109 |
110 | 1. Close the Azure portal.
111 |
112 | ### Delete the Lab resources only
113 |
114 | If you didn't create a new resource group for this lab, and want to leave the resource group and its previous resources intact, you can still delete the resources created in this lab.
115 |
116 | 1. In the Azure portal, select **Resource groups** from the left navigation pane or search for **Resource groups** in the search bar and select it from the results.
117 |
118 | 1. Go into the resource group that you created for this lab. The resource group will contain the virtual machine and other resources created in this lab.
119 |
120 | 1. Select all the resources prefixed with the virtual machine name you previously specified in the lab.
121 |
122 | 1. Select **Delete** from the top menu.
123 |
124 | 1. In the **Delete resources** dialog, type **delete** and select **Delete**.
125 |
126 | 1. Select **Delete** again to confirm the deletion of the resources.
127 |
128 | 1. Wait for the resources to be deleted.
129 |
130 | 1. Close the Azure portal.
131 |
132 | ---
133 |
134 | You have successfully completed this lab.
135 |
--------------------------------------------------------------------------------
/Instructions/Labs/02-provision-sql-database.md:
--------------------------------------------------------------------------------
1 | ---
2 | lab:
3 | title: 'Lab 2 - Provision an Azure SQL Database'
4 | module: 'Plan and Implement Data Platform Resources'
5 | ---
6 |
7 | # Provision an Azure SQL Database
8 |
9 | **Estimated Time: 40 minutes**
10 |
11 | Students will configure basic resources needed to deploy an Azure SQL Database with a Virtual Network Endpoint. Connectivity to the SQL Database will be validated using SQL Server Management Studio from the lab VM if available or from your local machine setup.
12 |
13 | As a database administrator for AdventureWorks, you will set up a new SQL Database, including a Virtual Network Endpoint to increase and simplify the security of the deployment. SQL Server Management Studio will be used to evaluate the use of a SQL Notebook for data querying and results retention.
14 |
15 | ## Navigate on Azure portal
16 |
17 | 1. On the lab virtual machine if provided, otherwise on your local machine, open a browser window.
18 |
19 | 1. Navigate to the Azure portal at [https://portal.azure.com](https://portal.azure.com/). Log in to the Azure portal using your Azure account or provided credentials if available.
20 |
21 | 1. From the Azure portal, search for *resource groups* in the search box at the top, then select **Resource groups** from the list of options.
22 |
23 | 1. On the **Resource group** page, if provided, select the resource group starting with *contoso-rg*. If this resource group doesn't exist, either create a new resource group named with *contoso-rg* in your local region, or use an existing resource group and take note of the region its in.
24 |
25 | ## Create a Virtual Network
26 |
27 | 1. In the Azure portal home page, select the left hand menu.
28 |
29 | 1. In the left navigation pane, select **Virtual Networks**
30 |
31 | 1. Select **+ Create** to open the **Create Virtual Network** page. On the **Basics** tab, complete the following information:
32 |
33 | - **Subscription:** <Your subscription>
34 | - **Resource group:** starting with *DP300* or the resource group you previously selected
35 | - **Name:** lab02-vnet
36 | - **Region:** Select the same region where your resource group was created
37 |
38 | 1. Select **Review + Create**, review the settings for the new virtual network, and then select **Create**.
39 |
40 | ## Provision an Azure SQL Database in the Azure portal
41 |
42 | 1. From the Azure Portal, search for *SQL databases* in the search box at the top, then select **SQL databases** from the list of options.
43 |
44 | 1. On the **SQL databases** blade, select **+ Create**.
45 |
46 | 1. On the **Create SQL Database** page, select the following options on the **Basics** tab and then select **Next: Networking**.
47 |
48 | - **Subscription:** <Your subscription>
49 | - **Resource group:** starting with *DP300* or the resource group you previously selected
50 | - **Database Name:** AdventureWorksLT
51 | - **Server:** select on **Create new** link. The **Create SQL Database Server** page will open. Provide the server details as follow:
52 | - **Server name:** dp300-lab-<your initials (lower case)> and if needed a random 5 digit number (server name must be globally unique)
53 | - **Location:** <your local region, same as the selected region for your resource group, otherwise it may fail>
54 | - **Authentication method:** Use SQL authentication
55 | - **Server admin login:** dp300admin
56 | - **Password:** select a complex password and take note of it
57 | - **Confirm password:** select the same previously selected password
58 | - Select **OK** to to return to the **Create SQL Database** page.
59 | - **Want to use Elastic Pool?** set to **No**.
60 | - **Workload environment:** Development
61 | - On the **Compute + Storage** option, select on **Configure database** link. On the **Configure** page, for **Service tier** dropdown, select **Basic**, and then **Apply**.
62 |
63 | 1. For the **Backup storage redundancy** option, keep the default value: **Local-redundant backup storage**.
64 |
65 | 1. Then select **Next: Networking**.
66 |
67 | 1. On the **Networking** tab, for **Network Connectivity** option, select the **Private endpoint** radio button.
68 |
69 | 1. Then select the **+ Add private endpoint** link under the **Private endpoints** option.
70 |
71 | 1. Complete the **Create private endpoint** right pane as follows:
72 |
73 | - **Subscription:** <Your subscription>
74 | - **Resource group:** starting with *DP300* or the resource group you previously selected
75 | - **Location:** <your local region, same as the selected region for your resource group, otherwise it may fail>
76 | - **Name:** DP-300-SQL-Endpoint
77 | - **Target sub-resource:** SqlServer
78 | - **Virtual network:** lab02-vnet
79 | - **Subnet:** lab02-vnet/default (10.x.0.0/24)
80 | - **Integrate with private DNS zone:** Yes
81 | - **Private DNS zone:** keep the default value
82 | - Review settings, and then select **OK**
83 |
84 | 1. The new endpoint will appear on the **Private endpoints** list.
85 |
86 | 1. Select **Next: Security**, and then **Next: Additional settings**.
87 |
88 | 1. On the **Additional settings** page, select **Sample** on the **Use existing data** option. Select **OK** if a pop-up message is displayed for the sample database.
89 |
90 | 1. Select **Review + Create**.
91 |
92 | 1. Review the settings before selecting **Create**.
93 |
94 | 1. Once the deployment is complete, select **Go to resource**.
95 |
96 | ## Enable access to an Azure SQL Database
97 |
98 | 1. From the **SQL database** page, select the **Overview** section, and then select the link for the server name in the top section.
99 |
100 | 1. On the SQL servers navigation blade, select **Networking** under the **Security** section.
101 |
102 | 1. On the **Public access** tab, select **Selected networks**.
103 |
104 | 1. Select **+ Add your client IPv4 address**. This will add a firewall rule to allow your current IP address to access the SQL server.
105 |
106 | 1. Check the **Allow Azure services and resources to access this server** property.
107 |
108 | 1. Select **Save**.
109 |
110 | ---
111 |
112 | ## Connect to an Azure SQL Database in SQL Server Management Studio
113 |
114 | 1. On the Azure portal, select the **SQL databases** in the left navigation pane. And then select the **AdventureWorksLT** database.
115 |
116 | 1. Copy the **Server name** value from the **Overview** page.
117 |
118 | 1. Launch SQL Server Management Studio from the lab virtual machine if provided or your local machine if not.
119 |
120 | 1. In the **Connect to Server** dialog, paste the **Server name** value copied from the Azure portal.
121 |
122 | 1. In the **Authentication** dropdown, select **SQL Server Authentication**.
123 |
124 | 1. In the **Login** field, enter **dp300admin**.
125 |
126 | 1. In the **Password** field, enter the password selected during the SQL server creation.
127 |
128 | 1. Select **Connect**.
129 |
130 | 1. SQL Server Management Studio will connect to your Azure SQL Database server. You can expand the server and then the **Databases** node to see the *AdventureWorksLT* database.
131 |
132 | ## Query an Azure SQL Database with SQL Server Management Studio
133 |
134 | 1. In SQL Server Management Studio, right-click on the *AdventureWorksLT* database and select **New Query**.
135 |
136 | 1. Paste the following SQL statement into the query window:
137 |
138 | ```sql
139 | SELECT TOP 10 cust.[CustomerID],
140 | cust.[CompanyName],
141 | SUM(sohead.[SubTotal]) as OverallOrderSubTotal
142 | FROM [SalesLT].[Customer] cust
143 | INNER JOIN [SalesLT].[SalesOrderHeader] sohead
144 | ON sohead.[CustomerID] = cust.[CustomerID]
145 | GROUP BY cust.[CustomerID], cust.[CompanyName]
146 | ORDER BY [OverallOrderSubTotal] DESC
147 | ```
148 |
149 | 1. Select on the **Execute** button in the toolbar to execute the query.
150 |
151 | 1. In the **Results** pane, review the results of the query.
152 |
153 | 1. Right-click on the *AdventureWorksLT* database and select **New Query**.
154 |
155 | 1. Paste the following SQL statement into the query window:
156 |
157 | ```sql
158 | SELECT TOP 10 cat.[Name] AS ProductCategory,
159 | SUM(detail.[OrderQty]) AS OrderedQuantity
160 | FROM salesLT.[ProductCategory] cat
161 | INNER JOIN [SalesLT].[Product] prod
162 | ON prod.[ProductCategoryID] = cat.[ProductCategoryID]
163 | INNER JOIN [SalesLT].[SalesOrderDetail] detail
164 | ON detail.[ProductID] = prod.[ProductID]
165 | GROUP BY cat.[name]
166 | ORDER BY [OrderedQuantity] DESC
167 | ```
168 |
169 | 1. Select on the **Execute** button in the toolbar to execute the query.
170 |
171 | 1. In the **Results** pane, review the results of the query.
172 |
173 | 1. Close SQL Server Management Studio. Select **No** when prompted to save changes.
174 |
175 | ---
176 |
177 | ## Cleanup Resources
178 |
179 | If you are not using the virtual machine for any other purpose, you can clean up the resources you created in this lab.
180 |
181 | ### Delete the Resource Group
182 |
183 | If you created a new resource group for this lab, you can delete the resource group to remove all resources created in this lab.
184 |
185 | 1. In the Azure portal, select **Resource groups** from the left navigation pane or search for **Resource groups** in the search bar and select it from the results.
186 |
187 | 1. Go into the resource group that you created for this lab. The resource group will contain the virtual machine and other resources created in this lab.
188 |
189 | 1. Select **Delete resource group** from the top menu.
190 |
191 | 1. In the **Delete resource group** dialog, type the name of the resource group to confirm and select **Delete**.
192 |
193 | 1. Wait for the resource group to be deleted.
194 |
195 | 1. Close the Azure portal.
196 |
197 | ### Delete the Lab resources only
198 |
199 | If you didn't create a new resource group for this lab, and want to leave the resource group and its previous resources intact, you can still delete the resources created in this lab.
200 |
201 | 1. In the Azure portal, select **Resource groups** from the left navigation pane or search for **Resource groups** in the search bar and select it from the results.
202 |
203 | 1. Go into the resource group that you created for this lab. The resource group will contain the virtual machine and other resources created in this lab.
204 |
205 | 1. Select all the resources prefixed with the SQL Server name you previously specified in the lab. Additionally, select the virtual network and the private DNS zone you created.
206 |
207 | 1. Select **Delete** from the top menu.
208 |
209 | 1. In the **Delete resources** dialog, type **delete** and select **Delete**.
210 |
211 | 1. Select **Delete** again to confirm the deletion of the resources.
212 |
213 | 1. Wait for the resources to be deleted.
214 |
215 | 1. Close the Azure portal.
216 |
217 | ---
218 |
219 | You have successfully completed this lab.
220 |
221 | In this exercise, you've seen how you deploy a Azure SQL Database with a Virtual Network Endpoint. You were also able to connect to the SQL Database you've created using SQL Server Management Studio.
222 |
--------------------------------------------------------------------------------
/Instructions/Labs/03-authorize-access-azure-sql-database.md:
--------------------------------------------------------------------------------
1 | ---
2 | lab:
3 | title: 'Lab 3 – Authorize access to Azure SQL Database with Microsoft Entra ID'
4 | module: 'Implement a Secure Environment for a Database Service'
5 | ---
6 |
7 | # Configure database authentication and authorization
8 |
9 | **Estimated Time: 25 minutes**
10 |
11 | The students will take the information gained in the lessons to configure and subsequently implement security in the Azure Portal and within the *AdventureWorksLT* database.
12 |
13 | You've been hired as a Senior Database Administrator to help ensure the security of the database environment.
14 |
15 | > 📝 These exercises ask you to copy and paste T-SQL code and makes use of existing SQL resources. Please verify that the code has been copied correctly, before executing the code.
16 |
17 | ## Setup environment
18 |
19 | If your lab virtual machine has been provided and pre-configured, you should find the lab files ready in the **C:\LabFiles** folder. *Take a moment to check, if the files are already there, skip this section*. However, if you're using your own machine or the lab files are missing, you'll need to clone them from *GitHub* to proceed.
20 |
21 | 1. From the lab virtual machine or your local machine if one wasn't provided, start a Visual Studio Code session.
22 |
23 | 1. Open the command palette (Ctrl+Shift+P) and type **Git: Clone**. Select the **Git: Clone** option.
24 |
25 | 1. Paste the following URL into the **Repository URL** field and select **Enter**.
26 |
27 | ```url
28 | https://github.com/MicrosoftLearning/dp-300-database-administrator.git
29 | ```
30 |
31 | 1. Save the repository to the **C:\LabFiles** folder on the lab virtual machine or your local machine if one wasn't provided (create the folder if it does not exist).
32 |
33 | ## Setup your SQL Server in Azure
34 |
35 | Log in to Azure and check if you have an existing Azure SQL Server instance running in Azure. *Skip this section if you already have a SQL Server instance running in Azure*.
36 |
37 | 1. From the lab virtual machine or your local machine if one wasn't provided, start a Visual Studio Code session and navigate to the cloned repository from the previous section.
38 |
39 | 1. Right-click on the **/Allfiles/Labs** folder and select **Open in Integrated Terminal**.
40 |
41 | 1. Let's connect to Azure using the Azure CLI. Type the following command and select **Enter**.
42 |
43 | ```bash
44 | az login
45 | ```
46 |
47 | > 📝 Note that a browser window will open. Use your Azure credentials to log in.
48 |
49 | 1. Once you are logged in to Azure, it's time to create a resource group if it doesn't already exist, and create a SQL server and database under that resource group. Type the following command and select **Enter**. *The script will take a few minutes to complete*.
50 |
51 | ```bash
52 | cd ./Setup
53 | ./deploy-sql-database.ps1
54 | ```
55 |
56 | > 📝 Note that by default this script will create or a resource group called **contoso-rg**, or use a resource whose name start with *contoso-rg* if it exists. By default it will also create all resources on the **West US 2** region (westus2). Finally it will generate a random 12 character password for the **SQL admin password**. You can change these values by using one or more of the parameters **-rgName**, **-location** and **-sqlAdminPw** with your own values. The password will have to meet the Azure SQL password complexity requirements, at least 12 characters long, and contain at least 1 uppercase letter, 1 lowercase letter, 1 number and 1 special character.
57 |
58 | > 📝 Note that the script will add your current Public IP address to the SQL server firewall rules.
59 |
60 | 1. Once the script has completed, it will return the resource group name, SQL server name and database name, and admin user name and password. Take note of these values as you will need them later in the lab.
61 |
62 | ---
63 |
64 | ## Authorize access to Azure SQL Database with Microsoft Entra
65 |
66 | You can create logins from Microsoft Entra accounts as a contained database user using the `CREATE USER [anna@contoso.com] FROM EXTERNAL PROVIDER` T-SQL syntax. A contained database user maps to an identity in the Microsoft Entra directory associated with the database and has no login in the `master` database.
67 |
68 | With the introduction of Microsoft Entra server logins in Azure SQL Database, you can create logins from Microsoft Entra principals in the virtual `master` database of a SQL Database. You can create Microsoft Entra logins from Microsoft Entra *users, groups, and service principals*. For more information, see [Microsoft Entra server principals](/azure/azure-sql/database/authentication-azure-ad-logins)
69 |
70 | Additionally, you can use the Azure portal only to create administrators, and Azure role-based access control roles don't propagate to Azure SQL Database logical servers. You must grant additional server and database permissions by using Transact-SQL (T-SQL). Let's create a Microsoft Entra admin for the SQL server.
71 |
72 | 1. From the lab virtual machine or your local machine if one wasn't provided, start a browser session and navigate to [https://portal.azure.com](https://portal.azure.com/). Connect to the Portal using your Azure credentials.
73 |
74 | 1. On the Azure portal home page search for **SQL servers** and select it.
75 |
76 | 1. Select the SQL server **dp300-lab-xxxxxxxx**, where *xxxxxxxx* is a random numeric string.
77 |
78 | > 📝 Note, if you are using your own Azure SQL server not created by this lab, select the name of that SQL server.
79 |
80 | 1. In the *Overview* blade, select **Not configured** next to *Microsoft Entra admin*.
81 |
82 | 1. On the next screen, select **Set admin**.
83 |
84 | 1. In the **Microsoft Entra ID** sidebar, search for the Azure username you logged into the Azure portal with, then click on **Select**.
85 |
86 | 1. Select **Save** to complete the process. This will make your username the Microsoft Entra admin for the server.
87 |
88 | 1. On the left select **Overview**, then copy the **Server name**.
89 |
90 | 1. Open SQL Server Management Studio (SSMS) and select **Connect** > **Database Engine**. In the **Server name** paste the name of your server. Change the authentication type to **Microsoft Entra MFA**.
91 |
92 | 1. Select **Connect**.
93 |
94 | ## Manage access to database objects
95 |
96 | In this task you will manage access to the database and its objects. The first thing you will do is create two users in the *AdventureWorksLT* database.
97 |
98 | 1. From the lab virtual machine or your local machine if one wasn't provided, in SSMS, login to the *AdventureWorksLT* database using the Azure Server admin account or the Microsoft Entra admin account.
99 |
100 | 1. Use the **Object Explorer** and expand **Databases**.
101 |
102 | 1. Right-click on **AdventureWorksLT**, and select **New Query**.
103 |
104 | 1. In the new query window, copy and paste the below T-SQL into it. Execute the query to create the two users.
105 |
106 | ```sql
107 | CREATE USER [DP300User1] WITH PASSWORD = 'Azur3Pa$$';
108 | GO
109 |
110 | CREATE USER [DP300User2] WITH PASSWORD = 'Azur3Pa$$';
111 | GO
112 | ```
113 |
114 | **Note:** These users are created in the scope of the AdventureWorksLT database. Next you will create a custom role and add the users to it.
115 |
116 | 1. Execute the following T-SQL in the same query window.
117 |
118 | ```sql
119 | CREATE ROLE [SalesReader];
120 | GO
121 |
122 | ALTER ROLE [SalesReader] ADD MEMBER [DP300User1];
123 | GO
124 |
125 | ALTER ROLE [SalesReader] ADD MEMBER [DP300User2];
126 | GO
127 | ```
128 |
129 | Next create a new stored procedure in the **SalesLT** schema.
130 |
131 | 1. Execute the below T-SQL in your query window.
132 |
133 | ```sql
134 | CREATE OR ALTER PROCEDURE SalesLT.DemoProc
135 | AS
136 | SELECT P.Name, Sum(SOD.LineTotal) as TotalSales ,SOH.OrderDate
137 | FROM SalesLT.Product P
138 | INNER JOIN SalesLT.SalesOrderDetail SOD on SOD.ProductID = P.ProductID
139 | INNER JOIN SalesLT.SalesOrderHeader SOH on SOH.SalesOrderID = SOD.SalesOrderID
140 | GROUP BY P.Name, SOH.OrderDate
141 | ORDER BY TotalSales DESC
142 | GO
143 | ```
144 |
145 | Next use the `EXECUTE AS USER` syntax to test out the security. This allows the database engine to execute a query in the context of your user.
146 |
147 | 1. Execute the following T-SQL.
148 |
149 | ```sql
150 | EXECUTE AS USER = 'DP300User1'
151 | EXECUTE SalesLT.DemoProc
152 | ```
153 |
154 | This will fail with the message:
155 |
156 | Msg 229, Level 14, State 5, Procedure SalesLT.DemoProc, Line 1 [Batch Start Line 0]
157 | The EXECUTE permission was denied on the object 'DemoProc', database 'AdventureWorksLT', schema 'SalesLT'.
158 |
159 | 1. Next grant permissions to the role to allow it to execute the store procedure. Execute the below T-SQL.
160 |
161 | ```sql
162 | REVERT;
163 | GRANT EXECUTE ON SCHEMA::SalesLT TO [SalesReader];
164 | GO
165 | ```
166 |
167 | The first command reverts the execution context back to the database owner.
168 |
169 | 1. Rerun the previous T-SQL.
170 |
171 | ```sql
172 | EXECUTE AS USER = 'DP300User1'
173 | EXECUTE SalesLT.DemoProc
174 | ```
175 |
176 | ---
177 |
178 | ## Cleanup Resources
179 |
180 | If you are not using the Azure SQL Server for any other purpose, you can clean up the resources you created in this lab.
181 |
182 | ### Delete the Resource Group
183 |
184 | If you created a new resource group for this lab, you can delete the resource group to remove all resources created in this lab.
185 |
186 | 1. In the Azure portal, select **Resource groups** from the left navigation pane or search for **Resource groups** in the search bar and select it from the results.
187 |
188 | 1. Go into the resource group that you created for this lab. The resource group will contain the Azure SQL Server and other resources created in this lab.
189 |
190 | 1. Select **Delete resource group** from the top menu.
191 |
192 | 1. In the **Delete resource group** dialog, type the name of the resource group to confirm and select **Delete**.
193 |
194 | 1. Wait for the resource group to be deleted.
195 |
196 | 1. Close the Azure portal.
197 |
198 | ### Delete the Lab resources only
199 |
200 | If you didn't create a new resource group for this lab, and want to leave the resource group and its previous resources intact, you can still delete the resources created in this lab.
201 |
202 | 1. In the Azure portal, select **Resource groups** from the left navigation pane or search for **Resource groups** in the search bar and select it from the results.
203 |
204 | 1. Go into the resource group that you created for this lab. The resource group will contain the Azure SQL Server and other resources created in this lab.
205 |
206 | 1. Select all the resources prefixed with the SQL Server name you previously specified in the lab.
207 |
208 | 1. Select **Delete** from the top menu.
209 |
210 | 1. In the **Delete resources** dialog, type **delete** and select **Delete**.
211 |
212 | 1. Select **Delete** again to confirm the deletion of the resources.
213 |
214 | 1. Wait for the resources to be deleted.
215 |
216 | 1. Close the Azure portal.
217 |
218 | ### Delete the LabFiles folder
219 |
220 | If you created a new LabFiles folder for this lab, and no longer need it, you can delete the LabFiles folder to remove all files created in this lab.
221 |
222 | 1. From the lab virtual machine or your local machine if one wasn't provided, open file explorer and navigate to the **C:\\** drive.
223 | 1. Right-click on the **LabFiles** folder and select **Delete**.
224 | 1. Select **Yes** to confirm the deletion of the folder.
225 |
226 | ---
227 |
228 | You have successfully completed this lab.
229 |
230 | In this exercise, you've seen how you can use Microsoft Entra ID to grant Azure credentials access to a SQL Server hosted in Azure. You've also used T-SQL statement to create new database users and granted them permissions to run stored procedures.
231 |
--------------------------------------------------------------------------------
/Instructions/Labs/04-configure-firewall-rule.md:
--------------------------------------------------------------------------------
1 | ---
2 | lab:
3 | title: 'Lab 4 – Configure Azure SQL Database firewall rules'
4 | module: 'Implement a Secure Environment for a Database Service'
5 | ---
6 |
7 | # Implement a Secure Environment
8 |
9 | **Estimated Time: 30 minutes**
10 |
11 | The students will take the information gained in the lessons to configure and subsequently implement security in the Azure Portal and within the *AdventureWorksLT* database.
12 |
13 | You have been hired as a Senior Database Administrator to help ensure the security of the database environment. These tasks will focus on Azure SQL Database.
14 |
15 | > 📝 These exercises ask you to copy and paste T-SQL code and makes use of existing SQL resources. Please verify that the code has been copied correctly, before executing the code.
16 |
17 | ## Setup environment
18 |
19 | If your lab virtual machine has been provided and pre-configured, you should find the lab files ready in the **C:\LabFiles** folder. *Take a moment to check, if the files are already there, skip this section*. However, if you're using your own machine or the lab files are missing, you'll need to clone them from *GitHub* to proceed.
20 |
21 | 1. From the lab virtual machine or your local machine if one wasn't provided, start a Visual Studio Code session.
22 |
23 | 1. Open the command palette (Ctrl+Shift+P) and type **Git: Clone**. Select the **Git: Clone** option.
24 |
25 | 1. Paste the following URL into the **Repository URL** field and select **Enter**.
26 |
27 | ```url
28 | https://github.com/MicrosoftLearning/dp-300-database-administrator.git
29 | ```
30 |
31 | 1. Save the repository to the **C:\LabFiles** folder on the lab virtual machine or your local machine if one wasn't provided (create the folder if it does not exist).
32 |
33 | ## Setup your SQL Server in Azure
34 |
35 | Log in to Azure and check if you have an existing Azure SQL Server instance running in Azure. *Skip this section if you already have a SQL Server instance running in Azure*.
36 |
37 | 1. From the lab virtual machine or your local machine if one wasn't provided, start a Visual Studio Code session and navigate to the cloned repository from the previous section.
38 |
39 | 1. Right-click on the **/Allfiles/Labs** folder and select **Open in Integrated Terminal**.
40 |
41 | 1. Let's connect to Azure using the Azure CLI. Type the following command and select **Enter**.
42 |
43 | ```bash
44 | az login
45 | ```
46 |
47 | > 📝 Note that a browser window will open. Use your Azure credentials to log in.
48 |
49 | 1. Once you are logged in to Azure, it's time to create a resource group if it doesn't already exist, and create a SQL server and database under that resource group. Type the following command and select **Enter**. *The script will take a few minutes to complete*.
50 |
51 | ```bash
52 | cd ./Setup
53 | ./deploy-sql-database.ps1
54 | ```
55 |
56 | > 📝 Note that by default this script will create or a resource group called **contoso-rg**, or use a resource whose name start with *contoso-rg* if it exists. By default it will also create all resources on the **West US 2** region (westus2). Finally it will generate a random 12 character password for the **SQL admin password**. You can change these values by using one or more of the parameters **-rgName**, **-location** and **-sqlAdminPw** with your own values. The password will have to meet the Azure SQL password complexity requirements, at least 12 characters long, and contain at least 1 uppercase letter, 1 lowercase letter, 1 number and 1 special character.
57 |
58 | > 📝 Note that the script will add your current Public IP address to the SQL server firewall rules.
59 |
60 | 1. Once the script has completed, it will return the resource group name, SQL server name and database name, and admin user name and password. *Take note of these values as you will need them later in the lab*.
61 |
62 | ---
63 |
64 | ## Configure Azure SQL Database firewall rules
65 |
66 | 1. From the lab virtual machine or your local machine if one wasn't provided, start a browser session and navigate to [https://portal.azure.com](https://portal.azure.com/). Connect to the Portal using your Azure credentials.
67 |
68 | 1. From the Azure Portal, search for *SQL servers* in the search box at the top, then select **SQL servers** from the list of options.
69 |
70 | 1. Select the SQL server **dp300-lab-xxxxxxxx**, where *xxxxxxxx* is a random numeric string.
71 |
72 | > 📝 Note, if you are using your own Azure SQL server not created by this lab, select the name of that SQL server.
73 |
74 | 1. In the *Overview* screen for your SQL server, to the right of the server name, select the **Copy to clipboard** button.
75 |
76 | 1. Select **Show networking settings**.
77 |
78 | 1. On the **Networking** page, under **Firewall rules**, review the list and make sure that your client IP address is listed. If it isn't listed, select on **+ Add your client IPv4 address (your IP address)**, and then select **Save**.
79 |
80 | > 📝 Note that your client IP address was automatically entered for you. Adding your client IP address to the list will allow you to connect to your Azure SQL Database using SQL Server Management Studio (SSMS) or any other client tools. **Make note of your client IP address, you will use it later.**
81 |
82 | 1. Open SQL Server Management Studio. On the Connect to Server dialog box, paste in the name of your Azure SQL Database server, and login with the following credentials:
83 |
84 | - **Server name:** <_paste your Azure SQL Database server name here_>
85 | - **Authentication:** SQL Server Authentication
86 | - **Server admin login:** Your Azure SQL Database server admin login
87 | - **Password:** Your Azure SQL Database server admin password
88 |
89 | 1. Select **Connect**.
90 |
91 | 1. In Object Explorer expand the server node, and right click on **Databases**. Select **Import a Data-tier Application**.
92 |
93 | 1. In the **Import Data Tier Application** dialog, click **Next** on the first screen.
94 |
95 | 1. In the **Import Settings** screen, click **Browse** and navigate to **C:\LabFiles\dp-300-database-administrator\Allfiles\Labs\04** folder, click on the **AdventureWorksLT.bacpac** file, and then click **Open**. Back to the **Import Data-tier Application** screen select **Next**.
96 |
97 | 1. On the **Database Settings** screen, make the changes as below:
98 |
99 | - **Database name:** AdventureWorksFromBacpac
100 | - **Edition of Microsoft Azure SQL Database**: Basic
101 |
102 | 1. Select **Next**.
103 |
104 | 1. On the **Summary** screen select **Finish**. This could take a few minutes. When your import completes you will see the results below. Then select **Close**.
105 |
106 | 1. Back to SQL Server Management Studio, in **Object Explorer**, expand the **Databases** folder. Then right-click on **AdventureWorksFromBacpac** database, and then **New Query**.
107 |
108 | 1. Execute the following T-SQL query by pasting the text into your query window.
109 | 1. **Important:** Replace **000.000.000.000** with your client IP address. Select **Execute**.
110 |
111 | ```sql
112 | EXECUTE sp_set_database_firewall_rule
113 | @name = N'AWFirewallRule',
114 | @start_ip_address = '000.000.000.000',
115 | @end_ip_address = '000.000.000.000'
116 | ```
117 |
118 | 1. Next you will create a contained user in the **AdventureWorksFromBacpac** database. Select **New Query** and execute the following T-SQL.
119 |
120 | ```sql
121 | USE [AdventureWorksFromBacpac]
122 | GO
123 | CREATE USER ContainedDemo WITH PASSWORD = 'P@ssw0rd01'
124 | ```
125 |
126 | > 📝 This command creates a contained user within the **AdventureWorksFromBacpac** database. We will test this credential in the next step.
127 |
128 | 1. Navigate to the **Object Explorer**. Click on **Connect**, and then **Database Engine**.
129 |
130 | 1. Attempt to connect with the credentials you created in the previous step. You will need to use the following information:
131 |
132 | - **Login:** ContainedDemo
133 | - **Password:** P@ssw0rd01
134 |
135 | Click **Connect**.
136 |
137 | You will receive the following error.
138 |
139 | Login failed for user 'ContainedDemo'. (Microsoft SQL Server, Error: 18456)
140 |
141 | > 📝 This error is generated because the connection attempted to login to the *master* database and not **AdventureWorksFromBacpac** where the user was created. Change the connection context by selecting **OK** to exit the error message, and then selecting on **Options >>** in the **Connect to Server**.
142 |
143 | 1. On the **Connection Properties** tab, type the database name **AdventureWorksFromBacpac**, and then select **Connect**.
144 |
145 | 1. Notice that you were able to successfully authenticate using the **ContainedDemo** user. This time you were directly logged into **AdventureWorksFromBacpac**, which is the only database to which the newly created user has access to.
146 |
147 | ---
148 |
149 | ## Cleanup Resources
150 |
151 | If you are not using the Azure SQL Server for any other purpose, you can clean up the resources you created in this lab.
152 |
153 | ### Delete the Resource Group
154 |
155 | If you created a new resource group for this lab, you can delete the resource group to remove all resources created in this lab.
156 |
157 | 1. In the Azure portal, select **Resource groups** from the left navigation pane or search for **Resource groups** in the search bar and select it from the results.
158 |
159 | 1. Go into the resource group that you created for this lab. The resource group will contain the Azure SQL Server and other resources created in this lab.
160 |
161 | 1. Select **Delete resource group** from the top menu.
162 |
163 | 1. In the **Delete resource group** dialog, type the name of the resource group to confirm and select **Delete**.
164 |
165 | 1. Wait for the resource group to be deleted.
166 |
167 | 1. Close the Azure portal.
168 |
169 | ### Delete the Lab resources only
170 |
171 | If you didn't create a new resource group for this lab, and want to leave the resource group and its previous resources intact, you can still delete the resources created in this lab.
172 |
173 | 1. In the Azure portal, select **Resource groups** from the left navigation pane or search for **Resource groups** in the search bar and select it from the results.
174 |
175 | 1. Go into the resource group that you created for this lab. The resource group will contain the Azure SQL Server and other resources created in this lab.
176 |
177 | 1. Select all the resources prefixed with the SQL Server name you previously specified in the lab.
178 |
179 | 1. Select **Delete** from the top menu.
180 |
181 | 1. In the **Delete resources** dialog, type **delete** and select **Delete**.
182 |
183 | 1. Select **Delete** again to confirm the deletion of the resources.
184 |
185 | 1. Wait for the resources to be deleted.
186 |
187 | 1. Close the Azure portal.
188 |
189 | ### Delete the LabFiles folder
190 |
191 | If you created a new LabFiles folder for this lab, and no longer need it, you can delete the LabFiles folder to remove all files created in this lab.
192 |
193 | 1. From the lab virtual machine or your local machine if one wasn't provided, open file explorer and navigate to the **C:\\** drive.
194 | 1. Right-click on the **LabFiles** folder and select **Delete**.
195 | 1. Select **Yes** to confirm the deletion of the folder.
196 |
197 | ---
198 |
199 | You have successfully completed this lab.
200 |
201 | In this exercise, you've configured server and database firewall rules to access a database hosted on Azure SQL Database. You've also used T-SQL statements to create a contained user, and used SQL Server Management Studio to check the access.
202 |
--------------------------------------------------------------------------------
/Instructions/Labs/05-enable-sql-defender-and-data-classification.md:
--------------------------------------------------------------------------------
1 | ---
2 | lab:
3 | title: 'Lab 5 – Enable Microsoft Defender for SQL and Data classification'
4 | module: 'Implement a Secure Environment for a Database Service'
5 | ---
6 |
7 | # Enable Microsoft Defender for SQL and Data Classification
8 |
9 | **Estimated Time: 30 minutes**
10 |
11 | The students will take the information gained in the lessons to configure and subsequently implement security in the Azure Portal and within the AdventureWorks database.
12 |
13 | You have been hired as a Senior Database Administrator help ensure the security of the database environment. These tasks will focus on Azure SQL Database.
14 |
15 | > 📝 These exercises ask you to copy and paste T-SQL code and makes use of existing SQL resources. Please verify that the code has been copied correctly, before executing the code.
16 |
17 | ## Setup environment
18 |
19 | If your lab virtual machine has been provided and pre-configured, you should find the lab files ready in the **C:\LabFiles** folder. *Take a moment to check, if the files are already there, skip this section*. However, if you're using your own machine or the lab files are missing, you'll need to clone them from *GitHub* to proceed.
20 |
21 | 1. From the lab virtual machine or your local machine if one wasn't provided, start a Visual Studio Code session.
22 |
23 | 1. Open the command palette (Ctrl+Shift+P) and type **Git: Clone**. Select the **Git: Clone** option.
24 |
25 | 1. Paste the following URL into the **Repository URL** field and select **Enter**.
26 |
27 | ```url
28 | https://github.com/MicrosoftLearning/dp-300-database-administrator.git
29 | ```
30 |
31 | 1. Save the repository to the **C:\LabFiles** folder on the lab virtual machine or your local machine if one wasn't provided (create the folder if it does not exist).
32 |
33 | ## Setup your SQL Server in Azure
34 |
35 | Log in to Azure and check if you have an existing Azure SQL Server instance running in Azure. *Skip this section if you already have a SQL Server instance running in Azure*.
36 |
37 | 1. From the lab virtual machine or your local machine if one wasn't provided, start a Visual Studio Code session and navigate to the cloned repository from the previous section.
38 |
39 | 1. Right-click on the **/Allfiles/Labs** folder and select **Open in Integrated Terminal**.
40 |
41 | 1. Let's connect to Azure using the Azure CLI. Type the following command and select **Enter**.
42 |
43 | ```bash
44 | az login
45 | ```
46 |
47 | > 📝 Note that a browser window will open. Use your Azure credentials to log in.
48 |
49 | 1. Once you are logged in to Azure, it's time to create a resource group if it doesn't already exist, and create a SQL server and database under that resource group. Type the following command and select **Enter**. *The script will take a few minutes to complete*.
50 |
51 | ```bash
52 | cd ./Setup
53 | ./deploy-sql-database.ps1
54 | ```
55 |
56 | > 📝 Note that by default this script will create or a resource group called **contoso-rg**, or use a resource whose name start with *contoso-rg* if it exists. By default it will also create all resources on the **West US 2** region (westus2). Finally it will generate a random 12 character password for the **SQL admin password**. You can change these values by using one or more of the parameters **-rgName**, **-location** and **-sqlAdminPw** with your own values. The password will have to meet the Azure SQL password complexity requirements, at least 12 characters long, and contain at least 1 uppercase letter, 1 lowercase letter, 1 number and 1 special character.
57 |
58 | > 📝 Note that the script will add your current Public IP address to the SQL server firewall rules.
59 |
60 | 1. Once the script has completed, it will return the resource group name, SQL server name and database name, and admin user name and password. *Take note of these values as you will need them later in the lab*.
61 |
62 | ---
63 |
64 | ## Enable Microsoft Defender for SQL
65 |
66 | 1. From the lab virtual machine or your local machine if one wasn't provided, start a browser session and navigate to [https://portal.azure.com](https://portal.azure.com/). Connect to the Portal using your Azure credentials.
67 |
68 | 1. From the Azure Portal, search for *SQL servers* in the search box at the top, then select **SQL servers** from the list of options.
69 |
70 | 1. Select the SQL server **dp300-lab-xxxxxxxx**, where *xxxxxxxx* is a random numeric string.
71 |
72 | > 📝 Note, if you are using your own Azure SQL server not created by this lab, select the name of that SQL server.
73 |
74 | 1. In the *Overview* blade, select **Not configured** next to *Microsoft Defender for SQL*.
75 |
76 | 1. Select the **X** at the upper right to close the *Microsoft Defender for Cloud* Overview pane.
77 |
78 | 1. Select **Enable** under *Microsoft Defender for SQL*.
79 |
80 | 1. In a production environment, there should be multiple recommendations listed. You would want to select **View all recommendations in Defender for Cloud** and review all the *Microsoft Defender* recommendations listed for your Azure SQL Server and implement them as appropriate.
81 |
82 | ## Vulnerability Assessment
83 |
84 | 1. From the main blade of your Azure SQL server, navigate to the **Settings** section, and select **SQL databases**, and then select the database named **AdventureWorksLT**.
85 |
86 | 1. Select the **Microsoft Defender for Cloud** setting under **Security**.
87 |
88 | 1. Select the **X** at the upper right to close the *Microsoft Defender for Cloud* Overview pane and view the **Microsoft Defender for Cloud** dashboard for your `AdventureWorksLT` database.
89 |
90 | 1. To begin reviewing the Vulnerability Assessment capabilities, under **Vulnerability assessment findings**, select **View additional findings in Vulnerability Assessment**.
91 |
92 | 1. Select **Scan** to get the most current Vulnerability Assessment results. This process takes a few moments while Vulnerability Assessment scans the database.
93 |
94 | 1. Every security risk has a risk level (high, medium, or low) and additional information. The rules in place are based on benchmarks that are provided by the [Center for Internet Security](https://www.cisecurity.org/benchmark/microsoft_sql_server/?azure-portal=true). In the **Findings** tab, select a vulnerability. Take note of the vulnerability's **ID**, for example **VA1143** (if listed).
95 |
96 | 1. Depending on the security check, there will be alternate views and recommendations. Review the information that's provided. For this security check, you can select the **Add all results as baseline** button and then select **Yes** to set the baseline. Now that a baseline is in place, this security check will fail in any future scans where the results are different from the baseline. Select the **X** at the top-right to close the pane for the specific rule.
97 |
98 | 1. Let's run **Scan** again confirm that the selected vulnerability is now showing up as a *Passed* security check.
99 |
100 | If you select the preceding passed security check, you should be able to see the baseline you configured. If anything changes in the future, Vulnerability Assessment scans will pick it up and the security check will fail.
101 |
102 | ## Advanced Threat Protection
103 |
104 | 1. Select the **X** at the upper right to close the Vulnerability Assessment pane and return to the **Microsoft Defender for Cloud** dashboard for your database. Under **Security incidents and alerts**, you shouldn't see any items. This means **Advanced Threat Protection** hasn't detected any issues. Advanced Threat Protection detects anomalous activities that indicate unusual and potentially harmful attempts to access or exploit databases.
105 |
106 | > 📝 You aren't expected to see any security alerts at this stage. In the next step, you'll run a test that will trigger an alert so that you can review the results in Advanced Threat Protection.
107 |
108 | You can use Advanced Threat Protection to identify threats and alert you when it suspects that any of the following events are occurring:
109 |
110 | - SQL injection
111 | - SQL injection vulnerability
112 | - Data exfiltration
113 | - Unsafe action
114 | - Brute force
115 | - Anomalous client login
116 |
117 | In this section, you'll learn how a SQL Injection alert can be triggered through SSMS. SQL Injection alerts are intended for custom-written applications, not for standard tools such as SSMS. Therefore, to trigger an alert through SSMS as a test for a SQL Injection, you need to "set" the **Application Name**, which is a connection property for clients that connect to SQL Server or Azure SQL.
118 |
119 | 1. From the lab virtual machine or your local machine if one wasn't provided, open SQL Server Management Studio (SSMS). On the Connect to Server dialog box, paste in the name of your Azure SQL Database server, and login with the following credentials:
120 |
121 | - **Server name:** <_paste your Azure SQL Database server name here_>
122 | - **Authentication:** SQL Server Authentication
123 | - **Server admin login:** Your Azure SQL Database server admin login
124 | - **Password:** Your Azure SQL Database server admin password
125 |
126 | 1. Select **Connect**.
127 |
128 | 1. In SSMS, select **File** > **New** > **Database Engine Query** to create a query by using a new connection.
129 |
130 | 1. In the main login window, log in to the **AdventureWorksLT** database as you usually would, with SQL authentication and your Azure SQL Server name and administrator credentials. Before you connect, select **Options >>** > **Connection Properties**. Type in **AdventureWorksLT** for the option **Connect to database**.
131 |
132 | 1. Select the **Additional Connection Parameters** tab, and then insert the following connection string in the text box:
133 |
134 | ```sql
135 | Application Name=webappname
136 | ```
137 |
138 | 1. Select **Connect**.
139 |
140 | 1. In the new query window, paste the following query, then select **Execute**:
141 |
142 | ```sql
143 | SELECT * FROM sys.databases WHERE database_id like '' or 1 = 1 --' and family = 'test1';
144 | ```
145 |
146 | 1. In the Azure portal, go to your **AdventureWorksLT** database. On the left pane, under **Security**, select **Microsoft Defender for Cloud**.
147 |
148 | 1. Under **Security incidents and alerts**, select **Check for alerts on this resources in Microsoft Defender for Cloud**.
149 |
150 | 1. You can now see the overall security alerts.
151 |
152 | 1. Select **Potential SQL injection** to display more specific alerts and receive investigation steps.
153 |
154 | 1. Select **View full details** to display the details of the alert.
155 |
156 | 1. Under the **Alert details** tab, note that the *Vulnerable statement* is shown. This is the SQL statement that was executed to
157 | trigger the alert. This was also the SQL statement that was executed in SSMS. Additionally, note that teh **Client application** is shown as **webappname**. This is the name that you specified in the connection string in SSMS.
158 |
159 | 1. As a clean-up step, consider closing all your query editors in SSMS and removing all connections so that you don't accidentally trigger additional alerts in the next exercises.
160 |
161 | ## Enable Data Classification
162 |
163 | 1. From the main blade of your Azure SQL server, navigate to the **Settings** section, and select **SQL databases**, and then select the database named **AdventureWorksLT**.
164 |
165 | 1. On the main blade for the **AdventureWorksLT** database, navigate to the **Security** section, and then select **Data Discovery & Classification**.
166 |
167 | 1. On the **Data Discovery & Classification** page, you will see an informational message that reads: **Currently using SQL Information Protection policy. We have found 15 columns with classification recommendations**. Select this link.
168 |
169 | 1. On the next **Data Discovery & Classification** screen select the check box next to **Select all**, select **Accepted selected recommendations**, and then select **Save** to save the classifications into the database.
170 |
171 | 1. Back to the **Data Discovery & Classification** screen, notice that fifteen columns were successfully classified across five different tables. Review the *Information type* and *Sensitivity label* for each of the columns.
172 |
173 | ## Configure data classification and masking
174 |
175 | 1. In the Azure portal, go to your **AdventureWorksLT** Azure SQL Database instance (not your logical server).
176 |
177 | 1. On the left pane, under **Security**, select **Data Discovery & Classification**.
178 |
179 | 1. In the SalesLT Customer table, *Data Discovery & Classification* identified `FirstName` and `LastName` to be classified, but not `MiddleName`. Use the drop-down lists to add it now. Select **Name** for the *Information type* and **Confidential - GDPR** for the *Sensitivity label* and then select **Add classification**.
180 |
181 | 1. Select **Save**.
182 |
183 | 1. Confirm that the classification was successfully added by viewing the **Overview** tab, and confirm that `MiddleName` is now displayed in the list of classified columns under the SalesLT schema.
184 |
185 | 1. On the left pane, select **Overview** to go back to the overview of your database.
186 |
187 | Dynamic Data Masking (DDM) is available in both Azure SQL and SQL Server. DDM limits data exposure by masking sensitive data to non-privileged users at the SQL Server level instead of at the application level where you have to code those types of rules. Azure SQL recommends items for you to mask, or you can add masks manually.
188 |
189 | In the next steps, you'll mask the `FirstName`, `MiddleName`, and `LastName` columns, which you reviewed in the previous step.
190 |
191 | 1. In the Azure portal, go to your Azure SQL Database. On the left pane, under **Security**, select **Dynamic Data Masking**, then select **Add mask**.
192 |
193 | 1. In the dropdown lists, select the **SalesLT** schema, **Customer** table, and **FirstName** column. You can review the options for masking, but the default option is good for this scenario. Select **Add** to add the masking rule.
194 |
195 | 1. Repeat the previous steps for both **MiddleName** and **LastName** in that table.
196 |
197 | You now have three masking rules.
198 |
199 | 1. Select **Save**.
200 |
201 | > 📝 Note if your Azure SQL Server name is not made up of only lower case letters, numbers and dashes, this step will fail and you wont be able to continue with the data masking sections.
202 |
203 | 1. On the left pane, select **Overview** to go back to the overview of your database.
204 |
205 | ## Retrieve data that is classified and masked
206 |
207 | Next, you'll simulate someone querying the classified columns and explore Dynamic Data Masking in action.
208 |
209 | 1. Go to SQL Server Management Studio (SSMS), connect to your Azure SQL server, and open a new query window.
210 |
211 | 1. Right-click the database **AdventureWorksLT** database and select **New Query**.
212 |
213 | 1. Run the following query to return the classified data and, in some cases, columns marked for masked data. Select **Execute** to run the query.
214 |
215 | ```sql
216 | SELECT TOP 10 FirstName, MiddleName, LastName
217 | FROM SalesLT.Customer;
218 | ```
219 |
220 | Your result should display the first 10 names, with no masking applied. Why? Because you're the admin for this Azure SQL Database logical server.
221 |
222 | 1. In the following query, you'll create a new user and run the preceding query as that user. You'll also use `EXECUTE AS` to impersonate `Bob`. When an `EXECUTE AS` statement is run, the session's execution context is switched to the login or user. This means that the permissions are checked against the login or user instead of the person executing the `EXECUTE AS` command (in this case, you). `REVERT` is then used to stop impersonating the login or user.
223 |
224 | You might recognize the first few parts of the commands that follow, because they're a repeat from a previous exercise. Create a new query with the following commands, then select **Execute** to run the query and observe the results.
225 |
226 | ```sql
227 | -- Create a new SQL user and give them a password
228 | CREATE USER Bob WITH PASSWORD = 'c0mpl3xPassword!';
229 |
230 | -- Until you run the following two lines, Bob has no access to read or write data
231 | ALTER ROLE db_datareader ADD MEMBER Bob;
232 | ALTER ROLE db_datawriter ADD MEMBER Bob;
233 |
234 | -- Execute as our new, low-privilege user, Bob
235 | EXECUTE AS USER = 'Bob';
236 | SELECT TOP 10 FirstName, MiddleName, LastName
237 | FROM SalesLT.Customer;
238 | REVERT;
239 | ```
240 |
241 | The result should now display the first 10 names, but with masking applied. Bob hasn't been granted access to the unmasked form of this data.
242 |
243 | What if, for some reason, Bob needs access to the names and gets permission to have it?
244 |
245 | You can update excluded users from masking in the Azure portal by going to the **Dynamic Data Masking** pane, under **Security**, but you can also do it by using T-SQL.
246 |
247 | 1. Right-click the **AdventureWorksLT** database and select **New Query**, then enter the following query to allow Bob to query the names results without masking. Select **Execute** to run the query.
248 |
249 | ```sql
250 | GRANT UNMASK TO Bob;
251 | EXECUTE AS USER = 'Bob';
252 | SELECT TOP 10 FirstName, MiddleName, LastName
253 | FROM SalesLT.Customer;
254 | REVERT;
255 | ```
256 |
257 | Your results should include the names in full.
258 |
259 | 1. You can also take away a user's unmasking privileges and confirm that action by running the following T-SQL commands in a new query:
260 |
261 | ```sql
262 | -- Remove unmasking privilege
263 | REVOKE UNMASK TO Bob;
264 |
265 | -- Execute as Bob
266 | EXECUTE AS USER = 'Bob';
267 | SELECT TOP 10 FirstName, MiddleName, LastName
268 | FROM SalesLT.Customer;
269 | REVERT;
270 | ```
271 |
272 | Your results should include the masked names.
273 |
274 | ---
275 |
276 | ## Cleanup Resources
277 |
278 | If you are not using the Azure SQL Server for any other purpose, you can clean up the resources you created in this lab.
279 |
280 | ### Delete the Resource Group
281 |
282 | If you created a new resource group for this lab, you can delete the resource group to remove all resources created in this lab.
283 |
284 | 1. In the Azure portal, select **Resource groups** from the left navigation pane or search for **Resource groups** in the search bar and select it from the results.
285 |
286 | 1. Go into the resource group that you created for this lab. The resource group will contain the Azure SQL Server and other resources created in this lab.
287 |
288 | 1. Select **Delete resource group** from the top menu.
289 |
290 | 1. In the **Delete resource group** dialog, type the name of the resource group to confirm and select **Delete**.
291 |
292 | 1. Wait for the resource group to be deleted.
293 |
294 | 1. Close the Azure portal.
295 |
296 | ### Delete the Lab resources only
297 |
298 | If you didn't create a new resource group for this lab, and want to leave the resource group and its previous resources intact, you can still delete the resources created in this lab.
299 |
300 | 1. In the Azure portal, select **Resource groups** from the left navigation pane or search for **Resource groups** in the search bar and select it from the results.
301 |
302 | 1. Go into the resource group that you created for this lab. The resource group will contain the Azure SQL Server and other resources created in this lab.
303 |
304 | 1. Select all the resources prefixed with the SQL Server name you previously specified in the lab.
305 |
306 | 1. Select **Delete** from the top menu.
307 |
308 | 1. In the **Delete resources** dialog, type **delete** and select **Delete**.
309 |
310 | 1. Select **Delete** again to confirm the deletion of the resources.
311 |
312 | 1. Wait for the resources to be deleted.
313 |
314 | 1. Close the Azure portal.
315 |
316 | ### Delete the LabFiles folder
317 |
318 | If you created a new LabFiles folder for this lab, and no longer need it, you can delete the LabFiles folder to remove all files created in this lab.
319 |
320 | 1. From the lab virtual machine or your local machine if one wasn't provided, open file explorer and navigate to the **C:\\** drive.
321 | 1. Right-click on the **LabFiles** folder and select **Delete**.
322 | 1. Select **Yes** to confirm the deletion of the folder.
323 |
324 | ---
325 |
326 | You have successfully completed this lab.
327 |
328 | In this exercise, you've enhanced the security of an Azure SQL Database by enabling Microsoft Defender for SQL. You've also created classified columns based on Azure portal recommendations.
329 |
--------------------------------------------------------------------------------
/Instructions/Labs/06-isolate-performance-problems.md:
--------------------------------------------------------------------------------
1 | ---
2 | lab:
3 | title: 'Lab 6 – Isolate performance problems through monitoring'
4 | module: 'Monitor and optimize operational resources in Azure SQL'
5 | ---
6 |
7 | # Isolate performance problems through monitoring
8 |
9 | **Estimated Time: 30 minutes**
10 |
11 | The students will take the information gained in the lessons to scope out the deliverables for a digital transformation project within AdventureWorksLT. Examining the Azure portal as well as other tools, students will determine how to utilize tools to identify and resolve performance related issues.
12 |
13 | You have been hired as a database administrator to identify performance related issues and provide viable solutions to resolve any issues found. You need to use the Azure portal to identify the performance issues and suggest methods to resolve them.
14 |
15 | > 📝 These exercises ask you to copy and paste T-SQL code and makes use of existing SQL resources. Please verify that the code has been copied correctly, before executing the code.
16 |
17 | ## Setup environment
18 |
19 | If your lab virtual machine has been provided and pre-configured, you should find the lab files ready in the **C:\LabFiles** folder. *Take a moment to check, if the files are already there, skip this section*. However, if you're using your own machine or the lab files are missing, you'll need to clone them from *GitHub* to proceed.
20 |
21 | 1. From the lab virtual machine or your local machine if one wasn't provided, start a Visual Studio Code session.
22 |
23 | 1. Open the command palette (Ctrl+Shift+P) and type **Git: Clone**. Select the **Git: Clone** option.
24 |
25 | 1. Paste the following URL into the **Repository URL** field and select **Enter**.
26 |
27 | ```url
28 | https://github.com/MicrosoftLearning/dp-300-database-administrator.git
29 | ```
30 |
31 | 1. Save the repository to the **C:\LabFiles** folder on the lab virtual machine or your local machine if one wasn't provided (create the folder if it does not exist).
32 |
33 | ## Setup your SQL Server in Azure
34 |
35 | Log in to Azure and check if you have an existing Azure SQL Server instance running in Azure. *Skip this section if you already have a SQL Server instance running in Azure*.
36 |
37 | 1. From the lab virtual machine or your local machine if one wasn't provided, start a Visual Studio Code session and navigate to the cloned repository from the previous section.
38 |
39 | 1. Right-click on the **/Allfiles/Labs** folder and select **Open in Integrated Terminal**.
40 |
41 | 1. Let's connect to Azure using the Azure CLI. Type the following command and select **Enter**.
42 |
43 | ```bash
44 | az login
45 | ```
46 |
47 | > 📝 Note that a browser window will open. Use your Azure credentials to log in.
48 |
49 | 1. Once you are logged in to Azure, it's time to create a resource group if it doesn't already exist, and create a SQL server and database under that resource group. Type the following command and select **Enter**. *The script will take a few minutes to complete*.
50 |
51 | ```bash
52 | cd ./Setup
53 | ./deploy-sql-database.ps1
54 | ```
55 |
56 | > 📝 Note that by default this script will create or a resource group called **contoso-rg**, or use a resource whose name start with *contoso-rg* if it exists. By default it will also create all resources on the **West US 2** region (westus2). Finally it will generate a random 12 character password for the **SQL admin password**. You can change these values by using one or more of the parameters **-rgName**, **-location** and **-sqlAdminPw** with your own values. The password will have to meet the Azure SQL password complexity requirements, at least 12 characters long, and contain at least 1 uppercase letter, 1 lowercase letter, 1 number and 1 special character.
57 |
58 | > 📝 Note that the script will add your current Public IP address to the SQL server firewall rules.
59 |
60 | 1. Once the script has completed, it will return the resource group name, SQL server name and database name, and admin user name and password. *Take note of these values as you will need them later in the lab*.
61 |
62 | ---
63 |
64 | ## Review CPU utilization in Azure portal
65 |
66 | 1. From the lab virtual machine or your local machine if one wasn't provided, start a browser session and navigate to [https://portal.azure.com](https://portal.azure.com/). Connect to the Portal using your Azure credentials.
67 |
68 | 1. From the Azure Portal, search for *SQL servers* in the search box at the top, then select **SQL servers** from the list of options.
69 |
70 | 1. Select the SQL server **dp300-lab-xxxxxxxx**, where *xxxxxxxx* is a random numeric string.
71 |
72 | > 📝 Note, if you are using your own Azure SQL server not created by this lab, select the name of that SQL server.
73 |
74 | 1. On the Azure SQL server main page, under **Security**, select **Networking**.
75 |
76 | 1. On the **Networking** page, verify if your current public IP is already added to the **Firewall rules** list, if not, select **+ Add your client IPv4 address (your IP address)** to add it and then select **Save**.
77 |
78 | 1. From the main blade of your Azure SQL server, navigate to the **Settings** section, and select **SQL databases**, and then select the **AdventureWorksLT** database.
79 |
80 | 1. In the left navigation, select **Query editor (preview)**.
81 |
82 | **Note:** This feature is in preview.
83 |
84 | 1. Select the SQL Server admin user name and enter the password or your Microsoft Entra credentials if assigned to connect to the database.
85 |
86 | 1. In **Query 1**, type the following query, and select **Run**:
87 |
88 | ```sql
89 | DECLARE @Counter INT
90 | SET @Counter=1
91 | WHILE ( @Counter <= 10000)
92 | BEGIN
93 | SELECT
94 | RTRIM(a.Firstname) + ' ' + RTRIM(a.LastName)
95 | , b.AddressLine1
96 | , b.AddressLine2
97 | , RTRIM(b.City) + ', ' + RTRIM(b.StateProvince) + ' ' + RTRIM(b.PostalCode)
98 | , CountryRegion
99 | FROM SalesLT.Customer a
100 | INNER JOIN SalesLT.CustomerAddress c
101 | ON a.CustomerID = c.CustomerID
102 | RIGHT OUTER JOIN SalesLT.Address b
103 | ON b.AddressID = c.AddressID
104 | ORDER BY a.LastName ASC
105 | SET @Counter = @Counter + 1
106 | END
107 | ```
108 |
109 | 1. Wait for the query to complete.
110 |
111 | 1. Re-run the query *two* more times to generate some CPU load on the database.
112 |
113 | 1. On the blade for the **AdventureWorksLT** database, select the **Metrics** icon on the **Monitoring** section.
114 |
115 | If the message *Your unsaved changes will be discarded* pops-up, select **OK**.
116 |
117 | 1. Change the **Metric** menu option to reflect **CPU Percentage**, then select an **Aggregation** of **Avg**. This will display the average CPU Percentage for the given time frame.
118 |
119 | 1. Observe the the CPU average across time. You should note a spike in CPU utilization at end of the graph when the query was running.
120 |
121 | ## Identify high CPU queries
122 |
123 | 1. Locate the **Query Performance Insight** icon on the **Intelligent Performance** section of the blade for the **AdventureWorksLT** database.
124 |
125 | 1. Select **Reset settings**.
126 |
127 | 1. Select the query in the grid below the graph. If you don't see the query we previously ran several times, wait for up 2 to 5 minutes and select **Refresh**.
128 |
129 | > 📝 If there is more than one query listed, select each one to observe the results. Note the rich amount of information available for each query.
130 |
131 | 1. For the query you ran earlier, note that the total duration was over a minute and that it ran around thirty thousands times.
132 |
133 | 1. Reviewing the SQL text on the **Query details** page against the query you ran, you will note that the **Query details** only includes the **SELECT** statement and not the **WHILE** loop or other statement. This happens because **Query Performance Insight** relies on data from the **Query Store**, which only tracks Data Manipulation Language (DML) statements such as **SELECT, INSERT, UPDATE, DELETE, MERGE,** and **BULK INSERT** while ignoring Data Definition Language (DDL) statements.
134 |
135 | Not all performance issues are related to a high CPU utilization by one single query execution. In this case, the query was executed thousands of times, which can also result in high CPU utilization.
136 |
137 | ---
138 |
139 | ## Cleanup Resources
140 |
141 | If you are not using the Azure SQL Server for any other purpose, you can clean up the resources you created in this lab.
142 |
143 | ### Delete the Resource Group
144 |
145 | If you created a new resource group for this lab, you can delete the resource group to remove all resources created in this lab.
146 |
147 | 1. In the Azure portal, select **Resource groups** from the left navigation pane or search for **Resource groups** in the search bar and select it from the results.
148 |
149 | 1. Go into the resource group that you created for this lab. The resource group will contain the Azure SQL Server and other resources created in this lab.
150 |
151 | 1. Select **Delete resource group** from the top menu.
152 |
153 | 1. In the **Delete resource group** dialog, type the name of the resource group to confirm and select **Delete**.
154 |
155 | 1. Wait for the resource group to be deleted.
156 |
157 | 1. Close the Azure portal.
158 |
159 | ### Delete the Lab resources only
160 |
161 | If you didn't create a new resource group for this lab, and want to leave the resource group and its previous resources intact, you can still delete the resources created in this lab.
162 |
163 | 1. In the Azure portal, select **Resource groups** from the left navigation pane or search for **Resource groups** in the search bar and select it from the results.
164 |
165 | 1. Go into the resource group that you created for this lab. The resource group will contain the Azure SQL Server and other resources created in this lab.
166 |
167 | 1. Select all the resources prefixed with the SQL Server name you previously specified in the lab.
168 |
169 | 1. Select **Delete** from the top menu.
170 |
171 | 1. In the **Delete resources** dialog, type **delete** and select **Delete**.
172 |
173 | 1. Select **Delete** again to confirm the deletion of the resources.
174 |
175 | 1. Wait for the resources to be deleted.
176 |
177 | 1. Close the Azure portal.
178 |
179 | ### Delete the LabFiles folder
180 |
181 | If you created a new LabFiles folder for this lab, and no longer need it, you can delete the LabFiles folder to remove all files created in this lab.
182 |
183 | 1. From the lab virtual machine or your local machine if one wasn't provided, open file explorer and navigate to the **C:\\** drive.
184 | 1. Right-click on the **LabFiles** folder and select **Delete**.
185 | 1. Select **Yes** to confirm the deletion of the folder.
186 |
187 | ---
188 |
189 | You have successfully completed this lab.
190 |
191 | In this exercise, you've learned how to explore server resources for an Azure SQL Database and identify potential query performance issues through Query Performance Insight.
192 |
--------------------------------------------------------------------------------
/Instructions/Labs/07-detect-correct-fragmentation-issues.md:
--------------------------------------------------------------------------------
1 | ---
2 | lab:
3 | title: 'Lab 7 – Detect and correct fragmentation issues'
4 | module: 'Monitor and optimize operational resources in Azure SQL'
5 | ---
6 |
7 | # Detect and correct fragmentation issues
8 |
9 | **Estimated Time: 20 minutes**
10 |
11 | The students will take the information gained in the lessons to scope out the deliverables for a digital transformation project within AdventureWorks. Examining the Azure portal as well as other tools, students will determine how to utilize native tools to identify and resolve performance related issues. Finally, students will be able to identify fragmentation within the database as well as learn steps to resolve it appropriately.
12 |
13 | You have been hired as a database administrator to identify performance related issues and provide viable solutions to resolve any issues found. AdventureWorks has been selling bicycles and bicycle parts directly to consumers and distributors for over a decade. Recently the company has noticed performance degradation in their products that are used to service customer requests. You need to use SQL tools to identify the performance issues and suggest methods to resolve them.
14 |
15 | > 📝 These exercises ask you to copy and paste T-SQL code. Please verify that the code has been copied correctly, before executing the code.
16 |
17 | ## Setup environment
18 |
19 | If your lab virtual machine has been provided and pre-configured, you should find the lab files ready in the **C:\LabFiles** folder. *Take a moment to check, if the files are already there, skip this section*. However, if you're using your own machine or the lab files are missing, you'll need to clone them from *GitHub* to proceed.
20 |
21 | 1. From the lab virtual machine or your local machine if one wasn't provided, start a Visual Studio Code session.
22 |
23 | 1. Open the command palette (Ctrl+Shift+P) and type **Git: Clone**. Select the **Git: Clone** option.
24 |
25 | 1. Paste the following URL into the **Repository URL** field and select **Enter**.
26 |
27 | ```url
28 | https://github.com/MicrosoftLearning/dp-300-database-administrator.git
29 | ```
30 |
31 | 1. Save the repository to the **C:\LabFiles** folder on the lab virtual machine or your local machine if one wasn't provided (create the folder if it does not exist).
32 |
33 | ---
34 |
35 | ## Restore a database
36 |
37 | If you already have the **AdventureWorks2017** database restored, you can skip this section.
38 |
39 | 1. From the lab virtual machine or your local machine if one wasn't provided, start a SQL Server Management Studio session (SSMS).
40 |
41 | 1. When SSMS opens, by default the **Connect to Server** dialog will appear. Choose the Default instance and select **Connect**. You might need to check to the **Trust server certificate** checkbox.
42 |
43 | > 📝 Note that if you are using your own SQL Server instance, you will need to connect to it using the appropriate server instance name and credentials.
44 |
45 | 1. Select the **Databases** folder, and then **New Query**.
46 |
47 | 1. In the new query window, copy and paste the below T-SQL into it. Execute the query to restore the database.
48 |
49 | ```sql
50 | RESTORE DATABASE AdventureWorks2017
51 | FROM DISK = 'C:\LabFiles\dp-300-database-administrator\Allfiles\Labs\Shared\AdventureWorks2017.bak'
52 | WITH RECOVERY,
53 | MOVE 'AdventureWorks2017'
54 | TO 'C:\LabFiles\AdventureWorks2017.mdf',
55 | MOVE 'AdventureWorks2017_log'
56 | TO 'C:\LabFiles\AdventureWorks2017_log.ldf';
57 | ```
58 |
59 | > 📝 You must have a folder named **C:\LabFiles**. If you don't have this folder, create it or specify another location for the database and backup files.
60 |
61 | 1. Under the **Messages** tab, you should see a message indicating that the database was restored successfully.
62 |
63 | ## Investigate index fragmentation
64 |
65 | 1. Select **New Query**. Copy and paste the following T-SQL code into the query window. Select **Execute** to execute this query.
66 |
67 | ```sql
68 | USE AdventureWorks2017
69 |
70 | GO
71 |
72 | SELECT i.name Index_Name
73 | , avg_fragmentation_in_percent
74 | , db_name(database_id)
75 | , i.object_id
76 | , i.index_id
77 | , index_type_desc
78 | FROM sys.dm_db_index_physical_stats(db_id('AdventureWorks2017'),object_id('person.address'),NULL,NULL,'DETAILED') ps
79 | INNER JOIN sys.indexes i ON ps.object_id = i.object_id
80 | AND ps.index_id = i.index_id
81 | WHERE avg_fragmentation_in_percent > 50 -- find indexes where fragmentation is greater than 50%
82 | ```
83 |
84 | This query will report any indexes that have a fragmentation over **50%**. The query should not return any result.
85 |
86 | 1. Index fragmentation can be caused by a number of factors, including the following:
87 |
88 | - Frequent updates to the table or index.
89 | - Frequent inserts or deletes to the table or index.
90 | - Page splits.
91 |
92 | To increase the fragmentation level of the Person.Address table and its indexes, you will insert and delete a large number of records. To do this, you will run the following query.
93 |
94 | Select **New Query**. Copy and paste the following T-SQL code into the query window. Select **Execute** to execute this query.
95 |
96 | ```sql
97 | USE AdventureWorks2017
98 |
99 | GO
100 |
101 | -- Insert 60000 records into the Address table
102 |
103 | INSERT INTO [Person].[Address]
104 | ([AddressLine1], [AddressLine2], [City], [StateProvinceID], [PostalCode], [SpatialLocation], [rowguid], [ModifiedDate])
105 | SELECT
106 | 'Split Avenue ' + CAST(v1.number AS VARCHAR(10)),
107 | 'Apt ' + CAST(v2.number AS VARCHAR(10)),
108 | 'PageSplitTown',
109 | 100 + (v1.number % 60), -- 60 different StateProvinceIDs (100-159)
110 | '88' + RIGHT('000' + CAST(v2.number AS VARCHAR(3)), 3), -- Structured postal codes
111 | NULL,
112 | NEWID(), -- Ensure unique rowguid
113 | GETDATE()
114 | FROM master.dbo.spt_values v1
115 | CROSS JOIN master.dbo.spt_values v2
116 | WHERE v1.type = 'P' AND v1.number BETWEEN 1 AND 300
117 | AND v2.type = 'P' AND v2.number BETWEEN 1 AND 200;
118 | GO
119 |
120 | -- DELETE 25000 records from the Address table
121 | DELETE FROM [Person].[Address] WHERE AddressID BETWEEN 35001 AND 60000;
122 |
123 | GO
124 |
125 | -- Insert 40000 records into the Address table
126 | INSERT INTO [Person].[Address]
127 | ([AddressLine1], [AddressLine2], [City], [StateProvinceID], [PostalCode], [SpatialLocation], [rowguid], [ModifiedDate])
128 | SELECT
129 | 'Fragmented Street ' + CAST(v1.number AS VARCHAR(10)),
130 | 'Suite ' + CAST(v2.number AS VARCHAR(10)),
131 | 'FragmentCity',
132 | 100 + (v1.number % 60), -- 60 different StateProvinceIDs (100-159)
133 | '99' + RIGHT('000' + CAST(v2.number AS VARCHAR(3)), 3), -- Structured postal codes
134 | NULL,
135 | NEWID(), -- Ensure a unique rowguid per row
136 | GETDATE()
137 | FROM master.dbo.spt_values v1
138 | CROSS JOIN master.dbo.spt_values v2
139 | WHERE v1.type = 'P' AND v1.number BETWEEN 1 AND 200
140 | AND v2.type = 'P' AND v2.number BETWEEN 1 AND 200;
141 |
142 | GO
143 | ```
144 |
145 | This query will increase the fragmentation level of the Person.Address table and its indexes by adding and deleting a large number of records.
146 |
147 | 1. Execute the first query again. Now you should be able to see four highly fragmented indexes.
148 |
149 | 1. Select a **New Query** and copy and paste the following T-SQL code into the query window. Select **Execute** to execute this query.
150 |
151 | ```sql
152 | SET STATISTICS IO,TIME ON
153 |
154 | GO
155 |
156 | USE AdventureWorks2017
157 |
158 | GO
159 |
160 | SELECT DISTINCT (StateProvinceID)
161 | ,count(StateProvinceID) AS CustomerCount
162 | FROM person.Address
163 | GROUP BY StateProvinceID
164 | ORDER BY count(StateProvinceID) DESC;
165 |
166 | GO
167 | ```
168 |
169 | Select the **Messages** tab in the result pane of SQL Server Management Studio. Make note of the count of logical reads performed by the query on the **Address** table.
170 |
171 | ## Rebuild fragmented indexes
172 |
173 | 1. Select a **New Query** and copy and paste the following T-SQL code into the query window. Select **Execute** to execute this query.
174 |
175 | ```sql
176 | USE AdventureWorks2017
177 |
178 | GO
179 |
180 | ALTER INDEX [IX_Address_StateProvinceID] ON [Person].[Address] REBUILD PARTITION = ALL
181 | WITH (PAD_INDEX = OFF,
182 | STATISTICS_NORECOMPUTE = OFF,
183 | SORT_IN_TEMPDB = OFF,
184 | IGNORE_DUP_KEY = OFF,
185 | ONLINE = OFF,
186 | ALLOW_ROW_LOCKS = ON,
187 | ALLOW_PAGE_LOCKS = ON)
188 | ```
189 |
190 | 1. Select a **New Query** and Execute the following query to confirm that the **IX_Address_StateProvinceID** index no longer has fragmentation greater than 50%.
191 |
192 | ```sql
193 | USE AdventureWorks2017
194 |
195 | GO
196 |
197 | SELECT DISTINCT i.name Index_Name
198 | , avg_fragmentation_in_percent
199 | , db_name(database_id)
200 | , i.object_id
201 | , i.index_id
202 | , index_type_desc
203 | FROM sys.dm_db_index_physical_stats(db_id('AdventureWorks2017'),object_id('person.address'),NULL,NULL,'DETAILED') ps
204 | INNER JOIN sys.indexes i ON (ps.object_id = i.object_id AND ps.index_id = i.index_id)
205 | WHERE i.name = 'IX_Address_StateProvinceID'
206 | ```
207 |
208 | Comparing the results we can see the **IX_Address_StateProvinceI** fragmentation dropped from 88% to 0.
209 |
210 | 1. Re-execute the select statement from the previous section. Make note of the logical reads in the **Messages** tab of the **Results** pane in Management Studio. *Was there a change from the number of logical reads encountered before you rebuilt the index for the address table*?
211 |
212 | ```sql
213 | SET STATISTICS IO,TIME ON
214 |
215 | GO
216 |
217 | USE AdventureWorks2017
218 |
219 | GO
220 |
221 | SELECT DISTINCT (StateProvinceID)
222 | ,count(StateProvinceID) AS CustomerCount
223 | FROM person.Address
224 | GROUP BY StateProvinceID
225 | ORDER BY count(StateProvinceID) DESC;
226 |
227 | GO
228 | ```
229 |
230 | Because the index has been rebuilt, it will now be as efficient as possible and the logical reads should reduce. You have now seen that index maintenance can have an effect on query performance.
231 |
232 | ---
233 |
234 | ## Cleanup
235 |
236 | If you are not using the Database or the lab files for any other purpose, you can clean up the objects you created in this lab.
237 |
238 | ### Delete the C:\LabFiles folder
239 |
240 | 1. From the lab virtual machine or your local machine if one wasn't provided, open **File Explorer**.
241 | 1. Navigate to **C:\\** .
242 | 1. Delete the **C:\LabFiles** folder.
243 |
244 | ### Delete the AdventureWorks2017 database
245 |
246 | 1. From the lab virtual machine or your local machine if one wasn't provided, start a SQL Server Management Studio session (SSMS).
247 | 1. When SSMS opens, by default the **Connect to Server** dialog will appear. Choose the Default instance and select **Connect**. You might need to check to the **Trust server certificate** checkbox.
248 | 1. In **Object Explorer**, expand the **Databases** folder.
249 | 1. Right-click on the **AdventureWorks2017** database and select **Delete**.
250 | 1. In the **Delete Object** dialog, check the **Close existing connections** checkbox.
251 | 1. Select **OK**.
252 |
253 | ---
254 |
255 | You have successfully completed this lab.
256 |
257 | In this exercise, you've learned how to rebuild index and analyze logical reads to increase query performance.
258 |
--------------------------------------------------------------------------------
/Instructions/Labs/08-identify-resolve-blocking-issues.md:
--------------------------------------------------------------------------------
1 | ---
2 | lab:
3 | title: 'Lab 8 – Identify and resolve blocking issues'
4 | module: 'Optimize query performance in Azure SQL'
5 | ---
6 |
7 | # Identify and resolve blocking issues
8 |
9 | **Estimated Time: 15 minutes**
10 |
11 | The students will take the information gained in the lessons to scope out the deliverables for a digital transformation project within AdventureWorks. Examining the Azure portal as well as other tools, students will determine how to utilize native tools to identify and resolve performance related issues. Finally, students will be able to identify and resolve blocking issues appropriately.
12 |
13 | You have been hired as a database administrator to identify performance related issues and provide viable solutions to resolve any issues found. You need to investigate the performance problems and suggest methods to resolve them.
14 |
15 | > 📝 These exercises ask you to copy and paste T-SQL code. Please verify that the code has been copied correctly, before executing the code.
16 |
17 | ## Setup environment
18 |
19 | If your lab virtual machine has been provided and pre-configured, you should find the lab files ready in the **C:\LabFiles** folder. *Take a moment to check, if the files are already there, skip this section*. However, if you're using your own machine or the lab files are missing, you'll need to clone them from *GitHub* to proceed.
20 |
21 | 1. From the lab virtual machine or your local machine if one wasn't provided, start a Visual Studio Code session.
22 |
23 | 1. Open the command palette (Ctrl+Shift+P) and type **Git: Clone**. Select the **Git: Clone** option.
24 |
25 | 1. Paste the following URL into the **Repository URL** field and select **Enter**.
26 |
27 | ```url
28 | https://github.com/MicrosoftLearning/dp-300-database-administrator.git
29 | ```
30 |
31 | 1. Save the repository to the **C:\LabFiles** folder on the lab virtual machine or your local machine if one wasn't provided (create the folder if it does not exist).
32 |
33 | ---
34 |
35 | ## Restore a database
36 |
37 | If you already have the **AdventureWorks2017** database restored, you can skip this section.
38 |
39 | 1. From the lab virtual machine or your local machine if one wasn't provided, start a SQL Server Management Studio session (SSMS).
40 |
41 | 1. When SSMS opens, by default the **Connect to Server** dialog will appear. Choose the Default instance and select **Connect**. You might need to check to the **Trust server certificate** checkbox.
42 |
43 | > 📝 Note that if you are using your own SQL Server instance, you will need to connect to it using the appropriate server instance name and credentials.
44 |
45 | 1. Select the **Databases** folder, and then **New Query**.
46 |
47 | 1. In the new query window, copy and paste the below T-SQL into it. Execute the query to restore the database.
48 |
49 | ```sql
50 | RESTORE DATABASE AdventureWorks2017
51 | FROM DISK = 'C:\LabFiles\dp-300-database-administrator\Allfiles\Labs\Shared\AdventureWorks2017.bak'
52 | WITH RECOVERY,
53 | MOVE 'AdventureWorks2017'
54 | TO 'C:\LabFiles\AdventureWorks2017.mdf',
55 | MOVE 'AdventureWorks2017_log'
56 | TO 'C:\LabFiles\AdventureWorks2017_log.ldf';
57 | ```
58 |
59 | > 📝 You must have a folder named **C:\LabFiles**. If you don't have this folder, create it or specify another location for the database and backup files.
60 |
61 | 1. Under the **Messages** tab, you should see a message indicating that the database was restored successfully.
62 |
63 | ## Run blocked queries report
64 |
65 | 1. Select **New Query**. Copy and paste the following T-SQL code into the query window. Select **Execute** to execute this query.
66 |
67 | ```sql
68 | USE MASTER
69 |
70 | GO
71 |
72 | CREATE EVENT SESSION [Blocking] ON SERVER
73 | ADD EVENT sqlserver.blocked_process_report(
74 | ACTION(sqlserver.client_app_name,sqlserver.client_hostname,sqlserver.database_id,sqlserver.database_name,sqlserver.nt_username,sqlserver.session_id,sqlserver.sql_text,sqlserver.username))
75 | ADD TARGET package0.ring_buffer
76 | WITH (MAX_MEMORY=4096 KB, EVENT_RETENTION_MODE=ALLOW_SINGLE_EVENT_LOSS, MAX_DISPATCH_LATENCY=30 SECONDS, MAX_EVENT_SIZE=0 KB,MEMORY_PARTITION_MODE=NONE, TRACK_CAUSALITY=OFF,STARTUP_STATE=ON)
77 |
78 | GO
79 |
80 | -- Start the event session
81 | ALTER EVENT SESSION [Blocking] ON SERVER
82 | STATE = start;
83 |
84 | GO
85 | ```
86 |
87 | The above T-SQL code will create an Extended Event session that will capture blocking events. The data will contain the following elements:
88 |
89 | - Client application name
90 | - Client host name
91 | - Database ID
92 | - Database name
93 | - NT Username
94 | - Session ID
95 | - T-SQL Text
96 | - Username
97 |
98 | 1. Select **New Query**. Copy and paste the following T-SQL code into the query window. Select **Execute** to execute this query.
99 |
100 | ```sql
101 | EXEC sys.sp_configure N'show advanced options', 1
102 |
103 | RECONFIGURE WITH OVERRIDE;
104 |
105 | GO
106 | EXEC sp_configure 'blocked process threshold (s)', 60
107 |
108 | RECONFIGURE WITH OVERRIDE;
109 |
110 | GO
111 | ```
112 |
113 | > 📝 Note, the command above specify the threshold, in seconds, at which blocked process reports are generated. As a result, we are not required to wait as long for the *blocked_process_report* to be raised in this lesson.
114 |
115 | 1. Select **New Query**. Copy and paste the following T-SQL code into the query window. Select **Execute** to execute this query.
116 |
117 | ```sql
118 | USE AdventureWorks2017
119 |
120 | GO
121 |
122 | BEGIN TRANSACTION
123 | UPDATE Person.Person
124 | SET LastName = LastName;
125 |
126 | GO
127 | ```
128 |
129 | 1. Open another query window by selecting the **New Query** button. Copy and paste the following T-SQL code into the new query window. Select **Execute** to execute this query.
130 |
131 | ```sql
132 | USE AdventureWorks2017
133 |
134 | GO
135 |
136 | SELECT TOP (1000) [LastName]
137 | ,[FirstName]
138 | ,[Title]
139 | FROM Person.Person
140 | WHERE FirstName = 'David'
141 | ```
142 |
143 | > 📝 Note that this query does not return any results and appears to run indefinitely.
144 |
145 | 1. In **Object Explorer**, expand **Management** -> **Extended Events** -> **Sessions**.
146 |
147 | Notice the extended event named *Blocking* we just created is in the list.
148 |
149 | 1. Expand the *Blocking* extended event and right click on **package0.ring_buffer**. Select **View Target Data**.
150 |
151 | 1. Select the hyperlink listed.
152 |
153 | 1. The XML will show you which processes are being blocked, and which process is causing the blocking. You can see the queries that ran in this process as well as system information. Take note that the session IDs (SPID).
154 |
155 | 1. Alternatively, you can run the following query to identify sessions blocking other sessions, including a list of session IDs blocked per *session_id*. Open a **New Query** window, copy and paste the following T-SQL code into it, and select **Execute**.
156 |
157 | ```sql
158 | WITH cteBL (session_id, blocking_these) AS
159 | (SELECT s.session_id, blocking_these = x.blocking_these FROM sys.dm_exec_sessions s
160 | CROSS APPLY (SELECT isnull(convert(varchar(6), er.session_id),'') + ', '
161 | FROM sys.dm_exec_requests as er
162 | WHERE er.blocking_session_id = isnull(s.session_id ,0)
163 | AND er.blocking_session_id <> 0
164 | FOR XML PATH('') ) AS x (blocking_these)
165 | )
166 | SELECT s.session_id, blocked_by = r.blocking_session_id, bl.blocking_these
167 | , batch_text = t.text, input_buffer = ib.event_info, *
168 | FROM sys.dm_exec_sessions s
169 | LEFT OUTER JOIN sys.dm_exec_requests r on r.session_id = s.session_id
170 | INNER JOIN cteBL as bl on s.session_id = bl.session_id
171 | OUTER APPLY sys.dm_exec_sql_text (r.sql_handle) t
172 | OUTER APPLY sys.dm_exec_input_buffer(s.session_id, NULL) AS ib
173 | WHERE blocking_these is not null or r.blocking_session_id > 0
174 | ORDER BY len(bl.blocking_these) desc, r.blocking_session_id desc, r.session_id;
175 | ```
176 |
177 | > 📝 Note that the above query will return the same SPIDs as the XML.
178 |
179 | 1. Right click on extended event named **Blocking**, and then select **Stop Session**.
180 |
181 | 1. Navigate back to the query session that is causing the blocking, and type `ROLLBACK TRANSACTION` on the line below the query. Highlight `ROLLBACK TRANSACTION`, and select **Execute**.
182 |
183 | 1. Navigate back to the query session that was being blocked. You will notice that the query has now completed.
184 |
185 | ## Enable Read Commit Snapshot isolation level
186 |
187 | 1. Select **New Query** from SQL Server Management Studio. Copy and paste the following T-SQL code into the query window. Select the **Execute** button to execute this query.
188 |
189 | ```sql
190 | USE master
191 |
192 | GO
193 |
194 | ALTER DATABASE AdventureWorks2017 SET READ_COMMITTED_SNAPSHOT ON WITH ROLLBACK IMMEDIATE;
195 |
196 | GO
197 | ```
198 |
199 | 1. Rerun the query that caused the blocking in a new query editor. *Don't run the ROLLBACK TRANSACTION command*.
200 |
201 | ```sql
202 | USE AdventureWorks2017
203 | GO
204 |
205 | BEGIN TRANSACTION
206 | UPDATE Person.Person
207 | SET LastName = LastName;
208 | GO
209 | ```
210 |
211 | 1. Rerun the query that was being blocked in a new query editor.
212 |
213 | ```sql
214 | USE AdventureWorks2017
215 | GO
216 |
217 | SELECT TOP (1000) [LastName]
218 | ,[FirstName]
219 | ,[Title]
220 | FROM Person.Person
221 | WHERE firstname = 'David'
222 | ```
223 |
224 | Why the same query completes whereas in the previous task it was blocked by the update statement?
225 |
226 | Read Commit Snapshot isolation level is an optimistic form of transaction isolation, and the last query will show the latest committed version of the data, rather than being blocked.
227 |
228 | ---
229 |
230 | ## Cleanup
231 |
232 | If you are not using the Database or the lab files for any other purpose, you can clean up the objects you created in this lab.
233 |
234 | ### Delete the C:\LabFiles folder
235 |
236 | 1. From the lab virtual machine or your local machine if one wasn't provided, open **File Explorer**.
237 | 1. Navigate to **C:\\** .
238 | 1. Delete the **C:\LabFiles** folder.
239 |
240 | ### Delete the AdventureWorks2017 database
241 |
242 | 1. From the lab virtual machine or your local machine if one wasn't provided, start a SQL Server Management Studio session (SSMS).
243 | 1. When SSMS opens, by default the **Connect to Server** dialog will appear. Choose the Default instance and select **Connect**. You might need to check to the **Trust server certificate** checkbox.
244 | 1. In **Object Explorer**, expand the **Databases** folder.
245 | 1. Right-click on the **AdventureWorks2017** database and select **Delete**.
246 | 1. In the **Delete Object** dialog, check the **Close existing connections** checkbox.
247 | 1. Select **OK**.
248 |
249 | ---
250 |
251 | You have successfully completed this lab.
252 |
253 | In this exercise, you've learned how to identify sessions being blocked, and to mitigate those scenarios.
254 |
--------------------------------------------------------------------------------
/Instructions/Labs/09-identify-issues-database-design.md:
--------------------------------------------------------------------------------
1 | ---
2 | lab:
3 | title: 'Lab 9 – Identify database design issues'
4 | module: 'Optimize query performance in Azure SQL'
5 | ---
6 |
7 | # Identify database design issues
8 |
9 | **Estimated Time: 15 minutes**
10 |
11 | The students will take the information gained in the lessons to scope out the deliverables for a digital transformation project within AdventureWorks. Examining the Azure portal as well as other tools, students will determine how to utilize native tools to identify and resolve performance related issues. Finally, students will be able to evaluate a database design for problems with normalization, data type selection and index design.
12 |
13 | You have been hired as a database administrator to identify performance related issues and provide viable solutions to resolve any issues found. AdventureWorks has been selling bicycles and bicycle parts directly to consumers and distributors for over a decade. Your job is to identify issues in query performance and remedy them using techniques learned in this module.
14 |
15 | > 📝 These exercises ask you to copy and paste T-SQL code. Please verify that the code has been copied correctly, before executing the code.
16 |
17 | ## Setup environment
18 |
19 | If your lab virtual machine has been provided and pre-configured, you should find the lab files ready in the **C:\LabFiles** folder. *Take a moment to check, if the files are already there, skip this section*. However, if you're using your own machine or the lab files are missing, you'll need to clone them from *GitHub* to proceed.
20 |
21 | 1. From the lab virtual machine or your local machine if one wasn't provided, start a Visual Studio Code session.
22 |
23 | 1. Open the command palette (Ctrl+Shift+P) and type **Git: Clone**. Select the **Git: Clone** option.
24 |
25 | 1. Paste the following URL into the **Repository URL** field and select **Enter**.
26 |
27 | ```url
28 | https://github.com/MicrosoftLearning/dp-300-database-administrator.git
29 | ```
30 |
31 | 1. Save the repository to the **C:\LabFiles** folder on the lab virtual machine or your local machine if one wasn't provided (create the folder if it does not exist).
32 |
33 | ---
34 |
35 | ## Restore a database
36 |
37 | If you already have the **AdventureWorks2017** database restored, you can skip this section.
38 |
39 | 1. From the lab virtual machine or your local machine if one wasn't provided, start a SQL Server Management Studio session (SSMS).
40 |
41 | 1. When SSMS opens, by default the **Connect to Server** dialog will appear. Choose the Default instance and select **Connect**. You might need to check to the **Trust server certificate** checkbox.
42 |
43 | > 📝 Note that if you are using your own SQL Server instance, you will need to connect to it using the appropriate server instance name and credentials.
44 |
45 | 1. Select the **Databases** folder, and then **New Query**.
46 |
47 | 1. In the new query window, copy and paste the below T-SQL into it. Execute the query to restore the database.
48 |
49 | ```sql
50 | RESTORE DATABASE AdventureWorks2017
51 | FROM DISK = 'C:\LabFiles\dp-300-database-administrator\Allfiles\Labs\Shared\AdventureWorks2017.bak'
52 | WITH RECOVERY,
53 | MOVE 'AdventureWorks2017'
54 | TO 'C:\LabFiles\AdventureWorks2017.mdf',
55 | MOVE 'AdventureWorks2017_log'
56 | TO 'C:\LabFiles\AdventureWorks2017_log.ldf';
57 | ```
58 |
59 | > 📝 You must have a folder named **C:\LabFiles**. If you don't have this folder, create it or specify another location for the database and backup files.
60 |
61 | 1. Under the **Messages** tab, you should see a message indicating that the database was restored successfully.
62 |
63 | ## Examine the query and identify the problem
64 |
65 | 1. Select **New Query**. Copy and paste the following T-SQL code into the query window. Select **Execute** to execute this query.
66 |
67 | ```sql
68 | USE AdventureWorks2017
69 |
70 | GO
71 |
72 | SELECT BusinessEntityID, NationalIDNumber, LoginID, HireDate, JobTitle
73 | FROM HumanResources.Employee
74 | WHERE NationalIDNumber = 14417807;
75 | ```
76 |
77 | 1. Select **Include Actual Execution Plan** icon to the right of the **Execute** button before running the query or press **CTRL+M**. This will cause the execution plan to be displayed when you execute the query. Select **Execute** to execute this query.
78 |
79 | 1. Navigate to the execution plan, by selecting the **Execution plan** tab in the results panel. You will notice that the **SELECT** operator has a yellow triangle with an exclamation point in it. This indicates that there is a warning message associated with the operator. Hover over the warning icon to see the message and read the warning message.
80 |
81 | > 📝 The warning message states that there is an implicit conversion in the query. This means that the SQL Server query optimizer had to convert the data type of one of the columns in the query to another data type in order to execute the query.
82 |
83 | ## Identify ways to fix the warning message
84 |
85 | The *[HumanResources].[Employee]* table structure is defined by the following data definition language (DDL) statement. Review the fields that are used in the previous SQL query against this DDL, paying attention to their types.
86 |
87 | ```sql
88 | CREATE TABLE [HumanResources].[Employee](
89 | [BusinessEntityID] [int] NOT NULL,
90 | [NationalIDNumber] [nvarchar](15) NOT NULL,
91 | [LoginID] [nvarchar](256) NOT NULL,
92 | [OrganizationNode] [hierarchyid] NULL,
93 | [OrganizationLevel] AS ([OrganizationNode].[GetLevel]()),
94 | [JobTitle] [nvarchar](50) NOT NULL,
95 | [BirthDate] [date] NOT NULL,
96 | [MaritalStatus] [nchar](1) NOT NULL,
97 | [Gender] [nchar](1) NOT NULL,
98 | [HireDate] [date] NOT NULL,
99 | [SalariedFlag] [dbo].[Flag] NOT NULL,
100 | [VacationHours] [smallint] NOT NULL,
101 | [SickLeaveHours] [smallint] NOT NULL,
102 | [CurrentFlag] [dbo].[Flag] NOT NULL,
103 | [rowguid] [uniqueidentifier] ROWGUIDCOL NOT NULL,
104 | [ModifiedDate] [datetime] NOT NULL
105 | ) ON [PRIMARY]
106 | ```
107 |
108 | 1. According to the warning message presented in the execution plan, what change would you recommend?
109 |
110 | 1. Identify what field is causing the implicit conversion and why.
111 | 1. If you review the query:
112 |
113 | ```sql
114 | SELECT BusinessEntityID, NationalIDNumber, LoginID, HireDate, JobTitle
115 | FROM HumanResources.Employee
116 | WHERE NationalIDNumber = 14417807;
117 | ```
118 |
119 | You'll note that the value compared to the *NationalIDNumber* column in the **WHERE** clause is compared as a number, since **14417807** isn't in a quoted string.
120 |
121 | After examining the table structure you will find the *NationalIDNumber* column is using the **NVARCHAR** data type and not an **INT** data type. This inconsistency causes the database optimizer to implicitly convert the number to a *NVARCHAR* value, causing additional overhead to the query performance by creating a suboptimal plan.
122 |
123 | There are two approaches we can implement to fix the implicit conversion warning. We will investigate each of them in the next steps.
124 |
125 | ### Change the code
126 |
127 | 1. How would you change the code to resolve the implicit conversion? Change the code and rerun the query.
128 |
129 | Remember to turn on the **Include Actual Execution Plan** (**CTRL+M**) if it is not already on.
130 |
131 | In this scenario, just adding a single quote on each side of the value changes it from a number to a character format. Keep the query window open for this query.
132 |
133 | Run the updated SQL query:
134 |
135 | ```sql
136 | SELECT BusinessEntityID, NationalIDNumber, LoginID, HireDate, JobTitle
137 | FROM HumanResources.Employee
138 | WHERE NationalIDNumber = '14417807';
139 | ```
140 |
141 | > 📝 Note that the warning message is now gone, and the query plan has improved. Changing the *WHERE* clause so that the value compared to the *NationalIDNumber* column matches the column's data type in the table, the optimizer was able to get rid of the implicit conversion and generate a more optimal plan.
142 |
143 | ### Change the data type
144 |
145 | 1. We can also fix the implicit conversion warning by changing the table structure.
146 |
147 | To attempt to fix the index, copy and paste the query below into a new query window, to change the column's data type. Attempt to execute the query, by selecting **Execute** or pressing F5.
148 |
149 | ```sql
150 | ALTER TABLE [HumanResources].[Employee] ALTER COLUMN [NationalIDNumber] INT NOT NULL;
151 | ```
152 |
153 | Changing the *NationalIDNumber* column data type to INT would solve the conversion issue. However, this change introduces another issue that as a database administrator you need to resolve. Running the above query will result in the following error message:
154 |
155 | Msg 5074, Level 16, Sate 1, Line1
156 | The index 'AK_Employee_NationalIDNumber' is dependent on column 'NationalIDNumber
157 | Msg 4922, Level 16, State 9, Line 1
158 | ALTER TABLE ALTER COLUMN NationalIDNumber failed because one or more objects access this column
159 |
160 | The *NationalIDNumber* column is part of an already existing nonclustered index, the index has to be rebuilt/recreated in order to change the data type. **This could lead to extended downtime in production, which highlights the importance of choosing the right data types in your design.**
161 |
162 | 1. In order to resolve this issue, copy and paste the code below into your query window and execute it by selecting **Execute**.
163 |
164 | ```sql
165 | USE AdventureWorks2017
166 |
167 | GO
168 |
169 | --Dropping the index first
170 | DROP INDEX [AK_Employee_NationalIDNumber] ON [HumanResources].[Employee]
171 |
172 | GO
173 |
174 | --Changing the column data type to resolve the implicit conversion warning
175 | ALTER TABLE [HumanResources].[Employee] ALTER COLUMN [NationalIDNumber] INT NOT NULL;
176 |
177 | GO
178 |
179 | --Recreating the index
180 | CREATE UNIQUE NONCLUSTERED INDEX [AK_Employee_NationalIDNumber] ON [HumanResources].[Employee]( [NationalIDNumber] ASC );
181 |
182 | GO
183 | ```
184 |
185 | 1. Run the following query to confirm that the data type was successfully changed.
186 |
187 | ```sql
188 | SELECT c.name, t.name
189 | FROM sys.all_columns c INNER JOIN sys.types t
190 | ON (c.system_type_id = t.user_type_id)
191 | WHERE OBJECT_ID('[HumanResources].[Employee]') = c.object_id
192 | AND c.name = 'NationalIDNumber'
193 | ```
194 |
195 | 1. Now let's check the execution plan. Rerun the original query without the quotes.
196 |
197 | ```sql
198 | USE AdventureWorks2017
199 | GO
200 |
201 | SELECT BusinessEntityID, NationalIDNumber, LoginID, HireDate, JobTitle
202 | FROM HumanResources.Employee
203 | WHERE NationalIDNumber = 14417807;
204 | ```
205 |
206 | Examine the query plan, and note that you can now use an integer to filter by *NationalIDNumber* without the implicit conversion warning. The SQL query optimizer can now generate and execute the most optimal plan.
207 |
208 | >📝 While changing the data type of a column can resolve implicit conversion issues, it is not always the best solution. In this case, changing the data type of the *NationalIDNumber* column to an **INT** data type would have caused downtime in production, as the index on that column would have to be dropped and recreated. It is important to consider the impact of changing a column's data type on existing queries and indexes before making any changes. Additionally, there may be other queries that rely on the *NationalIDNumber* column being an **NVARCHAR** data type, so changing the data type could break those queries.
209 |
210 | ---
211 |
212 | ## Cleanup
213 |
214 | If you are not using the Database or the lab files for any other purpose, you can clean up the objects you created in this lab.
215 |
216 | ### Delete the C:\LabFiles folder
217 |
218 | 1. From the lab virtual machine or your local machine if one wasn't provided, open **File Explorer**.
219 | 1. Navigate to **C:\\** .
220 | 1. Delete the **C:\LabFiles** folder.
221 |
222 | ## Delete the AdventureWorks2017 database
223 |
224 | 1. From the lab virtual machine or your local machine if one wasn't provided, start a SQL Server Management Studio session (SSMS).
225 | 1. When SSMS opens, by default the **Connect to Server** dialog will appear. Choose the Default instance and select **Connect**. You might need to check to the **Trust server certificate** checkbox.
226 | 1. In **Object Explorer**, expand the **Databases** folder.
227 | 1. Right-click on the **AdventureWorks2017** database and select **Delete**.
228 | 1. In the **Delete Object** dialog, check the **Close existing connections** checkbox.
229 | 1. Select **OK**.
230 |
231 | ---
232 |
233 | You have successfully completed this lab.
234 |
235 | In this exercise, you've learned how to identify query problems caused by implicit data type conversions, and how to fix it to improve the query plan.
236 |
--------------------------------------------------------------------------------
/Instructions/Labs/10-isolate-problem-areas-poor-performing-queries.md:
--------------------------------------------------------------------------------
1 | ---
2 | lab:
3 | title: 'Lab 10 – Isolate problem areas in poorly performing queries in a SQL Database'
4 | module: 'Optimize query performance in Azure SQL'
5 | ---
6 |
7 | # Isolate problem areas in poorly performing queries in a SQL Database
8 |
9 | **Estimated Time: 30 minutes**
10 |
11 | You've been hired as a Senior Database Administrator to help with performance issues currently happening when users query the *AdventureWorks2017* database. Your job is to identify issues in query performance and remedy them using techniques learned in this module.
12 |
13 | You'll run queries with suboptimal performance, examine the query plans, and attempt to make improvements within the database.
14 |
15 | > 📝 These exercises ask you to copy and paste T-SQL code. Please verify that the code has been copied correctly, before executing the code.
16 |
17 | ## Setup environment
18 |
19 | If your lab virtual machine has been provided and pre-configured, you should find the lab files ready in the **C:\LabFiles** folder. *Take a moment to check, if the files are already there, skip this section*. However, if you're using your own machine or the lab files are missing, you'll need to clone them from *GitHub* to proceed.
20 |
21 | 1. From the lab virtual machine or your local machine if one wasn't provided, start a Visual Studio Code session.
22 |
23 | 1. Open the command palette (Ctrl+Shift+P) and type **Git: Clone**. Select the **Git: Clone** option.
24 |
25 | 1. Paste the following URL into the **Repository URL** field and select **Enter**.
26 |
27 | ```url
28 | https://github.com/MicrosoftLearning/dp-300-database-administrator.git
29 | ```
30 |
31 | 1. Save the repository to the **C:\LabFiles** folder on the lab virtual machine or your local machine if one wasn't provided (create the folder if it does not exist).
32 |
33 | ---
34 |
35 | ## Restore a database
36 |
37 | If you already have the **AdventureWorks2017** database restored, you can skip this section.
38 |
39 | 1. From the lab virtual machine or your local machine if one wasn't provided, start a SQL Server Management Studio session (SSMS).
40 |
41 | 1. When SSMS opens, by default the **Connect to Server** dialog will appear. Choose the Default instance and select **Connect**. You might need to check to the **Trust server certificate** checkbox.
42 |
43 | > 📝 Note that if you are using your own SQL Server instance, you will need to connect to it using the appropriate server instance name and credentials.
44 |
45 | 1. Select the **Databases** folder, and then **New Query**.
46 |
47 | 1. In the new query window, copy and paste the below T-SQL into it. Execute the query to restore the database.
48 |
49 | ```sql
50 | RESTORE DATABASE AdventureWorks2017
51 | FROM DISK = 'C:\LabFiles\dp-300-database-administrator\Allfiles\Labs\Shared\AdventureWorks2017.bak'
52 | WITH RECOVERY,
53 | MOVE 'AdventureWorks2017'
54 | TO 'C:\LabFiles\AdventureWorks2017.mdf',
55 | MOVE 'AdventureWorks2017_log'
56 | TO 'C:\LabFiles\AdventureWorks2017_log.ldf';
57 | ```
58 |
59 | > 📝 You must have a folder named **C:\LabFiles**. If you don't have this folder, create it or specify another location for the database and backup files.
60 |
61 | 1. Under the **Messages** tab, you should see a message indicating that the database was restored successfully.
62 |
63 | ## Generate actual execution plan
64 |
65 | There are several ways to generate an execution plan in SQL Server Management Studio.
66 |
67 | 1. Select **New Query**. Copy and paste the following T-SQL code into the query window. Select **Execute** to execute this query.
68 |
69 | **Note:** Use **SHOWPLAN_ALL** to see a text version of a query's execution plan in the results pane instead of graphically in a separate tab.
70 |
71 | ```sql
72 | USE AdventureWorks2017;
73 |
74 | GO
75 |
76 | SET SHOWPLAN_ALL ON;
77 |
78 | GO
79 |
80 | SELECT BusinessEntityID
81 | FROM HumanResources.Employee
82 | WHERE NationalIDNumber = '14417807';
83 |
84 | GO
85 |
86 | SET SHOWPLAN_ALL OFF;
87 |
88 | GO
89 | ```
90 |
91 | In the results pane, you'll see a text version of the execution plan, instead of the actual query results for the **SELECT** statement.
92 |
93 | 1. Take a moment to examine the text in the second row of the **StmtText** column:
94 |
95 | ```console
96 | |--Index Seek(OBJECT:([AdventureWorks2017].[HumanResources].[Employee].[AK_Employee_NationalIDNumber]), SEEK:([AdventureWorks2017].[HumanResources].[Employee].[NationalIDNumber]=CONVERT_IMPLICIT(nvarchar(4000),[@1],0)) ORDERED FORWARD)
97 | ```
98 |
99 | The above text explains that the execution plan use an **Index Seek** on the **AK_Employee_NationalIDNumber** key. It also shows that the execution plan needed to do a **CONVERT_IMPLICIT** step.
100 |
101 | The query optimizer was able to locate an appropriate index to fetch the required records.
102 |
103 | ## Resolve a suboptimal query plan
104 |
105 | 1. Copy and paste the code below into a new query window.
106 |
107 | Select the **Include Actual Execution Plan** icon to the right of the Execute button, or press CTRL+M. Execute the query by selecting **Execute** or press F5. Make note of the execution plan and the logical reads in the messages tab.
108 |
109 | ```sql
110 | SET STATISTICS IO, TIME ON;
111 |
112 | SELECT [SalesOrderID] ,[CarrierTrackingNumber] ,[OrderQty] ,[ProductID], [UnitPrice] ,[ModifiedDate]
113 | FROM [AdventureWorks2017].[Sales].[SalesOrderDetail]
114 | WHERE [ModifiedDate] > '2012/01/01' AND [ProductID] = 772;
115 | ```
116 |
117 | When reviewing the execution plan, you will note there is a **Key Lookup**. If you hover your mouse over the icon, you will see that the properties indicate it is performed for each row retrieved by the query. You can see the execution plan is performing a **Key Lookup** operation.
118 |
119 | Make a note of the columns in the **Output List** section. How would you improve this query?
120 |
121 | To identify what index needs to be altered in order to remove the key lookup, you need to examine the index seek above it. Hover over the index seek operator with your mouse and the properties of the operator will appear.
122 |
123 | 1. **Key Lookups** can be removed by adding a covering index that includes all fields being returned or searched in the query. In this example the index only uses the **ProductID** column. The following is the current definition of the index, note that the **ProductID** column is the only key column forcing a **Key Lookup** to retrieve the other columns required by the query.
124 |
125 | ```sql
126 | CREATE NONCLUSTERED INDEX [IX_SalesOrderDetail_ProductID] ON [Sales].[SalesOrderDetail]
127 | ([ProductID] ASC)
128 | ```
129 |
130 | If we add the **Output List** fields to the index as included columns, then the **Key Lookup** will be removed. Since the index already exists you either have to DROP the index and recreate it, or set the **DROP_EXISTING=ON** in order to add the columns. Note that the **ProductID** column is already part of the index and does not need to be added as an included column. There is another performance improvement we can make to the index by adding the **ModifiedDate**. Open a **New Query** window and execute the following script to drop and recreate the index.
131 |
132 | ```sql
133 | CREATE NONCLUSTERED INDEX [IX_SalesOrderDetail_ProductID]
134 | ON [Sales].[SalesOrderDetail] ([ProductID],[ModifiedDate])
135 | INCLUDE ([CarrierTrackingNumber],[OrderQty],[UnitPrice])
136 | WITH (DROP_EXISTING = on);
137 | GO
138 | ```
139 |
140 | 1. Rerun the query from step 1. Make note of the changes to the logical reads and execution plan changes. The plan now only needs to use the nonclustered index we created.
141 |
142 | > 📝 Reviewing the execution plan, you will note that the **key lookup** is now gone and we are only using the nonclustered index.
143 |
144 | ## Use Query Store to detect and handle regression
145 |
146 | Next you'll run a workload to generate query statistics for query store, examine **Top Resource Consuming Queries** report to identify poor performance, and see how to force a better execution plan.
147 |
148 | 1. Select **New Query**. Copy and paste the following T-SQL code into the query window. Select **Execute** to execute this query.
149 |
150 | This script will enable the Query Store feature for AdventureWorks2017 database and sets the database to Compatibility Level 100.
151 |
152 | ```sql
153 | USE [master];
154 |
155 | GO
156 |
157 | ALTER DATABASE [AdventureWorks2017] SET QUERY_STORE = ON;
158 |
159 | GO
160 |
161 | ALTER DATABASE [AdventureWorks2017] SET QUERY_STORE (OPERATION_MODE = READ_WRITE);
162 |
163 | GO
164 |
165 | ALTER DATABASE [AdventureWorks2017] SET COMPATIBILITY_LEVEL = 100;
166 |
167 | GO
168 | ```
169 |
170 | Changing the compatibility level is like moving the database back in time. It restricts the features SQL server can use to those that were available in SQL Server 2008.
171 |
172 | 1. Select the **File** > **Open** > **File** menu in SQL Server Management Studio.
173 |
174 | 1. Navigate to the **C:\LabFiles\dp-300-database-administrator\Allfiles\Labs\10\CreateRandomWorkloadGenerator.sql** file.
175 |
176 | 1. Once opened into SQL Server Management Studio, select **Execute** to execute the query.
177 |
178 | 1. In a new query editor, open the file **C:\LabFiles\dp-300-database-administrator\Allfiles\Labs\10\ExecuteRandomWorkload.sql**, and select **Execute** to execute the query.
179 |
180 | 1. After the execution completes, run the script a second time to create additional load on the server. Leave the query tab open for this query.
181 |
182 | 1. Copy and paste the following code into a new query window and execute it by selecting **Execute**.
183 |
184 | This script changes the database compatibility mode to SQL Server 2022 (**160**). All the features and improvements since SQL Server 2008 will now be available to the database.
185 |
186 | ```sql
187 | USE [master];
188 |
189 | GO
190 |
191 | ALTER DATABASE [AdventureWorks2017] SET COMPATIBILITY_LEVEL = 160;
192 |
193 | GO
194 | ```
195 |
196 | 1. Navigate back to the query tab from **ExecuteRandomWorkload.sql** file, and re-execute it.
197 |
198 | ## Examine Top Resource Consuming Queries report
199 |
200 | 1. In order to view the Query Store node you will need to refresh the AdventureWorks2017 database in SQL Server Management Studio. Right click on database name and choose select **Refresh**. You will then see the Query Store node under the database.
201 |
202 | 1. Expand the **Query Store** node to view all the reports available. Select the **Top Resource Consuming Queries** report.
203 |
204 | 1. When the report opens, select the menu dropdown, then select **Configure** on the top right hand corner of the report.
205 |
206 | 1. In the configuration screen, change the filter for the minimum number of query plans to 2. Then select **OK**.
207 |
208 | 1. Choose the query with the longest duration by selecting the left most bar in the bar chart in the top left portion of the report.
209 |
210 | This will show you the query and plan summary for your longest duration query in your query store. Check out the *Plan summary* chart in the top right corner of the report and the *query plan* in the bottom of the report.
211 |
212 | ## Force a better execution plan
213 |
214 | 1. Navigate to the plan summary portion of the report as shown below. You will note there are two execution plans with widely different durations.
215 |
216 | 1. Select the Plan ID with the lowest duration (this is indicated by a lower position on the Y-axis of the chart) in the top right window of the report. Select the plan ID next to the Plan Summary chart.
217 |
218 | 1. Select **Force Plan** under the summary chart. A confirmation window will popup, select **Yes**.
219 |
220 | Once the plan is forced you will see that the **Forced Plan** is now greyed out and the plan in the plan summary window now has a check mark indicating is it forced.
221 |
222 | There can be times when the query optimizer can make a poor choice on which execution plan to use. When this happens you can force SQL server to use the plan you want when you know it performs better.
223 |
224 | ## Use query hints to impact performance
225 |
226 | Next you'll run a workload, change the query to use a parameter, apply a query hint to the query, and re-execute it.
227 |
228 | Before continuing with the exercise close all the current query windows by selecting the **Window** menu, then select **Close All Documents**. In the popup select **No**.
229 |
230 | 1. Select **New Query**, then select the **Include Actual Execution Plan** icon before running the query or use CTRL+M.
231 |
232 | 1. Execute the query below. Note that the execution plan shows an index seek operator.
233 |
234 | ```sql
235 | USE AdventureWorks2017;
236 |
237 | GO
238 |
239 | SELECT SalesOrderId, OrderDate
240 | FROM Sales.SalesOrderHeader
241 | WHERE SalesPersonID=288;
242 | ```
243 |
244 | 1. In a new query window, run the next query. Compare both execution plans.
245 |
246 | ```sql
247 | USE AdventureWorks2017;
248 | GO
249 |
250 | SELECT SalesOrderId, OrderDate
251 | FROM Sales.SalesOrderHeader
252 | WHERE SalesPersonID=277;
253 | ```
254 |
255 | The only change this time is that the SalesPersonID value is set to 277. Note the Clustered Index Scan operation in the execution plan.
256 |
257 | As we can see, based on the index statistics the query optimizer has chosen a different execution plan because of the different values in the **WHERE** clause.
258 |
259 | Why do we have different plans if we only changed the *SalesPersonID* value?
260 |
261 | This query uses a constant in its **WHERE** clause, the optimizer sees each of these queries as unique and generates a different execution plan each time.
262 |
263 | ## Change the query to use a variable and use a Query Hint
264 |
265 | 1. Change the query to use a variable value for SalesPersonID.
266 |
267 | 1. Use the T-SQL **DECLARE** statement to declare @SalesPersonID so you can pass in a value instead of hard-code the value in the **WHERE** clause. You should ensure that the data type of your variable matches the data type of the column in the target table to avoid implicit conversion. Execute the query with the actual query plan enabled.
268 |
269 | ```sql
270 | USE AdventureWorks2017;
271 |
272 | GO
273 |
274 | SET STATISTICS IO, TIME ON;
275 |
276 | DECLARE @SalesPersonID INT;
277 |
278 | SELECT @SalesPersonID = 288;
279 |
280 | SELECT SalesOrderId, OrderDate
281 | FROM Sales.SalesOrderHeader
282 | WHERE SalesPersonID= @SalesPersonID;
283 | ```
284 |
285 | If you examine the execution plan, you will note it is using an index scan to get the results. The query optimizer couldn't make good optimizations because it can't know the value of the local variable until runtime.
286 |
287 | 1. You can help the query optimizer to make better choices by providing a query hint. Rerun the above query with **OPTION (RECOMPILE)**:
288 |
289 | ```sql
290 | USE AdventureWorks2017
291 |
292 | GO
293 |
294 | SET STATISTICS IO, TIME ON;
295 |
296 | DECLARE @SalesPersonID INT;
297 |
298 | SELECT @SalesPersonID = 288;
299 |
300 | SELECT SalesOrderId, OrderDate
301 | FROM Sales.SalesOrderHeader
302 | WHERE SalesPersonID= @SalesPersonID
303 | OPTION (RECOMPILE);
304 | ```
305 |
306 | Note that the query optimizer has been able to choose a more efficient execution plan. The **RECOMPILE** option causes the query compiler to replace the variable with its value.
307 |
308 | Comparing the statistics, you can see in the message tab that the difference between logical reads is **68%** more (689 versus 409) for the query without the query hint.
309 |
310 | ---
311 |
312 | ## Cleanup
313 |
314 | If you are not using the Database or the lab files for any other purpose, you can clean up the objects you created in this lab.
315 |
316 | ### Delete the C:\LabFiles folder
317 |
318 | 1. From the lab virtual machine or your local machine if one wasn't provided, open **File Explorer**.
319 | 1. Navigate to **C:\\** .
320 | 1. Delete the **C:\LabFiles** folder.
321 |
322 | ### Delete the AdventureWorks2017 database
323 |
324 | 1. From the lab virtual machine or your local machine if one wasn't provided, start a SQL Server Management Studio session (SSMS).
325 | 1. When SSMS opens, by default the **Connect to Server** dialog will appear. Choose the Default instance and select **Connect**. You might need to check to the **Trust server certificate** checkbox.
326 | 1. In **Object Explorer**, expand the **Databases** folder.
327 | 1. Right-click on the **AdventureWorks2017** database and select **Delete**.
328 | 1. In the **Delete Object** dialog, check the **Close existing connections** checkbox.
329 | 1. Select **OK**.
330 |
331 | ---
332 |
333 | You have successfully completed this lab.
334 |
335 | In this exercise, you've learned how to identify query problems, and how to fix it to improve the query plan.
336 |
--------------------------------------------------------------------------------
/Instructions/Labs/11-deploy-azure-database-using-template.md:
--------------------------------------------------------------------------------
1 | ---
2 | lab:
3 | title: 'Lab 11 – Deploy Azure SQL Database using Azure Resource Manager template'
4 | module: 'Automate database tasks for Azure SQL'
5 | ---
6 |
7 | # Deploy an Azure SQL Database from a template
8 |
9 | **Estimated Time: 15 minutes**
10 |
11 | You've been hired as a Senior Data Engineer to help automate day to day operations of database administration. This automation is to help ensure that the databases for AdventureWorks continue to operate at peak performance and provide methods for alerting based on certain criteria. AdventureWorks uses SQL Server in both Infrastructure as a Service (IaaS) and Platform as a Service (PaaS) offerings.
12 |
13 | ## Explore Azure Resource Manager template
14 |
15 | 1. In Microsoft Edge, open a new tab and navigate to the following path in a GitHub repository, which contains an ARM template to deploy a SQL Database resource
16 |
17 | ```url
18 | https://github.com/Azure/azure-quickstart-templates/tree/master/quickstarts/microsoft.sql/sql-database
19 | ```
20 |
21 | 1. Right-click **azuredeploy.json**, and select **Open link in new tab** to view the ARM template, which should look similar to this:
22 |
23 | ```JSON
24 | {
25 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
26 | "contentVersion": "1.0.0.0",
27 | "parameters": {
28 | "serverName": {
29 | "type": "string",
30 | "defaultValue": "[uniqueString('sql', resourceGroup().id)]",
31 | "metadata": {
32 | "description": "The name of the SQL logical server."
33 | }
34 | },
35 | "sqlDBName": {
36 | "type": "string",
37 | "defaultValue": "SampleDB",
38 | "metadata": {
39 | "description": "The name of the SQL Database."
40 | }
41 | },
42 | "location": {
43 | "type": "string",
44 | "defaultValue": "[resourceGroup().location]",
45 | "metadata": {
46 | "description": "Location for all resources."
47 | }
48 | },
49 | "administratorLogin": {
50 | "type": "string",
51 | "metadata": {
52 | "description": "The administrator username of the SQL logical server."
53 | }
54 | },
55 | "administratorLoginPassword": {
56 | "type": "securestring",
57 | "metadata": {
58 | "description": "The administrator password of the SQL logical server."
59 | }
60 | }
61 | },
62 | "variables": {},
63 | "resources": [
64 | {
65 | "type": "Microsoft.Sql/servers",
66 | "apiVersion": "2020-02-02-preview",
67 | "name": "[parameters('serverName')]",
68 | "location": "[parameters('location')]",
69 | "properties": {
70 | "administratorLogin": "[parameters('administratorLogin')]",
71 | "administratorLoginPassword": "[parameters('administratorLoginPassword')]"
72 | },
73 | "resources": [
74 | {
75 | "type": "databases",
76 | "apiVersion": "2020-08-01-preview",
77 | "name": "[parameters('sqlDBName')]",
78 | "location": "[parameters('location')]",
79 | "sku": {
80 | "name": "Standard",
81 | "tier": "Standard"
82 | },
83 | "dependsOn": [
84 | "[resourceId('Microsoft.Sql/servers', concat(parameters('serverName')))]"
85 | ]
86 | }
87 | ]
88 | }
89 | ]
90 | }
91 | ```
92 |
93 | 1. Review and observe the JSON properties.
94 |
95 | 1. Close the **azuredeploy.json** tab and return to the tab containing the **sql-database** GitHub folder. Scroll down and select **Deploy to Azure**.
96 |
97 | 
98 |
99 | 1. The **Create a SQL Server and Database** quickstart template page will open on Azure portal, with resource details partially filled in from the ARM template. Complete the blank fields with the information below:
100 |
101 | - **Resource group:** starting with *contoso-rg*
102 | - **Sql Administrator Login:** labadmin
103 | - **Sql Administrator Login Password:** <enter a strong password>
104 |
105 | 1. Select **Review + create**, and then select **Create**. Your deployment will take 5 minutes or so to deploy.
106 |
107 | 
108 |
109 | 1. When deployment has completed, select **Go to resource group**. You'll be taken to your Azure Resource Group, which contains a randomly named **SQL Server** resource created by the deployment.
110 |
111 | 
112 |
113 | ---
114 |
115 | ## Cleanup Resources
116 |
117 | If you are not using the Azure SQL Server for any other purpose, you can clean up the resources you created in this lab.
118 |
119 | ### Delete the Resource Group
120 |
121 | If you created a new resource group for this lab, you can delete the resource group to remove all resources created in this lab.
122 |
123 | 1. In the Azure portal, select **Resource groups** from the left navigation pane or search for **Resource groups** in the search bar and select it from the results.
124 |
125 | 1. Go into the resource group that you created for this lab. The resource group will contain the Azure SQL Server and other resources created in this lab.
126 |
127 | 1. Select **Delete resource group** from the top menu.
128 |
129 | 1. In the **Delete resource group** dialog, type the name of the resource group to confirm and select **Delete**.
130 |
131 | 1. Wait for the resource group to be deleted.
132 |
133 | 1. Close the Azure portal.
134 |
135 | ### Delete the Lab resources only
136 |
137 | If you didn't create a new resource group for this lab, and want to leave the resource group and its previous resources intact, you can still delete the resources created in this lab.
138 |
139 | 1. In the Azure portal, select **Resource groups** from the left navigation pane or search for **Resource groups** in the search bar and select it from the results.
140 |
141 | 1. Go into the resource group that you created for this lab. The resource group will contain the Azure SQL Server and other resources created in this lab.
142 |
143 | 1. Select all the resources prefixed with the SQL Server name you previously specified in the lab.
144 |
145 | 1. Select **Delete** from the top menu.
146 |
147 | 1. In the **Delete resources** dialog, type **delete** and select **Delete**.
148 |
149 | 1. Select **Delete** again to confirm the deletion of the resources.
150 |
151 | 1. Wait for the resources to be deleted.
152 |
153 | 1. Close the Azure portal.
154 |
155 | ---
156 |
157 | You have successfully completed this lab.
158 |
159 | You've just seen how, with a single click on an Azure Resource Manager template link, you can easily create both an Azure SQL server and database.
160 |
--------------------------------------------------------------------------------
/Instructions/Labs/12-create-cpu-status-alert.md:
--------------------------------------------------------------------------------
1 | ---
2 | lab:
3 | title: 'Lab 12 – Create a CPU status alert for a SQL Server'
4 | module: 'Automate database tasks for Azure SQL'
5 | ---
6 |
7 | # Create a CPU status alert for a SQL Server on Azure
8 |
9 | **Estimated Time: 20 minutes**
10 |
11 | You have been hired as a Senior Data Engineer to help automate day to day operations of database administration. This automation is to help ensure that the databases for AdventureWorks continue to operate at peak performance as well as provide methods for alerting based on certain criteria.
12 |
13 | > 📝 These exercises ask you to copy and paste T-SQL code and makes use of existing SQL resources. Please verify that the code has been copied correctly, before executing the code.
14 |
15 | ## Setup environment
16 |
17 | If your lab virtual machine has been provided and pre-configured, you should find the lab files ready in the **C:\LabFiles** folder. *Take a moment to check, if the files are already there, skip this section*. However, if you're using your own machine or the lab files are missing, you'll need to clone them from *GitHub* to proceed.
18 |
19 | 1. From the lab virtual machine or your local machine if one wasn't provided, start a Visual Studio Code session.
20 |
21 | 1. Open the command palette (Ctrl+Shift+P) and type **Git: Clone**. Select the **Git: Clone** option.
22 |
23 | 1. Paste the following URL into the **Repository URL** field and select **Enter**.
24 |
25 | ```url
26 | https://github.com/MicrosoftLearning/dp-300-database-administrator.git
27 | ```
28 |
29 | 1. Save the repository to the **C:\LabFiles** folder on the lab virtual machine or your local machine if one wasn't provided (create the folder if it does not exist).
30 |
31 | ## Setup your SQL Server in Azure
32 |
33 | Log in to Azure and check if you have an existing Azure SQL Server instance running in Azure. *Skip this section if you already have a SQL Server instance running in Azure*.
34 |
35 | 1. From the lab virtual machine or your local machine if one wasn't provided, start a Visual Studio Code session and navigate to the cloned repository from the previous section.
36 |
37 | 1. Right-click on the **/Allfiles/Labs** folder and select **Open in Integrated Terminal**.
38 |
39 | 1. Let's connect to Azure using the Azure CLI. Type the following command and select **Enter**.
40 |
41 | ```bash
42 | az login
43 | ```
44 |
45 | > 📝 Note that a browser window will open. Use your Azure credentials to log in.
46 |
47 | 1. Once you are logged in to Azure, it's time to create a resource group if it doesn't already exist, and create a SQL server and database under that resource group. Type the following command and select **Enter**. *The script will take a few minutes to complete*.
48 |
49 | ```bash
50 | cd ./Setup
51 | ./deploy-sql-database.ps1
52 | ```
53 |
54 | > 📝 Note that by default this script will create or a resource group called **contoso-rg**, or use a resource whose name start with *contoso-rg* if it exists. By default it will also create all resources on the **West US 2** region (westus2). Finally it will generate a random 12 character password for the **SQL admin password**. You can change these values by using one or more of the parameters **-rgName**, **-location** and **-sqlAdminPw** with your own values. The password will have to meet the Azure SQL password complexity requirements, at least 12 characters long, and contain at least 1 uppercase letter, 1 lowercase letter, 1 number and 1 special character.
55 |
56 | > 📝 Note that the script will add your current Public IP address to the SQL server firewall rules.
57 |
58 | 1. Once the script has completed, it will return the resource group name, SQL server name and database name, and admin user name and password. *Take note of these values as you will need them later in the lab*.
59 |
60 | ---
61 |
62 | ## Create an alert when a CPU exceeds an average of 80 percent
63 |
64 | 1. From the lab virtual machine or your local machine if one wasn't provided, start a browser session and navigate to [https://portal.azure.com](https://portal.azure.com/). Connect to the Portal using your Azure credentials.
65 |
66 | 1. From the Azure Portal, in the search bar at the top of the Azure portal, type **SQL databases**, and select **SQL databases**. Select the **AdventureWorksLT** database name listed.
67 |
68 | 1. On the main blade for the **AdventureWorksLT** database, navigate down to the monitoring section. Select **Alerts**.
69 |
70 | 1. Select **Create alert rule**.
71 |
72 | 1. In the **Create an alert rule** page, select **CPU percentage**.
73 |
74 | 1. In the **Alert logic** section, select **Static** for the **Threshold type**. Then check that the **Aggregation** type is **Average** and that the **Value is** property is **Greater than**. Then in **Threshold** enter a value of **80**. Review the *Check every* and *lookback period* values.
75 |
76 | 1. Select **Next: Actions >**.
77 |
78 | 1. In the **Actions** tab, select **Create action group**.
79 |
80 | 1. On the **Action Group** screen, type **emailgroup** in the **Action group name** and **Display name** fields, and then select **Next: Notifications**.
81 |
82 | 1. On the **Notifications** tab, enter the following information:
83 |
84 | - **Notification type:** Email/SMS message/Push/Voice
85 |
86 | > 📝 When you select this option, a Email/SMS message/Push/Voice flyout will appear. Check the Email property and type the Azure username you signed in with. Select **OK**.
87 |
88 | - **Name:** DemoLab
89 |
90 | 1. Select **Review + create**, then select **Create**.
91 |
92 | 1. Back in the **Create an alert rule** page, select **Next: Details** and give the alert rule a unique name.
93 |
94 | 1. Select **Review + create**, then select **Create**.
95 |
96 | 1. With the alert in place, if the CPU usage on average exceeds 80%, an email is sent out.
97 |
98 | ---
99 |
100 | ## Cleanup Resources
101 |
102 | If you are not using the Azure SQL Server for any other purpose, you can clean up the resources you created in this lab.
103 |
104 | ### Delete the Resource Group
105 |
106 | If you created a new resource group for this lab, you can delete the resource group to remove all resources created in this lab.
107 |
108 | 1. In the Azure portal, select **Resource groups** from the left navigation pane or search for **Resource groups** in the search bar and select it from the results.
109 |
110 | 1. Go into the resource group that you created for this lab. The resource group will contain the Azure SQL Server and other resources created in this lab.
111 |
112 | 1. Select **Delete resource group** from the top menu.
113 |
114 | 1. In the **Delete resource group** dialog, type the name of the resource group to confirm and select **Delete**.
115 |
116 | 1. Wait for the resource group to be deleted.
117 |
118 | 1. Close the Azure portal.
119 |
120 | ### Delete the Lab resources only
121 |
122 | If you didn't create a new resource group for this lab, and want to leave the resource group and its previous resources intact, you can still delete the resources created in this lab.
123 |
124 | 1. In the Azure portal, select **Resource groups** from the left navigation pane or search for **Resource groups** in the search bar and select it from the results.
125 |
126 | 1. Go into the resource group that you created for this lab. The resource group will contain the Azure SQL Server and other resources created in this lab.
127 |
128 | 1. Select all the resources prefixed with the SQL Server name you previously specified in the lab.
129 |
130 | 1. Select **Delete** from the top menu.
131 |
132 | 1. In the **Delete resources** dialog, type **delete** and select **Delete**.
133 |
134 | 1. Select **Delete** again to confirm the deletion of the resources.
135 |
136 | 1. Wait for the resources to be deleted.
137 |
138 | 1. Close the Azure portal.
139 |
140 | ---
141 |
142 | You have successfully completed this lab.
143 |
144 | Alerts can send you an email or call a web hook when some metric (for example database size or CPU usage) reaches a threshold you define. You've just seen how you can easily configure alerts for Azure SQL Databases.
145 |
--------------------------------------------------------------------------------
/Instructions/Labs/13-deploy-automation-runbook-rebuild-indexes.md:
--------------------------------------------------------------------------------
1 | ---
2 | lab:
3 | title: 'Lab 13 – Deploy an automation runbook to automatically rebuild indexes'
4 | module: 'Automate database tasks for Azure SQL'
5 | ---
6 |
7 | # Deploy an automation runbook to automatically rebuild indexes
8 |
9 | **Estimated Time: 30 minutes**
10 |
11 | You have been hired as a Senior Database Administrator to help automate day to day operations of database administration. This automation is to help ensure that the databases for AdventureWorks continue to operate at peak performance as well as provide methods for alerting based on certain criteria. AdventureWorks utilizes SQL Server in both Infrastructure as a Service (IaaS) and Platform as a Service (PaaS) offerings.
12 |
13 | > 📝 These exercises may ask you to copy and paste T-SQL code and makes use of existing SQL resources. Please verify that the code has been copied correctly, before executing the code.
14 |
15 | ## Setup environment
16 |
17 | If your lab virtual machine has been provided and pre-configured, you should find the lab files ready in the **C:\LabFiles** folder. *Take a moment to check, if the files are already there, skip this section*. However, if you're using your own machine or the lab files are missing, you'll need to clone them from *GitHub* to proceed.
18 |
19 | 1. From the lab virtual machine or your local machine if one wasn't provided, start a Visual Studio Code session.
20 |
21 | 1. Open the command palette (Ctrl+Shift+P) and type **Git: Clone**. Select the **Git: Clone** option.
22 |
23 | 1. Paste the following URL into the **Repository URL** field and select **Enter**.
24 |
25 | ```url
26 | https://github.com/MicrosoftLearning/dp-300-database-administrator.git
27 | ```
28 |
29 | 1. Save the repository to the **C:\LabFiles** folder on the lab virtual machine or your local machine if one wasn't provided (create the folder if it does not exist).
30 |
31 | ## Setup your SQL Server in Azure
32 |
33 | Log in to Azure and check if you have an existing Azure SQL Server instance running in Azure. *Skip this section if you already have a SQL Server instance running in Azure*.
34 |
35 | 1. From the lab virtual machine or your local machine if one wasn't provided, start a Visual Studio Code session and navigate to the cloned repository from the previous section.
36 |
37 | 1. Right-click on the **/Allfiles/Labs** folder and select **Open in Integrated Terminal**.
38 |
39 | 1. Let's connect to Azure using the Azure CLI. Type the following command and select **Enter**.
40 |
41 | ```bash
42 | az login
43 | ```
44 |
45 | > 📝 Note that a browser window will open. Use your Azure credentials to log in.
46 |
47 | 1. Once you are logged in to Azure, it's time to create a resource group if it doesn't already exist, and create a SQL server and database under that resource group. Type the following command and select **Enter**. *The script will take a few minutes to complete*.
48 |
49 | ```bash
50 | cd ./Setup
51 | ./deploy-sql-database.ps1
52 | ```
53 |
54 | > 📝 Note that by default this script will create or a resource group called **contoso-rg**, or use a resource whose name start with *contoso-rg* if it exists. By default it will also create all resources on the **West US 2** region (westus2). Finally it will generate a random 12 character password for the **SQL admin password**. You can change these values by using one or more of the parameters **-rgName**, **-location** and **-sqlAdminPw** with your own values. The password will have to meet the Azure SQL password complexity requirements, at least 12 characters long, and contain at least 1 uppercase letter, 1 lowercase letter, 1 number and 1 special character.
55 |
56 | > 📝 Note that the script will add your current Public IP address to the SQL server firewall rules.
57 |
58 | 1. Once the script has completed, it will return the resource group name, SQL server name and database name, and admin user name and password. *Take note of these values as you will need them later in the lab*.
59 |
60 | ---
61 |
62 | ## Create an Automation Account
63 |
64 | 1. From the lab virtual machine or your local machine if one wasn't provided, start a browser session and navigate to [https://portal.azure.com](https://portal.azure.com/). Connect to the Portal using your Azure credentials.
65 |
66 | 1. In the Azure portal in the search bar type *automation* and then select **Automation Accounts** from the search results, and then select **+ Create**.
67 |
68 | 1. On the **Create an Automation Account** page, enter the information below, and then select **Review + Create**.
69 |
70 | - **Resource Group:** <Your resource group>
71 | - **Automation account name:** autoAccount
72 | - **Region:** Use the default.
73 |
74 | 1. On the review page, select **Create**.
75 |
76 | > 📝 Your automation account could take a few minutes to create.
77 |
78 | ## Connect to an existing Azure SQL Database
79 |
80 | 1. In the Azure portal, navigate to your database by searching for **sql databases**.
81 |
82 | 1. Select the SQL database **AdventureWorksLT**.
83 |
84 | 1. On the main section for your SQL Database page, select **Query editor (preview)**.
85 |
86 | 1. You will be prompted for credentials to sign in to your database using the database admin account and select **OK**.
87 |
88 | This will open a new tab in your browser. Select **Add client IP** and then select **Save**. Once saved, return to the previous tab and select **OK** again.
89 |
90 | > 📝 You might receive the error message *Cannot open server 'your-sql-server-name' requested by the login. Client with IP address 'xxx.xxx.xxx.xxx' is not allowed to access the server.* If so, you will need to add your current Public IP address to the SQL server firewall rules.
91 |
92 | If you need to setup the firewall rules, follow these steps:
93 |
94 | 1. select **Set server firewall** from the top menu bar of the database's **Overview** page.
95 | 1. Select **Add your current IPv4 address (xxx.xxx.xxx.xxx)** and then select **Save**.
96 | 1. Once saved, return to the **AdventureWorksLT** database page and select **Query editor (preview)** again.
97 | 1. You will be prompted for credentials to sign in to your database using the database admin account and select **OK**.
98 |
99 | 1. In the **Query editor (preview)**, select **Open query**.
100 |
101 | 1. Select the browse *folder* icon and navigate to the **C:\LabFiles\dp-300-database-administrator\Allfiles\Labs\Module13** folder. Select the **usp_AdaptiveIndexDefrag.sql** file and select **Open**, and then select **OK**.
102 |
103 | 1. Delete **USE msdb** and **GO** on lines 5 and 6 of the query, and then select **Run**.
104 |
105 | 1. Expand the **Stored Procedures** folder to see the newly created stored procedures.
106 |
107 | ## Configure Automation Account assets
108 |
109 | The next steps consist of configuring the assets required in preparation for the runbook creation. Then select **Automation Accounts**.
110 |
111 | 1. On the Azure portal, in the top search box, type **automation** and select **Automation Accounts**.
112 |
113 | 1. Select the **autoAccount** automation account that you created.
114 |
115 | 1. Select **Modules** from the **Shared Resources** section of the Automation blade. Then select **Browse gallery**.
116 |
117 | 1. Search for **SqlServer** within the Gallery.
118 |
119 | 1. Select **SqlServer** which will direct to the next screen, and then select the **Select** button.
120 |
121 | 1. On the **Add a module** page, select the latest runtime version available, then select **Import**. This will import the PowerShell module into your Automation account.
122 |
123 | 1. You'll need to create a credential to securely sign in to your database. From the blade for the *Automation Account* navigate to the **Shared Resources** section and select **Credentials**.
124 |
125 | 1. Select **+ Add a Credential**, enter the information below, and then select **Create**.
126 |
127 | - Name: **SQLUser**
128 | - User name: **sqladmin**
129 | - Password: <Enter a strong password, 12 characters long, and containing at least 1 uppercase letter, 1 lowercase letter, 1 number and 1 special character.>
130 | - Confirm password: <Re-enter the password you previously entered.>
131 |
132 | ## Create a PowerShell runbook
133 |
134 | 1. In the Azure portal, navigate to your database by searching for **sql databases**.
135 |
136 | 1. Select the SQL database **AdventureWorksLT**.
137 |
138 | 1. On the **Overview** page copy the **Server name** of your Azure SQL Database (Your server name should start with *dp300-lab*). You'll paste this in later steps.
139 |
140 | 1. On the Azure portal, in the top search box, type **automation** and select **Automation Accounts**.
141 |
142 | 1. Select the **autoAccount** automation account.
143 |
144 | 1. Expand to the **Process Automation** section of the Automation account blade, select **Runbooks**.
145 |
146 | 1. Select **+ Create a runbook**.
147 |
148 | > 📝 As we've learned, note that there are two existing runbooks created. These were automatically created during the automation account deployment.
149 |
150 | 1. Enter the runbook name as **IndexMaintenance** and a runbook type of **PowerShell**. Select the latest runtime version available, then select **Review + Create**.
151 |
152 | 1. On the **Create runbook** page, select **Create**.
153 |
154 | 1. Once the runbook has been created, copy and paste the Powershell code snippet below into your runbook editor.
155 |
156 | > 📝 Please verify that the code has been copied correctly, before saving the runbook.
157 |
158 | ```powershell
159 | $AzureSQLServerName = ''
160 | $DatabaseName = 'AdventureWorksLT'
161 |
162 | $Cred = Get-AutomationPSCredential -Name "SQLUser"
163 | $SQLOutput = $(Invoke-Sqlcmd -ServerInstance $AzureSQLServerName -UserName $Cred.UserName -Password $Cred.GetNetworkCredential().Password -Database $DatabaseName -Query "EXEC dbo.usp_AdaptiveIndexDefrag" -Verbose) 4>&1
164 |
165 | Write-Output $SQLOutput
166 | ```
167 |
168 | > 📝 Note that the code above is a PowerShell script that will execute the stored procedure **usp_AdaptiveIndexDefrag** on the **AdventureWorksLT** database. The script uses the **Invoke-Sqlcmd** cmdlet to connect to the SQL server and execute the stored procedure. The **Get-AutomationPSCredential** cmdlet is used to retrieve the credentials stored in the Automation account.
169 |
170 | 1. On the first line of the script paste in the server name you copied in the previous steps.
171 |
172 | 1. Select **Save**, and then select **Publish**.
173 |
174 | 1. Select **Yes** to confirm the publish action.
175 |
176 | 1. The *IndexMaintenance* runbook is now published.
177 |
178 | ## Create a schedule for a runbook
179 |
180 | Next you will schedule the runbook to execute on a regular basis.
181 |
182 | 1. Under **Resources** in the left hand navigation of your **IndexMaintenance** runbook, select **Schedules**.
183 |
184 | 1. Select **+ Add a schedule**.
185 |
186 | 1. Select **Link a schedule to your runbook**.
187 |
188 | 1. Select **+ Add a schedule**.
189 |
190 | 1. Enter the information below, and then select **Create**.
191 |
192 | - **Name:** DailyIndexDefrag
193 | - **Description:** Daily Index defrag for AdventureWorksLT database.
194 | - **Starts:** 4:00 AM (next day)
195 | - **Time zone:** <Select the time zone that matches your location>
196 | - **Recurrence:** Recurring
197 | - **Recur every:** 1 day
198 | - **Set expiration:** No
199 |
200 | > 📝 Note that the start time is set to 4:00 AM the next day. The time zone is set to your local time zone. The recurrence is set to every 1 day. Never expires.
201 |
202 | 1. Select **Create**, and then select **OK**.
203 |
204 | 1. The schedule is now created and linked to the runbook. Select **OK**.
205 |
206 | Azure Automation delivers a cloud-based automation, and configuration service that supports consistent management across your Azure and non-Azure environments.
207 |
208 | ---
209 |
210 | ## Cleanup Resources
211 |
212 | If you are not using the Azure SQL Server for any other purpose, you can clean up the resources you created in this lab.
213 |
214 | ### Delete the Resource Group
215 |
216 | If you created a new resource group for this lab, you can delete the resource group to remove all resources created in this lab.
217 |
218 | 1. In the Azure portal, select **Resource groups** from the left navigation pane or search for **Resource groups** in the search bar and select it from the results.
219 |
220 | 1. Go into the resource group that you created for this lab. The resource group will contain the Azure SQL Server and other resources created in this lab.
221 |
222 | 1. Select **Delete resource group** from the top menu.
223 |
224 | 1. In the **Delete resource group** dialog, type the name of the resource group to confirm and select **Delete**.
225 |
226 | 1. Wait for the resource group to be deleted.
227 |
228 | 1. Close the Azure portal.
229 |
230 | ### Delete the Lab resources only
231 |
232 | If you didn't create a new resource group for this lab, and want to leave the resource group and its previous resources intact, you can still delete the resources created in this lab.
233 |
234 | 1. In the Azure portal, select **Resource groups** from the left navigation pane or search for **Resource groups** in the search bar and select it from the results.
235 |
236 | 1. Go into the resource group that you created for this lab. The resource group will contain the Azure SQL Server and other resources created in this lab.
237 |
238 | 1. Select all the resources prefixed with the SQL Server name you previously specified in the lab.
239 |
240 | 1. Select **Delete** from the top menu.
241 |
242 | 1. In the **Delete resources** dialog, type **delete** and select **Delete**.
243 |
244 | 1. Select **Delete** again to confirm the deletion of the resources.
245 |
246 | 1. Wait for the resources to be deleted.
247 |
248 | 1. Close the Azure portal.
249 |
250 | ### Delete the LabFiles folder
251 |
252 | If you created a new LabFiles folder for this lab, and no longer need it, you can delete the LabFiles folder to remove all files created in this lab.
253 |
254 | 1. From the lab virtual machine or your local machine if one wasn't provided, open file explorer and navigate to the **C:\\** drive.
255 | 1. Right-click on the **LabFiles** folder and select **Delete**.
256 | 1. Select **Yes** to confirm the deletion of the folder.
257 |
258 | ---
259 |
260 | You have successfully completed this lab.
261 |
262 | By completing this exercise you've automated the defragging of indexes on a SQL server database to run every day, at 4am.
263 |
--------------------------------------------------------------------------------
/Instructions/Labs/14-configure-geo-replication-for-azure-sql-database.md:
--------------------------------------------------------------------------------
1 | ---
2 | lab:
3 | title: 'Lab 14 – Configure geo-replication for Azure SQL Database'
4 | module: 'Plan and implement a high availability and disaster recovery solution'
5 | ---
6 |
7 | # Configure geo replication for Azure SQL Database
8 |
9 | **Estimated Time: 30 minutes**
10 |
11 | As a DBA within AdventureWorks, you need to enable geo-replication for Azure SQL Database, and ensure it is working properly. Additionally, you will manually fail it over to another region using the portal.
12 |
13 | > 📝 These exercises may ask you to copy and paste T-SQL code and makes use of existing SQL resources. Please verify that the code has been copied correctly, before executing the code.
14 |
15 | ## Setup environment
16 |
17 | If your lab virtual machine has been provided and pre-configured, you should find the lab files ready in the **C:\LabFiles** folder. *Take a moment to check, if the files are already there, skip this section*. However, if you're using your own machine or the lab files are missing, you'll need to clone them from *GitHub* to proceed.
18 |
19 | 1. From the lab virtual machine or your local machine if one wasn't provided, start a Visual Studio Code session.
20 |
21 | 1. Open the command palette (Ctrl+Shift+P) and type **Git: Clone**. Select the **Git: Clone** option.
22 |
23 | 1. Paste the following URL into the **Repository URL** field and select **Enter**.
24 |
25 | ```url
26 | https://github.com/MicrosoftLearning/dp-300-database-administrator.git
27 | ```
28 |
29 | 1. Save the repository to the **C:\LabFiles** folder on the lab virtual machine or your local machine if one wasn't provided (create the folder if it does not exist).
30 |
31 | ## Setup your SQL Server in Azure
32 |
33 | Log in to Azure and check if you have an existing Azure SQL Server instance running in Azure. *Skip this section if you already have a SQL Server instance running in Azure*.
34 |
35 | 1. From the lab virtual machine or your local machine if one wasn't provided, start a Visual Studio Code session and navigate to the cloned repository from the previous section.
36 |
37 | 1. Right-click on the **/Allfiles/Labs** folder and select **Open in Integrated Terminal**.
38 |
39 | 1. Let's connect to Azure using the Azure CLI. Type the following command and select **Enter**.
40 |
41 | ```bash
42 | az login
43 | ```
44 |
45 | > 📝 Note that a browser window will open. Use your Azure credentials to log in.
46 |
47 | 1. Once you are logged in to Azure, it's time to create a resource group if it doesn't already exist, and create a SQL server and database under that resource group. Type the following command and select **Enter**. *The script will take a few minutes to complete*.
48 |
49 | ```bash
50 | cd ./Setup
51 | ./deploy-sql-database.ps1
52 | ```
53 |
54 | > 📝 Note that by default this script will create or a resource group called **contoso-rg**, or use a resource whose name start with *contoso-rg* if it exists. By default it will also create all resources on the **West US 2** region (westus2). Finally it will generate a random 12 character password for the **SQL admin password**. You can change these values by using one or more of the parameters **-rgName**, **-location** and **-sqlAdminPw** with your own values. The password will have to meet the Azure SQL password complexity requirements, at least 12 characters long, and contain at least 1 uppercase letter, 1 lowercase letter, 1 number and 1 special character.
55 |
56 | > 📝 Note that the script will add your current Public IP address to the SQL server firewall rules.
57 |
58 | 1. Once the script has completed, it will return the resource group name, SQL server name and database name, and admin user name and password. *Take note of these values as you will need them later in the lab*.
59 |
60 | ---
61 |
62 | ## Enable geo-replication
63 |
64 | 1. From the lab virtual machine or your local machine if one wasn't provided, start a browser session and navigate to [https://portal.azure.com](https://portal.azure.com/). Connect to the Portal using your Azure credentials.
65 |
66 | 1. In the Azure portal, navigate to your database by searching for **sql databases**.
67 |
68 | 1. Select the SQL database **AdventureWorksLT**.
69 |
70 | 1. On the blade for the database, in **Data management** section, select **Replicas**.
71 |
72 | 1. Select **+ Create replica**.
73 |
74 | 1. On the **Create SQL Database - Geo Replica** page notice that the **Project details** and **Primary database** sections are already filled in with the subscription, resource group and database name.
75 |
76 | 1. For the **Replica Configuration** section, select **Geo replica** for the *Replica type*.
77 |
78 | 1. For the **Geo-secondary database details** fill in the following values:
79 |
80 | - **Subscription**: <Your subscription name> (the same as the primary database).
81 | - **Resource group**: <Select the same resource group as the primary database.>
82 | - **Database name**: The database name will be grayed out and will be the same as the primary database name.
83 | - **Server**: Select **Create new**.
84 | - On the **Create SQL Database Server** page, fill in the following values:
85 |
86 | - **Server name**: Enter a unique name for the secondary server. The name must be unique across all Azure SQL Database servers.
87 | - **Location**: Select a different region from the primary database. Note that your subscription may not have all regions available.
88 | - Check the **Allow Azure services to access server** checkbox. Note that in a production environment, you may want to restrict access to the server.
89 | - For authentication, select **SQL authentication**. Note that in a production environment, you may want to use **Use Microsoft Entra-only** authentication. Enter **sqladmin* for the admin login name and a secure password. The password must meet the Azure SQL password complexity requirements, at least 12 characters long, and contain at least 1 uppercase letter, 1 lowercase letter, 1 number and 1 special character.
90 | - Select **OK** to create the server.
91 |
92 | - **Want to use elastic pool?**: No.
93 | - **Compute + storage**: General Purpose, Gen 5, 2 vCores, 32 GB storage.
94 | - **Backup storage redundancy**: Locally redundant storage (LRS). Note that in a production environment, you may want to use **Geo-redundant storage (GRS)**.
95 |
96 | 1. Select **Review + Create**.
97 |
98 | 1. Select **Create**. It will take a few minutes to create the secondary server and database. Once it completes, the progress will change from **Deployment in progress** to **Your deployment is complete**.
99 |
100 | 1. Select **Go to resource** to navigate to the secondary server's database for the next step.
101 |
102 | ## Failover SQL Database to a secondary region
103 |
104 | Now that the Azure SQL Database replica is created, you will perform a failover.
105 |
106 | 1. If not already on the secondary server's database, search for **sql databases** in the Azure portal and select the SQL database **AdventureWorksLT** on the secondary server.
107 |
108 | 1. On the SQL database main blade, in **Data management** section, select **Replicas**.
109 |
110 | 1. Note that the geo replication link is now established. The *Replica state* value of the primary database is **Online** and the *Replica state* value of the geo replicas is **Readable**.
111 |
112 | 1. Select the **...** menu for the secondary geo replica server, and select **Forced Failover**.
113 |
114 | > 📝 Forced failover will switch the secondary database to the primary role. All sessions are disconnected during this operation.
115 |
116 | 1. When prompted by the warning message, click **Yes**.
117 |
118 | 1. The status of the primary replica will switch to **Pending** and the secondary to **Failover**.
119 |
120 | > 📝 Note that since the database is small, the failover will be quick. In a production environment, this process can take a few minutes.
121 |
122 | 1. When complete, the roles will switch with the secondary becoming the new primary, and the old primary the secondary. You might need to refresh the page to see the new status.
123 |
124 | We've seen the readable secondary database may be in the same Azure region as the primary, or, more commonly, in a different region. This kind of readable secondary databases are also known as geo-secondaries, or geo-replicas.
125 |
126 | ---
127 |
128 | ## Cleanup Resources
129 |
130 | If you are not using the Azure SQL Server for any other purpose, you can clean up the resources you created in this lab.
131 |
132 | ### Delete the Resource Group
133 |
134 | If you created a new resource group for this lab, you can delete the resource group to remove all resources created in this lab.
135 |
136 | 1. In the Azure portal, select **Resource groups** from the left navigation pane or search for **Resource groups** in the search bar and select it from the results.
137 |
138 | 1. Go into the resource group that you created for this lab. The resource group will contain the Azure SQL Server and other resources created in this lab.
139 |
140 | 1. Select **Delete resource group** from the top menu.
141 |
142 | 1. In the **Delete resource group** dialog, type the name of the resource group to confirm and select **Delete**.
143 |
144 | 1. Wait for the resource group to be deleted.
145 |
146 | 1. Close the Azure portal.
147 |
148 | ### Delete the Lab resources only
149 |
150 | If you didn't create a new resource group for this lab, and want to leave the resource group and its previous resources intact, you can still delete the resources created in this lab.
151 |
152 | 1. In the Azure portal, select **Resource groups** from the left navigation pane or search for **Resource groups** in the search bar and select it from the results.
153 |
154 | 1. Go into the resource group that you created for this lab. The resource group will contain the Azure SQL Server and other resources created in this lab.
155 |
156 | 1. Select all the resources prefixed with the SQL Server name you previously specified in the lab.
157 |
158 | 1. Select **Delete** from the top menu.
159 |
160 | 1. In the **Delete resources** dialog, type **delete** and select **Delete**.
161 |
162 | 1. Select **Delete** again to confirm the deletion of the resources.
163 |
164 | 1. Wait for the resources to be deleted.
165 |
166 | 1. Close the Azure portal.
167 |
168 | ### Delete the LabFiles folder
169 |
170 | If you created a new LabFiles folder for this lab, and no longer need it, you can delete the LabFiles folder to remove all files created in this lab.
171 |
172 | 1. From the lab virtual machine or your local machine if one wasn't provided, open file explorer and navigate to the **C:\\** drive.
173 | 1. Right-click on the **LabFiles** folder and select **Delete**.
174 | 1. Select **Yes** to confirm the deletion of the folder.
175 |
176 | ---
177 |
178 | You have successfully completed this lab.
179 |
180 | You have now seen how to enable geo-replicas for Azure SQL Database, and manually fail it over to another region using the portal.
181 |
--------------------------------------------------------------------------------
/Instructions/Labs/15-backup-url.md:
--------------------------------------------------------------------------------
1 | ---
2 | lab:
3 | title: 'Lab 15 – Backup to URL and Restore from URL'
4 | module: 'Plan and implement a high availability and disaster recovery solution'
5 | ---
6 |
7 | # Backup to URL
8 |
9 | **Estimated Time: 30 minutes**
10 |
11 | As a DBA for AdventureWorks, you need to back up a database to a URL in Azure and restore it from Azure blob storage after a human error has occurred.
12 |
13 | ## Setup environment
14 |
15 | If your lab virtual machine has been provided and pre-configured, you should find the lab files ready in the **C:\LabFiles** folder. *Take a moment to check, if the files are already there, skip this section*. However, if you're using your own machine or the lab files are missing, you'll need to clone them from *GitHub* to proceed.
16 |
17 | 1. From the lab virtual machine or your local machine if one wasn't provided, start a Visual Studio Code session.
18 |
19 | 1. Open the command palette (Ctrl+Shift+P) and type **Git: Clone**. Select the **Git: Clone** option.
20 |
21 | 1. Paste the following URL into the **Repository URL** field and select **Enter**.
22 |
23 | ```url
24 | https://github.com/MicrosoftLearning/dp-300-database-administrator.git
25 | ```
26 |
27 | 1. Save the repository to the **C:\LabFiles** folder on the lab virtual machine or your local machine if one wasn't provided (create the folder if it does not exist).
28 |
29 | ## Restore the database
30 |
31 | If you already have the **AdventureWorks2017** database restored, you can skip this section.
32 |
33 | 1. From the lab virtual machine or your local machine if one wasn't provided, start a SQL Server Management Studio session (SSMS).
34 |
35 | 1. When SSMS opens, by default the **Connect to Server** dialog will appear. Choose the Default instance and select **Connect**. You might need to check to the **Trust server certificate** checkbox.
36 |
37 | > 📝 Note that if you are using your own SQL Server instance, you will need to connect to it using the appropriate server instance name and credentials.
38 |
39 | 1. Select the **Databases** folder, and then **New Query**.
40 |
41 | 1. In the new query window, copy and paste the below T-SQL into it. Execute the query to restore the database.
42 |
43 | ```sql
44 | RESTORE DATABASE AdventureWorks2017
45 | FROM DISK = 'C:\LabFiles\dp-300-database-administrator\Allfiles\Labs\Shared\AdventureWorks2017.bak'
46 | WITH RECOVERY,
47 | MOVE 'AdventureWorks2017'
48 | TO 'C:\LabFiles\AdventureWorks2017.mdf',
49 | MOVE 'AdventureWorks2017_log'
50 | TO 'C:\LabFiles\AdventureWorks2017_log.ldf';
51 | ```
52 |
53 | > 📝 You must have a folder named **C:\LabFiles**. If you don't have this folder, create it or specify another location for the database and backup files.
54 |
55 | 1. Under the **Messages** tab, you should see a message indicating that the database was restored successfully.
56 |
57 | ## Configure Backup to URL
58 |
59 | 1. From the lab virtual machine or your local machine if one wasn't provided, start a Visual Studio Code session.
60 |
61 | 1. Open the cloned repo at **C:\LabFiles\dp-300-database-administrator**.
62 |
63 | 1. Right-click on the **Allfiles** folder and select **Open in Integrated Terminal**. This will open a terminal window at the correct location.
64 |
65 | 1. In the terminal, type the following and press **Enter**.
66 |
67 | ```bash
68 | az login
69 | ```
70 |
71 | 1. You will be prompted to open a browser and enter a code. Follow the instructions to log in to your Azure account.
72 |
73 | 1. *Skip this step if you have a resource group already*. If you don't have a resource group, create one by executing the following command in the terminal. Replace *contoso-rgXXX######* with a unique name for your resource group. The name must be unique across Azure. Replace your location (-l) with the location of your resource group.
74 |
75 | ```bash
76 | az group create -n "contoso-rglod#######" -l eastus2
77 | ```
78 |
79 | Replace **######** for some random characters.
80 |
81 | 1. In the terminal, type the following and press **Enter** to create a storage account. Make sure to use a unique name for the storage account. *The name must be between 3 and 24 characters in length and can contain numbers and lowercase letters only*. Replace *########* for 8 random numeric characters. The name must be unique across Azure. Replace contoso-rgXXX###### with the name of your resource group. Finally replace your location (-l) with the location of your resource group.
82 |
83 | ```bash
84 | az storage account create -n "dp300bckupstrg########" -g "contoso-rgXXX########" --kind StorageV2 -l eastus2
85 | ```
86 |
87 | 1. Next you will get the keys for your storage account, which you will use in subsequent steps. Execute the following code in the terminal using the unique name of your storage account and resource group.
88 |
89 | ```bash
90 | az storage account keys list -g contoso-rgXXX######## -n dp300bckupstrg########
91 | ```
92 |
93 | Your account key will be in the results of the above command. Make sure you use the same name (after the **-n**) and resource group (after the **-g**) that you used in the previous command. Copy the returned value for **key1** (without the double quotes).
94 |
95 | 1. Backing up a database in SQL Server to a URL uses container within a storage account. You will create a container specifically for backup storage in this step. To do this, execute the commands below.
96 |
97 | ```bash
98 | az storage container create --name "backups" --account-name "dp300bckupstrg########" --account-key "storage_key" --fail-on-exist
99 | ```
100 |
101 | Where **dp300bckupstrg########** is the unique storage account name used when creating the storage account, and **storage_key** is the key previously generated. The output should return **true**.
102 |
103 | 1. To verify if the container backups has been created properly, execute:
104 |
105 | ```bash
106 | az storage container list --account-name "dp300bckupstrg########" --account-key "storage_key"
107 | ```
108 |
109 | Where **dp300bckupstrg########** is the unique storage account name used when creating the storage account, and **storage_key** is the key generated.
110 |
111 | 1. A shared access signature (SAS) at the container level is required for security. Execute the following command on the terminal:
112 |
113 | ```bash
114 | az storage container generate-sas -n "backups" --account-name "dp300bckupstrg########" --account-key "storage_key" --permissions "rwdl" --expiry "date_in_the_future" -o tsv
115 | ```
116 |
117 | Where **dp300bckupstrg########** is the unique storage account name used when creating the storage account, **storage_key** is the key generated, and **date_in_the_future** is a time later than now. **date_in_the_future** must be in UTC. An example is **2025-12-31T00:00Z** which translates to expiring at Dec 31, 2025 at midnight.
118 |
119 | The output should return something similar to the following. Copy the whole shared access signature and paste it in **Notepad**, it will be used in the next task.
120 |
121 | *se=2020-12-31T00%3A00Z&sp=rwdl&sv=2018-11-09&sr=c&sig=rnoGlveGql7ILhziyKYUPBq5ltGc/pzqOCNX5rrLdRQ%3D*
122 |
123 | ## Create credential
124 |
125 | Now that the functionality is configured, you can generate a backup file as a blob in Azure Storage Account.
126 |
127 | 1. Start **SQL Server Management Studio (SSMS)**.
128 |
129 | 1. You will be prompted to connect to SQL Server. Ensure that **Windows Authentication** is selected, and select **Connect**.
130 |
131 | 1. Select **New Query**.
132 |
133 | 1. Create the credential that will be used to access storage in the cloud with the following Transact-SQL. Fill in the appropriate values, then select **Execute**.
134 |
135 | ```sql
136 | IF NOT EXISTS
137 | (SELECT *
138 | FROM sys.credentials
139 | WHERE name = 'https://.blob.core.windows.net/backups')
140 | BEGIN
141 | CREATE CREDENTIAL [https://.blob.core.windows.net/backups]
142 | WITH IDENTITY = 'SHARED ACCESS SIGNATURE',
143 | SECRET = ''
144 | END;
145 | GO
146 | ```
147 |
148 | Where both occurrences of **** are the unique storage account name created, and **** is the value generated at the end of the previous task similar to the following:
149 |
150 | *se=2020-12-31T00%3A00Z&sp=rwdl&sv=2018-11-09&sr=c&sig=rnoGlveGql7ILhziyKYUPBq5ltGc/pzqOCNX5rrLdRQ%3D*
151 |
152 | 1. You can check if the credential was created successfully by navigating to **Security -> Credentials** on Object Explore in SSMS.
153 |
154 | 1. If you mistyped and need to recreate the credential, you can drop it with the following command, making sure to change the name of the storage account:
155 |
156 | ```sql
157 | -- Only run this command if you need to go back and recreate the credential!
158 | DROP CREDENTIAL [https://.blob.core.windows.net/backups]
159 | ```
160 |
161 | ## Backup database to URL
162 |
163 | 1. Using SSMS, back up the database **AdventureWorks2017** to Azure with the following command in Transact-SQL:
164 |
165 | ```sql
166 | BACKUP DATABASE AdventureWorks2017
167 | TO URL = 'https://.blob.core.windows.net/backups/AdventureWorks2017.bak';
168 | GO
169 | ```
170 |
171 | Where **** is the unique storage account name used created.
172 |
173 | If an error occurs, check that you did not mistype anything during the credential creation, and that everything was created successfully.
174 |
175 | ## Validate the backup through Azure CLI
176 |
177 | To see that the file is actually in Azure, you can use Storage Explorer (preview) or Azure Cloud Shell.
178 |
179 | 1. Back on the Visual Studio code terminal, run this Azure CLI command:
180 |
181 | ```bash
182 | az storage blob list -c "backups" --account-name "dp300bckupstrg########" --account-key "storage_key" --output table
183 | ```
184 |
185 | Make sure you use the same unique storage account name (after the **--account-name**) and account key (after the **--account-key**) that you used in the previous commands.
186 |
187 | We can confirm the backup file was generated successfully.
188 |
189 | ## Validate the backup through Storage Browser
190 |
191 | 1. In a browser windows, go to the Azure portal and search and select **Storage accounts**.
192 |
193 | 1. Select the unique storage account name you created for the backups.
194 |
195 | 1. In the left navigation, select **Storage browser**. Expand **Blob containers**.
196 |
197 | 1. Select **backups**.
198 |
199 | 1. Note that the backup file is stored in the container.
200 |
201 | ## Restore from URL
202 |
203 | This task will show you how to restore a database from an Azure blob storage.
204 |
205 | 1. From **SQL Server Management Studio (SSMS)**, select **New Query**, then paste and execute the following query.
206 |
207 | ```sql
208 | USE AdventureWorks2017;
209 | GO
210 | SELECT * FROM Person.Address WHERE AddressId = 1;
211 | GO
212 | ```
213 |
214 | 1. Run this command to change the address of that customer.
215 |
216 | ```sql
217 | UPDATE Person.Address
218 | SET AddressLine1 = 'This is a human error'
219 | WHERE AddressId = 1;
220 | GO
221 | ```
222 |
223 | 1. Re-run **Step 1** to verify that the address has been changed. Now imagine if someone had changed thousands or millions of rows without a WHERE clause – or the wrong WHERE clause. One of the solutions involves restoring the database from the last available backup.
224 |
225 | 1. To restore the database to get it back to where it was before the customer name was mistakenly changed, execute the following.
226 |
227 | > 📝 **SET SINGLE_USER WITH ROLLBACK IMMEDIATE** syntax the open transactions will all be rolled back. This can prevent the restore failing due to active connections.
228 |
229 | ```sql
230 | USE [master]
231 | GO
232 |
233 | ALTER DATABASE AdventureWorks2017 SET SINGLE_USER WITH ROLLBACK IMMEDIATE
234 | GO
235 |
236 | RESTORE DATABASE AdventureWorks2017
237 | FROM URL = 'https://.blob.core.windows.net/backups/AdventureWorks2017.bak'
238 | GO
239 |
240 | ALTER DATABASE AdventureWorks2017 SET MULTI_USER
241 | GO
242 | ```
243 |
244 | Where **** is the unique storage account name you created.
245 |
246 | 1. Re-run **Step 1** to verify that the customer name has been restored.
247 |
248 | It is important to understand the components and the interaction to do a backup to or restore from the Azure Blob Storage service.
249 |
250 | ---
251 |
252 | ## Cleanup Resources
253 |
254 | If you are not using the Azure SQL Server for any other purpose, you can clean up the resources you created in this lab.
255 |
256 | ### Delete the Resource Group
257 |
258 | If you created a new resource group for this lab, you can delete the resource group to remove all resources created in this lab.
259 |
260 | 1. In the Azure portal, select **Resource groups** from the left navigation pane or search for **Resource groups** in the search bar and select it from the results.
261 |
262 | 1. Go into the resource group that you created for this lab. The resource group will contain the Azure SQL Server and other resources created in this lab.
263 |
264 | 1. Select **Delete resource group** from the top menu.
265 |
266 | 1. In the **Delete resource group** dialog, type the name of the resource group to confirm and select **Delete**.
267 |
268 | 1. Wait for the resource group to be deleted.
269 |
270 | 1. Close the Azure portal.
271 |
272 | ### Delete the Lab resources only
273 |
274 | If you didn't create a new resource group for this lab, and want to leave the resource group and its previous resources intact, you can still delete the resources created in this lab.
275 |
276 | 1. In the Azure portal, select **Resource groups** from the left navigation pane or search for **Resource groups** in the search bar and select it from the results.
277 |
278 | 1. Go into the resource group that you created for this lab. The resource group will contain the Azure SQL Server and other resources created in this lab.
279 |
280 | 1. Select all the resources prefixed with the SQL Server name you previously specified in the lab.
281 |
282 | 1. Select **Delete** from the top menu.
283 |
284 | 1. In the **Delete resources** dialog, type **delete** and select **Delete**.
285 |
286 | 1. Select **Delete** again to confirm the deletion of the resources.
287 |
288 | 1. Wait for the resources to be deleted.
289 |
290 | 1. Close the Azure portal.
291 |
292 | If you are not using the Database or the lab files for any other purpose, you can clean up the objects you created in this lab.
293 |
294 | ### Delete the C:\LabFiles folder
295 |
296 | 1. From the lab virtual machine or your local machine if one wasn't provided, open **File Explorer**.
297 | 1. Navigate to **C:\\** .
298 | 1. Delete the **C:\LabFiles** folder.
299 |
300 | ## Delete the AdventureWorks2017 database
301 |
302 | 1. From the lab virtual machine or your local machine if one wasn't provided, start a SQL Server Management Studio session (SSMS).
303 | 1. When SSMS opens, by default the **Connect to Server** dialog will appear. Choose the Default instance and select **Connect**. You might need to check to the **Trust server certificate** checkbox.
304 | 1. In **Object Explorer**, expand the **Databases** folder.
305 | 1. Right-click on the **AdventureWorks2017** database and select **Delete**.
306 | 1. In the **Delete Object** dialog, check the **Close existing connections** checkbox.
307 | 1. Select **OK**.
308 |
309 | ---
310 |
311 | You have successfully completed this lab.
312 |
313 | You have now seen that you can back up a database to a URL in Azure and, if necessary, restore it.
314 |
--------------------------------------------------------------------------------
/Instructions/Templates/03-provision-an-azure-sql-database.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | "sqlServerName": {
6 | "type": "string",
7 | "defaultValue": "dp300-lab"
8 | },
9 | "sqlAdministratorLogin": {
10 | "type": "string",
11 | "defaultValue": "sqladmin"
12 | },
13 | "sqlAdministratorLoginPassword": {
14 | "type": "securestring",
15 | "defaultValue": "P@ssw0rd01"
16 | },
17 | "transparentDataEncryption": {
18 | "type": "string",
19 | "defaultValue": "Enabled"
20 | },
21 | "location": {
22 | "type": "string",
23 | "defaultValue": "[resourceGroup().location]"
24 | }
25 | },
26 | "variables": {
27 | "subscriptionId": "[subscription().subscriptionId]",
28 | "resourceGroupName": "[resourceGroup().name]",
29 | "databaseName": "AdventureWorksLT",
30 | "sampleName": "AdventureWorksLT",
31 | "databaseEdition": "Basic",
32 | "databaseCollation": "SQL_Latin1_General_CP1_CI_AS",
33 | "databaseServiceObjectiveName": "Basic",
34 | "skuName": "GP_Gen5_2",
35 | "tier": "GeneralPurpose"
36 | },
37 | "resources": [
38 | {
39 | "name": "[parameters('sqlServerName')]",
40 | "type": "Microsoft.Sql/servers",
41 | "apiVersion": "2020-02-02-preview",
42 | "location": "[parameters('location')]",
43 | "tags": {
44 | "displayName": "SqlServer"
45 | },
46 | "properties": {
47 | "administratorLogin": "[parameters('sqlAdministratorLogin')]",
48 | "administratorLoginPassword": "[parameters('sqlAdministratorLoginPassword')]",
49 | "version": "12.0"
50 | },
51 | "resources": [
52 | {
53 | "name": "[variables('databaseName')]",
54 | "type": "databases",
55 | "apiVersion": "2020-02-02-preview",
56 | "location": "[resourceGroup().location]",
57 | "tags": {
58 | "displayName": "Database"
59 | },
60 | "properties": {
61 | "edition": "[variables('databaseEdition')]",
62 | "collation": "[variables('databaseCollation')]",
63 | "requestedServiceObjectiveName": "[variables('databaseServiceObjectiveName')]",
64 | "sampleName": "[variables('sampleName')]"
65 | },
66 | "sku": {
67 | "name": "[variables('skuName')]",
68 | "tier": "[variables('tier')]"
69 | },
70 | "dependsOn": [
71 | "[parameters('sqlServerName')]"
72 | ],
73 | "resources": [
74 | {
75 | "comments": "Transparent Data Encryption",
76 | "name": "current",
77 | "type": "transparentDataEncryption",
78 | "apiVersion": "2017-03-01-preview",
79 | "properties": {
80 | "status": "[parameters('transparentDataEncryption')]"
81 | },
82 | "dependsOn": [
83 | "[variables('databaseName')]"
84 | ]
85 | }
86 | ]
87 | },
88 | {
89 | "name": "AllowAllMicrosoftAzureIps",
90 | "type": "firewallrules",
91 | "apiVersion": "2020-02-02-preview",
92 | "location": "[parameters('location')]",
93 | "properties": {
94 | "endIpAddress": "0.0.0.0",
95 | "startIpAddress": "0.0.0.0"
96 | },
97 | "dependsOn": [
98 | "[parameters('sqlServerName')]"
99 | ]
100 | }
101 | ]
102 | }
103 | ],
104 | "outputs": {
105 | "sqlServerFqdn": {
106 | "type": "string",
107 | "value": "[reference(resourceId('Microsoft.Sql/servers/', parameters('sqlServerName'))).fullyQualifiedDomainName]"
108 | },
109 | "databaseName": {
110 | "type": "string",
111 | "value": "[variables('databaseName')]"
112 | }
113 | }
114 | }
--------------------------------------------------------------------------------
/Instructions/Templates/AdventureWorks.bacpac:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MicrosoftLearning/dp-300-database-administrator/3c74b78e799d1ed721474f0e8aab85a240548ba7/Instructions/Templates/AdventureWorks.bacpac
--------------------------------------------------------------------------------
/Instructions/Templates/AdventureWorks2017.bak:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MicrosoftLearning/dp-300-database-administrator/3c74b78e799d1ed721474f0e8aab85a240548ba7/Instructions/Templates/AdventureWorks2017.bak
--------------------------------------------------------------------------------
/Instructions/Templates/AdventureWorksLT.bacpac:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MicrosoftLearning/dp-300-database-administrator/3c74b78e799d1ed721474f0e8aab85a240548ba7/Instructions/Templates/AdventureWorksLT.bacpac
--------------------------------------------------------------------------------
/Instructions/Templates/CreateRandomWorkloadGenerator.sql:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MicrosoftLearning/dp-300-database-administrator/3c74b78e799d1ed721474f0e8aab85a240548ba7/Instructions/Templates/CreateRandomWorkloadGenerator.sql
--------------------------------------------------------------------------------
/Instructions/Templates/ExecuteRandomWorkload.sql:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MicrosoftLearning/dp-300-database-administrator/3c74b78e799d1ed721474f0e8aab85a240548ba7/Instructions/Templates/ExecuteRandomWorkload.sql
--------------------------------------------------------------------------------
/Instructions/images/dp-300-module-11-lab-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MicrosoftLearning/dp-300-database-administrator/3c74b78e799d1ed721474f0e8aab85a240548ba7/Instructions/images/dp-300-module-11-lab-01.png
--------------------------------------------------------------------------------
/Instructions/images/dp-300-module-11-lab-02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MicrosoftLearning/dp-300-database-administrator/3c74b78e799d1ed721474f0e8aab85a240548ba7/Instructions/images/dp-300-module-11-lab-02.png
--------------------------------------------------------------------------------
/Instructions/images/dp-300-module-11-lab-03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MicrosoftLearning/dp-300-database-administrator/3c74b78e799d1ed721474f0e8aab85a240548ba7/Instructions/images/dp-300-module-11-lab-03.png
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Sidney Andrews
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/_build.yml:
--------------------------------------------------------------------------------
1 | name: '$(Date:yyyyMMdd)$(Rev:.rr)'
2 | jobs:
3 | - job: build_markdown_content
4 | displayName: 'Build Markdown Content'
5 | workspace:
6 | clean: all
7 | pool:
8 | vmImage: 'Ubuntu 16.04'
9 | container:
10 | image: 'microsoftlearning/markdown-build:latest'
11 | steps:
12 | - task: Bash@3
13 | displayName: 'Build Content'
14 | inputs:
15 | targetType: inline
16 | script: |
17 | cp /{attribution.md,template.docx,package.json,package.js} .
18 | npm install
19 | node package.js --version $(Build.BuildNumber)
20 | - task: GitHubRelease@0
21 | displayName: 'Create GitHub Release'
22 | inputs:
23 | gitHubConnection: 'github-microsoftlearning-organization'
24 | repositoryName: '$(Build.Repository.Name)'
25 | tagSource: manual
26 | tag: 'v$(Build.BuildNumber)'
27 | title: 'Version $(Build.BuildNumber)'
28 | releaseNotesSource: input
29 | releaseNotes: '# Version $(Build.BuildNumber) Release'
30 | assets: '$(Build.SourcesDirectory)/out/*.zip'
31 | assetUploadMode: replace
32 | - task: PublishBuildArtifacts@1
33 | displayName: 'Publish Output Files'
34 | inputs:
35 | pathtoPublish: '$(Build.SourcesDirectory)/out/'
36 | artifactName: 'Lab Files'
37 |
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | remote_theme: MicrosoftLearning/Jekyll-Theme
2 | exclude:
3 | - readme.md
4 | - .github/
5 | header_pages:
6 | - index.html
7 | author: Microsoft Learning
8 | twitter_username: mslearning
9 | github_username: MicrosoftLearning
10 | plugins:
11 | - jekyll-sitemap
12 | - jekyll-mentions
13 | - jemoji
14 | markdown: kramdown
15 | kramdown:
16 | syntax_highlighter_opts:
17 | disable : true
18 |
--------------------------------------------------------------------------------
/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Online Hosted Instructions
3 | permalink: index.html
4 | layout: home
5 | ---
6 |
7 | # Database Administration Exercises
8 |
9 | These exercises support Microsoft course [DP-300: Administering Microsoft Azure SQL Solutions](https://docs.microsoft.com/training/courses/dp-300t00).
10 |
11 | {% assign labs = site.pages | where_exp:"page", "page.url contains '/Instructions/Labs'" %}
12 | | Module | Exercise |
13 | | --- | --- |
14 | {% for activity in labs %}| {{ activity.lab.module }} | [{{ activity.lab.title }}{% if activity.lab.type %} - {{ activity.lab.type }}{% endif %}]({{ site.github.url }}{{ activity.url }}) |
15 | {% endfor %}
16 |
17 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # DP-300: Administering Microsoft Azure SQL Solutions
2 |
3 | - **Are you a MCT?** - Have a look at our [GitHub User Guide for MCTs](https://microsoftlearning.github.io/MCT-User-Guide/)
4 |
5 | ## What are we doing?
6 |
7 | - To support this course, we will need to make frequent updates to the course content to keep it current with the Azure services used in the course. We are publishing the lab instructions and lab files on GitHub to allow for open contributions between the course authors and MCTs to keep the content current with changes in the Azure platform.
8 |
9 | - We hope that this brings a sense of collaboration to the labs like we've never had before - when Azure changes and you find it first during a live delivery, go ahead and make an enhancement right in the lab source. Help your fellow MCTs.
10 |
11 | ## How should I use these files relative to the released MOC files?
12 |
13 | - The instructor handbook and PowerPoints are still going to be your primary source for teaching the course content.
14 |
15 | - These files on GitHub are designed to be used in conjunction with the student handbook, but are in GitHub as a central repository so MCTs and course authors can have a shared source for the latest lab files.
16 |
17 | - It will be recommended that for every delivery, trainers check GitHub for any changes that may have been made to support the latest Azure services, and get the latest files for their delivery.
18 |
19 | ## What about changes to the student handbook?
20 |
21 | - We will review the student handbook on a quarterly basis and update through the normal MOC release channels as needed.
22 |
23 | ## How do I contribute?
24 |
25 | - Any MCT can submit a pull request to the code or content in the GitHub repro, Microsoft and the course author will triage and include content and lab code changes as needed.
26 |
27 | - You can submit bugs, changes, improvement and ideas. Find a new Azure feature before we have? Submit a new demo!
28 |
29 | ## Notes
30 |
31 | ### Classroom Materials
32 |
33 | It is strongly recommended that MCTs and Partners access these materials and in turn, provide them separately to students. Pointing students directly to GitHub to access Lab steps as part of an ongoing class will require them to access yet another UI as part of the course, contributing to a confusing experience for the student. An explanation to the student regarding why they are receiving separate Lab instructions can highlight the nature of an always-changing cloud-based interface and platform. Microsoft Learning support for accessing files on GitHub and support for navigation of the GitHub site is limited to MCTs teaching this course only.
34 |
--------------------------------------------------------------------------------