Call a stored procedure with parameters from an Azure runbook - sql-server

I'm trying to call a maintenance SP from within an Azure runbook:
inlinescript {
.........
$Cmd = New-object System.Data.SqlClient.SqlCommand
$Cmd.Connection = $Conn
$Cmd.CommandText = "EXEC [dbo].[BackupLogTable] #tableName, #olderThan"
$Cmd.Parameters.AddWithValue("#tableName", $TableName)
$Cmd.Parameters.AddWithValue("#olderThan", $OlderThan)
$Cmd.ExecuteNonQuery()
.....
}
The SP is declared like this:
alter procedure [dbo].[BackupLogTable] (
#tableName nvarchar(512),
#olderThan int
)
with execute as owner as
and I can successfully run it from SSMS under the same user my runbook uses. But when testing it in Azure portal I'm getting the following error:
Exception calling "ExecuteNonQuery" with "0" argument(s): "The
parameterized query '(#tableName nvarchar(4000),#olderThan
nvarchar(4000))EXEC [dbo].' expects the parameter '#tableName', which
was not supplied."
I tried every other variants of passing the parameters found on the net like this one:
$Cmd.CommandText = "[BackupLogTable]"
$Cmd.CommandType = [System.Data.CommandType]::StoredProcedure
$Cmd.Parameters.Add("#tableName", [System.Data.SqlDbType]::NVarChar, 512) | Out-Null
$Cmd.Parameters["#tableName"].Value = $TableName
$Cmd.Parameters.Add("#olderThan", [System.Data.SqlDbType]::Int) | Out-Null
$Cmd.Parameters["#olderThan"].Value = $OlderThan
and many others but it always fails:
Exception calling "ExecuteNonQuery" with "0" argument(s): "Procedure
or function 'BackupLogTable' expects parameter '#tableName', which
was not supplied."
What am I doing wrong?

How are you passing your parameters to your inline script?
There's some limitations; e.g. Get-AutomationVariable / AutomationCredential is not available in the InlineScript.
"The InlineScript activity runs a block of commands in a separate, non-workflow session and returns its output to the workflow. While commands in a workflow are sent to Windows Workflow Foundation for processing, commands in an InlineScript block are processed by Windows PowerShell. The activity uses the standard workflow common parameters including PSComputerName and PSCredential which allow you to specify that the code block be run on another computer or using alternate credentials."
So, there's some limitations on how to pass and get variables.
However you can pass values into the inlinescript using $Using.
E.g: InlineScript { Write-output $using:TableName }
Hopefully that'd do the trick.
See also the recommendations for inlinescript on: https://technet.microsoft.com/en-us/library/dn469257(v=sc.16).aspx#bkmk_InlineScript

Related

SQL Server migration 2008 to 2016 - raiserror syntax error

We are going to migrate from SQL Server 2008 to 2016. I am in the process of identifying errors and I get the following error in a trigger:
Incorrect syntax near '51001'.
I looked through the Raiserror documentation on the Microsoft website and it did not help. The following is the SQL. Any help would be appreciated.
IF (SELECT count(*) FROM dbo.u_sample_concrete ref, deleted
WHERE ref.lab_cd = deleted.lab_id) > 0
BEGIN
RAISERROR 51001 ''Trigger td_tblLAB on table dbo.tblLAB: Primary key values found in table dbo.u_sample_concrete (lab_cd). Delete restricted; the transaction is being rolled back.''
ROLLBACK TRANSACTION
RETURN
END
Adding to #DaleK's answer with the proper syntax, the problem RAISERRROR syntax is long obsolete. IIRC, it was deprecated 20 years ago (with the SQL Server 2000 release) and removed entirely in SQL Server 2012.
Below is a powershell script that uses the T-SQL Script DOM (also available with the Dacfx NuGet package) to identify existing T-SQL modules with invalid syntax. It won't catch problems in dynamic SQL, though.
$connectionString = "Data Source=.;Initial Catalog=YourDatabase;Integrated Security=SSPI"
try {
$query = #"
SELECT
QUOTENAME(OBJECT_SCHEMA_NAME(object_id)) + N'.' + QUOTENAME(OBJECT_NAME(object_id)) AS ObjectName
, OBJECTPROPERTY(object_id, 'ExecIsQuotedIdentOn') AS ExecIsQuotedIdentOn
, definition
FROM sys.sql_modules;
"#
Add-Type -Path "C:\Program Files (x86)\Microsoft SQL Server\140\DAC\bin\Microsoft.SqlServer.TransactSql.ScriptDom.dll"
$connection = New-Object Data.SqlClient.SqlConnection($connectionString)
$command = New-Object Data.SqlClient.SqlCommand($query, $connection)
$connection.Open()
$reader = $command.ExecuteReader()
while ($reader.Read()) {
# use TSqlParser appropriate for your SQL Server version
$parser = New-Object Microsoft.SqlServer.TransactSql.ScriptDom.TSql130Parser($reader["ExecIsQuotedIdentOn"])
$parseErrors = New-Object Collections.Generic.List[Microsoft.SqlServer.TransactSql.ScriptDom.ParseError]
$scriptReader = New-Object IO.StringReader($($reader["definition"]))
Write-Host "Parsing $($reader["ObjectName"]) ..."
[void]$parser.Parse($scriptReader, [ref]$parseErrors)
if($parseErrors.Count -ne 0) {
Write-Host "Parsing errors for object $($reader["ObjectName"]): $($parseErrors | ConvertTo-Json)" -ForegroundColor Yellow
}
}
$connecton.Close()
}
catch {
throw
}
The Microsoft Documentation does indeed show 4 things wrong with your statement.
The parameters must be inside brackets raiserror().
4 parameters are expected when msd_id (51001) is used - you are providing 2.
Parameters should be separated by commas ,.
You are double quoting the string, when it should be single quoted.
RAISERROR (51001, -1,- 1, 'Trigger td_tblLAB on table dbo.tblLAB: Primary key values found in table dbo.u_sample_concrete (lab_cd). Delete restricted; the transaction is being rolled back.')
Note: its best practice now to use throw rather than raiserror.

