Test Database Connectivity - powershell

Is there an easy way to test the connectivity to a MS SQL Server instance from a client (without loading any SQL assemblies) with PowerShell?
MS Sql: Servername\Instance Port 1433
How can I test the connectivity to the server with PowerShell from a normal client?

Use the SqlConnection class to test a connection. You don't have to load any SQL assemblies.
Helper function:
function Test-SQLConnection
{
[OutputType([bool])]
Param
(
[Parameter(Mandatory=$true,
ValueFromPipelineByPropertyName=$true,
Position=0)]
$ConnectionString
)
try
{
$sqlConnection = New-Object System.Data.SqlClient.SqlConnection $ConnectionString;
$sqlConnection.Open();
$sqlConnection.Close();
return $true;
}
catch
{
return $false;
}
}
Usage example:
Test-SQLConnection "Data Source=localhost;database=myDB;User ID=myUser;Password=myPassword;"

This is basically the same as Martin's answer, only the connection string is build from the parameters, and the time taken to connect is measured.
e.g:
Test-SQLDatabase -Server SQLServer -Database SomeDB -Username SQLUser -Password password
or
Test-SQLDatabase -Server Server1\SQLExpress -Database SomeDB -UseWindowsAuthentication
.
function Test-SQLDatabase
{
param(
[Parameter(Position=0, Mandatory=$True, ValueFromPipeline=$True)] [string] $Server,
[Parameter(Position=1, Mandatory=$True)] [string] $Database,
[Parameter(Position=2, Mandatory=$True, ParameterSetName="SQLAuth")] [string] $Username,
[Parameter(Position=3, Mandatory=$True, ParameterSetName="SQLAuth")] [string] $Password,
[Parameter(Position=2, Mandatory=$True, ParameterSetName="WindowsAuth")] [switch] $UseWindowsAuthentication
)
# connect to the database, then immediatly close the connection. If an exception occurrs it indicates the conneciton was not successful.
process {
$dbConnection = New-Object System.Data.SqlClient.SqlConnection
if (!$UseWindowsAuthentication) {
$dbConnection.ConnectionString = "Data Source=$Server; uid=$Username; pwd=$Password; Database=$Database;Integrated Security=False"
$authentication = "SQL ($Username)"
}
else {
$dbConnection.ConnectionString = "Data Source=$Server; Database=$Database;Integrated Security=True;"
$authentication = "Windows ($env:USERNAME)"
}
try {
$connectionTime = measure-command {$dbConnection.Open()}
$Result = #{
Connection = "Successful"
ElapsedTime = $connectionTime.TotalSeconds
Server = $Server
Database = $Database
User = $authentication}
}
# exceptions will be raised if the database connection failed.
catch {
$Result = #{
Connection = "Failed"
ElapsedTime = $connectionTime.TotalSeconds
Server = $Server
Database = $Database
User = $authentication}
}
Finally{
# close the database connection
$dbConnection.Close()
#return the results as an object
$outputObject = New-Object -Property $Result -TypeName psobject
write-output $outputObject
}
}
}

That depends on what you actually want to test. If you just want to verify that you can connect to the port on the remote host something like this will do:
$server = 'servername'
$port = 1433
$tcp = New-Object Net.Sockets.TcpClient
if ([void]$tcp.Connect($server, $port)) {
'connected'
} else {
'not connected'
}
$tcp.Dispose()
If you want to verify that a connection to an SQL Server instance can be established you'll need something like this:
$dbhost = 'servername'
$dbinst = 'instance'
$dbname = 'master'
$username = ...
$password = ...
$cs = "Server=$dbhost\$dbinst;Database=$dbname;User Id=$username;" +
"Password=$password;"
$cn = New-Object -COM 'ADODB.Connection'
$cn.ConnectionString = $cs
try {
$cn.Open()
if ($cn.State -eq 1) {
'connected'
$cn.Close()
} else {
'not connected'
}
} catch {
'not connected'
}

