SQL Server database queries in Ansible - sql-server

I would like to find out if anyone in the community has used Ansible to manipulate a Microsoft SQL Server database?
We want to add a task to our environment configuration scripts written in Ansible that will INSERT rows into a SQL Server table. The rows will have parameters that are specified in our variables files.
I have been unable to locate a specific Ansible module for achieving this so would like to hear if anyone has has success in some other way?

For completeness here is the syntax of the solution used in Ansible to SELECT & INSERT for Microsoft SQL Server:
INSERT
- name: 'insert row to SQL server DB'
win_shell: "invoke-sqlcmd -username \"{{db_user}}\" -password \"{{db_pass}}\" -Query \"INSERT INTO Addresses (DoorNum,Street,Town,PostCode) VALUES ({{ item.doornum }},'{{ item.street }}','{{ item.town }}''{{ item.postcode }}')\""
SELECT
- name: 'select from SQL server DB'
win_shell: "invoke-sqlcmd -username \"{{db_user}}\" -password \"{{db_pass}}\" -Query \"SELECT ID FROM Addresses WHERE PostCode = '{{ item.postcode }}'\" | Select-Object * -ExcludeProperty ItemArray, Table, RowError, RowState, HasErrors | ConvertTo-Json"
register: response
- set_fact:
ids: "{{ response.stdout|from_json}}"
- debug:
var: ids

Ansible has db specific modules but none is available to launch queries on Microsoft SQL Server.
win_shell is your friend in this context.
How I see it from your scenario:
Create a template for your queries using your vars
Push the templated content to your server with the template module
Use win_shell to feed that file to your mssql command line for execution.
Eventually delete the pushed file.

Related

Capturing logs for .sql statements though powershell

I'm trying to figure out a good way to run insert/select/update statements into a MSSQL database through PowerShell scripting and log it thoroughly. I have managed to use Invoke-Sqlcmd which does the inserts and select statements pretty fine. But my concern is to capture the logs some where as an output file. That is when doing inserts the log should capture the no. of rows affected/inserted with the data that was inserted.
Invoke-Sqlcmd used:
PS C:\Users\moshink\Desktop\Powershell SQL> Invoke-Sqlcmd -Server $server -Database $db -Username $user -Password $pass -InputFile ".\test.sql" | Out-File -FilePath ".\test_$datetime.rpt" >> $LogFile
test.sql file query below:
use TCS_MIS
select * from tblMasterAccountType where acctType in ('0121') and intCat in ('0020')
--insert into tblMasterAccountType values ('DEP','0121','0020','PARENTHOOD ASSISTANCE ACCOUNT')
the test.sql file is called in the Invoke-Sqlcmd.
This file can be dynamic with any query going in there.
The Out-File ".\test_$datetime.rpt" does capture the select statement outputs if the data exits matching the criteria, but it will be blank if no data.
Is there something that can be ran to capture instantly when running a Insert .sql file/script? i.e. which will say 20 rows inserted and listing out the data inserted.
Basically what I'm after is a thorough logging when running any .sql scripts through PowerShell. It should capture no of rows affected with the data inserted/updated/deleted and the user who performed it.

Enumerate SQL Server services with two different versions of SQL Server present on server

