Error passing variable SQL instance name into Invoke-SQLcmd - sql-server

I'm working on a project that pulls a list of SQL instances from server A and then loops through the returned list and runs a query (eventually to audit users and insert results into table) but getting an error instance not found. It seems I'm not defining the variable correctly in the loop because if I hardcode the instance name it works.
I appreciate any input on how to fix this.
$Serverlist = invoke-sqlcmd -ServerInstance TESTSERVER1 -Database TESTDB -Query "SELECT instancename from testtable"
foreach ($SQLInst in $Serverlist)
{
$Inst = $SQLInst.INSTANCE
Invoke-Sqlcmd -ServerInstance ${$Inst} -Database Master -Query "select ##servername as servername" | select -ExpandProperty servername
} #end foreach loop
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) At line:12 char:1
+ Invoke-Sqlcmd -ServerInstance ${$SQLInst} -Database Master -Query "select ##serv ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [Invoke-Sqlcmd], SqlException
+ FullyQualifiedErrorId : SqlExectionError,Microsoft.SqlServer.Management.PowerShell.GetScriptCommand

There's no reason to use curly braces like that ${$Inst} in this instance.
Simply using $Inst will work fine. If you do use curly braces, then you don't use the $ inside: ${Inst}.
Invoke-Sqlcmd -ServerInstance $Inst
# or
Invoke-Sqlcmd -ServerInstance ${Inst}

I would check to make sure that each instance is the correct one:
$Serverlist = invoke-sqlcmd -ServerInstance TESTSERVER1 -Database TESTDB -Query "SELECT instancename from testtable"
foreach ($SQLInst in $Serverlist)
{
$Inst = $SQLInst.INSTANCE
Write-Host $Inst
} #end foreach loop
I noticed some problems with my previous statement. Can you try this?
$Serverlist = invoke-sqlcmd -ServerInstance TESTSERVER1 -Database TESTDB -Query "SELECT instancename from testtable"
foreach ($SQLInst in $Serverlist)
{
$Inst = $SQLInst.instancename
Invoke-Sqlcmd -ServerInstance "$Inst" -Database Master -Query "select ##servername as servername" | select -ExpandProperty servername
} #end foreach loop

When you are referencing a result from a query, you must specify the column name even if there is only one column in the query. Enclosing the query in parentheses and using dot notation with the column name will convert the rows to a list of values. Your original problem was you had the column name incorrect.
Try this:
$Serverlist = ( invoke-sqlcmd -ServerInstance TESTSERVER1 -Database TESTDB -Query "SELECT instancename from testtable").instancename
$Serverlist | ForEach-Object {
( Invoke-Sqlcmd -ServerInstance $SQLInst -Database Master -Query 'select ##servername as servername' ).servername
}

Related

Invoke-Sqlcmd error in Loop, No error once?