I've used the ConnectionState enum to check the database connection state.
Documentation on this enum can be found here
It can be accessed with the following: [System.Data.ConnectionState]::Open
Other options are Broken, Closed, Connecting, Executing, and Fetching.
Example:
class Database
{
[System.Data.SqlClient.SqlConnection]$Connection
[void]Connect
{
$sqlConnection = New-Object System.Data.SqlClient.SqlConnection
$sqlConnection.ConnectionString = $connectionString
$sqlConnection.Open()
$this.Connection = $sqlConnection
}
[bool]IsConnected
{
return $this.Connection.State -eq [System.Data.ConnectionState]::Open
}
}

Related

Powershell ODBC Postgresql ExecuteReader() Times Out

The below is the error I get from my powershell connection below. The ODBc DSN connection is setup and successfully connects to the postgres database, any help appreciated?
Code
Param (
[string] $sourceDsn,
[string] $sourceDsnType,
[string] $sourceServer,
[string] $sourcePort,
[string] $sourceDatabase,
[string] $sourceTable,
[string] $sourceUser,
[string] $sourcePass
)
Function CreateOdbcConnection([string] $dsn, [string] $dsnType, [string] $serverName, [string] $port, [string] $dbName) {
Try {
$dsnCheck = Get-OdbcDsn -Name $dsn
If ($dsnCheck -eq $null) {
Add-OdbcDsn -Name $dsn -DriverName "PostgreSQL Unicode(x64)" -DsnType $dsnType -SetPropertyValue #("Server=$serverName", "Trusted_Connection=Yes", "Database=$dbName") -Platform "64-bit" -PassThru
Write-Host "Please update port in configuration of ODBC connection if using a non-standard port."
Exit
}
} Catch [System.Exception] {
Write-Host $_.Exception
}
}
Function PostgresConnectionString([string] $dsn, [string] $username, [string] $password) {
"dsn=$dsn;uid=$username;pwd=$password"
}
CreateOdbcConnection $sourceDsn $sourceDsnType $sourceServer $sourcePort $sourceDatabase
$sourceConn = New-Object System.Data.Odbc.OdbcConnection
$sourceConn.ConnectionString = PostgresConnectionString $sourceDsn $sourceUser $sourcePass
$sqlCmdText = "SELECT * FROM " + $sourceTable + " LIMIT 1"
$sqlCmd = New-Object System.Data.Odbc.OdbcCommand($sqlCmdText, $sourceConn)
$sourceConn.Open()
$sqlReader = $sqlCmd.ExecuteReader()
$sourceConn.Close()
$sourceConn.Dispose()
Error
System.Data.Odbc.OdbcException (0x80131937): ERROR [57014] ERROR: canceling statement due to statement timeout;
Error while executing the query
at System.Data.Odbc.OdbcConnection.HandleError(OdbcHandle hrHandle, RetCode retcode)
at System.Data.Odbc.OdbcCommand.ExecuteReaderObject(CommandBehavior behavior, String method, Boolean needReader, Object[] methodArguments, SQL_API odbcApiMethod)
at System.Data.Odbc.OdbcCommand.ExecuteReaderObject(CommandBehavior behavior, String method, Boolean needReader)
at System.Data.Odbc.OdbcCommand.ExecuteNonQuery()
at CallSite.Target(Closure , CallSite , Object )

Validate credentials for remote domain

