<# .SYNOPSIS Comprehensive SQL Server database user audit across multiple Azure SQL servers. .DESCRIPTION This script connects to multiple Azure SQL servers and databases to generate a comprehensive audit report of all database users, including their authentication types, creation dates, and permissions. It provides essential information for security auditing, compliance reporting, and user access management. Features: • Multi-server database user enumeration across Azure SQL instances • Authentication type detection (SQL, Windows, Azure AD) • User creation and modification date tracking • Comprehensive CSV reporting with timestamped output files • Azure AD authentication using access tokens • Automatic database discovery per server .PARAMETER ServerList Array of Azure SQL server FQDNs to audit. If not specified, uses a default list of common servers. Each server should be provided as a fully qualified domain name (e.g., 'servername.database.windows.net'). .EXAMPLE .\SQLUserCheck.ps1 Executes a complete user audit across all default Azure SQL servers and databases. .EXAMPLE .\SQLUserCheck.ps1 -ServerList @('signin-effectory.database.windows.net', 'c0m7f8nybr.database.windows.net') Audits users on specific Azure SQL servers instead of using the default server list. .EXAMPLE Connect-AzAccount .\SQLUserCheck.ps1 Ensures Azure authentication is established before running the SQL user audit. .INPUTS None. This script does not accept pipeline input. .OUTPUTS System.IO.FileInfo Generates a timestamped CSV file containing user audit results with the following columns: - ServerName: Azure SQL server name - DatabaseName: Database name within the server - UserName: Database user account name - CreateDate: User account creation timestamp - ModifyDate: Last modification timestamp - Type: User principal type (User, Role, etc.) - AuthenticationType: Authentication method (SQL_USER, WINDOWS_USER, EXTERNAL_USER) .NOTES Requires PowerShell 5.1 or later Requires SqlServer PowerShell module Requires Az.Accounts PowerShell module for Azure authentication Prerequisites: - Must be connected to Azure (Connect-AzAccount) - Requires appropriate SQL Server permissions on target databases - Network connectivity to Azure SQL servers Security Considerations: - Uses Azure AD authentication with access tokens - Does not store or transmit SQL credentials - Audit trail is maintained in timestamped CSV files .LINK https://docs.microsoft.com/en-us/sql/relational-databases/system-catalog-views/sys-database-principals-transact-sql https://docs.microsoft.com/en-us/azure/azure-sql/database/authentication-aad-overview .COMPONENT SqlServer PowerShell Module, Azure PowerShell Module .ROLE Database Administration, Security Auditing, Compliance Reporting .FUNCTIONALITY Azure SQL Server user auditing, database security assessment, access management reporting #> param( [Parameter(Mandatory = $false, HelpMessage = "Array of Azure SQL server FQDNs to audit")] [string[]]$ServerList = @( 'c0m7f8nybr.database.windows.net', 'calculations.database.windows.net', 'effectory.database.windows.net', 'effectorycore.database.windows.net', 'logit-backup.database.windows.net', 'mhpfktialk.database.windows.net', 'participants.database.windows.net', 'signin-effectory.database.windows.net', 'sqlserver01prod.6a1f4aa9f43a.database.windows.net' ) ) Import-Module SqlServer # Ensure Azure authentication is available # Uncomment the following lines if not already authenticated to Azure #Clear-AzContext #Connect-AzAccount Write-Host "======================================================================================================================================================================" Write-Host "Creating comprehensive SQL user audit across Azure SQL servers." Write-Host "====================================================================================================================================================================" # Generate timestamped output filename [string] $date = Get-Date -Format "yyyy-MM-dd HHmm" $filename = ".\$date SQL User check.csv" Write-Host "📄 Output will be saved to: $filename" -ForegroundColor Cyan <# .SYNOPSIS Data structure for SQL Server database user information. .DESCRIPTION This class represents a database user record containing essential information for security auditing and access management. Each instance captures user details from a specific database on a specific server. .NOTES Properties align with sys.database_principals system catalog view columns for consistent data representation across different SQL Server versions. #> class UserItem { [string] $ServerName = "" # Azure SQL server name [string] $DatabaseName = "" # Database name where user exists [string] $UserName = "" # Database user principal name [string] $CreateDate = "" # User creation timestamp [string] $ModifyDate = "" # Last modification timestamp [string] $Type = "" # Principal type (SQL_USER, WINDOWS_USER, etc.) [string] $AuthenticationType = "" # Authentication method (SQL, Windows, Azure AD) } Write-Host "🎯 Configured to audit $($ServerList.Count) Azure SQL servers" -ForegroundColor Green # SQL query to discover all databases on each server # Excludes system databases that are not relevant for user auditing $databaseListQuery = @' SELECT name, database_id, create_date FROM sys.databases WHERE name NOT IN ('master', 'tempdb', 'model', 'msdb') ORDER BY name; '@ # SQL query to retrieve database user information # Filters out roles ('R' type) and focuses on actual user principals # Excludes 'guest' user which exists by default in all databases $userListQuery = @' SELECT @@SERVERNAME as serverName, DB_NAME() as databaseName, name as username, create_date, modify_date, type_desc as type, authentication_type_desc as authentication_type FROM sys.database_principals WHERE type NOT IN ('R') -- Exclude database roles AND sid IS NOT NULL -- Exclude built-in principals without SIDs AND name NOT IN ('dbo', 'guest') -- Exclude default dbo and guest user AND name NOT LIKE '##%' -- Exclude system-generated users ORDER BY name; '@ # Initialize audit counters $totalServersProcessed = 0 $totalDatabasesProcessed = 0 $totalUsersFound = 0 # Main server processing loop foreach ($server in $ServerList) { $totalServersProcessed++ Write-Host "----------------------------------------------------------------------------------------------------------------------------------------------------------------------" Write-Host "🖥️ Processing Server [$server] ($totalServersProcessed of $($ServerList.Count))" -ForegroundColor Cyan Write-Host "----------------------------------------------------------------------------------------------------------------------------------------------------------------------" try { # Get Azure AD access token for SQL Database authentication # This provides secure, passwordless authentication to Azure SQL Write-Host "🔐 Obtaining Azure AD access token for SQL authentication..." -ForegroundColor Yellow $access_token_secure = (Get-AzAccessToken -ResourceUrl https://database.windows.net).Token $access_token = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($access_token_secure)) # Configure secure connection string for Azure SQL $connectionString = "Data Source=$server;Initial Catalog=master;Persist Security Info=False;Encrypt=True;TrustServerCertificate=False;Application Name=CloudEngineering-UserAudit" # Discover all user databases on the current server Write-Host "📋 Discovering databases on server..." -ForegroundColor Gray $databases = Invoke-Sqlcmd -Query $databaseListQuery -ConnectionString $connectionString -AccessToken $access_token -ErrorAction Stop Write-Host "✅ Found $($databases.Count) user databases to audit" -ForegroundColor Green # Process each database on the current server foreach ($database in $databases) { $totalDatabasesProcessed++ Write-Host " 📊 Auditing Database [$($database.name)]" -ForegroundColor White try { [UserItem[]]$Result = @() # Configure database-specific connection string $databaseName = $database.name $databaseConnectionString = "Data Source=$server;Initial Catalog=$databaseName;Persist Security Info=False;Encrypt=True;TrustServerCertificate=False;Application Name=CloudEngineering-UserAudit" # Query database users and their authentication details $users = Invoke-Sqlcmd -Query $userListQuery -ConnectionString $databaseConnectionString -AccessToken $access_token -ErrorAction Stop # Process each user found in the database foreach ($user in $users) { $totalUsersFound++ [UserItem] $userItem = [UserItem]::new() $userItem.ServerName = $server $userItem.DatabaseName = $database.name $userItem.UserName = $user.username $userItem.CreateDate = $user.create_date $userItem.ModifyDate = $user.modify_date $userItem.Type = $user.type $userItem.AuthenticationType = $user.authentication_type $Result += $userItem } # Export results for this database to CSV if ($Result.Count -gt 0) { $Result | Export-Csv -Path $fileName -Append -NoTypeInformation Write-Host " ✅ Found $($Result.Count) users in database [$($database.name)]" -ForegroundColor Green } else { Write-Host " ℹ️ No users found in database [$($database.name)]" -ForegroundColor Gray } } catch { Write-Warning " ❌ Failed to audit database [$($database.name)]: $($_.Exception.Message)" } } } catch { Write-Warning "❌ Failed to process server [$server]: $($_.Exception.Message)" Write-Warning " This may be due to connectivity issues or insufficient permissions" } } # Generate comprehensive audit summary Write-Host "======================================================================================================================================================================" Write-Host "📊 SQL User Audit Summary" -ForegroundColor Green Write-Host "======================================================================================================================================================================" Write-Host "" Write-Host "Audit Results:" -ForegroundColor Cyan Write-Host "==============" Write-Host "• Servers Processed: $totalServersProcessed of $($ServerList.Count)" -ForegroundColor White Write-Host "• Databases Audited: $totalDatabasesProcessed" -ForegroundColor White Write-Host "• Total Users Found: $totalUsersFound" -ForegroundColor White # Analyze authentication types if CSV file exists if (Test-Path $fileName) { try { $csvData = Import-Csv $fileName $sqlAuthUsers = ($csvData | Where-Object { $_.type -eq "SQL_USER" }).Count $azureADUsers = ($csvData | Where-Object { $_.type -eq "EXTERNAL_USER" }).Count $windowsUsers = ($csvData | Where-Object { $_.type -eq "WINDOWS_USER" }).Count $otherUsers = $csvData.Count - $sqlAuthUsers - $azureADUsers - $windowsUsers Write-Host "" Write-Host "Authentication Type Breakdown:" -ForegroundColor Yellow Write-Host "==============================" Write-Host "• SQL Authentication Users: $sqlAuthUsers" -ForegroundColor $(if($sqlAuthUsers -gt 0) {'Red'} else {'Green'}) Write-Host "• Azure AD Users: $azureADUsers" -ForegroundColor Green Write-Host "• Windows Authentication Users: $windowsUsers" -ForegroundColor White if ($otherUsers -gt 0) { Write-Host "• Other Authentication Types: $otherUsers" -ForegroundColor Gray } # Security recommendation if SQL users found if ($sqlAuthUsers -gt 0) { Write-Host "" Write-Host "⚠️ Security Notice:" -ForegroundColor Red Write-Host " $sqlAuthUsers SQL Authentication users detected." -ForegroundColor Yellow Write-Host " Consider migrating to Azure AD authentication for enhanced security." -ForegroundColor Yellow } } catch { Write-Warning "Could not analyze authentication types: $($_.Exception.Message)" } } Write-Host "" if (Test-Path $fileName) { $fileSize = (Get-Item $fileName).Length Write-Host "📄 Results Export:" -ForegroundColor Cyan Write-Host "==================" Write-Host "• Output File: $fileName" -ForegroundColor White Write-Host "• File Size: $([math]::Round($fileSize/1KB, 2)) KB" -ForegroundColor White Write-Host "" # Display sample of results if file exists and has content try { $sampleData = Import-Csv $fileName | Select-Object -First 5 if ($sampleData) { Write-Host "📋 Sample Results (First 5 Users):" -ForegroundColor Cyan Write-Host "====================================" $sampleData | Format-Table -AutoSize } } catch { Write-Warning "Could not display sample results: $($_.Exception.Message)" } } Write-Host "✅ SQL User audit completed successfully!" -ForegroundColor Green Write-Host "" Write-Host "📝 Next Steps:" -ForegroundColor Yellow Write-Host "==============" Write-Host "• Review the CSV file for user access patterns" -ForegroundColor White Write-Host "• Identify users with inappropriate authentication types" -ForegroundColor White Write-Host "• Validate user access against business requirements" -ForegroundColor White Write-Host "• Consider implementing Azure AD authentication where applicable" -ForegroundColor White Write-Host "======================================================================================================================================================================" Write-Host "🏁 Audit process completed." -ForegroundColor Green