Task Manager - Powerhshell not launching MS Access - powershell

I have come across a very strange situation and I have not a clue as to why this bug is occuring.
In short, I have a powershell script that if I run manually works 100%
I have set this as a scheduled task in Windows Task Manager and the event launches, runs the SQL element of the powershell script so I know that the issue doesn't exist there. However, within the powershell code I have it open a specific MS-Access database and this is not working.
shorter version, - Works when not automated, automation works but not MS-Access
I am confused, I have included the code can anyone see anything untoward?
$NewFile = '\\Operations Database\Compliance\CancelledLimitOrders\LIOAnalysis.csv'
$ComplianceSQL = #"
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
DECLARE #startdate CHAR(8)
DECLARE #finaldate CHAR(8)
SELECT #startdate=CONVERT(CHAR(8),GETDATE(),112), -- get date is editable in the negative figure to provide date range for SQL
#finaldate=CONVERT(CHAR(8),GETDATE()+7,112)
select LIOORDERREF,
lioclientcode,
liodatetimeinput,
liosecurityid,
CAST(LIOPRICE1 AS MONEY) AS PRICE1,
CAST(LIOPRICE2 AS MONEY) AS PRICE2,
CAST(lioexpiryperiod AS FLOAT) AS EXPIRYDAYS,
lioexpirydatetime,
liolastupdated,
liodatetimeoflaststatus,
liofailurecount,
liotransactiontype,
liobidprice1,
liobidprice2,
liobidprice3,
lioaskprice1,
lioaskprice2,
lioaskprice3,
CAST(lioordercashamount AS FLOAT) AS LIOCASHAMOUNT,
LIOSTOCKSYMBOL,
CASE WHEN liosecurityexchange = 'XLON' THEN LTRIM(RTRIM(liostocksymbol)) + '.L'
ELSE liostocksymbol
END AS STOCKAPI,
liocurrency,
lioquoteprovider,
liosecurityexchange,
CASE WHEN lioparenttype = 'NULL' THEN 'UNKNOWN' + '-' + LIOPARENTTYPE
WHEN lioparenttype = '' THEN 'UNKNOWN' + '-' + LIOPARENTTYPE
WHEN lioparenttype = 'BO' THEN 'BULK ORDER'
WHEN lioparenttype = 'DB' THEN 'BUY DEAL AT BEST'
WHEN lioparenttype = 'DS' THEN 'SELL DEAL AT BEST'
WHEN lioparenttype = 'LB' THEN 'LIMIT BUY'
WHEN lioparenttype = 'LS' THEN 'LIMIT SELL'
WHEN lioparenttype = 'PL' THEN 'UNKNOWN' + '-' + LIOPARENTTYPE
WHEN lioparenttype = 'RB' THEN 'RISING BUY'
WHEN lioparenttype = 'RT' THEN 'UNKNOWN' + '-' + LIOPARENTTYPE
WHEN lioparenttype = 'SL' THEN 'STOP LOSS'
WHEN LIOPARENTTYPE = 'SO' THEN 'SELL ORDER'
WHEN lioparenttype = 'TS' THEN 'UNKNOWN' + '-' + LIOPARENTTYPE
ELSE 'UNKNOWN' + '-' + LIOPARENTTYPE
END AS PARENTTYPE
from xtlimitorder
where left(liolastupdated,8) = #StartDate
and left(lioorderref,2) not like 'PP'
and liostatus = 'c'--
"#
##Development Notes##
## PS Script to run once every 5 minutes
## PS will download CSV of newly cancelled trades
## On download MS-Access Utilised to 'append data' to daily trades table
#####################
## - CREDENTIALS - ##
#####################
$MISA = 'xxxxxxxx'
$MISB = 'xxxxxxxx'
$userName = 'xxxxxxxxx'
$PassWord='xxxxxxxxxxx'
$DB = 'xxxxxxxxx'
$timeout=0
###### - StopWatch - ######
$timeout2 = new-timespan -Minutes 5
$sw = [diagnostics.stopwatch]::StartNew()
## CREATE MIS CREDENTIALS ##
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection("Connection Timeout=0")
$SqlConnection.ConnectionString = "Data Source=$MISA;Initial Catalog=$DB;
Initial Catalog=$DB;User ID=$userName;Password=$PassWord;"
## - Runs Script from Set Location
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand;
$SqlCmd.CommandTimeout=$timeout;
$SqlCMD.CommandText = $ComplianceSQL;
$SqlCmd.Connection = $SqlConnection;
## - Extract Data and build sql data object
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter;
$SqlAdapter.SelectCommand = $SqlCmd;
$DataSet = New-Object System.Data.DataSet;
$SqlAdapter.Fill($DataSet);
$DataSetTable = $DataSet.Tables["Table"];
$DSETCOUNT = $DataSetTable.Rows.Count
IF($DSETCOUNT -GT 0){
ECHO "AA"
}
else {
echo "NULL NULL NULL"
$SqlConnection.Close()
## CREATE MIS CREDENTIALS ##
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection("Connection Timeout=0")
$SqlConnection.ConnectionString = "Data Source=$MISb;Initial Catalog=$DB;
Initial Catalog=$DB;User ID=$userName;Password=$PassWord;"
## - Runs Script from Set Location
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand;
$SqlCmd.CommandTimeout=$timeout;
$SqlCMD.CommandText = $SECTABLEQUERY;
$SqlCmd.Connection = $SqlConnection;
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter;
$SqlAdapter.SelectCommand = $SqlCmd;
$DataSet = New-Object System.Data.DataSet;
$SqlAdapter.Fill($DataSet);
$DataSetTable = $DataSet.Tables["Table"];
$DSETCOUNT = $DataSetTable.Rows.Count
IF($DSETCOUNT -GT 0){
echo "A"
}
else {
echo "NULL NULL NULL"
$SqlConnection.Close()
}
$SqlConnection.Close()
}
#Finally Write the Data to the Table
$DataSet.Tables["Table"] | Export-Csv $NewFile -NoTypeINformation
#################################################
#### Data Download Concluded #############
################################################
### CALL ACCESS FOR FIVE MINUTE UPDATE
$CommandFinal = 'FiveMinuteDataUpdate'
$COMPDB = '\\operations database\compliance\cancelledlimitorders\DBCanxLIO.accdb'
$MSACCESS = New-Object -ComObject Access.Application
### this is not being done in automation, why?
$MSACCESS.OpenCurrentDatabase($COMPDB)
#$MSACCESS.AutomationSecurity = $False
$MSACCESS.Visible = $true
$MSACCESS.DoCmd.OpenForm($CommandFinal)
#$MSACCESS.RunCommand($CommandFinal)
[System.Runtime.InteropServices.Marshal]::ReleaseComObject($MSACCESS)
#### Convert xlsx to csv file ####
$PricesToCSv = '\\Operations Database\Compliance\CancelledLimitOrders\GetCanxPriceTable.xlsx'
$PriceswbCSv = '\\Operations Database\Compliance\CancelledLimitOrders\GetCanxPriceTable.csv'
$Excel = New-Object -ComObject Excel.Application
$Excel.Visible = $true
$Excel.DisplayAlerts = $false
$PricesToCSVWB = $Excel.Workbooks.Open($PricesToCSv)
$xlcsv = 6
$WorkIt = $PricesToCSVWB.ActiveSheet
$WorkIt.SaveAs($priceswbcsv,$xlcsv)
$Excel.Quit()
[System.Runtime.InteropServices.Marshal]::ReleaseComObject($WorkIt)
[System.Runtime.InteropServices.Marshal]::ReleaseComObject($xlCsv)
[System.Runtime.InteropServices.Marshal]::ReleaseComObject($PricesToCSVWB)
[System.Runtime.InteropServices.Marshal]::ReleaseComObject($Excel)
######
### CSV Converted
##############################################
Remove-Item '\\operations database\compliance\cancelledlimitorders\array.csv'
#####
### Iterate through each line of the converted csv and obtain the quote from yahoo finance writing all output to the array.csv file
$GetCanxTradeImport = '\\operations database\compliance\cancelledlimitorders\GetCanxPriceTable.csv'
$GCTI = Import-Csv $GetCanxTradeImport -Header ('LIOORDERREF', 'lioclientcode', 'liodatetimeinput', 'liosecurityid', 'PRICE1', 'PRICE2', 'EXPIRYDAYS', 'lioexpirydatetime', 'liolastupdated', 'liodatetimeoflaststatus', 'liofailurecount', 'liotransactiontype', 'liobidprice1', 'liobidprice2', 'liobidprice3', 'lioaskprice1', 'lioaskprice2', 'lioaskprice3', 'LIOCASHAMOUNT', 'LIOSTOCKSYMBOL', 'STOCKAPI', 'liocurrency', 'lioquoteprovider', 'liosecurityexchange', 'PARENTTYPE') | SELECT -Skip 1
$IE = New-Object -ComObject internetexplorer.application
ForEach ($StockApi in $GCTI.STOCKAPI) {
$URLA = "http://download.finance.yahoo.com/d/quotes.csv?s="
$URLB = "&f=sc1ll1b2&e=.csv"
#echo $URLA, $StockApi, $URLB
$FUllURL = ($URLA+ $StockApi+ $URLB)
#$IE.navigate2($FUllURL)
#$IE.visible=$true
$OP = '\\operations database\compliance\cancelledlimitorders\array.csv'
$String = Invoke-RestMethod $FullUrl
$Data = #()
$Row = New-Object PSObject
$row | Add-Member -MemberType NoteProperty -Name "STRING" -Value $String
$Data += $Row
$Data | Export-CSv $OP -NoTypeInformation -Append
###############################################################
### Has now been written out to array.csv
###############################################################
### Array file requires importing into access to allow updating of files and percentages
}
#### this is not working when automated why??????????????
$Command3 = 'LogicUpdate'
$COMPDB = '\\operations database\compliance\cancelledlimitorders\DBCanxLIO.accdb'
$MSACCESS = New-Object -ComObject Access.Application
$MSACCESS.OpenCurrentDatabase($COMPDB)
#$MSACCESS.AutomationSecurity = $False
$MSACCESS.Visible = $true
$MSACCESS.DoCmd.OpenForm($Command3)
[System.Runtime.InteropServices.Marshal]::ReleaseComObject($MSACCESS)
## Kill resident processes
Stop-Process -Name MSACCESS
Stop-Process -Name iexplore
The automated task is simply written as
Action = Start a program
Program/Script: = C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe
Add arguments (optional) = C:\scripts\ComplianceLimitOrderTestRunTime.ps1
I had wondered if this was an execution policy issue but it is set as unrestricted.
I cannot understand why this would work as a manual execution but not as an automated execution, any one shed any light on this?