Can anyone advise on how to validate credentials on a remote domain?
My environment has multiple domains that do not have trust relationships defined between them.
I have a Powershell script that needs to access a shared folder residing on a server in another domain which obviously requires authentication. Prior to accessing it, I need to validate credentials to avoid lock-outs (The script can be ran against multiple servers).
In the past I've used this wonderful script which used current domain for validation but I cannot get it to work against a remote domain.
I tried this is (slightly modified script from link above):
function Test-Cred {
[CmdletBinding()]
[OutputType([String])]
Param (
[Parameter(
Mandatory = $false,
ValueFromPipeLine = $true,
ValueFromPipelineByPropertyName = $true
)]
[Alias(
'PSCredential'
)]
[ValidateNotNull()]
[System.Management.Automation.PSCredential]
[System.Management.Automation.Credential()]
$Credentials
)
$Domain = $null
$Root = $null
$Username = $null
$Password = $null
If($Credentials -eq $null)
{
Try
{
$Credentials = Get-Credential "domain\$env:username" -ErrorAction Stop
}
Catch
{
$ErrorMsg = $_.Exception.Message
Write-Warning "Failed to validate credentials: $ErrorMsg "
Pause
Break
}
}
# Checking module
Try
{
# Split username and password
$Username = $credentials.username
$Password = $credentials.GetNetworkCredential().password
# Get Domain
###$Root = "LDAP://" + ([ADSI]'').distinguishedName
$Root = "LDAP://DC=remote_domain,DC=com" ### statically define the remote domain
$Domain = New-Object System.DirectoryServices.DirectoryEntry($Root,$UserName,$Password)
}
Catch
{
$_.Exception.Message
Continue
}
If(!$domain)
{
Write-Warning "Something went wrong"
}
Else
{
If ($domain.name -ne $null)
{
return "Authenticated"
}
Else
{
$Domain ### diagnosing the error
return "Not authenticated"
}
}
}
I get the following error:
format-default : The following exception occurred while retrieving member "distinguishedName": "The user name or
password is incorrect.
"
+ CategoryInfo : NotSpecified: (:) [format-default], ExtendedTypeSystemException
+ FullyQualifiedErrorId : CatchFromBaseGetMember,Microsoft.PowerShell.Commands.FormatDefaultCommand
The username/password is 100% correct.
Thank you
EDIT 1
I have found the following blog post that goes over how to work with Active Directory using .Net assemblies. The following has worked quite well
Add-Type -AssemblyName System.DirectoryServices.AccountManagement
#store credentials (of account with appropriate permissions)
$creds = Get-Credential
#set the domain name
$dn = 'contoso.com'
#Create the principal context object (so to say connect to a domain with the credentials provided)
$pc = [System.DirectoryServices.AccountManagement.PrincipalContext]::new([System.DirectoryServices.AccountManagement.ContextType]::`
Domain,$dn,$($creds.UserName),$($creds.GetNetworkCredential().Password))
I assume I could use this in an If statement to achieve what I need. Admittedly, I do not know the way of the .Net and it is a bit scary but I will have to learn it.
EDIT 2
Here is what I pieced together:
Function Test-Cred
{
[CmdletBinding()]
[OutputType([String])]
Param (
[Parameter(
Mandatory = $false,
ValueFromPipeLine = $true,
ValueFromPipelineByPropertyName = $true
)]
[Alias(
'PSCredential'
)]
[ValidateNotNull()]
[System.Management.Automation.PSCredential]
[System.Management.Automation.Credential()]
$Credentials
)
Add-Type -AssemblyName System.DirectoryServices.AccountManagement
# Checking module
$Validated = [System.DirectoryServices.AccountManagement.PrincipalContext]::new([System.DirectoryServices.AccountManagement.ContextType]::Domain,'remote_domain',$($Credentials.UserName),$($Credentials.GetNetworkCredential().Password))
If ($Validated.ConnectedServer)
{
Return "Authenticated"
}
Else
{
Return "Not authenticated"
}
}
Any feedback?
EDIT 3
Well, EDIT 2 does not work for Powershell 4, grrr
Method invocation failed because [System.DirectoryServices.AccountManagement.PrincipalContext] dies not contain method named 'new'
I had to make it work like this:
$ContextType = [System.DirectoryServices.AccountManagement.ContextType]::Domain
$ContextName = 'target_domain.com'
$Validated = New-Object -TypeName System.DirectoryServices.AccountManagement.PrincipalContext -ArgumentList $ContextType, $ContextName, $($Credentials.UserName),$($Credentials.GetNetworkCredential().Password)
Here is my final version of this test function that works with Powershell version older than 5.1.
Function Test-Cred
{
[CmdletBinding()]
[OutputType([String])]
Param (
[Parameter(
Mandatory = $false,
ValueFromPipeLine = $true,
ValueFromPipelineByPropertyName = $true
)]
[Alias(
'PSCredential'
)]
[ValidateNotNull()]
[System.Management.Automation.PSCredential]
[System.Management.Automation.Credential()]
$Credentials
)
# Checking module
Add-Type -AssemblyName System.DirectoryServices.AccountManagement
$ContextType = [System.DirectoryServices.AccountManagement.ContextType]::Domain
$ContextName = 'remote_domain.com'
$Validated = New-Object -TypeName System.DirectoryServices.AccountManagement.PrincipalContext -ArgumentList $ContextType, $ContextName, $($Credentials.UserName),$($Credentials.GetNetworkCredential().Password)
If ($Validated.ConnectedServer)
{
Return "Authenticated"
}
Else
{
Return "Not authenticated"
}
}

Access datarow from a workflow

I have a script which look like below:
function Invoke-SQL {
param(
[string] $dataSource = ".\MSSQLSERVER",
[string] $database = "master",
[string] $sqlCommand = $(throw "Please specify a query.")
)
$connectionString = "Data Source=$dataSource; Integrated Security=True; Initial Catalog=$database; Connect Timeout=100"
$connection = new-object system.data.SqlClient.SQLConnection($connectionString)
$command = new-object system.data.sqlclient.sqlcommand($sqlCommand,$connection)
Try {
$connection.Open()
} catch {
return, $null
}
$adapter = New-Object System.Data.sqlclient.sqlDataAdapter $command
$dataset = New-Object System.Data.DataSet
$adapter.Fill($dataSet) | Out-Null
$connection.Close()
$dataSet.Tables
}
[string]$SQL = "select CmsSrvName from CmsServerList_VM"
$DbInstances = Invoke-SQL "DBSRV" "Test" $SQL
workflow wf1{
Param([System.Data.DataTable]$instance)
ForEach -Parallel -ThrottleLimit 10 ($i in $instance) {
InlineScript{
$t = $using:i
Write-Verbose "$t['CmsSrvName']"
}
}
}
wf1 -Verbose $DbInstances
Output:
VERBOSE: [localhost]:System.Data.DataRow['CmsSrvName']
VERBOSE: [localhost]:System.Data.DataRow['CmsSrvName']
VERBOSE: [localhost]:System.Data.DataRow['CmsSrvName']
The output is not what I expected, it just print out the type name not the value. How can I access the DataRow value in a workflow?(in Powershell 5)
Thanks in advance for the help

How do I pass parameters properly?

Please see my Function-to-be below:
Function Query {
param (
[string]$query
[string]$server
[string]$dbase
[string]$user
[string]$pass
)
if ($user) {
$connstr = "Server={0};Database={1};User ID={2};Password={3};Trusted_Connection=False;Connect Timeout=15" -f $server, $dbase, $user, $pass
}
else {
$connstr = "Server={0};Database={1};Integrated Security=True;Connect Timeout=15" -f $server, $dbase
}
$conn.ConnectionString = $connstr
switch ($query.Split()[0]) {
"SELECT" {
$cmd = New-Object System.Data.SqlClient.SqlCommand($query,$conn)
$adapter = New-Object System.Data.SqlClient.SqlDataAdapter($cmd)
$dataset = New-Object System.Data.DataSet
$adapter.Fill($dataset) | Out-Null
return $dataset
}
"UPDATE" {
$cmd = New-Object System.Data.SqlClient.SqlCommand($query,$conn)
return $cmd.ExecuteNonQuery()
}
"INSERT" {
$cmd = New-Object System.Data.SqlClient.SqlCommand($query,$conn)
return $cmd.ExecuteNonQuery()
}
}
}
Query -query "SELECT TOP 10 myField FROM myTable" -server "SQLEXPRESS" -dbase "TEST"
This doesn't work, Powershell ISE gives me red in the param section but I don't understand why. Because of the many different ways people seem to construct Powershell functions (I'm a beginner :)) I am somewhat confused.
How do I make this function work?
Put commas , between the parameters:
Function SomeName {
Param ($param1,$param2,$param3)
}
etc. You can use whitespace or line break after the comma for readability, which is what most people do:
Function SomeName {
param (
$param1,
$param2
)
}

Error while implementing using in powershell

Following this post when I am trying to implement Using functionality in Powershell
Function cUsing {
param (
[System.IDisposable] $inputObject = $(throw "The parameter -inputObject is required."),
[ScriptBlock] $scriptBlock = $(throw "The parameter -scriptBlock is required.")
)
Try { &$scriptBlock }
Finally {
if ($inputObject -ne $null) {
if ($inputObject.psbase -eq $null) {
$inputObject.Dispose()
} else {
$inputObject.psbase.Dispose()
}
}
}
}
cUsing($sqlConnection = New-Object System.Data.SqlClient.SqlConnection)
{
$sqlConnection.ConnectionString = "Server=myserver; Database=master; Integrated Security= True"
$sqlConnection.Open()
$sqlCmd = New-Object System.Data.SqlClient.SqlCommand
$sqlCmd.CommandText = "SELECT STATEMENT"
$sqlCmd.Connection = $sqlConnection
$dsValues = New-Object System.Data.DataSet
$daValues = New-Object System.Data.SqlClient.SqlDataAdapter($sqlCmd)
$daValues.Fill($dsValues)
Write-Host $dsValues.Tables[0]
}
Error:
[ScriptBlock] $scriptBlock = $(throw <<<< "The parameter -scriptBlock is required.")
Edit [Graimer]
When I have moved the curly braces like below
cUsing($sqlConnection = New-Object System.Data.SqlClient.SqlConnection){
....
....
Write-Host $dsValues.Tables[0]}
I am not getting any error but the output which I am getting is
$sqlConnection.ConnectionString = "Server=myserver; Database=master; Integrated Security= True"
$sqlConnection.Open()
$sqlCmd = New-Object System.Data.SqlClient.SqlCommand
$sqlCmd.CommandText = "Select query"
$sqlCmd.Connection = $sqlConnection
$dsValues = New-Object System.Data.DataSet
$daValues = New-Object System.Data.SqlClient.SqlDataAdapter($sqlCmd)
$daValues.Fill($dsValues)
Write-Host $dsValues.Tables[0]
EDIT [Ansgar Wiechers]
when I have changed the scriptblock line to
Function cUsing {
param (
[System.IDisposable] $inputObject = $(throw "The parameter -inputObject is required."),
[ScriptBlock] $scriptBlock = ${throw "The parameter -scriptBlock is required."}
)
I am not getting any error but I am getting the same output which I have showed in Edit above.
The problem with your code is that you don't provide the scriptblock as a parameter, but as a command on it's own. Commands in Powershell are on a single line. Your code runs the command with only the using parameter, and fails. THEN you declare a scriptblock.
You have two options here; escape the linebreak(BAD practice), or move the opening curly brace one line up so it is considered part of the cusing command. Like this:
cUsing($sqlConnection = New-Object System.Data.SqlClient.SqlConnection) {
$sqlConnection.ConnectionString = "Server=myserver; Database=master; Integrated Security= True"
...
..
.
}
A script block is defined via curly braces, not via $(). Like this:
[ScriptBlock]$scriptBlock = { throw "The parameter ..." }
$() is the operator for evaluating subexpressions.
Add-Type #"
using System;
public class Code : System.IDisposable
{
public bool IsDisposed { get; set; }
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!IsDisposed)
{
IsDisposed = true;
}
}
}
"#
function using-block {
param (
[System.IDisposable]$InputObject = $(throw "The parameter -inputObject is required."),
[ScriptBlock]$ScriptBlock = $(throw "The parameter -scriptBlock is required.")
)
try { &$ScriptBlock }
finally {
if ($InputObject) {
if ($InputObject.PSBase) {
$InputObject.PSBase.Dispose()
} else {
$InputObject.Dispose()
}
}
}
}
using-block($c = New-Object Code) {
$c.IsDisposed
}
$c.IsDisposed
Outputs:
False
True