Public/Invoke-SqlCmd.ps1
function Invoke-SqlCmd { <# .SYNOPSIS Executes SQLCMD .DESCRIPTION Wrapper tp the commandline tool SQLCMD. It provides parameter validation, output and error handling. .NOTES Check https://github.com/abbgrade/PsSqlClient and https://github.com/abbgrade/PsSmo if one of them already supports your use case. They provide better PowerShell integration. .LINK https://docs.microsoft.com/de-de/sql/tools/sqlcmd-utility?view=sql-server-ver15 #> [CmdletBinding()] param( # Path to the executed SQL script file. [Parameter( Mandatory, ParameterSetName='File' )] [ValidateScript({ $_.Exists })] [Alias('File')] [System.IO.FileInfo] $InputFile, # Executed SQL script source. [Parameter( Mandatory, ParameterSetName='Command' )] [ValidateNotNullOrEmpty()] [string] $Command, # Name of the SQL Server Instance. [Parameter( Mandatory, ValueFromPipelineByPropertyName )] [Alias('DataSource')] [ValidateNotNullOrEmpty()] [string] $ServerInstance, [Parameter( ValueFromPipelineByPropertyName )] [PSCredential] $DatabaseCredential, [Parameter( ValueFromPipelineByPropertyName )] [PSCredential] $WindowsCredential, [Parameter( ValueFromPipelineByPropertyName )] [string] $AccessToken, # Name fo the SQL Database. [Parameter( ValueFromPipelineByPropertyName )] [ValidateNotNullOrEmpty()] [Alias('Database')] [string] $DatabaseName, # Minimum event severity to include in output. [ValidateRange(-1, 30)] [int] $ErrorLevel = 0, # Minimum event severity to interpret as error. [ValidateRange(-1, 30)] [int] $ErrorSeverityLevel = 10, # Flag if a error must terminate the execution. [ValidateNotNullOrEmpty()] [switch] $TerminateOnError, # Timeout in seconds for the execution. [Parameter( ValueFromPipelineByPropertyName )] [Alias('ConnectionTimeout')] [int] $Timeout, # Values for variables, used in the script. [hashtable] $Variables ) # soon... # Write-Warning 'This cmdlet is deprecated use PsSqlClient or PsSmo instead' if ( -not $Global:SqlCmd ) { throw "SqlCmd is not inizialized. Please run Initialize-SqlCmd." } $arguments = @() [string] $script = $null switch ($PSCmdlet.ParameterSetName) { File { $arguments += "-i ""$InputFile""" $script = $InputFile } Command { $arguments += "-Q ""$Command""" $script = $Command } } if ( $DatabaseName ) { $arguments += "-d ""$DatabaseName""" } $Credential = $null if ( $DatabaseCredential ) { $Credential = $DatabaseCredential } if ( $WindowsCredential ) { $Credential = $WindowsCredential } if ( $Credential ) { Write-Verbose "use credential-based authentication." $arguments += "-U $( $Credential.UserName )" if ( $Credential.GetNetworkCredential().Password ) { $arguments += "-P $( $Credential.GetNetworkCredential().Password )" } } if ( $AccessToken ) { Write-Verbose "use token-based authentication." $arguments += "-P $AccessToken" } if ( $ServerInstance ) { $arguments += "-S ""$ServerInstance""" if ( $ServerInstance.Contains( 'database.windows.net' ) -and -not $DatabaseCredential ) { Write-Verbose "use azure active directory." $arguments += "-G" # use Azure Active Directory authentication } } if ( $Timeout ) { $arguments += "-t $Timeout" } $arguments += "-X" $arguments += "-m$ErrorLevel" $arguments += "-V $ErrorSeverityLevel" if ( $TerminateOnError ) { $arguments += "-b" } foreach ( $variable in $Variables.Keys ) { $arguments += "-v $variable=""$( $Variables[$variable] )""" } #region Prepare process $process = New-Object System.Diagnostics.Process $process.StartInfo.FileName = $Global:SqlCmd $process.StartInfo.RedirectStandardError = $true $process.StartInfo.RedirectStandardOutput = $true $process.StartInfo.UseShellExecute = $false $process.StartInfo.CreateNoWindow = $true $process.StartInfo.Arguments = $arguments #endregion if ( $process.StartInfo.Arguments -notlike '*-P *' ) { Write-Verbose "$( $process.StartInfo.FileName ) $( $process.StartInfo.Arguments )" } #region Start process $outputBuffer = New-Object System.Text.StringBuilder $errorBuffer = New-Object System.Text.StringBuilder $oScripBlock = { if (! [String]::IsNullOrEmpty($EventArgs.Data)) { $Event.MessageData.AppendLine($EventArgs.Data) Write-Verbose $EventArgs.Data } } $eScripBlock = { if (! [String]::IsNullOrEmpty($EventArgs.Data)) { $Event.MessageData.AppendLine($EventArgs.Data) Write-Warning $EventArgs.Data } } try { $outputEvent = Register-ObjectEvent -InputObject $process ` -Action $oScripBlock -EventName 'OutputDataReceived' ` -MessageData $outputBuffer $errorEvent = Register-ObjectEvent -InputObject $process ` -Action $eScripBlock -EventName 'ErrorDataReceived' ` -MessageData $errorBuffer $process.Start() | Out-Null $process.BeginOutputReadLine(); $process.BeginErrorReadLine(); $timeoutMS = ($Timeout + 5 ) * 1000 if ( -not $Timeout ) { $process.WaitForExit() $returnCode = $process.ExitCode } elseif ( $process.WaitForExit( $timeoutMS ) ) { $returnCode = $process.ExitCode } elseif ( $process.HasExited ) { $returnCode = $process.ExitCode } else { Write-Verbose "Invoke-SqlCmd: Timeout $Timeout reached." $process.Kill() $returnCode = -1 } } finally { Unregister-Event -SourceIdentifier $outputEvent.Name Unregister-Event -SourceIdentifier $errorEvent.Name } #endregion #region handle result $standardOutput = $outputBuffer.ToString().Trim() $standardError = $errorBuffer.ToString().Trim() $process.Close() | Out-Null if ( $standardOutput -contains 'Timeout expired' ) { Write-Verbose "Invoke-SqlCmd: Timeout $Timeout reached." $returnCode = -1 } if ( $returnCode -ne 0 ) { Write-Verbose "Invoke-SqlCmd: '$script' Output $( $standardOutput.Length ), Error $( $standardError.Length ) chars" # Write-Verbose $standardOutput throw "SqlCmd exit code $returnCode; out: '$standardOutput'; err: '$standardError'." } #endregion } |