Conclusion
This script controls the export scope via the DatabaseName parameter: if a value is provided, only the specified DB is exported; if it’s empty, all user databases under the entire Azure SQL Server are automatically exported.
Where is this useful?
- To test if a single DB export works correctly
- To back up all user databases on an entire Azure SQL Server at once
- To turn the backup process into a reusable PowerShell script
- To retain export results and error messages for each run, facilitating future investigation
Steps
1. Define Parameters and Login Information
First, prepare the Azure resource name, Storage location, and SQL Admin account, and add the DatabaseName parameter. This parameter is the core of this post: an empty value means all databases, while a value means a single specified database. Then, enter the SQL Admin password and retrieve the Storage Account Key, which will be used later for bacpac export.
# ===== 基本參數 =====$ResourceGroupName = "your-rg"$ServerName = "your-sql-server"$DatabaseName = "" # 空值 = 全部 DB;有值 = 指定單一 DB$StorageAccountName = "yourstorageacct"$ContainerName = "sql-backup"$SqlAdminLogin = "your_sql_admin"
# 會跳出輸入框,請輸入 Azure SQL admin 密碼$SqlAdminPassword = Read-Host "Enter Azure SQL admin password" -AsSecureString
# 取得 Storage Account Key$StorageKey = (Get-AzStorageAccountKey ` -ResourceGroupName $ResourceGroupName ` -Name $StorageAccountName)[0].Value2. Determine Which Databases to Process Based on DatabaseName
This step only processes the list, not the export itself. If DatabaseName is empty, it retrieves all databases under the logical server, excluding master; if a value is provided, it creates a list for only that single database. This allows the subsequent export process to be shared, avoiding the need to write two separate scripts.
if ([string]::IsNullOrWhiteSpace($DatabaseName)) { # 抓出此 logical server 底下的資料庫,排除 master $databases = Get-AzSqlDatabase ` -ResourceGroupName $ResourceGroupName ` -ServerName $ServerName | Where-Object { $_.DatabaseName -ne "master" }}else { # 若有指定 DB,則只建立單一清單 $databases = @( [PSCustomObject]@{ DatabaseName = $DatabaseName } )}3. Export bacpac to Blob Storage One by One
Each database is appended with a timestamp to prevent overwriting files with the same name. Then, New-AzSqlDatabaseExport is called to submit an export request, writing the bacpac to the specified Blob Container. This section is the core and most practical part.
# 建立結果紀錄$results = @()
foreach ($db in $databases) { $CurrentDatabaseName = $db.DatabaseName $TimeStamp = Get-Date -Format "yyyyMMdd-HHmmss" $BacpacFileName = "$CurrentDatabaseName-$TimeStamp.bacpac" $StorageUri = "<https://$StorageAccountName.blob.core.windows.net/$ContainerName/$BacpacFileName>"
Write-Host "Starting export for $CurrentDatabaseName ..."
try { $exportRequest = New-AzSqlDatabaseExport ` -ResourceGroupName $ResourceGroupName ` -ServerName $ServerName ` -DatabaseName $CurrentDatabaseName ` -StorageKeyType "StorageAccessKey" ` -StorageKey $StorageKey ` -StorageUri $StorageUri ` -AdministratorLogin $SqlAdminLogin ` -AdministratorLoginPassword $SqlAdminPassword4. Continuously Query Status and Organize Results
Export is not a synchronous operation, so Get-AzSqlDatabaseImportExportStatus is used to poll the status. Once completed, each DB’s status, output location, and error message are collected into a results table, which is then output uniformly. This prevents a situation where you only see one DB stuck without knowing the status of others.
# 輪詢匯出狀態 do { Start-Sleep -Seconds 15 $status = Get-AzSqlDatabaseImportExportStatus ` -OperationStatusLink $exportRequest.OperationStatusLink
Write-Host "$CurrentDatabaseName => $($status.Status)" } while ($status.Status -eq "InProgress")
$results += [PSCustomObject]@{ DatabaseName = $CurrentDatabaseName Status = $status.Status StorageUri = $StorageUri ErrorMessage = $status.ErrorMessage } } catch { $results += [PSCustomObject]@{ DatabaseName = $CurrentDatabaseName Status = "Failed" StorageUri = $StorageUri ErrorMessage = $_.Exception.Message } }}
$results | Format-Table -AutoSizeAdditional Notes
- It’s recommended to exclude
masterdirectly, as it usually has no practical value when backed up as a bacpac. - It’s recommended to use
$CurrentDatabaseNameand avoid overwriting the original$DatabaseNameparameter within theforeachloop to prevent confusion later. - If you encounter issues like unsupported schema, cross-database references, or external objects, export failures are often due to bacpac limitations, not PowerShell syntax errors.
Commands / Examples Summary
Click to expand
# 安裝 Az PowerShell 模組Install-Module -Name Az -Scope CurrentUser
# 匯入 Az 模組Import-Module Az
# 登入 AzureConnect-AzAccount
# ===== 基本參數 =====$ResourceGroupName = "your-rg"$ServerName = "your-sql-server"$DatabaseName = "" # 空值 = 全部 DB;有值 = 指定單一 DB$StorageAccountName = "yourstorageacct"$ContainerName = "sql-backup"$SqlAdminLogin = "your_sql_admin"
# 會跳出輸入框,請輸入 Azure SQL admin 密碼$SqlAdminPassword = Read-Host "Enter Azure SQL admin password" -AsSecureString
# 取得 Storage Account Key$StorageKey = (Get-AzStorageAccountKey ` -ResourceGroupName $ResourceGroupName ` -Name $StorageAccountName)[0].Value
# 抓取 DB 清單:空值 = 全部 DB;有值 = 單一 DBif ([string]::IsNullOrWhiteSpace($DatabaseName)) { $databases = Get-AzSqlDatabase ` -ResourceGroupName $ResourceGroupName ` -ServerName $ServerName | Where-Object { $_.DatabaseName -ne "master" }}else { $databases = @( [PSCustomObject]@{ DatabaseName = $DatabaseName } )}
# 建立結果紀錄$results = @()
foreach ($db in $databases) { $CurrentDatabaseName = $db.DatabaseName $TimeStamp = Get-Date -Format "yyyyMMdd-HHmmss" $BacpacFileName = "$CurrentDatabaseName-$TimeStamp.bacpac" $StorageUri = "<https://$StorageAccountName.blob.core.windows.net/$ContainerName/$BacpacFileName>"
Write-Host "Starting export for $CurrentDatabaseName ..."
try { $exportRequest = New-AzSqlDatabaseExport ` -ResourceGroupName $ResourceGroupName ` -ServerName $ServerName ` -DatabaseName $CurrentDatabaseName ` -StorageKeyType "StorageAccessKey" ` -StorageKey $StorageKey ` -StorageUri $StorageUri ` -AdministratorLogin $SqlAdminLogin ` -AdministratorLoginPassword $SqlAdminPassword
# 輪詢匯出狀態 do { Start-Sleep -Seconds 15 $status = Get-AzSqlDatabaseImportExportStatus ` -OperationStatusLink $exportRequest.OperationStatusLink
Write-Host "$CurrentDatabaseName => $($status.Status)" } while ($status.Status -eq "InProgress")
$results += [PSCustomObject]@{ DatabaseName = $CurrentDatabaseName Status = $status.Status StorageUri = $StorageUri ErrorMessage = $status.ErrorMessage } } catch { $results += [PSCustomObject]@{ DatabaseName = $CurrentDatabaseName Status = "Failed" StorageUri = $StorageUri ErrorMessage = $_.Exception.Message } }}
# 顯示結果$results | Format-Table -AutoSize
# ===== 補充查詢 / 驗證指令 =====
# 查詢某台 SQL Server 底下所有 DBGet-AzSqlDatabase ` -ResourceGroupName "your-rg" ` -ServerName "your-sql-server"
# 查詢指定 DBGet-AzSqlDatabase ` -ResourceGroupName "your-rg" ` -ServerName "your-sql-server" ` -DatabaseName "your-db-name"
# 查詢 Storage Account KeyGet-AzStorageAccountKey ` -ResourceGroupName "your-rg" ` -Name "yourstorageacct"
# 建立 Storage Context$ctx = New-AzStorageContext ` -StorageAccountName "yourstorageacct" ` -StorageAccountKey "your-storage-key"
# 查詢 Blob Container 是否存在Get-AzStorageContainer ` -Name "sql-backup" ` -Context $ctx
# 建立 Blob ContainerNew-AzStorageContainer ` -Name "sql-backup" ` -Context $ctx
# 手動測試單一 DB 匯出$DatabaseName = "appdb"
# 手動切換成匯出全部 DB$DatabaseName = ""
# 手動查某次匯出狀態Get-AzSqlDatabaseImportExportStatus ` -OperationStatusLink "貼上 OperationStatusLink"
# 查看匯出後的 bacpac 檔案Get-AzStorageBlob ` -Container "sql-backup" ` -Context $ctxConclusion
This version isn’t overly complex; it simply gathers all necessary commands in one place. This makes it easier for single-file testing, full-site backups, or scheduling, reducing the need for repeated fixes.