Providers/SQL/config.ps1

#Provider Class
Class SQLProvider : ProviderBase {
    
    SQLProvider([string]$ConnectionName
                , [int]$CommandTimeout
                , [System.Data.SqlClient.SqlConnection]$Connection) {

        $this.ConnectionName = $ConnectionName
        $this.CommandTimeout = $CommandTimeout
        $this.Connection = $Connection
        
        $messages = $this.Messages
        $handler = {Param($sender, [System.Data.SqlClient.SqlInfoMessageEventArgs]$e)
            $messages.Enqueue(([SqlMessage]@{Received=(Get-Date); Message=$e.Message}))
        }.GetNewClosure()

        $this.Connection.add_InfoMessage([System.Data.SqlClient.SqlInfoMessageEventHandler]$handler)
    }

    [string] ProviderType() { return "MSSQL" }

    [PSCustomObject] ConnectionInfo() {
        return [PSCustomObject]@{
            ConnectionName = $this.ConnectionName
            ProviderType = $this.ProviderType()
            ConnectionState = $this.Connection.State
            ConnectionString = $this.Connection.ConnectionString
            ServerVersion = $this.Connection.ServerVersion
            Server = $this.Connection.DataSource
            Database = $this.Connection.Database
            CommandTimeout = $this.CommandTimeout
            HasTransaction = $this.HasTransaction()
        }
    }

    [void] ChangeDatabase([string]$DatabaseName) { $this.Connection.ChangeDatabase($DatabaseName) }

    [System.Data.DataSet] GetDataSet([System.Data.IDbCommand]$cmd, [Boolean]$ProviderTypes) {
        $ds = [System.Data.DataSet]::new()
        $da = [System.Data.SqlClient.SqlDataAdapter]::new($cmd)
        if ($ProviderTypes) {
            $da.ReturnProviderSpecificTypes = $true
        }
        Try {
            $da.Fill($ds)
            return $ds 
        }
        Catch { Throw $_ }
        Finally { $da.dispose() }
    }

    [long] BulkLoad([System.Data.IDataReader]$DataReader
                    , [string]$DestinationTable
                    , [hashtable]$ColumnMap = @{}
                    , [int]$BatchSize
                    , [int]$BatchTimeout
                    , [ScriptBlock]$Notify) {
        
        [long]$RowCount = 0
        $bcp = [System.Data.SqlClient.SqlBulkCopy]::new($this.Connection, [System.Data.SqlClient.SqlBulkCopyOptions]::KeepIdentity, $null)
        Try {
            $bcp.DestinationTableName = $DestinationTable
            $bcp.BatchSize = $BatchSize
            $bcp.BulkCopyTimeout = $BatchTimeout
            $bcp.EnableStreaming = $true

            If($ColumnMap -and $ColumnMap.Count -gt 0) {
                ForEach ($de in $ColumnMap.GetEnumerator()) {
                    $bcp.ColumnMappings.Add($de.Key, $de.Value)
                }
            }
            
            If($Notify) {
                $bcp.NotifyAfter = $BatchSize
                $bcp.add_SqlRowsCopied({
                    Param($sender, [System.Data.SqlClient.SqlRowsCopiedEventArgs]$e)
                    $Notify.Invoke($e.RowsCopied)
                })
            }
            
            $RowCount -= $this.GetScalar("SELECT COUNT(1) FROM $DestinationTable", 30, @{})
            $bcp.WriteToServer($DataReader)
            $RowCount += $this.GetScalar("SELECT COUNT(1) FROM $DestinationTable", 30, @{})
        }
        Finally {
            $bcp.Close()
            $bcp.Dispose()
            $DataReader.Close()
            $DataReader.Dispose()
            $this.Update("IF EXISTS(SELECT 1 FROM sys.columns WHERE object_id = OBJECT_ID('$DestinationTable') AND is_identity = 1) SET IDENTITY_INSERT $DestinationTable OFF", 30, @{}) | Out-Null #make sure Identity Insert is off...
        }
        
        return $RowCount
    }
}