If I run this .ps1 script-
$secID = Invoke-Sqlcmd -ServerInstance "MyDBServer" -Database "MyDataBase"-Query "SELECT SysID FROM dbo.SecurityLevels WHERE LEVELNAME LIKE '%User%';"
Write-Host "MyDataBase"
Write-Host $secID.SysID
I get the following on the console with no error -
MyDataBase
18
However if I try this same query in a for loop in a larger script -
$dbservers = #('DataBaseServer1', 'DataBaseServer2')
foreach ($dbserver in $dbservers)
{
$databases = Get-SqlDatabase -ServerInstance $dbserver | Where-Object { $_.Name -Match '\d{3,4}' -and $_.Name -notlike '*test*'}
foreach ($database in $databases)
{
$secID = Invoke-Sqlcmd -ServerInstance $dbserver.Name -Database $database.Name -Query "SELECT SysID FROM dbo.SecurityLevels WHERE LEVELNAME LIKE '%User%';"
Write-Host $database.Name
Write-Host $secID.SysID
}
}
I get the correct query result but errors preceding it on the console -
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)
At \SQL.ps1:28 char:13
+ ... $secID = Invoke-Sqlcmd -ConnectionString $ConnectionString -Query ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [Invoke-Sqlcmd], SqlException
+ FullyQualifiedErrorId : SqlExceptionError,Microsoft.SqlServer.Management.PowerShell.GetScriptCommand
Invoke-Sqlcmd :
At \SQL.ps1:28 char:13
+ ... $secID = Invoke-Sqlcmd -ConnectionString $ConnectionString -Query ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ParserError: (:) [Invoke-Sqlcmd], ParserException
+ FullyQualifiedErrorId : ExecutionFailureException,Microsoft.SqlServer.Management.PowerShell.GetScriptCommand*
MyDataBase
18
Why do I get errors when running it in the loop versus running it one time? Also why does the query work even though I get an error? I think I am missing something any help would be greatly appreciated!
I think its because you are calling property Name on a String, effectively returning nothing. try this (I just deleted the calling of the property $dbserver.Nameto $dbserver):
$dbservers = #('DataBaseServer1', 'DataBaseServer2')
foreach ($dbserver in $dbservers)
{
$databases = Get-SqlDatabase -ServerInstance $dbserver | Where-Object { $_.Name -Match '\d{3,4}' -and $_.Name -notlike '*test*'}
foreach ($database in $databases)
{
$secID = Invoke-Sqlcmd -ServerInstance $dbserver -Database $database.Name -Query "SELECT SysID FROM dbo.SecurityLevels WHERE LEVELNAME LIKE '%User%';"
Write-Host $database.Name
Write-Host $secID.SysID
}
}
The error must be because its looking for a DB instance "" and it doesn't find it, and the query might still go though because the the first server might not have an instance, or because those values were already loaded into the properties from before. But I'm just speculating, haven't really used this cmdlets.
PS: if you want to avoid calling empty properties add Set-StrictMode -Version 2 to your script, example:
PS > $text = "This is just a string"
PS > $text.AnyProperty
PS > Set-StrictMode -Version 2
PS > $text.AnyProperty
The property 'AnyProperty' cannot be found on this object. Verify that the property exists.
At line:1 char:1
+ $text.AnyProperty
+ ~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], PropertyNotFoundException
+ FullyQualifiedErrorId : PropertyNotFoundStrict
Hope it helps.

Script designed to back up sql database cannot open database [<database>] requested by the login <ComputerName\account>

When I run BACKUP DATABASE landofbeds TO DISK = 'c:\temp\backups\lob_backup.bak' in SQL Server Management Studio it creates a back up and works fine.
I want to create a powershell script that does this so I've written:
$Server = 'localhost'
$Database = '<DbName>'
$Filepath = 'c:\temp\backups\lob_backup.bak'
$Query = "BACKUP DATABASE '$Database' TO DISK = '$Filepath'"
Invoke-Sqlcmd -ServerInstance $Server -Database $Database -Query $Query
Anything in angular brackets (<>) is a placeholder for the actual data.
This returns an error of:
Invoke-Sqlcmd : Cannot open database "<DbName>" requested by the login. The login
failed. Login failed for user '<ComputerName\Account>'.
At line:6 char:1
+ Invoke-Sqlcmd -ServerInstance $Server -Database $Database -Query $Que ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [Invoke-Sqlcmd], SqlException
+ FullyQualifiedErrorId : SqlExectionError,Microsoft.SqlServer.Management.PowerShell.GetScriptCommand
I am having the same issue, with the same error.
I know the Login Works, I had to login to SSMS with the correct creds myself.
$thisSrvr = hostname
$cred_SQL = get-credential -userName "username" -message "Creds to Run the SQL Backup"
$SqlServer = (Get-SqlDatabase -Credential $cred_SQL -ServerInstance $thisSrvr).name
$date = get-date -UFormat "%Y%m%d"
$backups = "D:\"
Foreach ($db in $SQLServer){
$pw = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($cred_SQL.Password)) #converts SecureString PW to Plain Txt.
$ASver = (Invoke-SQLCmd -Query "getting app version info" -ServerInstance $thisSrvr -UserName $cred_SQL.UserName -Password $pw -Database $DB).version -replace "\.", "-"
$BAKFile = $db + "_ROLE_" + $ASVer + "_" + $date + ".bak"
$BAKStr = $backups + $BAKFile
Backup-SqlDatabase -Credential $cred_SQL -ServerInstance $thisSrvr -Database $db -BackupAction "Database" -BackupFile $BAKStr -CompressionOption "On" -Initialize | out-null
}
Clear-Variable pw -Scope Global
I am not sure where to go to look at log info in SSMS, but I can search that myself.
My only thought about why this might be happening is that perhaps the login doesn't have permissions to this DB? But I'd find that weird since this login is the one that created the DB.

