Run write-host output while SqlConnection.Open() is running - sql-server

I would like to have a trailing ..... that expands/grows while I'm running SqlConnection.Open().
I can't figure out how to do this and the output stops when i run the command SqlConnection.Open() until the connection is made then my code proceeds.
I tried a while loop, but the while content does nothing until the actual connection is established or fails, which can take 10 -15 seconds.
$waiting = ".", ".", ".", ".", ".";
Try
{
#write-host -ForegroundColor GREEN "Connecting to SQL Server: $svr"
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Server = $svr ;Database = $db; User ID = $uid ;Password = $pwd;"
while (!$SqlConnection.State -eq 'Open')
{
write-host -ForegroundColor GREEN "Connecting to SQL Server: $svr" -NoNewline
ForEach ($p in $waiting) {
Write-Host -ForegroundColor Cyan "`r$p" -NoNewLine
Start-Sleep -Milliseconds 300
}
$SqlConnection.Open()
}
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.Connection = $Global:SqlConnection
$SqlCmd.CommandText = $UsrSqlQuery
}

You have a single threaded script. Once you call the .Open method, nothing's going to happen until it returns. I think multithreading is the only way you're going to get what you want.
Here's an example of multithreading in PS:
multithreading

Related

PowerShell System.Data.SqlClient.SqlConnection shows no error but also no result

This script runs without any problems for a SQL Server connection:
[string] $connectionString = "Server=$server;Database=$database;Integrated Security = False; User ID = $uid; Password = $pwd;"
$sqlConn = New-Object System.Data.SqlClient.SqlConnection
$sqlConn.ConnectionString = $connectionString
$sqlConn.Open()
Write-Host "The connection is $($sqlConn.State)"
$command = $sqlConn.CreateCommand()
$command.CommandText = $query
$result = $command.ExecuteReader()
$sqlConn.Close();
Write-Host "The connection is $($sqlConn.State)"
$table = new-object “System.Data.DataTable”
$table.Load($result)
But only with that result
The connection is Open
The connection is Closed
I have tried many proper SQL queries which run in Management Studio without any problems. Any hint how to properly execute and maybe check the SQL connection?
The $result variable is a SqlDataReader. You need to leave the connection open when loading the data table from the reader:
$sqlConn.Open()
Write-Host "The connection is $($sqlConn.State)"
$command = $sqlConn.CreateCommand()
$command.CommandText = $query
$table = new-object “System.Data.DataTable”
$result = $command.ExecuteReader()
$table.Load($result)
$sqlConn.Close();
Write-Host "The connection is $($sqlConn.State)"
Consider simplifying using a SqlDataAdapter:
$dataAdapter = New-Object System.Data.SqlClient.SqlDataAdapter($query, $connectionString)
$table = new-object “System.Data.DataTable”
$dataAdapter.Fill($table)

How do you script out SQL Server agent jobs to single or individual files

