Getting script (SQL Server object) into a variable in PowerShell - sql

I need to get a script from a urn object into a variable in order to persist in the database.
When I try to persist, the script column is null.
The part of the code that I'm stuck is:
Foreach ($tb in $db.Tables)
{
If ($tb.IsSystemObject -eq $FALSE)
{
$smoObjects.Add($tb.Urn)
######## add the scripts (obj) to table ########
$Script = Write-Output $scrp.Script($tb.Urn)
$Table = $tb
$insertquery = "INSERT INTO $tableInsertName ([VersionDate], [DBName], [TableName], [Script]) VALUES ('$DateInsert', '$db', '$Table', '$Script')"
$Command.CommandText = $insertquery
$Command.ExecuteNonQuery()
}
}
Any ideas please?
Thanks in advance

I've just found the issue.
The $Script it was null because the variable in the session was like that and used before. I killed the session, rerun the script and worked properly.
I hope it helps someone else.

Related

Data inserting to ODBC destination with powershell

I need to load data table to ODBC driver connection with powershell.
With OLEDB and SQL server we can use Bulk Copy and insert data quickly.
Is there such posibility with ODBC ?
I'm using powershell because it shoud have the best support for these kind of opperations,
but my current code doesn't utillise an of the dlls.
So my code firstly needs to create an insert statements with two for loops and iterate on every row and hold it in its memory,
and then to construct INSERT INTO with 1000 rows, and then repeat same thing.
Am i doomed to something like this ?
$Datatable = New-Object System.Data.DataTable
$tabledump= $src_cmd.ExecuteReader()
$Datatable.Load($tabledump)
foreach ($item in $Datatable.Rows) {
$f +=1
for ($i = 0; $i -lt $item.ItemArray.Length; $i++) {
$items = $item[$i] -replace "'" , "''"
$val +="'"+ $items + "',"
}
$vals += $val
if ($f % 1000 -eq 0 -or $f -eq $row_cnt) {
$values = [system.String]::Join(" ", $vals)
$values = $values.TrimEnd(",")
$cols = [system.String]::Join(",", $columns)
$postgresCommand = "Insert Into $dst_schema.$dst_table ($cols) values $values"
$dest_cmd_.CommandText = $postgresCommand
$dest_cmd_.ExecuteNonQuery()
Bad code i admit, any advice on code compositions are welcomed.
You can use Get-ODBCDSN command to retrieve the values of the ODBC connections and use it with a query
$conn.ConnectionString= "DSN=$dsn;"
$cmd = new-object System.Data.Odbc.OdbcCommand($query,$conn)
$conn.open()
$cmd.ExecuteNonQuery()
$conn.close()
https://www.andersrodland.com/working-with-odbc-connections-in-powershell/
But the ODBC provider doesnt do bulk copy
https://learn.microsoft.com/en-us/sql/relational-databases/native-client-odbc-bulk-copy-operations/performing-bulk-copy-operations-odbc?view=sql-server-ver15
I know this post is not new, but i've been fiddeling around looking for a solution and also found nothing, however this post gave me a couple of insights.
First: There is no such thing as 'Bad Code'. If it works is not bad, heck even if it didn't worked, but helped with something..
Alright, what i did is not the best solution, but i'm trying to import Active Directory data on PostgreSQL, so...
I noticed that you're trying with pgsql as well, so you can use the COPY statement.
https://www.postgresql.org/docs/9.2/sql-copy.html
https://www.postgresqltutorial.com/import-csv-file-into-posgresql-table/
In my case i used it with a csv file:
*Assuming you have installed pgsql ODBC driver
$DBConn = New-Object System.Data.Odbc.OdbcConnection
$DBConnectionString = "Driver={PostgreSQL UNICODE(x64)};Server=$ServerInstance;Port=$Port;Database=$Database;Uid=$Username;Pwd=$(ConvertFrom-SecureString -SecureString $Password);"
$DBConn.ConnectionString = $DBConnectionString
try
{
$ADFObject = #()
$ADComputers = Get-ADComputer -Filter * -SearchBase "OU=Some,OU=OrgU,OU=On,DC=Domain,DC=com" -Properties Description,DistinguishedName,Enabled,LastLogonTimestamp,modifyTimestamp,Name,ObjectGUID | Select-Object Description,DistinguishedName,Enabled,LastLogonTimestamp,modifyTimestamp,Name,ObjectGUID
foreach ($ADComputer in $ADComputers) {
switch ($ADComputer.Enabled) {
$true {
$ADEnabled = 1
}
$false {
$ADEnabled = 0
}
}
$ADFObject += [PSCustomObject] #{
ADName = $ADComputer.Name
ADInsert_Time = Get-Date
ADEnabled = $ADEnabled
ADDistinguishedName = $ADComputer.DistinguishedName
ADObjectGUID = $ADComputer.ObjectGUID
ADLastLogonTimestamp = [datetime]::FromFileTime($ADComputer.LastLogonTimestamp)
ADModifyTimestamp = $ADComputer.modifyTimestamp
ADDescription = $ADComputer.Description
}
}
$ADFObject | Export-Csv $Env:TEMP\TempPsAd.csv -Delimiter ',' -NoTypeInformation
docker cp $Env:TEMP\TempPsAd.csv postgres_docker:/media/TempPsAd.csv
$DBConn.Open()
$DBCmd = $DBConn.CreateCommand()
$DBCmd.CommandText = #"
COPY AD_Devices (ADName,ADInsert_Time,ADEnabled,ADDistinguishedName,ADObjectGUID,ADLastLogonTimestamp,ADModifyTimestamp,ADDescription)
FROM '/media/TempPsAd.csv'
DELIMITER ','
CSV HEADER
"#
$DBCmd.ExecuteReader()
$DBConn.Close()
docker exec postgres_docker rm -rf /media/TempPsAd.csv
Remove-Item $Env:TEMP\TempPsAd.csv -Force
}
catch
{
Write-Error "$($_.Exception.Message)"
continue
}
Hope it helps!
Cheers!

is there a way to capture a exact error message in ps catch block?

is there any way to capture specific error message while this call to store that error message in sql table ?
function Get-SqlData {
param([string]$serverName=$(throw 'serverName is required.'), [string]$databaseName=$(throw 'databaseName is required.'),
[string]$query=$(throw 'query is required.'))
try {
Write-Verbose "Get-SqlData serverName:$serverName databaseName:$databaseName query:$query"
$connection = new-object system.data.sqlclient.sqlconnection( "Data Source=$serverName;Initial Catalog=$databaseName;Integrated Security=SSPI;")
$adapter = new-object system.data.sqlclient.sqldataadapter ($query, $connection)
$table = new-object system.data.datatable
[void]$adapter.Fill($table) #| out-null
$table
} catch {
write-host $Server
write-host 'Connection issue'
}
}
$Query = "set nocount on; SELECT CASE WHEN Is_Clustered = 1 THEN SQLClusterName ELSE ServerName END FROM Server_Master_List WHERE Is_Monitored = 1 "
$Servers = sqlcmd -b -S XYZ-XYZ -d DBA -h -1 -Q $Query -W
$sqltbl = #()
foreach($Server in $Servers) { $sqltbl += Get-SqlData $Server 'master' $qry }
#$sqltbl
<#Insert data from Powershell variable to SQL table #>
$connectionString = "Server=$env:ComputerName;Database=DBA;Integrated Security=SSPI;"
Yes. You can tell PowerShell to only catch certain types of exceptions.
For example...
$serverName = 'SOMERANDOMSERVER'
$databaseName = 'DoesntMatter'
$query = 'SELECT 1'
try {
$connection = New-Object System.Data.SqlClient.SqlConnection ("Data Source=$serverName;Initial Catalog=$databaseName;Integrated Security=SSPI;")
$adapter = New-Object System.Data.SqlClient.SqlDataAdapter ($query, $connection)
$table = New-Object System.Data.DataTable
[void]$adapter.Fill($table)
$table
} catch [System.Data.SqlClient.SqlException] {
'CAUGHT A SQL EXCEPTION!!'
} catch {
'Caught some other type of exception'
}
However, if you want to get further into the details, you'll need to start parsing the exceptions themselves.
And that's where this leads me to ask...why do you need to do this? A query with bad syntax, a query that throws an error, an unavailable server...those will all return a SqlException. Do you plan on implementing something which handles each of these exceptions in a particular way?
Personal opinion:
Any time I see someone starting to write code in PowerShell for running SQL queries, my first question is...Are you trying to build some sort of maintenance/utility script where it's okay to utilize existing community modules? If so, you need to look up dbatools. It's a PowerShell module that is packed with cmdlets that handle all this stuff for you. For example, you've basically just written their cmdlet called Invoke-DbaQuery
Another tip...learn about advanced parameters in PowerShell. You can add various checks against parameters to ensure they are mandatory, and even include verification checks to ensure the parameter values are valid prior to executing the script. That would allow you to properly implement required parameters, and you can remove the hack you've used here.

PowerShell Script DB Query not passing all results

Good morning stackoverflow. I have a PowerShell script that is executing a SQL query against an Oracle database and then taking the results and passing them to a local shell command. It works, mostly. What is happening is some of the results are being dropped and the only significance I can see about these is that they have a couple of columns that have null values (but only 2 out of the 8 columns that are being returned). When the query is executed in sQL developer I get all expected results. This issue applies to the $eventcheck switch, the $statuscheck works fine. The Powershell script is below:
param(
[parameter(mandatory=$True)]$username,
[parameter(mandatory=$True)]$password,
$paramreport,
$paramsite,
[switch]$eventcheck,
[switch]$statuscheck
)
$qry1 = Get-Content .\vantageplus_processing.sql
$qry2 = #"
select max(TO_CHAR(VP_ACTUAL_RPT_DETAILS.ETLLOADER_OUT,'YYYYMMDDHH24MISS')) Completed
from MONITOR.VP_ACTUAL_RPT_DETAILS
where VP_ACTUAL_RPT_DETAILS.REPORTNUMBER = '$($paramreport)' and VP_ACTUAL_RPT_DETAILS.SITE_NAME = '$($paramsite)'
order by completed desc
"#
$connString = #"
Provider=OraOLEDB.Oracle;Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST="HOST")(PORT="1521"))
(CONNECT_DATA=(SERVICE_NAME="SERVICE")));User ID="$username";Password="$password"
"#
function Get-OLEDBData ($connectstring, $sql) {
$OLEDBConn = New-Object System.Data.OleDb.OleDbConnection($connectstring)
$OLEDBConn.open()
$readcmd = New-Object system.Data.OleDb.OleDbCommand($sql,$OLEDBConn)
$readcmd.CommandTimeout = '300'
$da = New-Object system.Data.OleDb.OleDbDataAdapter($readcmd)
$dt = New-Object System.Data.DataTable
[void]$da.fill($dt)
$OLEDBConn.close()
return $dt
}
if ($eventcheck)
{
$output = Get-OLEDBData $connString $qry1
ForEach ($lines in $output)
{
start-process -NoNewWindow -FilePath msend.exe -ArgumentList #"
-n bem_snmp01 -r CRITICAL -a CSG_VANTAGE_PLUS -m "The report $($lines.RPT) for site $($lines.SITE) has not been loaded by $($lines.EXPDTE)" -b "vp_reportnumber='$($lines.RPT)'; vp_sitename='$($lines.SITE)'; vp_expectedcomplete='$($lines.SIMEXPECTED)'; csg_environment='Production';"
"# # KEEP THIS TERMINATOR AT THE BEGINNING OF LINE
}
}
if ($statuscheck)
{
$output = Get-OLEDBData $connString $qry2
# $output | foreach {$_.completed}
write-host -nonewline $output.completed
}
So that you can see the data, below is a csv output from Oracle SQL Developer with the ACTUAL results to the query that is being referenced by my script. Of these results lines 4, 5, 6, 7, 8, 10 are the only ones being passed along in the ForEach loop, while the others are not even captured in the $output array. If anyone can advise of a method for getting all of the results passed along, I would appreciate it.
"SITE","RPT","LSDTE","EXPDTE","CIMEXPECTED","EXPECTED_FREQUENCY","DATE_TIMING","ETME"
"chrcse","CPHM-054","","2014/09/21 12:00:00","20140921120000","MONTHLY","1",
"chrcse","CPSM-226","","2014/09/21 12:00:00","20140921120000","MONTHLY","1",
"dsh","CPSD-176","2014/09/28 23:20:04","2014/09/30 04:00:00","20140930040000","DAILY","1",1.41637731481481481481481481481481481481
"dsh","CPSD-178","2014/09/28 23:20:11","2014/09/30 04:00:00","20140930040000","DAILY","1",1.4162962962962962962962962962962962963
"exp","CPSM-610","2014/08/22 06:42:10","2014/09/21 09:00:00","20140921090000","MONTHLY","1",39.10936342592592592592592592592592592593
"mdc","CPKD-264","2014/09/24 00:44:32","2014/09/30 04:00:00","20140930040000","DAILY","1",6.35771990740740740740740740740740740741
"nea","CPKD-264","2014/09/24 01:00:31","2014/09/30 03:00:00","20140930030000","DAILY","1",6.34662037037037037037037037037037037037
"twtla","CPOD-034","","2014/09/29 23:00:00","20140929230000","DAILY","0",
"twtla","CPPE-002","2014/09/29 02:40:35","2014/09/30 06:00:00","20140930060000","DAILY","1",1.27712962962962962962962962962962962963
"twtla","CPXX-004","","2014/09/29 23:00:00","20140929230000","DAILY","0",
It appears, actually, that somehow a comment that was in the query was causing this issue. I removed it and the results started returning normal. I have no idea why this would be the case, unless it has to do with the way the import works (does it import everything as one line?). Either way, the results are normal now.

