Powershell SQL insert randomly fails - sql

$timelimit = (get-date).AddMinutes(-65)
$logpath = "C:\_SCRIPT_\_SCHED_\_Eventlog_to_SQL.txt"
$now = get-date
$nowstring = "{0:yyyy-MM-dd-HH:mm:ss}" -f $now
$dbServer = "myserver"
$dbDatabase = "logdb"
$dbuid = "logdbuser"
$dbpwd = "logdbpass"
$dbTableFWevents = "dbo.fwevents"
$FWEvents = Get-WinEvent -logname ForwardedEvents | where-object {$_.timecreated -ge $timelimit} | Select * | Sort-Object TimeCreated
$FWEventsstat = $FWEvents | Measure-Object
$dbConnection = New-Object System.Data.SqlClient.SqlConnection
$dbConnectionString = "Server=$dbServer;Database=$dbDatabase;Integrated Security=True;User ID=$dbuid;Password=$dbpwd;Connect Timeout=0"
$dbconnection.ConnectionString = $dbConnectionString
$dbconnection.Open()
$transaction = $dbConnection.BeginTransaction("LogParserUpload")
$nowstring + " ---START---" | out-file $logpath -Append
$nowstring + " EVENT COUNT: " +$FWEventsstat.Count | out-file $logpath -Append
foreach ($evnt in $FWEvents)
{
$Command = $dbconnection.CreateCommand()
$Command.CommandText = "INSERT INTO "+$dbDatabase+"."+$dbTableFWevents+" (Message, Id, Level, ProviderName, LogName, ProcessId, ThreadId, MachineName, UserId, TimeCreated, LevelDisplayName) VALUES (#Message, #Id, #Level, #ProviderName, #LogName, #ProcessId, #ThreadId, #MachineName, #UserId, #TimeCreated, #LevelDisplayName)";
if ([string]$evnt.message){$Command.Parameters.Add("#Message", [string]$evnt.Message);}else{$Command.Parameters.Add("#Message", [DBNull]::Value);}
if ([string]$evnt.id){$Command.Parameters.Add("#Id", [string]$evnt.id);}else{$Command.Parameters.Add("#Id", [DBNull]::Value);}
if ([string]$evnt.level){$Command.Parameters.Add("#Level", [string]$evnt.level);}else{$Command.Parameters.Add("#Level", [DBNull]::Value);}
if ([string]$evnt.providername){$Command.Parameters.Add("#ProviderName", [string]$evnt.ProviderName);}else{$Command.Parameters.Add("#ProviderName", [DBNull]::Value);}
if ([string]$evnt.logname){$Command.Parameters.Add("#LogName", [string]$evnt.LogName);}else{$Command.Parameters.Add("#LogName", [DBNull]::Value);}
if ([string]$evnt.processid){$Command.Parameters.Add("#ProcessId", [string]$evnt.ProcessId);}else{$Command.Parameters.Add("#ProcessId", [DBNull]::Value);}
if ([string]$evnt.threadid){$Command.Parameters.Add("#ThreadId", [string]$evnt.threadId);}else{$Command.Parameters.Add("#ThreadId", [DBNull]::Value);}
if ([string]$evnt.machinename){$Command.Parameters.Add("#MachineName", [string]$evnt.MachineName);}else{$Command.Parameters.Add("#MachineName", [DBNull]::Value);}
if ([string]$evnt.userid){$Command.Parameters.Add("#UserId", [string]$evnt.UserId);}else{$Command.Parameters.Add("#UserId", [DBNull]::Value);}
if ([string]$evnt.timecreated){$Command.Parameters.Add("#TimeCreated", [string]$evnt.TimeCreated);}else{$Command.Parameters.Add("#TimeCreated", [DBNull]::Value);}
if ([string]$evnt.leveldisplayname){$Command.Parameters.Add("#LevelDisplayName", [string]$evnt.LevelDisplayName);}else{$Command.Parameters.Add("#LevelDisplayName", [DBNull]::Value);}
$Command.Transaction = $transaction
$eredmenyin = $Command.ExecuteNonQuery()
$nowstring + " INSERT RESULT: " +$eredmenyin | out-file $logpath -Append
}
$eredmenytr = $transaction.Commit()
$nowstring + " TRANSACTION RESULT: " +$eredmenyin | out-file $logpath -Append
$nowstring + " ---END---" | out-file $logpath -Append
#$transaction.Rollback()
$dbconnection.Close()
Hello guys.
For historical reasons we collect a bunch of server info through Windows server Forwarded Event solution.
Forwarded Events is not easy to collect from a collector computer as logparser and other solutions hardly access it.
So I decided to collect and uplaod it with Powershell.
The script runs every 60 minutes as scheduled job, collects events backward for 65 minutes.
The script runs fine. But from time to time it does skips 1-2 lines. I do not understand why. It does not skips a whole session - but for example if in a 65 minute interval there are 5 events, it uploads 4. Next time it runns fluently. Next time it again skips from 10 event 2 randomly.
I would like to know why. But I do not understand the reason AND I have no clue how could I log the actual insert command to fix it, or create other error handling.
(As you can see I implemented transaction as well - hopefully not wrongly, and transaction gives an ok as well).
Have no Idea how to find the reason and/or source of error.