Using PowerShell to Run a SQL query then insert the results into a table

I have a small PowerShell script that runs a query on about 30+ servers which pulls the server name and which version of SQL Server is installed. I then want to insert that data into a table but can't quite figure out how to do that with the returned data set my query returns. This is my code so far
$SvrNameList = #( invoke-sqlcmd -serverinstance MyServer -Database MyDB -Query "SELECT ServerName FROM ServerNames WHERE [Enabled] = 1" ) | select-object -expand ServerName
foreach ( $i in $SvrNameList )
{
invoke-sqlcmd -ServerInstance $i -Query "SELECT ##ServerName AS ServerName, ##Version AS Version"
}
Any help is appreciated
First, add the instance names and versions into a hash table. After it's populated, you can do inserts into the result table. Like so,
$ht=#{} # Create empty hashtable
foreach ( $i in $SvrNameList ){
$r = invoke-sqlcmd -ServerInstance $i -Query "SELECT s=##ServerName, v=##Version" # Query server names and versions
$ht.Add($r.s, $r.v) # Add name and version into hashtable
}
# Enumerate the hashtable and generate insert commands
$ht.GetEnumerator() | % {
invoke-sqlcmd -ServerInstance foo -query "insert into t(s, v) values ('" + $_.value + "', '"+ $_.name +"');"
}