Related

Determine the number of records in an OLEDB Recordset

I stole the following code that allows me to query a csv file with via a SQL query. The code reliably outputs a table which is nice. Ultimately what I would like to do is perform an action if the results of my query is zero records.
How would I go about doing that?
$firstRowColumnNames = "Yes"
$delimiter = ","
$provider = 'Microsoft.ACE.OLEDB.16.0'
$connstring = "Provider=$provider;Data Source=$(Split-Path $csv);Extended Properties='text;HDR=$firstRowColumnNames;';"
$tablename = (Split-Path $csv -leaf).Replace(".","#")
$sql = "SELECT * from [$tablename] Where sSamAccountName='acco'"
$sql
$conn = New-Object System.Data.OleDb.OleDbconnection
$conn.ConnectionString = $connstring
$conn.Open()
$cmd = New-Object System.Data.OleDB.OleDBCommand
$cmd.Connection = $conn
$cmd.CommandText = $sql
# Load into datatable
$dt = New-Object System.Data.DataTable
$dt.Load($cmd.ExecuteReader("CloseConnection"))
#Clean up
$cmd.dispose | Out-Null; $conn.dispose | Out-Null
#Output results
$dt | Format-Table -AutoSize
Following Dai's helpful comment, on PowerShell syntax you can check this using the -not logical operator as another alternative to Rows.Count -eq 0:
$dt = [System.Data.DataTable]::new()
$dt.Columns.AddRange(#('col1','col2'))
# DataTable only has 2 columns defined but now Rows
-not $dt.Rows.Count # -not 0 or -not $null -> $true // [int]$null = 0
[bool]$dt.Rows.Count # [bool]$null or [bool]0 -> $false
# Add a new Row to the DataTable
$row = $dt.NewRow()
$row['col1'] = 'ExampleVal1'
$row['col2'] = 'ExampleVal2'
$dt.Rows.Add($row)
-not $dt.Rows.Count # not 0 or not null when negated -> $false
[bool]$dt.Rows.Count # not 0 or not null in boolean expression -> $true
$dt.Dispose()

How to do looping for checking an existing folder with PowerShell?

I want to decide which folder that I need to choose based on my data, then if I can't find it, it will show the GUI for waiting and do looping to check it. I try this code, I can find the folder, but when I can't find it once I want to show the GUI it returns some error.
This is how I checking the folder
function FIND {
Write-Host "call the function that can call the GUI.ps1 script"
$Path = "D:\Process"
Write-Host "Starting Mapping SSID and Finding Job"
$SSID_Unit = "111dddddfafafesa"
Try{
$Path_Job = (Get-Item (Get-ChildItem "$Path\*\SSID_LST" | Select-String -Pattern "$SSID_Unit").Path).Directory.FullName
$global:Result = [PSCustomObject]#{
Exists = $true
FileName = $Path_Job.FullName
Attempts = 1
}
Write-Host "Job'$($global:Result.FileName)' Exists. Found after $($global:Result.Attempts) attempts." -ForegroundColor Green
Write-Host "Continue to Assigned Job"
Pause
} Catch {
Write-Host "Waiting for the jobss"
& D:\X\Wait_GUI.ps1 -Path $Path_Job -MaxAttempts 20
Write-Host "Job not found after $($global:Result.Attempts) attempts." -ForegroundColor Red
}
}
FIND
This is the GUI
Param (
[string]$Path = '*.*',
[string]$MaxAttempts = 5
)
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.Application]::EnableVisualStyles()
# set things up for the timer
$script:nAttempts = 0
$timer = New-Object System.Windows.Forms.Timer
$timer.Interval = 1000 # 1 second
$timer.Add_Tick({
$global:Result = $null
$script:nAttempts++
$Path_Job = Get-Item -Path $Path
if ($Path_Job) {
$global:Result = [PSCustomObject]#{
Exists = $true
FileName = $Path_Job.FullName
Attempts = $script:nAttempts
}
$timer.Dispose()
$Form.Close()
}
elseif ($script:nAttempts -ge $MaxAttempts) {
$global:Result = [PSCustomObject]#{
Exists = $false
FileName = ''
Attempts = $script:nAttempts
}
$timer.Dispose()
$Form.Close()
}
})
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.Application]::EnableVisualStyles()
$Form = New-Object system.Windows.Forms.Form
$Form.ClientSize = '617,418'
$Form.text = "AutoGM"
$Form.BackColor = "#8b572a"
$Form.TopMost = $false
$Form.WindowState = 'Maximized'
$Label1 = New-Object system.Windows.Forms.Label
$Label1.text = "UNDER AUTOMATION PROCESS"
$Label1.AutoSize = $true
$Label1.width = 25
$Label1.height = 10
$Label1.Anchor = 'top,right,bottom,left'
$Label1.ForeColor = "#ffffff"
$Label1.Anchor = "None"
$Label1.TextAlign = "MiddleCenter"
$Label2 = New-Object system.Windows.Forms.Label
$Label2.text = "Waiting for the job..."
$Label2.AutoSize = $true
$Label2.width = 25
$Label2.height = 10
$Label2.ForeColor = "#ffffff"
$Label2.Anchor = "None"
$Label2.TextAlign = "MiddleCenter"
$Form.controls.AddRange(#($Label1,$Label2))
[void]$Form.Show()
# Write-Host $Form.Height
# Write-Host $Form.Width
$Label1.location = New-Object System.Drawing.Point(($Form.Width*0.35), ($Form.Height*0.4))
$Label2.location = New-Object System.Drawing.Point(($form.Width*0.43), ($Form.Height*0.5))
$L_S = (($Form.Width/2) - ($Form.Height / 2)) / 15
$Label1.Font = "Microsoft Sans Serif, $L_S, style=Bold"
$Label2.Font = "Microsoft Sans Serif, $L_S, style=Bold"
$Form.controls.AddRange(#($Label1,$Label2))
# start the timer as soon as the dialog is visible
$Form.Add_Shown({ $timer.Start() })
$Form.Visible = $false
[void]$Form.ShowDialog()
# clean up when done
$Form.Dispose()
if not found, it return this
Waiting for the jobss
Job not found after 1 attempts.
and the GUI is not shown
Ok, first of all, you are using different tests for the file and/or directory in the code and in the GUI. Furthermore, you call the GUI.ps1 file with a path set to a $null value.
I would change your code to something like this:
$Path = "D:\Process\*\SSID_LST\*" # the path to look for files
$SSID_Unit = "111dddddfafafesa" # the Search pattern to look for inside the files
function Test-FileWithGui {
[CmdletBinding()]
param(
[Parameter(Mandatory = $true, Position = 0)]
[string]$Path,
[Parameter(Mandatory = $true, Position = 2)]
[string]$Pattern,
[int]$MaxAttempts = 5
)
Write-Host "Starting Mapping SSID and Finding Job"
# set up an 'empty' $global:Result object to return on failure
$global:Result = '' | Select-Object #{Name = 'Exists'; Expression = {$false}}, FileName, Directory, #{Name = 'Attempts'; Expression = {1}}
# test if the given path is valid. If not, exit the function
if (!(Test-Path -Path $Path -PathType Container)) {
Write-Warning "Path '$Path' does not exist."
return
}
# try and find the first file that contains your search pattern
$file = Select-String -Path $Path -Pattern $Pattern -SimpleMatch -ErrorAction SilentlyContinue | Select-Object -First 1
if ($file) {
$file = Get-Item -Path $file.Path
$global:Result = [PSCustomObject]#{
Exists = $true
FileName = $file.FullName
Directory = $file.DirectoryName
Attempts = 1
}
}
else {
& "D:\GUI.ps1" -Path $Path -Pattern $Pattern -MaxAttempts $MaxAttempts
}
}
# call the function that can call the GUI.ps1 script
Test-FileWithGui -Path $Path -Pattern $SSID_Unit -MaxAttempts 20
# show the $global:Result object with all properties
$global:Result | Format-List
# check the Global result object
if ($global:Result.Exists) {
Write-Host "File '$($global:Result.FileName)' Exists. Found after $($global:Result.Attempts) attempts." -ForegroundColor Green
}
else {
Write-Host "File not found after $($global:Result.Attempts) attempts." -ForegroundColor Red
}
Next the GUI file.
As you are now searching for a file that contains some text, you need a third parameter to call this named Pattern.
Inside the GUI file we perform the exact same test as we did in the code above, using the parameter $Pattern as search string:
Param (
[string]$Path,
[string]$Pattern,
[int]$MaxAttempts = 5
)
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.Application]::EnableVisualStyles()
# set things up for the timer
$script:nAttempts = 0
$timer = New-Object System.Windows.Forms.Timer
$timer.Interval = 1000 # 1 second
$timer.Add_Tick({
$global:Result = $null
$script:nAttempts++
# use the same test as you did outside of the GUI
# try and find the first file that contains your search pattern
$file = Select-String -Path $Path -Pattern $Pattern -SimpleMatch -ErrorAction SilentlyContinue | Select-Object -First 1
if ($file) {
$file = Get-Item -Path $file.Path
$global:Result = [PSCustomObject]#{
Exists = $true
FileName = $file.FullName
Directory = $file.DirectoryName
Attempts = $script:nAttempts
}
$timer.Dispose()
$Form.Close()
}
elseif ($script:nAttempts -ge $MaxAttempts) {
$global:Result = [PSCustomObject]#{
Exists = $false
FileName = $null
Directory = $null
Attempts = $script:nAttempts
}
$script:nAttempts = 0
$timer.Dispose()
$Form.Close()
}
})
$Form = New-Object system.Windows.Forms.Form
$Form.ClientSize = '617,418'
$Form.Text = "AutoGM"
$Form.BackColor = "#8b572a"
$Form.TopMost = $true
$Form.WindowState = 'Maximized'
# I have removed $Label2 because it is easier to use
# just one label here and Dock it to Fill.
$Label1 = New-Object system.Windows.Forms.Label
$Label1.Text = "UNDER AUTOMATION PROCESS`r`n`r`nWaiting for the job..."
$Label1.AutoSize = $false
$Label1.Dock = 'Fill'
$Label1.TextAlign = "MiddleCenter"
$Label1.ForeColor = "#ffffff"
$L_S = (($Form.Width/2) - ($Form.Height / 2)) / 10
$Label1.Font = "Microsoft Sans Serif, $L_S, style=Bold"
$Form.controls.Add($Label1)
# start the timer as soon as the dialog is visible
$Form.Add_Shown({ $timer.Start() })
[void]$Form.ShowDialog()
# clean up when done
$Form.Dispose()
The results during testing came out like below
If the file was found within the set MaxAttempts tries:
Starting Mapping SSID and Finding Job
Exists : True
FileName : D:\Process\test\SSID_LST\blah.txt
Directory : D:\Process\test\SSID_LST
Attempts : 7
File 'D:\Process\test\SSID_LST\blah.txt' Exists. Found after 7 attempts.
When the file was NOT found:
Starting Mapping SSID and Finding Job
Exists : False
FileName :
Directory :
Attempts : 20
File not found after 20 attempts.
If even the folder $Path was not found, the output is
Starting Mapping SSID and Finding Job
WARNING: Path 'D:\Process\*\SSID_LST\*' does not exist.
Exists : False
FileName :
Directory :
Attempts : 1
File not found after 1 attempts.
Hope that helps

