Blog Post

Query multiple Azure SQL Databases

,

Photo by Dan Dennis on Unsplash

I was sick of changing context in Azure Data Studio while cleaning up things discovered in our vulnerability assessments.

Changing things via PowerShell

I wanted to be cautious with drops across multiple subscriptions, so I started with a single subscription. I use Ghostty for my command line stuff. Get into a PowerShell prompt, and execute Connect-AzAccount.

This prompts me to log in with a browser, and then in Ghostty, I can choose which subscription I want to run this script in. I had some leftover users we don’t need anymore, so that’s what I’m dropping here.

# SQL to run on each DB
$sqlCommand = "DROP USER [DBDiagramUser];"

# Get access token for Azure SQL
function Get-AzSqlAccessToken {
    $tokenResponse = Get-AzAccessToken -ResourceUrl "https://database.windows.net/"
    return $tokenResponse.Token
}

# Loop through all servers and DBs
$servers = Get-AzSqlServer
foreach ($server in $servers) {
    $databases = Get-AzSqlDatabase -ServerName $server.ServerName -ResourceGroupName $server.ResourceGroupName

    foreach ($db in $databases) {
        # Skip system DBs
        if ($db.DatabaseName -in @("master", "tempdb", "model", "msdb")) {
            continue
        }

        $accessToken = Get-AzSqlAccessToken

        Write-Host "Running script on $($server.ServerName).database.windows.net / $($db.DatabaseName)"

        try {
            # Create SQL connection with token
            $connection = New-Object System.Data.SqlClient.SqlConnection
            $connection.ConnectionString = "Server=$($server.ServerName).database.windows.net;Database=$($db.DatabaseName);Encrypt=True;TrustServerCertificate=False;"
            $connection.AccessToken = $accessToken
            $connection.Open()

            $command = $connection.CreateCommand()
            $command.CommandText = $sqlCommand
            $command.ExecuteNonQuery()

            $connection.Close()
        }
        catch {
            Write-Warning "Failed on $($db.DatabaseName): $_"
        }
    }
}

Maybe I get more comfortable dropping across subscriptions, or I get bored of manually changing to different subscriptions, and I use this instead. I like to be very cautious about changing things wholesale, though.

# Get all subscriptions for the logged-in account
$subscriptions = Get-AzSubscription

# Define the SQL script to run (e.g., DROP USER)
$sqlScript = "DROP USER [DBDiagramUser];"

# Loop through each subscription
foreach ($subscription in $subscriptions) {
    # Set the context to the current subscription
    Set-AzContext -SubscriptionId $subscription.Id

    Write-Host "Processing Subscription: $($subscription.Name) ($($subscription.Id))"

    # Get all SQL Servers in the current subscription
    $servers = Get-AzSqlServer

    # Loop through each server and list databases
    foreach ($server in $servers) {
        Write-Host "  Server: $($server.ServerName)"

        # Get all databases on the current server
        $databases = Get-AzSqlDatabase -ServerName $server.ServerName -ResourceGroupName $server.ResourceGroupName

        foreach ($db in $databases) {
            # Skip system databases
            if ($db.DatabaseName -in @("master", "tempdb", "model", "msdb")) {
                continue
            }

            Write-Host "Running script on Database: $($db.DatabaseName)"

            try {
                # Get access token for Azure SQL
                $accessToken = (Get-AzAccessToken -ResourceUrl "https://database.windows.net/").Token

                # Create SQL connection with token
                $connection = New-Object System.Data.SqlClient.SqlConnection
                $connection.ConnectionString = "Server=$($server.ServerName).database.windows.net;Database=$($db.DatabaseName);Encrypt=True;TrustServerCertificate=False;"
                $connection.AccessToken = $accessToken
                $connection.Open()

                # Create and execute the SQL command
                $command = $connection.CreateCommand()
                $command.CommandText = $sqlScript
                $command.ExecuteNonQuery()

                # Close the connection
                $connection.Close()

                Write-Host "Successfully executed script on $($db.DatabaseName)"
            }
            catch {
                Write-Warning "Failed to execute script on $($db.DatabaseName): $_"
            }
        }
    }
}

Querying things via PowerShell

If you want to run SELECTs across multiple subscriptions and return the results, you have to change that PowerShell a bit so it will output the results for you. I feel much better about multiple subscriptions when I’m returning some results, but still be careful; you aren’t running crazy-intensive SELECTs.

# Get all subscriptions
$subscriptions = Get-AzSubscription

# Define query for top 5 largest tables by row count
$sqlScript = @"
SELECT TOP 5 
    s.name AS SchemaName,
    t.name AS TableName,
    SUM(p.rows) AS TotalRows
FROM sys.tables t
JOIN sys.schemas s ON t.schema_id = s.schema_id
JOIN sys.indexes i ON t.object_id = i.object_id
JOIN sys.partitions p ON i.object_id = p.object_id AND i.index_id = p.index_id
WHERE i.index_id <= 1
GROUP BY s.name, t.name
ORDER BY TotalRows DESC;
"@

# Initialize result collection
$results = @()

# Loop through all subscriptions
foreach ($subscription in $subscriptions) {
    Set-AzContext -SubscriptionId $subscription.Id

    $servers = Get-AzSqlServer

    foreach ($server in $servers) {
        $databases = Get-AzSqlDatabase -ServerName $server.ServerName -ResourceGroupName $server.ResourceGroupName

        foreach ($db in $databases) {
            if ($db.DatabaseName -in @("master", "tempdb", "model", "msdb")) { continue }

            try {
                $accessToken = (Get-AzAccessToken -ResourceUrl "https://database.windows.net/").Token

                $connection = New-Object System.Data.SqlClient.SqlConnection
                $connection.ConnectionString = "Server=$($server.ServerName).database.windows.net;Database=$($db.DatabaseName);Encrypt=True;TrustServerCertificate=False;"
                $connection.AccessToken = $accessToken
                $connection.Open()

                $command = $connection.CreateCommand()
                $command.CommandText = $sqlScript
                $reader = $command.ExecuteReader()

                while ($reader.Read()) {
                    $results += [PSCustomObject]@{
                        Subscription = $subscription.Name
                        Server       = $server.ServerName
                        Database     = $db.DatabaseName
                        Schema       = $reader["SchemaName"]
                        Table        = $reader["TableName"]
                        TotalRows    = $reader["TotalRows"]
                    }
                }

                $reader.Close()
                $connection.Close()
            }
            catch {
                Write-Warning "Failed to query $($db.DatabaseName) on $($server.ServerName): $_"
            }
        }
    }
}

# Output as table
$results | Sort-Object Subscription, Server, Database, TotalRows -Descending | Format-Table -AutoSize

The post Query multiple Azure SQL Databases appeared first on sqlkitty.

Original post (opens in new tab)
View comments in original post (opens in new tab)

Read 8,446 times
(19 in last 30 days)

Rate

5 (1)

You rated this post out of 5. Change rating

Share