#Open Cmdlet
Function Open-SqlConnection {
    <#
    .Synopsis
        Open a connection to a SQL Server.
 
    .Description
        Open a connection to a SQL Server. Default authentication is Integrated Windows Authetication.
         
        System.Data.SqlClient.
 
    .Parameter ConnectionName
        The name to associate with the newly created connection.
        This connection will be used with other cmdlets when specifying
        -ConnectionName <name>. If you don't specify one, it will be
        set to the default.
 
    .Parameter ConnectionString
        Specifies a provider specific connectionstring to be used.
 
    .Parameter CommandTimeout
        The default command timeout to be used for all commands executed against this connection.
 
    .Parameter DataSource
        The datasource for the connection.
 
    .Parameter InitialCatalog
        Database catalog connecting to.
 
    .Parameter UserName
        Username to connect as.
 
    .Parameter Password
        Password for the connecting user.
 
    .Parameter AzureAD
        Use this when connecting to an Azure SQL Database and you are using Azure AD credentials.
        You can specify the credentials either by using the UserName/Password parameters or by
        passing in a credential object to the Credential parameter.
     
    .Parameter AzureToken
        Pass in Azure Token (make sure you use the proper resource). If your token begins with "bearer "
        that will be stripped off first.
 
    .Parameter Credential
        Credential object containing the SQL user/pass.
 
    #>

    [CmdletBinding(DefaultParameterSetName="default")]
    Param([Parameter(ValueFromPipelineByPropertyName)][Alias("cn")][string]$ConnectionName = "default"
        , [Parameter(ValueFromPipelineByPropertyName)][int]$CommandTimeout = 30
        , [Parameter(ValueFromPipelineByPropertyName, ParameterSetName="default", Position=0)]
            [Parameter(ValueFromPipelineByPropertyName, ParameterSetName="user", Position=0)]
            [Parameter(ValueFromPipelineByPropertyName, ParameterSetName="cred", Position=0)]
            [Parameter(ValueFromPipelineByPropertyName, ParameterSetName="token", Position=0)]
                [Alias("SqlInstance","SqlServer","DataSource")][string]$Server = "localhost"
        , [Parameter(ValueFromPipelineByPropertyName, ParameterSetName="default", Position=1)]
            [Parameter(ValueFromPipelineByPropertyName, ParameterSetName="user", Position=1)]
            [Parameter(ValueFromPipelineByPropertyName, ParameterSetName="cred", Position=1)]
            [Parameter(ValueFromPipelineByPropertyName, ParameterSetName="token", Position=1)]
                [Alias("SqlDatabase","InitialCatalog")][string]$Database = "master"
        , [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName="user")]
            [string]$UserName
        , [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName="user")]
            [string]$Password
        , [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName="cred")]
            [Parameter(ValueFromPipelineByPropertyName, ParameterSetName="conn")]
                [pscredential]$Credential
        , [Parameter(ParameterSetName="user")]
            [Parameter(ParameterSetName="cred")][Switch]$AzureAD
        , [Parameter(Mandatory, ParameterSetName="token")][string]$AzureToken
        , [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName="Conn")]
            [Parameter(ParameterSetName="token")][string]$ConnectionString)

    If($Script:Connections.ContainsKey($ConnectionName)) { Close-SqlConnection $ConnectionName }

    If($ConnectionString) { $sb = [System.Data.SqlClient.SqlConnectionStringBuilder]::new($ConnectionString) }
    Else {
        $sb = [System.Data.SqlClient.SqlConnectionStringBuilder]::new()

        If($Server) { $sb.Server = $Server }
        If($Database) { $sb.Database = $Database }
        If($UserName) { 
            Write-Warning "You are using -UserName and -Password, these options are deprecated and will be removed in the future. Please consider using -Credential."
            $sb["User Id"] = $UserName
            $sb.Password = $Password
        }
        If($AzureAD) {
            $sb.Encrypt = $true
            $sb.Authentication = "Active Directory Password"
        }
        If(-not $UserName -and -not $Credential -and -not $AzureToken) { $sb["Integrated Security"] = $true }
    }
    $sb["Application Name"] = "PowerShell ({0})" -f $ConnectionName

    If($Credential) {
        [securestring]$sqlCred = $Credential.Password.Copy()
        $sqlCred.MakeReadOnly()
        $conn =  [System.Data.SqlClient.SqlConnection]::new($sb.ConnectionString, [System.Data.SqlClient.SqlCredential]::new($Credential.UserName, $sqlCred))
    }
    Else {
        $conn = [System.Data.SqlClient.SqlConnection]::new($sb.ConnectionString)
        if($AzureToken) { 
            if($AzureToken.Substring(0,7) -eq "bearer ") { $AzureToken = $AzureToken.Substring(7) }
            $conn.AccessToken = $AzureToken
        }
    }
    
    Try { $conn.Open() }
    Catch {
        $conn.Dispose()
        Throw $_
    }
    $Script:Connections.$ConnectionName = [SQLProvider]::new($ConnectionName, $CommandTimeout, $conn)
}

Export-ModuleMember -Function Open-SqlConnection