How to use ApplicationIntent=ReadOnly inside my powershell code in SQL command

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

Powershell error setting Environment variable for an SSIS package execution

My Powershell script executes an SSIS package, but first over-rides an Environment variable. The Alter method on the EnvironmentInfo object fails with a generic error message: "Operation 'Alter' on object [EnvironmentInfo[#Name='MyVariable']' failed during execution."
I also tried removing the environment variable and changing the Project parameter, but received the same error on the Alter method for the Project object.
I suspect this is either 1) a shortcoming of using the 32-bit version of SQL Server 2012, or 2) a permissions issue.
I've made sure the executing Windows Account has full privileges on the SSISDB database and the SSIS Catalog project, and the child folder, environment, etc.
Any ideas on the error or how I can get more details? I don't see anything in the Windows Event Logs.
Here's my code:
# Variables
$ServerInstance = "MyServer"
$32bitSQLServer = "false" #use #null for 32-bit
$SSISNamespace = "Microsoft.SqlServer.Management.IntegrationServices"
$FolderName = "MyFolder"
$ProjectName = "MyProject"
$PackageName = "MyPackage.dtsx"
$EnvironmentName = "MyEnvironment"
$VariableName = "MyVariable"
$VariableValue = Read-Host "What is the new environment variable value? "
# Create a connection to the server - Have to use Windows Authentication in order to Execute the Package
$sqlConnectionString = `
"Data Source=" + $ServerInstance + ";Initial Catalog=master;Integrated Security=SSPI;"
$sqlConnection = New-Object System.Data.SqlClient.SqlConnection $sqlConnectionString
# Create the Integration Services object
[Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Management.IntegrationServices")
$integrationServices = New-Object $SSISNamespace".IntegrationServices" $sqlConnection
# Get the Integration Services catalog
$catalog = $integrationServices.Catalogs["SSISDB"]
# Get the folder
$folder = $catalog.Folders[$FolderName]
# Get the project
$project = $folder.Projects[$ProjectName]
# Get the environment
$environment = $folder.Environments[$EnvironmentName]
# Get the environment reference
$environmentReference = $project.References.Item($EnvironmentName, $FolderName)
$environmentReference.Refresh()
# Get the package
$package = $project.Packages[$PackageName]
# Set the Environment Variable
$environment.Variables[$VariableName].Value = $VariableValue
$environment.Alter()
# Execute the package
Write-Host "Running Package " $PackageName "..."
$result = $package.Execute($32bitSQLServer, $environmentReference)
# Alternate approach, also not working
# $project.Parameters[$VariableName].Set([Microsoft.SqlServer.Management.IntegrationServices.ParameterInfo+ParameterValueType]::Literal,$VariableValue)
# $project.Alter()
# $result = $package.Execute($32bitSQLServer, $environmentReference)
Write-Host "Done."
Just alter the Parameter on the package and don't worry about the environment variable. I am sure it doesn't change the value stored in the package on the server, just the object held in your$package variable. Something like this:
$package.Parameters[$VariableName].Set([Microsoft.SqlServer.Management.IntegrationServices.ParameterInfo+ParameterValueType]::Literal, $VariableValue)
$package.Alter()
$execution = $integrationServices.Catalogs['SSISDB'].Executions[$package.Execute($false, ($package.Parent.References[$package_name, $folder_Name]))]
do {
$execution.Refresh()
} until ($execution.Completed)
So with some digging I found the answer to my error. Turned out I had multiple versions (11.0.0.0, 12.0.0.0, 13.0.0.0) of the assembly Microsoft.SqlServer.Management.IntegrationServices in my GAC (Windows\assembly). Examining the schema of the SSISDB catalog, it was version 12.0.5000.0, which meant I needed the 12.0.0.0 version of the assembly. The code I was using:
[Reflection.Assembly]::LoadWithPartialName( "Microsoft.SqlServer.Management.IntegrationServices")
was loading the wrong version (probably 13.0.0.0), so I needed to explicitly load the assembly version matching this installation of SSIS, which was 12.0.0.0:
[System.Reflection.Assembly]::Load("Microsoft.SqlServer.Management.IntegrationServices, Version=12.0.0.0, Culture=neutral, processorArchitecture=MSIL, PublicKeyToken=89845dcd8080cc91")

Unable to Get Information using PowerShell to Query Oracle

I use PowerShell to query SQL databases, and I am quite familiar with that process. However, I am now tasked with building an automated task that queries Oracle for information.
It seems straight forward: Install proper Oracle DLL's, import them into PS, execute the query much like SQL. However, this is not the case. All I get when I request information is a list called FieldCount. This seems to imply that I am able to see the information, it's just not displaying correctly. I'd like the actual values, and nothing seems to get this for me.
Thanks to anyone who knows anything about this, as my hands are tied and this is the only way I can think of to get this information from Oracle on a scheduled basis. I am not the Oracle admin, I only have read access to this view.
function Get-OracleData($cmdText){
Add-Type -Path 'C:\app\client\username\product\12.1.0\client_1\odp.net\managed\common\Oracle.ManagedDataAccess.dll'
$username = 'username'
$password = 'password'
$con = New-Object Oracle.ManagedDataAccess.Client.OracleConnection('User Id=$username;Password=$password;Data Source=OracleServerName')
$con.Open()
$cmd = New-Object Oracle.ManagedDataAccess.Client.OracleCommand
$cmd.Connection = $con
$cmd.CommandText = $cmdText
$rdr = $cmd.ExecuteReader()
if($rdr.Read()){
return $rdr
}else{return 0}
}
Get-OracleData -cmdText '
SELECT em.employee_number,
em.last_name,
em.first_name,
em.middle_names,
em.email_address,
em.start_date,
em.term_date,
em.location_addr_line_1,
em.location_city,
em.location_work_state,
FROM CustomView em
'
Found the answer in the link below. I was able to get what I needed by inserting the below code at the line where $cmd.CommandText = $cmdText is located in my original post, and getting rid of what's below it.
$ds = New-Object system.Data.DataSet
$da = New-Object Oracle.ManagedDataAccess.Client.OracleDataAdapter($cmd)
[void]$da.fill($ds)
return $ds.Tables[0] | Select *
This returns to a variable, and I can get the first entry using $results[0], and $results[0].EMPLOYEE_NUMBER, etc.
Reference: http://poshcode.org/3965 #line55

Powershell to configure SQL Server Facets

has anyone tried configuring SQL Server Facets using powershell... i tried with below code..and i am able to find out properties of the Facets but not getting idea on how to set values to these properties.
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.Dmf') | Out-Null
[System.Reflection.Assembly]::LoadWithPartialNam('Microsoft.SQLServer.Management.Sdk.Sfc') | Out-Null
$conn = New-Object Microsoft.SQlServer.Management.Sdk.Sfc.SqlStoreConnection("server='Ramu-pc';Trusted_Connection=true")
$PolicyStore = New-Object Microsoft.SqlServer.Management.DMF.PolicyStore($conn)
$facets = [Microsoft.SqlServer.Management.Dmf.PolicyStore]::Facets | Where {$_.Name -eq 'ISurfaceAreaFacet'}
$facets | Format-Table –Auto
when i execute below command, i see different methods but i am not getting help on how to user those methods.
$Facets | gm
i need to configure below values in the above Facet:
AdHocRemoteQueriesEnabled = True
xp_cmdshell = true
While it's not exactly using what you show but here's what I use.
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SMO') | Out-Null
$server = New-Object 'Microsoft.SqlServer.Management.SMO.Server' ('Ramu-pc')
$server.Configuration.AdHocDistributedQueriesEnabled.ConfigValue = 1
$server.Configuration.XPCmdShellEnabled.ConfigValue = 1
$server.Configuration.Alter()
You can automate with normal batch and sp_configure
I hope this helps someone find an answer while automating setting up facets, it would have saved me some time.
Replace AdHocRemoteQueriesEnabled for your answer instead of my working example below "remote access".
Similar to you I was looking for a way to automate, my case was RemoteAccessEnabled instead of installing the manager on any target
machine I wanted to setup as a master. The above special keyword didn't even register in a search.
sp_configure Transact-SQL statement:
exec sp_configure "remote access", 1
also:
remote query timeout
remote proc trans
HTH someone

Resources