I've tried various Powershell scripts but they fail with:
The following exception occurred while trying to enumerate the collection: "An exception occurred while executing a Transact-SQL statement or batch.".
At H:\Create_SQLAgentJobSripts2.ps1:89 char:22
foreach ($job in $s.JobServer.Jobs)
~~~~~~~~~~~~~~~~~
CategoryInfo : NotSpecified: (:) [], ExtendedTypeSystemException
FullyQualifiedErrorId : ExceptionInGetEnumerator
What has gone wrong or how can I get better debugging on this error?
I executed this script:
.\Create_SQLAgentJobSripts2.ps1 .\ServerNameList.txt
Here's the script
param([String]$ServerListPath)
#write-host "Parameter: $ServerListPath"
#Load the input file into an Object array
$ServerNameList = get-content -path $ServerListPath
#Load the SQL Server SMO Assemly
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo") | Out-Null
#Create a new SqlConnection object
$objSQLConnection = New-Object System.Data.SqlClient.SqlConnection
#For each server in the array do the following.
foreach($ServerName in $ServerNameList)
{
Write-Host "Beginning with Server: $ServerName"
Try
{
$objSQLConnection.ConnectionString = "Server=$ServerName;Initial Catalog=CED_NCT_RESOURCE_TRACK;Persist Security Info=True;User ID=CEDNCTAdmin;Password=CEDNCTAdmin;"
Write-Host "Trying to connect to SQL Server instance on $ServerName..." -NoNewline
$objSQLConnection.Open() | Out-Null
Write-Host "Success."
$objSQLConnection.Close()
}
Catch
{
Write-Host -BackgroundColor Red -ForegroundColor White "Fail"
$errText = $Error[0].ToString()
if ($errText.Contains("network-related"))
{Write-Host "Connection Error. Check server name, port, firewall."}
Write-Host $errText
continue
}
# Won't be using this object again
Remove-Variable -Name objSQLConnection
#If the output folder does not exist then create it
$OutputFolder = ".\$ServerName"
if (!(Test-Path $OutputFolder))
{
write-host ("Creating directory: " + $OutputFolder)
New-Item -ItemType directory -Path $OutputFolder
}
else
{
write-host ("Directory already exists: " + $OutputFolder)
}
write-host "File: $(".\$OutputFolder\" + $($_.Name -replace '\\', '') + ".job.sql")"
# Connect to the instance using SMO
$s = new-object ('Microsoft.SqlServer.Management.Smo.Server') $ServerName
write-host ("SQL Server Edition: " + $s.Edition)
write-host ("SQL Agent ErrorLogFile: " + $s.JobServer.ErrorLogFile)
# Instantiate the Scripter object and set the base properties
$scrp = new-object ('Microsoft.SqlServer.Management.Smo.Scripter') ($ServerName)
write-host ("SCRP ToString():" + $scrp.ToString())
write-host ("Test scrp - Server: " + $scrp.Server)
#The next step is to set the properties for the script files:
$scrp.Options.ScriptDrops = $False
$scrp.Options.WithDependencies = $False
$scrp.Options.IncludeHeaders = $True
$scrp.Options.AppendToFile = $False
$scrp.Options.ToFileOnly = $True
$scrp.Options.ClusteredIndexes = $True
$scrp.Options.DriAll = $True
$scrp.Options.Indexes = $False
$scrp.Options.Triggers = $False
$scrp.Options.IncludeIfNotExists = $True
#Now, we can cycle through the jobs and create scripts for each job on the server.
# Create the script file for each job
foreach ($job in $s.JobServer.Jobs)
{
$jobname = $job.Name
write-host ("Job: " + $jobname)
$jobfilename = ($OutputFolder + "\" + $jobname + ".job.sql")
$scrp.Options.FileName = $jobfilename
write-host "Filename: $jobfilename"
#This line blows up
$scrp.Script($job)
}
}
Possibly you're not instantiating the Server object correctly. Try the following instead...
# Alternative 1: With servername and port, using Trusted Connection...
$ServerName = 'YourServerName,1433'
$ServerConnection = New-Object Microsoft.SqlServer.Management.Common.ServerConnection -ArgumentList #( $ServerName )
# Alternative 2: With an SqlConnection object
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Server=$ServerName;Initial Catalog=CED_NCT_RESOURCE_TRACK;Persist Security Info=True;User ID=CEDNCTAdmin;Password=CEDNCTAdmin;"
$SqlConnection.Open() | Out-Null
$ServerConnection = New-Object Microsoft.SqlServer.Management.Common.ServerConnection -ArgumentList #( $SqlConnection )
# Then...
$Server = New-Object Microsoft.SqlServer.Management.Smo.Server -ArgumentList #( $ServerConnection )
$Server.JobServer.Jobs | ForEach-Object {
Write-Host "Job: $($_.Name)"
}

Monitor a folder using Powershell and check it against a SQL table and send an email out if file doesn't exists in the last 6 hours

I am using powershell to check a folder and when a file gets added to the folder, it queries a sql table and if there hasn't been a file added in the last 6 hours then it will send an email to several people letting them know that the file was copied/uploaded to that folder.
I can send the email when a file gets added, but when I added the code to check the SQL table, it stopped working. Can someone help me figure out the rest of this script?
[code]
# make sure you adjust this to point to the folder you want to monitor
$PathToMonitor = "U:\temp\test"
explorer $PathToMonitor
$FileSystemWatcher = New-Object System.IO.FileSystemWatcher
$FileSystemWatcher.Path = $PathToMonitor
$FileSystemWatcher.IncludeSubdirectories = $true
# make sure the watcher emits events
$FileSystemWatcher.EnableRaisingEvents = $true
# define the code that should execute when a file change is detected
$Action = {
$details = $event.SourceEventArgs
$Name = $details.Name
$FullPath = $details.FullPath
$OldFullPath = $details.OldFullPath
$OldName = $details.OldName
$ChangeType = $details.ChangeType
$Timestamp = $event.TimeGenerated
$JustPath = Path.GetDirectoryName($FullPath)
# SQL Work ---------------------------------------------------------------------
$Server = 'SQL01'
$Database = 'FTP_Upload'
$Connection = New-Object System.Data.SQLClient.SQLConnection
$Connection.ConnectionString = "server='$Server';database='$Database';trusted_connection=true;"
$Connection.Open()
$Command = New-Object System.Data.SQLClient.SQLCommand
$Command.Connection = $Connection
$text = "{0} was {1} at {2}" -f $FullPath, $ChangeType, $Timestamp
$sql = "IF Not Exists (Select 1 From Transmit Where DateDiff(IsNull(TimeGenerated, '01/01/2020 01:00:00 PM'), '$Timestamp') < 6 ) AND PathOnly = '$JustPath' )
BEGIN
Insert Transmit(FullPath, PathOnly, TimeGenerated)
Values('$FullPath', '$JustPath', '$Timestamp')
END "
Write-Host ""
Write-Host $text -ForegroundColor Green
Write-Host $sql
# you can also execute code based on change type here
switch ($ChangeType)
{
'Created' {
# Check SQL to see if there has been a file ftp'd in the last 6 hours ------
$Command.CommandText = $sql
$Command.ExecuteReader()
$Connection.Close()
# Send Email ---------------------------------
$EmailFrom = “email1#domain1.com”
$EmailTo = “email2#domain2.com, email3#domain3.com”
$Subject = “FTP Notification”
$Body = $text
$SMTPServer = “smtp.office365.com”
$SMTPClient = New-Object Net.Mail.SmtpClient($SmtpServer, 587)
$SMTPClient.EnableSsl = $true
$SMTPClient.Credentials = New-Object System.Net.NetworkCredential(“email1#domain1.com”, “password”);
$SMTPClient.Send($EmailFrom, $EmailTo, $Subject, $Body)
Start-Sleep -Seconds 5
$SMTPClient.Dispose()
# this executes only when a file was renamed
$text = "File {0} was Created" -f $FullPath
Write-Host $text -ForegroundColor Yellow
}
default { Write-Host $_ -ForegroundColor Red -BackgroundColor White }
}
}
# add event handlers
$handlers = . {
Register-ObjectEvent -InputObject $FileSystemWatcher -EventName Changed -Action $Action -SourceIdentifier FSChange
Register-ObjectEvent -InputObject $FileSystemWatcher -EventName Created -Action $Action -SourceIdentifier FSCreate
Register-ObjectEvent -InputObject $FileSystemWatcher -EventName Deleted -Action $Action -SourceIdentifier FSDelete
Register-ObjectEvent -InputObject $FileSystemWatcher -EventName Renamed -Action $Action -SourceIdentifier FSRename
}
Write-Host "Watching for changes to $PathToMonitor"
try
{
do
{
Wait-Event -Timeout 1
Write-Host "." -NoNewline
} while ($true)
}
finally
{
# this gets executed when user presses CTRL+C
# remove the event handlers
Unregister-Event -SourceIdentifier FSChange
Unregister-Event -SourceIdentifier FSCreate
Unregister-Event -SourceIdentifier FSDelete
Unregister-Event -SourceIdentifier FSRename
# remove background jobs
$handlers | Remove-Job
# remove filesystemwatcher
$FileSystemWatcher.EnableRaisingEvents = $false
$FileSystemWatcher.Dispose()
"Event Handler disabled."
}
[/code]

Powershell "The Microsoft.ACE.OLEDB.12.0 Provider is not registered on the local machine

i am running a powershell program to insert the data from Excel to database:
param([int]$StepNumber=1)
$invocation = (Get-Variable MyInvocation).Value
$directorypath = Split-Path $invocation.MyCommand.Path
$datestr=$(Get-Date).ToString("yyyy-MM-dd")
$url="http://www.dtcc.com/~/media/Files/Downloads/client-center/NSCC/NSCC-MPID-Directory.xls"
$destination = $directorypath +"/NSCC-MPID-Directory"+"_"+$datestr+".xls"
## if destination does not exsist, creat a new one.
if (!(Test-Path $destination) ) {
New-Item $destination -type file -force
}
$client = new-object System.Net.WebClient
$client.DownloadFile( $url, $destination)
$filepath = $destination
$excelConnection = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=$filepath;Extended Properties='Excel 12.0 Xml;HDR=YES;IMEX=1'"
$sqlConnection = 'Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=SIAC;Data Source=database1;'
$excelQuery = 'select * from [OTC$]'
$tablename = 'NSCC_MPID_OTC'
Try {
$conn = New-Object System.Data.OleDb.OleDbConnection($excelConnection)
$conn.open()
$cmd = $conn.CreateCommand()
$cmd.CommandText = $excelQuery
$rdr = $cmd.ExecuteReader()
$sqlbc = New-Object System.Data.SqlClient.SqlBulkCopy($sqlConnection, [System.Data.SqlClient.SqlBulkCopyOptions]::TableLock)
$sqlbc.DestinationTableName = $tableName
#$sqlbc.Batchsize = 1000
#$sqlbc.BulkCopyTimeout = 60
#$sqlbc.EnableStreaming=$true
# add all columns - you can add as few as you like.
for ($i = 0; $i -lt $rdr.FieldCount; $i++) {
$fname = $rdr.GetName($i)
Write-Host $fname -ForegroundColor green
[void]$sqlbc.ColumnMappings.Add($fname, $fname)
}
# write all of the data to the table
Try {
$sqlbc.WriteToServer($rdr)
} Catch {
Write-Host "$_" -Fore red -back white
} Finally {
$rdr.Close()
}
} Catch {
Write-Host "$_" -ForegroundColor red
}
When I run the program which needs to use the OLE DB Provider, it gives the error: "The Microsoft.ACE.OLEDB.12.0 Provider is not registered on the local machine. Can someone tell me why it can not find the provider when it is already there.
I tried dowloading Microsoft Database Access Engine 2017 and Microsoft Database Access Engine 2010 Redistributible and still have the same error. I also restart my computer and try run as administrator.... No thing worked for me.
Could anyone help me with this? Thanks.
The OLEDB provider are 32-bit and 64-bit aware.
If only 32-bit provider is installed (when installed Office is 32-bit),
The script must be run using powershell (x86).
To do that, run this command:
& "$Env:WINDIR\SysWOW64\WindowsPowerShell\v1.0\powershell.exe" -File <script_file_path>
To list installed OLEDB, see List all Ole DB Providers in Powershell.
The output may be different when run in Powershell (x64) and Powershell (x86).
function Get-OledbRegistered
{
[CmdletBinding()]
[OutputType([System.Collections.Generic.List[PSObject]])]
param ()
Process
{
$list = New-Object ([System.Collections.Generic.List[PSObject]])
foreach ($provider in [System.Data.OleDb.OleDbEnumerator]::GetRootEnumerator())
{
$v = New-Object PSObject
for ($i = 0; $i -lt $provider.FieldCount; $i++)
{
Add-Member -in $v NoteProperty $provider.GetName($i) $provider.GetValue($i)
}
$list.Add($v)
}
return $list
}
}
Get-OledbRegistered

Powershell DBCC CheckDB from Powershell

I was wondering if anyone could help with the following code shown. I'm basically trying to get this code re-hashed if possible to allow me to run it against a set of server names supplied in a text file named "servers.txt".
The DBCC should be run by the PS script and run against all DB for that servername. I'm not up to speed enough with PS to understand how to do this for this script.
How to change it allow to plug in values instead of being hardcoded for each servername?
I've read a bit around this and looked at the Invoke-Sql command which I believe is a SQL 2008 extension to PS.
Unfortunately the PS environment is from a SQL 2005 box and I dont have the power to get this moved so dont think ill be able to use invoke
Please see the original code and then my experiment to try and get it to run using invoke.
$ScriptName = $myInvocation.MyCommand.Name
[void][reflection.assembly]::LoadWithPartialName("System.Data.SqlClient")
$ConnString = "Server=DB-OCC05;Integrated Security=SSPI;Database=master;Application Name=$ScriptName"
$MasterConn = new-object ('System.Data.SqlClient.SqlConnection') $ConnString
$MasterCmd = new-object System.Data.SqlClient.SqlCommand
$MasterCmd.Connection = $MasterConn
$SqlDBCC = "DBCC CHECKDB(master) WITH TABLERESULTS"
$MasterCmd.CommandText = $SqlDBCC
$MasterConn.Open()
$Rset = $MasterCmd.ExecuteReader()
If ($Rset.HasRows -eq $true) {
While ($Rset.Read()) {
$line = $Rset["MessageText"]
If ($Rset["Level"] -gt 10) {
Write-Host $line -backgroundcolor Yellow -foregroundcolor Red
} else {
Write-Host $line
}
}
$Rset.Close()
}
$MasterConn.Close()
And then my test running from SQL 2005 environment:
Invoke-Sqlcmd -Query "SELECT GETDATE() AS TimeOfQuery;" -ServerInstance "MyComputer\MyInstance"
And also tried this test:
gc "C:\Powershell\Servers.txt" | foreach-object {Invoke-Sqlcmd "DBCC checkdb;" -ServerInstance "$_\MyInstance"}
But the above test runs didnt work cause of the:
The term 'Invoke-Sqlcmd' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path.
A few modifications to your script. Everything is basically the same except for the connection string and the few lines at the bottom for loading your servers.txt file (a text file with one line per instance) and enumerating its content:
function Execute-DBCC
{
param (
[parameter(Mandatory = $true)][string]$serverInstance
)
$connString = "Server=$serverInstance;Integrated Security=SSPI;Database=master;Application Name=$ScriptName"
$masterConn = new-object ('System.Data.SqlClient.SqlConnection') $connString
$masterCmd = new-object System.Data.SqlClient.SqlCommand
$masterCmd.Connection = $masterConn
$masterCmd.CommandText = "EXECUTE master.sys.sp_MSforeachdb 'DBCC CHECKDB([?]) WITH TABLERESULTS'"
$masterConn.Open()
$reader = $masterCmd.ExecuteReader()
if ($reader.HasRows -eq $true)
{
while ($reader.Read())
{
$messageText = $reader["MessageText"]
if ($reader["Level"] -gt 10)
{ Write-Host $messageText -backgroundcolor Yellow -foregroundcolor Red }
else
{ Write-Host $messageText }
}
$reader.Close()
}
$masterConn.Close()
}
[void][reflection.assembly]::LoadWithPartialName("System.Data.SqlClient")
$servers = #(Get-Content ".\servers.txt")
$servers | %{
Execute-DBCC $_
}

Resources