powershell v2 - how to get process ID

I have an Application, that runs multiple instances of itself. e.g
AppName.exe instance1
AppName.exe instance2
AppName.exe instance3
Using Powershell v2 I am trying to create a simple script that given an array of AppNames and instances, it loops through them, checks if they are running, and then shuts them down.
I figured the best way to do this would be check for each instance, if found capture it's processID, and pass that to the stop-process cmdlet.
BUT, I can't figure out how to get the process id.
So far I have:
$appName = "AppName.exe"
$instance = "instance1"
$filter = "name like '%"+$appName+"%'"
$result = Get-WmiObject win32_process -Filter $filter
foreach($process in $result )
{
$desc = $process.Description
$commArr = $process.CommandLine -split"( )"
$inst = $commArr[2]
$procID = "GET PROCESS ID HERE"
if($inst -eq $instance)
{
Stop-Process $procID
}
}
Can anyone tell me where to get the process ID from please?
you can use the get-process cmdlet instead of using wmi :
$procid=get-process appname |select -expand id
$procid=(get-process appname).id
When using Get-WmiObject win32_process ..., the objects returned have an attribute named ProcessId.
So, in the question, where you have:
$procID = "GET PROCESS ID HERE"
use:
$procID = $process.ProcessId
You could also use that in the $filter assignment, e.g.
$filter = "ProcessId=1234"

