mirror of
https://dev.azure.com/effectory/Survey%20Software/_git/Cloud%20Engineering
synced 2026-02-27 18:52:18 +01:00
added documetation
This commit is contained in:
@@ -1,90 +1,318 @@
|
||||
<#
|
||||
.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 SQL user list."
|
||||
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 = ""
|
||||
[string] $DatabaseName = ""
|
||||
[string] $UserName = ""
|
||||
[string] $CreateDate = ""
|
||||
[string] $ModifyDate = ""
|
||||
[string] $Type = ""
|
||||
[string] $AuthenticationType = ""
|
||||
[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)
|
||||
}
|
||||
|
||||
$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')
|
||||
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
|
||||
order by name;
|
||||
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,
|
||||
SELECT @@SERVERNAME as serverName,
|
||||
DB_NAME() as databaseName,
|
||||
name as username,
|
||||
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')
|
||||
and sid is not null
|
||||
and name != 'guest'
|
||||
order by name;
|
||||
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;
|
||||
'@
|
||||
|
||||
foreach ($server in $serverlist) {
|
||||
# Initialize audit counters
|
||||
$totalServersProcessed = 0
|
||||
$totalDatabasesProcessed = 0
|
||||
$totalUsersFound = 0
|
||||
|
||||
# Main server processing loop
|
||||
foreach ($server in $ServerList) {
|
||||
$totalServersProcessed++
|
||||
|
||||
Write-Host "----------------------------------------------------------------------------------------------------------------------------------------------------------------------"
|
||||
Write-Host "Server [$server)]"
|
||||
Write-Host "🖥️ Processing Server [$server] ($totalServersProcessed of $($ServerList.Count))" -ForegroundColor Cyan
|
||||
Write-Host "----------------------------------------------------------------------------------------------------------------------------------------------------------------------"
|
||||
|
||||
$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))
|
||||
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))
|
||||
|
||||
$connectionString = "Data Source=$server;Initial Catalog=master;Persist Security Info=False;Encrypt=True;TrustServerCertificate=False;Application Name=CloudEngineering";
|
||||
# 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"
|
||||
|
||||
$databases = Invoke-Sqlcmd -Query $databaseListQuery -ConnectionString $connectionString -AccessToken $access_token
|
||||
# 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
|
||||
|
||||
foreach ($database in $databases) {
|
||||
# Process each database on the current server
|
||||
foreach ($database in $databases) {
|
||||
$totalDatabasesProcessed++
|
||||
|
||||
Write-Host " 📊 Auditing Database [$($database.name)]" -ForegroundColor White
|
||||
|
||||
Write-Host "Database [$($database.name)]"
|
||||
try {
|
||||
[UserItem[]]$Result = @()
|
||||
|
||||
[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"
|
||||
|
||||
$databaseName = $database.name
|
||||
$databaseConnectionString = "Data Source=$server;Initial Catalog=$databaseName;Persist Security Info=False;Encrypt=True;TrustServerCertificate=False;Application Name=CloudEngineering";
|
||||
# Query database users and their authentication details
|
||||
$users = Invoke-Sqlcmd -Query $userListQuery -ConnectionString $databaseConnectionString -AccessToken $access_token -ErrorAction Stop
|
||||
|
||||
$users = Invoke-Sqlcmd -Query $userListQuery -ConnectionString $databaseConnectionString -AccessToken $access_token
|
||||
# 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
|
||||
}
|
||||
|
||||
foreach ($user in $users) {
|
||||
[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)"
|
||||
}
|
||||
}
|
||||
|
||||
$Result | Export-Csv -Path $fileName -Append -NoTypeInformation
|
||||
}
|
||||
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 "Done."
|
||||
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
|
||||
Reference in New Issue
Block a user