I am trying to find a solution to my problem online but no luck yet. Hence posting the question:
I am running a powershell script which will fetch backup details from all SQL Servers mentioned in a notepad. The script is working fine for SQL Servers till 2008R2. But I am getting error while connecting to SQL Server 2012/2014. The script is running on Windows Server 2008 having SQL Server 2008R2 installed on it. So I will request you to help me to troubleshoot it.
Input File:E:\Sachin\SQL_Servers2.txt
SERVERNAME,SERVERNAME,1433
$servers = Get-Content 'E:\Sachin\SQL_Servers2.txt'
#Create a new Excel object using COM
$Excel = New-Object -ComObject Excel.Application
$Excel.visible = $True
$Excel = $Excel.Workbooks.Add()
$Sheet = $Excel.Worksheets.Item(1)
#Counter variable for rows
$global:intRow = 1
foreach ($sv in $servers) {
# Separate the server and instance names
$srvr = $sv.Split(",")
$server = $srvr[0]
$instance = $srvr[1]
$port = $srvr[2]
getsqlinfo $server $instance $port
function getsqlinfo {
param (
[string]$svr,
[string]$inst,
[string]$port
)
foreach ($instancename in $inst)
{
#Create column headers
$Sheet.Cells.Item($intRow,1) = "INSTANCE NAME:"
$Sheet.Cells.Item($intRow,2) = $instancename
$Sheet.Cells.Item($intRow,1).Font.Bold = $True
$Sheet.Cells.Item($intRow,2).Font.Bold = $True
$global:intRow++
$Sheet.Cells.Item($intRow,1) = "DATABASE NAME"
$Sheet.Cells.Item($intRow,2) = "LAST FULL BACKUP"
$Sheet.Cells.Item($intRow,3) = "LAST DIFFERENTIAL BACKUP"
$Sheet.Cells.Item($intRow,4) = "FULL BACKUP AGE(DAYS)"
#Format the column headers
for ($col = 1; $col -le 4; $col++)
{
$Sheet.Cells.Item($intRow,$col).Font.Bold = $True
$Sheet.Cells.Item($intRow,$col).Interior.ColorIndex = 48
$Sheet.Cells.Item($intRow,$col).Font.ColorIndex = 34
}
$global:intRow++
#######################################################
#This script gets SQL Server database information using PowerShell
$ConnectionString = "data source = $inst,$port; initial catalog = master; trusted_connection = true;"
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SMO') | out-null
# Create an SMO connection to the instance
$s = New-Object ('Microsoft.SqlServer.Management.Smo.Server')
$s.ConnectionContext.ConnectionString = $ConnectionString
$dbs = $s.Databases
#Formatting using Excel
ForEach ($db in $dbs)
Error Msg:
The following exception was thrown when trying to enumerate the collection: "Failed to connect to server .".
At E:\sachin\BackupDetailsVersion7.ps1:51 char:8
+ ForEach <<<< ($db in $dbs)
+ CategoryInfo : NotSpecified: (:) [], ExtendedTypeSystemException
+ FullyQualifiedErrorId : ExceptionInGetEnumerator
Pre-Checks Done:
I am able to telnet the target SQL Server from the Server where I am running the script.
I am having access to the target SQL Server as well.
Microsoft® Windows PowerShell Extensions for Microsoft® SQL Server® 2012 SP2,Microsoft® SQL Server® 2012 SP2 Shared Management Objects and Microsoft® System CLR Types for Microsoft® SQL Server® 2012 SP2 are already installed on the target Server.
Execution policy is set to unrestricted on the target Server.
I am able to get the SQL Server details on the target machine when checking locally, but not able to get details from other machine.
I am suspecting whether is there any restriction/limitation to connect between different SQL Server versions through powershell (any kind of compatibility issue).
Any help/suggestions will be highly appreciated. Thanks in advance. :)
Related
Wondering if yall can help me resolving an error I'm receiving when using Invoke-Sqlcmd to query a linked server in powershell. Thank you for any help you might be able to provide!
Here's my code
$SQLServer = "SERVER1"
$database = "Database1"
$query = "
SELECT
a.id,
a.location,
a.name,
b.office
FROM [SERVER1].[Database1].[dbo].[Table1] a
LEFT JOIN [SERVER1].[Database1].[dbo].[Table2] b ON b.id = a.id
"
Invoke-Sqlcmd -ServerInstance $SQLServer -Database $database -Query $query
Here's the error I receive:
Invoke-Sqlcmd : A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote
connections. (provider: Named Pipes Provider, error: 40 - Could not open a connection to SQL Server)
In my experience working with powershell and SQL I've been unable to get Invoke-SQL command to work properly. I have however used this method with much success.
You can change the integrated security to true and it will use the currently logged in Windows account to authenticate with the SQL server instead of providing the credentials in the script.
#creates connection, enter the SQL server host name, the user and password.
$connectionString = “Server=#SQLServer;uid=$User2; pwd=$pwdencrypt2;Integrated Security=False;”
$connection = New-Object System.Data.SqlClient.SqlConnection
$connection.ConnectionString = $connectionString
$connection.Open()
#This will be your query bulk insert as an example
[string]$queryCL1 = "
BULK INSERT [Databasename].[dbo].[tablename]
FROM 'filepath'
WITH(
FIELDTERMINATOR = ',',
ROWTERMINATOR = '\n',
FIRSTROW = 2
)"
#this uses the set variables to build and execute the command with execute reader
$command = $connection.CreateCommand()
$command.CommandText = $queryCL1
$command.CommandTimeout=0
$resultCL1 = $command.ExecuteReader()
I need to use ApplicationIntent=ReadOnly with my SQL command in powershell which is connecting to a replica database. Can anyone help ?
Since replicas servers could not be accessed directly. So I need to use this command. I know how to manually do it but need help on code.
$SQLQuery = "SELECT x.SCode, x.DatabaseName FROM dbo.Logins x ORDER BY x.SCode"
$auth = #{Username = $SQLUserName; Password = $SQLAdminPassword}
try
{
$allTenants = Invoke-Sqlcmd -Query $SQLQuery -ServerInstance $SQLServerName -Database 'SShared'-QueryTimeout -0 #Auth -ErrorAction Stop
Write-Log -LogFileName $logfile -LogEntry ("Found {0} tenants" -f $allTenants.Count)
}
I am geeting the below error using this -
Exception Message A network-related or instance-specific error
occurred while establishing a connection to SQL Server
The server was not found or was not accessible. Verify that the
instance name is correct and that SQL Server is configured to allow
remote connections. (provider: Named Pipes Provider, error: 40
- Could not open a connection to SQL Server)
There's a few ways that you can do this.
Easy way
dbatools
There is a PowerShell module for interacting with SQL Server created by the SQL Server community called dbatools.
In the module, there is a function called Invoke-DbaQuery which is essentially a wrapper for Invoke-Sqlcmd.
This function has a parameter, -ReadOnly, that you can use that was created exactly for this scenario.
# Changing your $auth to a PSCredential object.
$cred = [System.Management.Automation.PSCredential]::New(
$SqlUserName,
(ConvertTo-SecureString -String $SqlAdminPassword -AsPlainText -Force))
# Splatting the parameters for read-ability.
$QueryParams = #{
Query = $SQLQuery
SqlInstance = $SQLServerName
Database = 'SShared'
QueryTimeout = 0
SqlCredential = $cred
ReadOnly = $true # <-- Specifying read-only intent.
ErrorAction = 'Stop'
}
$allTenants = Invoke-DbaQuery #QueryParams
Other way
Invoke-Sqlcmd
If you can't, won't, don't want to use dbatools, you can still use Invoke-Sqlcmd. The latest release at the time of writing, has the option to specify the parameter -ConnectionString.
You can state that it's read-only there.
# Splatting again for read-ability.
$SqlcmdParams = #{
Query = $SQLQuery
QueryTimeout = 0
ConnectionString = "Data Source=$SQLServerName;Initial Catalog=SShared;User ID=$SqlUserName;Password=$SqlAdminPassword;Integrated Security=false;ApplicationIntent=ReadOnly" # <-- Specifying read-only intent.
ErrorAction = 'Stop'
}
Invoke-Sqlcmd #SqlcmdParams
We use an Azure Elastic Pool resulting in multiple client databases and one master database with references to the client database.
We already have multiple databases and are working on a new version of the code. We use EF6 Code-First.
When we make a change to our model (add a property) we create the migration file and need to call Update-Database for all existing client databases.
This is monkey work we want to skip.
I already have a Powershell script to connect to the master database and execute a query on a table. This returns the names of the child databases.
With it I can change the Web.config and replace the Template database name with the proper name of the child database.
Now I need to call Update-Database to execute the migration scripts. With this last part I'm struggling because I'm running the ps1-script outside Visual Studio and thus the command Update-database is unknown. I tried using migrate.exe but then I get lots of errors.
I think the easiest solution is to run my script within the Package manager console but I can't figure out how to do that.
I managed to get it working. After I placed the ps1-file in the root of my code folder I could run it in the Package Manager Console using .\UpdateDatabases.ps1.
For completeness here's the script I created. I'm new to PowerShell so some optimizations might be possible.
cls
$currentPath = (Get-Item -Path ".\" -Verbose).FullName
#Read Web.config
$webConfig = $currentPath + "\<your project>\Web.config"
$doc = (Get-Content $webConfig) -as [Xml]
$DatabaseNamePrefix = $doc.configuration.appSettings.add | where {$_.Key -eq 'DatabaseNamePrefix'}
#Get Master connectionstring
$root = $doc.get_DocumentElement();
foreach($connString in $root.connectionStrings.add | where {$_.Name -eq "Master"})
{
$masterConn = $connString.connectionString
}
#Connect to master database
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = $masterConn
#Query Client table for the child database names
$SqlQuery = "select Code from Clients"
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.CommandText = $SqlQuery
$SqlCmd.Connection = $SqlConnection
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SqlAdapter.SelectCommand = $SqlCmd
#Put query result in dataset
$DataSet = New-Object System.Data.DataSet
$SqlAdapter.Fill($DataSet)
$SqlConnection.Close()
foreach ($row in $DataSet.Tables[0].Rows)
{
$clientDbName = $row[0].ToString().Trim()
#Change Web.Config
foreach($connString in $root.connectionStrings.add | where {$_.Name -eq "DevelopmentDb"})
{
$newDatabaseName = "Database=" + $DatabaseNamePrefix.value + $clientDbName + ";";
$newConn = $connString.connectionString -replace "(Database=.*?;)",$newDatabaseName
$connString.connectionString = $newConn;
}
$doc.Save($webConfig)
#Update database
Update-Database -ConfigurationTypeName Application
}
"Finished"
You may want to take a look at Azure Elastic Database Jobs. Which is designed to work with the elastic database pools.
The Elastic Database Jobs SDK includes also PowerShell components.
I found some code online but so far I can't get it to connect to my SQL Server database. I have followed this website to the letter: https://blogs.msdn.microsoft.com/walzenbach/2010/04/14/how-to-enable-remote-connections-in-sql-server-2008/
I have allowed remote connections, added port 1433 to my firewall etc. I then run this code from PowerShell ISE:
$dataSource = “\\SCCM12-01\MSSQLSERVER”
$user = “MyID\OurDomain.org”
$pwd = “MyPassword”
$database = “CM1”
$connectionString = “Server=$dataSource;uid=$user; pwd=$pwd;Database=$database;Integrated Security=False;”
$connection = New-Object System.Data.SqlClient.SqlConnection
$connection.ConnectionString = $connectionString
$connection.Open()
when I run this I get the following error.
Exception calling "Open" with "0" argument(s): "A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was
not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: SQL Network Interfaces, error: 25 - Connection
string is not valid)"
If you have simple query to do I recommend Select-SQLView powershell module. It allows to quickly select rows from table or view. It stores your database and server name so you do not have to provide this values every time.
As usual You can push results to table or to GridView.
If more complex queries are needed use SQLCommands module.
Not sure why you are getting this error. You can refer to this link https://github.com/Tervis-Tumbler/InvokeSQL
You can try this one-
function Invoke-SQL {
param(
[string] $dataSource = ".\SQLEXPRESS",
[string] $database = "MasterData",
[string] $sqlCommand = $(throw "Please specify a query.")
)
$connectionString = "Data Source=$dataSource; " +
"Integrated Security=SSPI; " +
"Initial Catalog=$database"
$connection = new-object system.data.SqlClient.SQLConnection($connectionString)
$command = new-object system.data.sqlclient.sqlcommand($sqlCommand,$connection)
$connection.Open()
$adapter = New-Object System.Data.sqlclient.sqlDataAdapter $command
$dataset = New-Object System.Data.DataSet
$adapter.Fill($dataSet) | Out-Null
$connection.Close()
$dataSet.Tables
}
The error message actually explains what's wrong:
"SQL Network Interfaces, error: 25 - Connection string is not valid".
There is something amiss on the connection string. What exactly is hard to say as you have masked most of the details. Maybe the smart quotes wreck things? Maybe you got a quote character in the password? Anyway, it looks like you have invalid parameter for the user id:
$connectionString = “Server=$dataSource;uid=$user; pwd=$pwd;Database=$database;Integrated Security=False;”
Try User Id instead of uid like so,
Server=myServerAddress;Database=myDataBase;User Id=myUsername;Password=myPassword;
You properly just need to change 'Integrated Security' to 'true' when not using a db login
**Integrated Security=True**
I have a code snippet that is used to connect to a SQL Server Instance, and query, this works fine.
$instance="Instance"
$DB = "master"
$sqlConnection = ConnectionString $Instance $DB
$sqlConnection = New-Object System.Data.SqlClient.SQLConnection($sqlConnection)
$sqlConnection.Open()
However, the same code snippet, if put into a foreach loop, where it has to iterate through a set of servers, it doesn't work.
foreach ($instance in $Instances) {
$instance
$DB = "master"
$sqlConnection = ConnectionString $Instance $DB
$sqlConnection = New-Object System.Data.SqlClient.SQLConnection($sqlConnection)
$sqlConnection.Open()
}
Error:
Exception calling "Open" with "0" argument(s): "A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: Named Pipes Provider, error: 40 - Could not open a connection to SQL Server)"
Ansgar is correct. You shouldn't reuse variable names.
An easier way to implement this is using the Invoke-Query module, which collapses most of your code to a single line.
$sql = "Update myTable set myColumn = 1;"
$db= "master"
$instances = ("instance1","instance2")
foreach ($instance in $instances)
## This command uses windows auth.
## You can pass in a Credential object to use SqlServer auth.
$rowcount = $sql | Invoke-SqlServerQuery -Server $instance -Database $db -CUD
Write-Host "Updated $rowcount rows on server $instance."
}
Disclaimer: I'm the author of the module.