The first Column is uniqid - Allow Nulls is "no" - because its the row identifier.
Other rows are - Allow Nulls "yes".
(btw as I told the skript works - as 70-80% of the evenlogs are uploaded, but only 20-30% is missing - absolutley randomly ...)
So i doubt this is the source of the issue.
I suspect there is two way of finding the problem.
1) log the actual SQL command from $command variable - but I did not found out how can be that done.
write-host $command.parameters
only writes the content of $Command.CommandText - means no info only the #### variables in it. So I do not know, how to doublecheck if the actual INSERT is not "wrong"
2) use somehow the transaction log (or other resource) to find out if the actual INSERT command "arrives" and if did "arrived" if it was denied and for what reason. As I am not really an SQL expert this kind of magic is way beyond my knowledge (and google did not helped me out neither :( )

Related

How to query SQL Server using PowerShell

I have this code that I got from a website and it's connected to my SQL Server using window authentication but I'm not sure how can I choose a database and query some table?.
[Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo") | out-Null
$s = new-object ('Microsoft.SqlServer.Management.Smo.Server') "server instance"
$s.ConnectionContext.LoginSecure=$true
$s.Databases | select name, size, status
If I run this code, it show me a list of databases but I want to choose a database called "LitHold" and query some table from that database inside.
For SMO like you have in your question, you can run queries that return data using ExecuteWithResults() like so:
$s = New-Object Microsoft.SqlServer.Management.Smo.Server "server instance"
$db = $s.Databases.Item("master")
$query = "SELECT * FROM [master].[sys].[databases] ORDER BY [name];"
$result = $db.ExecuteWithResults($query)
# Show output
$result.Tables[0]

WHERE clause not working with blank values

In the code below I try to query an Excel file for a particular field "Username" Where there are blanks (= ''). But nothing gets returned to the data adapter.
The WHERE clause will return data for example if I specify a NOT LIKE 'thistext%', but again omits blanks.
Is there some way to better code the clause?
I have read there is an issue if fields are blank and need to be converted to DB NULL (or populated with a value) unsure how to do it.
$connection.ConnectionString = $connectstring
$connection.Open()
$objOleDbCommand.Connection = $connection
$objOleDbCommand.CommandText = "SELECT * FROM [$strSheetName] WHERE [Username] = ''"
$objOleDbAdapter.SelectCommand = $objOleDbCommand
$objOleDbAdapter.Fill($objDataTable)
$objDataTable | Export-Csv "C:\output\MyData_$dateandtime.csv" -NoTypeInformation
$connection.Close()
Try this instead to cover blanks and nulls.
Select * from [$strSheetName] where [Username] = '' OR [USERNAME] IS NULL
Note that this itself is not a powershell issue technically, you might encounter this same issue with other spreadsheets and databases.

Sql Query Generation Using bat file

I have a requirement where i need to see data for every week using a sql query.
I used query like SELECT * from table Between '27-08-2012' and '30-08-2012'.
Now my requirement is that i need a batch file that can given me this script based on user selection. Like user can give start date and end date and sql query should generate automatically.
A much less error prone way to get a date range is to use a graphical date picker
than manual user input which has to be checked for validity.
(month/day names will match your locale, not my German one)
This PowerShell script:
# Function Pick-Date
[void][System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms")
Function Pick-Date {
$Cal = new-object System.Windows.Forms.MonthCalendar
$Cal.ShowWeekNumbers = $true
$Cal.MaxSelectionCount = 10 # change this value for the max date distance
$Cal.Dock = 'Fill'
$Form = new-object Windows.Forms.Form
$Form.text = "Drag the mouse to select a date range then hit [enter]"
$Form.Size = new-object Drawing.Size #(656,620)
$btnSelect = new-object System.Windows.Forms.Button
$btnSelect.Size = "1,1"
$btnSelect.add_Click({ $Form.close() })
$Form.Controls.Add($btnSelect )
$Form.AcceptButton = $btnSelect
$Form.Controls.Add($Cal)
$Form.Add_Shown({$Form.Activate()})
[void]$Form.showdialog()
return ("SELECT * from table Between '"+
(Get-Date($Cal.SelectionStart) -format 'dd-MM-yyyy')+
"' and '"+
(Get-Date($Cal.SelectionEnd) -format 'dd-MM-yyyy')+
"'")
}
Pick-Date
Will have this output, you can save to a file.sql
PS> .\Pick-Date.ps1
SELECT * from table Between '27-08-2012' and '31-08-2012'
EDIT
This batch wrapper for the powerShell script will store the query in the variable SqlQuery
#Echo off&SetLocal EnableExtensions EnableDelayedExpansion
::Wrap Pick-Date.ps1 in same folder as batch
For /F "delims=" %%%A in (
'Powershell -NoP -NonI -NoLogo -File Pick-Date.ps1 '
) Do Set "SqlQuery=%%A"
Set SqlQuery

invoke-expression for SQLCMD -i "test.sql" and put results to a variable

I have a huge sql query where I want to run on a remote server from another server via powershell, however I am having trouble trying get the results per column per row of since test.sql produces a table with multiple table and result. I know I can query it using the
$ConnectionToServer = New-Object System.Data.SQLClient.SQLConnection
$CommandCMSQry = New-Object System.Data.SQLClient.SQLCommand
Where $CommandCMSQry contains the whole query in a string format. However, I am trying lessen the rows of the script thus I wanted to put the SQL query outside of the PS script.
$QryPath = "D:\CNA\DatabaseIntegrityCheck.sql"
invoke-expression "SQLCMD -E -S $InstanceNm -d 'master' -i $QryPath -b" | Tee-Object -Variable ResultCreateSP | Select-Object name,database_id
$ResultCreateSP
Example result:
main result
a b
1 foo1
2 foo2
and if I access the columns something with like this
write-host "a = " + $ResultCreateSP.name + "b = " + $ResultCreateSP.database_id
a = 1 b = foo1

Powershell Get-QADUser results to SQL table

I'm querying Active Directory with a string which loops through each domain controller in our system and returns a set of results. The script works great with export-csv but because we wish to retain all data in the custom info field (it contains carriage-returns) I'd like to export this directly into an SQL table.
The error reported by Powershell reads:
Exception calling "ExecuteNonQuery" with "0" argument(s): "Insert Error: Column name or >number of supplied values does not match table definition."
Which is a pretty verbose response, I've created and named the columns of each table to exactly match the output of the get-object.
Here's the output from the pipe:
SamAccountName : testuser
DisplayName : Test User (COMPANY)
info : Test Entry 1234567890
Test for output.
Entering
Multiple lines.
whenCreated : 09/11/2004 09:08:42
whenChanged : 19/07/2012 09:25:21
AccountExpires :
pwdLastSet : 13/06/2012 07:43:43
LastLogonTimestamp : 18/07/2012 15:38:35
userAccountControl : 512
Name : Test User
LastLogon :
DC : DCNAME1
And here's the code:
##--AD data output to SQL script, you need the Quest plugin!
$SamAccountName = Read-Host "Enter the username to query for last logon"
##--Query domain for all domain controllers and funnel into a forEach loop
Get-QADComputer -ComputerRole DomainController | Foreach-Object{
$dc = $_.Name
##--Query each domain controller for the user object and retrieve the LastLogon timestamp
$user = Get-QADUser -Service $dc -SamAccountName $SamAccountName -IncludedProperties info,pwdLastSet,AccountExpires,userAccountControl | Select-Object SamAccountName,displayName,info,whenCreated,whenChanged,accountExpires,pwdLastSet,lastLogonTimestamp,userAccountControl,name,LastLogon,#{n='DC';e={$dc}} |
out-host
##--Open database connection
$conn = New-Object System.Data.SqlClient.SqlConnection("Data Source=SQLSERVER; Initial Catalog=ADomain; Integrated Security=SSPI")
$conn.Open()
##--AAGH! How to grab the results of the Select-Object above?
$cmd = $conn.CreateCommand()
$cmd.CommandText ="INSERT extract VALUES ('$user')"
$cmd.ExecuteNonQuery()
##--Don't forget to close it!
$conn.Close()
Now I'm messing something up which is probably plainly obvious, any help much appreciated.
Assume the following test object:
$exampleObject = New-Object PSObject -Property #{SamAccountName="TestSAN";DisplayName="TestDN"}
$user = $exampleObject | Select-Object SamAccountName, DisplayName
##--AAGH! How to grab the results of the Select-Object above?
$commandText = "INSERT extract VALUES ('$($user.SamAccountName)','$($user.DisplayName)'"
I would also move the sql connect and close to outside of a loop and place it in a try/finally block, so it is only done once instead of for each DC entry and the Close is still called when there is an Exception during the execution of the try block. See here for reference on using a try/catch/finally block with Powershell.
here I'm using SqlServerCmdletSnapin and it's Invoke-sqlcmd. This script is using MSSQL database EMPLOYEE and table EMPLOYEE_DOMAIN. Hope it helps. Works fine for me..
Add-PSSnapin Quest.ActiveRoles.ADManagement
Add-PSSnapin SqlServerCmdletSnapin100
Add-PSSnapin SqlServerProviderSnapin100
$db_server = "10.3.18.55"
$db = "EMPLOYEE"
$table = "EMPLOYEE_DOMAIN"
$username = "import"
$pwd = "Okinawa84561"
# First, clear existing table
$sql_query_del = "DELETE FROM $table"
Invoke-Sqlcmd -ServerInstance $db_server -Database $db -Username $username -Password $pwd -Query $sql_query_del
# Get users with employeeID only and write their accountname and employeeID to DB
Get-QADUser -IncludeAllProperties | ? {$_.employeeID} | select sAMAccountName, employeeID | foreach {
$an = $_.sAMAccountName
$eid = $_.employeeID
Write-Host " sAMAccountName : $an employeeID : $eid"
$sql_query = "INSERT INTO $table (employeeID, domainName) VALUES ('$eid', '$an')"
Invoke-Sqlcmd -ServerInstance $db_server -Database $db -Username $username -Password $pwd -Query $sql_query
}