From 65f8915b7ea9d3548dec41896af107810822c120 Mon Sep 17 00:00:00 2001 From: Charles Gagnon Date: Thu, 27 Apr 2023 13:56:14 -0700 Subject: [PATCH] Remove azurehybridtoolkit extension (#22879) --- build/filters.js | 2 - build/gulpfile.vscode.js | 1 - build/lib/extensions.ts | 1 - build/npm/dirs.js | 1 - extensions/azurehybridtoolkit/.eslintrc.json | 9 - extensions/azurehybridtoolkit/.gitignore | 2 - extensions/azurehybridtoolkit/.vscodeignore | 7 - extensions/azurehybridtoolkit/README.md | 22 - .../extension.webpack.config.js | 17 - .../azurehybridtoolkit/images/extension.png | Bin 3338 -> 0 bytes .../notebooks/hybridbook/_config.yml | 2 - .../notebooks/hybridbook/_data/toc.yml | 78 --- .../hybridbook/content/Appendices.ipynb | 104 --- .../compatibility-assessment.ipynb | 100 --- .../hybridbook/content/Assessments/readme.md | 10 - .../Assessments/sql-server-assessment.ipynb | 164 ----- .../notebooks/hybridbook/content/LICENSE.md | 5 - .../content/data-portability/AdpSetup.png | Bin 125871 -> 0 bytes .../data-portability/export-sql-server.ipynb | 563 --------------- .../data-portability/import-sql-server.ipynb | 533 --------------- .../content/data-portability/readme.md | 20 - .../content/data-portability/setup-adp.ipynb | 643 ------------------ .../notebooks/hybridbook/content/glossary.md | 39 -- .../content/hadr/backup-to-blob.ipynb | 373 ---------- .../content/hadr/configure-failover.ipynb | 392 ----------- .../hybridbook/content/hadr/readme.md | 14 - .../networking/download-VpnClient.ipynb | 172 ----- .../content/networking/p2svnet-creation.ipynb | 229 ------- .../hybridbook/content/networking/readme.md | 13 - .../content/networking/s2svnet-creation.ipynb | 376 ---------- .../offline-migration/db-to-SQLDB.ipynb | 296 -------- .../content/offline-migration/db-to-VM.ipynb | 270 -------- .../offline-migration/instance-to-VM.ipynb | 264 ------- .../content/offline-migration/readme.md | 25 - .../hybridbook/content/prereqs.ipynb | 204 ------ .../content/provisioning/create-sqldb.ipynb | 292 -------- .../content/provisioning/create-sqlmi.ipynb | 343 ---------- .../content/provisioning/create-sqlvm.ipynb | 347 ---------- .../hybridbook/content/provisioning/readme.md | 9 - .../notebooks/hybridbook/content/readme.md | 45 -- .../notebooks/hybridbook/requirements.txt | 2 - extensions/azurehybridtoolkit/package.json | 62 -- .../azurehybridtoolkit/package.nls.json | 7 - extensions/azurehybridtoolkit/src/main.ts | 14 - .../azurehybridtoolkit/src/typings/ref.d.ts | 9 - extensions/azurehybridtoolkit/tsconfig.json | 18 - extensions/azurehybridtoolkit/yarn.lock | 8 - .../LCL/de/azurehybridtoolkit.xlf.lcl | 60 -- .../LCL/es/azurehybridtoolkit.xlf.lcl | 60 -- .../LCL/fr/azurehybridtoolkit.xlf.lcl | 60 -- .../LCL/it/azurehybridtoolkit.xlf.lcl | 60 -- .../LCL/ja/azurehybridtoolkit.xlf.lcl | 60 -- .../LCL/ko/azurehybridtoolkit.xlf.lcl | 60 -- .../LCL/pt-BR/azurehybridtoolkit.xlf.lcl | 60 -- .../LCL/ru/azurehybridtoolkit.xlf.lcl | 60 -- .../LCL/zh-Hans/azurehybridtoolkit.xlf.lcl | 60 -- .../LCL/zh-Hant/azurehybridtoolkit.xlf.lcl | 60 -- resources/xlf/LocProject.json | 6 - resources/xlf/de/azurehybridtoolkit.de.xlf | 27 - resources/xlf/en/azurehybridtoolkit.xlf | 20 - resources/xlf/es/azurehybridtoolkit.es.xlf | 27 - resources/xlf/fr/azurehybridtoolkit.fr.xlf | 27 - resources/xlf/it/azurehybridtoolkit.it.xlf | 27 - resources/xlf/ja/azurehybridtoolkit.ja.xlf | 27 - resources/xlf/ko/azurehybridtoolkit.ko.xlf | 27 - .../xlf/pt-br/azurehybridtoolkit.pt-BR.xlf | 27 - resources/xlf/ru/azurehybridtoolkit.ru.xlf | 27 - .../zh-hans/azurehybridtoolkit.zh-Hans.xlf | 27 - .../zh-hant/azurehybridtoolkit.zh-Hant.xlf | 27 - 69 files changed, 7003 deletions(-) delete mode 100644 extensions/azurehybridtoolkit/.eslintrc.json delete mode 100644 extensions/azurehybridtoolkit/.gitignore delete mode 100644 extensions/azurehybridtoolkit/.vscodeignore delete mode 100644 extensions/azurehybridtoolkit/README.md delete mode 100644 extensions/azurehybridtoolkit/extension.webpack.config.js delete mode 100644 extensions/azurehybridtoolkit/images/extension.png delete mode 100644 extensions/azurehybridtoolkit/notebooks/hybridbook/_config.yml delete mode 100644 extensions/azurehybridtoolkit/notebooks/hybridbook/_data/toc.yml delete mode 100644 extensions/azurehybridtoolkit/notebooks/hybridbook/content/Appendices.ipynb delete mode 100644 extensions/azurehybridtoolkit/notebooks/hybridbook/content/Assessments/compatibility-assessment.ipynb delete mode 100644 extensions/azurehybridtoolkit/notebooks/hybridbook/content/Assessments/readme.md delete mode 100644 extensions/azurehybridtoolkit/notebooks/hybridbook/content/Assessments/sql-server-assessment.ipynb delete mode 100644 extensions/azurehybridtoolkit/notebooks/hybridbook/content/LICENSE.md delete mode 100644 extensions/azurehybridtoolkit/notebooks/hybridbook/content/data-portability/AdpSetup.png delete mode 100644 extensions/azurehybridtoolkit/notebooks/hybridbook/content/data-portability/export-sql-server.ipynb delete mode 100644 extensions/azurehybridtoolkit/notebooks/hybridbook/content/data-portability/import-sql-server.ipynb delete mode 100644 extensions/azurehybridtoolkit/notebooks/hybridbook/content/data-portability/readme.md delete mode 100644 extensions/azurehybridtoolkit/notebooks/hybridbook/content/data-portability/setup-adp.ipynb delete mode 100644 extensions/azurehybridtoolkit/notebooks/hybridbook/content/glossary.md delete mode 100644 extensions/azurehybridtoolkit/notebooks/hybridbook/content/hadr/backup-to-blob.ipynb delete mode 100644 extensions/azurehybridtoolkit/notebooks/hybridbook/content/hadr/configure-failover.ipynb delete mode 100644 extensions/azurehybridtoolkit/notebooks/hybridbook/content/hadr/readme.md delete mode 100644 extensions/azurehybridtoolkit/notebooks/hybridbook/content/networking/download-VpnClient.ipynb delete mode 100644 extensions/azurehybridtoolkit/notebooks/hybridbook/content/networking/p2svnet-creation.ipynb delete mode 100644 extensions/azurehybridtoolkit/notebooks/hybridbook/content/networking/readme.md delete mode 100644 extensions/azurehybridtoolkit/notebooks/hybridbook/content/networking/s2svnet-creation.ipynb delete mode 100644 extensions/azurehybridtoolkit/notebooks/hybridbook/content/offline-migration/db-to-SQLDB.ipynb delete mode 100644 extensions/azurehybridtoolkit/notebooks/hybridbook/content/offline-migration/db-to-VM.ipynb delete mode 100644 extensions/azurehybridtoolkit/notebooks/hybridbook/content/offline-migration/instance-to-VM.ipynb delete mode 100644 extensions/azurehybridtoolkit/notebooks/hybridbook/content/offline-migration/readme.md delete mode 100644 extensions/azurehybridtoolkit/notebooks/hybridbook/content/prereqs.ipynb delete mode 100644 extensions/azurehybridtoolkit/notebooks/hybridbook/content/provisioning/create-sqldb.ipynb delete mode 100644 extensions/azurehybridtoolkit/notebooks/hybridbook/content/provisioning/create-sqlmi.ipynb delete mode 100644 extensions/azurehybridtoolkit/notebooks/hybridbook/content/provisioning/create-sqlvm.ipynb delete mode 100644 extensions/azurehybridtoolkit/notebooks/hybridbook/content/provisioning/readme.md delete mode 100644 extensions/azurehybridtoolkit/notebooks/hybridbook/content/readme.md delete mode 100644 extensions/azurehybridtoolkit/notebooks/hybridbook/requirements.txt delete mode 100644 extensions/azurehybridtoolkit/package.json delete mode 100644 extensions/azurehybridtoolkit/package.nls.json delete mode 100644 extensions/azurehybridtoolkit/src/main.ts delete mode 100644 extensions/azurehybridtoolkit/src/typings/ref.d.ts delete mode 100644 extensions/azurehybridtoolkit/tsconfig.json delete mode 100644 extensions/azurehybridtoolkit/yarn.lock delete mode 100644 resources/localization/LCL/de/azurehybridtoolkit.xlf.lcl delete mode 100644 resources/localization/LCL/es/azurehybridtoolkit.xlf.lcl delete mode 100644 resources/localization/LCL/fr/azurehybridtoolkit.xlf.lcl delete mode 100644 resources/localization/LCL/it/azurehybridtoolkit.xlf.lcl delete mode 100644 resources/localization/LCL/ja/azurehybridtoolkit.xlf.lcl delete mode 100644 resources/localization/LCL/ko/azurehybridtoolkit.xlf.lcl delete mode 100644 resources/localization/LCL/pt-BR/azurehybridtoolkit.xlf.lcl delete mode 100644 resources/localization/LCL/ru/azurehybridtoolkit.xlf.lcl delete mode 100644 resources/localization/LCL/zh-Hans/azurehybridtoolkit.xlf.lcl delete mode 100644 resources/localization/LCL/zh-Hant/azurehybridtoolkit.xlf.lcl delete mode 100644 resources/xlf/de/azurehybridtoolkit.de.xlf delete mode 100644 resources/xlf/en/azurehybridtoolkit.xlf delete mode 100644 resources/xlf/es/azurehybridtoolkit.es.xlf delete mode 100644 resources/xlf/fr/azurehybridtoolkit.fr.xlf delete mode 100644 resources/xlf/it/azurehybridtoolkit.it.xlf delete mode 100644 resources/xlf/ja/azurehybridtoolkit.ja.xlf delete mode 100644 resources/xlf/ko/azurehybridtoolkit.ko.xlf delete mode 100644 resources/xlf/pt-br/azurehybridtoolkit.pt-BR.xlf delete mode 100644 resources/xlf/ru/azurehybridtoolkit.ru.xlf delete mode 100644 resources/xlf/zh-hans/azurehybridtoolkit.zh-Hans.xlf delete mode 100644 resources/xlf/zh-hant/azurehybridtoolkit.zh-Hant.xlf diff --git a/build/filters.js b/build/filters.js index c24a7a3eab..27d30272f3 100644 --- a/build/filters.js +++ b/build/filters.js @@ -144,7 +144,6 @@ module.exports.indentationFilter = [ '!extensions/admin-tool-ext-win/license/**', '!extensions/resource-deployment/notebooks/**', '!extensions/mssql/notebooks/**', - '!extensions/azurehybridtoolkit/notebooks/**', '!extensions/integration-tests/testData/**', '!extensions/arc/src/controller/generated/**', '!extensions/sql-database-projects/resources/templates/*.xml', @@ -196,7 +195,6 @@ module.exports.copyrightFilter = [ '!src/vs/editor/test/node/classification/typescript-test.ts', // {{SQL CARBON EDIT}} Except for stuff in our code that doesn't use our copyright - '!extensions/azurehybridtoolkit/notebooks/**', '!extensions/azuremonitor/src/prompts/**', '!extensions/import/flatfileimportservice/**', '!extensions/kusto/src/prompts/**', diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 877e088292..f8a4be9aa4 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -123,7 +123,6 @@ const extensionsFilter = filter([ '**/asde-deployment.xlf', '**/azcli.xlf', '**/azurecore.xlf', - '**/azurehybridtoolkit.xlf', '**/azuremonitor.xlf', '**/cms.xlf', '**/dacpac.xlf', diff --git a/build/lib/extensions.ts b/build/lib/extensions.ts index 60f8d9ccef..187b1a90fa 100644 --- a/build/lib/extensions.ts +++ b/build/lib/extensions.ts @@ -301,7 +301,6 @@ const externalExtensions = [ 'arc', 'asde-deployment', 'azcli', - 'azurehybridtoolkit', 'azuremonitor', 'cms', 'dacpac', diff --git a/build/npm/dirs.js b/build/npm/dirs.js index a45c19be8e..19af457dbb 100644 --- a/build/npm/dirs.js +++ b/build/npm/dirs.js @@ -14,7 +14,6 @@ exports.dirs = [ 'extensions/arc', 'extensions/azcli', 'extensions/azurecore', - 'extensions/azurehybridtoolkit', 'extensions/azuremonitor', 'extensions/cms', 'extensions/configuration-editing', diff --git a/extensions/azurehybridtoolkit/.eslintrc.json b/extensions/azurehybridtoolkit/.eslintrc.json deleted file mode 100644 index 1814eab8e3..0000000000 --- a/extensions/azurehybridtoolkit/.eslintrc.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "parserOptions": { - "project": "./extensions/azurehybridtoolkit/tsconfig.json" - }, - "rules": { - // Disabled until the issues can be fixed - "@typescript-eslint/explicit-function-return-type": ["off"] - } -} diff --git a/extensions/azurehybridtoolkit/.gitignore b/extensions/azurehybridtoolkit/.gitignore deleted file mode 100644 index 3e214a1b91..0000000000 --- a/extensions/azurehybridtoolkit/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -notebooks/hybridbook/Components/**/obj -*.vsix \ No newline at end of file diff --git a/extensions/azurehybridtoolkit/.vscodeignore b/extensions/azurehybridtoolkit/.vscodeignore deleted file mode 100644 index 7d823be72e..0000000000 --- a/extensions/azurehybridtoolkit/.vscodeignore +++ /dev/null @@ -1,7 +0,0 @@ -.gitignore -src/** -out/** -tsconfig.json -extension.webpack.config.js -*.vsix -yarn.lock diff --git a/extensions/azurehybridtoolkit/README.md b/extensions/azurehybridtoolkit/README.md deleted file mode 100644 index f0930848e7..0000000000 --- a/extensions/azurehybridtoolkit/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# Azure SQL Hybrid Cloud Toolkit *(preview)* - -Adds a Jupyter Book that has several utilities for Azure SQL Hybrid Cloud. To open the Book, install the extension then -* Open the Command Palette (Ctrl+Shift+P, or select from the View menu) -* Type Jupyter Books: Azure SQL Hybrid Cloud Toolkit and select it as a command -* Press Enter - -The Book will be displayed in the Provided Books panel in the Notebooks viewlet. - -## Code of Conduct - -This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. - -## Privacy Statement - -To learn more about our Privacy Statement visit [this link](https://go.microsoft.com/fwlink/?LinkID=824704). - -## License - -Copyright (c) Microsoft Corporation. All rights reserved. - -Licensed under the [Source EULA](https://raw.githubusercontent.com/Microsoft/azuredatastudio/main/LICENSE.txt). diff --git a/extensions/azurehybridtoolkit/extension.webpack.config.js b/extensions/azurehybridtoolkit/extension.webpack.config.js deleted file mode 100644 index 5eb11780d2..0000000000 --- a/extensions/azurehybridtoolkit/extension.webpack.config.js +++ /dev/null @@ -1,17 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -//@ts-check - -'use strict'; - -const withDefaults = require('../shared.webpack.config'); - -module.exports = withDefaults({ - context: __dirname, - entry: { - main: './src/main.ts' - }, -}); diff --git a/extensions/azurehybridtoolkit/images/extension.png b/extensions/azurehybridtoolkit/images/extension.png deleted file mode 100644 index c86d6d1e009f12f1ccb246db45303707052175e1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3338 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 locations page for a complete list of Azure regions along with their general physical location. The following is a list of common North American location settings for this guide:\r\n", - "\r\n", - "### Regions\r\n", - "| Setting | Location\r\n", - "| ------------ | ---------\r\n", - "| Central US | Iowa\r\n", - "| East US | Virginia\r\n", - "| East US 2 | Virginia\r\n", - "| North Central US | Illinois\r\n", - "| South Central US | Texas\r\n", - "| West US 2 | Washington\r\n", - "| West Central US | Wyoming\r\n", - "| West US | California\r\n", - "| Canada Central | Toronto\r\n", - "| Canada East | Quebec City\r\n", - "| Brazil South | Sao Paulo\r\n", - "| Mexico Central | Queretaro" - ], - "metadata": { - "azdata_cell_guid": "31b9ebca-6102-438b-8a1c-d14c2598abc5" - } - }, - { - "cell_type": "markdown", - "source": [ - "## Appendix: Storage SKUs\r\n", - "Use these as values for provisioning storage skus. \r\n", - "\r\n", - "Data for table taken from SKU Types page but is subject to change. Not all skus are listed here. SKU type names are case-sensitive.\r\n", - "\r\n", - "| Name | Description |\r\n", - "| -----| ----------- |\r\n", - "| Standard_LRS | standard locally redundant storage |\r\n", - "| Standard_GRS | standard geo-replicated storage |\r\n", - "| Standard_RAGRS | standard read-access geo replicated storage |\r\n", - "| Standard_ZRS | standard zone redundant storage |\r\n", - "| Premium_LRS | Premium - provisioned IO locally redundant |\r\n", - "| Premium_ZRS | Premium - provisioned IO locally redundant |\r\n", - "" - ], - "metadata": { - "azdata_cell_guid": "a5272577-2f81-4cea-8dc2-8aaa7bbb6603" - } - }, - { - "cell_type": "markdown", - "source": [ - "## Appendix: Azure SQL VM Offers\n", - "\n", - "Run the following command using PowerShell to get updated list of offers for Microsoft SQL Server in a location. These settings can be used for the OfferName associated with this migration. \n", - "\n", - "Note that the SQL Version is first then appended with an operating system version. E.g.: \"WS2019\" means Windows Server 2019. Along with various versions of Windows Servers, there are also enterprise Linux versions such as RedHat Enterprise, Suse Enterprise, and Ubuntu. Some versions are BYOL (Bring Your Own License) aka [Hybrid Benefit](https://azure.microsoft.com/en-us/pricing/hybrid-benefit/)." - ], - "metadata": { - "azdata_cell_guid": "3a67fc67-c1d3-4dcc-b2a0-5f893d175000" - } - }, - { - "cell_type": "code", - "source": [ - "$Location = \"\" # Name of location to discover offering \r\n", - "$PublisherName = \"\" # Name of Publisher, Default would be 'MicrosoftSQLServer'\r\n", - "\r\n", - "Connect-AzAccount # Opens a dialog in another window\r\n", - "Get-AzVMImageOffer -Location $Location -Publisher $PublisherName | Select Offer" - ], - "metadata": { - "azdata_cell_guid": "7eb125fd-dce9-4421-89c8-8c4830e3bfe7" - }, - "outputs": [], - "execution_count": null - } - ] -} \ No newline at end of file diff --git a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/Assessments/compatibility-assessment.ipynb b/extensions/azurehybridtoolkit/notebooks/hybridbook/content/Assessments/compatibility-assessment.ipynb deleted file mode 100644 index 8f9ea0940c..0000000000 --- a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/Assessments/compatibility-assessment.ipynb +++ /dev/null @@ -1,100 +0,0 @@ -{ - "metadata": { - "kernelspec": { - "name": "powershell", - "display_name": "PowerShell", - "language": "powershell" - }, - "language_info": { - "name": "powershell", - "codemirror_mode": "shell", - "mimetype": "text/x-sh", - "file_extension": ".ps1" - } - }, - "nbformat_minor": 2, - "nbformat": 4, - "cells": [ - { - "cell_type": "markdown", - "source": [ - "# Migration Compatibility Assessment\n", - "Use dmacmd.exe to assess databases in an unattended mode, and output the result to JSON or CSV file. This method is especially useful when assessing several databases or huge databases.\n", - "\n", - "## Notebook Variables\n", - "\n", - "| Line | Variable | Description |\n", - "| --- | --- | --- |\n", - "| 1 | ExecutableFile | Path to DmaCmd.exe file, usually _\"C:\\\\Program Files\\\\Microsoft Data Migration Assistant\\\\DmaCmd.exe\"_ if installed to default location |\n", - "| 2 | AssessmentName | Unique name for assessment |\n", - "| 3 | Server | Target SQL Server |\n", - "| 4 | InitialCatalog | Name of the database for the specified server |\n", - "| 5 | ResultPath | Path and name of the file to store results in json format |" - ], - "metadata": { - "azdata_cell_guid": "6764dd37-fb1f-400d-8f2b-70bc36fc3b61" - } - }, - { - "cell_type": "code", - "source": [ - "$ExecutableFile = \"C:\\Program Files\\Microsoft Data Migration Assistant\\DmaCmd.exe\" # Update if different\r\n", - "$AssessmentName = \"\"\r\n", - "$Server = \"\"\r\n", - "$InitialCatalog = \"\"\r\n", - "$ResultPath = \"\"" - ], - "metadata": { - "azdata_cell_guid": "d81972c1-3b0b-47d9-b8a3-bc5ab4001a34", - "tags": [ - "parameters" - ] - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "### **Single-database assessment using Windows authentication and running compatibility rules**\r\n", - "\r\n", - "This command assess the single databse using authentication as Windows and save the result in specified path.\r\n", - "It requires Sql Server name & Database name in order to assess" - ], - "metadata": { - "azdata_cell_guid": "146181e8-c0e5-4fab-b4a4-40276762ff9f" - } - }, - { - "cell_type": "code", - "source": [ - "& $ExecutableFile /AssessmentName=$AssessmentName /AssessmentDatabases=\"Server=$Server;Initial Catalog=$InitialCatalog;Integrated Security=true\" /AssessmentEvaluateCompatibilityIssues /AssessmentOverwriteResult /AssessmentResultJson=$ResultPath" - ], - "metadata": { - "azdata_cell_guid": "c3abe0ea-e971-4058-9c85-9f2d909a9291" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "_Get-Content_ cmdlet gets the content of the item at the location specified by the path, such as the text in a file or the content of a function. For files, the content is read one line at a time and returns a collection of objects, each of which represents a line of content." - ], - "metadata": { - "azdata_cell_guid": "4b1c8811-8b78-4543-bfa3-f4879510795b" - } - }, - { - "cell_type": "code", - "source": [ - "Get-Content -Path $ResultPath" - ], - "metadata": { - "azdata_cell_guid": "7e69969a-0d53-4a00-830c-3ed41318234a" - }, - "outputs": [], - "execution_count": null - } - ] -} \ No newline at end of file diff --git a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/Assessments/readme.md b/extensions/azurehybridtoolkit/notebooks/hybridbook/content/Assessments/readme.md deleted file mode 100644 index 93fd2cb537..0000000000 --- a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/Assessments/readme.md +++ /dev/null @@ -1,10 +0,0 @@ -# Assessments - -[Home](../readme.md) - -Preparing for the cloud requires a crawl-walk-run mentality. The first step, or crawl, towards hybrid migration is determining the fitness of existing on-premise resources. An assessment is an analysis performed against a chosen SQL Server object such as a Server or Database instance. It is recommended to fix any issues found by the analysis prior to migrating a database from on-premise to Azure. - -## Notebooks in this Chapter -- [SQL Server Best Practices Assessment](sql-server-assessment.ipynb) - demonstrates the use of the [SQL Server Assessment API](https://docs.microsoft.com/en-us/sql/sql-assessment-api/sql-assessment-api-overview), a tool to review the configuration of a SQL Server and Databases for best practices. - -- [Compatibility Assessment](compatibility-assessment.ipynb) - Analyze an on-premises SQL Server instance or database for compatibility for migration to SQL Azure. The assessment will provide guidance on features not currently supported in Azure and remediation actions that can be taken to prepare for migration. \ No newline at end of file diff --git a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/Assessments/sql-server-assessment.ipynb b/extensions/azurehybridtoolkit/notebooks/hybridbook/content/Assessments/sql-server-assessment.ipynb deleted file mode 100644 index e9d2892626..0000000000 --- a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/Assessments/sql-server-assessment.ipynb +++ /dev/null @@ -1,164 +0,0 @@ -{ - "metadata": { - "kernelspec": { - "name": "powershell", - "display_name": "PowerShell", - "language": "powershell" - }, - "language_info": { - "name": "powershell", - "codemirror_mode": "shell", - "mimetype": "text/x-sh", - "file_extension": ".ps1" - } - }, - "nbformat_minor": 2, - "nbformat": 4, - "cells": [ - { - "cell_type": "markdown", - "source": [ - "# SQL Server Assessment Tool\n", - "\n", - "Performs a best-practices assessment on a local SQL Server Instance. A single assessment may take some time, so fill out the variables and execute the cell that matches the desired environment to perform the assessment needed.\n", - "\n", - "## Notebook Variables\n", - "\n", - "| Line | Variable | Description | Example |\n", - "| --- | --- | --- | --- |\n", - "| 1 | ServerInstance | Name of the SQL Server instance | MSSQLSERVER |\n", - "| 2 | OutputDb | New or existing database to place assessment results in | DB1 |\n", - "| 3 | OutputTable | Target table to place assessment results (for a clean assessment, use a new table name) | AssessmentResults |" - ], - "metadata": { - "azdata_cell_guid": "86ecfb01-8c38-4a99-92a8-687d8ec7f4b0" - } - }, - { - "cell_type": "code", - "source": [ - "$ServerInstance = \"\"\r\n", - "$OutputDb = \"\"\r\n", - "$OutputTable = \"\"" - ], - "metadata": { - "azdata_cell_guid": "db21129e-9bda-4db9-8d61-d2b264a3cad8", - "tags": [ - "parameters" - ] - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Notebook Steps\n", - "\n", - "1. Ensure that the proper APIs and modules are installed per the prerequisites notebook\n", - "2. Define above variables corresponding to the SQL Server instance to be assessed\n", - "3. Run the notebook to list the Assessment rules being considered, perform the assessment, and list the results\n", - "4. Fix any warnings and rerun Assessment API until clear by reviewing the results table" - ], - "metadata": { - "azdata_cell_guid": "541f6806-f8d2-4fc5-a8fb-6d42947d1a64" - } - }, - { - "cell_type": "markdown", - "source": [ - "### Retrieve Checks for SQL Instance\n", - "\n", - "Pipe the output of the Get-SqlInstance cmdlet to the _Get-SqlAssessmentItem_ cmdlet to get the list of checks and their status. These results ought to display a list of rules for a default assessment." - ], - "metadata": { - "azdata_cell_guid": "c6f94c36-0566-4963-acb8-4a419758d26e" - } - }, - { - "cell_type": "code", - "source": [ - "Get-SqlInstance -ServerInstance $serverInstance | Get-SqlAssessmentItem" - ], - "metadata": { - "azdata_cell_guid": "420e135e-0190-476b-812d-f716ec619ed3" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "### Export Assessment to SQL Table\n", - "\n", - "Invoke assessment for the instance and save the results to a SQL table by piping the output of the _Get-SqlInstance_ cmdlet to the _Invoke-SqlAssessment_ cmdlet. The results are piped to the _Write-SqlTableData_ cmdlet. The _Invoke-Assessment_ cmdlet is run with the -**FlattenOutput** parameter in this example. This parameter makes the output suitable for the _Write-SqlTableData_ cmdlet. The latter raises an error if the parameter is omitted." - ], - "metadata": { - "azdata_cell_guid": "aeaa588a-a3a2-4bc3-9b4b-794427b77649" - } - }, - { - "cell_type": "code", - "source": [ - "Get-SqlInstance -ServerInstance $serverInstance |\r\n", - "Invoke-SqlAssessment -FlattenOutput |\r\n", - "Write-SqlTableData -ServerInstance $serverInstance -DatabaseName $outputDb -SchemaName Assessment -TableName $OutputTable -Force" - ], - "metadata": { - "azdata_cell_guid": "28ce8df2-1da8-4462-8e91-62646642d4b1" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "### Display the Results\n", - "\n", - "Use _Invoke-SqlCmd_ cmdlet to execute a short query to display the assessment results in this notebook in a custom format." - ], - "metadata": { - "azdata_cell_guid": "44bb8b3f-b456-43b4-ba2f-ae22c715a984" - } - }, - { - "cell_type": "code", - "source": [ - "$q = \"SELECT TOP (1000) * FROM [$outputDb].[Assessment].[$OutputTable]\"\r\n", - "$results = Invoke-SqlCmd -Query $q -ServerInstance $ServerInstance\r\n", - "foreach ($result in $results)\r\n", - "{\r\n", - " Write-Output \"----------------------------------------------------------\"\r\n", - " Write-Output $result.CheckName\r\n", - " if ($result.Severity -ne \"Information\")\r\n", - " {\r\n", - " Write-Warning $result.Message\r\n", - " }\r\n", - " else \r\n", - " {\r\n", - " Write-Output $result.Message\r\n", - " }\r\n", - " Write-Output $result.HelpLink\r\n", - "}" - ], - "metadata": { - "azdata_cell_guid": "5bab466f-2ea0-4c3a-9ac1-923ac1548dd3" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Next Steps\n", - "\n", - "### Install SQL Assessment ADS Extension\n", - "\n", - "Consider installing the **SQL Assessment** extension for Azure Data Studio from the extensions sidebar. Like this notebook, it uses the SQL Server Assessment API to provides a mechanism to evaluate the configuration of SQL Server for best practices. Simply search the Extensions Marketplace for \"sql assessment\" to list the extension package then click the green Install link to load it into ADS." - ], - "metadata": { - "azdata_cell_guid": "b3bfb32f-9dcc-49ba-bf22-670116e4a72a" - } - } - ] -} \ No newline at end of file diff --git a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/LICENSE.md b/extensions/azurehybridtoolkit/notebooks/hybridbook/content/LICENSE.md deleted file mode 100644 index 6236026fa1..0000000000 --- a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/LICENSE.md +++ /dev/null @@ -1,5 +0,0 @@ -# License for this book - -All content in this book (ie, any files and content in the `content/` folder) -is licensed under the [Creative Commons Attribution-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-sa/4.0/) -(CC BY-SA 4.0) license. \ No newline at end of file diff --git a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/data-portability/AdpSetup.png b/extensions/azurehybridtoolkit/notebooks/hybridbook/content/data-portability/AdpSetup.png deleted file mode 100644 index 7d59bd976c89edf68ac7eb895ccc188ac2bf5e5b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 125871 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�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 z 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⁣wRn+)eQxvbooZz_Ahenk<-wNNQ4Ne)M@3f389W+>{LV|A`Y8#f>e2B zHKs4fM8A{>{X~s26MoO2pXT^$laK;pS?WZ<21IVI-2G?D)~>F-)t}2*jJmn%P^4Oi za%$Cq@oR@NHWSjy6N9Odd`N?s%h7t?Ar1~6Sn3WOG#~POW)*tlt}_d<~qA?RIG;REb9)U|W_CX08c9YC^RxHbd~Unfd?B!t&R4;`0ZxkkY9NrR81-4DiBx zvjb6G!jiF4M|yRqoeOk6sl1aIG%hYfG;#BoE*b%uRY(1wX9wfjE|!V|Zg zY)RW7tWgcZOqO!@f{oI!+_gJKJBh&fu}jBuP`0N-vsNy2TgsehtE+#Q~@;rAjvn zfxT?M03a^B8U-Tf-Dl}@gKFUQUxM(^MFjxev%E2Bco{m!Rp}~kNA|8ju`~eQ`6*bx za~~^qRB~VET3s%7eTE;l=Sn!gS}fS)k6su_mYK9^!s1C_00nlCQW!6rddPD+-_J~w( z4jO}1fLg*F%$Cv(8S0UW6pjuQ4A(IH#?(o61B>}qUc)1WDNs_(#7k>!Bqo0K61}Y?5 z?#kwZoK{tFu?w>}4TNZ^H|P)+Sc@zn)_7?=4DVKf59b8wvaV6#Bx@mMNtY|VafP{c zJA6?kA=+Le)hBR!Ok(fdgn-T$yDMRBj{isPYFSZ7*+-)Rvi*d}3dI5^aB z2;IT&gOJ}=Zc`dk1Aif6_HIubS&qB@kdJFmDMD}%SBsJxO&;co(^e5hW!?x46gC!Ff9Z)2NvTAq7-4Y>>}W78gSF3*#KG$+rzS##*xmA|~UoQ^C zs3CpPIU&e$X-jZlG^>WTIuf@gR|mCxVRc2pydltEorN+&E}0a0uPOK7)^ZXn?RF zcQ_OBeS2mglBM@-6QnM)ECdUFspTU*W6=WKY~}nTN^#E7CFmT-uDtQ<=UaHE%7^^3)BL&hJnIye__R=?J`bfjX9g1=66%yC1bzWGLm4eg9 zx9y%Li`F)#0BOW#kpNFwB!*&)u_bCOs|i{w{_7I}jRC!Kd)0m^VS4VMK1_LO=}7_E z#)x@hn?`Fwje$r#>dCMXBk>-lleD&P>hgM9{fPP_Tda16OD z5kvnLhp{~>Fm*zyxR^<9x4j*TA(tnbS?==#bnSJ%hb}G`1t3RzX;vzTS<)jO_?I~J znwp5`KMuj4&nQMja1Huik${0y6Y$tqp?LVB0tAqNh2z=dW)I%(d6P6q=9GCy)Mzig zYg{c-yH$zI?7pV!P)k}lMcx>7TPy}j+??gUHQiTLQn|Dh`2N0YQYD5>3-TpmDfWhR< zo+xF4?wJ6bowN1~Z3da5G|=L zxgn0{jC$Q0o$tuSQ0}ft`{;&k->}WZ(MREjSxfQaU$zSr=C-{9p!B&*Cn5<~GapYkwL>|C|XggvI8*9TBwCqtG}p{@%o&fi&6ZdYvFQXFcjH>mPaB7Q zHFv&u39dT!ZY0NaYmqwe1k=bZ$-=)szX|uB`-U_odGkvunz)@BE$*5igpQSXODl>| zT3Lkbl3mEi--3{UaCD39hlrp!_516lFA?D5xzLdv0~@YYNQvoPmb(_c=s|#$nRHbN4AOhR-Q1$1P9H#8bD7 zM*#Pfw$QM295~nuIGeq;zz4O3H1c2w5t|KAL|)&G#4B#KrE=~;<^=7v(rY$;#b~3z z@@vbA<=omx#TAC>x#AK#!;!RkPB+d-=edVp_FyzFJ8wLvoJ6f;PFVgeH$wA6S)*hw zA*5ZkX{$GW+1RPsWZr6_bx6pOL@tJk#wHD8JZ%;%PYmAF(7J>D0L#-lx`)?Ix$6Dw zvPm0MdgRr$Ralxn6Ynp60?*I76Ynkj4_5A+jUD-$P+nE0{)nyFE0LDF*2AoEnJqc1 zP*j#@`2_NccVkQTDs0VJ-Oz}9r!*LX+D@wW3jf zG>oU9G~4nC>@3)-={4q;`O7ZaAmR6!!b*G=>NTN1!6nM z?a>68wR`Z%U}K>vG8ZLVQ3+i4Vj!-+^fV0U7HPTc9j&g1K2Lh+fl<;!pCXFU2(5#F z^kIW%md#4sv;)`+qKk)nJA{2@2c?Jp=J=((=8#0$^17~1|afl2yBL&T^KbCcC@1Z+{-p0^!gr14pp8Ohx zB{jJIf}`;I=Zi3^dm*-Ed7(%5eptS#2>w-D5E0^C=hLsl1$-(ZLw#}nxN_aIy&H+xMWy39b^Ux{@JxB<>zYx0EX(+EtAB1%-V!K1%o`=}$!@jzMOX&K( z$(0V>>z1+?K3o1gGK8qk9Q-FFTEo;1_6+vucNT8bVg2URAukzp$ zI(LJ2kCN!oR@+t&JijaxLB1gf@()$JgT14;JRi4zc@7>w^INs=tsYtK4xu}Q-lAwJ zLU*utTyod9xa^2@OdML5&^a-$w9E^4zZZbhCZB*K2E|)0%|~k|bo1kJ4~f;4mRvt4 zU>%OI?}CF(=w_tEf=gw4!^=mESnbA@gMGtW3m=7_cL2^Aa{XTGkwxrGigVzgjiT|k zF12SZep%&<*rb6N+Q+E|qB<971>C zpxF?m>RW>k=7ytZ_ulByIl_{+L+pjj>)H%FW&7er?9F|D;Tcy;$9+&?6S*3#9>wmW zbo5Q!w+WdJ>Y{UWZ~ZK;J-{J!2M(GEPJ~;y#usUMiMaCg9+tEnf`p;m!6A4#sYijd zee&xLJpIWgm!0OvMZ0ms6U%V*Ba3m(zn5U`Huo^*+tbr5n&}s>VIM z&*S#YGTi*k3S9l@VqEs%LQI>x%jLemVECmQ^YPlWt$MyYuN+T)v=KYA%YS#=2M1Oz z>k&DPXZ|CD_k9kB12^JAI-syD*Rr*39CAVr^!Iiq#O(vZLmEzob8z6HpJvJb&98^z z&le8|SJ7;*;HB+}%+q$2VDY*psb!Oc%C$IN9} z8fIO3F)lo=Gw!;qj}U4ZK9dH4Ckjd`5#Z;IyQlWSLs$31xPdV$w{x&RhTz28joXX$ zJU%i2e>x)-2{C4sw8p|TW)2zbZn(fEa%xIWkgGFEZa*+_1xPEcZwpJK$_1Yy$NA^_*QO7ouFbxd20YM#?>xsBUk4w+)HWfjDM( zoZ5d_n2B%a?ZTax^~O`uGrnk2XC%i5;fq-r*s!fw?$hq-k64>lgpYpSj+@TuuJaTE z{d|=8|MBKpJbB|lKepF`s2BqhapKw{i%;P;-xQ5?|tgYDY#zzuiI9Hhu>NU zZy(^dWjXji$@n_tB?(3+Q%o_stOv$Xfe$&U3@z|7UmTd3H zE-1%8USEq}rQCddywEW&2v?uh6~_%f_`Uqfsv7vU0zI`T?I&!^S%I?$?|G&>>&2Pn zz6X$${CvHn*XgSdpkwxKnj&)t2M~U>n7Py!n{r}Xgv?tj%+?!CgKi~4npyef=+P+@ zH=f-cKMHBvpi-*#ScRx-QZT}TeNiS2O=9VWd@Nj>hq=*2-*YL=g*P8nYNCJm?DG8+rlosqAg_YR0vlP{J_EX+7c1ha# zMP_t}^~3yBaUbOUK>rS-aYElqEL+>gA#Wb(gd4}~lW}nH;OkS1iUQ!P#bLPYv{9Dy z@WqbIG9iRq{IWC))93HTip}|0xkU=75fqkIAvn-S*GwSM^Bw!=zhz)}?uHUgKEE9$50&&2o3hpMSbYDBeN7Ki6JgO zM4>2q_S^{$>ytpZfyuUAr5N7Jy;N^fOc2&@FV+S_kK|Cqg!>^}@*N));L5|Hg1HuE z!Zrfy0;{B_w>L2VU(MR7a|yW98XHEoE@2NjPOg!o5!XMy3@?7M1zR%AD~hI2URjOD z-`b2fzutk8GE=tu3f5OhkgwLo&a85**;0hceIWD?zt6yemHB#JA`Qk3+e%PT)%@_D z@b?WusDFg(o@vWC^y3{49JEqUb#mL(Pv-cdNB2G$-M>A3bH0CACFJer4*sN)1EfT<1gp+#J!jI!RbeKP~}-ifr_P~vPOr-ZII+k zq)|X*WR+n@wtIsSdnABI`eQi0gJ(~au>FMu`RJ$?L9!<3Hz?2>Yqk}so!Gj&7@gvR zP$ThGS;WE~qi22&9hh(XiUg7;d)eJy3B$}uBlvOQZanb%8Z6tGkBg2^#(%CGfKmOT zkS)D(zJ9R5r~;+lSfp=%zYTZ2v|1-I7D@xCG0a<@hm;OMQm)?EvV+?uOT$7MO4Da& z;->}Kc;TZo%v_wKWy$)P_FE=?oR_8Y*~`r2GrN(*dxj0Z4pR1PXyi(7{ngLY@xsSx z_-TGN(lSc%*4Nwd(kI*S!SoDd=9XjS#zJib%ojp``Ke4@Z0iioGOHT?!-=Nt~#YFep`{Pz27qFx&7;{b-3go3vj``^U)E=iWUJQy*HOJ?_4p!?AE(9{&F93am>j5`Q&1*JRR&M1%_2pLxd|oOS11^y?aq zLER&yH(w+1nO-O3X1@2K<9Og8d0^3~;e}URrxx9f<#WRp_jI^ zRQwkx`M*0Y1JU9BIC4lddUOs(bc7#{8XAqs4$kM@`d~Z5f$!-&WS-7C;n(( zzP?Dq&sdnFM1Sn4Sj0s7jN^#7=F;<~F_}$<{q*^Z@4DZ)nl!qD0JzT-tDlouTXQ_`H810h9iqd@f)}aUs z_R~pkZ{9T=Puw^VPycx^Ef0Xr23Bzrd^wJ?%+G#i}``}gm z@Y3Ih;%^u9)N>AjcTNbx{a5s5P(+3L<32ej2}DUx@1Z~T!~gy|82`R*Ai5@+clfX0 zF&wuFi4Eu$fxlnW3mswuwKxB_3wq#%TZXFP#4vJ9Pst-E@ev$_c<7pb8b@RKKA14+ z%P&{zlZ%mOo#8d^2Y!PQ7)uPCT44z9U|{ zZ79w{#;4e=t!spmHy}O2@lhkvll>HAvz7IUN9v@EMsqf0eLiifTSBN}7(g{Ydln@;mVp{8@ z5pmkl@kr?qj3Wj`p;Fp?TZGKLYP~Q_(jU+x93%RL!&@3JRn`!FMO8UUDhu_zt>b_b zx=k=VI_3Zyk8*bK`;l8LO0BpDP8r|Tk~jzv#mU*#_ASO^XrP&8OQp;qc9JY7U3vNo z@|SRAq9#B$`tbzAl6a%T{B?31&k2%_-B2L4(T$)zP~xC}8umCT3k&kqeECWmbZ2w{q(BMh4_A<^pXoJ#D#MXOj-HL2}zUZ zkG``GXWlUvtJ4bcmvg)0!}~{KM6W1u<13G9?NJjzghia z)=qst;^4r7EzZ=AAvkJKj3~cWqKMis{&3&-_{X(lkr*3nNm>rA@w2svplBRB;LH|@ zv%SLRfrI(knu(vk9xNoF?aFsXimVsYW5&N?c=PyJr~dMh%dS6w0QB*`dg?2H`{jVn%0L2N_-mTfG+N8jzlAEqSh zC-r9^iuk3-j%k!{ldkM}vJ_#>;yiF(Rak!ns>X|ten6o?=M-7X{j0NVs{I%zH z!FRK>uq(S96Gz0LT=M(#{9IgcLJ}6PDbPm1xswvHbX_6xi>h$hDIM|ktW1=Z)!^(& zNm#NjAIsJiVamirom9Cn?MH0LUXF8z+_=ZOWO*O*Ta|e@NH&?)?1kykk0HRNrXKUXhKrrl+GzVh{$UL<@;VyA}gW-(8Ar zJIm0oTPU7>cPmB@io(_%r8*2gdO(ym4jy@BBgPJi*7xo7H=LX7 z@(1+_*H75K{AIUJxa`^?7-J>ApBH8$Bdc8JR3yduqks2so!h|4aA6_77}-ApTQf`^ zI9l`l+$^2+7A9nU7uTE6tgu}o6 zZv{U7c{`31zyG+hFCM#o0J_8n;m#LV)5&q1L^x?oY@L-oJBgn$1EchHOHoNR z#t)0aWv6yh8Astcfsqpp*^{Q6WKIP5dn=g}$%%nV=0s$eA1*qnBc`63ti5^??s22z zaLLJ?aKgAa4DJ=7v)SqIyvZGK(vk7nqvwP>`_&64#4)Wik4;iCC;Fy_DVY--#TeQ< zLgQ|4a0uOjgC-E*?{4`zaL}qDgGt@B&-MeFkPl^vJI<>z~^ezOfXJ+%Te7Vp+s|0GK`1?*4y>VpJ*-T7T4edfT8 zpa7Ad*TvP4Xtii31Jw?pJ6&C6TvKnnR}mEvh9J$Dv>0>>Y>G4pf+8XfDk3quLmKH2 zkd}~??rtWvfpiWST^qTHmCXE`k+Ql-L~^{J6Z*NM8|-659SOL3s|hE={sHmTAL%f zZRDfZ?10Q0O(RkMSlPWr+TD+agE_92dvx?|_IovtHdf$&>JVINzB3x{za4cvLn5In zB-91cNlX8td<;%;zPFgoICG?6&Ck_Y&27qYX4`jHIV9|MRSqTP^;DO2YntQA`RVyg z__SvD!EPGA`{0Cp`kS4s%|i#V7N2x?S@Tr8H;VBv!s^qUoyX&YUv4-@jrR6~n(G0a zd1aG&q^6}7W6DC$f^fR2HA72Jr7JJ>>Q#F1=#LhU->QaADe3nRWE8Xc3nNL46i{Bk zx}}3UQGGXj3pIV&ElUtcb-`Xcc=`E}jn^*uJ2X(fE(}|qa)?Y$jqN<^U^}cKBPzqm z19r(u?jRV^W~YihEu1+ssVB2vICGdIp(TSZ zw+-Ti&BFPE!l=)xI8|%E&PE#sI9UV^ifz@>nvShrYuYntXn}lrd?99;lYRn?D0aUI zBY6_bq*X!P$9pb#{$#p1yeT;$xRhHcNzfJx7WpU<_o+kC<0|bn$R?8PNozI-9}Juy5^LN(2TyTxyZiAvfvf% z`K5PKe6nY@bm&kq)qjBN_hadI!Y(XJArs)fHQi9QybnAzt0gs7*32GUhi1V%rj9<9 z&#nZ^CXY~Ck(a^0Pzqv;7IE4h8%dSWDd9DDWVz-z)J(tye3 zZPt<%{OwZb{1wRCNp$9G?@XMTN!pxa-)|#DiTMtd-&L{tq5W9hAOw0g2YaBrX*fUM zATiKcYV``h?Hg1&O4=eorCp_AY9zi07_^Ko)V*@n6HTd~KKMbc#uao13#wqHji>)z=2R9Ny7NwEhf9DG{M3Rvm7I zcPG7T5QH@52q|qB7YD8zquUn6)W2`A(1*wMa7$p3Aa-NxO~X zC+nIS*5-sEnkEq_``YJDMtWO;{p#D{**ChE#@F6%Iv1sHHH+V{(JZQ*)v$I?keYD3 zTR@@Ar<{f)JRk$!qRB>8D9N4Kt}FPej+civo3e`V8d3t=8r9` zUg17_7d<9OAzL+8*QJG*<=C?R!0RdO{0l{d>`fwBffMw{)tz;RuA1w?Lo?h~@e|Wf zJq#45Pt~L6niMyi%zkvv=BTcPxj3L#-wc4IClr=~nLBORmg!;2hzx%9=f+=)b-vgg zm|5DLyBP$m`{OzkE!LB$3%3lKU|u}C&A9;KiR!cxlB294eRU)-RRf%HUuKVm^Hx%Z z=bLNZda+@&%(rbrQ++NDY#gyHIc^)nl5%)CpPevALT|N^K)pY$*@5k3=?Ze3e&Q-6 znWI{YD4^3xlCm9w0+v<`ApK!_nWuqSbpy>5n^@9TSfUN&LWGNUO-;?F!bQC-8}Y42 ze`Jn=ABb;}=FrJd#T@(~tBbZag?nC*3QCfy7FJDn>1szZpHf3Y+8xi7p7J|<8xrW! zI+y7&jM9tYm%&FgbTXmKbyTwhDDLd$)_iul3QZ{|*K_{94$R9=3B@g_GK(N}psEuu zjVO7x>HiB;z@{W|E<=F6bzfcxG5A398oeXmvVX8Dotf7kf=4i@3t;%H6;)G9&%7k~ zpe>cV12uVzX8}L1%sR`9cgc-=IU$PG;4LDwI%>*220BMW#jmD#n+VI1e^aobu=FwA z;KfF#+wC$sc&fk0J#}oFgjQe=+}S9;T!}5ITMGYhrXA@cA;ZW#qH_1{CP(wdy;)b2 z+oz5c-G`F8w?KQ{#fdJRW7H7L2Mrjz6}I&5V7@$&a6I>~ISr&MJPTBZNiKP9HJGE!Tq|wllb@!)A?x9K1f8&vu>$akS>&5SMPEo{%oPR++H8Fklwz zh_Cwj45E|D>CjUzPH%7^)_ot2c?Z z|KC-!^MCUxn&_5%v<%5?X5U+g_p;kzELfiql|`3&@D=wNO4ceyP~s7ciq38Sn$&>^ zF1-@UX{LW*0C4V#h4y;!KYuxxUVE`zc4UExwVn9bT#X0cUCOh3g)@*e&(vU`4)e%8 z?ujsWQ4F5!geP`O>a}zu{Y1d4@U4F-6mS$y-&aEL95&y(+g`3vM&G4Ky09PDu+Uy~ zn&q-4Fz2h)50?0sCz&nKfk;PGi8XWE>_YB$hQp&ec?wkGPW@G^2N zg0&?V2CehCr_P)hQrP!?nXZnWbTVDHkeJB-E-tLvBG3uizEJ2?RC6zX<}by`pFZ)h z%Y#2p&P_W=9DAn$PUS-s-HGR(6kI#fhP4eYR-a~U`sV)IJX({HNYnEHf5a2U?#ai9 zZrMXmY!_VE4Ox|-oO(7M_vcYzn|-;1NkDuG7r&)L%ALEB)64!PrMluY^GR0E-m1?(pyNKhrEYaI)2e@vyMQ{V-+ynQ~3Ku5}gtf zlN{qah5_aPS~?L<on zmxTw&C$2d2a81`m{ zMBHgRih9zGQmqiMp68Q3=;c0Z5MefH_XhR_OXIH?+ zOEXR_s^ypcX+)((bIO$IuVC_eTjLR|y=;^$NXpT`GGhh!|Lyjn z`A~fw=%a1HbS4W1f%IpsRE`?kaF%4Upf$r|H0ovNH;mTi{JRrhyF@DOu63(vD=@*}A-YP}8Y<@F1Y#yex5I8%Zif0&sF}>DU|1JE?V*Uhb z@Qo-G6S2)4u?v0?U_9}tUAV;iuj@JkeE?7@Q6c21JI-)~U}i^CGJ50tG3ZAD^TjU& zkkSntZ>=77-HeSQ=Xz|OV0iL}eQq@9WC7fD10JAOXK)%;YfiEXu_*FfOD=$%rH znV>N}avXAw1GlwrC4DyjG(5g3Y6s%)TX1*KaJ~KcRyS6+--~A=F5R1X7e+k#6g)%2 zkXM=DanM%5PI>r#SDrS|^@8Xj_^~Tq>DyNIga|`!Lk&|{?Rhwbu4=gqJUdpw)%bhe zRDDTUVe-SAp5xB0^tCoL&y`2Qs}HA031X=m$ht`-38yw%`;4y8izhvbsu>EJZid;| zy+?)pc5)MON6he`bGselyTp2imI$lYeV&dH8$|&5qsSTA30i6`@GQ%;|8fbtFC;

jAeax8O;&eZD8N9|t{UTtIREORv4T2LcCkGs`cEF6t2!I5_hHqDLFo86dw+P0n0 zi#;?K)#xLnYhPg>u6K5g3NporIFvHWSTZHh8T2>*YDe)*_SIox=Ls9%vTbL&WymRdc8knHUX9U`@0PXshnE62L9Q>-DVvN5~+`Jkh;HgPiQy!G}(B? zZkb17^B+k@tb+?8?s?Pqv7Oml{P8^9qO?vO(2Mg|BTEDvrQ;D5l6H>NfOXn>bUg`y zNcywlZxi$juWN7Gz+%_AwpK9nm+CG`mTR7VRLlt>qEd9au`>Q{dwcusI3$hW$Ds_{ zc3g_x@xoSM9e1|n_7%r4=GnGGs3c7jk7f+iLZe$tqu6vyN)p&P z)!fW3Jh~|4-zYVm{<2XES!b_cdr1KM$+79c;gz*=uEqoD2W%Usy4iFs^B3#1u-}@k zBH_UlSNtfVzBMD;El5h<2$?7CSb|Gbi~`jEh7gtYz5+8!N4`#f;IyWv}&C;x@Ch_ z8eF8F$8rm)D?#!lg-2qq0Y+DvGkp8NiLxI^9!cubTwE1Pn_KJ&L33}WOMuF_p&bzGIA!*fRwgT?U6&B=$9 zf`S~MQX`I@(jyL~i+m+Z@NNvFcB#k7z~GN&;peLhIEh{A~F0M@P<)lC)U={T(ljofrDjIA;(GNMvn_t4Dtw&2Fs zQj{AMhxuWjJZ;_^D!{f1^X#>=kmOoU$&Mfb` z8;OAFFU>M{^Bs_a;G;NT7j-Qrew<>kTe)+1*X8(g6fCc(=hzqdy^DT1NfaCc{4%ZTP5p;f}s5+H;oBC1f?P888`g<+^G%o5&wM zJgRv!M*bhhBm!fRTnIWpM#^vV>Zp<|-XN25-2}a>E`+X4-9TXwCME2Kps(}|1M43- zvpr?M#S88*komgGUMfPfosa>Fw?Q&J%FzupXED z11KrYZCikLr zl`;+PentN#$6|^ZcMOlh%nJI*s^~*7wqM&0E680IJL@lBc)AK98BT7-OC8zcemcFX zVlEwUy=CyL1;yf|P5#~INsHlGg8E}=%jL1B%5bA+*kX4reUB^RJ8SrDPYpvz(^ZK8 z6vbIu-tg5$p5jgA>{vk)S0O{<7g4(6IqP~6l6^TukV~|}^T5N=%A>27&BiP=r>P~C zK`=dBq3UYe*N-uJsevf^Ccg`?BD-M4)>VE;Vd#&t>#5R*T2}prQR3d`QGH1zuf=cD zIWy+1+4Rf^Lja72K2>5_)`=)%oI3XR2RgN$sbJ4buZbNj3ug=8g+!oOnfymK@kmmZ zmu|bIFL0xm6kqmkI}SOwdu8!N?f8xGICjdQkexLKfrBkFvi|JDFgMOmzt09@&!ExL zSuZfQ7k9;tPiiDxkBN<%uVRZ%?Cz8~BZQ>S=fEL!eqc0K-QSTk#&YST6*x2 z&b|1R#c>zDw*9L5&GC0o@Q6@Q#+3^C^MNX<09Ox7l=L51j{K&lxgnE1x$rQ*TOBgn zLRsR)E-;Ws3%7UB;WyT3H}kp@H;U%@X>Om>|BH9XdZosV;JndTXR_~)!&@`@WePni zc+fGSI^VNhjsB^s55J=djw0Y96Sfxp3zJnoEZf8=jN~ogQ9Pi=I~-a(bFh7oj?XSI1=Cv>6jZ?e(fw%s;GPWg{liYd5TPqPOq> zv2`yyyx;>GRf^?%zM61 zADI{^-_w1ncIHLDhadc`reYW)he5wGuKZjk!jsAQ*;z9+5UU7s1dyD zH1c<7P32{O+)3dMd5l3BjpDK7)E~+=nH8#2wlOtPPcf5s zdtEmU;@`Ef_1jyp*qJCZ41Ik32~MDNbcQWB~y8rpq^!lU)c5gX6;l+)+%-D`~hDFDl*Qi-_zA6h)wRr_Wjo%*TDP;I3)I?G zidn45_MEZoQkZVYW#%!7JfIMhm=wq9a=K~go$|$IH5$v+W@~%@9Qt%Ctv_RRRVS+~ zhx*dnB~D3=IWPY=lP+m|zReAkx3C9~f`KT>2z_$qfQ;|(3cdFI(lXgZ?-l$C+&eK6 zaB_WR4mmjYjjFBXJDE;Bj|8)%FBG>g;`Q<(+p0l>ZLv;Cg)EzBLC@O~ZsvUlP@tkxR0fm*F(xfaXzkF5nL%6eUiU4e>F=^L z`gEsO!nOMTbmj3!e7e0`Ux^T`Sj@GX#8mtVdd>y$j4uGpdH!*k1PixJ2-^@tV)8s zm2a?}6ydA|!?)JlK>3u*Pw1gySS4%4K4O!w4O6!O!~FK&w33rtYXCX!E0ovdLBfp= z48*GI1^jn6GdkB~x@m4Q$sp*t47(!?&ln~=wv(hGzSs<@HCx?h0Y+rquYM7->IM)# zQ=LEDeEdEoj5*_739$&Y!Mb=j4HkKZU%mw%J8|q$=#smUWv2f*`vZ6z;`g0kNN_u% zZz$BAAk5!%EVctWj3NQ&R>=4GC%OC{AOz?s2U8BD&266TOgsYd3S?Fa3uMpoU?tT7VA^++7O zY%78AVb)0(o@1V{-c(f>Yxym}IV$ zIx|!|@wS)4YOCg(`LnS#771G%(HLfF#OFzYe&PYiiy)BP_!Tbo8D*Se7A<6v6uwJe zELHBsA#?ou%$`dL#)c-fJfLD)4?Bq32%u5mS+Hm8T_)V$_?QS(85%AE7;}j`uv{Mz zG3?{<^vmQb*`~82I?uy?XRtlb@N3Dp+7p|UHV_$s+t_(2>hs1x%;3gM8BpaWC}Y@o zpI#AH%3hpY+>`K-RqT?#ai62;&W0}6pMfV6JDSRbJeHV%@>s+4uEC>urzj`og%rug z=qDQx8P2!NRKQaut?7sY&oT0_4wGs$HQy@|6NP4{WeET3!ngBw@{M6XUy7geS)t~# zywjux0L3>6@nlJX4|Q%^T5{EAIL2puB?+|p(=vs_ktljFO4Dd;sUY~Nl&X96<1uFx zz3h_*#U0LUwx%hECBlKY^Wt=VyjW@B>UhB_*!7Ll*TW89jA9q2zLm0D$|b7nuTE6E z)9Z#zt+Vma+OqpbbMEmo4Y`~_4;CF7b;PI$NNMXtWy^jf^)lTX_6KeBChZpu_9K*R zF3elC^ht(*jc$pGoW8TE`SlYI%r}tOW~0wd%H!)s!*_VXU$&FRc`Vdi4))}NNh7F5 zTOk^u+kTY_MiH63n7!qR+JzJ@xFyHfu@99~ILXZ&x^&dvfoh{I_e;S{WeLksQKgsb zd8pZT&uQSWci0OLEo7qKvqRxHZX%51qTO}t?J)BgUb5$Pmt&&q4#SSAhqbyYONqBn zXnbXJpI?O%S}*xJ$;2;%_>`YB%!xDcdo|Pg=XwJ(eKinYRejpRyLF$EryU8vwM+Vl zhG#N{#7~bDzFk6PaXjIninc!?xwBBkk%pBiTBT@KBCjGRyVxv55fs)5bPR_yIt0)D z9+65Mg;1_j(c}mphxHd$W!fH)$_QG@i?RCn_=l|n#CjTuLvM_mzulIP&&U*3&MUq> zCO^9EmfE%tlMVEU%xb!s8su&N z{e}l2^O~D5F5c_@TAg-dyaw@AWYl~fl8bCD65b#DPQibLW%0b``pt2jLwTwXdVpY& zLzs$x&E|bR#Lvaz)s-sEeOD(v)^Etzh*RNamZE*gA+C8i`*nV<&w0|^ko>+v_&PY^ znT0fGNb7vs3%kBL%DUMv@0Mw`*KMIZXo?-iK-rBAhXCKbz#H|6dw_a@*Kvo651$Ex z5^TVxS$@f>4;05}sHqCEds#1*%0F!JziV-#1T;%PQF9+}K;d`R>G(G-ufjgujAdkq zN@{s(s?L&>jy&eF>D0lh|9w=td84C~mDOqF82s--LwwIR#d5q)3cPSj=8ptjy44eb`HYm)u>S-g zb8WBicG*%GS3#@>`fFejGkT~Sz(ICE|9uttlUM+hFxkN_9jY0nUHcTe4_%aibl2IB zoAuK_G0Nv)+`-r?#K|J!9nF%wKRF z_QXx;C1r34=LmN$9)NXz3gTWj{N4Xz;C{B(8t$J_>Jj$g--RXal<_?Q>DK>h_)zh0 zwPIgZ_}N9SG{0tFES_;;`sZlz53XUUB9XuX9|!OuU{zQCee?gn6$33?ULE7mf?pjJ RjJg86l;zdsN@R`w{s*H^%?|(o diff --git a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/data-portability/export-sql-server.ipynb b/extensions/azurehybridtoolkit/notebooks/hybridbook/content/data-portability/export-sql-server.ipynb deleted file mode 100644 index 2128b8c7c8..0000000000 --- a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/data-portability/export-sql-server.ipynb +++ /dev/null @@ -1,563 +0,0 @@ -{ - "metadata": { - "kernelspec": { - "name": "powershell", - "display_name": "PowerShell", - "language": "powershell" - }, - "language_info": { - "name": "powershell", - "codemirror_mode": "shell", - "mimetype": "text/x-sh", - "file_extension": ".ps1" - } - }, - "nbformat_minor": 2, - "nbformat": 4, - "cells": [ - { - "cell_type": "markdown", - "source": [ - "# Export Existing Azure SQL Server Resources\r\n", - "Export notebook that will utilize the ADP resources\r\n", - "\r\n", - "## Notebook Variables\r\n", - "| Line | Variable | Description |\r\n", - "| -- | -- | -- |\r\n", - "| 1 | AdpSubscription | Azure Subscription ID/Name for the ADP Resource Group # Both RG are assumed to be in the same subscription |\r\n", - "| 2 | AdpResourceGroup | Azure Resource Group which contains the ADP Resources | \r\n", - "| 3 | SourceResourceGroup | Azure ResourceGroup where the sql server to be exported exists | \r\n", - "| 4 | LogicalSQLServerName | Logical sql server name of the sql server to be exported | \r\n", - "| 5 | StorageAccount | target storage account to store exported files # any storage account, but must be in the same RG as the ADP resources | \r\n", - "| 6 | AdpFunc | |\r\n", - "| 7 | AdpBatch | | \r\n", - "| 8 | AdpVNET | | " - ], - "metadata": { - "azdata_cell_guid": "b72d138a-566f-4161-b7a6-7264487e446c" - } - }, - { - "cell_type": "code", - "source": [ - "$AdpSubscription = \"\"\r\n", - "$AdpResourceGroup = \"\"\r\n", - "$SourceResourceGroup= \"\"\r\n", - "$LogicalSQLServer = \"\"\r\n", - "$StorageAccount = \"\"\r\n", - "$AdpFunc = $AdpResourceGroup + \"Control\"\r\n", - "$AdpBatch = $AdpResourceGroup.ToLower() + \"batch\"\r\n", - "$AdpVNET = $AdpResourceGroup + \"Vnet\"" - ], - "metadata": { - "azdata_cell_guid": "417edc0e-1107-4a27-a4cf-e921f79b3f6a", - "tags": [ - "parameters" - ] - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Steps\r\n", - "Gather input:\r\n", - "* [ ] Connect to Azure Subscription\r\n", - "* [ ] Choose Resource Group (Read access required)\r\n", - "* [ ] Choose Microsoft SQL Server Resources to export\r\n", - "* [ ] Choose/Create Migration Storage \r\n", - "* [ ] Install Application + Data Portability function (orchestrator service)\r\n", - "* [ ] Install ADP Azure Batch processing pipeline\r\n", - "* [ ] Store SqlPackage.exe in Migration Storage for orchestrator to hand to Az Batch\r\n", - "\r\n", - "Execute:\r\n", - "* [ ] Check all pre-requisites\r\n", - "* [ ] Kick off orchestrator service\r\n", - "\r\n", - "Monitor:\r\n", - "* [ ] Check export status." - ], - "metadata": { - "azdata_cell_guid": "a9da248a-20f1-4574-bd04-7324e70c05a3" - } - }, - { - "cell_type": "markdown", - "source": [ - "## Notebook Functions\r\n", - "Defines logical functions for the rest of the notebook. Function blocks are combined in a single cell that can be collapsed for readability or expanded for further examination. Nothing is executed until called later in the notebook. As a result, this cell is a requirement for any of the other cells below it. " - ], - "metadata": { - "azdata_cell_guid": "b70909ed-1863-4882-bacc-a9956993268e" - } - }, - { - "cell_type": "code", - "source": [ - "# Expand cell to view framework\r\n", - "\r\n", - "function Login-Azure\r\n", - "{ \r\n", - " # query azure locations to test for existing az login session exists with valid access tocken\r\n", - " $azureLocations = az account list-locations -o JSON 2>$null | ConvertFrom-Json\r\n", - " \r\n", - " if (!$azureLocations){ #If there are no az locations, there is no existing az login session\r\n", - " $subscriptions = az login -o JSON | ConvertFrom-Json # Login \r\n", - " }\r\n", - " else {\r\n", - " $subscriptions = az account list -o JSON | ConvertFrom-Json # getting subscriptions for the user to use in gridview\r\n", - " }\r\n", - "\r\n", - " if(![string]::IsNullOrWhiteSpace($AdpSubscription)) #If there is a subscription specified by user in the variables section\r\n", - " {\r\n", - " $specified_Subscription= az account show --subscription $AdpSubscription -o json |ConvertFrom-Json \r\n", - " if (!$specified_Subscription) #if specified subscription is not valid\r\n", - " { \r\n", - " $currentUser= az ad signed-in-user show --query \"{displayName:displayName,UPN:userPrincipalName}\" -o json|ConvertFrom-Json # get current logged in user infomration\r\n", - " Write-Host \"Refer below for the list of subscriptions for logged in account '$($currentUser.UPN)'`n\"\r\n", - " az account list --query \"[].{Name:name,SubscriptionID:id}\" -o table # list subscriptions under current logged in account\r\n", - " }\r\n", - " else { # if specified subscription is valid\r\n", - " Write-Output \"Using subscription... '$($specified_Subscription.name)' ... '$($specified_Subscription.id)'\" \r\n", - " }\r\n", - " }\r\n", - " else { # if no subscription is specified, users are given a gridview to select subscription from\r\n", - "\r\n", - " $selectedSubscription = $subscriptions | Select-Object -Property Name, Id | Out-GridView -PassThru\r\n", - " $SubscriptionId = $selectedSubscription.Id\r\n", - " $Subscription = $selectedSubscription.Name \r\n", - " $AdpSubscription = $subscription \r\n", - " Write-Output \"Using subscription... '$AdpSubscription' ... '$SubscriptionId'\" \r\n", - " } \r\n", - "}\r\n", - "\r\n", - "function Verify-ADPResources\r\n", - "{ \r\n", - " [CmdletBinding()]\r\n", - " param(\r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$Subscription,\r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$ADPResourceGroupName,\r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$FunctionName, \r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$BatchAccountName,\r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$VNetName,\r\n", - " [Parameter (Mandatory=$false)] [ValidateNotNullOrEmpty()] [string]$ApplicationName=\"SqlPackageWrapper\", \r\n", - " [Parameter (Mandatory=$false)] [ValidateNotNullOrEmpty()] [string]$ApplicationPackageVersionName=\"1\",\r\n", - " [Parameter (Mandatory=$false)] [ValidateNotNullOrEmpty()] [string]$SubNetName=\"default\" \r\n", - " ) \r\n", - "\r\n", - "# validate Subscription\r\n", - "$specified_Subscription= az account show --subscription $Subscription -o json | ConvertFrom-Json\r\n", - "if(!$specified_Subscription){\r\n", - " $currentUser= az ad signed-in-user show --query \"{displayName:displayName,UPN:userPrincipalName}\" -o json|ConvertFrom-Json # get current logged in user information\r\n", - " Write-Host \"Refer below for the list of subscriptions for logged in account '$($currentUser.UPN)'`n\"\r\n", - " az account list --query \"[].{Name:name,SubscriptionID:id}\" -o table # list subscriptions under current logged in account\r\n", - " return } \r\n", - "# validate ResourceGroup \r\n", - "$specified_ResourceGroup= az group show -n $ADPResourceGroupName --subscription $Subscription -o json | ConvertFrom-Json\r\n", - "if(!$specified_ResourceGroup) { \r\n", - " return\r\n", - " } \r\n", - "\r\n", - "$Installed = [ordered]@{} # ordered hash to store status of installation\r\n", - "$countError=0\r\n", - "\r\n", - "#Verify if VNet exists \r\n", - "$specified_VNet= az network vnet show -n $VNetName -g $ADPResourceGroupName --subscription $Subscription -o JSON 2>$null |ConvertFrom-Json \r\n", - "if(!$specified_VNet) {\r\n", - " $Installed['VNET']=\"Not Found\"\r\n", - " $countError++\r\n", - "} \r\n", - "else { \r\n", - " $existingVnetSubnet = az network vnet subnet show -n $SubNetName --vnet-name $VNetName -g $ADPResourceGroupName --subscription $Subscription -o JSON 2>$null |ConvertFrom-Json\r\n", - " if(!$existingVnetSubnet){\r\n", - " $Installed['VNET']=\"Default Subnet under\"+ $VNetName + \"Not Found\"\r\n", - " $countError++\r\n", - " }\r\n", - " else {\r\n", - " $Installed['VNET']=\"Installed\"\r\n", - " }\r\n", - " }\r\n", - "\r\n", - "#Verify if FunctionApp Exists\r\n", - "$specified_FunctionApp = az functionapp show -n $FunctionName -g $ADPResourceGroupName --subscription $Subscription -o JSON 2>$null | ConvertFrom-Json\r\n", - "if(!$specified_FunctionApp)\r\n", - "{\r\n", - " $Installed['FunctionApp']=\"Not Installed\"\r\n", - " $countError++\r\n", - "}\r\n", - "else\r\n", - "{\r\n", - " $Installed['FunctionApp']=\"Installed\"\r\n", - "} \r\n", - "\r\n", - "#check if Batch account exists\r\n", - "$specified_BatchAccount = az batch account show -n $BatchAccountName -g $ADPResourceGroupName --subscription $Subscription -o JSON 2>$null | ConvertFrom-Json\r\n", - "if(!$specified_BatchAccount)\r\n", - "{\r\n", - " $Installed['Batch']=\"Not Installed\"\r\n", - " $countError++\r\n", - "}\r\n", - "else\r\n", - "{\r\n", - " $appPackageInstalled = az batch application package show --application-name $ApplicationName --version-name $ApplicationPackageVersionName -n $BatchAccountName -g $ADPResourceGroupName --subscription $Subscription -o JSON 2>$null | ConvertFrom-Json\r\n", - " $connectedToStorage= $specified_BatchAccount.autoStorage \r\n", - " if($connectedToStorage -and $appPackageInstalled){ # BatchAccount connected to storageaccount and applicationpackage is installed\r\n", - " $Installed['Batch']=\"Installed\"\r\n", - " $Installed['Batch_ApplicationPackage']=\"Installed\"\r\n", - " $Installed['Batch_StorageAccount']=\"Connected to storage- \"+$connectedToStorage.storageAccountId.Split(\"/\")[-1]\r\n", - " }\r\n", - " if(!$connectedToStorage)\r\n", - " {\r\n", - " $Installed['Batch_StorageAccount']='Not Found'\r\n", - " $countError++\r\n", - " } \r\n", - " if(!$appPackageInstalled)\r\n", - " {\r\n", - " $Installed['Batch_ApplicationPackage']=\"Not Found\"\r\n", - " $countError++\r\n", - " } \r\n", - "}\r\n", - "\r\n", - "if ($countError -gt 0){\r\n", - " Write-Output \"ADP Resources are not installed correctly. Please refer the list below and use the Bootstrap NB to install ADP Resources\"\r\n", - "}\r\n", - "$Installed\r\n", - "if ($countError -eq 0){\r\n", - " Write-Output \"`nFound all ADP Resources.\"\r\n", - "}\r\n", - "}\r\n", - "\r\n", - "function Prepare-InputForExportFunction\r\n", - "{ \r\n", - " [CmdletBinding()]\r\n", - " param(\r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$Subscription,\r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$ADPResourceGroupName,\r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$FunctionName, \r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$BatchAccountName,\r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$VNetName,\r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$SourceRGName,\r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$SqlServerName,\r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$StorageAccountName\r\n", - " )\r\n", - " \r\n", - " $InputResult = @{}\r\n", - " # Build Header \r\n", - " ## get Function key\r\n", - " $FunctionAppID =az functionapp show -n $FunctionName -g $ADPResourceGroupName --subscription $Subscription --query \"[id]\" -o JSON 2>$null | ConvertFrom-Json\r\n", - " $DefaultHostKey = az rest --method post --uri \"$FunctionAppID/host/default/listKeys?api-version=2018-11-01\" --query \"[functionKeys.default]\" -o JSON 2>$null | ConvertFrom-Json\r\n", - " ## Build Json Object for Headers\r\n", - " $headers = @{\r\n", - " 'x-functions-key' = $DefaultHostKey\r\n", - " }\r\n", - " $InputResult['Header']=$headers\r\n", - "\r\n", - " # Build string for Function URL \r\n", - " $specified_Subscription= az account show --subscription $Subscription -o json |ConvertFrom-Json #Get SpecifiedSubscriptionID\r\n", - " $SubscriptionID= $specified_Subscription.id\r\n", - " $FunctionUrl = 'https://'+ $FunctionName +'.azurewebsites.net/api/subscriptions/'+ $SubscriptionID +'/resourceGroups/' + $ADPResourceGroupName + '/Export'\r\n", - " $InputResult['FunctionURL']=$FunctionUrl\r\n", - "\r\n", - " # Set parameter variables for Body\r\n", - " ## Get BatchAccountURL \r\n", - " $specified_Batch = az batch account show -n $BatchAccountName -g $ADPResourceGroupName --subscription $Subscription -o JSON 2>$null | ConvertFrom-Json\r\n", - " $BatchAccountURL = 'https://' + $specified_Batch.accountEndpoint \r\n", - "\r\n", - " ## Get default SubNet ID for specified VNet\r\n", - " $specified_VNet_SubNet = az network vnet subnet show -g $ADPResourceGroupName --vnet-name $VNetName -n 'default' --subscription $Subscription -o JSON |ConvertFrom-Json\r\n", - " $VNetSubNetID = $specified_VNet_SubNet.id\n", - "\n", - " ## Create access token to source sql server\n", - " $sourceAccessToken = az account get-access-token --resource=https://database.windows.net --query accessToken\n", - " $sourceAccessToken\r\n", - "\r\n", - " ## Build JSon object for Body\r\n", - " $Body = @{\r\n", - " batchAccountUrl = $BatchAccountURL\r\n", - " storageAccountName = $StorageAccountName # any storage account, not neccessarily the one connected to the batch account\r\n", - " sourceSqlServerResourceGroupName = $SourceRGName \r\n", - " sourceSqlServerName = $SQLServerName \n", - " accessToken= $sourceAccessToken\r\n", - " VNetSubnetId= $VNetSubNetID\r\n", - " }\r\n", - " $json = $Body | ConvertTo-Json\r\n", - " $InputResult['Body']=$json\r\n", - "\r\n", - " $InputResult\r\n", - "}\r\n", - "\r\n", - "function Provision-FuncRBAC {\r\n", - " [CmdletBinding()]\r\n", - " param (\r\n", - " [Parameter(Mandatory=$true)][ValidateNotNullOrEmpty()][string]$Subscription,\r\n", - " [Parameter(Mandatory=$true)][ValidateNotNullOrEmpty()][string]$ResourceGroupName,\r\n", - " [Parameter(Mandatory=$true)][ValidateNotNullOrEmpty()][string]$FunctionName,\r\n", - " [Parameter(Mandatory=$true)][ValidateNotNullOrEmpty()][string]$ScopeRGName,\r\n", - " [Parameter(Mandatory=$false)][ValidateNotNullOrEmpty()][string]$Role=\"Contributor\"\r\n", - " )\r\n", - "\r\n", - " # Get the scope resource group's ID\r\n", - " $scopeID = az group show --resource-group $ScopeRGName --subscription $Subscription --query \"[id]\" -o JSON | ConvertFrom-Json \r\n", - " if(!$scopeID) {\r\n", - " Write-Output \"Provision-FuncRBAC failed.\" \r\n", - " return }\r\n", - " else { Write-Output \"Found scope '$ScopeRGName' with ID... '$scopeID'\"\r\n", - " }\r\n", - "\r\n", - " # Get the az function principal id\r\n", - " $app_PrincipalID = az functionapp show -n $FunctionName --resource-group $ResourceGroupName --subscription $Subscription --query \"[identity.principalId]\" -o JSON | ConvertFrom-Json \r\n", - " if(!$app_PrincipalID) {\r\n", - " Write-Output \"Provision-FuncRBAC failed.\" \r\n", - " return }\r\n", - " else { Write-Output \"Found principal id of Azure function '$FunctionName'... '$app_PrincipalID'\"\r\n", - " }\r\n", - "\r\n", - " # Verify if a role assignment has been created for function\r\n", - " $app_RoleAssignmentDefinition= az role assignment list --subscription $Subscription --assignee $app_PrincipalID --scope $scopeID --query \"[].roleDefinitionName\" -o JSON 2>$null | ConvertFrom-Json\r\n", - "\r\n", - " if($app_RoleAssignmentDefinition -eq $Role)\r\n", - " {\r\n", - " Write-Output \"Found Role Assignment for Principal ID.. '$app_PrincipalID' with Role.. '$app_RoleAssignmentDefinition' . No work needed\"\r\n", - " }\r\n", - " else\r\n", - " {\r\n", - " # Continue to setup RBAC, once we verify an assignment is not setup and all the resources exist\r\n", - " Write-Output \"Creating new role assignment by running: 'az functionapp identity assign -n $FunctionName --role $Role -g $ResourceGroupName --scope $scopeID --subscription $Subscription'\"\r\n", - " Write-Warning \"If your account does not have the access to assign new roles as Owner or User Access Administrator for the resource group, than you will need to contact your Azure AD Administrator to assign a service principle using the commands above\"\r\n", - " az functionapp identity assign -n $FunctionName --role $Role -g $ResourceGroupName --scope $scopeID --subscription $Subscription \r\n", - " }\r\n", - "}\r\n", - "Write-Host \"Helper Functions Created successfully\" " - ], - "metadata": { - "azdata_cell_guid": "af70490a-c6cb-4086-99ff-c3527b4315ed", - "tags": [ - "hide_input" - ] - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Connect to Azure Account\r\n", - "Run the below cell to login to an Azure account. Be sure to check the Windows Taskbar for a login dialog box underneath the notebook or other windows or by pressing Alt+TAB." - ], - "metadata": { - "azdata_cell_guid": "55f9b3b2-9ce0-4607-9a45-384279f5d16f" - } - }, - { - "cell_type": "code", - "source": [ - "Login-Azure " - ], - "metadata": { - "azdata_cell_guid": "4a577303-83b9-48f8-95ba-e4f74c7440c9" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Verify ADP Resources \r\n", - "Verify if ADP resources exists in specified Resource Group" - ], - "metadata": { - "azdata_cell_guid": "a7e4bef8-3db6-4e68-8eb8-5a1feb4b3ac5" - } - }, - { - "cell_type": "code", - "source": [ - "Verify-ADPResources -Subscription $AdpSubscription -ADPResourceGroupName $AdpResourceGroup `\r\n", - " -BatchAccountName $AdpBatch -FunctionName $AdpFunc -VNetName $AdpVNET " - ], - "metadata": { - "azdata_cell_guid": "8185f2ea-d368-42c5-9246-bc1871affc63" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Verify RBAC of Azure Function\r\n", - "Roles based access control is a function of Azure that assigns services to a role with a specific access scope (or area of access). The ADP Orchestrator function requires Contributor access over the Resource Group where the SQL Server to be exported exists. The function below will attempt to create the role assignment. Any user executing this notebook will need to have Owner or User Access Administrator permissions to the Resource Group to assign the permission. Otherwise, contact your Azure AD Administrator. " - ], - "metadata": { - "azdata_cell_guid": "e919b0e3-34d6-40aa-8b29-cf92b943954c" - } - }, - { - "cell_type": "code", - "source": [ - "Provision-FuncRBAC -FunctionName $AdpFunc -ScopeRGName $SourceResourceGroup -ResourceGroupName $AdpResourceGroup -Subscription $AdpSubscription" - ], - "metadata": { - "azdata_cell_guid": "7678701e-ec40-43d9-baff-fd1cdabba1cd" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Verify Access to Source Server\r\n", - "\r\n", - "Before scheduling the export, validate the source SQL Server is accessible." - ], - "metadata": { - "azdata_cell_guid": "e8310d46-b7ed-4012-a993-1515c2d17e66" - } - }, - { - "cell_type": "code", - "source": [ - "$sqlServer = az sql server show --name $LogicalSQLServerName --resource-group $SourceResourceGroup --subscription $AdpSubscription -o JSON | ConvertFrom-JSON\r\n", - "if ($sqlServer)\r\n", - "{\r\n", - " Write-Host \"Source SQL Server: \" $sqlServer.name\r\n", - " if ($sqlServer.state -eq \"Ready\")\r\n", - " {\r\n", - " Write-Host \"State: Ready\"\r\n", - " }\r\n", - " else\r\n", - " {\r\n", - " Write-Host \"ERROR: Source server is not in Ready state. Current state is: \" $sqlServer.state\r\n", - " }\r\n", - "\r\n", - " $sqlAzureAdmin = az sql server ad-admin list --server $LogicalSQLServerName --resource-group $SourceResourceGroup --subscription $AdpSubscription -o JSON | ConvertFrom-JSON\r\n", - " if ($sqlAzureAdmin)\r\n", - " {\r\n", - " Write-Host \"Azure AD admin set to\" $sqlAzureAdmin.login\r\n", - " Write-Host \"SUCCESS: source server accessible and properly configured.\"\r\n", - " }\r\n", - " else\r\n", - " {\r\n", - " Write-Host \"ERROR: Source server has no Azure AD administrator configured. The Data Portability solution requires Azure Active Directory to be configured on source servers to provide secure access.\"\r\n", - " }\r\n", - "\r\n", - "}\r\n", - "else \r\n", - "{\r\n", - " Write-Host \"ERROR: Source server \" $sqlServer.name \"not found or current account lacks access to resource.\"\r\n", - " Write-Host \"Validate input settings:\"\r\n", - " Write-Host \"Resource group: \" $SourceResourceGroup\r\n", - " Write-Host \"Subscription: \" $AdpSubscription\r\n", - "}" - ], - "metadata": { - "azdata_cell_guid": "5e217942-b9b0-47d0-a9b7-79b5db93ea55" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Prepare input variable for Orchestrator Export Function" - ], - "metadata": { - "azdata_cell_guid": "bc9acbcc-f5e1-4642-b1e3-677fca704dfc" - } - }, - { - "cell_type": "code", - "source": [ - "$InputForExportFunction = Prepare-InputForExportFunction -Subscription $AdpSubscription -ADPResourceGroupName $AdpResourceGroup `\r\n", - " -BatchAccountName $AdpBatch -FunctionName $AdpFunc -VNetName $AdpVNET -SourceRGName $SourceResourceGroup `\r\n", - " -SqlServerName $LogicalSQLServerName -StorageAccountName $StorageAccount\r\n", - "Write-Host \"Setting parameter variables for Export Function Call...\"\r\n", - "$InputForExportFunction.Header\r\n", - "$InputForExportFunction.FunctionURL\r\n", - "$InputForExportFunction.Body" - ], - "metadata": { - "azdata_cell_guid": "9e6f142c-3295-4c12-9b9f-588c7c283b2d" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Start Export of SQL Server \r\n", - "Run the cell to start export operation of specified sql server" - ], - "metadata": { - "azdata_cell_guid": "d6a9e5c3-bd3c-4f06-8adc-c7751e33b4cf" - } - }, - { - "cell_type": "code", - "source": [ - "$ExportResponse = Invoke-RestMethod -Method 'Post' -Headers $InputForExportFunction.Header -Uri $InputForExportFunction.FunctionURL -Body $InputForExportFunction.Body -ContentType 'application/json'\r\n", - "$ExportResponse" - ], - "metadata": { - "azdata_cell_guid": "2e0167f6-b91c-4d65-a6e6-a1ef0bada436" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Get Status of Export Operation\r\n", - "Run the cell to get export operation status" - ], - "metadata": { - "azdata_cell_guid": "34812090-faa7-4375-8e76-72defea6d57a" - } - }, - { - "cell_type": "code", - "source": [ - "$statusCheckResponse = Invoke-RestMethod -Method 'Get' -Uri $ExportResponse.statusQueryGetUri\r\n", - "\r\n", - "\r\n", - "Write-Host \"Orchestrator Request: \" $statusCheckResponse.name\r\n", - "Write-Host \"`tOrchestrator Status: \" $statusCheckResponse.runtimeStatus\r\n", - "\r\n", - "$outputParams = $statusCheckResponse.output \r\n", - "if ($outputParams)\r\n", - "{\r\n", - " $batchJobID = $outputParams.Item2[0]\r\n", - " $containerUrl = $outputParams.Item2[1]\r\n", - "\r\n", - " Write-Host \"`tCreated Export Batch Job ID: \" $batchJobId\r\n", - " Write-Host \"`tExport container URL: \" $containerUrl\r\n", - "\r\n", - " $azBatchLogin = az batch account login --name $AdpBatch --resource-group $AdpResourceGroup -o JSON | ConvertFrom-Json\r\n", - " $jobStatus = az batch job show --job-id $batchJobID -o JSON | ConvertFrom-Json\r\n", - " Write-Host \"Export Job running on Pool: \" $jobStatus.poolInfo.poolId\r\n", - " Write-Host \"`tExport Request Status: \" $jobStatus.state\r\n", - "\r\n", - " $taskList = az batch task list --job-id $batchJobId -o JSON | ConvertFrom-Json\r\n", - " if ($taskList)\r\n", - " {\r\n", - " foreach ($task in $taskList)\r\n", - " {\r\n", - " Write-Host \"`tDatabase Export Task ID: \" $task.id \r\n", - " Write-Host \"`t`tStatus: \" $task.state\r\n", - " $taskExecution = $task.executionInfo\r\n", - " if ($taskExecution)\r\n", - " {\r\n", - " Write-Host \"`t`tResult: \" $taskExecution.result\r\n", - " }\r\n", - " }\r\n", - " }\r\n", - "}" - ], - "metadata": { - "azdata_cell_guid": "de949b83-195d-455b-ab63-5607cad7c9dd" - }, - "outputs": [], - "execution_count": null - } - ] -} \ No newline at end of file diff --git a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/data-portability/import-sql-server.ipynb b/extensions/azurehybridtoolkit/notebooks/hybridbook/content/data-portability/import-sql-server.ipynb deleted file mode 100644 index 9f0a965eef..0000000000 --- a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/data-portability/import-sql-server.ipynb +++ /dev/null @@ -1,533 +0,0 @@ -{ - "metadata": { - "kernelspec": { - "name": "powershell", - "display_name": "PowerShell", - "language": "powershell" - }, - "language_info": { - "name": "powershell", - "codemirror_mode": "shell", - "mimetype": "text/x-sh", - "file_extension": ".ps1" - } - }, - "nbformat_minor": 2, - "nbformat": 4, - "cells": [ - { - "cell_type": "markdown", - "source": [ - "# Import Existing Azure SQL Server Resources\r\n", - "" - ], - "metadata": { - "azdata_cell_guid": "baa1b762-f2a9-461f-a126-49f0d3b5e4f0" - } - }, - { - "cell_type": "markdown", - "source": [ - "The notebook will help accomplish the below steps as a part of Importing existing Azure SQL Server Resources\n", - "\n", - "- [ ] Connect to Azure Subscription\n", - "- [ ] Choose Resource Group (Read access required)\n", - "- [ ] Choose Microsoft SQL Server Resources to import\n", - "- [ ] Choose/Create Migration Storage\n", - "- [ ] Install Application + Data Portability function\n", - "- [ ] Install ADP Azure Batch processing pipeline\n", - "- [ ] Store SqlPackage.exe in Migration Storage for ADP function to hand to Az Batch\n", - "\n", - "Execute:\n", - "\n", - "- [ ] Check all prerequisites\n", - "- [ ] Kick off ADP service\n", - "\n", - "Monitor:\n", - "\n", - "- [ ] Check import status." - ], - "metadata": { - "azdata_cell_guid": "1ccbf203-d568-408c-a641-7e5cfa93802a" - } - }, - { - "cell_type": "markdown", - "source": [ - "## Set Variables for the Notebook" - ], - "metadata": { - "azdata_cell_guid": "09f648ab-67e8-4127-94bd-649d5e970321" - } - }, - { - "cell_type": "code", - "source": [ - "# ADP Resource \r\n", - "$AdpSubscription = \"\" # Azure Subscription ID/Name # The bacpac files and ADP Resources are assumed to be in the same subscription\r\n", - "$AdpResourceGroup = \"\" # Azure Resource Group which contains the ADP Resources\r\n", - "\r\n", - "# SQL Server \r\n", - "$TargetResourceGroupName = \"\" # Azure ResourceGroup into which the sql server backup needs to be restored\r\n", - "$StorageAccountName = \"\"\r\n", - "$ContainerName = \"\"\r\n", - "$LogicalSQLServerName = \"\" # New sql server name\r\n", - "$LSqlServerPassword = \"\"\r\n", - "\r\n", - "# Set Variables for ADP Resources\r\n", - "$AdpFunc = $AdpResourceGroup + \"Control\" \r\n", - "$AdpBatch = $AdpResourceGroup.ToLower() + \"batch\"\r\n", - "$AdpVNET = $AdpResourceGroup + \"Vnet\"" - ], - "metadata": { - "azdata_cell_guid": "01888595-0d1c-445b-ba85-dd12caa30192", - "tags": [ - "parameters" - ] - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Notebook Functions\r\n", - "Defines logical functions for the rest of the notebook. Function blocks are combined in a single cell that can be collapsed for readability or expanded for further examination. Nothing is executed until called later in the notebook. As a result, this cell is a requirement for any of the other cells below it. " - ], - "metadata": { - "azdata_cell_guid": "4a35cf8e-6598-43e8-a0da-2c0c369df548" - } - }, - { - "cell_type": "code", - "source": [ - "# Expand cell to view framework\r\n", - "\r\n", - "function Login-Azure\r\n", - "{ \r\n", - " # query azure locations to test for existing az login session exists with valid access tocken\r\n", - " $azureLocations = az account list-locations -o JSON 2>$null | ConvertFrom-Json\r\n", - " \r\n", - " if (!$azureLocations){ #If there are no az locations, there is no existing az login session\r\n", - " $subscriptions = az login -o JSON | ConvertFrom-Json # Login \r\n", - " }\r\n", - " else {\r\n", - " $subscriptions = az account list -o JSON | ConvertFrom-Json # getting subscriptions for the user to use in gridview\r\n", - " }\r\n", - "\r\n", - " if(![string]::IsNullOrWhiteSpace($AdpSubscription)) #If there is a subscription specified by user in the variables section\r\n", - " {\r\n", - " $specified_Subscription= az account show --subscription $AdpSubscription -o json |ConvertFrom-Json \r\n", - " if (!$specified_Subscription) #if specified subscription is not valid\r\n", - " { \r\n", - " $currentUser= az ad signed-in-user show --query \"{displayName:displayName,UPN:userPrincipalName}\" -o json|ConvertFrom-Json # get current logged in user infomration\r\n", - " Write-Host \"Refer below for the list of subscriptions for logged in account '$($currentUser.UPN)'`n\"\r\n", - " az account list --query \"[].{Name:name,SubscriptionID:id}\" -o table # list subscriptions under current logged in account\r\n", - " }\r\n", - " else { # if specified subscription is valid\r\n", - " Write-Output \"Using subscription... '$($specified_Subscription.name)' ... '$($specified_Subscription.id)'\" \r\n", - " }\r\n", - " }\r\n", - " else { # if no subscription is specified, users are given a gridview to select subscription from\r\n", - "\r\n", - " $selectedSubscription = $subscriptions | Select-Object -Property Name, Id | Out-GridView -PassThru\r\n", - " $SubscriptionId = $selectedSubscription.Id\r\n", - " $Subscription = $selectedSubscription.Name \r\n", - " $AdpSubscription = $subscription \r\n", - " Write-Output \"Using subscription... '$AdpSubscription' ... '$SubscriptionId'\" \r\n", - " } \r\n", - "}\r\n", - "\r\n", - "function Verify-ADPResources\r\n", - "{ \r\n", - " [CmdletBinding()]\r\n", - " param(\r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$Subscription,\r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$ADPResourceGroupName,\r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$FunctionName, \r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$BatchAccountName,\r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$VNetName,\r\n", - " [Parameter (Mandatory=$false)] [ValidateNotNullOrEmpty()] [string]$ApplicationName=\"SqlPackageWrapper\", \r\n", - " [Parameter (Mandatory=$false)] [ValidateNotNullOrEmpty()] [string]$ApplicationPackageVersionName=\"1\",\r\n", - " [Parameter (Mandatory=$false)] [ValidateNotNullOrEmpty()] [string]$SubNetName=\"default\" \r\n", - " ) \r\n", - "\r\n", - "# validate Subscription\r\n", - "$specified_Subscription= az account show --subscription $Subscription -o json | ConvertFrom-Json\r\n", - "if(!$specified_Subscription){\r\n", - " $currentUser= az ad signed-in-user show --query \"{displayName:displayName,UPN:userPrincipalName}\" -o json|ConvertFrom-Json # get current logged in user information\r\n", - " Write-Host \"Refer below for the list of subscriptions for logged in account '$($currentUser.UPN)'`n\"\r\n", - " az account list --query \"[].{Name:name,SubscriptionID:id}\" -o table # list subscriptions under current logged in account\r\n", - " return } \r\n", - "# validate ResourceGroup \r\n", - "$specified_ResourceGroup= az group show -n $ADPResourceGroupName --subscription $Subscription -o json | ConvertFrom-Json\r\n", - "if(!$specified_ResourceGroup) { \r\n", - " return\r\n", - " } \r\n", - "\r\n", - "$Installed = [ordered]@{} # ordered hash to store status of installation\r\n", - "$countError=0\r\n", - "\r\n", - "#Verify if VNet exists \r\n", - "$specified_VNet= az network vnet show -n $VNetName -g $ADPResourceGroupName --subscription $Subscription -o JSON 2>$null |ConvertFrom-Json \r\n", - "if(!$specified_VNet) {\r\n", - " $Installed['VNET']=\"Not Found\"\r\n", - " $countError++\r\n", - "} \r\n", - "else { \r\n", - " $existingVnetSubnet = az network vnet subnet show -n $SubNetName --vnet-name $VNetName -g $ADPResourceGroupName --subscription $Subscription -o JSON 2>$null |ConvertFrom-Json\r\n", - " if(!$existingVnetSubnet){\r\n", - " $Installed['VNET']=\"Default Subnet under\"+ $VNetName + \"Not Found\"\r\n", - " $countError++\r\n", - " }\r\n", - " else {\r\n", - " $Installed['VNET']=\"Installed\"\r\n", - " }\r\n", - " }\r\n", - "\r\n", - "#Verify if FunctionApp Exists\r\n", - "$specified_FunctionApp = az functionapp show -n $FunctionName -g $ADPResourceGroupName --subscription $Subscription -o JSON 2>$null | ConvertFrom-Json\r\n", - "if(!$specified_FunctionApp)\r\n", - "{\r\n", - " $Installed['FunctionApp']=\"Not Installed\"\r\n", - " $countError++\r\n", - "}\r\n", - "else\r\n", - "{\r\n", - " $Installed['FunctionApp']=\"Installed\"\r\n", - "} \r\n", - "\r\n", - "#check if Batch account exists\r\n", - "$specified_BatchAccount = az batch account show -n $BatchAccountName -g $ADPResourceGroupName --subscription $Subscription -o JSON 2>$null | ConvertFrom-Json\r\n", - "if(!$specified_BatchAccount)\r\n", - "{\r\n", - " $Installed['Batch']=\"Not Installed\"\r\n", - " $countError++\r\n", - "}\r\n", - "else\r\n", - "{\r\n", - " $appPackageInstalled = az batch application package show --application-name $ApplicationName --version-name $ApplicationPackageVersionName -n $BatchAccountName -g $ADPResourceGroupName --subscription $Subscription -o JSON 2>$null | ConvertFrom-Json\r\n", - " $connectedToStorage= $specified_BatchAccount.autoStorage \r\n", - " if($connectedToStorage -and $appPackageInstalled){ # BatchAccount connected to storageaccount and applicationpackage is installed\r\n", - " $Installed['Batch']=\"Installed\"\r\n", - " $Installed['Batch_ApplicationPackage']=\"Installed\"\r\n", - " $Installed['Batch_StorageAccount']=\"Connected to storage- \"+$connectedToStorage.storageAccountId.Split(\"/\")[-1]\r\n", - " }\r\n", - " if(!$connectedToStorage)\r\n", - " {\r\n", - " $Installed['Batch_StorageAccount']='Not Found'\r\n", - " $countError++\r\n", - " } \r\n", - " if(!$appPackageInstalled)\r\n", - " {\r\n", - " $Installed['Batch_ApplicationPackage']=\"Not Found\"\r\n", - " $countError++\r\n", - " } \r\n", - "}\r\n", - "if ($countError -gt 0){\r\n", - " Write-Output \"ADP Resources are not installed correctly. Please refer the list below and use the Bootstrap NB to install ADP Resources\"\r\n", - "}\r\n", - "$Installed\r\n", - "if ($countError -eq 0){\r\n", - " Write-Output \"`nFound all ADP Resources.\"\r\n", - "}\r\n", - "}\r\n", - "\r\n", - "function Prepare-InputForImportFunction\r\n", - "{ \r\n", - " [CmdletBinding()]\r\n", - " param(\r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$Subscription,\r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$ADPResourceGroupName,\r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$FunctionName, \r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$BatchAccountName,\r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$BackupFiles_StorageAccount,\r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$BackupFiles_ContainerName,\r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$VNetName, \r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$TargetRGName,\r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$SqlServerName,\r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$SqlServerPassword\r\n", - " )\r\n", - " \r\n", - " $Result = @{}\r\n", - " # Build Header \r\n", - " ## get Function key\r\n", - " $FunctionAppID =az functionapp show -n $FunctionName -g $ADPResourceGroupName --subscription $Subscription --query \"[id]\" -o JSON 2>$null | ConvertFrom-Json\r\n", - " $DefaultHostKey = az rest --method post --uri \"$FunctionAppID/host/default/listKeys?api-version=2018-11-01\" --query \"[functionKeys.default]\" -o JSON 2>$null | ConvertFrom-Json\r\n", - " ## Build Json Object for Headers\r\n", - " $headers = @{\r\n", - " 'x-functions-key' = $DefaultHostKey\r\n", - " }\r\n", - " $Result['Header']=$headers\r\n", - "\r\n", - " # Build string for Function URL \r\n", - " $specified_Subscription= az account show --subscription $Subscription -o json |ConvertFrom-Json #Get SpecifiedSubscriptionID\r\n", - " $SubscriptionID= $specified_Subscription.id\r\n", - " $FunctionUrl = 'https://'+ $FunctionName +'.azurewebsites.net/api/subscriptions/'+ $SubscriptionID +'/resourceGroups/' + $ADPResourceGroupName + '/Import'\r\n", - " $Result['FunctionURL']=$FunctionUrl\r\n", - "\r\n", - " # Set parameter variables for Body\r\n", - " ## Get BatchAccountURL \r\n", - " $specified_Batch = az batch account show -n $BatchAccountName -g $ADPResourceGroupName --subscription $Subscription -o JSON 2>$null | ConvertFrom-Json\r\n", - " $BatchAccountURL = 'https://' + $specified_Batch.accountEndpoint\r\n", - "\r\n", - " ## Get default SubNet ID for specified VNet\r\n", - " $specified_VNet_SubNet = az network vnet subnet show -g $ADPResourceGroupName --vnet-name $VNetName -n 'default' --subscription $Subscription -o JSON |ConvertFrom-Json\r\n", - " $VNetSubNetID = $specified_VNet_SubNet.id\r\n", - "\r\n", - " ## Create access token to source sql server\r\n", - " $targetAccessToken = az account get-access-token --resource=https://database.windows.net --query accessToken\r\n", - " $targetAccessToken\r\n", - "\r\n", - " ## Build JSon object for Body\r\n", - " $Body = @{\r\n", - " batchAccountUrl = $BatchAccountURL\r\n", - " VNetSubnetId= $VNetSubNetID\r\n", - " storageAccountName = $BackupFiles_StorageAccount\r\n", - " containerName = $BackupFiles_ContainerName\r\n", - " targetSqlServerResourceGroupName = $TargetRGName\r\n", - " targetSqlServerName = $SQLServerName \r\n", - " userName = $SqlServerLogin \r\n", - " targetAccessToken = $targetAccessToken\r\n", - " sqlAdminPassword = $SqlServerPassword\r\n", - " }\r\n", - " $json = $Body | ConvertTo-Json\r\n", - " $Result['Body']=$json\r\n", - "\r\n", - " $Result\r\n", - " \r\n", - "}\r\n", - "\r\n", - "function Provision-FuncRBAC {\r\n", - " [CmdletBinding()]\r\n", - " param (\r\n", - " [Parameter(Mandatory=$true)][ValidateNotNullOrEmpty()][string]$Subscription,\r\n", - " [Parameter(Mandatory=$true)][ValidateNotNullOrEmpty()][string]$ResourceGroupName,\r\n", - " [Parameter(Mandatory=$true)][ValidateNotNullOrEmpty()][string]$FunctionName,\r\n", - " [Parameter(Mandatory=$true)][ValidateNotNullOrEmpty()][string]$ScopeRGName,\r\n", - " [Parameter(Mandatory=$false)][ValidateNotNullOrEmpty()][string]$Role=\"Contributor\"\r\n", - " )\r\n", - "\r\n", - " # Get the scope resource group's ID\r\n", - " $scopeID = az group show --resource-group $ScopeRGName --subscription $Subscription --query \"[id]\" -o JSON | ConvertFrom-Json \r\n", - " if(!$scopeID) {\r\n", - " Write-Output \"Provision-FuncRBAC failed.\" \r\n", - " return }\r\n", - " else { Write-Output \"Found scope '$ScopeRGName' with ID... '$scopeID'\"\r\n", - " }\r\n", - "\r\n", - " # Get the az function principal id\r\n", - " $app_PrincipalID = az functionapp show -n $FunctionName --resource-group $ResourceGroupName --subscription $Subscription --query \"[identity.principalId]\" -o JSON | ConvertFrom-Json \r\n", - " if(!$app_PrincipalID) {\r\n", - " Write-Output \"Provision-FuncRBAC failed.\" \r\n", - " return }\r\n", - " else { Write-Output \"Found principal id of Azure function '$FunctionName'... '$app_PrincipalID'\"\r\n", - " }\r\n", - "\r\n", - " # Verify if a role assignment has been created for function\r\n", - " $app_RoleAssignmentDefinition= az role assignment list --subscription $Subscription --assignee $app_PrincipalID --scope $scopeID --query \"[].roleDefinitionName\" -o JSON 2>$null | ConvertFrom-Json\r\n", - "\r\n", - " if($app_RoleAssignmentDefinition -eq $Role)\r\n", - " {\r\n", - " Write-Output \"Found Role Assignment for Principal ID.. '$app_PrincipalID' with Role.. '$app_RoleAssignmentDefinition' . No work needed\"\r\n", - " }\r\n", - " else\r\n", - " {\r\n", - " # Continue to setup RBAC, once we verify an assignment is not setup and all the resources exist\r\n", - " Write-Output \"Creating new role assignment by running: 'az functionapp identity assign -n $FunctionName --role $Role -g $ResourceGroupName --scope $scopeID --subscription $Subscription'\"\r\n", - " Write-Warning \"If your account does not have the access to assign new roles as Owner or User Access Administrator for the resource group, than you will need to contact your Azure AD Administrator to assign a service principle using the commands above\"\r\n", - " az functionapp identity assign -n $FunctionName --role $Role -g $ResourceGroupName --scope $scopeID --subscription $Subscription \r\n", - " }\r\n", - "}\r\n", - "Write-Host \"Helper Functions Created successfully\" " - ], - "metadata": { - "azdata_cell_guid": "4730aec5-7aa6-4a2a-baf4-696dc74aa898", - "tags": [ - "hide_input" - ] - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Connect to Azure Account\r\n", - "Run the below cell to login to an Azure account. Be sure to check the Windows Taskbar for a login dialog box underneath the notebook or other windows or by pressing Alt+TAB." - ], - "metadata": { - "azdata_cell_guid": "5cd37536-c2c5-4b97-8383-fa761d7cbda3" - } - }, - { - "cell_type": "code", - "source": [ - "Login-Azure " - ], - "metadata": { - "azdata_cell_guid": "e9cd7ac2-ff0b-43b4-baf8-b71d3128885c" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Verify ADP Resources \r\n", - "Verify if ADP resources exists in specified Resource Group" - ], - "metadata": { - "azdata_cell_guid": "bec05e08-67ba-4071-8459-5b32dc7f876a" - } - }, - { - "cell_type": "code", - "source": [ - "Verify-ADPResources -Subscription $AdpSubscription -ADPResourceGroupName $AdpResourceGroup `\r\n", - " -BatchAccountName $AdpBatch -FunctionName $AdpFunc -VNetName $AdpVNET " - ], - "metadata": { - "azdata_cell_guid": "e89f6eb9-fcbc-4b7d-bcd1-37f1eb52cc02", - "tags": [ - "hide_input" - ] - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Verify RBAC of Azure Function\r\n", - "Roles based access control is a function of Azure that assigns services to a role with a specific access scope (or area of access). The ADP Orchestrator function requires Contributor access over the Resource Group where the SQL Server to be exported exists. The function below will attempt to create the role assignment. Any user executing this notebook will need to have Owner or User Access Administrator permissions to the Resource Group to assign the permission. Otherwise, contact your Azure AD Administrator. " - ], - "metadata": { - "azdata_cell_guid": "0c95bb17-b3cf-4a8b-8aa6-690ac6139e37" - } - }, - { - "cell_type": "code", - "source": [ - "Provision-FuncRBAC -FunctionName $AdpFunc -ScopeRGName $TargetResourceGroupName -ResourceGroupName $AdpResourceGroup -Subscription $AdpSubscription" - ], - "metadata": { - "azdata_cell_guid": "c374e57c-51ec-4a3f-9966-1e50cefc8510" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Prepare input variable for Orchestrator Import Function" - ], - "metadata": { - "azdata_cell_guid": "b517742f-fa3d-4a4f-9ec0-b320c71738d4" - } - }, - { - "cell_type": "code", - "source": [ - "$InputForImportFunction = Prepare-InputForImportFunction -Subscription $AdpSubscription -ADPResourceGroupName $AdpResourceGroup `\r\n", - " -BatchAccountName $AdpBatch -FunctionName $AdpFunc -TargetRGName $TargetResourceGroupName `\r\n", - " -VNetName $AdpVNET -BackupFiles_StorageAccount $StorageAccountName -BackupFiles_ContainerName $ContainerName `\r\n", - " -SqlServerName $LogicalSQLServerName -SqlServerPassword $LSqlServerpassword\r\n", - "Write-Host \"Setting parameter variables for Import Function Call...\"\r\n", - "$InputForImportFunction.Header\r\n", - "$InputForImportFunction.FunctionURL\r\n", - "$InputForImportFunction.Body" - ], - "metadata": { - "azdata_cell_guid": "bfba288e-3466-4c57-9f3c-5281753601cf", - "tags": [ - "hide_input" - ] - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Start Import of SQL Server \r\n", - "Run the cell to start import from specified backup files" - ], - "metadata": { - "azdata_cell_guid": "8f615b19-1e1d-405f-9f4a-ad7cc303487a" - } - }, - { - "cell_type": "code", - "source": [ - "$importResponse = Invoke-RestMethod -Method 'Post' -Headers $InputForImportFunction.Header -Uri $InputForImportFunction.FunctionURL -Body $InputForImportFunction.Body -ContentType 'application/json'\r\n", - "$importResponse" - ], - "metadata": { - "azdata_cell_guid": "7e251aa5-7a92-4212-8c81-394c2058f1fa", - "tags": [ - "hide_input" - ] - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Get Status of Import Operation\r\n", - "Run the cell to get import operation status" - ], - "metadata": { - "azdata_cell_guid": "cb1988a9-9797-49f0-8ddb-1634df48f027" - } - }, - { - "cell_type": "code", - "source": [ - "$statusCheckResponse = Invoke-RestMethod -Method 'Get' -Uri $importResponse.statusQueryGetUri\r\n", - "\r\n", - "\r\n", - "Write-Host \"Orchestrator Request: \" $statusCheckResponse.name\r\n", - "Write-Host \"`tOrchestrator Status: \" $statusCheckResponse.runtimeStatus\r\n", - "\r\n", - "$outputParams = $statusCheckResponse.output \r\n", - "if ($outputParams)\r\n", - "{\r\n", - " $batchJobID = $outputParams.Item2[2]\r\n", - " $containerUrl = $outputParams.Item2[3]\r\n", - "\r\n", - " Write-Host \"`tCreated Import Batch Job ID: \" $batchJobId\r\n", - " $azBatchLogin = az batch account login --name $AdpBatch --resource-group $AdpResourceGroup -o JSON | ConvertFrom-Json\r\n", - " $jobStatus = az batch job show --job-id $batchJobID -o JSON | ConvertFrom-Json\r\n", - " Write-Host \"Import Job running on Pool: \" $jobStatus.poolInfo.poolId\r\n", - " Write-Host \"`Import Request Status: \" $jobStatus.state\r\n", - "\r\n", - " $taskList = az batch task list --job-id $batchJobId -o JSON | ConvertFrom-Json\r\n", - " if ($taskList)\r\n", - " {\r\n", - " foreach ($task in $taskList)\r\n", - " {\r\n", - " Write-Host \"`tDatabase Import Task ID: \" $task.id \r\n", - " Write-Host \"`t`tStatus: \" $task.state\r\n", - " $taskExecution = $task.executionInfo\r\n", - " if ($taskExecution)\r\n", - " {\r\n", - " Write-Host \"`t`tResult: \" $taskExecution.result\r\n", - " }\r\n", - " }\r\n", - " }\r\n", - "}\r\n", - "" - ], - "metadata": { - "azdata_cell_guid": "3c59dc7b-2648-46ee-b57e-d9e99580a093", - "tags": [ - "hide_input" - ] - }, - "outputs": [], - "execution_count": null - } - ] -} \ No newline at end of file diff --git a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/data-portability/readme.md b/extensions/azurehybridtoolkit/notebooks/hybridbook/content/data-portability/readme.md deleted file mode 100644 index 720dc114e1..0000000000 --- a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/data-portability/readme.md +++ /dev/null @@ -1,20 +0,0 @@ -# Data Portability -[Home](../readme.md) - -Notebooks in this chapter perform a data migration using a custom Azure function that can be deployed to an Azure subscription. It enables [Azure Batch](https://azure.microsoft.com/en-us/services/batch) computing of a complex SQL Server migration to and from a single Resource Group. Azure Batch is a process that runs large-scale parallel and high-performance computing (HPC) batch jobs efficiently in Azure. This greatly reduces the processing required locally which should prevent long execution times, timeouts and retries. Importing and exporting data to and from Azure is supported for multiple SQL database instances. Data is imported and exported to and from standard SQL backup formats (*.bacpac) which "encapsulates the database schema as well as the data stored in the database" ([Microsoft Docs](https://docs.microsoft.com/en-us/sql/relational-databases/data-tier-applications/data-tier-applications)). - -## Notebooks in this Chapter - -- [Azure Data Portability Setup](setup-adp.ipynb) - Configure and install a custom Azure function to migrate data to and from Azure
- - -- [Export Sql Server](export-sql-server.ipynb) - from SQL Azure to a standard SQL backup format - -- [Import Sql Server](import-sql-server.ipynb) - from SQL backup format to Azure - -## Steps -1. The Azure function must first be deployed using the setup notebook -2. Open the notebook for the desired migration path (import or export) -3. Configure and execute notebook -4. Monitor progress with periodic notebook queries -5. Verify data has been imported/exported by reviewing the storage account for the migrated Resource Group diff --git a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/data-portability/setup-adp.ipynb b/extensions/azurehybridtoolkit/notebooks/hybridbook/content/data-portability/setup-adp.ipynb deleted file mode 100644 index 249549f4d4..0000000000 --- a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/data-portability/setup-adp.ipynb +++ /dev/null @@ -1,643 +0,0 @@ -{ - "metadata": { - "kernelspec": { - "name": "powershell", - "display_name": "PowerShell", - "language": "powershell" - }, - "language_info": { - "name": "powershell", - "codemirror_mode": "shell", - "mimetype": "text/x-sh", - "file_extension": ".ps1" - } - }, - "nbformat_minor": 2, - "nbformat": 4, - "cells": [ - { - "cell_type": "markdown", - "source": [ - "# Application and Data Portability Setup Notebook\n", - "\n", - "This notebook provisions SQL Data Portability services to an Azure Subscription for migration purposes such as importing and exporting Azure SQL Server database(s). The following scenarios are covered:\n", - "\n", - "- Azure to Azure migration\n", - "- Azure to Air-gapped Azure (highly secured Azure offer)\n", - "- Azure to on-premises\n", - "\n", - "This notebook is designed to be ran from a client workstation or laptop with minimal modification. It will locate three different Azure Resources in a customer subscription. If not found, it will install a service to the given Resource Group. If the resource group specified does not exist. The following is installed during setup:\n", - "\n", - "1. A **Custom Azure ADP Function** to orchestrate the Azure Batch and Storage\n", - "2. An Azure **Storage Account** for storage\n", - "3. An **Azure Batch** resource\n", - "\n", - "This notebook will help accomplish the below steps as a part of setting up an ADP Resource Group\n", - "\n", - "![](AdpSetup.png)\n", - "\n", - "1. Set Environment Variables\n", - "2. Define Notebook Functions\n", - "3. MS Azure Login\n", - "4. Validate/ Create ADP Resource Group\n", - "5. Provision Virtual Network\n", - "6. Provision ADP Storage Account\n", - "7. Provision ADP Portability Function\n", - "8. Provision ADP Batch Account\n", - "9. Provision Contributor access (RBAC) to Portability function over ADP Resource Group\n", - "\n", - "Setup will not perform tasks on existing Azure assets such as Microsoft SQL Servers, databases, VMs, etc. Other notebooks cover the
import or export operations. This notebook will only check for the existence of these ADP resources and their setup." - ], - "metadata": { - "azdata_cell_guid": "c5d49b77-973b-47bc-92ef-7bcfe05e4885" - } - }, - { - "cell_type": "code", - "source": [ - "# Setup client environment variables that the rest of the notebook will use\r\n", - "$AdpResourceGroup = \"\" # Target Resource Group\r\n", - "$AdpRegion = \"eastus\" # Region/Location of the resource group\r\n", - "\r\n", - "# Derived settings\r\n", - "$AdpSubscription = \"\" # Target Azure Subscription Name or ID\r\n", - "$AdpFunc = $AdpResourceGroup + \"Control\"\r\n", - "$AdpStorage = $AdpResourceGroup.ToLower() + \"storage\"\r\n", - "$AdpBatch = $AdpResourceGroup.ToLower() + \"batch\"\r\n", - "$AdpVNET = $AdpResourceGroup + \"VNet\"\r\n", - "\r\n", - "# URLs - Update with the recommended toolkit version and build\r\n", - "$BaseToolkitUrl = \"https://hybridtoolkit.blob.core.windows.net/components\"\r\n", - "$ReleaseVersion = \"0.13\"\r\n", - "$BuildNumber = \"74938\"\r\n", - "$AdpDownloadUrl = \"$BaseToolkitUrl/$ReleaseVersion/ADPControl-$BuildNumber.zip\"\r\n", - "$AdpWrapperUrl = \"$BaseToolkitUrl/$ReleaseVersion/BatchWrapper-$BuildNumber.zip\"" - ], - "metadata": { - "azdata_cell_guid": "5896a9a8-5ba8-4888-967a-10c3ceaccf12", - "tags": [ - "parameters" - ] - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Define Helper Functions\r\n", - "Defines logical functions for the rest of the notebook. Function blocks are combined in a single cell that can be collapsed for readability or expanded for further examination. Nothing is executed until called later in the notebook. As a result, this cell is a requirement for any of the other cells below it. " - ], - "metadata": { - "azdata_cell_guid": "7df7c213-9e97-46e4-acb5-547993b9b03a" - } - }, - { - "cell_type": "code", - "source": [ - "# Expand cell to view framework\r\n", - "\r\n", - "function Get-ConnectionStringProperties\r\n", - "{\r\n", - " param([Parameter (Mandatory=$true)] [string]$ConnectionString)\r\n", - " $hash = @{}\r\n", - " $attributes = $ConnectionString.Split(\";\"); \r\n", - " foreach($at in $attributes)\r\n", - " {\r\n", - " $attributeKeyValue=$at.Split(\"=\",2) \r\n", - " $hash[$attributeKeyValue[0]]=$attributeKeyValue[1] \r\n", - " }$hash \r\n", - "}\r\n", - "\r\n", - "function Login-Azure\r\n", - "{ \r\n", - " # query azure locations to test for existing az login session exists with valid access tocken\r\n", - " $azureLocations = az account list-locations -o JSON 2>$null | ConvertFrom-Json\r\n", - " \r\n", - " if (!$azureLocations){ #If there are no az locations, there is no existing az login session\r\n", - " $subscriptions = az login -o JSON | ConvertFrom-Json # Login \r\n", - " }\r\n", - " else {\r\n", - " $subscriptions = az account list -o JSON | ConvertFrom-Json # getting subscriptions for the user to use in gridview\r\n", - " }\r\n", - "\r\n", - " if(![string]::IsNullOrWhiteSpace($AdpSubscription)) #If there is a subscription specified by user in the variables section\r\n", - " {\r\n", - " $specified_Subscription= az account show --subscription $AdpSubscription -o json |ConvertFrom-Json \r\n", - " if (!$specified_Subscription) #if specified subscription is not valid\r\n", - " { \r\n", - " $currentUser= az ad signed-in-user show --query \"{displayName:displayName,UPN:userPrincipalName}\" -o json|ConvertFrom-Json # get current logged in user infomration\r\n", - " Write-Host \"Refer below for the list of subscriptions for logged in account '$($currentUser.UPN)'`n\"\r\n", - " az account list --query \"[].{Name:name,SubscriptionID:id}\" -o table # list subscriptions under current logged in account\r\n", - " }\r\n", - " else { # if specified subscription is valid\r\n", - " Write-Output \"Using subscription... '$($specified_Subscription.name)' ... '$($specified_Subscription.id)'\" \r\n", - " }\r\n", - " }\r\n", - " else { # if no subscription is specified, users are given a gridview to select subscription from\r\n", - "\r\n", - " $selectedSubscription = $subscriptions | Select-Object -Property Name, Id | Out-GridView -PassThru\r\n", - " $SubscriptionId = $selectedSubscription.Id\r\n", - " $Subscription = $selectedSubscription.Name \r\n", - " $AdpSubscription = $subscription \r\n", - " Write-Output \"Using subscription... '$AdpSubscription' ... '$SubscriptionId'\" \r\n", - " } \r\n", - "}\r\n", - "\r\n", - "function Setup-AzResourceGroup\r\n", - "{\r\n", - " [CmdletBinding()]\r\n", - " param(\r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$Subscription,\r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$ResourceGroupName,\r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$ResourceGroupLocation \r\n", - " ) \r\n", - " \r\n", - " #Validating parameter $Subscription\r\n", - " $specified_Subscription = az account show --subscription $Subscription -o json |ConvertFrom-Json\r\n", - " if(!$specified_Subscription){\r\n", - " $currentUser= az ad signed-in-user show --query \"{displayName:displayName,UPN:userPrincipalName}\" -o json|ConvertFrom-Json # get current logged in user infomration\r\n", - " Write-Host \"Refer below for the list of subscriptions for logged in account '$($currentUser.UPN)'`n\"\r\n", - " az account list --query \"[].{Name:name,SubscriptionID:id}\" -o table # list subscriptions under current logged in account\r\n", - " return\r\n", - " }\r\n", - " #Validating parameter $ResourceGroupLocation\r\n", - " $specified_RGLocation = az account list-locations --query \"[?name == '$ResourceGroupLocation']\" -o JSON | ConvertFrom-Json\r\n", - " if(!$specified_RGLocation) {\r\n", - " Write-Host \"ResourceGroupLocation '$ResourceGroupLocation' not found. Refer below for the list of locations for the account:`n\" \r\n", - " az account list-locations --query \"[].{Locations: name}\" -o table\r\n", - " return\r\n", - " }\r\n", - " # check if Resource group already exists under specified subscription\r\n", - " $existingResourceGroup = az group show -n $ResourceGroupName --subscription $Subscription -o JSON 2>$null | ConvertFrom-Json\r\n", - "\r\n", - " if ($existingResourceGroup){ #Found specified resource group \r\n", - " Write-Output \"Found Resource Group '$($existingResourceGroup.name)'. \" \r\n", - " } \r\n", - " else { \r\n", - " # Resource group not found under specified subscription. Create new Resource group \r\n", - " Write-Output \"Resource group '$($ResourceGroupName)' not found. `nCreating new resource group ... \"\r\n", - " $newResourceGroup = az group create -n $ResourceGroupName -l $ResourceGroupLocation --subscription $Subscription -o JSON | ConvertFrom-Json\r\n", - " Write-Output \"Created Resource Group '$($newResourceGroup.name)'\"\r\n", - " } \r\n", - "}\r\n", - "\r\n", - "function Setup-AzVirtualNetwork\r\n", - "{ \r\n", - " [CmdletBinding()]\r\n", - " param(\r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$Subscription,\r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$ResourceGroupName,\r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$VNetName, \r\n", - " [Parameter (Mandatory=$false)] [ValidateNotNullOrEmpty()] [string]$SubNetName = \"default\" \r\n", - " )\r\n", - " \r\n", - " #Validating parameter $ResourceGroupName\r\n", - " $specified_RG = az group show -n $ResourceGroupName --subscription $Subscription 2>$null\r\n", - " if(!$specified_RG) {\r\n", - " Write-Host \"Invalid Input: ResourceGroupName. Resource Group $ResourceGroupName not found\"\r\n", - " return\r\n", - " } \r\n", - " \r\n", - " #Check if default subnet exists under specified Vnet \r\n", - " $existingVnetSubnet = az network vnet subnet show -n $SubNetName --vnet-name $VNetName -g $ResourceGroupName --subscription $Subscription -o JSON |ConvertFrom-Json\r\n", - " if ($existingVnetSubnet){ #Found default subnet under specified VNet\r\n", - " Write-Output \"Found default Subnet under specified VNet. `nSubNet ID '$($existingVnetSubnet.id)'\"\r\n", - " } \r\n", - " else { \r\n", - " #VNet or defaut subnet not found under specified resource group. Create new VNet with default Subnet /Add default subnet to existing VNet\r\n", - " Write-Output \"Creating new Virtual network with default Subnet ID ... \"\r\n", - " $newVNet = az network vnet create --name \"$AdpVNET\" --resource-group $AdpResourceGroup --subscription $AdpSubscription --subnet-name $SubNetName -o JSON |ConvertFrom-Json #vnet create/Update command: Bug: In this command, the output variable is not getting converted to PS objects.\r\n", - " $newVNet = az network vnet subnet show -g $AdpResourceGroup --vnet-name $AdpVNET -n $SubNetName --subscription $AdpSubscription -o JSON |ConvertFrom-Json # added this line due to above bug\r\n", - " Write-Output \"Created VNet with default Subnet - ID: '$($newVNet.id)'\"\r\n", - " }\r\n", - "}\r\n", - "\r\n", - "function Setup-AzStorageAccount\r\n", - "{ \r\n", - " [CmdletBinding()]\r\n", - " param(\r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$Subscription,\r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$ResourceGroupName,\r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$StorageAccountName,\r\n", - " [Parameter (Mandatory=$false)] [ValidateNotNullOrEmpty()] [string]$StorageAccountKind = \"StorageV2\",\r\n", - " [Parameter (Mandatory=$false)] [ValidateNotNullOrEmpty()] [string]$StorageAccountSKU = \"Standard_LRS\" \r\n", - " )\r\n", - "\r\n", - " $ListStorageAccountKind = \"BlobStorage\", \"BlockBlobStorage\", \"FileStorage\", \"Storage\", \"StorageV2\"\r\n", - " $ListStorageAccountSKU = \"Premium_LRS\", \"Premium_ZRS\", \"Standard_GRS\", \"Standard_GZRS\", \"Standard_LRS\", \"Standard_RAGRS\", \"Standard_RAGZRS\", \"Standard_ZRS\"\r\n", - "\r\n", - " \r\n", - " #Validating parameter $ResourceGroupName\r\n", - " $specified_RG = az group show -n $ResourceGroupName --subscription $Subscription 2>$null\r\n", - " if(!$specified_RG) {\r\n", - " Write-Host \"Invalid Input: ResourceGroupName. Resource Group $ResourceGroupName not found\"\r\n", - " return\r\n", - " } \r\n", - " #Validating parameter $StorageAccountKind\r\n", - " if($StorageAccountKind -notin $ListStorageAccountKind) {\r\n", - " Write-Host \"StorageAccountKind '$StorageAccountKind' not valid. Accepted values: $ListStorageAccountKind\" \r\n", - " return\r\n", - " }\r\n", - " #Validating parameter $StorageAccountSKU\r\n", - " if($StorageAccountSKU -notin $ListStorageAccountSKU) {\r\n", - " Write-Host \"StorageAccountSKU '$StorageAccountSKU' not valid. Accepted values: $ListStorageAccountSKU\" \r\n", - " return\r\n", - " }\r\n", - " \r\n", - " #Check if storage account exists under specified resource group \r\n", - " $existingStorageAccount = az storage account show -n $StorageAccountName -g $ResourceGroupName --subscription $Subscription -o JSON| ConvertFrom-Json\r\n", - "\r\n", - " if ($existingStorageAccount){ #Found specified storage account\r\n", - " Write-Output \"Found storage account '$($existingStorageAccount.name)'\"\r\n", - " } \r\n", - " else { \r\n", - " #Storage account not found under specified resource group. Create new storage account\r\n", - " Write-Output \"Creating new storage account ... \"\r\n", - " $newStorageAccount = az storage account create -n $StorageAccountName --kind $StorageAccountKind --sku $StorageAccountSKU -g $ResourceGroupName --subscription $Subscription -o JSON | ConvertFrom-Json\r\n", - " Write-Output \"Created Storage Account '$($newStorageAccount.name)'\"\r\n", - " }\r\n", - "}\r\n", - "\r\n", - "function Setup-AzFunctionApp\r\n", - "{\r\n", - " [CmdletBinding()]\r\n", - " param(\r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$Subscription,\r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$ResourceGroupName,\r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$FunctionName,\r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$StorageAccountName,\r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$FunctionAppPackageURL,\r\n", - " [Parameter (Mandatory=$false)] [ValidateNotNullOrEmpty()] [string]$FunctionAppVersion=\"3\",\r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$ConsumptionPlanLocation \r\n", - " ) \r\n", - " #Validating parameter $ResourceGroupName\r\n", - " $specified_RG = az group show -n $ResourceGroupName --subscription $Subscription 2>$null\r\n", - " if(!$specified_RG) {\r\n", - " Write-Host \"Invalid Input: ResourceGroupName. Resource Group $ResourceGroupName not found\"\r\n", - " return\r\n", - " } \r\n", - " #Validating parameter StorageAccountName\r\n", - " $specified_storageAccount = az storage account show -n $StorageAccountName -g $ResourceGroupName --subscription $Subscription 2>$null\r\n", - " if(!$specified_storageAccount){ \r\n", - " Write-Host \"Invalid Input: StorageAccount name. Storage Account $StorageAccountName not found\" \r\n", - " return\r\n", - " }\r\n", - " #Validating parameter FunctionAppVersion\r\n", - " if($FunctionAppVersion -notin (2,3)){\r\n", - " Write-Host \"FunctionAppVersion '$FunctionAppVersion' not valid. Accepted values: 2,3\" \r\n", - " return\r\n", - " }\r\n", - " #Validating parameter ConsumptionPlanLocation\r\n", - " $specified_Location = az functionapp list-consumption-locations --query \"[?name == '$ConsumptionPlanLocation']\" -o JSON | ConvertFrom-Json \r\n", - " if(!$specified_Location) { \r\n", - " Write-Host \"ComsumptionPlanLocation '$ConsumptionPlanLocation' not valid. Refer below for available options: `n\" \r\n", - " az functionapp list-consumption-locations --query \"[].{ConsumptionPlanLocations: name}\" -o table\r\n", - " return\r\n", - " }\r\n", - " \r\n", - " #Check if functionapp exists within the specified resource group\r\n", - " $existingFunctionApp = az functionapp show -n $FunctionName -g $ResourceGroupName --subscription $Subscription -o JSON 2>$null | ConvertFrom-Json\r\n", - "\r\n", - " if ($existingFunctionApp){ #ensure that function application is linked to the specified storage account\r\n", - "\r\n", - " #retrieve connection string of storage account linked to FunctionApp and check if the connected storage account is equal to the storage account passed to here\r\n", - " $funcApp_Storage_ConnString= az functionapp config appsettings list -n $FunctionName -g $ResourceGroupName --subscription $Subscription --query \"[?name == 'AzureWebJobsStorage'].value\" -o JSON 2>$null | ConvertFrom-Json\r\n", - " $connectionString = Get-ConnectionStringProperties -ConnectionString \"$funcApp_Storage_ConnString\"\r\n", - " $isConnected = $connectionString.AccountName -eq $StorageAccountName\r\n", - "\r\n", - " if($isConnected) { \r\n", - " Write-Output \"Found FunctionApp '$FunctionName'. `nConnected storage account is '$StorageAccountName'\"\r\n", - " } \r\n", - " else {\r\n", - " Write-Output \"Found FunctionApp '$FunctionName'. `nConnected storage account is NOT '$StorageAccountName', but '$($connectionString.AccountName)' \"\r\n", - " }\r\n", - " } \r\n", - " else { #Provision FunctionApp \r\n", - "\r\n", - " $newFuncApp= az functionapp create --name $FunctionName --resource-group $ResourceGroupName --subscription $Subscription --storage-account $StorageAccountName --functions-version $FunctionAppVersion --consumption-plan-location $ConsumptionPlanLocation -o JSON | ConvertFrom-JSON \r\n", - " Write-Output \"`nCreated Function Application '$($newFuncApp.name)'\"\r\n", - " $AppInsightsInstrumentationKey = az functionapp config appsettings list --name $FunctionName --resource-group $ResourceGroupName --subscription $Subscription --query \"[?name == 'APPINSIGHTS_INSTRUMENTATIONKEY'].value\" -o JSON | ConvertFrom-JSON \r\n", - " az functionapp config appsettings set --name $FunctionName --settings \"WEBSITE_RUN_FROM_PACKAGE=$FunctionAppPackageURL\" \"APPLICATIONINSIGHTS_CONNECTION_STRING=InstrumentationKey=$AppInsightsInstrumentationKey\" `\r\n", - " --resource-group $ResourceGroupName --subscription $Subscription -o none \r\n", - " Write-Output \"Set '$($newFuncApp.name)' to run from package '$FunctionAppPackageURL'\"\r\n", - " az functionapp identity assign -n $FunctionName -g $ResourceGroupName --subscription $Subscription -o none \r\n", - " Write-Output \"Enabled system assigned managed identity for '$($newFuncApp.name)'\" \r\n", - " }\r\n", - "}\r\n", - "\r\n", - "function Setup-AzBatchAccount\r\n", - "{\r\n", - " [CmdletBinding()]\r\n", - " param(\r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$Subscription,\r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$ResourceGroupName,\r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$BatchAccountLocation,\r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$BatchAccountName,\r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$StorageAccountName,\r\n", - " [Parameter (Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$ApplicationPackageURL,\r\n", - " [Parameter (Mandatory=$false)] [ValidateNotNullOrEmpty()] [string]$ApplicationName=\"SqlPackageWrapper\", \r\n", - " [Parameter (Mandatory=$false)] [ValidateNotNullOrEmpty()] [string]$ApplicationPackageVersionName=\"1\" \r\n", - " )\r\n", - " #Validating parameter $ResourceGroupName\r\n", - " $specified_RG = az group show -n $ResourceGroupName --subscription $Subscription 2>$null\r\n", - " if(!$specified_RG) {\r\n", - " Write-Host \"Invalid Input: ResourceGroupName. Resource Group $ResourceGroupName not found\"\r\n", - " return\r\n", - " } \r\n", - " #Validating parameter StorageAccountName\r\n", - " $specified_storageAccount = az storage account show -n $StorageAccountName -g $ResourceGroupName --subscription $Subscription 2>$null\r\n", - " if(!$specified_storageAccount){ \r\n", - " Write-Host \"Invalid Input: StorageAccount name. Storage Account $StorageAccountName not found\" \r\n", - " return\r\n", - " }\r\n", - " #Validating parameter $BatchAccountLocation\r\n", - " $specified_BLocation = az account list-locations --query \"[?name == '$BatchAccountLocation']\" -o JSON | ConvertFrom-Json\r\n", - " if(!$specified_BLocation) {\r\n", - " Write-Host \"BatchAccountLocation '$BatchAccountLocation' not found. Refer below for the list of locations for the account:`n\" \r\n", - " az account list-locations --query \"[].{Locations: name}\" -o table\r\n", - " return\r\n", - " } \r\n", - "\r\n", - " $SQLPackageWrapperLocal = \"$Env:USERPROFILE\" + \"\\Downloads\\batchApplicationPackage.zip\" #path to download application package\r\n", - " $downloaded= Test-Path $SQLPackageWrapperLocal \r\n", - "\r\n", - " #check if Batch account exists in the specified resource group\r\n", - " $existingBatchAccount = az batch account show -n $BatchAccountName -g $ResourceGroupName --subscription $Subscription -o JSON 2>$null | ConvertFrom-Json\r\n", - "\r\n", - " if ($existingBatchAccount){ #Batch Account exists, \r\n", - " Write-output \"Found Batch Account '$BatchAccountName'\"\r\n", - "\r\n", - " #checking if storage account is set for Batch Account\r\n", - " #$existingStorageAccount = az storage account show -n $StorageAccountName -g $ResourceGroupName --subscription $Subscription -o JSON 2>$null | ConvertFrom-Json # check if storage account set is ADP Storage A/c\r\n", - " $connectedToStorage= $existingBatchAccount.autoStorage #.storageAccountId -eq $existingStorageAccount.id \r\n", - "\r\n", - " # check if latest application package is installed \r\n", - " $appPackageInstalled = az batch application package show --application-name $ApplicationName --version-name $ApplicationPackageVersionName -n $BatchAccountName -g $ResourceGroupName --subscription $Subscription -o JSON 2>$null | ConvertFrom-Json\r\n", - "\r\n", - " if($connectedToStorage -and $appPackageInstalled){ # BatchAccount connected to specified storageaccount and latest applicationpackage is installed\r\n", - " $connectedStorageAccName = $existingBatchAccount.autoStorage.storageAccountId.Split(\"/\")[-1]\r\n", - " Write-Output \"Already connected to Storage account '$connectedStorageAccName' `nFound latest application package installed. `nReusing existing Resources\"\r\n", - " }\r\n", - " else {\r\n", - " if(!$connectedToStorage){ # Connect to ADPStorageAccount \r\n", - "\r\n", - " az batch account set --storage-account $StorageAccountName -n $BatchAccountName --resource-group $ResourceGroupName --subscription $Subscription -o none | ConvertFrom-JSON \r\n", - " Write-Output \"No Storage Account Found. Connected to storage account '$StorageAccountName'\"\r\n", - " }\r\n", - " if(!$appPackageInstalled) { #application package is not installed, installing application package \r\n", - " if(!$downloaded) { Invoke-WebRequest -Uri $ApplicationPackageURL -OutFile $SQLPackageWrapperLocal } #downloads package to current user's downloads folder\r\n", - " az batch application package create --application-name $ApplicationName --version-name $ApplicationPackageVersionName --package-file $SQLPackageWrapperLocal --name $BatchAccountName --resource-group $ResourceGroupName --subscription $Subscription -o none\r\n", - " Write-Output \"No Application package found. Installed application package under '$ApplicationName'\"\r\n", - " }\r\n", - " Write-Output \"Batch Account '$BatchAccountName' is provisioned as required\"\r\n", - " }\r\n", - " } else { #Provision Batch Account\r\n", - "\r\n", - " $newBatchAccount = az batch account create -n $BatchAccountName -l $BatchAccountLocation --resource-group $ResourceGroupName --subscription $Subscription -o JSON | ConvertFrom-JSON\r\n", - " Write-Output \"Created Batch Account '$($newBatchAccount.name)'\" \r\n", - " az batch account set --storage-account $StorageAccountName -n $BatchAccountName --resource-group $ResourceGroupName --subscription $Subscription -o none \r\n", - " Write-Output \"Set Storage Account to '$StorageAccountName'\" \r\n", - " if(!$downloaded) { Invoke-WebRequest -Uri $ApplicationPackageURL -OutFile $SQLPackageWrapperLocal } #downloads package to current user's downloads folder\r\n", - " az batch application package create --application-name $ApplicationName --version-name $ApplicationPackageVersionName --package-file $SQLPackageWrapperLocal --name $BatchAccountName --resource-group $ResourceGroupName --subscription $Subscription -o none \r\n", - " Write-Output \"Installed application package '$ApplicationName'\" \r\n", - " \r\n", - " } \r\n", - "}\r\n", - "\r\n", - "function Setup-FuncRBAC {\r\n", - " [CmdletBinding()]\r\n", - " param (\r\n", - " [Parameter(Mandatory=$true)][ValidateNotNullOrEmpty()][string]$Subscription,\r\n", - " [Parameter(Mandatory=$true)][ValidateNotNullOrEmpty()][string]$ResourceGroupName,\r\n", - " [Parameter(Mandatory=$true)][ValidateNotNullOrEmpty()][string]$AzFunctionName,\r\n", - " [Parameter(Mandatory=$false)][ValidateNotNullOrEmpty()][string]$Role=\"Contributor\"\r\n", - " )\r\n", - "\r\n", - " # Get the resource group's scope\r\n", - " $rg = az group show --resource-group $ResourceGroupName --subscription $Subscription | ConvertFrom-Json\r\n", - " if (!$rg) {\r\n", - " Write-Error \"Failed to setup RBAC for $AzFunctionName... Resource Group '$ResourceGroupName' not found on subscription '$Subscription'\"\r\n", - " exit\r\n", - " }\r\n", - "\r\n", - " $scope = $rg.id\r\n", - " Write-Output \"Found scope... $scope\"\r\n", - " \r\n", - " # Get the az function principal id\r\n", - " $app = az functionapp show -n $AzFunctionName --resource-group $ResourceGroupName --subscription $Subscription | ConvertFrom-Json\r\n", - " if (!$app) {\r\n", - " Write-Error \"Failed to setup RBAC for $AzFunctionName... Az function not found!\"\r\n", - " exit\r\n", - " }\r\n", - "\r\n", - " $prid = $app.identity.principalId\r\n", - " Write-Output \"Found principal id of Azure function... $prid\"\r\n", - "\r\n", - " # Verify if a role assignment has been created\r\n", - " $assignments = az role assignment list --subscription $Subscription -g $ResourceGroupName | ConvertFrom-Json\r\n", - "\r\n", - " # Get the correct assignment\r\n", - " $svcPrincipal = $assignments | Where-Object {($_.scope -eq $scope) -and ($_.principalType -eq \"ServicePrincipal\") -and ($_.principalId -eq $prid)}\r\n", - " $id = $svcPrincipal.id\r\n", - " $def = $svcPrincipal.roleDefinitionName\r\n", - "\r\n", - " Write-Output \"Found service principal role... '$def'\" \r\n", - " # Check if the right role is assigned\r\n", - " if ($def -eq $Role)\r\n", - " { \r\n", - " Write-Output \"Service Principle '$prid' found with '$Role' role. No work needed\"\r\n", - " return\r\n", - " }\r\n", - "\r\n", - " # Continue to setup RBAC, once we verify an assignment is not setup and all the resources exist\r\n", - " Write-Output \"Creating new role assignment by running: 'az functionapp identity assign -g $ResourceGroupName -n $AzFunctionName --scope $scope --subscription $Subscription'\"\r\n", - " Write-Warning \"If your account does not have the access to assign new roles as Owner or User Access Administrator for the resource group, than you will need to contact your Azure AD Administrator to assign a service principle using the commands above\" \r\n", - " az functionapp identity assign -g $ResourceGroupName -n $AzFunctionName --scope $scope --subscription $Subscription \r\n", - "}\r\n", - "\r\n", - "Write-Host \"Helper Functions Created successfully\" " - ], - "metadata": { - "azdata_cell_guid": "f524afff-ccca-4e19-94a1-dbe21aee2241", - "tags": [ - "hide_input" - ] - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Connect to Azure and Select Subscription\r\n", - "Run the below cell to login to an Azure account. Be sure to check the Windows Taskbar for a subscription selection dialog box.\r\n", - "\r\n", - "_Note: the dialog box window may appear behind the active Azure Data Studio window._" - ], - "metadata": { - "azdata_cell_guid": "4952284c-dec7-4df3-9a31-044d795ef279" - } - }, - { - "cell_type": "code", - "source": [ - "Login-Azure" - ], - "metadata": { - "azdata_cell_guid": "112d7a12-f66d-41c0-8a1c-0b234506d5ef", - "tags": [ - "hide_input" - ] - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Create Resource Group\r\n", - "The Data Portability Notebooks provision several resources to enable import and export of Azure SQL DB databases and Azure SQL Managed Instance databases. These resources include Storage, Azure Batch, Azure Function resources and their dependent Azure resources. All resources created by this notebook are associated with a common Azure Resource Group to enable easy management of resources related to Data Portability.\r\n", - "\r\n", - "_Note: this notebook will reuse the Resource Group specified if it already exists._" - ], - "metadata": { - "azdata_cell_guid": "8298d187-3021-485c-b4d4-442da9aadd53" - } - }, - { - "cell_type": "code", - "source": [ - "Setup-AzResourceGroup -ResourceGroupName $AdpResourceGroup -ResourceGroupLocation $AdpRegion -Subscription $AdpSubscription" - ], - "metadata": { - "azdata_cell_guid": "9beb8d22-4560-4c7e-917b-5a3c0d58e1a2", - "tags": [ - "hide_input" - ] - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Provision Virtual Network\r\n", - "\r\n", - "The Data Portability resources include Virtual Machines created to execute the database import and export operations. To provision this in a secure mode, this notebook will provision a Virtual Network to attach the Virtual Machines to. These VMs do not need to be directly accessed so no publicly addressable ports should be created." - ], - "metadata": { - "azdata_cell_guid": "8fd75088-78fa-4cf3-b0f2-166813d0bed1" - } - }, - { - "cell_type": "code", - "source": [ - "Setup-AzVirtualNetwork -VNetName $AdpVNET -ResourceGroupName $AdpResourceGroup -Subscription $AdpSubscription" - ], - "metadata": { - "azdata_cell_guid": "d014a6a6-57ff-4de7-8210-b3360bf34daa" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Provision Storage Account\r\n", - "\r\n", - "The Data Portability resources require a Storage Account to attach the Virtual Machines to. This storage account is used for maintaining log files and other Azure Batch related information. This Storage Account can be the same or different than the Storage Acount used during Import and Export operations to store the logical database (.bacpac) files." - ], - "metadata": { - "azdata_cell_guid": "4e91a3f7-a676-4a4e-9d6f-3a343fcc0bc0" - } - }, - { - "cell_type": "code", - "source": [ - "Setup-AzStorageAccount -StorageAccountName $AdpStorage -ResourceGroupName $AdpResourceGroup -Subscription $AdpSubscription" - ], - "metadata": { - "azdata_cell_guid": "290498ee-3f31-4395-adab-a5fa93d28c80", - "tags": [ - "hide_input" - ] - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Provision Data Portability Orchestrator\r\n", - "\r\n", - "The Data Portability resources include an orchestration component which coordinates the import and export operations. The orchestrator is built as an Azure Function and provides the REST APIs that the Import and Export notebooks use to either import or export an Azure SQL DB Server and its databases." - ], - "metadata": { - "azdata_cell_guid": "d1291c22-e5b7-42b3-bdd9-8a8d13fd3165" - } - }, - { - "cell_type": "code", - "source": [ - "Setup-AzFunctionApp -FunctionName $AdpFunc -StorageAccountName $AdpStorage -FunctionAppPackageURL $AdpDownloadUrl `\r\n", - " -ConsumptionPlanLocation $AdpRegion -ResourceGroupName $AdpResourceGroup -Subscription $AdpSubscription" - ], - "metadata": { - "azdata_cell_guid": "6fc2b5ec-c16f-4eb7-b2f9-c8c680d9a2df", - "tags": [ - "hide_input" - ] - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Provision Hosted SqlPackage Application\r\n", - "To Data Portability solution leverages SqlPackage and DacFx to perform the import and export of each database. To host SqlPackage the solution uses an Azure Batch application. Azure Batch provides the ability to create a pool of Virtual Machines that can perform the import and export operations. This notebook will provision a pool of 2 virutal machines. The machines are not actually allocated until the first Import or Export operation is performed using the Import or Export notebooks. " - ], - "metadata": { - "azdata_cell_guid": "126a5abd-a3f4-46e0-b311-8cd1b8639127" - } - }, - { - "cell_type": "code", - "source": [ - "Setup-AzBatchAccount -BatchAccountName $AdpBatch -StorageAccountName $AdpStorage -BatchAccountLocation $AdpRegion `\r\n", - " -ApplicationPackageURL $AdpWrapperUrl -ResourceGroupName $AdpResourceGroup -Subscription $AdpSubscription" - ], - "metadata": { - "azdata_cell_guid": "489733c4-1162-479b-82b4-b0c18954b25b", - "tags": [] - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Grant Data Portability Controller Access to Data Portability Resources\r\n", - "The Data Portability orchestrator requires Contributor access to the other Data Portability resources created. To do this, Azure RBAC (role based access control) is used to grant access to the orchestrator so that it can communicate with the hosted SqlPackage application. The function below will verify a Service Principal was created with the proper scope or attempt to create the role assignment. Any user executing this notebook will need to have Owner or User Access Administrator permissions to the Resource Group to assign the permission. Otherwise, contact your Azure AD Administrator. " - ], - "metadata": { - "azdata_cell_guid": "3d03a987-4bec-4c0b-afdb-e6ae3c828252" - } - }, - { - "cell_type": "code", - "source": [ - "Setup-FuncRBAC -AzFunctionName $AdpFunc -ResourceGroupName $AdpResourceGroup -Subscription $AdpSubscription" - ], - "metadata": { - "azdata_cell_guid": "75882d3a-2004-4304-ab8f-e5146e14500c", - "tags": [ - "hide_input" - ] - }, - "outputs": [], - "execution_count": null - } - ] -} \ No newline at end of file diff --git a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/glossary.md b/extensions/azurehybridtoolkit/notebooks/hybridbook/content/glossary.md deleted file mode 100644 index 1050049630..0000000000 --- a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/glossary.md +++ /dev/null @@ -1,39 +0,0 @@ -# Glossary -[Home](readme.md) - -A list of terms and their definitions can be found below - -* **ADS** - *Azure Data Studio* is a desktop tool for managing Azure Data resources in the cloud, on-premises, or hybrid environments. -* **ADS Notebook** - Feature of ADS that enables creating, sharing text alongside code blocks for execution with various Notebook kernels. -* **API** - *application programming interface*, a standard way for computers to interact programmatically such as through a web service or other function. -* **Azure** - a cloud computing service created by Microsoft. -* **Azure Batch** - an Azure service that enables high performance parallel computing tasks in the cloud. -* **Azure Blob Storage** - an Azure storage service used to store unstructured data as Binary Large Objects or BLOBs -* **Azure CLI** - Azure *command line interface*, a useful desktop library to interact with Azure resources from the command line -* **Azure Region** - Azure data centers are separated by geography into regions in order to ensure low-latency and security -* **Azure Resource Group** - a logical container of Azure resources for management of Azure resources within it -* **Azure SQL Database** - an Azure based version of SQL database offered as a PaaS -* **Azure SQL Managed Instance** - a PaaS Azure offer for SQL Server that is ran on Azure infrastructure. Microsoft will manage the complexities of the infrastructure for the user -* **Azure SQL Virtual Machine (VM)** - a IaaS Azure offer for SQL Server that is ran on Azure infrastructure. Microsoft will manage the complexities of the infrastructure for the user -* **Azure Storage Account** - provides a unique namespace in Azure which forms the basis for addressing objects in Azure Storage -* **Azure Storage SKU** - a *stock keeping unit* or SKU is a product unit designation that distinguishes between the product and other products based on its attributes such as manufacturer, description, and product type. (see Appendices for a list of Storage SKU types) -* **Azure Subscription** - associates a user with Azure resources created by that user -* **CLI** - *command line interface*, allows execution of programs using textual processing line-by-line which is useful for automation using scripts or pasted input. -* **HADR** - *High Availability, Disaster Recovery* HA is the degree which a system is functional for customers. DR is a set of processes, techniques, and methodologies that ensure fault tolerance following natural and man-made disasters. -* **IaaS** - *infrastructure-as-a-Service* a cloud service that allows provisioning and managing of infrastructure over the web -* **Jupyter Book** - A collection of Jupyter Notebooks bound with a table of contents to define a notebook structure. -* **Jupyter Kernel** - a wrapper program that handles requests for executing and inspecting code in a Notebook -* **Juptyer Notebook** - an open source web application that is the underlying technology for ADS Notebooks that features live code, equations, and visualizations alongside narrative text. -* **Juptyer Project** - a non-profit, OSS project that supports interactive data science computing across all programming languages -* **Markdown** - lightweight markup language with text formatting widely used in web documents -* **Microsoft PowerShell** - a task automation and configuration management framework. ADS Notebooks feature a PowerShell kernel in order to interface with the PS environment. PS is often used by systems engineers and admins. -* **Offline Migration** - moving data between environments in a manner that is disruptive to the operation of the database, usually when a system is able to tolerate the downtime required as a trade-off for simplicity -* **OSS** - *open source software* is a decentralized software development model that encourages open collaboration and sharing. -* **PaaS** - *Platform-as-a-Service* is a cloud service that allows the use of an application without the complexity of building and maintaining the hardware infrastructure required by the application -* **Point-to-Site (P2S) VPN** - a VPN solution that allows a secure connection from an individual client computer to a remote network on the VPN. -* **Python** - an interpreted high level OSS programming language often used by data scientists -* **Site-to-Site (S2S) VPN** - a VPN connection created between two sites, usually over the public Internet or a WAN connection. Normally requires a network adminstrator to setup and configure. -* **SQL Assessment API** - evaluates a SQL instance configuration for best practices -* **SQL Virtual Machine** - an IaaS Azure offer that provisions and manages virtual machine with SQL Server installed -* **SMO** - SQL Management Objects are "objects designed for programmatic management of Microsoft SQL Server" ([Microsoft](https://docs.microsoft.com/en-us/sql/relational-databases/server-management-objects-smo/overview-smo)) -* **VPN** - a *virtual private network* is a collection of computing resources that organizes and extends a private network configuration over the public Internet, normally using some kind of encryption for security and privacy. \ No newline at end of file diff --git a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/hadr/backup-to-blob.ipynb b/extensions/azurehybridtoolkit/notebooks/hybridbook/content/hadr/backup-to-blob.ipynb deleted file mode 100644 index b96078d201..0000000000 --- a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/hadr/backup-to-blob.ipynb +++ /dev/null @@ -1,373 +0,0 @@ -{ - "metadata": { - "kernelspec": { - "name": "powershell", - "display_name": "PowerShell", - "language": "powershell" - }, - "language_info": { - "name": "powershell", - "codemirror_mode": "shell", - "mimetype": "text/x-sh", - "file_extension": ".ps1" - } - }, - "nbformat_minor": 2, - "nbformat": 4, - "cells": [ - { - "cell_type": "markdown", - "source": [ - "Backup Database Azure Blob Storage\n", - "==================================\n", - "\n", - "Description\n", - "-----------\n", - "\n", - "Notebook to walk through Azure Storage configuration, key generation, and scripting of a backup to blob storage command for on-premises SQL Server Database." - ], - "metadata": { - "azdata_cell_guid": "6af59d69-ade7-480a-b33e-52a86fe5bfd3" - } - }, - { - "cell_type": "markdown", - "source": [ - "Steps of this procedure include:\r\n", - "1. Connect to Azure subscription\r\n", - "2. Provision resource group for SQL VM migration\r\n", - "3. Create a storage account\r\n", - "4. Create container for Blobs\r\n", - "5. Get Azure storage account key\r\n", - "6. Get Azure storage container Uri\r\n", - "7. Create Sql credentials on Sql server instances\r\n", - "8. Backup database on specific instance of Sql Server\r\n", - "9. Remove Sql credentials which were created on Sql server instances" - ], - "metadata": { - "azdata_cell_guid": "b911ea4f-a3d8-4ac1-bff6-6c5eb1b514f9" - } - }, - { - "cell_type": "code", - "source": [ - "#Set Parameters\r\n", - "$Location = \"\" # Specify the valid Location such as 'West US 2','EASTUS' etc...\r\n", - "$ResourceGroup = \"\" # Resource group name(It should be of alphabets case insensitive)\r\n", - "$SkuName = \"\" # Specify the valid SKU...Such as 'Standard_LRS'\r\n", - "\r\n", - "$SqlPath = \"sqlserver:\\sql\\$($env:COMPUTERNAME)\" #This script will generate Sql Path" - ], - "metadata": { - "azdata_cell_guid": "7ad525ec-4993-4e14-9677-4f77433b2123", - "tags": [ - "parameters" - ] - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "### Sign in to Azure \r\n", - "Sign in to your Azure Subscription with the _Connect-AzAccount_ command and follow the on-screen directions." - ], - "metadata": { - "azdata_cell_guid": "5dd9519d-3957-46ef-8988-440a043535b2" - } - }, - { - "cell_type": "code", - "source": [ - "Connect-AzAccount" - ], - "metadata": { - "azdata_cell_guid": "18c920f2-a19a-49d6-9766-2d7539f6fe43" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "### Get Subscription\r\n", - "Below command will open a _**Dialouge Box**_ with list of subscriptions. Selecting one of those will set that Subscription for rest of the commands." - ], - "metadata": { - "azdata_cell_guid": "cc6cb8c6-76b7-41a0-ab26-7713e72c2f7d" - } - }, - { - "cell_type": "code", - "source": [ - "$Subscription = Get-AzSubscription | Out-GridView -PassThru\r\n", - "Set-AzContext -SubscriptionName $Subscription" - ], - "metadata": { - "azdata_cell_guid": "741c35fa-7923-4200-8c3a-497d62b4ae66" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "_If you don't know which Location you want to use, you can list the available Locations. Display the list of Locations by using the following code example and find the one you want to use. This example uses eastus. Store the Location in a variable and use the variable so you can change it in one place._" - ], - "metadata": { - "azdata_cell_guid": "57de9eb7-a20b-4e9c-bf7b-ed6f2a838bee" - } - }, - { - "cell_type": "code", - "source": [ - "Get-AzLocation | select Location\r\n", - "$Location" - ], - "metadata": { - "azdata_cell_guid": "6b0ea659-6dcd-48e1-a0d3-47b4a3ea9d66" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "\r\n", - "### Create a resource group\r\n", - "Create an Azure resource group with _New-AzResourceGroup_. A resource group is a logical container into which Azure resources are deployed and managed." - ], - "metadata": { - "azdata_cell_guid": "455a6002-e5d5-4cb0-9146-8d417917c751" - } - }, - { - "cell_type": "code", - "source": [ - "# Create Azure resource group, if necessary\r\n", - "$ResourceGroup = Get-AzResourceGroup -Name $ResourceGroup\r\n", - "\r\n", - "if (!$ResourceGroup)\r\n", - "{\r\n", - " # Need to create a new resource group\r\n", - " Write-Output \"Resource Group $ResourceGroup does not exist. Creating...\"\r\n", - " $ResourceGroup = New-AzResourceGroup -Name $RG1 -Location $Location\r\n", - "}" - ], - "metadata": { - "azdata_cell_guid": "d48948e1-3136-4e42-91e7-77a3301ae97d" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "### Create a storage account\r\n", - "Create a standard, general-purpose storage account with LRS replication by using _New-AzStorageAccount_. Next, get the storage account context that defines the storage account you want to use. When acting on a storage account, reference the context instead of repeatedly passing in the credentials. Use the following example to create a storage account called storageaccountazure with locally redundant storage (LRS) and blob encryption (enabled by default)." - ], - "metadata": { - "azdata_cell_guid": "246d062f-e7d1-4f42-ba9f-153a12a286e4" - } - }, - { - "cell_type": "code", - "source": [ - "$StorageAccountName = \"storageaccountazure\"\r\n", - "$StorageAccount = New-AzStorageAccount -ResourceGroupName $ResourceGroup -Name $StorageAccountName -SkuName $SkuName -Location $Location\r\n", - "\r\n", - "$Ctx = $StorageAccount.Context" - ], - "metadata": { - "azdata_cell_guid": "17e3421b-a80a-455c-bd6a-25daaff46514" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "### Create a container\r\n", - "Blobs are always uploaded into a container. You can organize groups of blobs like the way you organize your files on your computer in folders.\r\n", - "Set the container name, then create the container by using _New-AzStorageContainer_. Set the permissions to blob to allow public access of the files. The container name in this example is quickstartblobs." - ], - "metadata": { - "azdata_cell_guid": "82503754-84ea-47b7-bcaf-31e5012da008" - } - }, - { - "cell_type": "code", - "source": [ - "$ContainerName = \"quickstartblobs\"\r\n", - "New-AzStorageContainer -Name $ContainerName -Context $Ctx -Permission blob" - ], - "metadata": { - "azdata_cell_guid": "c4557040-5ff7-4f31-9243-01b850270b90", - "tags": [] - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "### Get Azure Storage Acount Key\r\n", - "This script will get the key for Storage Account which is been created." - ], - "metadata": { - "azdata_cell_guid": "f48b23bf-01bc-48df-afed-ad3eea69c41d" - } - }, - { - "cell_type": "code", - "source": [ - "$StorageAccountKey = `\r\n", - " (Get-AzStorageAccountKey `\r\n", - " -ResourceGroupName $ResourceGroup `\r\n", - " -Name $StorageAccountName).Value[0]" - ], - "metadata": { - "azdata_cell_guid": "e450bc4a-3373-4c7e-b0d0-f334712298b2" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "### Get Azure Storage Container Uri\r\n", - "The following script can be used to get the Uri of Storage container." - ], - "metadata": { - "azdata_cell_guid": "5f7e616b-1e9f-4640-b61f-78d4327c5562" - } - }, - { - "cell_type": "code", - "source": [ - " $StorageUri = (Get-AzStorageAccount -ResourceGroupName $ResourceGroup -Name $StorageAccountName | Get-AzStorageContainer | Where-Object { $_.Name -eq $AzureContainerName }).CloudBlobContainer.Uri.AbsoluteUri " - ], - "metadata": { - "azdata_cell_guid": "615d8e6e-2cbf-4001-8da0-1826185a06bf" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "### Create a SQL credential on all Instances of SQL Server \r\n", - "The following script can be used to create a generic SQL credential on all the Instances of SQL Server on a computer. If there's already an existing credential with the same name on one of the Instances of the computer, the script shows the error and continues." - ], - "metadata": { - "azdata_cell_guid": "000a1320-fecf-4e0b-8d1f-00ab39fc44ce" - } - }, - { - "cell_type": "code", - "source": [ - "set-executionpolicy remotesigned\r\n", - "# load the sqlps module\r\n", - "import-module sqlps \r\n", - " \r\n", - "# set parameters\r\n", - "$SecureString = ConvertTo-SecureString $StorageAccountKey -AsPlainText -Force \r\n", - "$CredentialName = \"myCredential-$(Get-Random)\"\r\n", - "\r\n", - "Write-Host \"Generate credential: \" $CredentialName\r\n", - " \r\n", - "#cd to sql server and get Instances \r\n", - "cd $SqlPath\r\n", - "$Instances = Get-ChildItem\r\n", - "\r\n", - "#loop through Instances and create a SQL credential, output any errors\r\n", - "foreach ($Instance in $Instances) {\r\n", - " try {\r\n", - " $Path = \"$($SqlPath)\\$($Instance.DisplayName)\\credentials\"\r\n", - " New-SqlCredential -Name $CredentialName -Identity $StorageAccountName -Secret $SecureString -Path $Path -ea Stop | Out-Null\r\n", - " Write-Host \"...generated credential $($Path)\\$($CredentialName).\" }\r\n", - " catch { Write-Host $_.Exception.Message } }" - ], - "metadata": { - "azdata_cell_guid": "f59b2416-a41f-43c0-85ea-1e113e6f4221", - "tags": [] - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "### Full backup for system Databases on a specific Instance of SQL Server \r\n", - "The full script can be used to back up the master and the msdb Databases on a named Instance of SQL Server. The same script can be used for any Instance by changing the Instance parameter value. SQL Server's default Instance is named DEFAULT." - ], - "metadata": { - "azdata_cell_guid": "cca91ca6-bf10-4e32-9b4f-80133049c1b5" - } - }, - { - "cell_type": "code", - "source": [ - "set-executionpolicy remotesigned \r\n", - "import-module sqlps \r\n", - "\r\n", - "$InstanceName = \"DEFAULT\"\r\n", - "$BackupUrlContainer = \"https://$StorageAccountName.blob.core.windows.net/$ContainerName/\" \r\n", - "\r\n", - "Write-Host \"Backup Database: \" $InstanceName \" to \" $BackupUrlContainer\r\n", - " \r\n", - "cd \"$($SqlPath)\\$($InstanceName)\"\r\n", - "\r\n", - "#loop through Instance and backup specific Databases\r\n", - "$Databases = \"master\" \r\n", - "foreach ($Database in $Databases) {\r\n", - " try {\r\n", - " Write-Host \"...starting backup: \" $Database\r\n", - " Backup-SqlDatabase -Database $Database -BackupContainer $BackupUrlContainer -SqlCredential $CredentialName -Compression On\r\n", - " Write-Host \"...backup complete.\" }\r\n", - " catch { Write-Host $_.Exception.Message } }" - ], - "metadata": { - "azdata_cell_guid": "9b21ab69-5be2-4d09-ac4b-3d43521a1188" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "### Remove a SQL credential from Instances of SQL Server \r\n", - "The following script can be used to remove a specific credential from all the Instances of SQL Server installed on the computer. If the credential does not exist on a specific Instance, an error message is displayed, and the script continues until all Instances are checked." - ], - "metadata": { - "azdata_cell_guid": "63b41df3-6fe6-421e-9587-e5d65a0a5592" - } - }, - { - "cell_type": "code", - "source": [ - "set-executionpolicy remotesigned \r\n", - "import-module sqlps\r\n", - "\r\n", - "Write-Host \"Delete credential: \" $CredentialName\r\n", - "\r\n", - "cd $SqlPath\r\n", - "$Instances = Get-ChildItem\r\n", - "\r\n", - "#loop through Instances and delete a SQL credential\r\n", - "foreach ($Instance in $Instances) {\r\n", - " try {\r\n", - " $Path = \"$($SqlPath)\\$($Instance.DisplayName)\\credentials\\$($CredentialName)\"\r\n", - " Remove-SqlCredential -Path $Path -ea Stop | Out-Null\r\n", - " Write-Host \"...deleted credential $($Path).\" }\r\n", - " catch { Write-Host $_.Exception.Message } }" - ], - "metadata": { - "azdata_cell_guid": "e195e874-6178-4f87-b5e8-94524cbc6ea1" - }, - "outputs": [], - "execution_count": null - } - ] -} \ No newline at end of file diff --git a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/hadr/configure-failover.ipynb b/extensions/azurehybridtoolkit/notebooks/hybridbook/content/hadr/configure-failover.ipynb deleted file mode 100644 index c8c7134c2c..0000000000 --- a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/hadr/configure-failover.ipynb +++ /dev/null @@ -1,392 +0,0 @@ -{ - "metadata": { - "kernelspec": { - "name": "powershell", - "display_name": "PowerShell", - "language": "powershell" - }, - "language_info": { - "name": "powershell", - "codemirror_mode": "shell", - "mimetype": "text/x-sh", - "file_extension": ".ps1" - } - }, - "nbformat_minor": 2, - "nbformat": 4, - "cells": [ - { - "cell_type": "markdown", - "source": [ - "# Create and configure Failover Cluster\n", - "\n", - "## Description\n", - "\n", - "Notebook to walk through creating a failover cluster on on-premises SQL Server Database." - ], - "metadata": { - "azdata_cell_guid": "6af59d69-ade7-480a-b33e-52a86fe5bfd3" - } - }, - { - "cell_type": "markdown", - "source": [ - "Note: before running the steps below, make sure to have the following:\n", - "\n", - "- A resource group with a domain controller.\n", - "- One or more domain joined VMs in Azure running SQL Server 2016 (or later) in the _same_ availability set or _different_ availability zones that have been registered with the SQL IaaS Agent extension\n", - "- Domain joined VMs must have either a \"Enterprise\" or \"Developer\" image SKU to create a cluster in this notebook\n", - "\n", - "Refer to the Tutorial on [How to Setup a Domain with VMs in Azure](https://docs.microsoft.com/azure/azure-sql/virtual-machines/windows/availability-group-manually-configure-prerequisites-tutorial) (only follow the steps up to setting the SQL Server Service Accounts)\n", - "\n", - "Here is a listing of variables that will be used to create the failover cluster:\n", - "\n", - "| Line # | Name | Description | Example |\n", - "| --- | --- | --- | --- |\n", - "| 1 | **Subscription** | Azure Subscription ID/Name with permission to host a domain. | ToolsSubscription_1234 |\n" - "| 2 | **ResourceGroup** | Name of new or existing resource group (RG). An Azure Resource Group is a collection of Azure resources that share the same permissions, policies, etc. | \"TestRG1\" |\n", - "| 3 | **Location** | Value representing the region or location of the RG. See [Azure Geographies](https://azure.microsoft.com/global-infrastructure/geographies/) for more information. | \"East US\" |\n", - "| 4 | **Publisher** | Name of publisher that offers SQL Server configurations that can be installed on virtual machines. Default is MicrosoftSQLServer | \"MicrosoftSQLServer\" |\n", - "| 5 | **StorageAccountName** | Name of the storage account that will be used as a file share witness. See [Cloud Witness](https://docs.microsoft.com/windows-server/failover-clustering/deploy-cloud-witness) for more information. | \"TestStorageAccount\" |\n", - "| 6 | **StorageSku** | Name of SKU for a storage account. There are SKUs for different types of storage such as Standard\\_LRS for Standard Locally Redundant Storage. See [Storage Redundancy](https://docs.microsoft.com/azure/storage/common/storage-redundancy) for more information. | \"Standard\\_LRS\" |\n", - "| 7 | **ClusterName** | Name of the failover cluster that will be created. A failover cluster is a group of independent computers that work together to increase the availability and scalability of clustered roles. See [Failover Cluster](https://docs.microsoft.com/windows-server/failover-clustering/failover-clustering-overview) for more information. | \"TestCluster\" |\n", - "| 8 | **FQDN** | Fully Qualified Domain Name, this localized URL address is created as shown in the domain setup. | \"domain.testsite.com\" |\n", - "| 9 | **ServerSku** | Edition of SQL Server, each has different capabilities based on purpose, only supported versions are Enterprise or Developer. | \"Enterprise\" |\n", - "| 10 | **ServiceAccount** | Username for the SQL server service account for the domain (password is required to run commands). Any active directory account with sufficient permissions such as the installation account may be used as the service account. (will be used on all SQL VMs running in the cluster) | \"serviceaccount@domain.testsite.com\" |\n", - "| 11 | **OperatorAccount** | Username for the cluster operator account, may or may not be the same as the service account. (will be used on all SQL VMs running in the cluster) | \"operatoraccount@domain.testsite.com\" |\n", - "| 12 | **BootstrapAccount** | Username for the bootstrap account, may or may not be the same as the service account. (will be used on all SQL VMs running in the cluster) | \"bootstrapaccount@domain.testsite.com\" |" - ], - "metadata": { - "azdata_cell_guid": "b911ea4f-a3d8-4ac1-bff6-6c5eb1b514f9" - } - }, - { - "cell_type": "code", - "source": [ - "#Set general parameters\r\n", - "$Subscription = \"\"\r\n" - "$ResourceGroup = \"\"\r\n" - "$Location = \"\"\r\n" - "$Publisher = \"\"\r\n" - "\r\n", - "#Create Storage Account\r\n", - "$StorageAccountName = \"\"\r\n", - "$StorageSku = \"\"\r\n", - "\r\n", - "#Define Cluster Media\r\n", - "$ClusterName = \"\"\r\n", - "$FQDN = \"\"\r\n", - "$ServerSku = \"\"\r\n", - "\r\n", - "#Add VMs to the cluster\r\n", - "$ServiceAccount = \"\"\r\n", - "$ServiceAccountPassword = \"\"\r\n", - "$OperatorAccount = \"\"\r\n", - "$OperatorAccountPassword = \"\"\r\n", - "$BootstrapAccount = \"\"\r\n", - "$BootstrapAccountPassword = \"\" - ], - "metadata": { - "azdata_cell_guid": "7ad525ec-4993-4e14-9677-4f77433b2123", - "tags": [ - "parameters" - ] - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "Steps of this procedure include:\n", - "\n", - "1. Connect to Azure subscription\n", - "2. Provision resource group for SQL VM migration\n", - "3. Create a storage account\n", - "4. Define cluster metadata\n", - "5. Add VMs to the cluster\n", - "6. Validate cluster" - ], - "metadata": { - "azdata_cell_guid": "34dad8cf-9457-474c-b6a3-34b003e9c71a" - } - }, - { - "cell_type": "markdown", - "source": [ - "### **Set Subscription**\r\n", - "Set the subscription for rest of the commands." - ], - "metadata": { - "azdata_cell_guid": "cc6cb8c6-76b7-41a0-ab26-7713e72c2f7d" - } - }, - { - "cell_type": "code", - "source": [ - "if(!$Subscription){\r\n", - " $Subscription = Get-AzSubscription | Out-GridView -PassThru\r\n", - "}\r\n", - "\r\n", - "Set-AzContext -SubscriptionName $Subscription\r\n", - "Connect-AzAccount -Subscription $Subscription" - ], - "metadata": { - "azdata_cell_guid": "741c35fa-7923-4200-8c3a-497d62b4ae66" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "\r\n", - "### **Create a resource group**\r\n", - "Create an Azure resource group with _New-AzResourceGroup_. A resource group is a logical container into which Azure resources are deployed and managed." - ], - "metadata": { - "azdata_cell_guid": "455a6002-e5d5-4cb0-9146-8d417917c751" - } - }, - { - "cell_type": "code", - "source": [ - "# Create Azure resource group, if necessary\r\n", - "if (!(Get-AzResourceGroup -Name $ResourceGroup))\r\n", - "{\r\n", - " # Need to create a new resource group\r\n", - " Write-Output \"Resource Group $ResourceGroup does not exist. Creating...\"\r\n", - " New-AzResourceGroup -Name $ResourceGroup -Location $Location\r\n", - "}" - ], - "metadata": { - "azdata_cell_guid": "d48948e1-3136-4e42-91e7-77a3301ae97d" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "### **Create a storage account**\r\n", - "Create a standard, general-purpose storage account with LRS replication by using _New-AzStorageAccount_. Next, get the storage account context that defines the storage account you want to use. When acting on a storage account, reference the context instead of repeatedly passing in the credentials. Use the following example to create a storage account called storageaccountazure with locally redundant storage (LRS) and blob encryption (enabled by default)." - ], - "metadata": { - "azdata_cell_guid": "246d062f-e7d1-4f42-ba9f-153a12a286e4" - } - }, - { - "cell_type": "code", - "source": [ - "$StorageAccount = New-AzStorageAccount -ResourceGroupName $ResourceGroup -Name $StorageAccountName `\r\n", - " -SkuName $StorageSku -Location $Location -Kind StorageV2 `\r\n", - " -AccessTier Hot -EnableHttpsTrafficOnly $true\r\n", - "\r\n", - "$Ctx = $StorageAccount.Context" - ], - "metadata": { - "azdata_cell_guid": "17e3421b-a80a-455c-bd6a-25daaff46514" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "### **Create a container**\r\n", - "Blobs are always uploaded into a container. Blobs can be organized in groups like files in folders.\r\n", - "Set the container name, then create the container by using _New-AzStorageContainer_. Set the permissions to blob to allow public access of the files. The container name in this example is quickstartblobs." - ], - "metadata": { - "azdata_cell_guid": "82503754-84ea-47b7-bcaf-31e5012da008" - } - }, - { - "cell_type": "code", - "source": [ - "$ContainerName = \"quickstartblobs\"\r\n", - "New-AzStorageContainer -Name $ContainerName -Context $Ctx -Permission blob" - ], - "metadata": { - "azdata_cell_guid": "c4557040-5ff7-4f31-9243-01b850270b90", - "tags": [] - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "### **Get Azure Storage Acount Key**\r\n", - "This script will get the key for Storage Account which is been created." - ], - "metadata": { - "azdata_cell_guid": "f48b23bf-01bc-48df-afed-ad3eea69c41d" - } - }, - { - "cell_type": "code", - "source": [ - "$StorageAccountKey = `\r\n", - " (Get-AzStorageAccountKey `\r\n", - " -ResourceGroupName $ResourceGroup `\r\n", - " -Name $StorageAccountName).Value[0]" - ], - "metadata": { - "azdata_cell_guid": "e450bc4a-3373-4c7e-b0d0-f334712298b2", - "tags": [] - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "### **Get Azure Storage Container Uri**\r\n", - "The following script can be used to get the Uri of Storage container." - ], - "metadata": { - "azdata_cell_guid": "5f7e616b-1e9f-4640-b61f-78d4327c5562" - } - }, - { - "cell_type": "code", - "source": [ - " $StorageUri = (Get-AzStorageAccount -ResourceGroupName $ResourceGroup -Name $StorageAccountName | Get-AzStorageContainer | Where-Object { $_.Name -eq $AzureContainerName }).CloudBlobContainer.Uri.AbsoluteUri " - ], - "metadata": { - "azdata_cell_guid": "615d8e6e-2cbf-4001-8da0-1826185a06bf" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "### **Azure SQL VM Offers**\n", - "\n", - "Run the following command to get updated list of offers for Microsoft SQL Server in your location. Pick the offer that matches the operating system version of the VM servers.\n", - "\n", - "Note that the SQL Version is first then appended with an operating system version. E.g.: \"WS2019\" means Windows Server 2019. Along with various versions of Windows Servers, there are also enterprise Linux versions such as RedHat Enterprise, Suse Enterprise, and Ubuntu. Some versions are BYOL (Bring Your Own License) aka [Hybrid Benefit](https://azure.microsoft.com/pricing/hybrid-benefit/)." - ], - "metadata": { - "azdata_cell_guid": "c17d224e-e514-4dfd-b181-06d2cde378e4" - } - }, - { - "cell_type": "code", - "source": [ - "$Offer = Get-AzVMImageOffer -Location $Location -Publisher $Publisher | Out-GridView -PassThru" - ], - "metadata": { - "azdata_cell_guid": "6b2fa291-43cb-4619-a010-3a373dababcc" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "### **Define cluster metadata**\n", - "\n", - "The following script is used to define the metadata for Windows Server Failover Cluster so that when the first SQL Server VM is added, the cluster is created as defined." - ], - "metadata": { - "azdata_cell_guid": "000a1320-fecf-4e0b-8d1f-00ab39fc44ce" - } - }, - { - "cell_type": "code", - "source": [ - "$StorageAccountUrl = \"https://$StorageAccountName.blob.core.windows.net/\"\r\n", - "$SecureSAKey = ConvertTo-SecureString $StorageAccountKey -AsPlainText -Force\r\n", - "if($OperatorAccount -eq \"\"){\r\n", - " $OperatorAccount = $ServiceAccount\r\n", - " $SecureOAPassword = ConvertTo-SecureString $ServiceAccountPassword -AsPlainText -Force\r\n", - "}\r\n", - "if($BootstrapAccount -eq \"\"){\r\n", - " $BootstrapAccount = $ServiceAccount\r\n", - " $SecureBAPassword = ConvertTo-SecureString $ServiceAccountPassword -AsPlainText -Force\r\n", - "}\r\n", - "\r\n", - "$group = New-AzSqlVMGroup -Name $ClusterName -Location $Location `\r\n", - " -ResourceGroupName $ResourceGroup -Offer $Offer.Offer `\r\n", - " -Sku $ServerSku -DomainFqdn $FQDN -ClusterOperatorAccount $OperatorAccount `\r\n", - " -ClusterBootstrapAccount $BootstrapAccount -SqlServiceAccount $ServiceAccount `\r\n", - " -StorageAccountUrl $StorageAccountUrl `\r\n", - " -StorageAccountPrimaryKey $SecureSAKey" - ], - "metadata": { - "azdata_cell_guid": "f59b2416-a41f-43c0-85ea-1e113e6f4221", - "tags": [] - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "### **Select a VM to add to the cluster**\n", - "\n", - "Selecting and adding the first SQL Server VM to the cluster creates the cluster. Additional SQL Server VM nodes can be added by running this cell again. **(This will take some time, please wait for the process to finish.)**" - ], - "metadata": { - "azdata_cell_guid": "cca91ca6-bf10-4e32-9b4f-80133049c1b5" - } - }, - { - "cell_type": "code", - "source": [ - "$sqlvm = Get-AzSqlVM -ResourceGroupName $ResourceGroup | Out-GridView -PassThru\r\n", - "$SecureSAPassword = ConvertTo-SecureString $ServiceAccountPassword -AsPlainText -Force\r\n", - "if(!$SecureOAPassword){\r\n", - " $SecureOAPassword = $SecureSAPassword\r\n", - "}\r\n", - "if(!$SecureBAPassword){\r\n", - " $SecureBAPassword = $SecureSAPassword\r\n", - "}\r\n", - "\r\n", - "$sqlvmconfig = Set-AzSqlVMConfigGroup -SqlVM $sqlvm `\r\n", - " -SqlVMGroup $group -ClusterOperatorAccountPassword $SecureOAPassword `\r\n", - " -SqlServiceAccountPassword $SecureSAPassword `\r\n", - " -ClusterBootstrapAccountPassword $SecureBAPassword\r\n", - "\r\n", - "Update-AzSqlVM -ResourceId $sqlvm.ResourceId -SqlVM $sqlvmconfig" - ], - "metadata": { - "azdata_cell_guid": "9b21ab69-5be2-4d09-ac4b-3d43521a1188", - "tags": [] - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "### **Validate Failover Cluster**\n", - "\n", - "After adding all the nodes to the cluster, connect to the virtual machine (ex. RDP) using an administrator account to run the following command via PowerShell:\n", - "\n", - "**Test-Cluster**\n", - "\n", - "The cluster must pass validation to be supported by Microsoft. Failure to do so leaves your cluster in an unsupported state." - ], - "metadata": { - "azdata_cell_guid": "3ce199e7-f88f-4592-b61c-e03ed2c7235a" - } - }, - { - "cell_type": "markdown", - "source": [ - "### **Post cluster creation steps**\n", - "\n", - "You can add additional nodes in the following notebook, then verify after doing so.\n", - "\n", - "Once all the desired SQL VMs in the domain have been added as nodes and the cluster verified, proceed to creating the [availability group](.\\configure-ag.ipynb)" - ], - "metadata": { - "azdata_cell_guid": "0d7d63f0-ef95-4e40-9139-69957c53e85f" - } - } - ] -} \ No newline at end of file diff --git a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/hadr/readme.md b/extensions/azurehybridtoolkit/notebooks/hybridbook/content/hadr/readme.md deleted file mode 100644 index c7cff79f18..0000000000 --- a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/hadr/readme.md +++ /dev/null @@ -1,14 +0,0 @@ -# High Availability and Disaster Recovery - -[Home](../readme.md) - -Notebooks to help with HADR tasks in a Hybrid Cloud environment. - -## Notebooks in this Chapter - -### Blob Storage Backup: -- [Backup Database to Blob Storage](backup-to-blob.ipynb) - -### Availability Group Configuration: -- [Create Failover Cluster](configure-failover.ipynb) -- [Create Availability Group]() (Coming Soon...) \ No newline at end of file diff --git a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/networking/download-VpnClient.ipynb b/extensions/azurehybridtoolkit/notebooks/hybridbook/content/networking/download-VpnClient.ipynb deleted file mode 100644 index 22069e472b..0000000000 --- a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/networking/download-VpnClient.ipynb +++ /dev/null @@ -1,172 +0,0 @@ -{ - "metadata": { - "kernelspec": { - "name": "powershell", - "display_name": "PowerShell", - "language": "powershell" - }, - "language_info": { - "name": "powershell", - "codemirror_mode": "shell", - "mimetype": "text/x-sh", - "file_extension": ".ps1" - } - }, - "nbformat_minor": 2, - "nbformat": 4, - "cells": [ - { - "cell_type": "markdown", - "source": [ - "## Create and install VPN client configuration\r\n", - "Point-to-Site connections use certificates to authenticate. This article shows how to create a self-signed root certificate and generate client certificates using PowerShell on Windows 10 or Windows Server 2016. If needs are for different certificate instructions, see [Certificates - Linux or Certificates - MakeCert](https://docs.microsoft.com/en-us/azure/vpn-gateway/vpn-gateway-certificates-point-to-site-linux).\r\n", - "\r\n", - "The steps performed in this article are on a computer running Windows 10 or Windows Server 2016. The PowerShell cmdlets that used to generate certificates are part of the operating system and do not work on other versions of Windows. The Windows 10 or Windows Server 2016 computer is only needed to generate the certificates. Once the certificates are generated, it can be uploaded or installed on any supported client operating system." - ], - "metadata": { - "azdata_cell_guid": "7bf685b8-e375-47ed-93a3-5072c6a27235" - } - }, - { - "cell_type": "code", - "source": [ - "$RG = \"\" # Name of intended Resource Group\r\n", - "$GWName = \"\" # VPN Gateway name\r\n", - "$P2SRootCertName = \"\" # Name of Root Certificate. For example 'P2SRootCert2.cer'\r\n", - "$filePathForCert = \"\" # Path of the certificate file. For example 'D:\\Downloads\\rootcert2.cer'" - ], - "metadata": { - "azdata_cell_guid": "8eab9fb0-1e66-4a34-8e32-cf0644b157d9", - "tags": [ - "parameters" - ] - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "### **Create a self-signed root certificate**\r\n", - "_New-SelfSignedCertificate_ cmdlet is used to create a self-signed root certificate. For additional parameter information, see [New-SelfSignedCertificate](https://technet.microsoft.com/itpro/powershell/windows/pkiclient/new-selfsignedcertificate).\r\n", - "\r\n", - "Use the following example to create the self-signed root certificate. The following example creates a self-signed root certificate named 'P2SRootCert' that is automatically installed in 'Certificates-Current User\\Personal\\Certificates'. certificate can be viewed by opening certmgr.msc, or Manage User Certificates." - ], - "metadata": { - "azdata_cell_guid": "656a1830-ee0b-4a3f-aa54-0eb3435d730a" - } - }, - { - "cell_type": "code", - "source": [ - "$cert = New-SelfSignedCertificate -Type Custom -KeySpec Signature `\r\n", - "-Subject \"CN=P2SRootCert\" -KeyExportPolicy Exportable `\r\n", - "-HashAlgorithm sha256 -KeyLength 2048 `\r\n", - "-CertStoreLocation \"Cert:\\CurrentUser\\My\" -KeyUsageProperty Sign -KeyUsage CertSign" - ], - "metadata": { - "azdata_cell_guid": "4348e0f6-957b-47f6-99db-8a4d4f6f4d25" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "### **Generate a client certificate**\r\n", - "Each client computer that connects to a VNet using Point-to-Site must have a client certificate installed. A client certificate can be generated from the self-signed root certificate, and then it can export and install the client certificate. If the client certificate is not installed, authentication fails.\r\n", - "This example continues from the previous section and uses the declared '$cert' variable.\r\n", - "\r\n", - "Modify and run the example to generate a client certificate. If the following example is executed without modifying it, the result is a client certificate named 'P2SChildCert'. If name of the child certificate needs to be something else,then modify the CN value. Do not change the TextExtension when running this example. The client certificate which is generated is automatically installed in 'Certificates - Current User\\Personal\\Certificates' on computer." - ], - "metadata": { - "azdata_cell_guid": "a5a31ec0-2ead-4b41-9817-e81ea70082c0" - } - }, - { - "cell_type": "code", - "source": [ - "New-SelfSignedCertificate -Type Custom -DnsName P2SChildCert -KeySpec Signature `\r\n", - "-Subject \"CN=P2SChildCert\" -KeyExportPolicy Exportable `\r\n", - "-HashAlgorithm sha256 -KeyLength 2048 `\r\n", - "-CertStoreLocation \"Cert:\\CurrentUser\\My\" `\r\n", - "-Signer $cert -TextExtension @(\"2.5.29.37={text}1.3.6.1.5.5.7.3.2\")" - ], - "metadata": { - "azdata_cell_guid": "c993fb71-9c27-4b70-8477-3a0003825db4", - "tags": [] - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "### **Replace the file path variable with file path to the exported root certificate, and then run the variable cmdlets:**" - ], - "metadata": { - "azdata_cell_guid": "5df2cb79-0fe4-414e-a1f4-9f2270634d54" - } - }, - { - "cell_type": "code", - "source": [ - "$cert = new-object System.Security.Cryptography.X509Certificates.X509Certificate2($filePathForCert)\r\n", - "$CertBase64_3 = [system.convert]::ToBase64String($cert.RawData)\r\n", - "$p2srootcert = New-AzVpnClientRootCertificate -Name $P2SRootCertName -PublicCertData $CertBase64_3" - ], - "metadata": { - "azdata_cell_guid": "f75c1f83-82ca-4005-903e-935a787c1a5a" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "### **Upload the public key information to Azure**\r\n", - "_New-AzVpnClientRootCertificate_ cmdlet creates a new VPN client root certificate" - ], - "metadata": { - "azdata_cell_guid": "8e6f2cba-020a-4fe0-ad1c-95528fbb54b0" - } - }, - { - "cell_type": "code", - "source": [ - " New-AzVpnClientRootCertificate -VpnClientRootCertificateName $P2SRootCertName `\r\n", - " -VirtualNetworkGatewayName $GWName -ResourceGroupName $RG `\r\n", - " -PublicCertData $CertBase64_3" - ], - "metadata": { - "azdata_cell_guid": "2448dc49-ce6e-49f2-8abf-f824461738e6", - "tags": [ - "hide_input" - ] - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "### **Verify that the root certificate uploaded**\r\n", - "_Get-AzVpnClientRootCertificate_ cmdlet gets information about VPN root certificates" - ], - "metadata": { - "azdata_cell_guid": "20c00239-cd9f-4515-9275-c8797890744c" - } - }, - { - "cell_type": "code", - "source": [ - "Get-AzVpnClientRootCertificate -ResourceGroupName $RG -VirtualNetworkGatewayName $GWName" - ], - "metadata": { - "azdata_cell_guid": "9f714fa9-f3ec-4a75-b736-05ec9b8948c0" - }, - "outputs": [], - "execution_count": null - } - ] -} \ No newline at end of file diff --git a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/networking/p2svnet-creation.ipynb b/extensions/azurehybridtoolkit/notebooks/hybridbook/content/networking/p2svnet-creation.ipynb deleted file mode 100644 index 5ef32d87df..0000000000 --- a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/networking/p2svnet-creation.ipynb +++ /dev/null @@ -1,229 +0,0 @@ -{ - "metadata": { - "kernelspec": { - "name": "powershell", - "display_name": "PowerShell", - "language": "powershell" - }, - "language_info": { - "name": "powershell", - "codemirror_mode": "shell", - "mimetype": "text/x-sh", - "file_extension": ".ps1" - } - }, - "nbformat_minor": 2, - "nbformat": 4, - "cells": [ - { - "cell_type": "markdown", - "source": [ - "Create Point to Site Virtual Private Network (VPN)\n", - "==================================\n", - "As part of the migration process, if you do not already have an Azure VPN and Gateway setup, this notebook will help you get one created and verify you have the connectivity necessary to securely host your new Azure SQL Server Virtual Machines. \n", - "\n", - "If a S2S connection is needed instead of a P2S, then see [Create and manage S2S VPN connections using PowerShell](https://docs.microsoft.com/en-us/azure/vpn-gateway/vpn-gateway-tutorial-vpnconnection-powershell) for more information. S2S may be out of scope for a data user and can be attempted by a network administrator.\n", - "\n", - "" - ], - "metadata": { - "azdata_cell_guid": "6af59d69-ade7-480a-b33e-52a86fe5bfd3" - } - }, - { - "cell_type": "markdown", - "source": [ - "There are a handful of steps you have to walk through to get your resource group and VPN configured.\r\n", - "\r\n", - "Steps of this procedure include:\r\n", - "1. Connect to Azure subscription\r\n", - "1. Provision resource group for VPN\r\n", - "2. Create VPN\r\n", - "3. Provision IP address for Gateway\r\n", - "4. Create Gateway\r\n", - "\r\n", - "NOTE: Fill all the below variables with names in order to proceed ahead. " - ], - "metadata": { - "azdata_cell_guid": "2dfbdfb6-adbd-4b3c-8eeb-76237b8145cb" - } - }, - { - "cell_type": "code", - "source": [ - "$RG1 = \"\" # Resource group name(It should be of alphabets case insensitive)\r\n", - "$VNet1 = \"\" # VPN name of your choice(It should be of alphabets case insensitive)\r\n", - "$Location1 = \"\" # Specify the valid location such as 'West US 2','EASTUS' etc...\r\n", - "$FESubnet1 = \"\" # Frontend subnet name of your choice(It should be of alphabets case insensitive)\r\n", - "$VNet1Prefix = \"\" # VNet default I.P format would be like this '10.1.0.0/16'\r\n", - "$FEPrefix1 = \"\" # Frontend Gateway default IP format would be '10.1.0.0/24'\r\n", - "$GwPrefix1 = \"\" # Default value for Gateway subnet would be '10.1.255.0/27'\r\n", - "$Gw1 = \"\" # Gateway name of your choice (It should be of alphabets case insensitive)\r\n", - "$GwIP1 = \"\" # Gateway IP name of your choice(It should be of alphabets case insensitive)\r\n", - "$GwIPConf1 = \"\" # Gateway IP Config name of your choice(It would be alphanumeric case insensitive)" - ], - "metadata": { - "azdata_cell_guid": "424c911f-fb75-44f9-902d-c06dedf9eaf6", - "tags": [ - "parameters" - ] - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Connect to Azure Account" - ], - "metadata": { - "azdata_cell_guid": "39c51f44-acf4-4142-af82-b5daa4d3b53f" - } - }, - { - "cell_type": "code", - "source": [ - "Connect-AzAccount" - ], - "metadata": { - "azdata_cell_guid": "32849f41-ef18-4f8c-b4c3-4863db3329df" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Get Subscription \r\n", - "Below command will open a Dialouge Box with list of subscriptions.\r\n", - "Selecting one of those will set that subscription for rest of the commands." - ], - "metadata": { - "azdata_cell_guid": "1c8c15f8-80af-463a-8113-a71b1a4725ea" - } - }, - { - "cell_type": "code", - "source": [ - "$subscription = Get-AzSubscription | Out-GridView -PassThru\r\n", - "Set-AzContext -SubscriptionName $subscription" - ], - "metadata": { - "azdata_cell_guid": "cf04dfea-4a33-455b-83e8-753d75383f41", - "tags": [] - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Create the resource group \r\n", - "Create a resource group with the _New-AzResourceGroup_ command. An Azure resource group is a logical container into which Azure resources are deployed and managed. A resource group must be created first." - ], - "metadata": { - "azdata_cell_guid": "200961d3-879b-4325-8db4-e08854a36e4d" - } - }, - { - "cell_type": "code", - "source": [ - "# Create Azure resource group, if necessary\r\n", - "$RG1 = Get-AzResourceGroup -Name $RG1\r\n", - "\r\n", - "if (!$RG1)\r\n", - "{\r\n", - " # Need to create a new resource group\r\n", - " Write-Output \"Resource Group $RG1 does not exist. Creating...\"\r\n", - " $RG1 = New-AzResourceGroup -Name $RG1 -Location $Location1\r\n", - "}" - ], - "metadata": { - "azdata_cell_guid": "dabfb7d5-aa62-48a6-bc81-a82814befec7" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Create a VPN\r\n", - "Azure gateway provides cross-premises connectivity and P2S VPN server functionality for your VPN. Add a gateway to an existing VPN or create a new VPN and the gateway. Notice that the example specifies the name of the gateway subnet specifically. You must always specify the name of the gateway subnet as \"GatewaySubnet\" in order for it to function properly. This example creates a new VPN with two subnets: Frontend and GatewaySubnet using New-AzVirtualNetworkSubnetConfig and New-AzVirtualNetwork:" - ], - "metadata": { - "azdata_cell_guid": "629badb8-5338-4418-bdac-6e91af6c732b" - } - }, - { - "cell_type": "code", - "source": [ - "$fesub1 = New-AzVirtualNetworkSubnetConfig -Name $FESubnet1 -AddressPrefix $FEPrefix1\r\n", - "$gwsub1 = New-AzVirtualNetworkSubnetConfig -Name 'GatewaySubnet' -AddressPrefix $GwPrefix1\r\n", - "$vnet = New-AzVirtualNetwork `\r\n", - " -Name $VNet1 `\r\n", - " -ResourceGroupName $RG1 `\r\n", - " -Location $Location1 `\r\n", - " -AddressPrefix $VNet1Prefix `\r\n", - " -Subnet $fesub1,$gwsub1" - ], - "metadata": { - "azdata_cell_guid": "86ad1860-0e92-47b3-b198-1eac6187efe2" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Request a public IP address for the gateway\r\n", - "Azure VPN gateways communicate with your on-premises VPN devices over the Internet to performs IKE (Internet Key Exchange) negotiation and establish IPsec tunnels. Create and assign a public IP address to your gateway as shown in the example below with New-AzPublicIpAddress and New-AzVirtualNetworkGatewayIpConfig:\r\n", - "\r\n", - " #### Important :\r\n", - " Currently, you can only use a Dynamic public IP address for the gateway. Static IP address is not supported on Azure VPN gateways." - ], - "metadata": { - "azdata_cell_guid": "e68e487a-9fe1-43e1-9201-ad6c2abcf81f" - } - }, - { - "cell_type": "code", - "source": [ - "$gwpip = New-AzPublicIpAddress -Name $GwIP1 -ResourceGroupName $RG1 `\r\n", - " -Location $Location1 -AllocationMethod Dynamic\r\n", - "$subnet = Get-AzVirtualNetworkSubnetConfig -Name 'GatewaySubnet' `\r\n", - " -VirtualNetwork $vnet\r\n", - "$gwipconf = New-AzVirtualNetworkGatewayIpConfig -Name $GwIPConf1 `\r\n", - " -Subnet $subnet -PublicIpAddress $gwpip" - ], - "metadata": { - "azdata_cell_guid": "96800b54-48a8-463b-886c-3d0e96f29765" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Create a gateway \r\n", - "\r\n", - "A gateway can take 45 minutes or more to create. Once the gateway creation has completed, you can create a connection between your VPN and another VNet. Or create a connection between your VPN and an on-premises location. Create a gateway using the New-AzVirtualNetworkGateway cmdlet." - ], - "metadata": { - "azdata_cell_guid": "6b4924b6-35b4-49c1-a2c8-fc45349ff09d" - } - }, - { - "cell_type": "code", - "source": [ - "New-AzVirtualNetworkGateway -Name $Gw1 -ResourceGroupName $RG1 `\r\n", - " -Location $Location1 -IpConfigurations $gwipconf -GatewayType Vpn `\r\n", - " -VpnType RouteBased -GatewaySku VpnGw1" - ], - "metadata": { - "azdata_cell_guid": "e046ac0a-39fc-48e8-a7f8-26adb3a188e1" - }, - "outputs": [], - "execution_count": null - } - ] -} \ No newline at end of file diff --git a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/networking/readme.md b/extensions/azurehybridtoolkit/notebooks/hybridbook/content/networking/readme.md deleted file mode 100644 index 520f11b824..0000000000 --- a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/networking/readme.md +++ /dev/null @@ -1,13 +0,0 @@ -# Networking -[Home](../readme.md) - -This chapter contains notebooks to configure and make a secure network connection in an Azure hybrid cloud environment. - - - -## Notebooks in this Chapter -- [Download VPN Client Certificate](download-VpnClient.ipynb) - Used to install certificates that encrypt communication between on-site and Azure services - -- [Create Point-to-Site VPN](p2svnet-creation.ipynb) - Enables secure **Point-to-Site** (P2S) communication between a virtual private network in Azure and local resources. P2S is used by individuals and small groups for remote connectivity. A Point-to-Site (P2S) VPN gateway connection lets you create a secure connection to your VPN from an individual client computer. A P2S connection is established by starting it from the client computer. This solution is useful for telecommuters who want to connect to Azure VNets from a remote location, such as from home or a conference. P2S VPN is also a useful solution to use instead of S2S VPN when you have only a few clients that need to connect to a virtual network. - -- [Create Site-to-Site VPN](s2svnet-creation.ipynb) - **Site-to-site** (S2S) is normally used by organizations that want greater control between on-premise and cloud resources using a VPN gateway. A S2S VPN gateway connection is used to connect your on-premises network to an Azure virtual network over an IPsec/IKE (IKEv1 or IKEv2) VPN tunnel. This type of connection requires a VPN device located on-premises that has an externally facing public IP address assigned to it. For more information about VPN gateways, see [About VPN gateway](https://docs.microsoft.com/en-us/azure/vpn-gateway/vpn-gateway-about-vpngateways) and [Create and manage S2S VPN connections using PowerShell](https://docs.microsoft.com/en-us/azure/vpn-gateway/vpn-gateway-tutorial-vpnconnection-powershell "https://docs.microsoft.com/en-us/azure/vpn-gateway/vpn-gateway-tutorial-vpnconnection-powershell"). **NOTE:** *May require the help of a Network Administrator or similar role to setup a secure Gateway*. \ No newline at end of file diff --git a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/networking/s2svnet-creation.ipynb b/extensions/azurehybridtoolkit/notebooks/hybridbook/content/networking/s2svnet-creation.ipynb deleted file mode 100644 index 870d961a24..0000000000 --- a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/networking/s2svnet-creation.ipynb +++ /dev/null @@ -1,376 +0,0 @@ -{ - "metadata": { - "kernelspec": { - "name": "powershell", - "display_name": "PowerShell", - "language": "powershell" - }, - "language_info": { - "name": "powershell", - "codemirror_mode": "shell", - "mimetype": "text/x-sh", - "file_extension": ".ps1" - } - }, - "nbformat_minor": 2, - "nbformat": 4, - "cells": [ - { - "cell_type": "markdown", - "source": [ - "## Create a Site-to-Site Virtual Private Network in Azure\n", - "\n", - "* * *\n", - "\n", - "This notebook uses [PowerShell Az.Network cmdlets](https://docs.microsoft.com/en-us/powershell/module/az.network/ \"https://docs.microsoft.com/en-us/powershell/module/az.network/\") to create a S2S VPN gateway connection from an on-premises network to an Azure Virtual Network (VPN).\n", - "\n", - "![](https://docs.microsoft.com/en-us/azure/vpn-gateway/media/vpn-gateway-tutorial-vpnconnection-powershell/site-to-site-diagram.png)\n", - "\n", - "Use these parameters to create the environment or to better understand the examples in this notebook.\n", - "\n", - "| Line # | Name | Example | Description |\n", - "| --- | --- | --- | --- |\n", - "| 1 | **Subscription** | \"\" | Name or ID (guid) of Azure Subscription to setup S2S VPN in. |\n", - "| 2 | **ResourceGroup** | \"TestRG1\" | Name of new or existing resource group (RG). An Azure Resource Group is a collection of Azure resources that share the same permissions, policies, etc. In this case, the Resource Group for the virtual network is specified.  |\n", - "| 3 | **VnetName** | \"VNet1\" | Alphanumeric value represents the name of the Azure resource to create. |\n", - "| 4 | **Location** | \"East US\" | Value representing the region or location of the RG. See [Azure Geographies](https://azure.microsoft.com/en-us/global-infrastructure/geographies/ \"https://azure.microsoft.com/en-us/global-infrastructure/geographies/\") for more information. |\n", - "| 5 | **AddressSpace** | \"10.1.0.0/16\" | Defines a range of IP prefixes in the remote subnet. |\n", - "| 6 | **SubnetName** | \"Frontend\" | |\n", - "| 7 | **Subnet** | \"10.1.0.0/24\" | |\n", - "| 8 | **GatewaySubnet** | \"10.1.255.0/27\" | Specify the number of IP addresses that the subnet contains. The number of IP addresses needed depends on the VPN gateway configuration created. Some configurations require more IP addresses than others. It is recommended to create a gateway subnet that uses a /27 or /28. |\n", - "| 9 | **LocalNetworkGatewayName** | \"Site1\" | |\n", - "| 10 | **LNGPublicIP** | \"192.168.29.46\" | |\n", - "| 11 | **LocalAddressPrefix** | \"10.101.0.0/24, 10.101.1.0/24\" | |\n", - "| 12 | **GatewayName** | \"GatewaySubnet\" | |\n", - "| 13 | **PublicIP** | \"VNet1GWPIP\" | |\n", - "| 14 | **GatewayIPConfig** | \"gwipconfig1\" | |\n", - "| 15 | **VPNType** | \"RouteBased\" | |\n", - "| 16 | **GatewayType** | \"Vpn\" | |\n", - "| 17 | **ConnectionName** | \"VNet1toSite1\" | |" - ], - "metadata": { - "azdata_cell_guid": "130ad787-0d85-4edb-9cab-62824de6993f" - } - }, - { - "cell_type": "code", - "source": [ - "$Subscription = \"\"\r\n", - "$ResourceGroup = \"\"\r\n", - "$VnetName = \"\"\r\n", - "$Location = \"\"\r\n", - "$AddressSpace = \"\"\r\n", - "$SubnetName = \"\"\r\n", - "$Subnet = \"\"\r\n", - "$GatewaySubnet = \"\"\r\n", - "$LocalNetworkGatewayName = \"\"\r\n", - "$LNGPublicIP = \"\"\r\n", - "$LocalAddressPrefixes = \"\"\r\n", - "$GatewayName = \"\"\r\n", - "$PublicIP = \"\"\r\n", - "$GatewayIPConfig = \"\"\r\n", - "$VPNType = \"\"\r\n", - "$GatewayType = \"\"\r\n", - "$ConnectionName = \"\"" - ], - "metadata": { - "azdata_cell_guid": "f883288a-fc6a-4b0f-9215-6b771dc72b8d", - "tags": [ - "parameters" - ] - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "There are a handful of steps to setup a resource group and configure the VPN:\n", - "\n", - "1. Create or specify a resource group\n", - "2. Create a virtual network\n", - "3. Create a subnet configuration\n", - "4. Set the subnet configuration for the virtual network\n", - "5. Add a gateway subnet\n", - "6. Set the subnet configuration for the virtual network\n", - "7. Request a public IP address\n", - "8. Create the gateway IP address configuration\n", - "9. Create the VPN gateway\n", - "10. Create the local network gateway\n", - "11. Create the VPN connection" - ], - "metadata": { - "azdata_cell_guid": "a84bd796-4838-480a-83c5-48305870d2f0" - } - }, - { - "cell_type": "markdown", - "source": [ - "## Create a resource group" - ], - "metadata": { - "azdata_cell_guid": "009ae97a-8d89-45bb-9f0f-63ab3943f151" - } - }, - { - "cell_type": "code", - "source": [ - "New-AzResourceGroup -Name $ResourceGroup -Location $Location" - ], - "metadata": { - "azdata_cell_guid": "0e792bfb-5775-437c-b084-47593e5ce314" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Create a virtual network\n", - "\n", - "This example creates a virtual network and a gateway subnet. If the virtual network is already present then gateway subnet is needed, see [To add a gateway subnet to a virtual network which is already created](https://docs.microsoft.com/en-us/azure/vpn-gateway/vpn-gateway-create-site-to-site-rm-powershell#gatewaysubnet \"https://docs.microsoft.com/en-us/azure/vpn-gateway/vpn-gateway-create-site-to-site-rm-powershell#gatewaysubnet\"). When creating a virtual network, make sure that the address spaces don't overlap any of the address spaces that are in on-premises network." - ], - "metadata": { - "azdata_cell_guid": "53145be6-8eb0-4857-9029-888b55a12e85" - } - }, - { - "cell_type": "code", - "source": [ - "$virtualNetwork = New-AzVirtualNetwork `\r\n", - " -ResourceGroupName $ResourceGroup `\r\n", - " -Location $Location `\r\n", - " -Name $VnetName `\r\n", - " -AddressPrefix $AddressSpace" - ], - "metadata": { - "azdata_cell_guid": "f473191d-5bdd-4678-925d-bd26cf0fe53d" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Create a subnet configuration" - ], - "metadata": { - "azdata_cell_guid": "eaa39a66-f89d-4669-bcce-6e205c3ef5a1" - } - }, - { - "cell_type": "code", - "source": [ - "$subnetConfig = Add-AzVirtualNetworkSubnetConfig `\r\n", - " -Name $SubnetName `\r\n", - " -AddressPrefix $Subnet `\r\n", - " -VirtualNetwork $virtualNetwork" - ], - "metadata": { - "azdata_cell_guid": "cfbee82d-5950-426a-8887-06cc023b8dfd" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Set the subnet configuration for the virtual network" - ], - "metadata": { - "azdata_cell_guid": "d271f4fd-045e-4920-a384-9894c226e666" - } - }, - { - "cell_type": "code", - "source": [ - "$virtualNetwork | Set-AzVirtualNetwork" - ], - "metadata": { - "azdata_cell_guid": "6bee9590-7149-4233-954e-25a897800aaa" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Add a gateway subnet\r\n", - "The virtual network gateway uses specific subnet called the gateway subnet. The gateway subnet is part of the virtual network IP address range that you specify when configuring your virtual network. It contains the IP addresses that the virtual network gateway resources and services use. The subnet must be named 'GatewaySubnet' in order for Azure to deploy the gateway resources. You can't specify a different subnet to deploy the gateway resources to. If you don't have a subnet named 'GatewaySubnet', when you create your VPN gateway, it will fail." - ], - "metadata": { - "azdata_cell_guid": "27e2625c-ebef-4355-8667-c817c52dbf3a" - } - }, - { - "cell_type": "code", - "source": [ - "$vnet = Get-AzVirtualNetwork -ResourceGroupName $ResourceGroup -Name $VnetName\r\n", - "Add-AzVirtualNetworkSubnetConfig -Name $GatewayName -AddressPrefix $GatewaySubnet -VirtualNetwork $vnet" - ], - "metadata": { - "azdata_cell_guid": "4f13a28f-e6be-424a-942f-a8562c104787" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Set the subnet configuration for the virtual network" - ], - "metadata": { - "azdata_cell_guid": "25aed948-7fcd-440f-bb37-bb677cae5c79" - } - }, - { - "cell_type": "code", - "source": [ - "$vnet | Set-AzVirtualNetwork" - ], - "metadata": { - "azdata_cell_guid": "2b0cbaff-5aea-4029-9603-5eb7c75688b3" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Request a public IP address\r\n", - "A VPN gateway must have a Public IP address. It first requests the IP address resource, and then refer to it when creating virtual network gateway. The IP address is dynamically assigned to the resource when the VPN gateway is created.\r\n", - "\r\n", - "VPN Gateway currently only supports Dynamic Public IP address allocation. It cannot request a Static Public IP address assignment. However, this does not mean that the IP address will change after it has been assigned to created VPN gateway. The only time the Public IP address changes is when the gateway is deleted and re-created. It doesn't change across resizing, resetting, or other internal maintenance/upgrades of created VPN gateway." - ], - "metadata": { - "azdata_cell_guid": "c664e2c9-d9fc-4913-aaf8-8e3b3d40d7a8" - } - }, - { - "cell_type": "code", - "source": [ - "$gwpip= New-AzPublicIpAddress -Name $PublicIP -ResourceGroupName $ResourceGroup -Location $Location `\r\n", - " -AllocationMethod Dynamic" - ], - "metadata": { - "azdata_cell_guid": "1d8eef52-b085-4eb3-8130-0c16232d89b1" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Create the gateway IP address configuration\r\n", - "The gateway configuration defines the subnet (the 'GatewaySubnet') and the public IP address to use. Use the following example to create gateway configuration:" - ], - "metadata": { - "azdata_cell_guid": "a351a10f-54a6-4a78-be15-38c96d94bae0" - } - }, - { - "cell_type": "code", - "source": [ - "$vnet = Get-AzVirtualNetwork -Name $VnetName -ResourceGroupName $ResourceGroup\r\n", - "$subnet = Get-AzVirtualNetworkSubnetConfig -Name $GatewayName -VirtualNetwork $vnet\r\n", - "$gwipconfig = New-AzVirtualNetworkGatewayIpConfig -Name $GatewayIPConfig -SubnetId $subnet.Id -PublicIpAddressId $gwpip.Id" - ], - "metadata": { - "azdata_cell_guid": "b1f1662a-21a0-48b6-b5b6-5f9a15e1d662" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Create a gateway\r\n", - "A gateway can take 45 minutes or more to create. Once the gateway creation has completed, you can create a connection between your VPN and another VNet. Or create a connection between your VPN and an on-premises location. Create a gateway using the _New-AzVirtualNetworkGateway_ cmdlet." - ], - "metadata": { - "azdata_cell_guid": "b9adcd02-bb2a-4c57-b831-68f74f1d2fdc" - } - }, - { - "cell_type": "code", - "source": [ - "New-AzVirtualNetworkGateway -Name $GatewayName -ResourceGroupName $ResourceGroup `\r\n", - " -Location $Location -IpConfigurations $gwipconfig -GatewayType $GatewayType `\r\n", - " -VpnType $VPNType -GatewaySku VpnGw1" - ], - "metadata": { - "azdata_cell_guid": "9235b9b3-7cbe-4a4d-bc5e-f82e3e34bd4b" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Create the local network gateway\r\n", - "The local network gateway (LNG) typically refers to on-premises location. It is not the same as a virtual network gateway. Give the site a name by which Azure can refer to it, then specify the IP address of the on-premises VPN device to the connection to be created. Also specify the IP address prefixes that will be routed through the VPN gateway to the VPN device. The address prefixes specified are the prefixes located on on-premises network. If on-premises network changes, it can easily update the prefixes." - ], - "metadata": { - "azdata_cell_guid": "5452ac24-2bcf-475d-8284-8ea2cfce8ac3" - } - }, - { - "cell_type": "code", - "source": [ - "New-AzLocalNetworkGateway -Name $LocalNetworkGatewayName -ResourceGroupName $ResourceGroup `\r\n", - " -Location $Location -GatewayIpAddress '192.168.29.46' -AddressPrefix @('10.101.0.0/24','10.101.1.0/24')" - ], - "metadata": { - "azdata_cell_guid": "0e212918-89aa-474e-ad6d-73e8cefc3b25" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Create the VPN connection\r\n", - "Create the Site-to-Site VPN connection between virtual network gateway and VPN device. Be sure to replace the values. The shared key must match the value used for VPN device configuration. Notice that the '-ConnectionType' for Site-to-Site is IPsec." - ], - "metadata": { - "azdata_cell_guid": "08ffbbd7-deba-4472-950f-b92c83a0dbc2" - } - }, - { - "cell_type": "code", - "source": [ - "$gateway1 = Get-AzVirtualNetworkGateway -Name $GatewayName -ResourceGroupName $ResourceGroup\r\n", - "$local = Get-AzLocalNetworkGateway -Name $LocalNetworkGatewayName -ResourceGroupName $ResourceGroup\r\n", - "New-AzVirtualNetworkGatewayConnection -Name $ConnectionName -ResourceGroupName $ResourceGroup `\r\n", - " -Location $Location -VirtualNetworkGateway1 $gateway1 -LocalNetworkGateway2 $local `\r\n", - " -ConnectionType IPsec -RoutingWeight 10 -SharedKey 'abc123'" - ], - "metadata": { - "azdata_cell_guid": "c7d2e8dc-97ee-4764-8a39-704dfd1cd815" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Verify the VPN connection\r\n", - "Connection can be verified if it is succeeded by using the _Get-AzVirtualNetworkGatewayConnection_ cmdlet, with or without '-Debug'." - ], - "metadata": { - "azdata_cell_guid": "2dd64b80-488d-4ada-9805-ef7fe59d4058" - } - }, - { - "cell_type": "code", - "source": [ - "Get-AzVirtualNetworkGatewayConnection -Name $ConnectionName -ResourceGroupName $ResourceGroup" - ], - "metadata": { - "azdata_cell_guid": "03acce02-74fb-48eb-a38d-f2dde1a2e85e" - }, - "outputs": [], - "execution_count": null - } - ] -} \ No newline at end of file diff --git a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/offline-migration/db-to-SQLDB.ipynb b/extensions/azurehybridtoolkit/notebooks/hybridbook/content/offline-migration/db-to-SQLDB.ipynb deleted file mode 100644 index b30630333b..0000000000 --- a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/offline-migration/db-to-SQLDB.ipynb +++ /dev/null @@ -1,296 +0,0 @@ -{ - "metadata": { - "kernelspec": { - "name": "powershell", - "display_name": "PowerShell", - "language": "powershell" - }, - "language_info": { - "name": "powershell", - "codemirror_mode": "shell", - "mimetype": "text/x-sh", - "file_extension": ".ps1" - } - }, - "nbformat_minor": 2, - "nbformat": 4, - "cells": [ - { - "cell_type": "markdown", - "source": [ - "# Migrate SQL Server Database to Azure SQL DB\n", - "\n", - "## Overview\n", - "\n", - "Use this notebook when attempting to migrate a SQL Server database to Azure SQL Database. The process is different than using an Azure SQL Virtual Machine or Azure SQL Managed Instance. Refer to this [Microsoft docs](https://docs.microsoft.com/en-us/azure/dms/howto-sql-server-to-azure-sql-powershell) article for the method described.\n", - "\n", - "## Variables\n", - "\n", - "Use the table below as reference when configuring this notebook for execution.\n", - "\n", - "| Line | Variable | Description | Example |\n", - "| --- | --- | --- | --- |\n", - "| 1 | Subscription | Azure subscription name or ID | guid |\n", - "| 2 | dmsName | Name of the Data Migration Service | MyDms |\n", - "| 3 | dmsResourceGroup | Exact name of resource group to target or create | _MyAz\\_RG_ |\n", - "| 4 | loc | Specifies the location of the service | EastUS2 |\n", - "| 5 | vNetName | Specify the name of the vnet Azure resource to use | vnet1 |\n", - "| 6 | Subnet | Name of the Azure subnet resource to use | subnet1 |\n", - "| 7-8 | dmsSku | This parameter corresponds to DMS Sku name. The currently supported sku is listed. | GeneralPurpose\\_4vCores |\n", - "| 9 | TargetSqlServer | Target SQL server to migrate to | \"sqllazuretarget.database.windows.net\" |\n", - "| 10 | SourceDb | Name of database to migrate from, the _source_ | AdventureWorks2016 |\n", - "| 11 | TargetDb | Name of database to migrate to, in case it differs from the source, the _target_ | AdventureWorks2016 |\n", - "\n", - "Lastly, specify the table names explicitly as a PowerShell list of names:\n", - "\n", - "> `$Tables = @(`\"HumanResources.Department\",  \"HumanResources.Employee\",  \"HumanResources.EmployeeDepartmentHistory\")" - ], - "metadata": { - "azdata_cell_guid": "f706da59-22c3-4317-bf41-c00dde794097" - } - }, - { - "cell_type": "code", - "source": [ - "$Subscription = \"\"\r\n", - "$dmsName = \"\"\r\n", - "$dmsResourceGroup = \"\"\r\n", - "$loc = \"\"\r\n", - "$vNetName = \"\"\r\n", - "$Subnet = Subnet1\r\n", - "$dmsSku = GeneralPurpose_4vCores \r\n", - "$TargetSqlServer = \"\"\r\n", - "$SourceDb = \"\"\r\n", - "$TargetDb = \"\"\r\n", - "\r\n", - "# Define a list of tables to migrate\r\n", - "$Tables = @()\r\n", - "<# :::For example::: \r\n", - "$Tables = @(\"HumanResources.Department\", `\r\n", - " \"HumanResources.Employee\", `\r\n", - " \"HumanResources.EmployeeDepartmentHistory\", `\r\n", - " \"HumanResources.EmployeePayHistory\", `\r\n", - " \"HumanResources.JobCandidate\", `\r\n", - " \"HumanResources.Shift\")\r\n", - "#>" - ], - "metadata": { - "azdata_cell_guid": "2624afc0-2403-4d42-ad88-6adcfe1a5c2b", - "tags": [ - "parameters" - ] - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Connections" - ], - "metadata": { - "azdata_cell_guid": "f0a5e313-a2a6-4e75-ab94-5d96436b8473" - } - }, - { - "cell_type": "code", - "source": [ - "# Configure virtual network\r\n", - "$vNet = Get-AzVirtualNetwork -ResourceGroupName $dmsResourceGroup -Name $vNetName\r\n", - "\r\n", - "$vSubNet = Get-AzVirtualNetworkSubnetConfig -VirtualNetwork $vNet -Name $Subnet\r\n", - "\r\n", - "# Create a Database Connection Info object for the source connections\r\n", - "$sourceConnInfo = New-AzDmsConnInfo -ServerType SQL `\r\n", - " -DataSource $SourceSqlServer `\r\n", - " -AuthType SqlAuthentication `\r\n", - " -TrustServerCertificate:$true\r\n", - "\r\n", - "# target connection\r\n", - "$targetConnInfo = New-AzDmsConnInfo -ServerType SQL `\r\n", - " -DataSource $TargetSqlServer `\r\n", - " -AuthType SqlAuthentication `\r\n", - " -TrustServerCertificate:$false" - ], - "metadata": { - "azdata_cell_guid": "dbf40b4b-af83-46c6-9db1-15f45eb24382" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Select Data by Defining Database and Table Mapping" - ], - "metadata": { - "azdata_cell_guid": "b9113be6-0aef-41fd-afe8-e1ce51c5999b" - } - }, - { - "cell_type": "code", - "source": [ - "# Create a list of databases to migrate\r\n", - "$dbInfo1 = New-AzDataMigrationDatabaseInfo -SourceDatabaseName $SourceDb\r\n", - "$dbList = @($dbInfo1)\r\n", - "\r\n", - "# Create a table map \r\n", - "$tableMap = New-Object 'system.collections.generic.dictionary[string,string]'\r\n", - "foreach ($table in $Tables)\r\n", - "{\r\n", - " $tableMap.Add($table,$table)\r\n", - "}\r\n", - "\r\n", - "# Select the data\r\n", - "$selectedDbs = New-AzDmsSelectedDB -MigrateSqlServerSqlDb -Name $SourceDb `\r\n", - " -TargetDatabaseName $TargetDb `\r\n", - " -TableMap $tableMap" - ], - "metadata": { - "azdata_cell_guid": "3b6551e9-4d42-441e-8fe1-f247471995df" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Create Service Project" - ], - "metadata": { - "azdata_cell_guid": "a9abeeb9-4f45-4e0c-8b1b-20270dddece4" - } - }, - { - "cell_type": "code", - "source": [ - "$service = New-AzDms -ResourceGroupName $dmsResourceGroup `\r\n", - " -ServiceName $dmsName `\r\n", - " -Location $loc `\r\n", - " -Sku $dmsSku `\r\n", - " -VirtualSubnetId $vSubNet.Id\r\n", - "\r\n", - "# Create a project object\r\n", - "$project = New-AzDataMigrationProject -ResourceGroupName $dmsResourceGroup `\r\n", - " -ServiceName $service.Name `\r\n", - " -ProjectName $dmsNameProject `\r\n", - " -Location $loc `\r\n", - " -SourceType SQL `\r\n", - " -TargetType SQLDB `\r\n", - " -SourceConnection $sourceConnInfo `\r\n", - " -TargetConnection $targetConnInfo `\r\n", - " -DatabaseInfo $dbList" - ], - "metadata": { - "azdata_cell_guid": "77415bea-b098-4a9f-a166-9751e4261fc1" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Credentials for Source and Target" - ], - "metadata": { - "azdata_cell_guid": "f8c27b61-d2c9-43ee-bc93-8182752edb33" - } - }, - { - "cell_type": "code", - "source": [ - "$secpasswd = ConvertTo-SecureString -String $sourcePassword -AsPlainText -Force\r\n", - "$sourceCred = New-Object System.Management.Automation.PSCredential ($sourceUserName, $secpasswd)\r\n", - "$secpasswd = ConvertTo-SecureString -String $targetPassword -AsPlainText -Force\r\n", - "$targetCred = New-Object System.Management.Automation.PSCredential ($targetUserName, $secpasswd)" - ], - "metadata": { - "azdata_cell_guid": "61aa72d1-7492-41e8-847b-89a76ca28e0f" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Create the Migration Task\r\n", - "Uncomment lines to perform validation checks during the task." - ], - "metadata": { - "azdata_cell_guid": "681feaa0-f7d8-4322-af0c-75ab6402fd6e" - } - }, - { - "cell_type": "code", - "source": [ - "$migTask = New-AzDataMigrationTask -TaskType MigrateSqlServerSqlDb `\r\n", - " -ResourceGroupName $dmsResourceGroup `\r\n", - " -ServiceName $service.Name `\r\n", - " -ProjectName $project.Name `\r\n", - " -TaskName $dmsNameTask `\r\n", - " -SourceConnection $sourceConnInfo `\r\n", - " -SourceCred $sourceCred `\r\n", - " -TargetConnection $targetConnInfo `\r\n", - " -TargetCred $targetCred `\r\n", - " -SelectedDatabase $selectedDbs `\r\n", - "# -SchemaValidation `\r\n", - "# -DataIntegrityValidation `\r\n", - "# -QueryAnalysisValidation `" - ], - "metadata": { - "azdata_cell_guid": "4dc629cc-1978-4bf8-80fc-69de093ac435" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Monitor the Migration Task" - ], - "metadata": { - "azdata_cell_guid": "9074e9e5-f5f7-4d30-a4ec-b6d3f112a600" - } - }, - { - "cell_type": "code", - "source": [ - "# Monitor the migration\r\n", - "if (($migTask.ProjectTask.Properties.State -eq \"Running\") -or ($migTask.ProjectTask.Properties.State -eq \"Queued\"))\r\n", - "{\r\n", - " write-host \"migration task running\"\r\n", - "}\r\n", - "else \r\n", - "{\r\n", - " Write-Host \"migration task is $migTask.ProjectTask.Properties.State\"\r\n", - "}" - ], - "metadata": { - "azdata_cell_guid": "89af76b7-1801-4d26-b6cd-907a9b455839" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Deleting the DMS Instance\r\n", - "This should remove the DMS instance from the Resource Group. Uncomment to perform the task. " - ], - "metadata": { - "azdata_cell_guid": "ed3bf1e7-a92f-4985-b89c-85f45b8e6821" - } - }, - { - "cell_type": "code", - "source": [ - "#Remove-AzDms -ResourceGroupName $dmsResourceGroup -ServiceName $dmsName" - ], - "metadata": { - "azdata_cell_guid": "245b4d4e-7559-4534-bff8-3af390ac1e3a" - }, - "outputs": [], - "execution_count": null - } - ] -} \ No newline at end of file diff --git a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/offline-migration/db-to-VM.ipynb b/extensions/azurehybridtoolkit/notebooks/hybridbook/content/offline-migration/db-to-VM.ipynb deleted file mode 100644 index 71f999a7e4..0000000000 --- a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/offline-migration/db-to-VM.ipynb +++ /dev/null @@ -1,270 +0,0 @@ -{ - "metadata": { - "kernelspec": { - "name": "powershell", - "display_name": "PowerShell", - "language": "powershell" - }, - "language_info": { - "name": "powershell", - "codemirror_mode": "shell", - "mimetype": "text/x-sh", - "file_extension": ".ps1" - } - }, - "nbformat_minor": 2, - "nbformat": 4, - "cells": [ - { - "cell_type": "markdown", - "source": [ - "# Migrate SQL Server Database to Azure SQL VM" - ], - "metadata": { - "azdata_cell_guid": "f706da59-22c3-4317-bf41-c00dde794097" - } - }, - { - "cell_type": "markdown", - "source": [ - "## Source SQL Instance\r\n", - "The following code is used to specify the source SQL Server instance. Data and Server objects will be copied from this server to the target SQL Server instance. In the following code cell set the following parameters:\r\n", - "\r\n", - "*Note: the notebook currently is setup for SQL Authentication. Future updates will add support for multiple authentication types.*\r\n", - "\r\n", - "|Parameter|Description|\r\n", - "|---|---|\r\n", - "|sourceServerName| The name or IP address of the source instance|\r\n", - "|sourceLogin| sql login to connect to source instance with |\r\n", - "\r\n", - "*Note: source password should be set in the environment variable SQLMIG_SourcePassword. This is to avoid persisting the environment variable in the notebook file.*\r\n", - "\r\n", - "Edit the code below to specify the above parameters to test connectivity to the source instance.\r\n", - "\r\n", - "\r\n", - "" - ], - "metadata": { - "azdata_cell_guid": "2ac081f4-853a-4381-a303-e6ca557503fb" - } - }, - { - "cell_type": "code", - "source": [ - "$sourceServerName = ''\r\n", - "$sourceLogin = ''\r\n", - "\r\n", - "$env:SQLMIG_SourcePassword = ''\r\n", - "\r\n", - "## PowerShell Environment \r\n", - "$sourceLoginPassword = ConvertTo-SecureString $env:SQLMIG_SourcePassword -AsPlaintext -Force\r\n", - "$sourceCredential = New-Object System.Management.Automation.PSCredential ('', $sourceLoginPassword)\r\n", - "$sourceTest = Test-DbaConnection -SqlInstance $sourceServerName -SqlCredential $sourceCredential\r\n", - "$sourceTest\r\n", - "$sourceConnection = Connect-DbaInstance -SqlInstance $sourceServerName -SqlCredential $sourceCredential" - ], - "metadata": { - "azdata_cell_guid": "76a50416-b804-46ae-a49c-99baaeb31f7d" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Target SQL Instance\r\n", - "The following code is used to specify the target SQL Server instance. This is the SQL Server instance that Data and Server objects will be copied to. In the following code cell set the following parameters:\r\n", - "\r\n", - "*Note: the notebook currently is setup for SQL Authentication. Future updates will add support for multiple authentication types.*\r\n", - "\r\n", - "|Parameter|Description|\r\n", - "|---|---|\r\n", - "|targetServerName| The name or IP address of the target instance|\r\n", - "|targetLogin| sql login to connect to target instance with |\r\n", - "\r\n", - "*Note: the target login password should be set in the environment variable SQLMIG_TargetPassword. Thisis to avoid persisting the environment variable in the notebook file.*\r\n", - "\r\n", - "Edit the code below to specify the above parameters to test connectivity to the target instance." - ], - "metadata": { - "azdata_cell_guid": "43751f97-d545-4e84-ac99-109c719a048d" - } - }, - { - "cell_type": "code", - "source": [ - "$targetServerName = 'sqlmig.westus2.cloudapp.azure.com'\r\n", - "$targetLogin = 'cloudsa'\r\n", - "\r\n", - "## TEMP - REMOVE BEFORE PUSHING CHANGES\r\n", - "$env:SQLMIG_TargetPassword = ''\r\n", - "\r\n", - "## PowerShell Environment \r\n", - "$targetLoginPassword = ConvertTo-SecureString $env:SQLMIG_TargetPassword -AsPlaintext -Force\r\n", - "$targetCredential = New-Object System.Management.Automation.PSCredential ('', $targetLoginPassword)\r\n", - "$targetTest = Test-DbaConnection -SqlInstance $targetServerName -SqlCredential $targetCredential\r\n", - "$targetTest\r\n", - "$targetConnection = Connect-DbaInstance -SqlInstance $targetServerName -SqlCredential $targetCredential" - ], - "metadata": { - "azdata_cell_guid": "4f74315c-1147-4fe3-8959-13eb24cb0957" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Login to Microsoft Azure\r\n", - "To configure and provision resources you must log into your Azure account and set the current subscription that is being used for the target SQL Server instance. The following code will help you connect your account and choose the correct subscription. When presented with the list of subscriptions, click on the desired subscription and press OK." - ], - "metadata": { - "azdata_cell_guid": "2ffbcba3-934e-4498-87ed-7030124d3af2" - } - }, - { - "cell_type": "code", - "source": [ - "#Connect-AzAccount\r\n", - "$migrationSubscription = Get-AzSubscription | Select-Object -Property Name, Id | Out-GridView -PassThru\r\n", - "Set-AzContext -SubscriptionId $migrationSubscription.Id" - ], - "metadata": { - "azdata_cell_guid": "8b23dcba-8d28-44eb-abbb-c7a54ef6b003", - "tags": [] - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Verify No Active Connections" - ], - "metadata": { - "azdata_cell_guid": "81259d7e-62ac-4cdd-9e1b-2cb4ddb3d3b2" - } - }, - { - "cell_type": "code", - "source": [ - "#TODO - filter connected proceesses for user connections\r\n", - "\r\n", - "\r\n", - "Get-DbaProcess -SqlInstance $SourceServerName -SqlCredential $sourceLogin | \r\n", - "Select Host, login, Program" - ], - "metadata": { - "azdata_cell_guid": "28393e59-4ea1-4f0f-8f9f-8a504f15e723", - "tags": [] - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Temporary Storage for Data Movement\r\n", - "\r\n", - "Offline data migration attempts to use backup to URL and restore from URL as the mechanism for moving data from the source instance to the target instance. This code will check existance of the specified storage account and container to use for data migration. If the resources do not exist they will be created." - ], - "metadata": { - "azdata_cell_guid": "cc18027e-4636-465d-abaf-f3de88fea406" - } - }, - { - "cell_type": "code", - "source": [ - "$resourceGroup = \"sqlmig\"\r\n", - "$blobStorageAccount = \"tempsqlmigstorage\"\r\n", - "$containerName = \"backups\"\r\n", - "$location = \"West US 2\"\r\n", - "\r\n", - "# Storage Account\r\n", - "$storageAccount = Get-AzStorageAccount -ResourceGroupName $resourceGroup -Name $blobStorageAccount\r\n", - "if ($storageAccount -eq $null)\r\n", - "{\r\n", - " # specified storage account does not yet exist, attempt to create it\r\n", - " $storageAccount = New-AzStorageAccount -ResourceGroupName $resourceGroup -Name $blobStorageAccount -Location $location -SkuName Standard_LRS -Kind StorageV2\r\n", - "}\r\n", - "$storageAccount\r\n", - "\r\n", - "# Container\r\n", - "$storageContext = $storageAccount.Context\r\n", - "$storageContainer = Get-AzStorageContainer -Name $containerName -Context $storageContext\r\n", - "if ($storageContainer -eq $null)\r\n", - "{\r\n", - " #specified storage container does not yet exist, attempt to create it\r\n", - " $storageContainer = New-AzStorageContainer -Name $containerName -Context $storageContext -Permission Container\r\n", - "}\r\n", - "$storageContainer\r\n", - "\r\n", - "# Provide source instance with SAS token for blob access\r\n", - "$sourceSAS = (New-AzStorageAccountSASToken -Service Blob -ResourceType Object -Permission \"rw\" -Context $storageContext).TrimStart('?')\r\n", - "$sourceCred = New-DbaCredential -SqlInstance $sourceConnection -Name \"https://$blobStorageAccount.blob.core.windows.net/$containerName\" -Identity \"SHARED ACCESS SIGNATURE\" -SecurePassword (ConvertTo-SecureString $sourceSAS -AsPlainText -Force) -Force\r\n", - "$sourceCred\r\n", - "\r\n", - "$targetSAS = (New-AzStorageAccountSASToken -Service Blob -ResourceType Object -Permission \"rw\" -Context $storageContext).TrimStart('?') # -ResourceType Container,Object\r\n", - "$targetCred = New-DbaCredential -SqlInstance $targetConnection -Name \"https://$blobStorageAccount.blob.core.windows.net/$containerName\" -Identity \"SHARED ACCESS SIGNATURE\" -SecurePassword (ConvertTo-SecureString $targetSAS -AsPlainText -Force) -Force\r\n", - "$targetCred\r\n", - "\r\n", - "" - ], - "metadata": { - "azdata_cell_guid": "f7d53cb1-a55d-4634-95f7-d3e8cf9fab52" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Copy Databases to Target Server\r\n", - "\r\n", - "The following code will present a list of the databases from the source SQL Server instance. Select the list of databases to copy and press OK. The Copy-DbaDatabase CMDLET will take a backup of each database using the azure storage account information above. Each database backup will then be restored from the blob storage account. The database restore will use server defaults for database file location and structure.\r\n", - "\r\n", - "If the database being restored already exists on the target instance, the *Force* parameter determines the behavior of the notebook:\r\n", - "\r\n", - "|Force|Description|\r\n", - "|---|---|\r\n", - "|$true| Overwrite the existing database on the target instance|\r\n", - "|$false| Do not restore the database if it already exists on the target instance|\r\n", - "\r\n", - "*Note: This can be a very long running process based on the size of the databases being copied. The notebook should be allowed to run until the CMDLET completes.*\r\n", - "\r\n", - "" - ], - "metadata": { - "azdata_cell_guid": "a82b0092-53a1-4dc3-8d73-16fae8c59ff7" - } - }, - { - "cell_type": "code", - "source": [ - "$databasesToCopy = Get-DbaDatabase -SqlInstance $sourceConnection | Select-Object -Property Name | Out-GridView -PassThru\r\n", - "$databaseList = New-Object System.Collections.ArrayList\r\n", - "foreach ($db in $databasesToCopy)\r\n", - "{\r\n", - " $databaseList.Add($db.Name) \r\n", - "}\r\n", - "\r\n", - "$copyDatabaseParams = @{\r\n", - " Database = $databaseList\r\n", - " Source = $sourceConnection\r\n", - " Destination = $targetConnection\r\n", - " BackupRestore = $true\r\n", - " SharedPath = \"https://$blobStorageAccount.blob.core.windows.net/$containerName\"\r\n", - " Force = $true\r\n", - " Verbose = $false \r\n", - "}\r\n", - "\r\n", - "Copy-DbaDatabase @copyDatabaseParams" - ], - "metadata": { - "azdata_cell_guid": "5614e073-0a94-45af-8432-e7a6bf9121ea" - }, - "outputs": [], - "execution_count": null - } - ] -} \ No newline at end of file diff --git a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/offline-migration/instance-to-VM.ipynb b/extensions/azurehybridtoolkit/notebooks/hybridbook/content/offline-migration/instance-to-VM.ipynb deleted file mode 100644 index 871b4ec3d0..0000000000 --- a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/offline-migration/instance-to-VM.ipynb +++ /dev/null @@ -1,264 +0,0 @@ -{ - "metadata": { - "kernelspec": { - "name": "powershell", - "display_name": "PowerShell", - "language": "powershell" - }, - "language_info": { - "name": "powershell", - "codemirror_mode": "shell", - "mimetype": "text/x-sh", - "file_extension": ".ps1" - } - }, - "nbformat_minor": 2, - "nbformat": 4, - "cells": [ - { - "cell_type": "markdown", - "source": [ - "# Migrate SQL Server Instance to Azure SQL VM" - ], - "metadata": { - "azdata_cell_guid": "f706da59-22c3-4317-bf41-c00dde794097" - } - }, - { - "cell_type": "markdown", - "source": [ - "## Source SQL Instance\r\n", - "The following code is used to specify the source SQL Server instance. Data and Server objects will be copied from this server to the target SQL Server instance. The code below will first prompt for the name of the source SQL Server instance. Secondarily, the code will use a secure credential prompt for the login credentials for the source instance. \r\n", - "\r\n", - "*Note: the notebook currently is setup for SQL Authentication. Future updates will add support for multiple authentication types.*\r\n", - "" - ], - "metadata": { - "azdata_cell_guid": "2ac081f4-853a-4381-a303-e6ca557503fb" - } - }, - { - "cell_type": "code", - "source": [ - "# set the name or IP address of the source SQL Server instance\n", - "Add-Type -Assemblyname PresentationFramework\n", - "$sourceInfoBox = New-Object AnyBox.AnyBox\n", - "\n", - "$sourceInfoBox.Prompts = New-AnyBoxPrompt -Name \"serverName\" -Message \"Source SQL Instance name or IP address:\" -ValidateNotEmpty\n", - "$sourceInfoBox.Buttons = New-AnyBoxButton -Name 'submit' -Text \"OK\" -IsDefault\n", - "\n", - "$sourceInfoBox.Topmost = $true\n", - "$sourceInfoBox.WindowStyle = 'None'\n", - "\n", - "$sourceInfoResponse = $sourceInfoBox | Show-AnyBox\n", - "$sourceServerName = $sourceInfoResponse['serverName']\n", - "\n", - "# prompt for username and pw for authentication - supports SQL Authentication currently\n", - "$sourceCredential = Get-Credential -Message \"Enter Source Login Credentials\"\n", - "\n", - "# test the connectivity and display instance information\n", - "$sourceTest = Test-DbaConnection -SqlInstance $sourceServerName -SqlCredential $sourceCredential\n", - "$sourceTest\n", - "\n", - "# sourceConnection is used in the SQL migration cell below\n", - "$sourceConnection = Connect-DbaInstance -SqlInstance $sourceServerName -SqlCredential $sourceCredential" - ], - "metadata": { - "azdata_cell_guid": "76a50416-b804-46ae-a49c-99baaeb31f7d", - "tags": [ - "parameters" - ] - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Target SQL Instance\r\n", - "The following code is used to specify the target SQL Server instance. Data and Server objects will be copied to this server from the source SQL Server instance. The code below will first prompt for the name of the target SQL Server instance. Secondarily, the code will use a secure credential prompt for the login credentials for the target instance. \r\n", - "\r\n", - "*Note: the notebook currently is setup for SQL Authentication. Future updates will add support for multiple authentication types.*\r\n", - "" - ], - "metadata": { - "azdata_cell_guid": "43751f97-d545-4e84-ac99-109c719a048d" - } - }, - { - "cell_type": "code", - "source": [ - "# set the name or IP address of the source SQL Server instance\r\n", - "Add-Type -Assemblyname PresentationFramework\r\n", - "$targetInfoBox = New-Object AnyBox.AnyBox\r\n", - "\r\n", - "$targetInfoBox.Prompts = New-AnyBoxPrompt -Name \"serverName\" -Message \"Target SQL Instance name or IP address:\" -ValidateNotEmpty\r\n", - "$targetInfoBox.Buttons = New-AnyBoxButton -Name 'submit' -Text \"OK\" -IsDefault\r\n", - "\r\n", - "$targetInfoBox.Topmost = $true\r\n", - "$targetInfoBox.WindowStyle = 'None'\r\n", - "\r\n", - "$targetInfoResponse = $targetInfoBox | Show-AnyBox\r\n", - "$targetServerName = $targetInfoResponse['serverName']\r\n", - "\r\n", - "# prompt for username and pw for authentication - supports SQL Authentication currently\r\n", - "$targetCredential = Get-Credential -Message \"Enter Target Login Credentials\"\r\n", - "\r\n", - "## PowerShell Environment \r\n", - "$targetTest = Test-DbaConnection -SqlInstance $targetServerName -SqlCredential $targetCredential\r\n", - "$targetTest\r\n", - "$targetConnection = Connect-DbaInstance -SqlInstance $targetServerName -SqlCredential $targetCredential" - ], - "metadata": { - "azdata_cell_guid": "4f74315c-1147-4fe3-8959-13eb24cb0957" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Login to Microsoft Azure\r\n", - "To configure and provision resources you must log into your Azure account and set the current subscription that is being used for the target SQL Server instance. The following code will help you connect your account and choose the correct subscription. When presented with the list of subscriptions, click on the desired subscription and press OK." - ], - "metadata": { - "azdata_cell_guid": "2ffbcba3-934e-4498-87ed-7030124d3af2" - } - }, - { - "cell_type": "code", - "source": [ - "Connect-AzAccount\r\n", - "$migrationSubscription = Get-AzSubscription | Select-Object -Property Name, Id | Out-GridView -PassThru\r\n", - "Set-AzContext -SubscriptionId $migrationSubscription.Id" - ], - "metadata": { - "azdata_cell_guid": "8b23dcba-8d28-44eb-abbb-c7a54ef6b003", - "tags": [] - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Verify No Active Connections" - ], - "metadata": { - "azdata_cell_guid": "81259d7e-62ac-4cdd-9e1b-2cb4ddb3d3b2" - } - }, - { - "cell_type": "code", - "source": [ - "#TODO - filter connected proceesses for user connections\r\n", - "\r\n", - "\r\n", - "Get-DbaProcess -SqlInstance $SourceServerName -SqlCredential $sourceLogin | \r\n", - "Select Host, login, Program" - ], - "metadata": { - "azdata_cell_guid": "28393e59-4ea1-4f0f-8f9f-8a504f15e723", - "tags": [] - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Temporary Storage for Data Movement\r\n", - "\r\n", - "Offline data migration attempts to use backup to URL and restore from URL as the mechanism for moving data from the source instance to the target instance. This code will check existance of the specified storage account and container to use for data migration. If the resources do not exist they will be created." - ], - "metadata": { - "azdata_cell_guid": "cc18027e-4636-465d-abaf-f3de88fea406" - } - }, - { - "cell_type": "code", - "source": [ - "# TODO - add interactive support for these values\r\n", - "$resourceGroup = \"sqlmig\"\r\n", - "$blobStorageAccount = \"tempsqlmigstorage\"\r\n", - "$containerName = \"backups\"\r\n", - "$location = \"West US 2\"\r\n", - "\r\n", - "# Storage Account\r\n", - "$storageAccount = Get-AzStorageAccount -ResourceGroupName $resourceGroup -Name $blobStorageAccount\r\n", - "if ($storageAccount -eq $null)\r\n", - "{\r\n", - " # specified storage account does not yet exist, attempt to create it\r\n", - " $storageAccount = New-AzStorageAccount -ResourceGroupName $resourceGroup -Name $blobStorageAccount -Location $location -SkuName Standard_LRS -Kind StorageV2\r\n", - "}\r\n", - "$storageAccount\r\n", - "\r\n", - "# Container\r\n", - "$storageContext = $storageAccount.Context\r\n", - "$storageContainer = Get-AzStorageContainer -Name $containerName -Context $storageContext\r\n", - "if ($storageContainer -eq $null)\r\n", - "{\r\n", - " #specified storage container does not yet exist, attempt to create it\r\n", - " $storageContainer = New-AzStorageContainer -Name $containerName -Context $storageContext -Permission Container\r\n", - "}\r\n", - "$storageContainer\r\n", - "\r\n", - "# Provide source instance with SAS token for blob access\r\n", - "$sourceSAS = (New-AzStorageAccountSASToken -Service Blob -ResourceType Object -Permission \"rw\" -Context $storageContext).TrimStart('?')\r\n", - "$sourceCred = New-DbaCredential -SqlInstance $sourceConnection -Name \"https://$blobStorageAccount.blob.core.windows.net/$containerName\" -Identity \"SHARED ACCESS SIGNATURE\" -SecurePassword (ConvertTo-SecureString $sourceSAS -AsPlainText -Force) -Force\r\n", - "$sourceCred\r\n", - "\r\n", - "$targetSAS = (New-AzStorageAccountSASToken -Service Blob -ResourceType Object -Permission \"rw\" -Context $storageContext).TrimStart('?') # -ResourceType Container,Object\r\n", - "$targetCred = New-DbaCredential -SqlInstance $targetConnection -Name \"https://$blobStorageAccount.blob.core.windows.net/$containerName\" -Identity \"SHARED ACCESS SIGNATURE\" -SecurePassword (ConvertTo-SecureString $targetSAS -AsPlainText -Force) -Force\r\n", - "$targetCred\r\n", - "\r\n", - "" - ], - "metadata": { - "azdata_cell_guid": "f7d53cb1-a55d-4634-95f7-d3e8cf9fab52", - "tags": [] - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Start the Server Migration\r\n", - "\r\n", - "The follow code executes the migration of objects from the source SQL Server instance to the target SQL Server instance. There are a couple of options of interest:\r\n", - "\r\n", - "|Parameter|Description|\r\n", - "|---|---|\r\n", - "|Verbose|Include detailed logging information during the migration process|\r\n", - "|WhatIf|Display what the migration process is going to do but do **not** actually perform the migration|\r\n", - "\r\n", - "The above parameters can be turned off by changing the $true value following the parameter to $false in the code cell below. " - ], - "metadata": { - "azdata_cell_guid": "518662fe-3ccf-4ead-80a7-3b890a394975" - } - }, - { - "cell_type": "code", - "source": [ - "$migrationParams = @{\r\n", - " Verbose = $true\r\n", - " WhatIf = $true\r\n", - " Source = $sourceConnection\r\n", - " Destination = $targetConnection\r\n", - " BackupRestore = $true\r\n", - " SharedPath = \"https://$blobStorageAccount.blob.core.windows.net/$containerName\" \r\n", - "}\r\n", - "\r\n", - "Start-DbaMigration @migrationParams" - ], - "metadata": { - "azdata_cell_guid": "b3a291f0-d9c4-438d-8b34-2c42a1a3b373", - "tags": [] - }, - "outputs": [], - "execution_count": null - } - ] -} \ No newline at end of file diff --git a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/offline-migration/readme.md b/extensions/azurehybridtoolkit/notebooks/hybridbook/content/offline-migration/readme.md deleted file mode 100644 index 44ddbbdf4e..0000000000 --- a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/offline-migration/readme.md +++ /dev/null @@ -1,25 +0,0 @@ -# Offline Migration -[Home](../readme.md) - -This chapter is useful for performing an **Offline Migration** of SQL Server 2005 and above to Azure using ADS Notebooks. Offline migrations are by definition disruptive to the flow of data such as in a web application. With these notebooks, a foundation for automated offline migration can be built. - -There are [many methods](https://datamigration.microsoft.com/) to consider when migrating to the cloud. The notebooks in this chapter cover the following scenarios: - -## Notebooks in this Chapter -- [Migrate SQL Server Instance to Azure SQL VM](instance-to-VM.ipynb) using [dbatools](https://dbatools.io), a powerful PS library for SQL tasks. - -- [Migrate SQL Server Database to Azure SQL VM](db-to-VM.ipynb) using dbatools - -- [Migrate SQL Server Database to Azure SQL DB](db-to-SQLDB.ipynb) using PowerShell - -## Why Migrate Data Offline? -There are many reasons to do an offline data migration. If the cost of losing connectivity for the duration of an offline migration can be minimized, then it often outperforms ensuring uptime when moving to the cloud from on-premise. Consider the time it takes to develop and test the deployment process, the infrastructure required to switch networking routing dynamically, and the cost of managing a migration team with the appropriate skills required for an online migration. - -## Recommendations for a Successful Migration -Since the Data Migration Assistant and the Data Migration Service both complement a full migration, use the [Assessments](..\Assessments/readme.md) notebooks and fix all recommendations prior to migration to avoid potential problems during and after migration. -- All pre-requisite libraries are outlined in the [pre-requisites notebook](../prereqs.ipynb) -- Always keep backups of the data in standard formats available to be redeployed in case of a problem -- Test the entire migration process including the process used to restore from backup -- Automate the migration in such a way that anyone can perform it, given access -- Consider creating a new notebook that contains simple data tests with known values on the new system after deployment -## \ No newline at end of file diff --git a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/prereqs.ipynb b/extensions/azurehybridtoolkit/notebooks/hybridbook/content/prereqs.ipynb deleted file mode 100644 index ffe2fb66df..0000000000 --- a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/prereqs.ipynb +++ /dev/null @@ -1,204 +0,0 @@ -{ - "metadata": { - "kernelspec": { - "name": "powershell", - "display_name": "PowerShell", - "language": "powershell" - }, - "language_info": { - "name": "powershell", - "codemirror_mode": "shell", - "mimetype": "text/x-sh", - "file_extension": ".ps1" - } - }, - "nbformat_minor": 2, - "nbformat": 4, - "cells": [ - { - "cell_type": "markdown", - "source": [ - "# Prerequisites and Initial Setup\n", - "\n", - "Most of the notebooks in the Azure SQL Hybrid Cloud Toolkit use either Microsoft PowerShell or Python for their script functionality. This notebook will perform the local machine configuration necessary to execute the various notebooks successfully.\n", - "\n", - "## Microsoft PowerShell Version\n", - "\n", - "Some notebooks use a [PowerShell Notebook kernel](https://youtu.be/BhbiAINQBYE) that requires PS v5.1 or greater." - ], - "metadata": { - "azdata_cell_guid": "26e96caf-0241-43ee-a44b-9743265919d4" - } - }, - { - "cell_type": "code", - "source": [ - "# Check that PowerShell >= v5.1 and Azure Command Line Interface (Az CLI) are installed\r\n", - "if ($PSVersionTable.PSVersion.Major -lt 5)\r\n", - "{\r\n", - " Write-Warning \"Some notebooks require a greater version of PowerShell.\"\r\n", - "}\r\n", - "else {\r\n", - " $psVersion = $PSVersionTable.PSVersion.ToString()\r\n", - " Write-Output \"Found PS version... $psVersion\"\r\n", - "}" - ], - "metadata": { - "azdata_cell_guid": "3ce85087-8cd5-49fc-8e67-00c996e95fbe", - "tags": [] - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## PowerShell Modules\n", - "\n", - "If you do not have PS Nuget provider installed, then please see [Installing PowerShellGet](https://docs.microsoft.com/en-us/powershell/scripting/gallery/installing-psget). If the following code block fails on the first line, setup PowerShellGet with an elevated PowerShell in a separate command window.\n", - "\n", - "The modules required by the toolkit are available on the [PowerShell Gallery](https://www.powershellgallery.com/), a central repository for sharing PS modules and scripts. The setup code in this notebook will create a temporary call to install required PowerShell modules in the current user scope. If the modules have not already been installed, the following code will install them with a new gallery registration setup as trusted to enable installation of modules without prompting. After installing the modules, the gallery registration will be removed.\n", - "\n", - "| Module | Description |\n", - "| --- | --- |\n", - "| dbatools | Provides high-level cmdlets that perform SQL management operations. These CMDLETs have extensive validation tests and configuration options. |\n", - "| Az.Accounts | Provides cmdlets to configure subscription and account settings |\n", - "| Az.Resources | Provides cmdlets to access and provision Microsoft Azure resources |\n", - "| Az.Storage | Provides cmdlets to simplify configuration and access to Azure storage resources |\n", - "| Az.Network | Networking cmdlets for Azure Resource Manager |\n", - "| Az.Compute | Compute service cmdlets |\n", - "| AnyBox | Provides GUI input box support (Windows only) |\n", - "| SqlServer | Provides cmdlets to automate database development and server administration, as well as both multidimensional and tabular cube processing |" - ], - "metadata": { - "azdata_cell_guid": "836a217a-b348-420e-b525-e06580f717b0" - } - }, - { - "cell_type": "code", - "source": [ - "# need to check if nuget package provider is installed\r\n", - "$nuget=(Get-PackageProvider -Name NuGet)\r\n", - "\r\n", - "if (!$nuget)\r\n", - "{\r\n", - " Get-PackageProvider -Name NuGet -ForceBootstrap\r\n", - " Import-PackageProvider -Name NuGet -RequiredVersion 2.8.5.201\r\n", - "}\r\n", - "\r\n", - "#temporary reposistory registration\r\n", - "Register-PSRepository -Name PSGalleryTemp -SourceLocation https://www.powershellgallery.com/api/v2/ -PublishLocation https://www.powershellgallery.com/api/v2/package/ -ScriptSourceLocation https://www.powershellgallery.com/api/v2/items/psscript/ -ScriptPublishLocation https://www.powershellgallery.com/api/v2/package/ -InstallationPolicy Trusted -PackageManagementProvider NuGet\r\n", - "\r\n", - "#required modules for this notebook\r\n", - "$requiredModules = @(\"dbatools\", \"Az.Accounts\", \"Az.Resources\", \"Az.Storage\", \"Az.Network\", \"Az.Compute\", \"AnyBox\",\"SqlServer\")\r\n", - "\r\n", - "#check each module and install if necessary\r\n", - "foreach ($moduleName in $requiredModules) {\r\n", - " if (!(Get-Module -ListAvailable -Name $moduleName)) {\r\n", - " Install-Module -Name $moduleName -Scope CurrentUser -AllowClobber -Repository PSGalleryTemp\r\n", - " }\r\n", - "} \r\n", - "\r\n", - "#remove temporary gallery registration\r\n", - "Unregister-PSRepository -Name PSGalleryTemp\r\n", - "\r\n", - "# Output any missing modules\r\n", - "foreach ($moduleName in $requiredModules) {\r\n", - " $module = Get-InstalledModule $moduleName\r\n", - " if (!($module)) {\r\n", - " Write-Warning \"The specified module is not installed...$moduleName\"\r\n", - " }\r\n", - " else{\r\n", - " $module\r\n", - " }\r\n", - "} " - ], - "metadata": { - "azdata_cell_guid": "e9c3177c-fde7-4c62-8220-62622620f9b4" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## SQL Assessment cmdlets and SMO extension\n", - "\n", - "SQL Assessment API is part of the SQL Server Management Objects (SMO) and can be used with the SQL Server PowerShell module. Because installing the modules may require a local Administrator account's permission, it cannot be done automatically with this Notebook. The **Assessments** Notebooks require the following:\n", - "\n", - "- [Install SMO](https://docs.microsoft.com/en-us/sql/relational-databases/server-management-objects-smo/installing-smo?view=sql-server-ver15)\n", - "- [Install SQL Server PowerShell module](https://docs.microsoft.com/en-us/sql/powershell/download-sql-server-ps-module?view=sql-server-ver15)\n", - "\n", - "## Compatibility Assessment Tool - Data Migration Assistant\n", - "\n", - "The Compatibility Assessment Notebook requires the [Data Migration Assistant](https://www.microsoft.com/en-us/download/confirmation.aspx?id=53595) tool to be installed in order to execute.\n", - "\n", - "**Note:**\n", - "\n", - "> By Default, DMACMD.EXE will be installed to the \"%ProgramFiles%\\\\Microsoft Data Migration Assistant\" folder." - ], - "metadata": { - "azdata_cell_guid": "1b49a7e5-a773-4104-8f88-bd2ea3c806a3" - } - }, - { - "cell_type": "markdown", - "source": [ - "## Azure CLI\n", - "\n", - "Some notebooks also require the latest version of Azure command line interface (CLI). To install, see [https://aka.ms/installazurecliwindows](https://aka.ms/installazurecliwindows). Update instructions can be found at [https://aka.ms/doc/UpdateAzureCliMsi](https://aka.ms/doc/UpdateAzureCliMsi)" - ], - "metadata": { - "azdata_cell_guid": "e032f30b-5ca7-47bf-bf5e-1bd36d2763f8" - } - }, - { - "cell_type": "code", - "source": [ - "# Check for Azure CLI\r\n", - "$azCli = (az --version)\r\n", - "if ($azCli) { \r\n", - " $azVersion = $azCli[0].Split()[-1]\r\n", - " if ($azVersion -eq \"*\") {\r\n", - " $azVersion = $azCli[0].Split()[-2]\r\n", - " }\r\n", - " Write-Output \"Found Az CLI version... $azVersion\"\r\n", - "}\r\n", - "else {\r\n", - " Write-Warning \"Some notebooks require the Az CLI. Please install if to run these notebooks.\"\r\n", - "}" - ], - "metadata": { - "azdata_cell_guid": "398e7abe-5a60-477c-8d04-0545389614b0" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## PowerShell ADS Extension\n", - "\n", - "It may also be useful to install the official release of the PowerShell ADS extension by following the steps in the [Azure Data Studio documentation](vscode-webview://extensioneditor/electron-browser/docs.microsoft.com/en-us/sql/azure-data-studio/extensions). Installing this extension is optional but will enable syntax highlighting and intellisense within ADS. \n", - "\n", - "In the Extensions pane, search for \"PowerShell\" extension and install it there. It is also possible to install a VSIX package from the [PowerShell extension's Releases page](https://github.com/PowerShell/vscode-powershell/releases) through the command line:\n", - "\n", - "`azuredatastudio --install-extension PowerShell-.vsix`" - ], - "metadata": { - "azdata_cell_guid": "1b4aadb8-753b-43e4-8582-d05e51edbb7f" - } - }, - { - "cell_type": "code", - "source": [ - "# azuredatastudio --install-extension PowerShell-v2020.9.0-preview.vsix # Uncomment to install a specific version" - ], - "metadata": { - "azdata_cell_guid": "86ff5c4f-bf3b-4ca0-aba0-edf592106958" - }, - "outputs": [], - "execution_count": null - } - ] -} \ No newline at end of file diff --git a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/provisioning/create-sqldb.ipynb b/extensions/azurehybridtoolkit/notebooks/hybridbook/content/provisioning/create-sqldb.ipynb deleted file mode 100644 index 7520b174ad..0000000000 --- a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/provisioning/create-sqldb.ipynb +++ /dev/null @@ -1,292 +0,0 @@ -{ - "metadata": { - "kernelspec": { - "name": "powershell", - "display_name": "PowerShell", - "language": "powershell" - }, - "language_info": { - "name": "powershell", - "codemirror_mode": "shell", - "mimetype": "text/x-sh", - "file_extension": ".ps1" - } - }, - "nbformat_minor": 2, - "nbformat": 4, - "cells": [ - { - "cell_type": "markdown", - "source": [ - "# Create Azure SQL Database\n", - "\n", - "## Description\n", - "\n", - "Run this notebook to create a Azure SQL Database in a new Azure SQL Server in the cloud and then configure a server-level firewall rule it. For more information, see [Use PowerShell to create a single database and configure a server-level firewall rule](https://docs.microsoft.com/en-us/azure/azure-sql/database/scripts/create-and-configure-database-powershell).\n", - "\n", - "| Line | Variable | Description | Example |\n", - "| --- | --- | --- | --- |\n", - "| 1 | Subscription | Specify the name or ID of the Azure subscription to create Azure resources in | \"ContosoCorp\\_Infra\" |\n", - "| 2 | ResourceGroup | Choose a name to logically group the Azure resources | \"ContosoBackend\" |\n", - "| 3 | Location | Name of geographic location (See the [Appendices](.\\................\\Program%20Files\\Azure%20Data%20Studio\\resources\\app\\out\\vs\\code\\electron-browser\\Appendices.ipynb) for more information) | \"EastUS2\" |\n", - "| 4 | ServerName | The logical server name has to be unique in the system | \"contoso-srv1\" |\n", - "| 5 | AdminLogin | SQL admin login to create | \"SqlAdmin\" |\n", - "| 6 | Password | Temporary password to use (change in the portal for greater security) | \"Temp123\" |\n", - "| 7 | StartIp | Starting IP to begin the IP firewall rule | \"0.0.0.0\" for everyone |\n", - "| 8 | EndIp | Ending IP to end the IP firewall rule | \"0.0.0.0\" for everyone |\n", - "| 9 | DatabaseName | The sample database name | \"ContosoDb1\" |\n", - "| 10 | DbEdition | Database edition. Allowed values include: _Basic_, _Standard_, _Premium_, _GeneralPurpose_, _BusinessCritical_, _Hyperscale_ | GeneralPurpose |\n", - "| 11 | DbCores | Integer for number of vcores to utilize | 2 |\n", - "| 12 | DbComputeGen | The compute generation component for vcores. Allowed values include: _Gen4_, _Gen5_. | Gen5 |\n", - "| 13 | DbMinCapacity | Minimum capacity of vcores to utilize, integer only | 2 |" - ], - "metadata": { - "azdata_cell_guid": "6af59d69-ade7-480a-b33e-52a86fe5bfd3" - } - }, - { - "cell_type": "code", - "source": [ - "$Subscription = \"\"\r\n", - "$ResourceGroup = \"\"\r\n", - "$Location = \"\"\r\n", - "$ServerName = \"\"\r\n", - "$AdminLogin = \"\"\r\n", - "$Password = \"\"\r\n", - "$StartIp = \"\"\r\n", - "$EndIp = \"\"\r\n", - "$DatabaseName = \"\"\r\n", - "$DbEdition = \"\"\r\n", - "$DbCores = \"\"\r\n", - "$DbComputeGen = \"\"\r\n", - "$DbMinCapacity= = \"\"" - ], - "metadata": { - "azdata_cell_guid": "c5c06fd6-8e47-4abb-808a-edc8b1c2d690", - "tags": [ - "parameters" - ] - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Notebook Steps\r\n", - "\r\n", - "Steps of this procedure include:\r\n", - "1. Connect to Azure subscription\r\n", - "1. Provision resource group for SQL Managed Instance\r\n", - "2. Create Sql Server\r\n", - "3. Provision firewall rules to allow access\r\n", - "4. Create Sql Database" - ], - "metadata": { - "azdata_cell_guid": "d5346c50-c03b-4e3a-983f-7b4b22c78319" - } - }, - { - "cell_type": "markdown", - "source": [ - "### Connect to Azure\r\n", - "Prompt for Azure account credentials in a dialog window outside of ADS." - ], - "metadata": { - "azdata_cell_guid": "e34334a7-0d55-4c18-8c0a-1c4a673629cd" - } - }, - { - "cell_type": "code", - "source": [ - "Connect-AzAccount" - ], - "metadata": { - "azdata_cell_guid": "96800b54-48a8-463b-886c-3d0e96f29765" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "### Get Subscription\r\n", - "If a subscription is not specified, open a dialog with list of subscriptions. Selecting one will set that subscription for rest of the notebook." - ], - "metadata": { - "azdata_cell_guid": "ed6b781d-ce7e-4b51-a7ec-1eeeb2032c73" - } - }, - { - "cell_type": "code", - "source": [ - "if (!$Subscription)\r\n", - "{\r\n", - " $Subscription = Get-AzSubscription | Out-GridView -PassThru\r\n", - "}\r\n", - "\r\n", - "Set-AzContext -SubscriptionName $Subscription" - ], - "metadata": { - "azdata_cell_guid": "17b57956-98cf-44de-9ab5-348469ddabf4" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "### Resource Group Provisioning\n", - "\n", - "If the specified Azure Resource Group cannot be found, _New-AzResourceGroup_ will create new resource group in the given subscription." - ], - "metadata": { - "azdata_cell_guid": "3ecc2a29-fb77-4f7f-8901-e9c5c71ce1a2" - } - }, - { - "cell_type": "code", - "source": [ - "$rg = Get-AzResourceGroup | Where ResourceGroupName -eq $ResourceGroup\r\n", - "\r\n", - "if (!$rg)\r\n", - "{\r\n", - " # Need to create a new resource group\r\n", - " Write-Output \"Resource Group $ResourceGroup does not exist. Creating...\"\r\n", - " $rg = New-AzResourceGroup -Name $ResourceGroup -Location $Location\r\n", - "}\r\n", - "\r\n", - "Write-Output \"Using Resource Group:\"\r\n", - "$rg | Format-Table" - ], - "metadata": { - "azdata_cell_guid": "4837690a-2204-49ab-8a19-414a8ce782b6" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "### Create SQL Server\n", - "\n", - "_New-AzSqlServer_ will create an Azure Sql Server instance to logically group databases in." - ], - "metadata": { - "azdata_cell_guid": "2d951526-40dc-49cc-8668-c393eaf58000" - } - }, - { - "cell_type": "code", - "source": [ - "Write-Output \"Creating SqlServer with name $ServerName\"\r\n", - "New-AzSqlServer -ResourceGroupName $ResourceGroup `\r\n", - " -ServerName $ServerName `\r\n", - " -Location $Location `\r\n", - " -SqlAdministratorCredentials $(New-Object -TypeName System.Management.Automation.PSCredential `\r\n", - " -ArgumentList $AdminLogin, $(ConvertTo-SecureString -String $Password -AsPlainText -Force))" - ], - "metadata": { - "azdata_cell_guid": "c45757ac-6a58-468d-a04c-04504f8a2e0e" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "### Create a Server Firewall Rule\n", - "\n", - "_New-AzSqlServerFirewallRule_ creates a new firewall rule for the new SQL Server that allows a range of specified IPs." - ], - "metadata": { - "azdata_cell_guid": "ba895abf-3176-48b5-9e49-a060b3f74370" - } - }, - { - "cell_type": "code", - "source": [ - "Write-Output \"Configuring firewall for Sql Server\"\r\n", - "New-AzSqlServerFirewallRule -ResourceGroupName $ResourceGroup `\r\n", - " -ServerName $ServerName `\r\n", - " -FirewallRuleName \"AllowedIPs\" -StartIpAddress $StartIp -EndIpAddress $EndIp" - ], - "metadata": { - "azdata_cell_guid": "ceae5670-292f-4c45-9c10-4ac85baf2d07" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "### Create SQL Database\r\n", - "_New-AzSqlDatabase_ command will create a new database in the server." - ], - "metadata": { - "azdata_cell_guid": "b460ca8f-65a7-4d6c-94b7-6d7dd9655fad" - } - }, - { - "cell_type": "code", - "source": [ - "New-AzSqlDatabase -ResourceGroupName $ResourceGroup `\r\n", - " -ServerName $ServerName `\r\n", - " -DatabaseName $DatabaseName `\r\n", - " -Edition $DbEdition `\r\n", - " -VCore $DbCores `\r\n", - " -ComputeGeneration $DbComputeGen `\r\n", - " -MinimumCapacity $DbMinCapacity" - ], - "metadata": { - "azdata_cell_guid": "dc3b2f6f-83ac-4a4d-9d81-2f534e90913e" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "### Get Database Details\n", - "\n", - "_Get-AzSqlDatabase_ command gets database details on the server specified." - ], - "metadata": { - "azdata_cell_guid": "0b35ed4f-1786-4102-a09a-a6a360fd20f2" - } - }, - { - "cell_type": "code", - "source": [ - "Get-AzSqlDatabase -ResourceGroupName $ResourceGroup -ServerName $ServerName -DatabaseName $DatabaseName" - ], - "metadata": { - "azdata_cell_guid": "5001bf24-5f3f-434e-abf6-a5c21af4aa32" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Next Steps\r\n", - "\r\n", - "### Update SQL Server Password\r\n", - "Do not forget to open the Azure portal to the specified Resource Group in an external browser and change the new server's password. It is not recommended to keep the database password used in this notebook because it stores results as plain text. \r\n", - "\r\n", - "### More Automation\r\n", - "Use ADS Notebooks to write custom scripts for:\r\n", - "* Commonly used Extract, Transform, Load operations\r\n", - "* Data migration and validation\r\n", - "* Storage backup\r\n", - "* Data integration\r\n", - "\r\n", - "### Related Notebooks\r\n", - "For existing database owners, Migrate a database to Azure SQLDB using this notebook. " - ], - "metadata": { - "azdata_cell_guid": "d8b88393-baba-428d-b136-7125be6d5630" - } - } - ] -} \ No newline at end of file diff --git a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/provisioning/create-sqlmi.ipynb b/extensions/azurehybridtoolkit/notebooks/hybridbook/content/provisioning/create-sqlmi.ipynb deleted file mode 100644 index b67012a02a..0000000000 --- a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/provisioning/create-sqlmi.ipynb +++ /dev/null @@ -1,343 +0,0 @@ -{ - "metadata": { - "kernelspec": { - "name": "powershell", - "display_name": "PowerShell", - "language": "powershell" - }, - "language_info": { - "name": "powershell", - "codemirror_mode": "shell", - "mimetype": "text/x-sh", - "file_extension": ".ps1" - } - }, - "nbformat_minor": 2, - "nbformat": 4, - "cells": [ - { - "cell_type": "markdown", - "source": [ - "# Create Azure SQL Managed Instance\n", - "\n", - "## Description\n", - "\n", - "This PowerShell script example creates a managed instance in a dedicated subnet within a new virtual network. It also configures a route table and a network security group for the virtual network. Once the script has been successfully run, the managed instance can be accessed from within the virtual network or from an on-premises environment. See [Configure Azure VM to connect to Azure SQL Database Managed Instance](https://docs.microsoft.com/en-us/azure/azure-sql/managed-instance/connect-vm-instance-configure) and [Configure a point-to-site connection to Azure SQL Managed Instance from on-premises](https://docs.microsoft.com/en-us/azure/azure-sql/managed-instance/point-to-site-p2s-configure).\n", - "\n", - "" - ], - "metadata": { - "azdata_cell_guid": "2dcca704-2945-4763-8cbe-116b90696f9c" - } - }, - { - "cell_type": "markdown", - "source": [ - "Steps of this procedure include:\r\n", - "1. Connect to Azure subscription\r\n", - "1. Provision resource group for SQL Managed Instance\r\n", - "2. Configure Network Settings\r\n", - "3. Create credentials to be used for Managed Instance\r\n", - "4. Create Managed Instance" - ], - "metadata": { - "azdata_cell_guid": "dd1cfc56-2919-416b-968d-cdf3345dcb1c" - } - }, - { - "cell_type": "code", - "source": [ - "$NSnetworkModels = \"Microsoft.Azure.Commands.Network.Models\"\r\n", - "$NScollections = \"System.Collections.Generic\"\r\n", - "\r\n", - "# Set the resource group name and Location for your managed instance\r\n", - "$ResourceGroupName = \"myResourceGroup-$(Get-Random)\"\r\n", - "$Location = \"eastus2\"\r\n", - "\r\n", - "# Set the networking values for your managed instance\r\n", - "$VNetName = \"myVnet-$(Get-Random)\"\r\n", - "$VNetAddressPrefix = \"10.0.0.0/16\"\r\n", - "$DefaultSubnetName = \"myDefaultSubnet-$(Get-Random)\"\r\n", - "$DefaultSubnetAddressPrefix = \"10.0.0.0/24\"\r\n", - "$MiSubnetName = \"myMISubnet-$(Get-Random)\"\r\n", - "$MiSubnetAddressPrefix = \"10.0.0.0/24\"\r\n", - "\r\n", - "#Set the managed instance name for the new managed instance\r\n", - "$InstanceName = \"myMIName-$(Get-Random)\"\r\n", - "# Set the admin login and password for your managed instance\r\n", - "$MiAdminSqlLogin = \"SqlAdmin\"\r\n", - "$MiAdminSqlPassword = \"ChangeYourAdminPassword1\"\r\n", - "\r\n", - "# Set the managed instance service tier, compute level, and License mode\r\n", - "$Edition = \"General Purpose\"\r\n", - "$VCores = 8\r\n", - "$MaxStorage = 256\r\n", - "$ComputeGeneration = \"Gen5\"\r\n", - "$License = \"LicenseIncluded\" #\"BasePrice\" or LicenseIncluded if you have don't have SQL Server licence that can be used for AHB discount\r\n", - "" - ], - "metadata": { - "azdata_cell_guid": "e574921c-3611-47d7-a872-564d07030815", - "tags": [ - "parameters" - ] - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Connect to Azure Account" - ], - "metadata": { - "azdata_cell_guid": "2ab3812a-cb4f-44a1-94d4-a0681a05606f" - } - }, - { - "cell_type": "code", - "source": [ - "Connect-AzAccount" - ], - "metadata": { - "azdata_cell_guid": "b137afd5-512a-4261-90c0-b43dbf1724ff" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Get Subscription\r\n", - "Below command will open a Dialouge Box with list of subscriptions.\r\n", - "Selecting one of those will set that subscription for rest of the commands." - ], - "metadata": { - "azdata_cell_guid": "d3af080f-c3d1-4a04-a168-78f8bfce247b" - } - }, - { - "cell_type": "code", - "source": [ - "$subscription = Get-AzSubscription | Out-GridView -PassThru\r\n", - "Set-AzContext -SubscriptionName $subscription" - ], - "metadata": { - "azdata_cell_guid": "6356291d-96e4-4cbb-bae8-27e81f6e0bb8" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Create the resource group\r\n", - "Create a resource group with the _New-AzResourceGroup_ command. An Azure resource group is a logical container into which Azure resources are deployed and managed. A resource group must be created first:" - ], - "metadata": { - "azdata_cell_guid": "c8a4555b-8b80-41f9-9e8f-eaab3b419030" - } - }, - { - "cell_type": "code", - "source": [ - "# Create Azure resource group, if necessary\r\n", - "$resourceGroup = Get-AzResourceGroup -Name $ResourceGroupName\r\n", - "\r\n", - "if (!$resourceGroup)\r\n", - "{\r\n", - " # Need to create a new resource group\r\n", - " Write-Output \"Resource Group $RG1 does not exist. Creating...\"\r\n", - " $resourceGroup = New-AzResourceGroup -Name $RG1 -Location $Location -Tag @{Owner=\"SQLDB-Samples\"}\r\n", - "}" - ], - "metadata": { - "azdata_cell_guid": "920c904f-6c6b-4e59-8305-f34b44297a64" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Configure virtual network, subnets, network security group, and routing table" - ], - "metadata": { - "azdata_cell_guid": "2ed3ccec-ab94-4c20-aa74-6d67c3db014d" - } - }, - { - "cell_type": "code", - "source": [ - "$networkSecurityGroupMiManagementService = New-AzNetworkSecurityGroup `\r\n", - " -Name 'myNetworkSecurityGroupMiManagementService' `\r\n", - " -ResourceGroupName $ResourceGroupName `\r\n", - " -Location $Location\r\n", - "\r\n", - "$routeTableMiManagementService = New-AzRouteTable `\r\n", - " -Name 'myRouteTableMiManagementService' `\r\n", - " -ResourceGroupName $ResourceGroupName `\r\n", - " -Location $Location\r\n", - "\r\n", - "$virtualNetwork = New-AzVirtualNetwork `\r\n", - " -ResourceGroupName $ResourceGroupName `\r\n", - " -Location $Location `\r\n", - " -Name $VNetName `\r\n", - " -AddressPrefix $VNetAddressPrefix\r\n", - "\r\n", - " Add-AzVirtualNetworkSubnetConfig `\r\n", - " -Name $MiSubnetName `\r\n", - " -VirtualNetwork $virtualNetwork `\r\n", - " -AddressPrefix $MiSubnetAddressPrefix `\r\n", - " -NetworkSecurityGroup $networkSecurityGroupMiManagementService `\r\n", - " -RouteTable $routeTableMiManagementService |\r\n", - " Set-AzVirtualNetwork\r\n", - "\r\n", - "$virtualNetwork = Get-AzVirtualNetwork -Name $VNetName -ResourceGroupName $ResourceGroupName\r\n", - "\r\n", - "$subnet= $virtualNetwork.Subnets[0]" - ], - "metadata": { - "azdata_cell_guid": "1f7cc6ec-ef04-4ffe-85d5-6ec40c5b2d1c" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Create a delegation" - ], - "metadata": { - "azdata_cell_guid": "a55da861-b130-451f-a017-4a033123e76d" - } - }, - { - "cell_type": "code", - "source": [ - "$subnet.Delegations = New-Object \"$NScollections.List``1[$NSnetworkModels.PSDelegation]\"\r\n", - "$delegationName = \"dgManagedInstance\" + (Get-Random -Maximum 1000)\r\n", - "$delegation = New-AzDelegation -Name $delegationName -ServiceName \"Microsoft.Sql/managedInstances\"\r\n", - "$subnet.Delegations.Add($delegation)\r\n", - "\r\n", - "Set-AzVirtualNetwork -VirtualNetwork $virtualNetwork\r\n", - "\r\n", - "$miSubnetConfigId = $subnet.Id\r\n", - "\r\n", - "\r\n", - "\r\n", - "$allowParameters = @{\r\n", - " Access = 'Allow'\r\n", - " Protocol = 'Tcp'\r\n", - " Direction= 'Inbound'\r\n", - " SourcePortRange = '*'\r\n", - " SourceAddressPrefix = 'VirtualNetwork'\r\n", - " DestinationAddressPrefix = '*'\r\n", - "}\r\n", - "$denyInParameters = @{\r\n", - " Access = 'Deny'\r\n", - " Protocol = '*'\r\n", - " Direction = 'Inbound'\r\n", - " SourcePortRange = '*'\r\n", - " SourceAddressPrefix = '*'\r\n", - " DestinationPortRange = '*'\r\n", - " DestinationAddressPrefix = '*'\r\n", - "}\r\n", - "$denyOutParameters = @{\r\n", - " Access = 'Deny'\r\n", - " Protocol = '*'\r\n", - " Direction = 'Outbound'\r\n", - " SourcePortRange = '*'\r\n", - " SourceAddressPrefix = '*'\r\n", - " DestinationPortRange = '*'\r\n", - " DestinationAddressPrefix = '*'\r\n", - "}\r\n", - "\r\n", - "Get-AzNetworkSecurityGroup `\r\n", - " -ResourceGroupName $ResourceGroupName `\r\n", - " -Name \"myNetworkSecurityGroupMiManagementService\" |\r\n", - " Add-AzNetworkSecurityRuleConfig `\r\n", - " @allowParameters `\r\n", - " -Priority 1000 `\r\n", - " -Name \"allow_tds_inbound\" `\r\n", - " -DestinationPortRange 1433 |\r\n", - " Add-AzNetworkSecurityRuleConfig `\r\n", - " @allowParameters `\r\n", - " -Priority 1100 `\r\n", - " -Name \"allow_redirect_inbound\" `\r\n", - " -DestinationPortRange 11000-11999 |\r\n", - " Add-AzNetworkSecurityRuleConfig `\r\n", - " @denyInParameters `\r\n", - " -Priority 4096 `\r\n", - " -Name \"deny_all_inbound\" |\r\n", - " Add-AzNetworkSecurityRuleConfig `\r\n", - " @denyOutParameters `\r\n", - " -Priority 4096 `\r\n", - " -Name \"deny_all_outbound\" |\r\n", - " Set-AzNetworkSecurityGroup" - ], - "metadata": { - "azdata_cell_guid": "cb4292f6-1a78-43ec-81cb-505ebd791527" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Create credentials" - ], - "metadata": { - "azdata_cell_guid": "e83be748-e6ec-46e1-b056-18be20185195" - } - }, - { - "cell_type": "code", - "source": [ - "$secpassword = ConvertTo-SecureString $MiAdminSqlPassword -AsPlainText -Force\r\n", - "$credential = New-Object System.Management.Automation.PSCredential ($MiAdminSqlLogin, $secpassword)\r\n", - "" - ], - "metadata": { - "azdata_cell_guid": "63bca2ea-fbf8-4e2d-8f58-a2b7bd913f6e" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Create managed instance" - ], - "metadata": { - "azdata_cell_guid": "d17b701e-4c36-423c-b297-602879022621" - } - }, - { - "cell_type": "markdown", - "source": [ - "### This script will take a minimum of 3 hours to create a new managed instance in a new virtual network. \r\n", - "### A second managed instance is created much faster." - ], - "metadata": { - "azdata_cell_guid": "3fdb03c6-3404-4ab6-9795-c2293cbea693" - } - }, - { - "cell_type": "code", - "source": [ - "New-AzSqlInstance -Name $InstanceName `\r\n", - " -ResourceGroupName $ResourceGroupName -Location $Location -SubnetId $miSubnetConfigId `\r\n", - " -AdministratorCredential $credential `\r\n", - " -StorageSizeInGB $MaxStorage -VCore $VCores -Edition $Edition `\r\n", - " -ComputeGeneration $ComputeGeneration -LicenseType $License\r\n", - "" - ], - "metadata": { - "azdata_cell_guid": "e3b55f89-d795-4d98-a24d-ade53cf53648" - }, - "outputs": [], - "execution_count": null - } - ] -} \ No newline at end of file diff --git a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/provisioning/create-sqlvm.ipynb b/extensions/azurehybridtoolkit/notebooks/hybridbook/content/provisioning/create-sqlvm.ipynb deleted file mode 100644 index b1dce3fe0c..0000000000 --- a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/provisioning/create-sqlvm.ipynb +++ /dev/null @@ -1,347 +0,0 @@ -{ - "metadata": { - "kernelspec": { - "name": "powershell", - "display_name": "PowerShell", - "language": "powershell" - }, - "language_info": { - "name": "powershell", - "codemirror_mode": "shell", - "mimetype": "text/x-sh", - "file_extension": ".ps1" - } - }, - "nbformat_minor": 2, - "nbformat": 4, - "cells": [ - { - "cell_type": "markdown", - "source": [ - "# Create Azure SQL Virtual Machine\n", - "\n", - "## Description\n", - "\n", - "For more information about other Azure PowerShell options for creating SQL VMs, see the [Provisioning guide for SQL Server VMs with Azure PowerShell](https://docs.microsoft.com/en-us/azure/virtual-machines/windows/sql/virtual-machines-windows-ps-sql-create). See also [Quickstart guide](https://docs.microsoft.com/en-us/azure/virtual-machines/windows/sql/quickstart-sql-vm-create-powershell) to creating a SQL Server VM with Azure PowerShell.\n", - "\n", - "Steps of this procedure include:\n", - "\n", - "1. Define notebook variables\n", - "2. Connect to Azure subscription\n", - "3. Provision resource group for SQL VM migration\n", - "4. Create a storage account\n", - "5. Configure Network Settings\n", - "6. Provision SQL VM\n", - "7. Configure SQL VM IaaS agent" - ], - "metadata": { - "azdata_cell_guid": "e479b550-d6bd-49c5-965a-34a7d1d16412" - } - }, - { - "cell_type": "markdown", - "source": [ - "## Define Notebook Variables" - ], - "metadata": { - "azdata_cell_guid": "37db2e50-dcde-4dd5-820c-7dc11212f1eb" - } - }, - { - "cell_type": "code", - "source": [ - "# Resource Group \r\n", - "$Subscription = \"\" # Leave blank to open a dialog to get Azure Subscription, or explicitly specify\r\n", - "$ResourceGroup = \"\" # Name of the resource group to create in the current subscription\r\n", - "$Location = \"\" # see Appendix for a list of location settings\r\n", - "\r\n", - "# Compute\r\n", - "$VMName = \"\" # VM to create\r\n", - "$PublisherName = \"\" # Name of Publisher, Default would be 'MicrosoftSQLServer'\r\n", - "$Version = \"\" # Version of VM, Default would be 'latest'\r\n", - "\r\n", - "# Storage\r\n", - "$StorageAccountName = $ResourceGroup + \"_storage\"\r\n", - "$StorageSKU = \"\" # Choose your storage SKU (see appendix)\r\n", - "$StorageName = \"sqlstorage\" + (Get-Random -Minimum 1 -Maximum 100)\r\n", - "\r\n", - "# VM Password\r\n", - "$secureVMPassword = \"\" # Create the password for VM." - ], - "metadata": { - "azdata_cell_guid": "b9aff9cc-a3af-41cb-a2a5-35f36b2bcc55", - "tags": [ - "parameters" - ] - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Get Subscription\n", - "\n", - "Open a dialog box with a list of subscriptions, if one isn't specified. Selecting one will set that subscription in the Az CLI context for rest of the notebook." - ], - "metadata": { - "azdata_cell_guid": "b58f1048-3e9d-4888-bda0-4d0443a11c97" - } - }, - { - "cell_type": "code", - "source": [ - "if (!$Subscription)\r\n", - "{\r\n", - " $Subscription = Get-AzSubscription | Out-GridView -PassThru\r\n", - "} \r\n", - "Set-AzContext -SubscriptionName $Subscription\r\n", - "Connect-AzAccount -Subscription $Subscription" - ], - "metadata": { - "azdata_cell_guid": "0cc44e68-3810-46f4-b29c-e6ad4321e384" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Create Azure Resource Group" - ], - "metadata": { - "azdata_cell_guid": "c17dd324-5c55-484f-8a25-2a5a7e43633e" - } - }, - { - "cell_type": "code", - "source": [ - "# Create Azure resource group, if necessary\r\n", - "$rg = Get-AzResourceGroup | Where ResourceGroupName -eq $ResourceGroup\r\n", - "\r\n", - "if (!$rg)\r\n", - "{\r\n", - " # Need to create a new resource group\r\n", - " Write-Output \"Resource Group '$ResourceGroup' does not exist\"\r\n", - " $rg = New-AzResourceGroup -Name $ResourceGroup -Location $Location\r\n", - "}" - ], - "metadata": { - "azdata_cell_guid": "2c37ef31-5f47-4918-a2b3-05e11aab28da" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Create a storage account\r\n", - "VMs require storage resources for OS, SQL data and logs. Create a new storage account as a place for it." - ], - "metadata": { - "azdata_cell_guid": "11b78695-40d3-45d7-8e3c-32d086ddf94a" - } - }, - { - "cell_type": "code", - "source": [ - "$StorageAccount = Get-AzStorageAccount | Where StorageAccountName -eq $StorageAccountName\r\n", - "\r\n", - "if (!$StorageAccount)\r\n", - "{\r\n", - " Write-Output \"Storage Account $StorageName does not exist. Creating...\"\r\n", - " $StorageAccount = New-AzStorageAccount -ResourceGroupName $ResourceGroup -Name $StorageName -SkuName $StorageSku -Kind \"Storage\" -Location $Location\r\n", - "}" - ], - "metadata": { - "azdata_cell_guid": "f992bf91-a84a-40c2-813b-cb778907370d", - "tags": [] - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Configure network settings\r\n", - "Create a virtual network, subnet, and a public IP address. These resources are used to provide network connectivity to the virtual machine and connect it to the internet." - ], - "metadata": { - "azdata_cell_guid": "202634eb-7edf-4ff4-8486-fffbda45dbc8" - } - }, - { - "cell_type": "code", - "source": [ - "$SubnetName = $ResourceGroup + \"subnet\"\r\n", - "$VnetName = $ResourceGroup + \"vnet\"\r\n", - "$PipName = $ResourceGroup + $(Get-Random)\r\n", - "\r\n", - "# Create a subnet configuration\r\n", - "$SubnetConfig = New-AzVirtualNetworkSubnetConfig -Name $SubnetName -AddressPrefix 192.168.1.0/24\r\n", - "\r\n", - "# Create a virtual network\r\n", - "$Vnet = New-AzVirtualNetwork -ResourceGroupName $ResourceGroup -Location $Location `\r\n", - " -Name $VnetName -AddressPrefix 192.168.0.0/16 -Subnet $SubnetConfig\r\n", - "\r\n", - "# Create a public IP address and specify a DNS name\r\n", - "$Pip = New-AzPublicIpAddress -ResourceGroupName $ResourceGroup -Location $Location `\r\n", - " -AllocationMethod Static -IdleTimeoutInMinutes 4 -Name $PipName" - ], - "metadata": { - "azdata_cell_guid": "af88cdae-1a62-4990-9231-094481c9337d" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "2. Create a network security group. Configure rules to allow remote desktop (RDP) and SQL Server connections." - ], - "metadata": { - "azdata_cell_guid": "3b25e16e-b150-4a2e-80dc-66f2d18b43fb" - } - }, - { - "cell_type": "code", - "source": [ - "# Rule to allow remote desktop (RDP)\r\n", - "$NsgRuleRDP = New-AzNetworkSecurityRuleConfig -Name \"RDPRule\" -Protocol Tcp `\r\n", - " -Direction Inbound -Priority 1000 -SourceAddressPrefix * -SourcePortRange * `\r\n", - " -DestinationAddressPrefix * -DestinationPortRange 3389 -Access Allow\r\n", - "\r\n", - "#Rule to allow SQL Server connections on port 1433\r\n", - "$NsgRuleSQL = New-AzNetworkSecurityRuleConfig -Name \"MSSQLRule\" -Protocol Tcp `\r\n", - " -Direction Inbound -Priority 1001 -SourceAddressPrefix * -SourcePortRange * `\r\n", - " -DestinationAddressPrefix * -DestinationPortRange 1433 -Access Allow\r\n", - "\r\n", - "# Create the network security group\r\n", - "$NsgName = $ResourceGroup + \"nsg\"\r\n", - "$Nsg = New-AzNetworkSecurityGroup -ResourceGroupName $ResourceGroup `\r\n", - " -Location $Location -Name $NsgName `\r\n", - " -SecurityRules $NsgRuleRDP,$NsgRuleSQL" - ], - "metadata": { - "azdata_cell_guid": "debe940d-0d0f-4540-be5b-4d6495d338e1" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "3. Create the network interface." - ], - "metadata": { - "azdata_cell_guid": "d44de03c-d4f2-48ef-8a60-507069d6c08e" - } - }, - { - "cell_type": "code", - "source": [ - "$InterfaceName = $ResourceGroup + \"int\"\r\n", - "$Interface = New-AzNetworkInterface -Name $InterfaceName `\r\n", - " -ResourceGroupName $ResourceGroup -Location $Location `\r\n", - " -SubnetId $VNet.Subnets[0].Id -PublicIpAddressId $Pip.Id `\r\n", - " -NetworkSecurityGroupId $Nsg.Id" - ], - "metadata": { - "azdata_cell_guid": "6dbb3ea0-b52f-4ed2-bd24-59096d134e88" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Create the SQL VM\r\n", - "1. Define your credentials to sign in to the VM. The username is \"azureadmin\". Make sure you change before running the command." - ], - "metadata": { - "azdata_cell_guid": "b25dca9e-269b-45db-8cdf-efa53e2213d2" - } - }, - { - "cell_type": "code", - "source": [ - "# Define a credential object\r\n", - "$SecurePassword = ConvertTo-SecureString $secureVMPassword `\r\n", - " -AsPlainText -Force\r\n", - "$Cred = New-Object System.Management.Automation.PSCredential (\"azureadmin\", $securePassword)" - ], - "metadata": { - "azdata_cell_guid": "40d0c992-860a-4b83-8104-16ec7e6e7983" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "2. Create a virtual machine configuration object and then create the VM. The following command creates a SQL Server 2017 Developer Edition VM on Windows Server 2016." - ], - "metadata": { - "azdata_cell_guid": "2038172f-3f29-499c-ad68-88a1d96ead1f" - } - }, - { - "cell_type": "code", - "source": [ - "# Create a virtual machine configuration\r\n", - "\r\n", - "$VMConfig = New-AzVMConfig -VMName $VMName -VMSize Standard_DS13_V2 |\r\n", - " Set-AzVMOperatingSystem -Windows -ComputerName $VMName -Credential $Cred -ProvisionVMAgent -EnableAutoUpdate |\r\n", - " Set-AzVMSourceImage -PublisherName \"MicrosoftSQLServer\" -Offer \"SQL2017-WS2016\" -Skus \"SQLDEV\" -Version \"latest\" |\r\n", - " Add-AzVMNetworkInterface -Id $Interface.Id" - ], - "metadata": { - "azdata_cell_guid": "6625d7b2-6c97-432e-b5f5-be4ca93017ae" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Create Virtual Machine" - ], - "metadata": { - "azdata_cell_guid": "c42ec570-331a-46ea-b358-b05e47320967" - } - }, - { - "cell_type": "code", - "source": [ - "# Create the VM\r\n", - "New-AzVM -ResourceGroupName $ResourceGroup -Location $Location -VM $VMConfig" - ], - "metadata": { - "azdata_cell_guid": "05fa1f3d-94e1-480f-ad20-d3006bafc6ac" - }, - "outputs": [], - "execution_count": null - }, - { - "cell_type": "markdown", - "source": [ - "## Install the SQL IaaS Agent\r\n", - "To get portal integration and SQL VM features, you must install the SQL Server IaaS Agent Extension. To install the agent on the new VM, run the following command after the VM is created." - ], - "metadata": { - "azdata_cell_guid": "ef07b3d1-3e2d-45f0-b9d3-fb00be2a7da9" - } - }, - { - "cell_type": "code", - "source": [ - "Set-AzVMSqlServerExtension -ResourceGroupName $ResourceGroup -VMName $VMName -name \"SQLIaasExtension\" -version \"2.0\" -Location $Location" - ], - "metadata": { - "azdata_cell_guid": "bb3b5436-c34b-44b3-b631-ea60c9dcf16a" - }, - "outputs": [], - "execution_count": null - } - ] -} \ No newline at end of file diff --git a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/provisioning/readme.md b/extensions/azurehybridtoolkit/notebooks/hybridbook/content/provisioning/readme.md deleted file mode 100644 index 9b041d5769..0000000000 --- a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/provisioning/readme.md +++ /dev/null @@ -1,9 +0,0 @@ -# Provisioning -[Home](../readme.md) - -This chapter contains Notebooks that help provision new Azure SQL resources that can be used as migration targets for existing on-premises SQL instances and databases. Use alongside the planning notebooks to use existing resources as the basis for the best type of resource to create and how it should be configured. You can use the notebooks and configure the settings manually or provide a provisioning plan created by the [Create Provisioning Plan](../provisioning/provisioning-plan.ipynb) notebook. - -## Notebooks in this Chapter -- [Create Azure SQL Virtual Machine](create-sqlvm.ipynb) - SQL Server on Azure Virtual Machines enables to use full versions of SQL Server in the cloud without having to manage any on-premises hardware. The virtual machine image gallery allows to create a SQL Server VM with the right version, edition, and operating system -- [Create Azure SQL Managed Instance](create-sqlmi.ipynb) - Azure SQL Managed Instance is the intelligent, scalable, cloud database service that combines the broadest SQL Server engine compatibility with all the benefits of a fully managed and evergreen platform as a service. An instance is a copy of the sqlservr.exe executable that runs as an operating system service -- [Create Azure SQL Database](create-sqldb.ipynb) - Azure SQL Database is Microsoft's fully managed cloud relational database service in Microsoft Azure. It shares the same code base as traditional SQL Servers but with Microsoft's Cloud first strategy the newest features of SQL Server are actually released to Azure SQL Database first. Use this notebook when a need is systematic collection of data that stores data in tables \ No newline at end of file diff --git a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/readme.md b/extensions/azurehybridtoolkit/notebooks/hybridbook/content/readme.md deleted file mode 100644 index da0e87fe70..0000000000 --- a/extensions/azurehybridtoolkit/notebooks/hybridbook/content/readme.md +++ /dev/null @@ -1,45 +0,0 @@ -# Welcome to the Azure SQL Hybrid Toolkit - -## About - -The **Azure SQL Hybrid Toolkit** is a [Jupyter Book](https://jupyterbook.org/intro.html) extension of [Azure Data Studio](https://docs.microsoft.com/en-us/sql/azure-data-studio/download-azure-data-studio) (ADS) designed to help [Azure SQL Database](https://azure.microsoft.com/en-us/services/sql-database/) and ADS users deploy, migrate and configure for a hybrid cloud environment. The toolkit was designed with and intended to be executed within ADS. This is to ensure the best possible user experience for those without vast knowledge of Azure services while adhering closely to the software _best practices_ standards required by experienced cloud users. - -## Chapters -* [Prerequisites and Initial Setup](prereqs.ipynb) - Notebook installation of required modules. - -* [Assessments](Assessments/readme.md) - Notebooks that contain examples to determine whether a given database or SQL Server instance is ready to migrate by utilizing SQL Assessments. SQL instances are scanned based on a "best practices" set of rules. - -* [Networking](networking/readme.md) - Setup secure Point-to-Site (P2S) or Site-to-Site (S2S) network connectivity to Microsoft Azure using a Virtual Private Network (VPN). This notebook serves as a building block for other notebooks as communicating securely between on-premise and Azure is essential for many tasks. - -* [Provisioning](provisioning/readme.md) - Creating and communicating with SQL Resources in Microsoft Azure. Includes common tasks such as creating SQL Virtual Machines or SQL Managed Instances in the cloud. - -* [Data Portability](data-portability/readme.md) - Install a custom Azure function to facilitate importing and exporting cloud resources. The solution uses parallel tasks in Azure Batch to perform data storage work. Azure Batch is a process that runs large-scale parallel and high-performance computing jobs efficiently in Azure. - -* [High Availability and Disaster Recovery](hadr/readme.md) - Notebooks to leverage Azure SQL for business continuity in a hybrid cloud environment. - -* [Offline Migration](offline-migration/readme.md) - Notebooks to manage SQL migration tasks to the cloud - -* [Glossary](glossary.md) - set of defined terms - -* [Appendices](Appendices.ipynb) - Additional information - -## Goals and Methodology -The toolkit better positions a customer with regards to planning, migrating, and thriving in a hybrid cloud environment by: - -* Providing SQL Azure users with reliable free software and content that is well-written and executable -* Greatly simplifying the integration of Azure Data services into an existing environment -* Positioning Azure to be the natural cloud services choice with a low-friction experience -* Notebooks are executable by a normal user (unless otherwise specified) on minimal hardware -* Most notebooks require some configuration. If so, the proper configurations should be clearly located towards the top of the notebook or cell, whichever is most appropriate -* By design, Notebooks are written to be executed from top-to-bottom. Therefore, each notebook has a specific task to perform and should focus only on that task. It may contain several cells to execute but it will adhere to the one-task per notebook paradigm - -**NOTE:** Executing notebooks could potentially create new Azure Resources which may incur charges to the Azure Subscription. Make sure the repercussions of executing any cells are understood. - -## General Guidelines -1. Read notebooks carefully to understand their use -2. Execute the Prerequisites and Initial Setup notebook once per setup -3. Configure individual notebooks by modifying the cells tagged as "Parameters" -4. Execute notebook from top-to-bottom to complete a task -5. Save the executed notebook as a different notebook, if desired - -The resulting output is displayed as text underneath each cell when executed. The notebook can be shared or archived easily. Furthermore, the toolkit's tasks are building blocks for a more advanced automation framework. Hopefully, this toolkit will enable greater understanding for the Azure SQL Hybrid user, especially when it comes to automation. \ No newline at end of file diff --git a/extensions/azurehybridtoolkit/notebooks/hybridbook/requirements.txt b/extensions/azurehybridtoolkit/notebooks/hybridbook/requirements.txt deleted file mode 100644 index 31e7ed1599..0000000000 --- a/extensions/azurehybridtoolkit/notebooks/hybridbook/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -# Requirements for the demo notebooks -# Useful for MyBinder configuration diff --git a/extensions/azurehybridtoolkit/package.json b/extensions/azurehybridtoolkit/package.json deleted file mode 100644 index 3d793af594..0000000000 --- a/extensions/azurehybridtoolkit/package.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "name": "azurehybridtoolkit", - "displayName": "%displayName%", - "description": "%description%", - "version": "0.1.0", - "publisher": "Microsoft", - "preview": true, - "license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/main/LICENSE.txt", - "icon": "images/extension.png", - "engines": { - "vscode": "*", - "azdata": "*" - }, - "activationEvents": [ - "*" - ], - "capabilities": { - "virtualWorkspaces": false, - "untrustedWorkspaces": { - "supported": true - } - }, - "repository": { - "type": "git", - "url": "https://github.com/Microsoft/azuredatastudio.git" - }, - "main": "./out/main", - "extensionDependencies": [ - "Microsoft.mssql", - "Microsoft.notebook" - ], - "contributes": { - "commands": [ - { - "command": "books.hybridbook", - "title": "%title.cloudHybridBooks%", - "category": "%books-preview-category%" - } - ], - "menus": { - "view/title": [ - { - "command": "books.hybridbook", - "when": "view == providedBooksView" - } - ] - }, - "notebook.books": [ - { - "name": "%title.cloudHybridBooks%", - "path": "notebooks/hybridbook" - } - ] - }, - "dependencies": { - "vscode-nls": "^4.0.0" - }, - "__metadata": { - "publisherDisplayName": "Microsoft", - "publisherId": "Microsoft" - } -} diff --git a/extensions/azurehybridtoolkit/package.nls.json b/extensions/azurehybridtoolkit/package.nls.json deleted file mode 100644 index d57f0613aa..0000000000 --- a/extensions/azurehybridtoolkit/package.nls.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "displayName": "Azure SQL Hybrid Cloud Toolkit", - "description": "Opens up Azure SQL Hybrid Cloud Toolkit Jupyter Book", - "title.openJupyterBook": "Open Azure SQL Hybrid Cloud Toolkit Jupyter Book", - "title.cloudHybridBooks": "Azure SQL Hybrid Cloud Toolkit", - "books-preview-category": "Jupyter Books" -} diff --git a/extensions/azurehybridtoolkit/src/main.ts b/extensions/azurehybridtoolkit/src/main.ts deleted file mode 100644 index 9bd84ad194..0000000000 --- a/extensions/azurehybridtoolkit/src/main.ts +++ /dev/null @@ -1,14 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - - -export function activate() { - -} - -// this method is called when your extension is deactivated -export function deactivate(): void { - -} diff --git a/extensions/azurehybridtoolkit/src/typings/ref.d.ts b/extensions/azurehybridtoolkit/src/typings/ref.d.ts deleted file mode 100644 index 641bd7ffe9..0000000000 --- a/extensions/azurehybridtoolkit/src/typings/ref.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -/// -/// -/// -/// diff --git a/extensions/azurehybridtoolkit/tsconfig.json b/extensions/azurehybridtoolkit/tsconfig.json deleted file mode 100644 index 1281974082..0000000000 --- a/extensions/azurehybridtoolkit/tsconfig.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "extends": "../tsconfig.base.json", - "compileOnSave": true, - "compilerOptions": { - "module": "commonjs", - "outDir": "./out", - "lib": [ - "es6", "es2015.promise" - ], - "sourceMap": true, - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "moduleResolution": "node" - }, - "exclude": [ - "node_modules" - ] -} diff --git a/extensions/azurehybridtoolkit/yarn.lock b/extensions/azurehybridtoolkit/yarn.lock deleted file mode 100644 index 42730cad83..0000000000 --- a/extensions/azurehybridtoolkit/yarn.lock +++ /dev/null @@ -1,8 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -vscode-nls@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.0.0.tgz#4001c8a6caba5cedb23a9c5ce1090395c0e44002" - integrity sha512-qCfdzcH+0LgQnBpZA53bA32kzp9rpq/f66Som577ObeuDlFIrtbEJ+A/+CCxjIh4G8dpJYNCKIsxpRAHIfsbNw== diff --git a/resources/localization/LCL/de/azurehybridtoolkit.xlf.lcl b/resources/localization/LCL/de/azurehybridtoolkit.xlf.lcl deleted file mode 100644 index 560b89ca61..0000000000 --- a/resources/localization/LCL/de/azurehybridtoolkit.xlf.lcl +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/resources/localization/LCL/es/azurehybridtoolkit.xlf.lcl b/resources/localization/LCL/es/azurehybridtoolkit.xlf.lcl deleted file mode 100644 index 2b9ddbc929..0000000000 --- a/resources/localization/LCL/es/azurehybridtoolkit.xlf.lcl +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/resources/localization/LCL/fr/azurehybridtoolkit.xlf.lcl b/resources/localization/LCL/fr/azurehybridtoolkit.xlf.lcl deleted file mode 100644 index 86ca96a068..0000000000 --- a/resources/localization/LCL/fr/azurehybridtoolkit.xlf.lcl +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/resources/localization/LCL/it/azurehybridtoolkit.xlf.lcl b/resources/localization/LCL/it/azurehybridtoolkit.xlf.lcl deleted file mode 100644 index 430e2d7596..0000000000 --- a/resources/localization/LCL/it/azurehybridtoolkit.xlf.lcl +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/resources/localization/LCL/ja/azurehybridtoolkit.xlf.lcl b/resources/localization/LCL/ja/azurehybridtoolkit.xlf.lcl deleted file mode 100644 index dda2c6d243..0000000000 --- a/resources/localization/LCL/ja/azurehybridtoolkit.xlf.lcl +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/resources/localization/LCL/ko/azurehybridtoolkit.xlf.lcl b/resources/localization/LCL/ko/azurehybridtoolkit.xlf.lcl deleted file mode 100644 index 57304769c0..0000000000 --- a/resources/localization/LCL/ko/azurehybridtoolkit.xlf.lcl +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/resources/localization/LCL/pt-BR/azurehybridtoolkit.xlf.lcl b/resources/localization/LCL/pt-BR/azurehybridtoolkit.xlf.lcl deleted file mode 100644 index b7e7ed2daa..0000000000 --- a/resources/localization/LCL/pt-BR/azurehybridtoolkit.xlf.lcl +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/resources/localization/LCL/ru/azurehybridtoolkit.xlf.lcl b/resources/localization/LCL/ru/azurehybridtoolkit.xlf.lcl deleted file mode 100644 index 21c358e0d6..0000000000 --- a/resources/localization/LCL/ru/azurehybridtoolkit.xlf.lcl +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/resources/localization/LCL/zh-Hans/azurehybridtoolkit.xlf.lcl b/resources/localization/LCL/zh-Hans/azurehybridtoolkit.xlf.lcl deleted file mode 100644 index 42da522610..0000000000 --- a/resources/localization/LCL/zh-Hans/azurehybridtoolkit.xlf.lcl +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/resources/localization/LCL/zh-Hant/azurehybridtoolkit.xlf.lcl b/resources/localization/LCL/zh-Hant/azurehybridtoolkit.xlf.lcl deleted file mode 100644 index e770ad0bb5..0000000000 --- a/resources/localization/LCL/zh-Hant/azurehybridtoolkit.xlf.lcl +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/resources/xlf/LocProject.json b/resources/xlf/LocProject.json index a4d255998c..06e7efc0fa 100644 --- a/resources/xlf/LocProject.json +++ b/resources/xlf/LocProject.json @@ -38,12 +38,6 @@ "LclFile": "resources\\localization\\LCL\\{Lang}\\azurecore.xlf.lcl", "CopyOption": "LangIDOnPath", "OutputPath": "resources\\xlf" - }, - { - "SourceFile": "resources\\xlf\\en\\azurehybridtoolkit.xlf", - "LclFile": "resources\\localization\\LCL\\{Lang}\\azurehybridtoolkit.xlf.lcl", - "CopyOption": "LangIDOnPath", - "OutputPath": "resources\\xlf" }, { "SourceFile": "resources\\xlf\\en\\cms.xlf", diff --git a/resources/xlf/de/azurehybridtoolkit.de.xlf b/resources/xlf/de/azurehybridtoolkit.de.xlf deleted file mode 100644 index 225975a625..0000000000 --- a/resources/xlf/de/azurehybridtoolkit.de.xlf +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - Jupyter Books - Jupyter Books - - - Opens up Azure SQL Hybrid Cloud Toolkit Jupyter Book - Öffnet das Jupyter Book für Azure SQL Hybrid Cloud Toolkit. - - - Azure SQL Hybrid Cloud Toolkit - Azure SQL Hybrid Cloud Toolkit - - - Azure SQL Hybrid Cloud Toolkit - Azure SQL Hybrid Cloud Toolkit - - - Open Azure SQL Hybrid Cloud Toolkit Jupyter Book - Jupyter Book für Azure SQL Hybrid Cloud Toolkit öffnen - - - - \ No newline at end of file diff --git a/resources/xlf/en/azurehybridtoolkit.xlf b/resources/xlf/en/azurehybridtoolkit.xlf deleted file mode 100644 index 35206d435a..0000000000 --- a/resources/xlf/en/azurehybridtoolkit.xlf +++ /dev/null @@ -1,20 +0,0 @@ - - - - - Jupyter Books - - - Opens up Azure SQL Hybrid Cloud Toolkit Jupyter Book - - - Azure SQL Hybrid Cloud Toolkit - - - Azure SQL Hybrid Cloud Toolkit - - - Open Azure SQL Hybrid Cloud Toolkit Jupyter Book - - - \ No newline at end of file diff --git a/resources/xlf/es/azurehybridtoolkit.es.xlf b/resources/xlf/es/azurehybridtoolkit.es.xlf deleted file mode 100644 index 5ff1335457..0000000000 --- a/resources/xlf/es/azurehybridtoolkit.es.xlf +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - Jupyter Books - Libros de Jupyter - - - Opens up Azure SQL Hybrid Cloud Toolkit Jupyter Book - Se abrirá el libro de Jupyter del kit de herramientas de nube híbrida de Azure SQL. - - - Azure SQL Hybrid Cloud Toolkit - Kit de herramientas de nube híbrida de Azure SQL - - - Azure SQL Hybrid Cloud Toolkit - Kit de herramientas de nube híbrida de Azure SQL - - - Open Azure SQL Hybrid Cloud Toolkit Jupyter Book - Abrir libro de Jupyter del kit de herramientas de nube híbrida de Azure SQL - - - - \ No newline at end of file diff --git a/resources/xlf/fr/azurehybridtoolkit.fr.xlf b/resources/xlf/fr/azurehybridtoolkit.fr.xlf deleted file mode 100644 index 5e73a65236..0000000000 --- a/resources/xlf/fr/azurehybridtoolkit.fr.xlf +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - Jupyter Books - Notebooks Jupyter - - - Opens up Azure SQL Hybrid Cloud Toolkit Jupyter Book - Ouvrir l’ouvrage kit de ressources cloud hybride Azure SQL - - - Azure SQL Hybrid Cloud Toolkit - kit de ressources cloud hybride Azure SQL - - - Azure SQL Hybrid Cloud Toolkit - kit de ressources cloud hybride Azure SQL - - - Open Azure SQL Hybrid Cloud Toolkit Jupyter Book - Ouvrir l’ouvrage kit de ressources cloud hybride Azure SQL - - - - \ No newline at end of file diff --git a/resources/xlf/it/azurehybridtoolkit.it.xlf b/resources/xlf/it/azurehybridtoolkit.it.xlf deleted file mode 100644 index a3b4dcb117..0000000000 --- a/resources/xlf/it/azurehybridtoolkit.it.xlf +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - Jupyter Books - Book di Jupyter - - - Opens up Azure SQL Hybrid Cloud Toolkit Jupyter Book - Apre il book di Jupyter di Azure SQL Hybrid Cloud Toolkit - - - Azure SQL Hybrid Cloud Toolkit - Azure SQL Hybrid Cloud Toolkit - - - Azure SQL Hybrid Cloud Toolkit - Azure SQL Hybrid Cloud Toolkit - - - Open Azure SQL Hybrid Cloud Toolkit Jupyter Book - Apri book di Jupyter di Azure SQL Hybrid Cloud Toolkit - - - - \ No newline at end of file diff --git a/resources/xlf/ja/azurehybridtoolkit.ja.xlf b/resources/xlf/ja/azurehybridtoolkit.ja.xlf deleted file mode 100644 index b117779773..0000000000 --- a/resources/xlf/ja/azurehybridtoolkit.ja.xlf +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - Jupyter Books - Jupyter ブック - - - Opens up Azure SQL Hybrid Cloud Toolkit Jupyter Book - Azure SQL ハイブリッドクラウドツールキット Jupyter ブックを開きます - - - Azure SQL Hybrid Cloud Toolkit - Azure SQL Hybrid Cloud Toolkit - - - Azure SQL Hybrid Cloud Toolkit - Azure SQL Hybrid Cloud Toolkit - - - Open Azure SQL Hybrid Cloud Toolkit Jupyter Book - Azure SQL ハイブリッドクラウド ツールキット Jupyter ブックを開く - - - - \ No newline at end of file diff --git a/resources/xlf/ko/azurehybridtoolkit.ko.xlf b/resources/xlf/ko/azurehybridtoolkit.ko.xlf deleted file mode 100644 index ce582d12d1..0000000000 --- a/resources/xlf/ko/azurehybridtoolkit.ko.xlf +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - Jupyter Books - Jupyter Book - - - Opens up Azure SQL Hybrid Cloud Toolkit Jupyter Book - Azure SQL 하이브리드 클라우드 도구 키트 Jupyter Book을 엽니다. - - - Azure SQL Hybrid Cloud Toolkit - Azure SQL 하이브리드 클라우드 도구 키트 - - - Azure SQL Hybrid Cloud Toolkit - Azure SQL 하이브리드 클라우드 도구 키트 - - - Open Azure SQL Hybrid Cloud Toolkit Jupyter Book - Azure SQL 하이브리드 클라우드 도구 키트 Jupyter Book 열기 - - - - \ No newline at end of file diff --git a/resources/xlf/pt-br/azurehybridtoolkit.pt-BR.xlf b/resources/xlf/pt-br/azurehybridtoolkit.pt-BR.xlf deleted file mode 100644 index 447a32ca8f..0000000000 --- a/resources/xlf/pt-br/azurehybridtoolkit.pt-BR.xlf +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - Jupyter Books - Jupyter Books - - - Opens up Azure SQL Hybrid Cloud Toolkit Jupyter Book - Abre o Livro Jupyter do Kit de Ferramentas de Nuvem Híbrida do SQL do Azure - - - Azure SQL Hybrid Cloud Toolkit - Kit de Ferramentas de Nuvem Híbrida do SQL do Azure - - - Azure SQL Hybrid Cloud Toolkit - Kit de Ferramentas de Nuvem Híbrida do SQL do Azure - - - Open Azure SQL Hybrid Cloud Toolkit Jupyter Book - Abrir o Livro Jupyter do Kit de Ferramentas de Nuvem Híbrida do SQL do Azure - - - - \ No newline at end of file diff --git a/resources/xlf/ru/azurehybridtoolkit.ru.xlf b/resources/xlf/ru/azurehybridtoolkit.ru.xlf deleted file mode 100644 index 3b0858836f..0000000000 --- a/resources/xlf/ru/azurehybridtoolkit.ru.xlf +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - Jupyter Books - Книги Jupyter - - - Opens up Azure SQL Hybrid Cloud Toolkit Jupyter Book - Открывает книгу Jupyter набора инструментов гибридного облака Azure SQL - - - Azure SQL Hybrid Cloud Toolkit - Набор инструментов гибридного облака Azure SQL - - - Azure SQL Hybrid Cloud Toolkit - Набор инструментов гибридного облака Azure SQL - - - Open Azure SQL Hybrid Cloud Toolkit Jupyter Book - Открыть книгу Jupyter набора инструментов гибридного облака Azure SQL - - - - \ No newline at end of file diff --git a/resources/xlf/zh-hans/azurehybridtoolkit.zh-Hans.xlf b/resources/xlf/zh-hans/azurehybridtoolkit.zh-Hans.xlf deleted file mode 100644 index 9053d4f213..0000000000 --- a/resources/xlf/zh-hans/azurehybridtoolkit.zh-Hans.xlf +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - Jupyter Books - Jupyter Book - - - Opens up Azure SQL Hybrid Cloud Toolkit Jupyter Book - 打开 Azure SQL 混合云工具包 Jupyter Book - - - Azure SQL Hybrid Cloud Toolkit - Azure SQL 混合云工具包 - - - Azure SQL Hybrid Cloud Toolkit - Azure SQL 混合云工具包 - - - Open Azure SQL Hybrid Cloud Toolkit Jupyter Book - 打开 Azure SQL 混合云工具包 Jupyter Book - - - - \ No newline at end of file diff --git a/resources/xlf/zh-hant/azurehybridtoolkit.zh-Hant.xlf b/resources/xlf/zh-hant/azurehybridtoolkit.zh-Hant.xlf deleted file mode 100644 index bcc431da40..0000000000 --- a/resources/xlf/zh-hant/azurehybridtoolkit.zh-Hant.xlf +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - Jupyter Books - Jupyter 書籍 - - - Opens up Azure SQL Hybrid Cloud Toolkit Jupyter Book - 開啟 Azure SQL 混合式雲端工具組 Jupyter 書籍 - - - Azure SQL Hybrid Cloud Toolkit - Azure SQL 混合式雲端工具組 - - - Azure SQL Hybrid Cloud Toolkit - Azure SQL 混合式雲端工具組 - - - Open Azure SQL Hybrid Cloud Toolkit Jupyter Book - 開啟 Azure SQL 混合式雲端工具組 Jupyter 書籍 - - - - \ No newline at end of file