Update two different SNMP OID values through Powershell - sql

I'm trying to update info from 4 ups's with two different OID values through powershell. I can update one but when I try to update both values I receive an error. I figured out why it's not updating the values by inserting the values onto a new table. When it inserts/updates the values the script enters both values into the table column instead of having one value for temp and one value for battery. My question is how can I update both values if there is a way. Below is my loop I am running.
# If success go call func SNMP
if($ping_reply.status -eq "Success"){
try {
$frm_snmp = Invoke-SNMPget $ups_ip $oidTemp, $oidBatload "public"
} catch {
Write-Host "$ups_ip SNMP Get error: $_"
Return null
}
# if the data doesn't match record update ups_data
if([String]::IsNullOrWhiteSpace($frm_snmp.Data)){
Write-Host "Given string is NULL"
}else{
if(($ups_temp -and $battery_load -ne $frm_snmp.Data)) {
Write-Output "database update needed"
Write-Output $ups_ip, $ups_upsname $frm_snmp.Data
$new_temp = $frm_snmp.Data
$new_battery_load = $frm_snmp.Data
$update_con = New-Object System.Data.SqlClient.SqlConnection
$update_con.ConnectionString = "connection info"
$update_con.Open()
$SQLstmt = "update ups_data set temp = '$new_temp', batteryload = '$new_battery_load' where ip_address = '$ups_ip'"
$up_cmd = $update_con.CreateCommand()
$up_cmd.CommandText = $SQLstmt
$up_cmd.ExecuteNonQuery()
$update_con.Close()

This is the working code below
# If success go call func SNMP
if($ping_reply.status -eq "Success"){
try {
$frm_snmp = Invoke-SNMPget $ups_ip $oidTemp, $oidBatload "public"
} catch {
Write-Host "$ups_ip SNMP Get error: $_"
Return null
}
# if the data doesn't match record update ups_data
if([String]::IsNullOrWhiteSpace($frm_snmp.Data)){
Write-Host "Given string is NULL"
}else{
if(($ups_temp -and $battery_load -ne $frm_snmp.Data)) {
Write-Output "database update needed"
Write-Output $ups_ip, $ups_upsname $frm_snmp.Data
$new_temp = $frm_snmp.Data
$new_battery_load = $frm_snmp.Data
$update_con = New-Object System.Data.SqlClient.SqlConnection
$update_con.ConnectionString = "connection info"
$update_con.Open()
$SQLstmt = "update ups_data set temp = '$($new_temp[0])', batteryload = '$($new_battery_load[1])' where ip_address = '$ups_ip'"
$up_cmd = $update_con.CreateCommand()
$up_cmd.CommandText = $SQLstmt
$up_cmd.ExecuteNonQuery()
$update_con.Close()

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!

Powershell – Ping Server Process with SQL data

I have a process that gets a list of servers from a SQL database table. From there, that list goes through a loop and does a ping test. The server and the ping test result are sent back to the same SQL database table but updates the column ‘IS_PINGABLE’ with the result.
The following Powershell script does this, but is super slow. I took part of the code from here: https://gallery.technet.microsoft.com/scriptcenter/Powershell-Script-to-ping-15e0610a and added some other steps.
If anyone has any suggestion to make this faster, better, strong, please let me know below. Many thanks in advance.
$MasterServerConnString = dbserver,1433
#**********************************************************************
#region Get Server List
#Get Server list from SQL DB Table and save to temp Powershell table
#**********************************************************************
$ping_cmd = "Set NOCOUNT ON; SELECT distinct machinename, IS_PINGABLE FROM [DB].[dbo].[TABLE] order by 1"
$ping_cn = new-object System.Data.SqlClient.SqlConnection("Data Source=$MasterServerConnString;Integrated Security=SSPI;Initial Catalog=DB");
$ping_cn.Open()
$ping_a = $ping_cn.CreateCommand()
$ping_a.CommandText = $ping_cmd
$ping_a = $ping_a.ExecuteReader()
$ping = new-object “System.Data.DataTable” "computername"
$ping.Load($ping_a)
$ping_cn.Close()
$Server = $ping.Item(0)
#**********************************************************************
#endregion
#**********************************************************************
#region Ping Sever Test
#Ping each server to see if there is connectivity. If pingable, 1. If not pingable 0.
#**********************************************************************
$PCData = foreach ($PC in $Server) {
Write-Verbose "Checking computer'$PC'"
try {
Test-Connection -ComputerName $PC -Count 2 -ErrorAction Stop | Out-Null
$Props = #{
ComputerName = $PC
Status = 1
}
New-Object -TypeName PSObject -Property $Props
} catch { # either ping failed or access denied
try {
Test-Connection -ComputerName $PC -Count 2 -ErrorAction Stop | Out-Null
$Props = #{
ComputerName = $PC
Status = 0
}
New-Object -TypeName PSObject -Property $Props
} catch {
$Props = #{
ComputerName = $PC
Status = 0
}
New-Object -TypeName PSObject -Property $Props
}
}
}
#**********************************************************************
#endregion
#**********************************************************************
#region Upload Results
#Upload the ping results for each server back to the database table
#**********************************************************************
foreach ($sv in $PCData)
{
$svr_name = $sv.ComputerName
$svr_stat = $sv.Status
$Updatequery = "
Update [DB].[dbo].[TABLE]
SET
IS_PINGABLE = $svr_stat
WHERE
MachineName = '$svr_name'
"
$A_cn = new-object System.Data.SqlClient.SqlConnection("Data Source=$MasterServerConnString;Integrated Security=SSPI;Initial Catalog=DB");
$A_cn.Open()
$command = $A_cn.CreateCommand()
$command.CommandText = $Updatequery
$result = $command.ExecuteReader()
$A_cn.Close()
}
#**********************************************************************
#endregion

write data into excel using powershell from sql table

I have a .xlsx file which was made into data table by oledb provider.Now I want to add value to that .xlsx based on the sql table data I have
(which is also converted into a csv file Book1.csv)
The sql table consists of name and notes...
Where name column is same in both .xlsx file and sql variable $sql
I want to add that close notes to f column of .xlsx file if the value of name matches with the value of sql table "A" column One I wrote below is very slow and not effective.
Any help would be highly appreciated.
$Excel = New-Object -ComObject Excel.Application
$Workbook = $Excel.Workbooks.Open('C:\Users\VIKRAM\Documents\Sample - Superstore.xlsx')
$workSheet = $Workbook.Sheets.Item(1)
$WorkSheet.Name
$Found = $WorkSheet.Cells.Find('$Data.number')
$Found.row
$Found.text
$Excel1 = New-Object -ComObject Excel.Application
$file = $Excel1.Workbooks.Open('C:\Users\VIKRAM\Documents\Book1.xlsx')
$ff=$file.Sheets.Item(1)
$ff.Name
$ff1=$ff.Range("A1").entirecolumn
$ff1.Value2
foreach ($line in $ff1.value2){
if( $found.text -eq $line)
{
Write-Host "success"
$fff=$ff1.Row
$WorkSheet.Cells.item($fff,20) =$ff.cells.item($fff,2)
}
}
Data in .xlsx file
Number Priority Comment
612721 4 - High
Data in Book1.csv
Number Clo_notes
612721 Order has been closed
I need to update clo_notes value to comment in .xlsx file if this "number" column in each file matches update the clos_notes to the corresponding column of comment
It looks like you answered my question about where "Nebraska" falls into the data.
Make sure to release any COM objects, or you'll have orphaned Excel processes.
You might try something like this. I was able to write the Clo_notes value into column 6 as you were requesting:
## function to close all com objects
function Release-Ref ($ref) {
([System.Runtime.InteropServices.Marshal]::ReleaseComObject([System.__ComObject]$ref) -gt 0)
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
}
## open Excel data
$Excel = New-Object -ComObject Excel.Application
$Workbook = $Excel.Workbooks.Open('C:\Users\51290\Documents\_temp\StackOverflowAnswers\Excel.xlsx')
$workSheet = $Workbook.Sheets.Item(1)
$WorkSheet.Name
## open SQL data
$Excel1 = New-Object -ComObject Excel.Application
$file = $Excel1.Workbooks.Open('C:\Users\51290\Documents\_temp\StackOverflowAnswers\SQL.xlsx')
$sheetSQL = $file.Sheets.Item(1)
$dataSQL = $sheetSQL.Range("A1").currentregion
$foundNumber = 0
$row_idx = 1
foreach ($row in $WorkSheet.Rows) {
"row_idx = " + $row_idx
if ($row_idx -gt 1) {
$foundNumber = $row.Cells.Item(1,1).Value2
"foundNumber = " + $foundNumber
if ($foundNumber -eq "" -or $foundNumber -eq $null) {
Break
}
foreach ($cell in $dataSQL.Cells) {
if ($cell.Row -gt 1) {
if ($cell.Column -eq 1 -and $cell.Value2 -eq $foundNumber) {
$clo_notes = $sheetSQL.Cells.Item($cell.Row, 2).Value2
Write-Host "success"
$WorkSheet.Cells.item($row_idx, 6).Value2 = $clo_notes
}
}
}
}
$row_idx++
}
$Excel.Quit()
$Excel1.Quit()
## close all object references
Release-Ref($WorkSheet)
Release-Ref($WorkBook)
Release-Ref($Excel)
Release-Ref($Excel1)

How to conditionally send email alert in perl

I have a perl script which is printing the table content from Oracle DB in HTML format.
My script will run on a daily basis , which will just email the o/p of the simple sql query (select query)
Now i want my script to stop email alert whenever record count of the table is NULL i.e no records in the table.
Here is my partial script
$retCode = executeSQL("select firstname,lastname,employee_id from employee");
if ($retCode) {
push(#HTML, "<tr><td> </td><td></td><td>");
push(#HTML, "<td></td><td></td></tr>\12");
}
push(#HTML, "</table>\12\12");
push(#HTML, "COUNT : $count\12");
&sendMail;
sub sendMail {
$sub = "sample data";
$from = 'xyz#abc.com';
$to = 'xys#abc.com';
open(MAIL, "|/usr/lib/sendmail -t");
print MAIL "From: $from \12"; print MAIL "To: $to \12";print MAIL "Cc: $Cc \12";
print MAIL "Subject: $sub \12";
print MAIL "Content-Type: text/html \12";
print MAIL "Content-Disposition:inline \12";
print MAIL #HTML;
close(MAIL);
}
sub executeSQL {
my $SQL = $_[0];
chomp($SQL);
print "$SQL\12";
my $hostname = $ENV{"ORACLE_DB"};
my $dbh = CommonFunctions::connect_DBI( $hostname, "USERNAME", "PASSWORD" )|| die "ERROR : Unable to connect to $hostname: $DBI::errstr\n\n";
my $sth = $dbh->prepare($SQL);
$sth->execute or die "EXEC ERROR $sth->errstr";
$count = 0;
while (#ary = $sth->fetchrow_array) {
$count++;
push(#HTML, "<tr>");
foreach(#ary) {
chomp($_);
push(#HTML, "<td>$_</td>");
print "$_,";
}
push(#HTML, "</tr>\12");
}
}
The solution is already there in the code. The program doesn't add table rows to the HTML body of the email if there are no rows returned from the DB query. Hence, you neeed to move the send command into that condition.
if($retCode) {
push(#HTML,"<tr><td> </td><td></td><td>");
push(#HTML,"<td></td><td></td></tr>\12");
push(#HTML,"</table>\12\12");
push(#HTML, "COUNT : $count\12");
&sendMail;
}
I think the big miss here is at the end of executeSQL where you failed to have a return clause indicating whether or not you found any rows in the query.
if (executeSQL("select firstname,lastname,employee_id from employee"))
{
push(#HTML, "<tr><td> </td><td></td><td>");
push(#HTML, "<td></td><td></td></tr>\12");
push(#HTML, "</table>\12\12");
push(#HTML, "COUNT : $count\12");
&sendMail;
}
sub sendMail {
# no changes
}
sub executeSQL {
my $SQL = shift;
print "$SQL\12";
my $hostname = $ENV{"ORACLE_DB"};
my $dbh = CommonFunctions::connect_DBI( $hostname, "USERNAME", "PASSWORD" ) ||
die "ERROR : Unable to connect to $hostname: $DBI::errstr\n\n";
my $sth = $dbh->prepare($SQL);
$sth->execute or die "EXEC ERROR $sth->errstr";
my $count = 0;
while (#ary = $sth->fetchrow_array) {
# no changes
}
$sth->finish;
$dbh->disconnect;
return $count; # this is what I think you're missing
}
That said, there is some other room for improvement, some of which has already been mentioned:
Consider passing a reference to #HTML instead of using it as a global -- loose coupling
Probably should close out your SQL -- I added the $sth->finish and $dbh->disconnect as examples
Have you looked into HTML::Table? I use it a lot, and it's a real time-saver. Creating HTML on the fly is always a last resort for me

Powershell SQL returns

I am trying to view the results of a sql query into a remote server. My issue is that on return i see the first value repeated for each of the other values.
Here is the code:
#Connect to VPN
cls
C:
cd "C:\Program Files (x86)\Cisco Systems\VPN Client"
& ".\vpnclient.exe" connect WWVPN1 user sceris pwd ******
$vendorNumber = "2130196"
$vendorName = ""
$invoiceNumber = "1362433"
$conn = New-Object System.Data.SqlClient.SqlConnection("Data Source=wwfinance; Initial Catalog=ScerIS; Integrated Security=False; uid=Peter; pwd=*****; MultipleActiveResultSets=true")
## Open DB Connection
$conn.Open()
$sqlText = "SELECT UdiValue1, UdiValue37, UdiValue38, UdiValue3
FROM ScerIS.dbo.indexedRangesView_4
WHERE (UdiValue37 like '%$vendorName%' OR UdiValue38 like '$VendorNumber') AND UdiValue3 = '$invoiceNumber'"
$cmd = New-Object System.Data.SqlClient.SqlCommand($sqlText, $conn)
$Reader = $cmd.ExecuteReader()
while ($Reader.Read()) {
$ArchiveDate = $Reader.GetValue($1)
$VendorNumber = $Reader.GetValue($2)
$VendorName = $Reader.GetValue($3)
$InvoiceNumber = $Reader.GetValue($4)
}
write-host $ArchiveDate
write-host $VendorNumber
write-host $VendorName
write-host $InvoiceNumber
$conn.close()
#Disconnect from VPN
cd "C:\Program Files (x86)\Cisco Systems\VPN Client"
& ".\vpnclient.exe" disconnect
The output will show the archive date 4 times once for each write-host. How can i successfully get the other values to display?
Sample Output
9/4/2015 12:00:00 AM
9/4/2015 12:00:00 AM
9/4/2015 12:00:00 AM
9/4/2015 12:00:00 AM
$1, $2, $3, and $4 are never defined. If you want to get the values from the first 4 columns use:
while ($Reader.Read()) {
$ArchiveDate = $Reader.GetValue(0)
$VendorNumber = $Reader.GetValue(1)
$VendorName = $Reader.GetValue(2)
$InvoiceNumber = $Reader.GetValue(3)
}