Error when inserting strings with $( with Invoke-SqlCmd in Powershell

Setup:
CREATE TABLE MyTest (TestCol1 nchar(5))
Test:
Following work:
Invoke-Sqlcmd -Database "databasename" -ServerInstance "hostname" -OutputSqlErrors $True -Query "INSERT INTO MyTest VALUES ('`$5')"
Invoke-Sqlcmd -Database "databasename" -ServerInstance "hostname" -OutputSqlErrors $True -Query "INSERT INTO MyTest VALUES ('(5')"
Following fails with the error below:
Invoke-Sqlcmd -Database "databasename" -ServerInstance "hostname" -OutputSqlErrors $True -Query "INSERT INTO MyTest VALUES ('`$(5')"
Invoke-Sqlcmd -Database "databasename" -ServerInstance "hostname" -OutputSqlErrors $True -Query "INSERT INTO MyTest VALUES ('`$`(5')"
Error:
Invoke-Sqlcmd :
At line:1 char:1
+ Invoke-Sqlcmd -Database "databasename" -ServerInstance "hostname" -Ou ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ParserError: (:) [Invoke-Sqlcmd], ParserException
+ FullyQualifiedErrorId : ExecutionFailureException,Microsoft.SqlServer.Management.PowerShell.GetScriptCommand
Aftersome research I found that $( provides functionality in powershell thus is reserved. However I tried escaping the parenthesis but with no success. I've tried finding alternatative. Any ideas on how I can do this? If I use the CHAR function in SQL Server that works but it would be a pain to deal with in my code. Thank you.
All you need to do is disable the variables lookup in the invoke-Sqlcmd by adding -DisableVariables.
$() is use to pass in variable into the sql statements.
Invoke-Sqlcmd -InputFile $fileName -ServerInstance $serverInstance -DisableVariables
Source http://msdn.microsoft.com/en-us/library/cc281720.aspx
try this:
'INSERT INTO MyTest VALUES ("$(5")'
or this:
'INSERT INTO MyTest VALUES (''$(5'')'
The $(anyvalue) are reserved variables to be used with SQLCMD.EXE
Change the symbol $ by the sql server CHAR(36):
Invoke-Sqlcmd -Database "databasename" -ServerInstance "hostname" -OutputSqlErrors $True -Query "INSERT INTO MyTest VALUES (CHAR(36)+'(5)')"
The issue seems that the $( is a reserved word in Powershell thus to make this work I had use the CHAR function to store the $ (CHAR(36)). Once I did this it worked but for this project I abandoned using the Invoke-SqlCmd command and used a standard ADO.NET method. It seems that the Invoke-SqlCmd wants to parse the command and for the reserved combination continues to see the reserved word even if you escape the characters.
"INSERT INTO MyTest VALUES ('`$5')"
Outputs a string
INSERT INTO MyTest VALUES ('$5')
"INSERT INTO MyTest VALUES ('(5')"
Outputs a string
INSERT INTO MyTest VALUES ('(5')
"INSERT INTO MyTest VALUES ('`$(5')"
Outputs a string
INSERT INTO MyTest VALUES ('$(5')
"INSERT INTO MyTest VALUES ('`$`(5')"
Outputs a string
INSERT INTO MyTest VALUES ('$(5')
What do you want to insert? Actually '$5' or the value of the variable $5? Also, you have strange parentheses around your variable $5. Not sure if is suppose to be like that or your don't understand another important part of powershell variables? Because this a variable in a string "There are $((Get-Process).Count) processes running" too.

Invoke-Sqlcmd cmdlet throws exception when using -Variable parameter

When I try to use the Invoke-Sqlcmd cmdlet from SQL Server 2008 to execute a query that contains scripting variables, ex. $(MyVar), I receive the following exception:
Invoke-Sqlcmd : Object reference not set to an instance of an object.
Here's the code I'm trying to run (which is copy/paste from the Books Online example with only the connection parameters added).
$MyArray = "MyVar1 = 'String1'", "MyVar2 = 'String2'"
Invoke-Sqlcmd -Query "SELECT `$(MyVar1) AS Var1, `$(MyVar2) AS Var2;" -Variable $MyArray -ServerInstance "localhost" -Database "master" -UserName "who" -Password "me"
If I replace $(MyVar1) and $(MyVar2) in the -Query with 'x' and 'y' then it runs perfectly.
$MyArray = "MyVar1 = 'String1'", "MyVar2 = 'String2'"
Invoke-Sqlcmd -Query "SELECT 'x' AS Var1, 'y' AS Var2;" -Variable $MyArray -ServerInstance "localhost" -Database "master" -UserName "who" -Password "me"
Can anyone tell me why this is not working?
Indeed this is a bug in SQL Server - tracked and fixed here
https://connect.microsoft.com/sqlserver/feedback/details/358291/invoke-sqlcmd-powershell-cmdlet-fails-when-array-passed-via-variable
However, there's a posted workaround. Remove the spaces around the assignment. So instead of
$MyArray = "MyVar1 = 'String1'", "MyVar2 = 'String2'"
use
$MyArray = "MyVar1='String1'", "MyVar2='String2'"
Ok. I posted this same question on the SQL Server forums and, apparently, this is a bug in SQL Server 2008's PowerShell cmdlets... follow the thread here.
Try this alone:
PS>$MyArray = "MyVar1 = 'String1'", "MyVar2 = 'String2'"
Now:
PS>$MyArray
and
PS>MyVar1
Now:
PS>$MyArray|get-member
PowerShell thinks you've assigned 2 string objects to $MyArray, nothing more. This approach does not result in defining the variables $MyVar1 and $MyVar2 to PowerShell.
Sorry, I can't fire up my SQL2008 VM right now to comment on the other parts...

Resources