Merge pull request #596 from srdan-bozovic-msft/master

Add script for Subnet Delegation
This commit is contained in:
Jovan Popovic (MSFT)
2019-07-23 13:02:45 +02:00
committed by GitHub
2 changed files with 531 additions and 0 deletions
@@ -0,0 +1,86 @@
# Delegate subnet for Managed Instance deployment
Script that validates and prepares virtual network and subnet for Managed Instance creation to comply with [networking requirements](https://docs.microsoft.com/azure/sql-database/sql-database-managed-instance-vnet-configuration#requirements).
### Contents
[About this sample](#about-this-sample)<br/>
[Before you begin](#before-you-begin)<br/>
[Run this sample](#run-this-sample)<br/>
[Sample details](#sample-details)<br/>
[Disclaimers](#disclaimers)<br/>
[Related links](#related-links)<br/>
<a name=about-this-sample></a>
## About this sample
- **Applies to:** Azure SQL Database
- **Key features:** Managed Instance
- **Workload:** n/a
- **Programming Language:** PowerShell
- **Authors:** Srdan Bozovic
- **Update history:** n/a
<a name=before-you-begin></a>
## Before you begin
To run this sample, you need the following prerequisites.
**Software prerequisites:**
1. PowerShell 5.1 or PowerShell Core 6.0
2. Azure PowerShell Az module
**Azure prerequisites:**
1. Permission to manage Azure virtual network
<a name=run-this-sample></a>
## Run this sample
Run the script below from either Windows or Azure Cloud Shell
```powershell
$scriptUrlBase = 'https://raw.githubusercontent.com/Microsoft/sql-server-samples/master/samples/manage/azure-sql-db-managed-instance/delegate-subnet'
$parameters = @{
subscriptionId = '<subscriptionId>'
resourceGroupName = '<resourceGroupName>'
virtualNetworkName = '<virtualNetworkName>'
subnetName = '<subnetName>'
}
Invoke-Command -ScriptBlock ([Scriptblock]::Create((iwr ($scriptUrlBase+'/delegateSubnet.ps1?t='+ [DateTime]::Now.Ticks)).Content)) -ArgumentList $parameters
```
<a name=sample-details></a>
## Sample details
This sample shows how to prepare Azure virtual network and subnet for Managed Instance deployment using PowerShell
This is done in three simple steps:
- Validate - Selected virtual netwok and subnet are validated for Managed Instance networking requirements
- Confirm - User is shown a set of changes that need to be made to prepare subnet for Managed Instance deployment and asked for consent
- Prepare - Virtual network and subnet are configured properly
<a name=disclaimers></a>
## Disclaimers
The scripts and this guide are copyright Microsoft Corporations and are provided as samples. They are not part of any Azure service and are not covered by any SLA or other Azure-related agreements. They are provided as-is with no warranties express or implied. Microsoft takes no responsibility for the use of the scripts or the accuracy of this document. Familiarize yourself with the scripts before using them.
<a name=related-links></a>
## Related Links
<!-- Links to more articles. Remember to delete "en-us" from the link path. -->
For more information, see these articles:
- [What is a Managed Instance?](https://docs.microsoft.com/azure/sql-database/sql-database-managed-instance)
- [Configure a VNet for Azure SQL Database Managed Instance](https://docs.microsoft.com/azure/sql-database/sql-database-managed-instance-vnet-configuration)
@@ -0,0 +1,445 @@
$parameters = $args[0]
$subscriptionId = $parameters['subscriptionId']
$resourceGroupName = $parameters['resourceGroupName']
$virtualNetworkName = $parameters['virtualNetworkName']
$subnetName = $parameters['subnetName']
$force = $parameters['force']
$NSnetworkModels = "Microsoft.Azure.Commands.Network.Models"
$NScollections = "System.Collections.Generic"
function VerifyPSVersion {
Write-Host "Verifying PowerShell version."
if ($PSVersionTable.PSEdition -eq "Desktop") {
if (($PSVersionTable.PSVersion.Major -ge 6) -or
(($PSVersionTable.PSVersion.Major -eq 5) -and ($PSVersionTable.PSVersion.Minor -ge 1))) {
Write-Host "PowerShell version verified." -ForegroundColor Green
}
else {
Write-Host "You need to install PowerShell version 5.1 or heigher." -ForegroundColor Red
Break;
}
}
else {
if ($PSVersionTable.PSVersion.Major -ge 6) {
Write-Host "PowerShell version verified." -ForegroundColor Green
}
else {
Write-Host "You need to install PowerShell version 6.0 or heigher." -ForegroundColor Red
Break;
}
}
}
function EnsureAzModule {
Write-Host "Checking if Az module is imported."
$module = Get-Module Az
If ($null -eq $module) {
try {
Import-Module Az -ErrorAction Stop
Write-Host "Module Az imported." -ForegroundColor Green
}
catch {
Install-Module Az -AllowClobber
Write-Host "Module Az installed." -ForegroundColor Green
}
} else {
Write-Host "Module Az imported." -ForegroundColor Green
}
}
function EnsureLogin () {
$context = Get-AzContext
If ($null -eq $context.Subscription) {
Write-Host "Sign-in..."
If ($null -eq (Connect-AzAccount -ErrorAction SilentlyContinue -ErrorVariable Errors)) {
Write-Host ("Sign-in failed: {0}" -f $Errors[0].Exception.Message) -ForegroundColor Red
Break
}
}
Write-Host "Sign-in successful." -ForegroundColor Green
}
function SelectSubscriptionId {
param (
$subscriptionId
)
Write-Host "Selecting subscription '$subscriptionId'."
$context = Get-AzContext
If($context.Subscription.Id -ne $subscriptionId)
{
Try
{
Select-AzSubscription -SubscriptionId $subscriptionId -ErrorAction Stop | Out-null
}
Catch
{
Write-Host "Subscription selection failed: $_" -ForegroundColor Red
Break
}
}
Write-Host "Subscription selected." -ForegroundColor Green
}
function LoadVirtualNetwork {
param (
$resourceGroupName,
$virtualNetworkName
)
Write-Host("Loading virtual network '{0}' in resource group '{1}'." -f $virtualNetworkName, $resourceGroupName)
$virtualNetwork = Get-AzVirtualNetwork -ResourceGroupName $resourceGroupName -Name $virtualNetworkName -ErrorAction SilentlyContinue -WarningAction SilentlyContinue
If($null -ne $virtualNetwork.Id)
{
Write-Host "Virtual network loaded." -ForegroundColor Green
return $virtualNetwork
}
else
{
Write-Host "Virtual network not found." -ForegroundColor Red
Break
}
}
function LoadVirtualNetworkSubnet {
param (
$virtualNetwork,
$subnetName
)
Write-Host("Loading subnet '{0}'." -f $subnetName)
$subnets = $virtualNetwork.Subnets.Name
If($true -eq $subnets.Contains($subnetName))
{
$subnetIndex = $subnets.IndexOf($subnetName)
$subnet = $virtualNetwork.Subnets[$subnetIndex]
Write-Host "Subnet loaded." -ForegroundColor Green
return $subnet
}
else
{
Write-Host "Subnet not found." -ForegroundColor Red
Break
}
}
function VerifySubnet {
param (
$subnet
)
Write-Host("Verifying subnet '{0}'." -f $subnet.Name)
If($subnet.AddressPrefix.Split('/')[1] -le 28)
{
Write-Host "Passed Validation - Subnet is of enough size." -ForegroundColor Green
}
Else
{
Write-Host "Failed Validation - Minimum supported subnet size is /28." -ForegroundColor Red
Break
}
If(
($subnet.IpConfigurations.Count -eq 0) -and
(
($subnet.ResourceNavigationLinks.Count -eq 0) -or
($subnet.ResourceNavigationLinks[0].LinkedResourceType -eq 'Microsoft.Sql/virtualClusters')
)
)
{
Write-Host "Passed Validation - There are no conflicting resources inside the subnet." -ForegroundColor Green
}
Else
{
Write-Host "Failed Validation - Subnet is already in use." -ForegroundColor Red
Break
}
}
function VerifyDelegation {
param (
$subnet
)
$result = @{
isDelegatedToManagedInstance = $false;
isDelegated = $false;
success = $false;
}
$delegation = Get-AzDelegation -Subnet $subnet
If($delegation -ne $null)
{
$result['isDelegated'] = $true
$result['isDelegatedToManagedInstance'] = $delegation.ServiceName -eq "Microsoft.Sql/managedInstances"
}
$result['success'] = -not $result['isDelegated'] -or $result['isDelegatedToManagedInstance']
return $result
}
function LoadNetworkSecurityGroup {
param (
$subnet
)
Write-Host("Loading Network security group for subnet '{0}'." -f $subnet.Name)
If(
$null -ne $subnet.NetworkSecurityGroup
)
{
$nsgSegments = ($subnet.NetworkSecurityGroup.Id).Split("/", [System.StringSplitOptions]::RemoveEmptyEntries)
$nsgName = $nsgSegments[-1].Trim()
$nsgResourceGroup = $nsgSegments[3].Trim()
$networkSecurityGroup = Get-AzNetworkSecurityGroup -ResourceGroupName $nsgResourceGroup -Name $nsgName
Write-Host "Network security group security group loaded." -ForegroundColor Green
return $networkSecurityGroup
}
Else
{
return $null
}
}
function HasNSG {
param (
$subnet
)
$nsg = LoadNetworkSecurityGroup $subnet
return $nsg -ne $null
}
function LoadRouteTable {
param (
$subnet
)
Write-Host("Loading Route table for subnet '{0}'." -f $subnet.Name)
If(
$null -ne $subnet.RouteTable
)
{
$rtSegments = ($subnet.RouteTable.Id).Split("/", [System.StringSplitOptions]::RemoveEmptyEntries)
$rtName = $rtSegments[-1].Trim()
$rtResourceGroup = $rtSegments[3].Trim()
$routeTable = Get-AzRouteTable -ResourceGroupName $rtResourceGroup -Name $rtName
Write-Host "Route table loaded." -ForegroundColor Green
return $routeTable
}
return $null
}
function HasRouteTable {
param (
$subnet
)
$routeTable = LoadRouteTable $subnet
return $routeTable -ne $null
}
function CreateNSG
{
param(
$virtualNetwork,
$subnet
)
Write-Host "Creating Network security group."
$networkSecurityGroupName = "nsgManagedInstance" + (Get-Random -Maximum 1000)
$securityRules = New-Object "$NScollections.List``1[$NSnetworkModels.PSSecurityRule]"
$rule = New-AzNetworkSecurityRuleConfig `
-Name prepare-allow_tds_inbound `
-Description "Allow access to data" `
-Direction Inbound -Priority 1000 -Access Allow -Protocol Tcp `
-SourceAddressPrefix VirtualNetwork -DestinationAddressPrefix $subnet.AddressPrefix `
-SourcePortRange * -DestinationPortRange @("1433","11000-11999")
$securityRules.Add($rule)
$rule = New-AzNetworkSecurityRuleConfig `
-Name prepare-deny_all_inbound `
-Description "Deny all other inbound traffic" `
-Direction Inbound -Priority 4096 -Access Deny -Protocol * `
-SourceAddressPrefix * -DestinationAddressPrefix * `
-SourcePortRange * -DestinationPortRange *
$securityRules.Add($rule)
$rule = New-AzNetworkSecurityRuleConfig `
-Name prepare-deny_all_outbound `
-Description "Deny all other outbound traffic" `
-Direction Outbound -Priority 4096 -Access Deny -Protocol * `
-SourceAddressPrefix * -DestinationAddressPrefix * `
-SourcePortRange * -DestinationPortRange *
$securityRules.Add($rule)
Try
{
$networkSecurityGroup = New-AzNetworkSecurityGroup -Name $networkSecurityGroupName -ResourceGroupName $virtualNetwork.ResourceGroupName -Location $virtualNetwork.Location -SecurityRules $securityRules
}
Catch
{
Write-Host "Failed: $_" -ForegroundColor Red
}
Write-Host "Associating Network security group."
$subnet.NetworkSecurityGroup = $networkSecurityGroup
}
function CreateRouteTable
{
param(
$virtualNetwork,
$subnet
)
Write-Host "Creating Route table."
$routeTableName = "rtManagedInstance" + (Get-Random -Maximum 1000)
Try
{
$routeTable = New-AzRouteTable -Name $routeTableName -ResourceGroupName $virtualNetwork.ResourceGroupName -Location $virtualNetwork.Location
}
Catch
{
Write-Host "Failed: $_" -ForegroundColor Red
}
Write-Host "Associating Route table."
$subnet.RouteTable = $routeTable
}
function DelegateSubnet
{
param(
$subnet
)
Write-Host "Creating Subnet Delegation for Managed Instance."
$subnet.Delegations = New-Object "$NScollections.List``1[$NSnetworkModels.PSDelegation]"
$delegationName = "dgManagedInstance" + (Get-Random -Maximum 1000)
Try
{
$delegation = New-AzDelegation -Name $delegationName -ServiceName "Microsoft.Sql/managedInstances"
}
Catch
{
Write-Host "Failed: $_" -ForegroundColor Red
}
Write-Host "Associating Subnet Delegation for Managed Instance."
$subnet.Delegations.Add($delegation)
}
function SetVirtualNetwork
{
param($virtualNetwork)
Write-Host "Applying changes to the virtual network."
Try
{
Set-AzVirtualNetwork -VirtualNetwork $virtualNetwork -ErrorAction Stop -WarningAction SilentlyContinue | Out-Null
}
Catch
{
Write-Host "Failed: $_" -ForegroundColor Red
}
}
VerifyPSVersion
EnsureAzModule
EnsureLogin
SelectSubscriptionId -subscriptionId $subscriptionId
$virtualNetwork = LoadVirtualNetwork -resourceGroupName $resourceGroupName -virtualNetworkName $virtualNetworkName
$subnet = LoadVirtualNetworkSubnet -virtualNetwork $virtualNetwork -subnetName $subnetName
Write-Host
$delegationVerificationResult = VerifyDelegation $subnet
If($delegationVerificationResult['success'])
{
VerifySubnet $subnet
$hasNsg = HasNSG $subnet
$hasRouteTable = HasRouteTable $subnet
$isValid = $delegationVerificationResult['isDelegatedToManagedInstance'] -and $hasNsg -and $hasRouteTable
If($isValid -ne $true)
{
Write-Host
Write-Host("---------- To delegate the virtual network subnet for Managed Instance this script will: --------------- ") -ForegroundColor Yellow
Write-Host
If(-not $hasNsg)
{
Write-Host "Create Network security group and associate it to the subnet." -ForegroundColor Yellow
}
If(-not $hasRouteTable)
{
Write-Host "Create Route table and associate it to the subnet." -ForegroundColor Yellow
}
If(-not $delegationVerificationResult['isDelegatedToManagedInstance'])
{
Write-Host "Delegate subnet to Managed Instance." -ForegroundColor Yellow
}
Write-Host
Write-Host("-------------------------------------------------------------------------------------------------------- ") -ForegroundColor Yellow
Write-Host
$applyChanges = $force
If($applyChanges -ne $true)
{
$reply = Read-Host -Prompt "Do you want to make these changes? [y/n]"
$applyChanges = $reply -match "[yY]"
Write-Host
}
If ($applyChanges)
{
If(-not $hasNsg)
{
CreateNSG $virtualNetwork $subnet
}
If(-not $hasRouteTable)
{
CreateRouteTable $virtualNetwork $subnet
}
If(-not $delegationVerificationResult['isDelegatedToManagedInstance'])
{
DelegateSubnet $subnet
}
SetVirtualNetwork $virtualNetwork
Write-Host
Write-Host "Subnet delegated to the Managed Instance." -ForegroundColor Green
Write-Host "https://portal.azure.com/#create/Microsoft.SQLManagedInstance"
}
Else
{
Write-Host
Write-Host "Subnet delegation canceled." -ForegroundColor Yellow
}
}
Else
{
Write-Host "Subnet is already delegated to the Managed Instance." -ForegroundColor Green
Write-Host "https://portal.azure.com/#create/Microsoft.SQLManagedInstance"
}
}
Else
{
Write-Host
Write-Host "Subnet is already delegated to other service." -ForegroundColor Red
}