Data type mismatch when querying a CSV with ACE OLEDB provider

I am attempting to query a CSV file using the Microsoft ACE OLEDB provider. When I add "PrctBusy > 60" to the where clause I receive the Error "Data type mismatch in criteria expression." I have searched StackOverFlow and used google to search for solutions, I see this is not an uncommon issue. From my readings it looks to be datatype issue. The data in the column PrctBusy is all numeric. I think I need to force it to be number but I have not found a solution.
Below is the code I am currently working with:
$ArrayNameUtil = "000198701258"
$CatNameUtil = "FE_DIR"
$sdLocalPath = "D:\Logs\SANData\Perf"
$InputCSV = "VMaxSANReportUtilFile.csv"
$csv = Join-Path $sdLocalPath $InputCSV
$provider = (New-Object System.Data.OleDb.OleDbEnumerator).GetElements() | Where-Object { $_.SOURCES_NAME -like "Microsoft.ACE.OLEDB.*" }
if ($provider -is [system.array]) { $provider = $provider[0].SOURCES_NAME } else { $provider = $provider.SOURCES_NAME }
$connstring = "Provider=$provider;Data Source=$(Split-Path $csv);Extended Properties='text;HDR=$firstRowColumnNames;';"
$firstRowColumnNames = "Yes"
$delimiter = ","
$tablename = (Split-Path $csv -leaf).Replace(".","#")
$conn = New-Object System.Data.OleDb.OleDbconnection
$conn.ConnectionString = $connstring
$provider = (New-Object System.Data.OleDb.OleDbEnumerator).GetElements() | Where-Object { $_.SOURCES_NAME -like "Microsoft.ACE.OLEDB.*" }
if ($provider -is [system.array]) { $provider = $provider[0].SOURCES_NAME } else { $provider = $provider.SOURCES_NAME }
$connstring = "Provider=$provider;Data Source=$(Split-Path $csv);Extended Properties='text;HDR=$firstRowColumnNames;';"
$firstRowColumnNames = "Yes"
$delimiter = ","
$tablename = (Split-Path $csv -leaf).Replace(".","#")
$conn = New-Object System.Data.OleDb.OleDbconnection
$conn.ConnectionString = $connstring
$conn.Open()
#
$sql = "SELECT TimeStamp, count(PrctBusy) AS Above60 FROM [$tablename] WHERE array = '$ArrayNameUtil' and Category like '$CatNameUtil' and PrctBusy > 60 Group by TimeStamp "
$cmd = New-Object System.Data.OleDB.OleDBCommand
$cmd.Connection = $conn
$cmd.CommandText = $sql
$dtp = New-Object System.Data.DataTable
$dtp.Load($cmd.ExecuteReader())
Because of the pointer from TessellatingHeckler to Codeproject and some follow on queries, I was lead to http://aspdotnetcodes.com/Importing_CSV_Database_Schema.ini.aspx. I found that a schema.ini file in the same directory as the CSV file could specify the data type.
The schema.ini file ended up in the following format:
[VMaxSANReportUtilFile.csv]
ColNameHeader=True
Format=CSVDelimited
Col1=Array Text Width 20
Col2=TimeStamp Text Width 20
Col3=Category Text Width 20
Col4=Instance Text Width 20
Col5=PrctBusy Short
Col6=QueUtil Short
I went through several revisions to get the data type correct for an ACE OLE DB provider. If the columns are named the names need to be in the schema.ini file.