I'm attempting to gather information about the SQL Server services on remote computers, using SMO thru a Powershell script. My code seems to work fine with multiple instances present on the server when they are the same SQL Server version.
My problem is that when there are two instances installed that are different versions of SQL Server, only one set of services are present in the ManagedComputer object I'm creating.
Specifically, I have an EXPRESS install of SQL Server 2008 R2, as a named instance called 'SQLEXPRESS'. The default instance of SQL Server is 2012. The below code gives the below output, which is missing the 2012 services:
PS C:\od\scripts\Powershell\ServerInventory> [void][reflection.assembly]::LoadWithPartialName("Microsoft.SqlServer.SqlWmiManagement")
PS C:\od\scripts\Powershell\ServerInventory> $s = New-Object -typeName Microsoft.SqlServer.Management.Smo.Wmi.ManagedComputer myComputer
PS C:\od\scripts\Powershell\ServerInventory> $s.services.name
MSSQL$SQLEXPRESS
SQLAgent$SQLEXPRESS
SQLBrowser
PS C:\od\scripts\Powershell\ServerInventory> $s
ConnectionSettings : Microsoft.SqlServer.Management.Smo.Wmi.WmiConnectionInfo
Services : {MSSQL$SQLEXPRESS, SQLAgent$SQLEXPRESS, SQLBrowser}
ClientProtocols : {}
ServerInstances : {MSSQLSERVER, SQLEXPRESS}
ServerAliases : {}
Urn : ManagedComputer[#Name='myComputer']
Name : myComputer
Properties : {}
UserData :
State : Existing
For the ManagedComputer object, I don't see the ability to pass it anything other than the computer name I'm running on, nothing specific to a SQL Server instance.
I'm looking for a way to gather information about both instance's services.
To search network for SQL Server instances I use this script:
clear
#get all servers with SQL Server
$servers = [System.Data.Sql.SqlDataSourceEnumerator]::Instance.GetDataSources()
#loop through them and get properties
ForEach ($comp in $servers) {
$machine = $comp.ServerName
try {
Get-WmiObject win32_Service -Computer $machine |
where {$_.DisplayName -match "SQL Server \("} |
select SystemName, DisplayName, Name, State, Status, StartMode, StartName, Caption
} catch {
Write-Host("Error retrieving data from $machine")
}
}
Output like:
SystemName : MACHINENAME
DisplayName : SQL Server (INSTANCE1)
Name : MSSQL$INSTANCE1
State : Running
Status : OK
StartMode : Auto
StartName : WORK\USSR$DBEngine
Caption : SQL Server (INSTANCE1)
SystemName : MACHINENAME
DisplayName : SQL Server (INSTANCE2)
Name : MSSQL$INSTANCE2
State : Running
Status : OK
StartMode : Auto
StartName : WORK\USSR$DBEngine
Caption : SQL Server (INSTANCE2)
Error retrieving data from NextMachineName
..etc
As you can see on MACHINENAME there is 2 instances running. We don't have any servers with 2 different versions of SQL Server, so I can not check this issue.

How to load Powershell output into a SQL Table using SSIS?

I'm trying to get all users that had been disabled in my domain and put it into a SQL Table. I'm trying to use SSIS to do that. Now that I can grab the right output and put it into a CSV file using this code:
Search-ADAccount -AccountDisabled -UsersOnly |
Select Name |
Export-CSV -Path C:\Users\hou\Downloads\Test.csv
But since I'm going to run the package in different servers and I couldn't have a fixed location to store the file and load into SQL Table. So either I'm going to use a variable in the Execute Process Task (where I run the Powershell script) to store the CSV file, or use SSIS to store the output directly in SQL table.
But I don't know neither of those. How can I do that?
Location should be define in the script, i.e:
$Path = Get-Location
"$Path\Test.csv"
#Option 1 just a Name
$Path = Get-Location ; Get-ChildItem C:\temp | Select-Object Name | Export-CSV -Path "$Path\Test.csv"
## Option 2 Name, with other property
$Path = Get-Location ; Get-ChildItem C:\temp | Select-Object Name,mode | Export-CSV -Path "$Path\Test.csv"
For one liner script , use the ";" to separate the commands.
I would suggest loading the data into SQL using PowerShell. There is a free PowerShell module from Microsoft called "sqlserver" that allows PowerShell to talk directly to SQL. You may already have it installed.
## Check if installed:
Get-InstalledModule -Name SqlServer
## If installed you can Update (you may need to run as local admin):
Update-Module -Name SqlServer
## If not installed (only need admin if you want all users to have access to this module):
Install-Module SqlServer
Once the module is installed there is a cmdlet called "Write-SqlTableData" to bulk copy data into SQL. The assumption is (1) the table already exists in SQL. (2) All the columns in the PowerShell Select match the order and datatype as they exist in the SQL table. In this case, there would be a SQL table with a single [Name] column. (3) You are using your AD credentials to access SQL, if not you will have to add credentials to the cmdlet.
The actual PowerShell code, update the variables in the quotes:
## Input Variables
$SqlServerName = ""
$SqlDatabaseName = ""
$SqlTableSchemaName = ""
$SqlTableName = ""
## Insert into SQL
Search-ADAccount -AccountDisabled -UsersOnly | Select-Object Name | Write-SqlTableData -ServerInstance $SqlServerName -DatabaseName $SqlDatabaseName -SchemaName $SqlTableSchemaName -TableName $SqlTableName -Force
As a side note, if you plan on doing a lot of PowerShell/SQL work, you may want to also install the "WFTools" module as it also has many additional SQL cmdlets.
Hope this helps.

Powershell Invoke-Sqlcmd - return multiple datasets

I'm looking for suggestions on either returning multiple datasets, or keeping the session open, with Invoke-SqlCmd?
I have a series of SQL queries that use temporary tables to generate a few related views of the data that I am sending on (via Excel) to a manager. As we work on what is required from the datasets, I am getting a little tired of cutting and pasting samples into Excel.
I thought to use Powershell to simply send the results to HTML files as output for the manager, however I ran into a couple of problems
If I put the final extracts into one SQL file, Powershell appends all of the data into a single result set (sort of a union of the tables)
If I attempt to build the temporary tables and then extract each query individually, each Invoke-Sqlcmd is a seperate session, meaning my Temporary tables get dropped.
I'm looking for suggestions on either returning multiple datasets, or keeping the session open?
Invoke-Sqlcmd -InputFile .\GenerateTimecard.sql -Variable $params | Out-Null;
#{
'Summary' = 'select * from #WeeklyTimeSummary;'
'ByDay' = 'select * from #WeeklyTimeDaily order by postdate desc;'
'ByTask' = 'select * from #WeeklyTimeEvents order by HoursSpent desc;'
'Detail' = 'select * from #WeeklyTimeDetail order by postdate desc;'
}.GetEnumerator() | ForEach-Object {
Write-Output $_.Name;
$fname = $_.Name + '.html';
Invoke-Sqlcmd -Query $_.Value | ConvertTo-Html | Out-File -Encoding ascii $fname;
};
The Description section from Get-Help Invoke-Sqlcmd says it supports GO commands so you could try running everything at once. Personally I'd use the -InputFile parameter and pipe the results to Out-File.
You can specify the ApplicationName parameter for Invoke-SqlCmd, which results in a different SQL connection.
Omitting ApplicationName will result in the temp tables getting removed the second time you call Invoke-SqlCmd.
Something like:
Invoke-SqlCmd -ApplicationName CreateTable -Query 'CREATE TABLE ##FooTable (FooKey INT)
Invoke-SqlCmd -ApplicationName SelectTable -Query 'SELECT * FROM ##FooTable'

