SQL Server instance discovery using SqlServer PowerShell module - sql-server

The dbatools module's Find-DbaInstance can accept a computer name and return a list of instances on that machine. Is there an equivalent in the SqlServer module that does this? I tried Get-SqlInstance, but it seems to need actual instance names, as opposed to being able to use it for instance discovery. I'd prefer to use dbatools, but the SqlServer module is what I have consistent access to in the current environment.

There are many good results that dont even depend on the sqlserver module in this article
eg.
$SQLInstances = Invoke-Command -ComputerName "localhost" {
(Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server').InstalledInstances
}
foreach ($sql in $SQLInstances) {
[PSCustomObject]#{
ServerName = $sql.PSComputerName
InstanceName = $sql
}
}

Related

How to run an Azure SQL Server Stored Procedure using an Automation Account Runbook with Managed Identity?

I am trying to run SQL Server DB index and statistics maintenance activating a stored procedure called AzureSQLMaintenance with a PowerShell Runbook in Automation Account.
Since I don't want to use standard SQL Server authentication, I am trying to use Managed Identities.
Online I found some Microsoft documentation getting quite close to the point on Microsoft Tech Community here and here, but both the threads are missing some pieces. A very good clue clue was given to me by this blog post but it was missing Managed Identity Authentication
I finally managed, after a couple of days of tests, to make it work, so I'll write the whole process down in case anybody will need to do the same:
Under Account Settings > Identity System Assigned Managed Identity must be set to On, and we'll need the Object (principal) Id, so remember to mark it down
In my Azure SQL Database Server, under Settings > Azure Active Directory, we'll need to check the value of the Azure Active Directory admin. In my case, this is a group
In Azure Active Directory, edit the group individuated on the previous step and add the Object (principal) Id obtained at the step 1 as a member of the group
A Powershell Runbook in Automation Account needs to be created
The powershell Runbook Code will need to look something like
Write-Output "Run started"
# Instantiate the connection to the SQL Database
$sqlConnection = new-object System.Data.SqlClient.SqlConnection
# Connect to the the account used the Managed Identity
Connect-AzAccount -Identity
# Get a Token
$token = (Get-AzAccessToken -ResourceUrl https://database.windows.net ).Token
# Initialize the connection String
$sqlConnection.ConnectionString = "Data Source=db-server-name.database.windows.net;Initial Catalog=<db-name>;Connect Timeout=60"
# Set the Token to be used by the connection
$sqlConnection.AccessToken = $token
# Open the connection
$sqlConnection.Open()
Write-Output "Azure SQL database connection opened"
# Define the SQL command to run
$sqlCommand = new-object System.Data.SqlClient.SqlCommand
# Allow Long Executions
$sqlCommand.CommandTimeout = 0
# Associate the created connection
$sqlCommand.Connection = $sqlConnection
Write-Output "Issuing command to run stored procedure"
# Execute the SQL command
$sqlCommand.CommandText= 'exec [dbo].[AzureSQLMaintenance] #parameter1 = ''param1Value'', #parameter2 = ''param2Value'' '
$result = $sqlCommand.ExecuteNonQuery()
Write-Output "Stored procedure execution completed"
# Close the SQL connection
$sqlConnection.Close()
Write-Output "Run completed"
At this point, run a test on the Runbook: in my case it worked perfectly, even if it took a while (that's the reason for the parameter $sqlCommand.CommandTimeout = 0)

Providing local variable to Invoke-Command using $using:Variablename

I'm trying to perform unattended SQL Installation using configuration file and I'm using $Using:Password to pass the variable from local machine to the invoke-command but it seems that it is not taking the credentials.
When I remove the /SQLSVCPASSWORD then it works fine.
Anything I'm missing here?
$SQLNodes = #("SQL10")
Foreach ($node in $SQLNodes ){
$Password = "Verystrongpassword"
if(Test-Connection -ComputerName $node -Count 1 -ea 0)
{
$Invoke = Invoke-Command -ComputerName $node {
$Exe = 'C:\SQLServerFull\Setup.exe'
Start-Process -FilePath $Exe -ArgumentList /ConfigurationFile="C:\Script\ConfigurationFile.INI", /SQLSVCPASSWORD=$($using:Password) -Wait -RedirectStandardOutput C:\error.txt
}
}
}
Below is the error
SQL Server 2017 transmits information about your installation experience, as well as other usage and performance data, to Microsoft to help improve the product. To learn more about SQL Server 2017 data processing and privacy controls, please see the Privacy Statement.
The following error occurred:
The SQL Server service account login or password is not valid. Use SQL Server Configuration Manager to update the service account.
Error result: -2061893563
Result facility code: 1306
Result error code: 69

How to get powershell to read a SQL Server error log

I need your help with PowerShell today. I have a SQL Server instance (on a server named VMDEV-APP11) configured as a Central Management Server (CMS) in which I have registered all my Dev/Test/Prod SQL instances and I want to use PowerShell to read the SQL Server error logs from all my servers. I have a query which retrieves all my SQL Server instances but when I pipe the output of this into a call to ReadErrorLog, I get an error.
This is the code that retrieves my list of the SQL Server instances which are registered on my CMS (note that I exclude my SQL Server 2000 instances):
Set-Location D:\MSSQL11.MSSQLSERVER\CMS
# Define functions to query SQL Server and write data to a SQL table
. ./invoke-sqlcmd2.ps1
. ./write-datatable.ps1
Invoke-sqlcmd2 -ServerInstance "VMDEV-APP11" -Database dba -Query "select s.server_name from msdb.dbo.sysmanagement_shared_registered_servers s, msdb.dbo.sysmanagement_shared_server_groups g where s.server_group_id = g.server_group_id and g.name not like '2000%'"
server_name
-----------
INF-SRV14
VMDEV-APP15
NEX-SRV48
...
And this is what I thought would work and the error I actually get:
Invoke-sqlcmd2 -ServerInstance "VMDEV-APP11" -Database dba -Query "select s.server_name from msdb.dbo.sysmanagement_shared_registered_servers s, msdb.dbo.sysmanagement_shared_server_groups g where s.server_group_id = g.server_group_id and g.name not like '2000%'" | foreach-object { $_.server_name.ReadErrorLog() }
Error:
Method invocation failed because [System.String] does not contain a method named 'ReadErrorLog'.
At D:\MSSQL11.MSSQLSERVER\CMS\db_errorlog.ps1:38 char:284
+ ... reach-object { $_.server_name.ReadErrorLog() }
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound
I suspect I need to convert the string returned by my query to another type of object (a server name perhaps?) in order to get the ReadErrorLog() call to work but I don't know how to do that.
Any suggestions?
Any help would be greatly appreciated.
Ken
You're trying to invoke the ReadErrorLog() method on a DataRow column value, which is a string.
What you need to do is something like this:
Import-Module sqlps -DisableNameChecking
Invoke-sqlcmd2 -ServerInstance "VMDEV-APP11" -Database dba -Query "select s.server_name from msdb.dbo.sysmanagement_shared_registered_servers s, msdb.dbo.sysmanagement_shared_server_groups g where s.server_group_id = g.server_group_id and g.name not like '2000%'" | `
foreach-object {
$server = $_.server_name
$logs = (get-item SQLSERVER:\sql\$server\default).ReadErrorLog()
# $logs is a DataTable so you can iterate the rows however you wish
}
This assuming all your servers are default instances, otherwise you may need to fiddle around a bit more to target specific instances.
The code I'm running on my SQL Central Management server is:
$logs = 0..6 | % { (get-item SQLSERVER:\sql\<servername>\default).ReadErrorLog($_) }
Originally, I got this to work by adding the service account that ran the PowerShell code to the local Administrators Windows group AND creating a SQL login with sysadmin privileges on the remote SQL server host. Not sursprisingly, my security officer had an issue with this. My initial attempts to reduce access resulted i the following error:
WARNING: Could not obtain SQL Server Service information. An attempt to connect
to WMI on 'NEX-SRV1' failed with the following error: SQL Server WMI provider
is not available on NEX-SRV1. --> Invalid namespace
After quite a bit of fooling around, I have what I think is the minimum security needed to read the SQL error logs. Perform the following grants on the remote server running the SQL Server database:
Local Windows Group
Add the service account to the "Distributed COM Users" group
WMIMgmt.msc
Add the service account to each of the following branches with all security options EXCEPT "Edit Security"
Root > cimv2
Root > cimv2 > ms_409
Root > Microsoft > SQLServer > ComputerManagement
SQL Server
Create a SQL login for the service account and add it to the "Security Admin" role.
After I had made these changes, I have the ability to monitor SQL Error logs from a central location without having to grant crazy levels of access to the service account.

How can I use invoke-sqlcmd without enabling named pipes?

I am using a script that loads the following SQL Server 2008 R2 powershell plugins
Add-PSSnapin SqlServerCmdletSnapin100
Add-PSSnapin SqlServerProviderSnapin100
I then user invoke-sql like this:
Invoke-Sqlcmd -Query "select * from table" -ServerInstance xyz -Database abc -username xxxxxx -password yyyyyyy
I am using method to run a number of upgrade scripts on our databases. I was quite happily using this in our dev\test environments but then we I tried it in production and it turns out we have a difference in server configurations. On our prod servers named pipes are disabled for security reasons (apparently worm attacks) and our DBA's don't want to enable.
This is the error I get and research says it is a named pipes problem - starts working when I enable them too.
INFO ERROR: Invoke-Sqlcmd : A connection was successfully
established with the server, but then an error occurred during the
login process. (provider: Shared Memory Provider, error: 0 - No
process is on the other end of the pipe.)
Does anyone know if there is some way to switch my script so that it does not require named pipes? Or is this the built in connection method for invoke-sqlcmd and I need to change tack (if so any suggestions).
Similar to Surreal's response to use LPC (local shared memory), for TCP/IP instead of named pipes you can also specify -ServerInstance tcp:foodb
This is an educated guess. But here goes:
I think you have to "override the default" by using the registry.
http://support.microsoft.com/kb/229929
Now, the easiest way to do this (IIRC) is to go through your
Control Panel / ODBC Data Source / System DSN.
Add a "Sql Server". (Not the native client ones).
The most important button is the "Client Configuration" where you can pick named-pipes or tcp/ip.
Try out the DSN method, and after completing the wizard, look at the registry entries under
HKEY_LOCAL_MACHINE\Software\Microsoft\MSSQLServer\Client\ConnectTo
.........
You might check out this:
http://sev17.com/2012/11/05/cloning-sql-servers-to-a-test-environment/
Look for this code.
sqlcmd -S myCMServerInstance -d msdb -Q $query -h -1 -W |
foreach { Set-ItemProperty -Path 'HKLM:SOFTWAREMicrosoftMSSQLServerClientConnectTo' -Name $($_ -replace 'TEST') -Value "DBMSSOCN,$_" }
}
You can change the connection method using prefixes to the instance name as for sqlcmd. With SQL Server 2012 and Powershell 4, this works for me:
Invoke-Sqlcmd -Query $sqlQuery -serverinstance "lpc:localhost" -Database "myDatabase"

"The target database schema provider could not be determined" With VSDBCMD but Only When /ConnectionString is Specified

I have been developing a deployment script to deploy our database project to multiple database instances. I'm testing it by running it manually. It is a very simple script, and just runs VSDBCMD against a deployment manifest created to deploy our base, or template database. In order to deploy to different databases on the same server, the script uses the deployment manifest, but then also specifies the /p:DatabaseName and /p:TargetDatabase properties.
This has been working all day.
We are using SQL Server authentication for these databases, and I wanted to be able to specify the username and password as parameters to the script, so that they wouldn't have to be hardcoded. The only way I could see to do that was to use the /ConnectionString switch. That's what caused the failure. Even when I specify the exact same connection string as is stored in the deployment manifest, the script fails with the error "The target database schema provider could not be determined". The script:
function DeployDatabase([string] $manifestPath, [string] $password, [string] $instance, [string] $server, [string] $user)
{
$vsdbcmdPath = "c:\Program Files (x86)\Microsoft Visual Studio 10.0\VSTSDB\Deploy"
$vsdbcmd = "$vsdbcmdPath\vsdbcmd.exe"
$outputScriptPath = "$instance.sql"
$logPath = "$instance.log"
$connectionString = "Data Source=$server;User ID=$user;"
& $vsdbcmd /dd /dsp:SQL /manifest:$manifestPath /a:Deploy /p:DatabaseName=$instance /p:TargetDatabase=$instance /script:$outputScriptPath /cs:"$connectionString" |
Out-File -FilePath $logPath
}
# Actual call is omitted to protect the $server, $user and $password
Simply omitting the /cs switch allows this to succeed again. Note from the connection string, above, that it's not even necessary to specify the password in the connection string in order to cause a failure.
The target database server is running SQL Server 2008 R2, and is up to date on patches.
I'd like to know how to avoid this error, or else I'd like to know a better way to specify the username and password when deploying, without requiring the password to be in the deployment manifest in cleartext.
I am having the same issue. Did you try to set the instance name?
The SQL Instance name in the connection string could be wrong.
Also doublecheck if User Id and Password are set, if you are not using Integrated Security.

Resources