Wow, that title is a bit of a mouthful, let me give you a scenario to help understand what the business problem was that I ran in to today.
We have a production database running on SQL 2008 which contains a CLR procedure that accepts a reportID value, queries some data and writes out to the filesystem on a remote share. This procedure is called by multiple daily and weekly jobs to perform extracts for business partners. Yes, I know this is ugly. The database has TRUSTWORTHY on, which is a big security risk and we wanted to mitigate that risk with the minimum amount of work required.
Here’s an example of one of the jobs that makes the call in to that proc:
DECLARE @ReportID INT;
SELECT @ReportID = ReportID
FROM dbo.ReportList
WHERE BusinessPartner = 'Customer1'
AND Frequency = 'Daily';
EXEC Data2File @ReportID;
The first step to changing this was to get the CLR code out of source control and rebuild it as an executable file. This took the developer about 60 minutes. Now I had to figure out how we were going to call the executable with the appropriate ReportID.
The obvious way to call this would be to create a cmdline job step for D:ExportExeData2File.exe (the name and location of the new executable). This would be great except that it doesn’t contain the ReportID. The smart move here would be to just pass along the ReportID in the cmdline, except that we don’t know what that is for any particular report as they get deleted and added fairly frequently, we need to actually pass the results of the query along. The cmdline really wasn’t going to help here.
As is frequently the case, PowerShell came to the rescue.
All I had to do was create a PowerShell job step, run the query, pass the result into a variable and then call the executable with the variable. Sounds really simple, it took a few minutes to get it right, in the end I wound up with the following PowerShell script that runs from within a PowerShell job step:
$ErrorActionPreference = "Stop"
$Query = @"
SELECT ReportID
FROM dbo.ReportList
WHERE BusinessPartner = 'Customer1'
AND Frequency = 'Daily';
"@
$ResultSet = invoke-sqlcmd -ServerInstance MySQLServer -Database MyDatabase -Query $Query -QueryTimeout 30
[int]$RptID = $ResultSet.ReportID
Write-Output "Calling Data2File.exe with ReportID: $RptID"
& "D:ExportExeData2File.exe" $RptId
In this script I’m building the query, calling it with invoke-sqlcmd and then passing the output ReportID to the external executable.
While this is still pretty ugly and not the way that this sort of thing should be done (SSIS anyone?) it does work and more importantly it allows me to turn off the trustworthy setting on that database and improve the security on my SQL Servers.