Scripts/azure-rm-sql-create.ps1 (554 lines of code) (raw):
<#
.SYNOPSIS
powershell script to list or create a new Azure SQL server and / or database in Azure Resource Manager
.DESCRIPTION
powershell script to list or create a new Azure SQL server and / or database in Azure Resource Manager
to enable script execution, you may need to Set-ExecutionPolicy Bypass -Force
Copyright 2017 Microsoft Corporation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
requires azure powershell sdk (install-module azurerm)
script does the following:
logs into azure rm
checks location for validity and for availability of Azure SQL
checks for resource group and creates if not exists
checks resource group for azure sql servers
if sql server is specified, will query server for existing database
will generate / prompt for sql server name if one is not existing / specified
checks password for complexity requirements
creates sql server and database with firewall rules
on success displays connection string info
https://aka.ms/azure-rm-sql-create.ps1
https://docs.microsoft.com/en-us/azure/sql-database/sql-database-get-started-powershell
https://docs.microsoft.com/en-us/azure/sql-database/sql-database-performance-guidance
minimum parameters : resource group, location, databaseName, adminPassword
.NOTES
File Name : azure-rm-sql-create.ps1
Author : jagilber
Version : 170808 made $sqlVersion a variable defaulting to 12.0. authenticate-azurerm
History :
170522 adding to wiki
170516 added -generateUniqueName
170514 added description
.EXAMPLE
.\azure-rm-sql-create.ps1 -resourceGroupName newResourceGroup -location eastus -databaseName myNewDatabase -adminPassword myNewP@ssw0rd
create a new sql database on an existing or new sql server
.EXAMPLE
.\azure-rm-sql-create.ps1 -resourceGroupName newResourceGroup -location eastus -databaseName myNewDatabase -credentials (get-credential) -generateUniqueName
create a new sql database on a new random named sql server using prompted credentials
.EXAMPLE
.\azure-rm-sql-create.ps1 -resourceGroupName existingResourceGroup -listAvailable
list available sql servers and databases in resource group existingResourceGroup
.EXAMPLE
.\azure-rm-sql-create.ps1 -resourceGroupName existingResourceGroup -server sql-server-01 -listAvailable
list existing databases on sql-server-01 in resource group existingResourceGroup
.EXAMPLE
.\azure-rm-sql-create.ps1 -resourceGroupName existingResourceGroup -server sql-server-01 -serviceTier S0 -databaseName TestDB
create a new sql database TestDB on sql-server-01 using service tier S0
.PARAMETER resourceGroupName
required paramater for the resource group name for new database and sql server
.PARAMETER location
required paramater for the resource group name region location
.PARAMETER serverName
if specified, will be used for the sql server name.
will check and if not exists, create new sql server. if named sql server exists, existing sql server will be used.
if not specified, or not exists, will prompt for name or will generate random name if generateUniqueName is specified.
.PARAMETER databaseName
if specifed, will be used for the database name.
will check and if not exists, create new database.
if not specified, will generate random name if generateUniqueName is specified.
.PARAMETER adminUserName
if specified, will be used for sql administrator logon.
if not specified, 'sql-administrator' will be used.
NOTE: admin and administrator can NOT be used.
.PARAMETER adminPassword
requred parameter for the sql administrator password.
will be checked for current azure rm password complexity requirements.
.PARAMETER credentials
if specified, will be used for the sql administrator and password credentials
NOTE: use (get-credential) as the argument.
.PARAMETER generateUniqueName
if specified, will generate random sql server name which have to be globally unique
.PARAMETER nsgStartIpAllow
if specified, ip address will be the starting range of ip addresses to allow
.PARAMETER nsgEndIpAllow
if specified, ip address will be the ending range of ip addresses to allow
.PARAMETER maskPassword
if specified, will not display provided password in connection string output
.PARAMETER listAvailable
if specified, will list available sql servers and databases in resource gropup
.PARAMETER serviceTier
if specified, will use the provided service tier.
by default, will use cheapest tier 'Basic'
https://docs.microsoft.com/en-us/azure/sql-database/sql-database-performance-guidance
.PARAMETER sqlServerVersion
sql server version. default is 12.0
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$resourceGroupName,
[string]$location,
[string]$servername,
[string]$adminUserName = "sql-administrator",
[string]$adminPassword,
[pscredential]$credentials,
[string]$nsgStartIpAllow = "0.0.0.0",
[string]$nsgEndIpAllow = "255.255.255.255",
[string]$databaseName,
[switch]$generateUniqueName,
[switch]$maskPassword,
[switch]$listAvailable,
[switch]$nolog,
[string]$serviceTier = "Basic", #cheapest
[string]$sqlServerVersion = "12.0"
)
$erroractionpreference = "Continue"
$warningPreference = "Continue"
$logFile = "azure-rm-create-sql.log"
$script:credential = $null
$script:servername = $servername
$script:databasename = $databaseName
$script:createSqlServer = $false
$script:createSqlDatabase = $false
# ----------------------------------------------------------------------------------------------------------------
function main()
{
log-info "$([System.DateTime]::Now):starting"
if (!(Get-Module AzureRM -ListAvailable))
{
if ((read-host "powershell module azure rm sdk (azurerm) is required for this script. is it ok to install?[y|n]") -imatch "y")
{
Install-Module AzureRM
Import-Module AzureRM
}
else
{
return
}
}
# see if we need to auth
authenticate-azureRm
if ($listAvailable)
{
list-availableSqlServers
return
}
if ($location)
{
log-info "checking location $($location)"
if (!(Get-AzureRmLocation | Where-Object Location -Like $location) -or [string]::IsNullOrEmpty($location))
{
(Get-AzureRmLocation).Location
write-warning "location: $($location) not found. supply -location using one of the above locations and restart script."
exit 1
}
}
log-info "checking for existing resource group $($resourceGroupName)"
# create resource group if it does not exist
$resourceGroupInfo = Get-AzureRmResourceGroup -Name $resourceGroupName -ErrorAction SilentlyContinue
if (!$resourceGroupInfo)
{
if ($location)
{
log-info "creating resource group $($resourceGroupName) in location $($location)"
New-AzureRmResourceGroup -Name $resourceGroupName -Location $location
}
else
{
log-info "resource group does not exist and location not specified. exiting"
exit 1
}
}
else
{
log-info "resource group $($resourceGroupName) already exists."
if (!$location)
{
$location = $resourceGroupInfo.Location
}
}
# make sure sql available in region
$sqlAvailable = Get-AzureRmSqlCapability -LocationName $location
log-info "sql server capability in $($location) : $($sqlAvailable.Status)"
if (!$sqlAvailable)
{
log-info "sql not available in this region. exiting"
return
}
# verify edition
$editions = $sqlAvailable.SupportedServerVersions.supportedEditions.EditionName
if (!($editions -ieq $serviceTier))
{
log-info "please choose another service tier."
log-info "tiers available in this location:"
log-info $editions
return
}
# verify version
$versions = $sqlAvailable.SupportedServerVersions.ServerVersionName
if (!($versions -ieq $sqlServerVersion))
{
log-info "please choose another version."
log-info "versions available in this location:"
log-info $versions
return
}
$created = create-database
if (!$created -and $generateUniqueName)
{
# retry 1 time in case of server name issue or db exists on server specified and -generateUniqueName was passed
log-info "clearing server name and retrying 1 time"
$script:servername = ""
$created = create-database
}
if ($created)
{
if ($maskPassword -or !$script:createSqlServer)
{
$adminPassword = "{enter_sql_password_here}"
}
log-info "connection string ADO:`r`nServer=tcp:$($script:servername).database.windows.net,1433;Initial Catalog=$($script:databaseName);Persist Security Info=False;User ID=$($adminUserName);Password=$($adminPassword);MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;"
$odbcString = "connection string ODBC Native client:`r`nDRIVER=SQL Server Native Client 11.0;Server=tcp:$($script:servername).database.windows.net,1433;Database=$($script:databaseName);Uid=$($adminUserName)@$($script:servername);Pwd=$($adminPassword);Encrypt=yes;TrustServerCertificate=no;Connection Timeout=30;"
log-info $odbcString
return $odbcString
}
return $false
}
# ----------------------------------------------------------------------------------------------------------------
function authenticate-azureRm()
{
# make sure at least wmf 5.0 installed
if ($PSVersionTable.PSVersion -lt [version]"5.0.0.0")
{
log-info "update version of powershell to at least wmf 5.0. exiting..." -ForegroundColor Yellow
start-process "https://www.bing.com/search?q=download+windows+management+framework+5.0"
# start-process "https://www.microsoft.com/en-us/download/details.aspx?id=50395"
exit
}
# verify NuGet package
$nuget = get-packageprovider nuget -Force
if (-not $nuget -or ($nuget.Version -lt [version]::New("2.8.5.22")))
{
log-info "installing nuget package..."
install-packageprovider -name NuGet -minimumversion ([version]::New("2.8.5.201")) -force
}
$allModules = (get-module azure* -ListAvailable).Name
# install AzureRM module
if ($allModules -inotcontains "AzureRM")
{
# at least need profile, resources, sql
if ($allModules -inotcontains "AzureRM.profile")
{
log-info "installing AzureRm.profile powershell module..."
install-module AzureRM.profile -force
}
if ($allModules -inotcontains "AzureRM.resources")
{
log-info "installing AzureRm.resources powershell module..."
install-module AzureRM.resources -force
}
if ($allModules -inotcontains "AzureRM.compute")
{
log-info "installing AzureRm.compute powershell module..."
install-module AzureRM.sql -force
}
Import-Module azurerm.profile
Import-Module azurerm.resources
Import-Module azurerm.sql
}
else
{
Import-Module azurerm
}
# authenticate
try
{
Get-AzureRmResourceGroup | Out-Null
}
catch
{
try
{
Add-AzureRmAccount
}
catch
{
log-info "exception authenticating. exiting $($error | out-string)" -ForegroundColor Yellow
exit 1
}
}
#Save-AzureRmContext -Path $profileContext -Force
}
# ----------------------------------------------------------------------------------------------------------------
function check-credentials()
{
try
{
log-info "checking adminUserName account name $($adminUsername)"
if ($adminUsername.ToLower() -eq "admin" -or $adminUsername.ToLower() -eq "administrator")
{
log-info "adminUserName cannot be 'admin' or 'administrator'. exiting"
return
}
log-info "using admin name: $($adminUserName)"
log-info "checking password"
if (!$credentials)
{
if ([string]::IsNullOrEmpty($adminPassword))
{
$script:credential = Get-Credential
}
else
{
$SecurePassword = $adminPassword | ConvertTo-SecureString -AsPlainText -Force
$script:credential = new-object Management.Automation.PSCredential -ArgumentList $adminUsername, $SecurePassword
}
}
else
{
$script:credential = $credentials
}
$adminUsername = $script:credential.UserName
$adminPassword = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($script:credential.Password))
$count = 0
# uppercase check
if ($adminPassword -match "[A-Z]") { $count++ }
# lowercase check
if ($adminPassword -match "[a-z]") { $count++ }
# numeric check
if ($adminPassword -match "\d") { $count++ }
# specialKey check
if ($adminPassword -match "\W") { $count++ }
if ($adminPassword.Length -lt 8 -or $adminPassword.Length -gt 123 -or $count -lt 3)
{
Write-warning @"
azure password requirements at time of writing (3/2017):
The supplied password must be between 8-123 characters long and must satisfy at least 3 of password complexity requirements from the following:
1) Contains an uppercase character
2) Contains a lowercase character
3) Contains a numeric digit
4) Contains a special character.
correct password and restart script.
"@
exit 1
}
return $true
}
catch
{
log-info "exception: checking credentials $($error)"
$error.Clear()
return $false
}
}
# ----------------------------------------------------------------------------------------------------------------
function create-database()
{
$script:createSqlServer = $false
$script:createSqlDatabase = $false
$sqlServersAvailable = @(enum-sqlServers -sqlServer $script:servername -resourceGroup $resourceGroupName)
if (!$generateUniqueName -and $sqlServersAvailable.Count -gt 0 -and !$script:servername)
{
log-info $sqlServersAvailable
$script:servername = read-host "enter server name to use for new database"
}
elseif (!$script:servername -and $generateUniqueName)
{
$script:servername = "sql-server-$(Get-Random)"
log-info "server name not provided. using random name $($script:servername)"
}
if (!$script:serverName)
{
log-info "error: need server name or use -generateUniqueName. exiting"
return $false
}
if ($sqlServersAvailable.Count -lt 1 -or $sqlServersAvailable.ServerName -inotmatch $script:servername)
{
$script:createSqlServer = $true
}
if (!$script:databasename -and $generateUniqueName)
{
$script:databasename = "sql-database-$(Get-Random)"
log-info "database name not provided. using random name $($script:databasename)"
}
if (!$script:createSqlServer)
{
# for odbc string in case server wasnt created
$adminUserName = (enum-sqlServers -resourceGroup $resourceGroupName -sqlServer $script:servername).SqlAdministratorLogin
# if database name specified / generated and it exists, exit
$sqlDatabasesAvailable = @(enum-sqlDatabases -sqlServer $script:servername -resourceGroup $resourceGroupName)
if ($script:databasename -and $sqlDatabasesAvailable.DatabaseName -imatch $script:databasename)
{
log-info "error: database $($script:databaseName) already exists on server $($script:servername). exiting"
return $false
}
}
if ($script:databasename)
{
$script:createSqlDatabase = $true
}
# everything should be populated, if not exit
if (!$script:createSqlServer -and !$script:createSqlDatabase)
{
log-info "error:invalid configuration. see help. exiting"
return $false
}
log-info "using server name $($script:servername)"
log-info "creating sql server : $($script:createSqlServer) creating sql db : $($script:createSqlDatabase)"
$error.Clear()
try
{
if ($script:createSqlServer)
{
log-info "create a logical server"
if (!(check-credentials))
{
return $false
}
$ret = New-AzureRmSqlServer -ResourceGroupName $resourceGroupName `
-ServerName $script:servername `
-Location $location `
-SqlAdministratorCredentials $script:credential `
-ServerVersion $sqlServerVersion
if ($error)
{
log-info "error creating sql server. returning: $($error)"
$error.Clear()
return $false
}
log-info "create a logical server result:"
log-info $ret
log-info "configure a server firewall rule"
$ret = New-AzureRmSqlServerFirewallRule -ResourceGroupName $resourcegroupname `
-ServerName $script:servername `
-FirewallRuleName "AllowSome" -StartIpAddress $nsgStartIpAllow -EndIpAddress $nsgEndIpAllow
if ($error)
{
log-info "error creating sql server. returning: $($error)"
$error.Clear()
return $false
}
log-info "configure a logical server firewall result:"
log-info $ret
}
if ($script:createSqlDatabase)
{
log-info "creating empty database $($script:databasename)"
$ret = New-AzureRmSqlDatabase -ResourceGroupName $resourceGroupName `
-ServerName $script:servername `
-DatabaseName $script:databaseName `
-RequestedServiceObjectiveName $serviceTier
if ($error)
{
log-info "error creating sql database. returning: $($error)"
$error.Clear()
return $false
}
log-info "create database result:"
log-info $ret
}
return $true
}
catch
{
log-info "error:$($error)"
return $false
}
}
# ----------------------------------------------------------------------------------------------------------------
function enum-sqlDatabases($sqlServer, $resourceGroup)
{
if (!$sqlServer)
{
return $false
}
log-info "checking sql dbs on server $($sqlServer)"
$sqlDatabasesAvaliable = @()
if (!$script:databasename)
{
$sqlDatabasesAvaliable = @(Get-AzureRmSqlDatabase -ServerName $sqlServer -ResourceGroupName $resourceGroup -ErrorAction SilentlyContinue)
}
else
{
$sqlDatabasesAvaliable = @(Get-AzureRmSqlDatabase -ServerName $sqlServer -ResourceGroupName $resourceGroup -DatabaseName $script:databasename -ErrorAction SilentlyContinue)
}
return $sqlDatabasesAvaliable
}
# ----------------------------------------------------------------------------------------------------------------
function enum-sqlServers($resourceGroup, $sqlServer)
{
log-info "checking for sql servers in resource group $($resourceGroup)"
$serverInfo = @()
if (!$sqlServer)
{
$serverInfo = @(Get-AzureRmSqlServer -ResourceGroupName $resourceGroup -ErrorAction SilentlyContinue)
}
else
{
$serverInfo = @(Get-AzureRmSqlServer -ServerName $sqlServer -ResourceGroupName $resourceGroup -ErrorAction SilentlyContinue)
}
return $serverInfo
}
# ----------------------------------------------------------------------------------------------------------------
function list-availableSqlServers()
{
$sqlServersAvailable = new-object Collections.ArrayList
if ($resourceGroupName -eq "*")
{
$resourceGroups = @((Get-AzureRmResourceGroup).ResourceGroupName)
}
else
{
$resourceGroups = @($resourceGroupName)
}
foreach ($resourceGroup in $resourceGroups)
{
$serverInfo = @(enum-sqlServers -resourceGroup $resourceGroup)
foreach ($server in $serverInfo.ServerName)
{
log-info "--------------------------------"
log-info "--------------------------------"
log-info " SQL SERVER: $($server)"
log-info "--------------------------------"
log-info "--------------------------------"
$dbInfo = @(enum-sqlDatabases -sqlServer $server -resourceGroup $resourceGroup)
foreach ($db in $dbInfo)
{
log-info "vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv"
log-info $db
log-info "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"
}
$dbCount += $dbInfo.Count
}
$sqlCount += $serverInfo.Count
}
log-info "sql servers available: $($sqlCount) sql databases available: $($dbCount)"
}
# ----------------------------------------------------------------------------------------------------------------
function log-info($data)
{
$data = $($data | format-list * | out-string)
if ($data -imatch "error|warning|exception|fail|terminate")
{
Write-Warning $data
}
else
{
write-host $data
}
if (!$nolog)
{
out-file -Append -InputObject $data -FilePath $logFile
}
}
# ----------------------------------------------------------------------------------------------------------------
main
log-info "$([System.DateTime]::Now):finished"