PowerShell and SQL server

Currently I'm working on a PowerShell script which is extracting info about OS, hotfix and installed software.
Well, everything is working well if I want to export to txt file - everything.
But my new task is to upload this information to sql server.
So I'm creating a foreach loop to print installed software and put everything in server. PowerShell isn't showing any errors but I can't get this data into sql server.
$conn = New-Object System.Data.SqlClient.SqlConnection
$conn.ConnectionString = "Data Source=myserver; Initial Catalog=table; Integrated Security=SSPI;"
$conn.Open()
$soft = Get-WmiObject Win32_Product
$cmd = New-Object System.Data.SqlClient.SqlCommand
$cmd.Connection = $conn
$cmd = $conn.CreateCommand()
foreach ($Software in $soft){
$query = "INSERT INTO dbo.mytable (SoftName, SoftVersion) VALUES ('$($Software.Name)', $($Software.Version))" }
$cmd.CommandText = $query
$result = $cmd.ExecuteNonQuery
$conn.close()
So the idea is that when I run this script, I get all software installed on pc listed in sql server.
SoftName: SoftVersion:
Office 14.202
Sql 15
Thanks!
What do you get in $result variable when you run your script?
I believe, that you get signatures of overloaded methods with the name ExecuteNonQuery. The proper method call looks like this:
$result = $cmd.ExecuteNonQuery()
If you see errors I would recommend you to use code below to get full information about them:
$ErrorActionPreference = "Stop"
$Error.Clear()
try {
# Your code
}
catch {
$Error | Format-List * -Force
}
You are overwriting the insert command instead of appending into it. In addition, as levgen points out, you are missing method call syntax:
foreach ($Software in $soft){
$query = "INSERT INTO dbo.mytable (SoftName, SoftVersion) VALUES ('$($Software.Name)', $($Software.Version))"
} # Oops!
# Now $query contains only the last insert statement.
$cmd.CommandText = $query
$result = $cmd.ExecuteNonQuery # Should be .ExecuteNonQuery()
$conn.close()
Try something like so,
foreach ($Software in $soft){
$query += "INSERT INTO dbo.mytable (SoftName, SoftVersion) VALUES ('$($Software.Name)', $($Software.Version));"
} # Appending insert staements
# Now $query should contain lots of insert staements
$cmd.CommandText = $query
$result = $cmd.ExecuteNonQuery()
$conn.close()