powershell logging results to DB

How can I save the results from a Powershell command to a MS SQL DB?
For example:
I run this at a Powershell prompt: get-psdrive and it returns some results in a column view.
How can I take each element of the result and log it into a separate DB row/column?
I recommend saving the results of your command to a variable. Such as:
$drives = Get-PSDrive
The variable can be indexed like this:
First Element:
$drives[0]
Last Element:
$drives[-1]
You can iterate through each element with foreach:
foreach ($drive in $drives) {
# current drive is $drive
}
Or the ForEach-Object cmdlet:
$drives | ForEach-Object {
# current drive is $_
}
Now that you have the data to populate your table with you are ready to connect to the database and perform the database record inserts.
You can make use of the Powershell SQL server cmdlets or you can connect using .NET objects. Depending on what version of SQL server you have will drive your choice on which to use. SQL Server 2008 has Powershell cmdlets, 2005 does not. There is a wealth of information about the SQL server 2008 Powershell integration here. For SQL Server 2005 you have some different options. This question answer here provides a list of Powershell options to use with SQL Server 2005.
More Info:
When Powershell displays object information it uses a type system to selectively determine what properties of the object to display on the screen. Not all of the object's are displayed. Powershell uses XML files to determine what properties to display which are stored in the Powershell directory:
dir $PSHOME/*format* | select Name
The objects returned from Get-PsDrive are of type System.Management.Automation.PSDriveInfo. The file PowerShellCore.format.ps1xml tells the formatting engine what properties to display in the Powershell window. It just might be that these are the exact properties you are looking for however many objects have additional properties that are not displayed. For example an object of type System.IO.DirectoryInfo will not have all it's properties displayed by default. You can view the rest of the objects properties using the Get-Member cmdlet, for example:
Get-Item $env:windir | Get-Member
This will show all of the object's methods and properties. You can also view all of the object's properties using the Select-Object cmdlet using a wildcard for the property parameter:
Get-Item $env:windir | Select-Object -Property *
To access an objects properties values use the following syntax:
$objectVariable.ObjectProperty
Now that you know how to view an objects properties and access their values you'll need to use this to construct an Insert SQL statement. Here is an example using the Invoke-SqlCmd cmdlet provided with SQL Server 2008.
Invoke-Sqlcmd -ServerInstance $env:COMPUTERNAME -Database Test -Query "Insert MyTable values ('a', 'b')"
Here's an example looping through objects returned from Get-PsDrive assuming you have a table called MyTable and it has at least two columns which accept textual data:
Get-PsDrive | ForEach-Object {
$providerName = $_.Name
$providerRoot = $_.Root
Invoke-Sqlcmd -ServerInstance $env:COMPUTERNAME -Database Test -Query "Insert MyTable values ('$providerName', '$providerRoot')"
}

Resources