Accessing all rows in dataset with powershell

I'm looping through a list of sql servers, querying data and creating a dataset which works great. The dataset is piped to a csv file, which has all the rows. I'm then trying to create and array from each row then email the results. The email is being sent but the contents only contain the records from the last server in the list.
ForEach ($instance in Get-Content "D:\servers\sqlservers2.txt")
{
$SQLServer = "$instance" #use Server\Instance for named SQL instances!
$SQLDBName = "msdb"
$SqlQuery = "
Select
##servername as [Server],
j.[name] AS [JobName],
run_status = CASE h.run_status
WHEN 0 THEN 'Failed'
WHEN 1 THEN 'Succeeded'
WHEN 2 THEN 'Retry'
WHEN 3 THEN 'Canceled'
WHEN 4 THEN 'In progress'
END,
h.run_date AS LastRunDate,
h.run_time AS LastRunTime
FROM sysjobhistory h
INNER JOIN sysjobs j ON h.job_id = j.job_id
WHERE j.enabled = 1
and j.name not like 'copytosan'
and j.name not like 'syspolicy%'
and j.name not like '%log%'
AND h.instance_id
IN
(SELECT MAX(h.instance_id)
FROM sysjobhistory h GROUP BY (h.job_id))
"
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Server =
$SQLServer; Database = $SQLDBName; Integrated Security = True"
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.CommandText = $SqlQuery
$SqlCmd.Connection = $SqlConnection
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SqlAdapter.SelectCommand = $SqlCmd
$DataSet = New-Object System.Data.DataSet
$SqlAdapter.Fill($DataSet)
$SqlConnection.Close()
#File is created with all rows from dataset
$DataSet.Tables[0] | Out-File D:\servers\myfiles.csv -Append
}
foreach ($line in $dataset.tables[0])
{
$body += $line
}
# (Out-String -InputObject $body -Width 100)
Send-MailMessage -To Bob#domain.com -from SQLMAIL#domain.com -Subject Test12345 -body (Out-String -InputObject $body -Width 100) -SmtpServer mail#domain.com
You need to nest your second loop. That way it will loop through the rows of each table instead of only the last table.
ForEach ($instance in Get-Content "D:\servers\sqlservers2.txt")
{
# code
foreach ($line in $dataset.tables[0])
{
$body += $line
}
}

Writing Powershell Error messages to SSIS

I have an SSIS execute process task where I am calling a PowerShell script. I found that I can get the process task to fail if the PowerShell script fails by trapping the errors like this:
trap {
$err = $_.Exception
while ( $err.InnerException )
{
$err = $err.InnerException
Write-Host $err.Message
};
exit 1
}
I would like to get the error messages to return to SSIS, but I can't seem to get it to work. It just returns the following:
The process exit code was "1" while the expected was "0".
Any help is appreciated.
Full Code:
Param(
[String]$excelPath,
[String]$serverName,
[String]$databaseName,
[String]$tableName
)
$ErrorActionPreference = 'Stop'
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SMO') | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SMO.SqlDataType') | Out-Null
Trap {
$err = $_.Exception
while ( $err.InnerException )
{
$err = $err.InnerException
Write-Host $err.Message
};
exit 1
}
$excel = New-Object -ComObject excel.application
$excel.visible = $False
$excel.displayalerts=$False
$workbook = $excel.Workbooks.Open($ExcelPath)
$workSheet = $workbook.worksheets.Item(1).name
$workbook.Close()
$excel.quit()
$excel = $null
$query = "select * from [$workSheet`$]";
$connectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=`"$excelPath`";Extended Properties=`"Excel 12.0 Xml;HDR=YES`";"
# Instantiate some objects which will be needed
$serverSMO = New-Object Microsoft.SqlServer.Management.Smo.Server($serverName)
$db = $serverSMO.Databases[$databaseName];
$newTable = New-Object Microsoft.SqlServer.Management.Smo.Table ;
$newTable.Parent = $db;
$newTable.Name = $tableName ;
$conn = New-Object System.Data.OleDb.OleDbConnection($connectionString)
$conn.open()
$cmd = New-Object System.Data.OleDb.OleDbCommand($query,$conn)
$dataAdapter = New-Object System.Data.OleDb.OleDbDataAdapter($cmd)
$dataTable = New-Object System.Data.DataTable
$dataAdapter.fill($dataTable)
$conn.close()
# Drop the table if it exists
if($db.Tables.Contains($tableName).Equals($true))
{
($db.Tables[$tableName]).Drop()
}
# Iterate the columns in the DataTable object and add dynamically named columns to the SqlServer Table object.
foreach($col in $dataTable.Columns)
{
$sqlDataType = [Microsoft.SqlServer.Management.Smo.SqlDataType]::Varchar
$dataType = New-Object Microsoft.SqlServer.Management.Smo.DataType($sqlDataType);
$dataType.MaximumLength = 1000;
$newColumn = New-Object Microsoft.SqlServer.Management.Smo.Column($newTable,$col.ColumnName,$dataType);
$newColumn.DataType = $dataType;
$newTable.Columns.Add($newColumn);
}
$newTable.Create();
#bcp data into new table
$connectionString = "Data Source=$serverName;Integrated Security=true;Initial Catalog=$databaseName;"
$bc = New-Object ("Data.SqlClient.SqlBulkCopy") $connectionString
$bc.DestinationTableName = "$tableName"
$bc.WriteToServer($dataTable)